<commit_before>////////////////////////////
// Plugin jQuery - webRTC //
////////////////////////////

/**
	TODO :
	- settings :
				signalling server						-> default : http://
				STUN/TURN config						-> default : NONE
				url parameter 							-> default : room
				onChannelMessage & sendMessage function -> default :
				local & remote 							-> default : #local & #remote
				status bar 								-> default : #status

 */

(function($){
	$.webRtc = function(elem, options) {

		// Variables Declaration //
		var  self = this;
		self.$elem = $(elem);
		self.elem = elem;

		self.init = function() {
			console.log("Initializing");

			// Plugin's options //
			self.options = $.extend( {}, $.fn.createWebrtc.options, options );

			self.localVideo = self.video("localVideo");
			self.remoteVideo = self.video("remoteVideo");

			self.$elem.find(self.options.local).append(self.localVideo);
			self.$elem.find(self.options.remote).append(self.remoteVideo);

		    self.connection = null;
		    self.pc = null;
		    self.openChannel();
		    self.getUserMedia();
		};

		self.video = function(idVideo) {
			return $('<video></video>')
						.attr({
							width: "100%",
							height: "100%",
							id: idVideo,
							autoplay: "autoplay"
						})
						.css({
							opacity: "0",
							"-webkit-transition-property": "opacity",
							"-webkit-transition-duration": "2s"
						});
		};

		self.resetStatus = function() {
			if (!self.guest) {
		        self.setStatus("Waiting for someone to join: "+window.location.href+"?"+self.options.urlParameters+"="+self.room+"");
			} else {
        		self.setStatus("Initializing...");
        	}
		};

		self.setStatus= function(state) {
		    $(self.options.status).html(state);
		};

		self.openChannel = function() {
			self.connection = new WebSocket(self.options.signallingServer);

			// When the connection is open, send some data to the server
			self.connection.onopen = self.onChannelOpened;

			// Log errors
			self.connection.onerror = function (error) {
			console.log('WebSocket Error ' + error);
			}
			// Log messages from the server
			self.connection.onmessage = self.onChannelMessage;

			self.connection.onclose = self.onChannelClosed;

			$(window).unload(function(){ self.connection.close(); self.connection = null });
		};

		self.getUserMedia = function() {
			try {
				navigator.webkitGetUserMedia(self.options.mediaParameters, self.onUserMediaSuccess,self.onUserMediaError);
				console.log("Requested access to local media.");
			} catch (e) {
				console.log("getUserMedia error.");
			}
		};

		self.onUserMediaSuccess = function(stream) {

		    console.log("User has granted access to local media.");
		    var url = webkitURL.createObjectURL(stream);
		    self.localVideo.css("opacity", "1");
		    self.localVideo.attr("src", url);
		    self.localStream = stream;
		    if (self.guest) self.maybeStart();
		};

		self.onUserMediaError = function(error) {
		    console.log("Failed to get access to local media. Error code was " + error.code);
		    alert("Failed to get access to local media. Error code was " + error.code + ".");
		};

		self.maybeStart = function() {
		    if (!self.started && self.localStream && self.channelReady) {
		    	self.setStatus("Connecting...");
		        console.log("Creating PeerConnection.");
		        self.createPeerConnection();
		        console.log("Adding local stream.");
		        self.pc.addStream(self.localStream);
		        self.started = true;
		    }
		};

		self.createPeerConnection = function() {
			if(typeof webkitPeerConnection === 'function')
		    	self.pc = new webkitPeerConnection(self.options.serverStunTurn, self.onSignalingMessage);
		    else
		    	self.pc = new webkitDeprecatedPeerConnection(self.options.serverStunTurn, self.onSignalingMessage);
		    self.pc.onconnecting = self.onSessionConnecting;
		    self.pc.onopen = self.onSessionOpened;
		    self.pc.onaddstream = self.onRemoteStreamAdded;
		    self.pc.onremovestream = self.onRemoteStreamRemoved;
		};

		self.onSignalingMessage = function(message) {
		    self.sendMessage("SDP", message);
		};

		self.onHangup = function() {
		    console.log("Hanging up.");
		    self.localVideo.css("opacity", "0");
		    self.remoteVideo.css("opacity", "0");
		    self.pc.close();
		    self.pc = null;
		    self.connection.close();
		    self.setStatus("You have left the call.");
		};

		self.onChannelOpened = function() {
		    console.log('Channel opened.');
		    self.channelReady = true;
		    self.setGuest();
		    if (self.guest) self.maybeStart();
		};

		self.setGuest = function() {
			var urlParameters = self.getUrlVars();
			if(urlParameters[self.options.urlParameters]) {
		      self.room = urlParameters[self.options.urlParameters];
		      self.sendMessage("INVITE", self.room)
		      self.guest =1;
		    }
		    else{
		      self.sendMessage("GETROOM")
		      self.guest =0;
		    }
		}

		self.getUrlVars = function() {
		    var vars = [], hash;
		    var hashes = window.location.href.slice(window.location.href.indexOf('?') + 1).split('&');
		    for(var i = 0; i < hashes.length; i++)
		    {
		        hash = hashes[i].split('=');
		        vars.push(hash[0]);
		        vars[hash[0]] = hash[1];
		    }
		    return vars;
		}

		self.sendMessage = function(type, mess) {
			if(!mess) mess = "";
			var message = JSON.stringify({"type" : type, "value" : mess});
			self.connection.send(message);
		};

		self.onChannelMessage = function(message) {

		    self.message = JSON.parse(message.data);

		    switch(self.message["type"]) {
		      case "GETROOM" :
		        self.room = self.message["value"];
		        self.resetStatus();
		      break;
		      case "SDP" :
		        if (self.message["value"].indexOf("\"ERROR\"", 0) == -1) {
		          if (!self.guest && !self.started) self.maybeStart();
		          self.pc.processSignalingMessage(self.message["value"]);
		        }
		      break;
		      case "BYE" :
		        self.onChannelBye();
		      break;
		    }
		};

		self.onChannelBye = function() {
		    console.log('Session terminated.');
		    self.remoteVideo.css("opacity", "0");
		    //self.remoteVideo.attr("src",null);
		    self.guest = 0;
		    self.started = false;
		    self.setStatus("Your partner have left the call.");
		};

		self.onChannelError = function() {
		    console.log('Channel error.');
		};

		self.onChannelClosed = function() {
		    console.log('Channel closed.');
		};

		self.onSessionConnecting = function(message) {
		    console.log("Session connecting.");
		};

		self.onSessionOpened = function(message) {
		    console.log("Session opened.");
		};

		self.onRemoteStreamAdded = function(event) {
		    console.log("Remote stream added.");
		    var url = webkitURL.createObjectURL(event.stream);
		    self.remoteVideo.css("opacity", "1");
		    self.remoteVideo.attr("src",url);
		    self.setStatus("Is currently in video conference.
<button id=\"hangup\">Hang Up</button>");
		    $('#hangup').on('click', self.onHangup);
		};

		self.onRemoteStreamRemoved = function(event) {
		    console.log("Remote stream removed.");
		};

		self.init();

		return self;
	};

	$.fn.createWebrtc = function( options ) {
		return this.each(function() {
			(new $.webRtc(this, options));
		});
	};

	$.fn.createWebrtc.options = {
		local: '#local',
		remote: '#remote',
		status: '#status',
		signallingServer: 'ws://localhost:8080',
		serverStunTurn: 'NONE',
		urlParameters : 'room',
		mediaParameters: 'audio,video'
	};
})(jQuery);<commit_msg>Update routine<commit_after>////////////////////////////
// Plugin jQuery - webRTC //
////////////////////////////

/**
	TODO :
	- settings :
				signalling server						-> default : http://
				STUN/TURN config						-> default : NONE
				url parameter 							-> default : room
				onChannelMessage & sendMessage function -> default :
				local & remote 							-> default : #local & #remote
				status bar 								-> default : #status

 */

// Utility
if ( typeof Object.create !== 'function' ) {
	Object.create = function( obj ) {
		function F() {};
		F.prototype = obj;
		return new F();
	};
}

(function($){
	$.webRtc = function(elem, options) {

		// Variables Declaration //
		var  self = this;
		self.$elem = $(elem);
		self.elem = elem;

		self.init = function() {
			console.log("Initializing");

			// Plugin's options //
			self.options = $.extend( {}, $.fn.createWebrtc.options, options );

			self.localVideo = self.video("localVideo");
			self.remoteVideo = self.video("remoteVideo");

			self.$elem.find(self.options.local).append(self.localVideo);
			self.$elem.find(self.options.remote).append(self.remoteVideo);

		    self.connection = null;
		    self.pc = null;
		    self.openChannel();
		    self.getUserMedia();
		};

		self.video = function(idVideo) {
			return $('<video></video>')
						.attr({
							width: "100%",
							height: "100%",
							id: idVideo,
							autoplay: "autoplay"
						})
						.css({
							opacity: "0",
							"-webkit-transition-property": "opacity",
							"-webkit-transition-duration": "2s"
						});
		};

		self.resetStatus = function() {
			if (!self.guest) {
		        self.setStatus("Waiting for someone to join: "+window.location.href+"?"+self.options.urlParameters+"="+self.room+"");
			} else {
        		self.setStatus("Initializing...");
        	}
		};

		self.setStatus= function(state) {
		    $(self.options.status).html(state);
		};

		self.openChannel = function() {
			self.connection = new WebSocket(self.options.signallingServer);

			// When the connection is open, send some data to the server
			self.connection.onopen = self.onChannelOpened;

			// Log errors
			self.connection.onerror = function (error) {
			console.log('WebSocket Error ' + error);
			}
			// Log messages from the server
			self.connection.onmessage = self.onChannelMessage;

			self.connection.onclose = self.onChannelClosed;

			$(window).unload(function(){ self.connection.close(); self.connection = null });
		};

		self.getUserMedia = function() {
			try {
				navigator.webkitGetUserMedia(self.options.mediaParameters, self.onUserMediaSuccess,self.onUserMediaError);
				console.log("Requested access to local media.");
			} catch (e) {
				console.log("getUserMedia error.");
			}
		};

		self.onUserMediaSuccess = function(stream) {

		    console.log("User has granted access to local media.");
		    var url = webkitURL.createObjectURL(stream);
		    self.localVideo.css("opacity", "1");
		    self.localVideo.attr("src", url);
		    self.localStream = stream;
		    if (self.guest) self.maybeStart();
		};

		self.onUserMediaError = function(error) {
		    console.log("Failed to get access to local media. Error code was " + error.code);
		    alert("Failed to get access to local media. Error code was " + error.code + ".");
		};

		self.maybeStart = function() {
		    if (!self.started && self.localStream && self.channelReady) {
		    	self.setStatus("Connecting...");
		        console.log("Creating PeerConnection.");
		        self.createPeerConnection();
		        console.log("Adding local stream.");
		        self.pc.addStream(self.localStream);
		        self.started = true;
		    }
		};

		self.createPeerConnection = function() {
			if(typeof webkitPeerConnection === 'function')
		    	self.pc = new webkitPeerConnection(self.options.serverStunTurn, self.onSignalingMessage);
		    else
		    	self.pc = new webkitDeprecatedPeerConnection(self.options.serverStunTurn, self.onSignalingMessage);
		    self.pc.onconnecting = self.onSessionConnecting;
		    self.pc.onopen = self.onSessionOpened;
		    self.pc.onaddstream = self.onRemoteStreamAdded;
		    self.pc.onremovestream = self.onRemoteStreamRemoved;
		};

		self.onSignalingMessage = function(message) {
		    self.sendMessage("SDP", message);
		};

		self.onHangup = function() {
		    console.log("Hanging up.");
		    self.localVideo.css("opacity", "0");
		    self.remoteVideo.css("opacity", "0");
		    self.pc.close();
		    self.pc = null;
		    self.connection.close();
		    self.setStatus("You have left the call.");
		};

		self.onChannelOpened = function() {
		    console.log('Channel opened.');
		    self.channelReady = true;
		    self.setGuest();
		    if (self.guest) self.maybeStart();
		};

		self.setGuest = function() {
			var urlParameters = self.getUrlVars();
			if(urlParameters[self.options.urlParameters]) {
		      self.room = urlParameters[self.options.urlParameters];
		      self.sendMessage("INVITE", self.room)
		      self.guest =1;
		    }
		    else{
		      self.sendMessage("GETROOM")
		      self.guest =0;
		    }
		}

		self.getUrlVars = function() {
		    var vars = [], hash;
		    var hashes = window.location.href.slice(window.location.href.indexOf('?') + 1).split('&');
		    for(var i = 0; i < hashes.length; i++)
		    {
		        hash = hashes[i].split('=');
		        vars.push(hash[0]);
		        vars[hash[0]] = hash[1];
		    }
		    return vars;
		}

		self.sendMessage = function(type, mess) {
			if(!mess) mess = "";
			var message = JSON.stringify({"type" : type, "value" : mess});
			self.connection.send(message);
		};

		self.onChannelMessage = function(message) {

		    self.message = JSON.parse(message.data);

		    switch(self.message["type"]) {
		      case "GETROOM" :
		        self.room = self.message["value"];
		        self.resetStatus();
		      break;
		      case "SDP" :
		        if (self.message["value"].indexOf("\"ERROR\"", 0) == -1) {
		          if (!self.guest && !self.started) self.maybeStart();
		          self.pc.processSignalingMessage(self.message["value"]);
		        }
		      break;
		      case "BYE" :
		        self.onChannelBye();
		      break;
		    }
		};

		self.onChannelBye = function() {
		    console.log('Session terminated.');
		    self.remoteVideo.css("opacity", "0");
		    //self.remoteVideo.attr("src",null);
		    self.guest = 0;
		    self.started = false;
		    self.setStatus("Your partner have left the call.");
		};

		self.onChannelError = function() {
		    console.log('Channel error.');
		};

		self.onChannelClosed = function() {
		    console.log('Channel closed.');
		};

		self.onSessionConnecting = function(message) {
		    console.log("Session connecting.");
		};

		self.onSessionOpened = function(message) {
		    console.log("Session opened.");
		};

		self.onRemoteStreamAdded = function(event) {
		    console.log("Remote stream added.");
		    var url = webkitURL.createObjectURL(event.stream);
		    self.remoteVideo.css("opacity", "1");
		    self.remoteVideo.attr("src",url);
		    self.setStatus("Is currently in video conference.
<button id=\"hangup\">Hang Up</button>");
		    $('#hangup').on('click', self.onHangup);
		};

		self.onRemoteStreamRemoved = function(event) {
		    console.log("Remote stream removed.");
		};

		self.init();

		return self;
	};

	$.fn.createWebrtc = function( options ) {
		return this.each(function() {
			var webrtc = Object.create( $.webRtc(this, options) );
		});
	};

	$.fn.createWebrtc.options = {
		local: '#local',
		remote: '#remote',
		status: '#status',
		signallingServer: 'ws://localhost:8080',
		serverStunTurn: 'NONE',
		urlParameters : 'room',
		mediaParameters: 'audio,video'
	};
})(jQuery);<|endoftext|>


<commit_before>'use strict';

define(['Base', 'jquery'], function (Base, jQuery) {

	var Slideshow = function (args) {
		console.group('Slideshow.constructor enter ', arguments);
		Base.call(this, args);

        if (this.slideModule === 'undefined'){
            throw new TypeError ('this.slideModule must be an object (AMD "Slide" module)');
        }
        this.addSlides();
		console.groupEnd('Slideshow.constructor leave ', this);
	};

	Slideshow.prototype = Object.create( Base.prototype	 );
	Slideshow.prototype.constructor = Slideshow;
	Slideshow.prototype.defaults = {
		el 				: null,	// HTML element that is the slideshow
		direction		: 1,
		currentIndex 	: 0,
		startIndex 		: 0,
        slideModule     : null,
		slides 			: []	// Array[<Slide>]
	};

	Slideshow.prototype.addSlides = function () {
		var self = this;
		this.el.children().each( function (index, el) {
			self.slides.push(
				self.addSlide({
                    el: el,
                    index: index
                })
			);
			console.debug('Slideshow.addSlides loaded %d', index);
		});
        this.slides[ this.currentIndex ].in( this.direction );
        this.slides[ this.currentIndex ].show();
        this.setupControls();
	};

    Slideshow.prototype.addSlide = function (properties) {
        var slide = new this.slideModule (properties);
        slide.onAdd();
        return slide;
    };

	Slideshow.prototype.setupControls = function (args) {
		var self = this;
        self.controls = {};
        self.controls.el = jQuery('

');
		self.controls.el.appendTo( self.el );
		self.controls.previous = jQuery('#controls > .previous');
        self.controls.previous.on('click', function (e) {
			self.change('previous');
		});
        self.controls.previous.show();
        self.controls.next = jQuery('#controls > .next');
  		self.controls.next.on('click', function (e) {
			self.change('next');
		});
		jQuery(window).keyup( function (e) {
			self.keypressed(self, e);
		});
        self.beforeChange(0);
	}

	Slideshow.prototype.keypressed = function (self, e) {
		var self = this;
		// Next slide
		switch (e.keyCode){
			case 40: // down
			case 39: // right
			case 13: // enter
				e.preventDefault();
				self.change('next');
				break;
			case 37: // left
			case 38: // up
			case 8 : // backspace
			case 46: // del
				e.preventDefault();
				self.change('previous');
				break;
		}
	};

	Slideshow.prototype.change = function (direction) {
        console.log('Slideshow.change enter for slide #%d, direction %d', this.currentIndex, direction);

		if (direction=='next'){
            this.direction = 1;
            console.log('Slideshow.change direction +1, currentIndex now [%d]', this.currentIndex);
		}
		else if (direction=='previous'){
			this.direction = -1;
            console.log('Slideshow.change direction -1, currentIndex now [%d]', this.direction, this.currentIndex);
		}
		else {
			this.direction = parseInt( direction );
		}

        var nextIndex  = this.currentIndex + this.direction;
		if (nextIndex == this.slides.length){
            console.log('Slideshow.change set currentIndex to 0');
			nextIndex = 0;
		}
		else if (nextIndex < 0){
			nextIndex = this.slides.length - 1;
            console.log('Slideshow.change setting currentIndex to the end');
		}

        this.changeToSlideIndex( nextIndex );
    };

    Slideshow.prototype.changeToSlideIndex = function (nextIndex) {
        var possNextIndex = this.beforeChange(nextIndex);
        if (possNextIndex !== this.currentIndex){
            this.slides[ this.currentIndex ].out();
            this.slides[ this.currentIndex ].hide();

            this.currentIndex = possNextIndex;

            if (this.currentIndex == 0){
                this.beforeShowFirst();
            } else if (this.currentIndex == this.slides.length -1){
                this.beforeShowFinal();
            }

    		console.log('Slideshow.change leave for slide #%d, direction', this.currentIndex, this.direction);
    		this.slides[ this.currentIndex ].in( this.direction );
    		this.slides[ this.currentIndex ].show();
            this.afterChange();
        }
	};

    /** @return value that this.currentIndex will be assigned */
    Slideshow.prototype.beforeChange = function (nextIndex) {
        return nextIndex;
    };

    Slideshow.prototype.afterChange = function () {};

    Slideshow.prototype.beforeShowFirst = function () {};
    Slideshow.prototype.beforeShowFinal = function () {};

	return Slideshow;
});
<commit_msg>Simplify and document<commit_after>'use strict';

define(['Base', 'jquery'], function (Base, jQuery) {

	var Slideshow = function (args) {
		console.group('Slideshow.constructor enter ', arguments);
		Base.call(this, args);

        if (this.slideModule === 'undefined'){
            throw new TypeError ('this.slideModule must be an object/prototype that suppoerts the interface in Slide.');
        }
        this.addSlides();
		console.groupEnd('Slideshow.constructor leave ', this);
	};

	Slideshow.prototype = Object.create( Base.prototype	 );
	Slideshow.prototype.constructor = Slideshow;
	Slideshow.prototype.defaults = {
		el 				: null,	// HTML element that is the slideshow
		direction		: 1,
		currentIndex 	: 0,
		startIndex 		: 0,
        slideModule     : null,
		slides 			: []	// Array[<Slide>]
	};

/** Adds the children of `this.el` as slides via `this.addSlide()`
    @return Void
*/
	Slideshow.prototype.addSlides = function () {
		var self = this;
		this.el.children().each( function (index, el) {
			self.slides.push(
				self.addSlide({
                    el: el,
                    index: index
                })
			);
            console.debug('Slideshow.addSlides loaded %d', index);
		});
        this.slides[ this.currentIndex ].in( this.direction );
        this.slides[ this.currentIndex ].show();
        this.setupControls();
	};

/** @return An object of the prototype in `slideModule` */
    Slideshow.prototype.addSlide = function (properties) {
        var slide = new this.slideModule (properties);
        slide.onAdd();
        return slide;
    };

/** @return Void */
	Slideshow.prototype.setupControls = function () {
		var self = this;
        self.controls = {};
        self.controls.el = jQuery('

');
		self.controls.el.appendTo( self.el );
		self.controls.previous = jQuery('#controls > .previous');
        self.controls.previous.on('click', function (e) {
			self.change('previous');
		});
        self.controls.previous.show();
        self.controls.next = jQuery('#controls > .next');
  		self.controls.next.on('click', function (e) {
			self.change('next');
		});
		jQuery(window).keyup( function (e) {
			self.keypressed(e);
		});
        self.beforeChange(0);
	}

/** Respond to a keyboard event.
    @param e [Event] - The key event.
    @return Void
*/
	Slideshow.prototype.keypressed = function (e) {
		var self = this;
		// Next slide
		switch (e.keyCode){
			case 40: // down
			case 39: // right
			case 13: // enter
				e.preventDefault();
				self.change('next');
				break;
			case 37: // left
			case 38: // up
			case 8 : // backspace
			case 46: // del
				e.preventDefault();
				self.change('previous');
				break;
		}
	};

/** @param direction [int|string] - If an int, let it be the index of a slide. If a string, let it be 'previous' or 'next'.
    @return Void
*/
	Slideshow.prototype.change = function (direction) {
        console.log('Slideshow.change enter for slide #%d, direction %d', this.currentIndex, direction);

		if (direction=='next'){
            this.direction = 1;
            console.log('Slideshow.change direction +1, currentIndex now [%d]', this.currentIndex);
		}
		else if (direction=='previous'){
			this.direction = -1;
            console.log('Slideshow.change direction -1, currentIndex now [%d]', this.direction, this.currentIndex);
		}
		else {
			this.direction = parseInt( direction );
		}

        var nextIndex  = this.currentIndex + this.direction;
		if (nextIndex >= this.slides.length){
            console.log('Slideshow.change set currentIndex to 0');
			nextIndex = 0;
		}
		else if (nextIndex < 0){
			nextIndex = this.slides.length - 1;
            console.log('Slideshow.change setting currentIndex to the end');
		}

        this.changeToSlideIndex( nextIndex );
    };

/** @param nextIndex [int] - The index of the slide to show.
    @event beforeChange - Accepts the input parameter, whose effective value it may change.
    @event beforeShowFirst
    @event beforeShowFinal
    @event afterChange
    @return Void
*/
    Slideshow.prototype.changeToSlideIndex = function (nextIndex) {
        var possNextIndex = this.beforeChange(nextIndex);
        if (possNextIndex !== this.currentIndex){
            this.slides[ this.currentIndex ].out();
            this.slides[ this.currentIndex ].hide();

            this.currentIndex = possNextIndex;

            if (this.currentIndex == 0){
                this.beforeShowFirst();
            } else if (this.currentIndex == this.slides.length -1){
                this.beforeShowFinal();
            }

    		console.log('Slideshow.change leave for slide #%d, direction', this.currentIndex, this.direction);
    		this.slides[ this.currentIndex ].in( this.direction );
    		this.slides[ this.currentIndex ].show();
            this.afterChange();
        }
	};

/** No-op event.
    @param nextIndex [int] The index of the slide about to be shown.
    @return [int] The index of the slide to show next —
    ie a value that will be assigned to `this.currentIndex`.
*/
    Slideshow.prototype.beforeChange = function (nextIndex) {
        return nextIndex;
    };

/** No-op event.
    @return Void
*/
    Slideshow.prototype.afterChange = function () {};

/** No-op event.
    @return Void
*/
    Slideshow.prototype.beforeShowFirst = function () {};

/** No-op event.
    @return Void
*/
    Slideshow.prototype.beforeShowFinal = function () {};

	return Slideshow;
});
<|endoftext|>


<commit_before>function StripedShape(type, dir, x, y, board) {
    Shape.call(this, type, x, y, board);
    this.stripeDirection = dir;
    if (dir === StripedShape.HORIZONTAL) {
        this.special = StripedShape.HORIZONTAL;
    }
    else {
        this.special = StripedShape.VERTICAL;
    }
}

StripedShape.HORIZONTAL = 1;
StripedShape.VERTICAL = 2;

StripedShape.prototype = new Shape();
StripedShape.prototype.constructor = StripedShape;

StripedShape.prototype.crush = function (board) {
    if (this.stripeDirection === 0) {
        // overridden
        return {score: 0, addition: 0, jelly: 0, blocker: 0, multiply: 1};
    }
    var e = new StripeEffect(board);
    e.addLine(this.x, this.y, this.stripeDirection, this.type);
    board.addItemToClear(e);
    return Shape.prototype.crush.call(this, board);
};

function StripeEffect(board) {
    this.board = board;
    board.goodCount += 1;
    this.lines = [];
    this.totalTicks = 2;
    this.tick = this.totalTicks;
    this.progress = 0;
}

StripeEffect.HORIZONTAL = 1;
StripeEffect.VERTICAL = 2;

StripeEffect.prototype.addLine = function (x, y, direction, color) {
    this.lines.push({x: x, y: y, direction: direction, stillChanging: true, type: color});
};

StripeEffect.prototype.lineUpdate = function (i, affect, board) {
    var line = this.lines[i];
    var work = false;
    if (line.direction === StripeEffect.VERTICAL) {
        if (line.y - this.progress >= 0) {
            affect.push({x: line.x, y: line.y - this.progress});
            board.lockPosition(line.x, line.y - this.progress, line);
            work = true;
        }
        if (line.y + this.progress < board.height) {
            affect.push({x: line.x, y: line.y + this.progress});
            work = true;
        }
        if (line.y - this.progress + 1 >= 0) {
            board.unlockPosition(line.x, line.y - this.progress + 1, line);
        }
        if (!work) {
            board.unlockPosition(line.x, 0, this);
            line.stillChanging = false;
        }
        work = line.y - this.progress >= -4 || line.y + this.progress < board.height + 4;
    }
    else {
        if (line.x - this.progress >= 0) {
            affect.push({x: line.x - this.progress, y: line.y});
            work = true;
        }
        if (line.x + this.progress < board.width) {
            affect.push({x: line.x + this.progress, y: line.y});
            work = true;
        }
        if (!work) {
            line.stillChanging = false;
        }
        work = line.x - this.progress >= -4 || line.x + this.progress < board.width + 4;
    }
    return work;
};

StripeEffect.prototype.update = function () {
    'use strict';
    this.tick--;
    var still = false;
    for (var i = 0; i < this.lines.length; i++) {
        if (this.lines[i].stillChanging) still = true;
    }
    if (still) {
        this.board.itemChanged = true;
    }
    var work = true;
    if (this.tick <= 0) {
        this.tick += this.totalTicks;
        work = false;
        this.progress++;
        var scores = [];
        for (var i = 0; i < this.lines.length; i++) {
            if (this.lineUpdate(i, scores, board)) work = true;
        }
        // score
        var combo = 0;
        for (var i = 0; i < scores.length; i++) {
            scores[i].type = this.board.getShape(scores[i].x, scores[i].y).type;
            scores[i].combo = this.board.clearShape(scores[i].x, scores[i].y);
            combo += scores[i].combo.multiply;
        }
        for (var i = 0; i < scores.length; i++) {
            var c = scores[i].combo;
            scores[i].score = (c.score + c.addition) * combo + c.jelly + c.blocker;
            if (scores[i].score !== 0) {
                this.board.gainScores.push(scores[i]);
                this.board.score += scores[i].score;
            }
        }
    }
    for (var i = 0; i < this.lines.length; i++) {
        var line = this.lines[i];
        if (line.direction === StripeEffect.VERTICAL) {
            if (line.y - this.progress >= 0) {
                this.board.lockPosition(line.x, line.y - this.progress, line);
            }
        }
    }
    return work;
};

// returns array of [x, y, width, height, frameName]'s
StripeEffect.prototype.getSpritePositions = function () {
    var t2 = this.progress + 1 - this.tick / this.totalTicks;
    var sw = 0.6, sh = 0.6;
    var arr = [];
    for (var n = 0; n < this.lines.length; n++) {
        var line = this.lines[n];
        var frm = Shape.typeNames[line.type-1];
        frm += line.direction === StripeEffect.VERTICAL ? "VStripe" : "HStripe";
        var t = t2;
        for (var i = 0; t >= 0 && i < 3; t-=1, i++) {
            if (line.direction === StripeEffect.VERTICAL) {
                if (line.y - t > -1) {
                    arr.push([line.x, line.y - t, sw, sh, frm]);
                }
                if (line.y + t < this.board.height) {
                    arr.push([line.x, line.y + t, sw, sh, frm]);
                }
            }
            else {
                if (line.x - t > -1) {
                    arr.push([line.x - t, line.y, sw, sh, frm]);
                }
                if (line.x + t < this.board.width) {
                    arr.push([line.x + t, line.y, sw, sh, frm]);
                }
            }
        }
    }
    return arr;
};

function BigStripeEffect(board, from, to) {
    this.board = board;
    this.totalTicks = 30;
    this.tick = this.totalTicks;
    this.progress = 0;
    this.color = from instanceof StripedShape ? from.type : to.type;
    this.x = to.x;
    this.y = to.y;
    board.setShape(from.x, from.y, 0);
    board.setShape(to.x, to.y, 0);
    for (var i = Math.max(this.x-1, 0); i <= this.x+1 && i < board.width; i++) {
        board.lockPosition(i, Math.max(this.y-1, 0));
    }
    for (var i = Math.max(this.x-1, 0); i <= this.x+1 && i < board.width; i++) {
        for (var j = this.y-1; j <= this.y+1; j++) {
            if (i < 0 || j < 0) continue;
            if (i >= board.width || j >= board.height) continue;
            var sh = board.getShape(i, j);
            if (sh.type > 0) {
                board.clearShape(i, j);
            }
        }
    }
}

BigStripeEffect.prototype.update = function (board) {
    this.tick--;
    board.changed = true;
    if (this.tick === 0) {
        if (this.progress === 0) {
            var e = new StripeEffect(board);
            for (var i = this.y-1; i <= this.y+1; i++) {
                if (i >= 0 && i < board.height)
                    e.addLine(this.x, i, StripeEffect.HORIZONTAL, this.color);
            }
            board.addItemToClear(e);
        }
        else if (this.progress === 2) {
            for (var i = Math.max(this.x-1, 0); i <= this.x+1 && i < board.width; i++) {
                board.unlockPosition(i, Math.max(this.y-1, 0));
            }
            var e = new StripeEffect(board);
            for (var i = this.x-1; i <= this.x+1; i++) {
                if (i >= 0 && i < board.width)
                    e.addLine(i, this.y, StripeEffect.VERTICAL, this.color);
            }
            board.addItemToClear(e);
            return false;
        }
        this.tick = this.totalTicks;
        this.progress++;
    }
    return true;
};

BigStripeEffect.prototype.getSpritePositions = function () {
    var size = 2;
    if (this.progress === 0 && (this.totalTicks - this.tick) < 10) {
        size = 1 + (this.totalTicks - this.tick) / 10;
    }
    if (this.progress <= 2) {
        var frm = Shape.typeNames[this.color-1];
        frm += this.progress >= 2 ? "VStripe" : "HStripe";
        return [[this.x, this.y, size, size, frm]];
    }
    return [];
};
<commit_msg>change timing of striped + wrapped<commit_after>function StripedShape(type, dir, x, y, board) {
    Shape.call(this, type, x, y, board);
    this.stripeDirection = dir;
    if (dir === StripedShape.HORIZONTAL) {
        this.special = StripedShape.HORIZONTAL;
    }
    else {
        this.special = StripedShape.VERTICAL;
    }
}

StripedShape.HORIZONTAL = 1;
StripedShape.VERTICAL = 2;

StripedShape.prototype = new Shape();
StripedShape.prototype.constructor = StripedShape;

StripedShape.prototype.crush = function (board) {
    if (this.stripeDirection === 0) {
        // overridden
        return {score: 0, addition: 0, jelly: 0, blocker: 0, multiply: 1};
    }
    var e = new StripeEffect(board);
    e.addLine(this.x, this.y, this.stripeDirection, this.type);
    board.addItemToClear(e);
    return Shape.prototype.crush.call(this, board);
};

function StripeEffect(board) {
    this.board = board;
    board.goodCount += 1;
    this.lines = [];
    this.totalTicks = 2;
    this.tick = this.totalTicks;
    this.progress = 0;
}

StripeEffect.HORIZONTAL = 1;
StripeEffect.VERTICAL = 2;

StripeEffect.prototype.addLine = function (x, y, direction, color) {
    this.lines.push({x: x, y: y, direction: direction, stillChanging: true, type: color});
};

StripeEffect.prototype.lineUpdate = function (i, affect, board) {
    var line = this.lines[i];
    var work = false;
    if (line.direction === StripeEffect.VERTICAL) {
        if (line.y - this.progress >= 0) {
            affect.push({x: line.x, y: line.y - this.progress});
            board.lockPosition(line.x, line.y - this.progress, line);
            work = true;
        }
        if (line.y + this.progress < board.height) {
            affect.push({x: line.x, y: line.y + this.progress});
            work = true;
        }
        if (line.y - this.progress + 1 >= 0) {
            board.unlockPosition(line.x, line.y - this.progress + 1, line);
        }
        if (!work) {
            board.unlockPosition(line.x, 0, this);
            line.stillChanging = false;
        }
        work = line.y - this.progress >= -4 || line.y + this.progress < board.height + 4;
    }
    else {
        if (line.x - this.progress >= 0) {
            affect.push({x: line.x - this.progress, y: line.y});
            work = true;
        }
        if (line.x + this.progress < board.width) {
            affect.push({x: line.x + this.progress, y: line.y});
            work = true;
        }
        if (!work) {
            line.stillChanging = false;
        }
        work = line.x - this.progress >= -4 || line.x + this.progress < board.width + 4;
    }
    return work;
};

StripeEffect.prototype.update = function () {
    'use strict';
    this.tick--;
    var still = false;
    for (var i = 0; i < this.lines.length; i++) {
        if (this.lines[i].stillChanging) still = true;
    }
    if (still) {
        this.board.itemChanged = true;
    }
    var work = true;
    if (this.tick <= 0) {
        this.tick += this.totalTicks;
        work = false;
        this.progress++;
        var scores = [];
        for (var i = 0; i < this.lines.length; i++) {
            if (this.lineUpdate(i, scores, board)) work = true;
        }
        // score
        var combo = 0;
        for (var i = 0; i < scores.length; i++) {
            scores[i].type = this.board.getShape(scores[i].x, scores[i].y).type;
            scores[i].combo = this.board.clearShape(scores[i].x, scores[i].y);
            combo += scores[i].combo.multiply;
        }
        for (var i = 0; i < scores.length; i++) {
            var c = scores[i].combo;
            scores[i].score = (c.score + c.addition) * combo + c.jelly + c.blocker;
            if (scores[i].score !== 0) {
                this.board.gainScores.push(scores[i]);
                this.board.score += scores[i].score;
            }
        }
    }
    for (var i = 0; i < this.lines.length; i++) {
        var line = this.lines[i];
        if (line.direction === StripeEffect.VERTICAL) {
            if (line.y - this.progress >= 0) {
                this.board.lockPosition(line.x, line.y - this.progress, line);
            }
        }
    }
    return work;
};

// returns array of [x, y, width, height, frameName]'s
StripeEffect.prototype.getSpritePositions = function () {
    var t2 = this.progress + 1 - this.tick / this.totalTicks;
    var sw = 0.6, sh = 0.6;
    var arr = [];
    for (var n = 0; n < this.lines.length; n++) {
        var line = this.lines[n];
        var frm = Shape.typeNames[line.type-1];
        frm += line.direction === StripeEffect.VERTICAL ? "VStripe" : "HStripe";
        var t = t2;
        for (var i = 0; t >= 0 && i < 3; t-=1, i++) {
            if (line.direction === StripeEffect.VERTICAL) {
                if (line.y - t > -1) {
                    arr.push([line.x, line.y - t, sw, sh, frm]);
                }
                if (line.y + t < this.board.height) {
                    arr.push([line.x, line.y + t, sw, sh, frm]);
                }
            }
            else {
                if (line.x - t > -1) {
                    arr.push([line.x - t, line.y, sw, sh, frm]);
                }
                if (line.x + t < this.board.width) {
                    arr.push([line.x + t, line.y, sw, sh, frm]);
                }
            }
        }
    }
    return arr;
};

function BigStripeEffect(board, from, to) {
    this.board = board;
    this.totalTicks = 20;
    this.tick = this.totalTicks;
    this.progress = 0;
    this.color = from instanceof StripedShape ? from.type : to.type;
    this.x = to.x;
    this.y = to.y;
    board.setShape(from.x, from.y, 0);
    board.setShape(to.x, to.y, 0);
    for (var i = Math.max(this.x-1, 0); i <= this.x+1 && i < board.width; i++) {
        board.lockPosition(i, Math.max(this.y-1, 0));
    }
    for (var i = Math.max(this.x-1, 0); i <= this.x+1 && i < board.width; i++) {
        for (var j = this.y-1; j <= this.y+1; j++) {
            if (i < 0 || j < 0) continue;
            if (i >= board.width || j >= board.height) continue;
            var sh = board.getShape(i, j);
            if (sh.type > 0) {
                board.clearShape(i, j);
            }
        }
    }
}

BigStripeEffect.prototype.update = function (board) {
    this.tick--;
    board.changed = true;
    if (this.tick === 0) {
        if (this.progress === 0) {
            var e = new StripeEffect(board);
            for (var i = this.y-1; i <= this.y+1; i++) {
                if (i >= 0 && i < board.height)
                    e.addLine(this.x, i, StripeEffect.HORIZONTAL, this.color);
            }
            board.addItemToClear(e);
        }
        else if (this.progress === 2) {
            for (var i = Math.max(this.x-1, 0); i <= this.x+1 && i < board.width; i++) {
                board.unlockPosition(i, Math.max(this.y-1, 0));
            }
            var e = new StripeEffect(board);
            for (var i = this.x-1; i <= this.x+1; i++) {
                if (i >= 0 && i < board.width)
                    e.addLine(i, this.y, StripeEffect.VERTICAL, this.color);
            }
            board.addItemToClear(e);
            return false;
        }
        this.tick = this.totalTicks;
        this.progress++;
    }
    return true;
};

BigStripeEffect.prototype.getSpritePositions = function () {
    var size = 2;
    if (this.progress === 0 && (this.totalTicks - this.tick) < 10) {
        size = 1 + (this.totalTicks - this.tick) / 10;
    }
    if (this.progress <= 2) {
        var frm = Shape.typeNames[this.color-1];
        frm += this.progress >= 2 ? "VStripe" : "HStripe";
        return [[this.x, this.y, size, size, frm]];
    }
    return [];
};
<|endoftext|>


<commit_before>'use strict';

define(['bitcoinjs-lib', 'util/btc'], function(Bitcoin, BtcUtils) {

/**
 * User oriented history view.
 * @param {Object} store Store for the object.
 * @param {Object} identity Parent identity for the object
 * @constructor
 */
function History(store, identity) {
    this.history = [];
    this.identity = identity;
}

/**
 * Add a row into the history.
 * Will make sure it's inserted sorted by its height.
 * @param {Object} newRow row coming from history.buildHistoryRow
 * Will return -1 if row is confirmed, -2 if still not confirmed, -3 if already was confirmed
 * @return {Number} 0 if inserted, 1 confirmed, 2 still not confirmed, 3 was already confirmed
 * @private
 */
History.prototype.addHistoryRow = function(newRow) {
    // Look for repeated element
    for(var idx=this.history.length-1; idx>=0; idx--) {
        var row = this.history[idx];
        if (row.hash == newRow.hash) {
            // replace by new row
            this.history[idx] = newRow;
            // set return code depending on status
            if (!row.height && newRow.height) {
                return 1; // confirmed
            } else if (!row.height && !newRow.height) {
                return 2; // still not confirmed
            } else {
                // keep initial height
                this.history[idx].height = row.height;
                return 3; // already confirmed
            }
        }
    }
    // Not found so insert
    this.history.push(newRow);
    return 0;
};

/**
 * Build a history row from a transaction.
 * Will find out which inputs and outputs correspond to the identity wallet and calculate impact.
 * @param {String} transaction Transaction in serialized form
 * @param {Number} height Height for the transaction
 * @return {Object} The new row
 */
History.prototype.buildHistoryRow = function(transaction, height) {
    var identity = this.identity,
        btcWallet = identity.wallet.wallet,
        inMine = 0,
        outMine = 0,
        myInValue = 0,
        myOutValue = 0,
        txAddr = "",
        txObj = new Bitcoin.Transaction(transaction);
    var isStealth = false;
    var txHash = Bitcoin.convert.bytesToHex(txObj.getHash());

    var pocketImpact = {};
    var addPocketImpact = function(pocketId, amount) {
        if (!pocketImpact.hasOwnProperty(pocketId)) {
            pocketImpact[pocketId] = {ins: 0, outs: 0, total: 0};
        }
        if (amount > 0) {
            pocketImpact[pocketId].outs += amount;
        } else {
            pocketImpact[pocketId].ins -= amount;
        }
        pocketImpact[pocketId].total += amount;
    };

    // XXX temporary while bitcoinjs-lib supports testnet better
    txObj = BtcUtils.fixTxVersions(txObj, this.identity);

    var inAddress, inPocket, outPocket;

    // Check inputs
    for(var idx=0; idx<txObj.ins.length; idx++) {
        var anIn = txObj.ins[idx];
        var outIdx = anIn.outpoint.hash+":"+anIn.outpoint.index;
        if (btcWallet.outputs[outIdx]) {
            var output = btcWallet.outputs[outIdx];
            // save in pocket
            var inWalletAddress = identity.wallet.getWalletAddress(output.address);
            inPocket = identity.wallet.pockets.getAddressPocketId(inWalletAddress);
            // counters
            inMine += 1;
            myInValue += output.value;
            addPocketImpact(inPocket, -output.value);
        } else {
            var address = BtcUtils.getInputAddress(anIn, this.identity.wallet.versions);
            inAddress = address || inAddress;
        }
    }
    if (!inMine) {
        txAddr = inAddress || 'unknown';
    }
    // Check outputs
    for(var idx=0; idx<txObj.outs.length; idx++) {
        var anOut = txObj.outs[idx];
        var outWalletAddress = identity.wallet.getWalletAddress(anOut.address.toString());
        if (outWalletAddress) {
            var output = btcWallet.outputs[txHash+":"+idx];
            // TODO: mark also when input is mine and output not
            if (output && output.stealth) {
                isStealth = true;
            }
            outMine += 1;
            myOutValue += anOut.value;
            var _outPocket = identity.wallet.pockets.getAddressPocketId(outWalletAddress);
            // save out pocket (don't set if it's change)
            if (!((typeof outWalletAddress.index[0] === 'number') && (outWalletAddress.index[0]%2 == 1))) {
                outPocket = _outPocket;
            }
            addPocketImpact(_outPocket, anOut.value);
        } else {
            if (inMine) {
                txAddr = anOut.address.toString();
            }
        }
    }
    if (!txAddr) {
        txAddr = 'internal';
    }
    // Create a row representing this change (if already referenced will
    // be replaced)
    var txHash = Bitcoin.convert.bytesToHex(txObj.getHash());
    var newRow = {hash: txHash, tx: txObj, inMine: inMine, outMine: outMine, myInValue: myInValue, myOutValue: myOutValue, height: height, address: txAddr, isStealth: isStealth, total: myOutValue-myInValue, outPocket: outPocket, inPocket: inPocket, impact: pocketImpact};
    return newRow;
};

/**
 * Callback to fill missing input (depending on another transaction)
 * @param {Object} transaction Transaction in serialized form.
 * @param {Object} data        DOCME
 * @private
 */
History.prototype.fillInput = function(transaction, data) {
    var index = data[0],
        newRow = data[1],
        txObj = new Bitcoin.Transaction(transaction);
    // XXX temporary while bitcoinjs-lib supports testnet better
    txObj = BtcUtils.fixTxVersions(txObj, this.identity);
    newRow.address = txObj.outs[index].address.toString();
    this.update();
};

/**
 * Callback for fetching a transaction
 * @param {Object} transaction   Transaction in serialized form.
 * @param {Number} height        Height of the block.
 * @return {Object|null}         DOCME
 * @private
 */
History.prototype.txFetched = function(transaction, height) {
    var self = this,
        newRow = this.buildHistoryRow(transaction, height);

    // unknown for now means we need to fill in some extra inputs for now get 1st one
    if (newRow.address == 'unknown') {
        if (newRow.tx.ins[0])
            this.identity.txdb.fetchTransaction(newRow.tx.ins[0].outpoint.hash,
                                            function(_a, _b) {self.fillInput(_a, _b);},
                                            [newRow.tx.ins[0].outpoint.index, newRow]);
        else
            console.log("No input!", newRow.tx);
     }
    if (this.addHistoryRow(newRow) < 2) {
        // It's a relevant event so return the row (initial unspent or initial confirm)
        return newRow;
    }
};

/**
 * Look for transactions from given history records
 * @param {Object} history History array as returned by obelisk
 */
History.prototype.fillHistory = function(history) {
    var self = this,
        txdb = this.identity.txdb;
    history.forEach(function(tx) {
        var outTxHash = tx[0],
            inTxHash = tx[4];
        if (inTxHash) {
            // fetch a row for the spend
            txdb.fetchTransaction(inTxHash, function(_a, _b) {self.txFetched(_a, _b);}, tx[6]);
        }
        // fetch a row for the incoming tx
        txdb.fetchTransaction(outTxHash, function(_a, _b) {self.txFetched(_a, _b);}, tx[2]);
    });
};

/**
 * Update callback, needs to be filled in by the application to register.
 */
History.prototype.update = function() {
    console.log("Program needs to register an update callback!");
};

return History;
});
<commit_msg>Fix wrong parsing of output address.<commit_after>'use strict';

define(['bitcoinjs-lib', 'util/btc'], function(Bitcoin, BtcUtils) {

/**
 * User oriented history view.
 * @param {Object} store Store for the object.
 * @param {Object} identity Parent identity for the object
 * @constructor
 */
function History(store, identity) {
    this.history = [];
    this.identity = identity;
}

/**
 * Add a row into the history.
 * Will make sure it's inserted sorted by its height.
 * @param {Object} newRow row coming from history.buildHistoryRow
 * Will return -1 if row is confirmed, -2 if still not confirmed, -3 if already was confirmed
 * @return {Number} 0 if inserted, 1 confirmed, 2 still not confirmed, 3 was already confirmed
 * @private
 */
History.prototype.addHistoryRow = function(newRow) {
    // Look for repeated element
    for(var idx=this.history.length-1; idx>=0; idx--) {
        var row = this.history[idx];
        if (row.hash == newRow.hash) {
            // replace by new row
            this.history[idx] = newRow;
            // set return code depending on status
            if (!row.height && newRow.height) {
                return 1; // confirmed
            } else if (!row.height && !newRow.height) {
                return 2; // still not confirmed
            } else {
                // keep initial height
                this.history[idx].height = row.height;
                return 3; // already confirmed
            }
        }
    }
    // Not found so insert
    this.history.push(newRow);
    return 0;
};

/**
 * Build a history row from a transaction.
 * Will find out which inputs and outputs correspond to the identity wallet and calculate impact.
 * @param {String} transaction Transaction in serialized form
 * @param {Number} height Height for the transaction
 * @return {Object} The new row
 */
History.prototype.buildHistoryRow = function(transaction, height) {
    var identity = this.identity,
        btcWallet = identity.wallet.wallet,
        inMine = 0,
        outMine = 0,
        myInValue = 0,
        myOutValue = 0,
        txAddr = "",
        txObj = new Bitcoin.Transaction(transaction);
    var isStealth = false;
    var txHash = Bitcoin.convert.bytesToHex(txObj.getHash());

    var pocketImpact = {};
    var addPocketImpact = function(pocketId, amount) {
        if (!pocketImpact.hasOwnProperty(pocketId)) {
            pocketImpact[pocketId] = {ins: 0, outs: 0, total: 0};
        }
        if (amount > 0) {
            pocketImpact[pocketId].outs += amount;
        } else {
            pocketImpact[pocketId].ins -= amount;
        }
        pocketImpact[pocketId].total += amount;
    };

    // XXX temporary while bitcoinjs-lib supports testnet better
    txObj = BtcUtils.fixTxVersions(txObj, this.identity);

    var inAddress, inPocket, outPocket;

    // Check inputs
    for(var idx=0; idx<txObj.ins.length; idx++) {
        var anIn = txObj.ins[idx];
        var outIdx = anIn.outpoint.hash+":"+anIn.outpoint.index;
        if (btcWallet.outputs[outIdx]) {
            var output = btcWallet.outputs[outIdx];
            // save in pocket
            var inWalletAddress = identity.wallet.getWalletAddress(output.address);
            inPocket = identity.wallet.pockets.getAddressPocketId(inWalletAddress);
            // counters
            inMine += 1;
            myInValue += output.value;
            addPocketImpact(inPocket, -output.value);
        } else {
            var address = BtcUtils.getInputAddress(anIn, this.identity.wallet.versions);
            inAddress = address || inAddress;
        }
    }
    if (!inMine) {
        txAddr = inAddress || 'unknown';
    }
    // Check outputs
    for(var idx=0; idx<txObj.outs.length; idx++) {
        var anOut = txObj.outs[idx];
        var outWalletAddress = identity.wallet.getWalletAddress(anOut.address.toString());
        if (outWalletAddress) {
            var output = btcWallet.outputs[txHash+":"+idx];
            // TODO: mark also when input is mine and output not
            if (output && output.stealth) {
                isStealth = true;
            }
            outMine += 1;
            myOutValue += anOut.value;
            var _outPocket = identity.wallet.pockets.getAddressPocketId(outWalletAddress);
            // save out pocket (don't set if it's change)
            if (!((typeof outWalletAddress.index[0] === 'number') && (outWalletAddress.index[0]%2 == 1))) {
                outPocket = _outPocket;
            }
            addPocketImpact(_outPocket, anOut.value);
        } else {
            if (inMine) {
                txAddr = anOut.address.toString();
            }
        }
    }
    if (!txAddr) {
        txAddr = 'internal';
    }
    // Create a row representing this change (if already referenced will
    // be replaced)
    var txHash = Bitcoin.convert.bytesToHex(txObj.getHash());
    var newRow = {hash: txHash, tx: txObj, inMine: inMine, outMine: outMine, myInValue: myInValue, myOutValue: myOutValue, height: height, address: txAddr, isStealth: isStealth, total: myOutValue-myInValue, outPocket: outPocket, inPocket: inPocket, impact: pocketImpact};
    return newRow;
};

/**
 * Callback to fill missing input (depending on another transaction)
 * @param {Object} transaction Transaction in serialized form.
 * @param {Object} data        DOCME
 * @private
 */
History.prototype.fillInput = function(transaction, data) {
    var index = data[0],
        newRow = data[1],
        txObj = new Bitcoin.Transaction(transaction);
    // XXX temporary while bitcoinjs-lib supports testnet better
    txObj = BtcUtils.fixTxVersions(txObj, this.identity);
    if (Array.isArray(txObj.outs[index].hash)) {
        newRow.address = txObj.outs[index].address.toString();
        this.update();
    } else {
        // TODO: need to handle this better
        console.log("output not recognized:", txObj.outs[index]);
    }
};

/**
 * Callback for fetching a transaction
 * @param {Object} transaction   Transaction in serialized form.
 * @param {Number} height        Height of the block.
 * @return {Object|null}         DOCME
 * @private
 */
History.prototype.txFetched = function(transaction, height) {
    var self = this,
        newRow = this.buildHistoryRow(transaction, height);

    // unknown for now means we need to fill in some extra inputs for now get 1st one
    if (newRow.address == 'unknown') {
        if (newRow.tx.ins[0])
            this.identity.txdb.fetchTransaction(newRow.tx.ins[0].outpoint.hash,
                                            function(_a, _b) {self.fillInput(_a, _b);},
                                            [newRow.tx.ins[0].outpoint.index, newRow]);
        else
            console.log("No input!", newRow.tx);
     }
    if (this.addHistoryRow(newRow) < 2) {
        // It's a relevant event so return the row (initial unspent or initial confirm)
        return newRow;
    }
};

/**
 * Look for transactions from given history records
 * @param {Object} history History array as returned by obelisk
 */
History.prototype.fillHistory = function(history) {
    var self = this,
        txdb = this.identity.txdb;
    history.forEach(function(tx) {
        var outTxHash = tx[0],
            inTxHash = tx[4];
        if (inTxHash) {
            // fetch a row for the spend
            txdb.fetchTransaction(inTxHash, function(_a, _b) {self.txFetched(_a, _b);}, tx[6]);
        }
        // fetch a row for the incoming tx
        txdb.fetchTransaction(outTxHash, function(_a, _b) {self.txFetched(_a, _b);}, tx[2]);
    });
};

/**
 * Update callback, needs to be filled in by the application to register.
 */
History.prototype.update = function() {
    console.log("Program needs to register an update callback!");
};

return History;
});
<|endoftext|>


<commit_before>// Copyright (c) 2009, Scott Ferguson
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//     * Redistributions of source code must retain the above copyright
//       notice, this list of conditions and the following disclaimer.
//     * Redistributions in binary form must reproduce the above copyright
//       notice, this list of conditions and the following disclaimer in the
//       documentation and/or other materials provided with the distribution.
//     * Neither the name of the software nor the
//       names of its contributors may be used to endorse or promote products
//       derived from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY SCOTT FERGUSON ''AS IS'' AND ANY
// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
// DISCLAIMED. IN NO EVENT SHALL SCOTT FERGUSON BE LIABLE FOR ANY
// DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

// Fetch extension settings
var parentExtensionId = 'nlcklobeoigfjmcigmhbjkepmniladed';
var settings = {};
var port = chrome.extension.connect(parentExtensionId);

port.onMessage.addListener(function(data) {
    settings = data;

    console.log(settings);

    populateMenu();
});

port.postMessage({
    'message': 'GetForumsJumpList'
});

/**
 * Opens a link in a tab.
 */
function openTab(tabUrl) {
    var button = event.button;
    if(button > 1)
        return;
    var selected = true;
    if(button == 1 || event.ctrlKey) // Middle Button or Ctrl click
        selected = false;
    chrome.tabs.create({ url: tabUrl, selected: selected });
}

/**
 * Changes the sticky status of a forum in the popup menu
 */
function toggleSticky(forumID) {
    var forums = JSON.parse(settings.forumsList);
    for(i in forums) {
        if (forums[i].id != forumID)
            continue;

        if (forums[i].sticky == true) {
            forums[i].sticky = false;
            jQuery('img#sticky-'+forumID).each( function() {
                jQuery(this).attr('src', '../images/sticky_off.gif');
            });
        } else {
            forums[i].sticky = true;
            jQuery('img#sticky-'+forumID).each( function() {
                jQuery(this).attr('src', '../images/sticky_on.gif');
            });
        }
        settings.forumsList = JSON.stringify(forums);
        port.postMessage({ 'message': 'ChangeSALRSetting',
                           'option' : 'forumsList',
                           'value'  : JSON.stringify(forums) });
        return;
    }
}

/**
 * Populate the menu with forums.
 */
function populateMenu() {
    var forums = JSON.parse(settings.forumsList);
    var newHTML = '';
    var numSeps = 0;
    var color = '#ffffff';

    jQuery(forums).each( function() {
        var splitUp = this.name.match(/^(-*)(.*)/);

        var indent = splitUp[1].length;
        var title = splitUp[2];

        if (indent > 10) { // Separator
            if (numSeps > 0) {
                // Loop through forum list and add stickied forums
                for(i in forums) {
                    if (forums[i].sticky == true)  {
                        newHTML += populateMenuHelper(forums[i], color, true);
                        if (color == '#ffffff') {
                            color = '#eeeeee';
                        } else {
                            color = '#ffffff';
                        }
                    }
                }

                newHTML += '
';
            }
            numSeps ++;
        } else if (indent == 0) {
            newHTML += '
';
            newHTML += '' + title + '
';
            newHTML += '
';

        } else {
            newHTML += populateMenuHelper(this, color, false);
        }

        if (color == '#ffffff') {
            color = '#eeeeee';
        } else {
            color = '#ffffff';
        }
    });

    jQuery('div#forums-list').html(newHTML);
}

function populateMenuHelper(forum, color, stuck) {
    var subHTML = '';

    var splitUp = forum.name.match(/^(-*)(.*)/);
    var indent = splitUp[1].length;
    if (stuck == true)
        indent=2;
    var title = splitUp[2];

    // Add sticky controls to popup window
    if (forum.sticky == true)
        subHTML += '

';
    else
        subHTML += '

';

    // Dynamically set the 10's digit for padding here, since we can have any number
    // of indentations
    subHTML += '
';
    subHTML += '' + title + '
';
    subHTML += '
';
    return subHTML;
}
<commit_msg>Updated for changes in forumList object<commit_after>// Copyright (c) 2009, Scott Ferguson
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//     * Redistributions of source code must retain the above copyright
//       notice, this list of conditions and the following disclaimer.
//     * Redistributions in binary form must reproduce the above copyright
//       notice, this list of conditions and the following disclaimer in the
//       documentation and/or other materials provided with the distribution.
//     * Neither the name of the software nor the
//       names of its contributors may be used to endorse or promote products
//       derived from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY SCOTT FERGUSON ''AS IS'' AND ANY
// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
// DISCLAIMED. IN NO EVENT SHALL SCOTT FERGUSON BE LIABLE FOR ANY
// DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

// Fetch extension settings
var parentExtensionId = 'nlcklobeoigfjmcigmhbjkepmniladed';
var settings = {};
var port = chrome.extension.connect(parentExtensionId);

port.onMessage.addListener(function(data) {
    settings = data;

    console.log(settings);

    populateMenu();
});

port.postMessage({
    'message': 'GetForumsJumpList'
});

/**
 * Opens a link in a tab.
 */
function openTab(tabUrl) {
    var button = event.button;
    if(button > 1)
        return;
    var selected = true;
    if(button == 1 || event.ctrlKey) // Middle Button or Ctrl click
        selected = false;
    chrome.tabs.create({ url: tabUrl, selected: selected });
}

/**
 * Changes the sticky status of a forum in the popup menu
 */
function toggleSticky(forumID) {
    var forums = JSON.parse(settings.forumsList);
    for(i in forums) {
        if (forums[i].id != forumID)
            continue;

        if (forums[i].sticky == true) {
            forums[i].sticky = false;
            jQuery('img#sticky-'+forumID).each( function() {
                jQuery(this).attr('src', '../images/sticky_off.gif');
            });
        } else {
            forums[i].sticky = true;
            jQuery('img#sticky-'+forumID).each( function() {
                jQuery(this).attr('src', '../images/sticky_on.gif');
            });
        }
        settings.forumsList = JSON.stringify(forums);
        port.postMessage({ 'message': 'ChangeSALRSetting',
                           'option' : 'forumsList',
                           'value'  : JSON.stringify(forums) });
        return;
    }
}

/**
 * Populate the menu with forums.
 */
function populateMenu() {
    var forums = JSON.parse(settings.forumsList);
    var newHTML = '';
    var color = '#ffffff';

    jQuery(forums).each( function() {
        var indent = this.level;
        var title = this.name;

        if (indent == -1) { // Separator
            // Loop through forum list and add stickied forums
            for(i in forums) {
                if (forums[i].sticky == true)  {
                    newHTML += populateMenuHelper(forums[i], color, true);
                    if (color == '#ffffff') {
                        color = '#eeeeee';
                    } else {
                        color = '#ffffff';
                    }
                }
            }

            newHTML += '
';
        } else if (indent == 0) {
            newHTML += '
';
            newHTML += '' + title + '
';
            newHTML += '
';

        } else {
            newHTML += populateMenuHelper(this, color, false);
        }

        if (color == '#ffffff') {
            color = '#eeeeee';
        } else {
            color = '#ffffff';
        }
    });

    jQuery('div#forums-list').html(newHTML);
}

function populateMenuHelper(forum, color, stuck) {
    var subHTML = '';

    var indent = 2*forum.level;
    if (stuck == true)
        indent=2;
    var title = forum.name;

    // Add sticky controls to popup window
    if (forum.sticky == true)
        subHTML += '

';
    else
        subHTML += '

';

    // Dynamically set the 10's digit for padding here, since we can have any number
    // of indentations
    subHTML += '
';
    subHTML += '' + title + '
';
    subHTML += '
';
    return subHTML;
}
<|endoftext|>


<commit_before>d3.chart('BaseChart').extend('ScatterChart', {
  initialize: function() {
    var chart = this;

    chart.base
      .classed('Areachart', true);

    // make actual layers
    chart.layer('circles', chart.areas.plot, {
      // data format:
      // [ { netPresentValue: 55.3, probability: 20 }, ...]
      dataBind : function(data) {

        // save the data in case we need to reset it
        chart.data = data;

        // adjust the x domain - the number of bars.
        var xmax = d3.max(data, function(d) {
          return d.netPresentValue;
        });

        chart.x.domain([0, xmax + 3]);

        chart.y.domain([0, 1]);

        // draw yaxis
        var yAxis = d3.svg.axis()
          .scale(chart.y)
          .orient('left')
          .ticks(4)
          .tickFormat(chart.y.tickFormat(4, '%'));

        chart.areas.ylabels
          .call(yAxis);

        return this.selectAll('circle')
          .data(data);
      },

      insert : function() {
        return this.append("circle")
          .classed('circle', true)
          .attr({
            cx: function(d) { return chart.x(+d.netPresentValue); },
            cy: function(d) { return chart.y(+d.probability); },
            r: 8,
            id: function(d) { return d.country; }
          });
        }
    });

    // a layer for the x text labels.
    chart.layer('xlabels', chart.areas.xlabels, {
      dataBind : function(data) {
        // first append a line to the top.
        this.append('line')
          .attr('x1', 0)
          .attr('x2', chart.w - chart.margins.left)
          .attr('y1', 0)
          .attr('y2', 0)
          .style('stroke-width', '1')
          .style('shape-rendering', 'crispEdges');

        return this.selectAll('text')
          .data(data);
      },
      insert : function() {
        return this.append('text')
          .classed('label', true)
          .attr('text-anchor', 'middle')
          .attr('x', function(d, i) {
            return chart.x(i) - 0.5 + chart.bar_width/2;
          })
          .attr('dy', '1em')
          .text(function(d) {
            return d.name;
          });
      }
    });

    // on new/update data
    // render the circles.
    var onEnter = function() {
      this.attr('cx', function(d) {
            return chart.x(d.netPresentValue);
          })
          .attr('cy', function(d) {
            return chart.h - chart.margins.bottom - chart.margins.top - chart.y(1 - d.probability);
          })
          .attr('val', function(d) {
            return d.netPresentValue;
          })
    };

    chart.layer('circles').on('enter', onEnter);
    chart.layer('circles').on('update', onEnter);
  }
});

<commit_msg>Red line for interest rate<commit_after>d3.chart('BaseChart').extend('ScatterChart', {
  initialize: function() {
    var chart = this;

    chart.base
      .classed('Areachart', true);

    chart.areas.rate = chart.base.append('g')
      .classed('rate', true)
      .attr('width', chart.w - chart.margins.left)
      .attr('height', chart.h - chart.margins.bottom - chart.margins.top)
      .attr('transform', 'translate(' + chart.margins.left + ',' + chart.margins.top+')');

    // make actual layers
    chart.layer('circles', chart.areas.plot, {
      // data format:
      // [ { netPresentValue: 55.3, probability: 20 }, ...]
      dataBind : function(data) {

        // save the data in case we need to reset it
        chart.data = data;

        // adjust the x domain - the number of bars.
        var xmax = d3.max(data, function(d) {
          return d.netPresentValue;
        });

        chart.x.domain([0, xmax + 3]);

        chart.y.domain([0, 1]);

        // draw yaxis
        var yAxis = d3.svg.axis()
          .scale(chart.y)
          .orient('left')
          .ticks(4)
          .tickFormat(chart.y.tickFormat(4, '%'));

        chart.areas.ylabels
          .call(yAxis);

        return this.selectAll('circle')
          .data(data);
      },

      insert : function() {
        return this.append("circle")
          .classed('circle', true)
          .attr({
            cx: function(d) { return chart.x(+d.netPresentValue); },
            cy: function(d) { return chart.y(+d.probability); },
            r: 8,
            id: function(d) { return d.country; }
          });
        }
    });

    chart.layer('interestRate', chart.areas.rate, {
      dataBind : function(data) {
        var interestRate = data.reduce(function(acc, outcome) {
          return acc + outcome.netPresentValue * outcome.probability;
        }, 0);
        console.log(interestRate);

        return this.selectAll('line')
          .data([interestRate])
      },
      insert: function() {
        return this.append("line")
          .classed('interest', true)
          .attr({
            x1: function(d) { return chart.x(+d); },
            y1: chart.y(0),
            x2: function(d) { return chart.x(+d); },
            y2: chart.y(1),
            style: "stroke:rgb(255,0,0);stroke-width:2"
          })
      }
    });

    // a layer for the x text labels.
    chart.layer('xlabels', chart.areas.xlabels, {
      dataBind : function(data) {
        // first append a line to the top.
        this.append('line')
          .attr('x1', 0)
          .attr('x2', chart.w - chart.margins.left)
          .attr('y1', 0)
          .attr('y2', 0)
          .style('stroke-width', '1')
          .style('shape-rendering', 'crispEdges');

        return this.selectAll('text')
          .data(data);
      },
      insert : function() {
        return this.append('text')
          .classed('label', true)
          .attr('text-anchor', 'middle')
          .attr('x', function(d, i) {
            return chart.x(i) - 0.5 + chart.bar_width/2;
          })
          .attr('dy', '1em')
          .text(function(d) {
            return d.name;
          });
      }
    });

    // on new/update data
    // render the circles.
    var onEnter = function() {
      this.attr('cx', function(d) {
            return chart.x(d.netPresentValue);
          })
          .attr('cy', function(d) {
            return chart.h - chart.margins.bottom - chart.margins.top - chart.y(1 - d.probability);
          })
          .attr('val', function(d) {
            return d.netPresentValue;
          })
    };

    var onRateEnter = function() {
      var xpos = function(d) { return chart.x(+d); };
      this.attr({
        x1: xpos,
        x2: xpos,
      });
    }

    chart.layer('circles').on('enter', onEnter);
    chart.layer('circles').on('update', onEnter);
    chart.layer('interestRate').on('update', onRateEnter);
  }
});

<|endoftext|>


<commit_before>  var chartSize = Math.min(screen.width, screen.height)/1.5;

    var width = chartSize,
        height = chartSize;
    var radius = Math.min(width, height) / 2;

    // Breadcrumb dimensions: width, height, spacing, width of tip/tail.
    var b = {
      w: 75, h: 30, s: 3, t: 10
    };

    // Mapping of step names to colors.
    var color = d3.scale.category20c();

    // Total size of all segments; we set this later, after loading the data.
    var totalSize = 0;

    var vis = d3.select("#chart").append("svg:svg")
        .attr("width", width)
        .attr("height", height)
        .append("svg:g")
        .attr("id", "container")
        .attr("transform", "translate(" + width / 2 + "," + height / 2 + ")");

    var partition = d3.layout.partition()
        .size([2 * Math.PI, radius * radius])
        .value(function(d) { return d.size; });

    var arc = d3.svg.arc()
        .startAngle(function(d) { return d.x; })
        .endAngle(function(d) { return d.x + d.dx; })
        .innerRadius(function(d) { return Math.sqrt(d.y); })
        .outerRadius(function(d) { return Math.sqrt(d.y + d.dy); });

    // Use d3.text and d3.csv.parseRows so that we do not need to have a header
    // row, and can receive the csv as an array of arrays.
    d3.text("account.csv", function(text) {
      var csv = d3.csv.parseRows(text);
      var json = buildHierarchy(csv);
      createVisualization(json);
    });

    // Main function to draw and set up the visualization, once we have the data.
    function createVisualization(json) {

      // Basic setup of page elements.
      initializeBreadcrumbTrail();
      drawLegend();
      d3.select("#togglelegend").on("click", toggleLegend);

      // Bounding circle underneath the sunburst, to make it easier to detect
      // when the mouse leaves the parent g.
      vis.append("svg:circle")
          .attr("r", radius)
          .style("opacity", 0);

      // For efficiency, filter nodes to keep only those large enough to see.
      var nodes = partition.nodes(json)
          .filter(function(d) {
          return (d.dx > 0.005); // 0.005 radians = 0.29 degrees
          });

      var path = vis.data([json]).selectAll("path")
          .data(nodes)
          .enter().append("svg:path")
          .attr("display", function(d) { return d.depth ? null : "none"; })
          .attr("d", arc)
          .attr("fill-rule", "evenodd")
          .style("fill", function(d) { return color((d.children ? d : d.parent).name); })
          .style("opacity", 1)
          .on("mouseover", mouseover);

      // Add the mouseleave handler to the bounding circle.
      d3.select("#container").on("mouseleave", mouseleave);

      // Get total size of the tree = value of root node from partition.
      totalSize = path.node().__data__.value;
     };

    // Fade all but the current sequence, and show it in the breadcrumb trail.
    function mouseover(d) {

      var percentage = (100 * d.value / totalSize).toPrecision(3);
      var percentageString = percentage + "%";
      if (percentage < 0.1) {
        percentageString = "< 0.1%";
      }

      d3.select("#percentage")
          .text(percentageString);

      d3.select("#explanation")
          .style("visibility", "");

      var sequenceArray = getAncestors(d);
      updateBreadcrumbs(sequenceArray, percentageString);

      // Fade all the segments.
      d3.selectAll("path")
          .style("opacity", 0.3);

      // Then highlight only those that are an ancestor of the current segment.
      vis.selectAll("path")
          .filter(function(node) {
                    return (sequenceArray.indexOf(node) >= 0);
                  })
          .style("opacity", 1);
    }

    // Restore everything to full opacity when moving off the visualization.
    function mouseleave(d) {

      // Hide the breadcrumb trail
      d3.select("#trail")
          .style("visibility", "hidden");

      // Deactivate all segments during transition.
      d3.selectAll("path").on("mouseover", null);

      // Transition each segment to full opacity and then reactivate it.
      d3.selectAll("path")
          .transition()
          .duration(1000)
          .style("opacity", 1)
          .each("end", function() {
                  d3.select(this).on("mouseover", mouseover);
                });

      d3.select("#explanation")
          .style("visibility", "hidden");
    }

    // Given a node in a partition layout, return an array of all of its ancestor
    // nodes, highest first, but excluding the root.
    function getAncestors(node) {
      var path = [];
      var current = node;
      while (current.parent) {
        path.unshift(current);
        current = current.parent;
      }
      return path;
    }

    function initializeBreadcrumbTrail() {
      // Add the svg area.
      var trail = d3.select("#sequence").append("svg:svg")
          .attr("width", width)
          .attr("height", 50)
          .attr("id", "trail");
      // Add the label at the end, for the percentage.
      trail.append("svg:text")
        .attr("id", "endlabel")
        .style("fill", "#000");
    }

    // Generate a string that describes the points of a breadcrumb polygon.
    function breadcrumbPoints(d, i) {
      var points = [];
      points.push("0,0");
      points.push(b.w + ",0");
      points.push(b.w + b.t + "," + (b.h / 2));
      points.push(b.w + "," + b.h);
      points.push("0," + b.h);
      if (i > 0) { // Leftmost breadcrumb; don't include 6th vertex.
        points.push(b.t + "," + (b.h / 2));
      }
      return points.join(" ");
    }

    // Update the breadcrumb trail to show the current sequence and percentage.
    function updateBreadcrumbs(nodeArray, percentageString) {

      // Data join; key function combines name and depth (= position in sequence).
      var g = d3.select("#trail")
          .selectAll("g")
          .data(nodeArray, function(d) { return d.name + d.depth; });

      // Add breadcrumb and label for entering nodes.
      var entering = g.enter().append("svg:g");

      entering.append("svg:polygon")
          .attr("points", breadcrumbPoints)
          .style("fill", function(d) { return colors[d.name]; });

      entering.append("svg:text")
          .attr("x", (b.w + b.t) / 2)
          .attr("y", b.h / 2)
          .attr("dy", "0.35em")
          .attr("text-anchor", "middle")
          .text(function(d) { return d.name; });

      // Set position for entering and updating nodes.
      g.attr("transform", function(d, i) {
        return "translate(" + i * (b.w + b.s) + ", 0)";
      });

      // Remove exiting nodes.
      g.exit().remove();

      // Now move and update the percentage at the end.
      d3.select("#trail").select("#endlabel")
          .attr("x", (nodeArray.length + 0.5) * (b.w + b.s))
          .attr("y", b.h / 2)
          .attr("dy", "0.35em")
          .attr("text-anchor", "middle")
          .text(percentageString);

      // Make the breadcrumb trail visible, if it's hidden.
      d3.select("#trail")
          .style("visibility", "");

    }

    function drawLegend() {

      // Dimensions of legend item: width, height, spacing, radius of rounded rect.
      var li = {
        w: 75, h: 30, s: 3, r: 3
      };

      var legend = d3.select("#legend").append("svg:svg")
          .attr("width", li.w)
          .attr("height", d3.keys(colors).length * (li.h + li.s));

      var g = legend.selectAll("g")
          .data(d3.entries(colors))
          .enter().append("svg:g")
          .attr("transform", function(d, i) {
                  return "translate(0," + i * (li.h + li.s) + ")";
               });

      g.append("svg:rect")
          .attr("rx", li.r)
          .attr("ry", li.r)
          .attr("width", li.w)
          .attr("height", li.h)
          .style("fill", function(d) { return d.value; });

      g.append("svg:text")
          .attr("x", li.w / 2)
          .attr("y", li.h / 2)
          .attr("dy", "0.35em")
          .attr("text-anchor", "middle")
          .text(function(d) { return d.key; });
    }

    function toggleLegend() {
      var legend = d3.select("#legend");
      if (legend.style("visibility") == "hidden") {
        legend.style("visibility", "");
      } else {
        legend.style("visibility", "hidden");
      }
    }

    // Take a 2-column CSV and transform it into a hierarchical structure suitable
    // for a partition layout. The first column is a sequence of step names, from
    // root to leaf, separated by hyphens. The second column is a count of how
    // often that sequence occurred.
    function buildHierarchy(csv) {
      var root = {"name": "root", "children": []};
      for (var i = 0; i < csv.length; i++) {
        var sequence = csv[i][0];
        var size = +csv[i][1];
        if (isNaN(size)) { // e.g. if this is a header row
          continue;
        }
        var parts = sequence.split(">");
        var currentNode = root;
        for (var j = 0; j < parts.length; j++) {
          var children = currentNode["children"];
          var nodeName = parts[j];
          var childNode;
          if (j + 1 < parts.length) {
       // Not yet at the end of the sequence; move down the tree.
     	var foundChild = false;
     	for (var k = 0; k < children.length; k++) {
     	  if (children[k]["name"] == nodeName) {
     	    childNode = children[k];
     	    foundChild = true;
     	    break;
     	  }
     	}
      // If we don't already have a child node for this branch, create it.
     	if (!foundChild) {
     	  childNode = {"name": nodeName, "children": []};
     	  children.push(childNode);
     	}
     	currentNode = childNode;
          } else {
     	// Reached the end of the sequence; create a leaf node.
     	childNode = {"name": nodeName, "size": size};
     	children.push(childNode);
          }
        }
      }
      return root;
    };
<commit_msg>minor updates<commit_after>  var chartSize = Math.min(screen.width, screen.height)/1.5;

    var width = chartSize,
        height = chartSize;
    var radius = Math.min(width, height) / 2;

    // Breadcrumb dimensions: width, height, spacing, width of tip/tail.
    var b = {
      w: 75, h: 30, s: 3, t: 10
    };

    // Mapping of step names to colors.
    var color = d3.scale.category20c();

    // Total size of all segments; we set this later, after loading the data.
    var totalSize = 0;

    var vis = d3.select("#chart").append("svg:svg")
        .attr("width", width)
        .attr("height", height)
        .append("svg:g")
        .attr("id", "container")
        .attr("transform", "translate(" + width / 2 + "," + height / 2 + ")");

    var partition = d3.layout.partition()
        .size([2 * Math.PI, radius * radius])
        .value(function(d) { return d.size; });

    var arc = d3.svg.arc()
        .startAngle(function(d) { return d.x; })
        .endAngle(function(d) { return d.x + d.dx; })
        .innerRadius(function(d) { return Math.sqrt(d.y); })
        .outerRadius(function(d) { return Math.sqrt(d.y + d.dy); });

    // Use d3.text and d3.csv.parseRows so that we do not need to have a header
    // row, and can receive the csv as an array of arrays.
    d3.text("account.csv", function(text) {
      var csv = d3.csv.parseRows(text);
      var json = buildHierarchy(csv);
      Console.lof(json);
      createVisualization(json);
    });

    // Main function to draw and set up the visualization, once we have the data.
    function createVisualization(json) {

      // Basic setup of page elements.
      initializeBreadcrumbTrail();
      drawLegend();
      d3.select("#togglelegend").on("click", toggleLegend);

      // Bounding circle underneath the sunburst, to make it easier to detect
      // when the mouse leaves the parent g.
      vis.append("svg:circle")
          .attr("r", radius)
          .style("opacity", 0);

      // For efficiency, filter nodes to keep only those large enough to see.
      var nodes = partition.nodes(json)
          .filter(function(d) {
          return (d.dx > 0.005); // 0.005 radians = 0.29 degrees
          });

      var path = vis.data([json]).selectAll("path")
          .data(nodes)
          .enter().append("svg:path")
          .attr("display", function(d) { return d.depth ? null : "none"; })
          .attr("d", arc)
          .attr("fill-rule", "evenodd")
          .style("fill", function(d) { return color((d.children ? d : d.parent).name); })
          .style("opacity", 1)
          .on("mouseover", mouseover);

      // Add the mouseleave handler to the bounding circle.
      d3.select("#container").on("mouseleave", mouseleave);

      // Get total size of the tree = value of root node from partition.
      totalSize = path.node().__data__.value;
     };

    // Fade all but the current sequence, and show it in the breadcrumb trail.
    function mouseover(d) {

      var percentage = (100 * d.value / totalSize).toPrecision(3);
      var percentageString = percentage + "%";
      if (percentage < 0.1) {
        percentageString = "< 0.1%";
      }

      d3.select("#percentage")
          .text(percentageString);

      d3.select("#explanation")
          .style("visibility", "");

      var sequenceArray = getAncestors(d);
      updateBreadcrumbs(sequenceArray, percentageString);

      // Fade all the segments.
      d3.selectAll("path")
          .style("opacity", 0.3);

      // Then highlight only those that are an ancestor of the current segment.
      vis.selectAll("path")
          .filter(function(node) {
                    return (sequenceArray.indexOf(node) >= 0);
                  })
          .style("opacity", 1);
    }

    // Restore everything to full opacity when moving off the visualization.
    function mouseleave(d) {

      // Hide the breadcrumb trail
      d3.select("#trail")
          .style("visibility", "hidden");

      // Deactivate all segments during transition.
      d3.selectAll("path").on("mouseover", null);

      // Transition each segment to full opacity and then reactivate it.
      d3.selectAll("path")
          .transition()
          .duration(1000)
          .style("opacity", 1)
          .each("end", function() {
                  d3.select(this).on("mouseover", mouseover);
                });

      d3.select("#explanation")
          .style("visibility", "hidden");
    }

    // Given a node in a partition layout, return an array of all of its ancestor
    // nodes, highest first, but excluding the root.
    function getAncestors(node) {
      var path = [];
      var current = node;
      while (current.parent) {
        path.unshift(current);
        current = current.parent;
      }
      return path;
    }

    function initializeBreadcrumbTrail() {
      // Add the svg area.
      var trail = d3.select("#sequence").append("svg:svg")
          .attr("width", width)
          .attr("height", 50)
          .attr("id", "trail");
      // Add the label at the end, for the percentage.
      trail.append("svg:text")
        .attr("id", "endlabel")
        .style("fill", "#000");
    }

    // Generate a string that describes the points of a breadcrumb polygon.
    function breadcrumbPoints(d, i) {
      var points = [];
      points.push("0,0");
      points.push(b.w + ",0");
      points.push(b.w + b.t + "," + (b.h / 2));
      points.push(b.w + "," + b.h);
      points.push("0," + b.h);
      if (i > 0) { // Leftmost breadcrumb; don't include 6th vertex.
        points.push(b.t + "," + (b.h / 2));
      }
      return points.join(" ");
    }

    // Update the breadcrumb trail to show the current sequence and percentage.
    function updateBreadcrumbs(nodeArray, percentageString) {

      // Data join; key function combines name and depth (= position in sequence).
      var g = d3.select("#trail")
          .selectAll("g")
          .data(nodeArray, function(d) { return d.name + d.depth; });

      // Add breadcrumb and label for entering nodes.
      var entering = g.enter().append("svg:g");

      entering.append("svg:polygon")
          .attr("points", breadcrumbPoints)
          .style("fill", function(d) { return colors[d.name]; });

      entering.append("svg:text")
          .attr("x", (b.w + b.t) / 2)
          .attr("y", b.h / 2)
          .attr("dy", "0.35em")
          .attr("text-anchor", "middle")
          .text(function(d) { return d.name; });

      // Set position for entering and updating nodes.
      g.attr("transform", function(d, i) {
        return "translate(" + i * (b.w + b.s) + ", 0)";
      });

      // Remove exiting nodes.
      g.exit().remove();

      // Now move and update the percentage at the end.
      d3.select("#trail").select("#endlabel")
          .attr("x", (nodeArray.length + 0.5) * (b.w + b.s))
          .attr("y", b.h / 2)
          .attr("dy", "0.35em")
          .attr("text-anchor", "middle")
          .text(percentageString);

      // Make the breadcrumb trail visible, if it's hidden.
      d3.select("#trail")
          .style("visibility", "");

    }

    function drawLegend() {

      // Dimensions of legend item: width, height, spacing, radius of rounded rect.
      var li = {
        w: 75, h: 30, s: 3, r: 3
      };

      var legend = d3.select("#legend").append("svg:svg")
          .attr("width", li.w)
          .attr("height", d3.keys(colors).length * (li.h + li.s));

      var g = legend.selectAll("g")
          .data(d3.entries(colors))
          .enter().append("svg:g")
          .attr("transform", function(d, i) {
                  return "translate(0," + i * (li.h + li.s) + ")";
               });

      g.append("svg:rect")
          .attr("rx", li.r)
          .attr("ry", li.r)
          .attr("width", li.w)
          .attr("height", li.h)
          .style("fill", function(d) { return d.value; });

      g.append("svg:text")
          .attr("x", li.w / 2)
          .attr("y", li.h / 2)
          .attr("dy", "0.35em")
          .attr("text-anchor", "middle")
          .text(function(d) { return d.key; });
    }

    function toggleLegend() {
      var legend = d3.select("#legend");
      if (legend.style("visibility") == "hidden") {
        legend.style("visibility", "");
      } else {
        legend.style("visibility", "hidden");
      }
    }

    // Take a 2-column CSV and transform it into a hierarchical structure suitable
    // for a partition layout. The first column is a sequence of step names, from
    // root to leaf, separated by hyphens. The second column is a count of how
    // often that sequence occurred.
    function buildHierarchy(csv) {
      var root = {"name": "root", "children": []};
      for (var i = 0; i < csv.length; i++) {
        var sequence = csv[i][0];
        var size = +csv[i][1];
        if (isNaN(size)) { // e.g. if this is a header row
          continue;
        }
        var parts = sequence.split(">");
        var currentNode = root;
        for (var j = 0; j < parts.length; j++) {
          var children = currentNode["children"];
          var nodeName = parts[j];
          var childNode;
          if (j + 1 < parts.length) {
       // Not yet at the end of the sequence; move down the tree.
     	var foundChild = false;
     	for (var k = 0; k < children.length; k++) {
     	  if (children[k]["name"] == nodeName) {
     	    childNode = children[k];
     	    foundChild = true;
     	    break;
     	  }
     	}
      // If we don't already have a child node for this branch, create it.
     	if (!foundChild) {
     	  childNode = {"name": nodeName, "children": []};
     	  children.push(childNode);
     	}
     	currentNode = childNode;
          } else {
     	// Reached the end of the sequence; create a leaf node.
     	childNode = {"name": nodeName, "size": size};
     	children.push(childNode);
          }
        }
      }
      return root;
    };
<|endoftext|>