<commit_before>/*jshint node:true*/
"use strict";

module.exports = function(jsmm) {
	jsmm.editor = {};

	jsmm.editor.autocompletion = {
		// expected text format: someObject.someProperty.startOfAFunction
		getExamples: function(scope, text) {
			var split = text.split('.');

			var obj = scope.find(split[0]);

			if (obj === undefined || typeof obj.value !== 'object' || obj.value.properties === undefined) return null;
			obj = obj.value;
			for (var i=1; i<split.length-1; i++) {
				obj = obj.properties[split[i]];
				if (typeof obj !== 'object' || obj.properties === undefined) return null;
			}

			var examples = [];
			var start = split[split.length-1].toLowerCase();
			for (var name in obj.properties) {
				var example;
				if (typeof obj.properties[name] === 'object' && obj.properties[name].example !== undefined) {
					example = obj.properties[name].example;
				} else {
					example = name;
				}

				if (start.length === 0 || example.substring(0, start.length).toLowerCase() === start) {
					// split into name part and "= 123" or "(100, 150)" part
					var splitExample = example.split(/( \=.*|\(.*)/);
					splitExample[1] = splitExample[1] || '';
					examples.push(splitExample);
				}
			}
			return {
				examples: examples,
				width: start.length,
				prefix: text.substring(0, text.length - start.length)
			};
		}
	};

	jsmm.editor.editables = {
		generate: function(tree, editorEditables, surface, editor) {
			var editables = [];
			var i;
			var booleanNodes = tree.getNodesByType('BooleanLiteral');
			if (booleanNodes !== undefined) {
				for (i=0; i<booleanNodes.length; i++) {
					editables.push(new editorEditables.CycleEditable(booleanNodes[i], surface, editor, this.parseBoolean, this.makeBoolean));
				}
			}
			var numberNodes = tree.getNodesByType('NumberLiteral');
			if (numberNodes !== undefined) {
				for (i=0; i<numberNodes.length; i++) {
					var node = numberNodes[i];
					if (node.parent.type === 'UnaryExpression') {
						node = node.parent;
					}
					editables.push(new editorEditables.NumberEditable(node, surface, editor, this.parseNumber, this.makeNumber));
				}
			}
			var stringNodes = tree.getNodesByType('StringLiteral');
			if (stringNodes !== undefined) {
				for (i=0; i<stringNodes.length; i++) {
					var str = stringNodes[i].str;
					if (jsmm.editor.editables.splitColor('"' + str + '"') !== null) {
						editables.push(new editorEditables.ColorEditable(stringNodes[i], surface, editor, this.parseColor, this.makeColor));
					}
				}
			}
			return editables;
		},

		parseBoolean: function(text) {
			this.value = text === 'true';
			return (text === 'true' || text === 'false');
		},

		makeBoolean: function() {
			return this.value ? 'false' : 'true';
		},

		splitNumber: function(text) {
			var match = /^[+]?([\-]?)[ ]*([0-9]+)(?:[.]([0-9]+))?(?:([eE])[+]?([\-]?[0-9]+))?$/g.exec(text);
			if (match === null) {
				return null;
			} else {
				return {
					sign: match[1], // either "-" or undefined ("+" is dropped)
					integer: match[2], // integer part, cannot be undefined (if the number is valid)
					decimals: match[3], // decimal part without ".", or undefined
					exponentLetter: match[4], // either "e", "E", or undefined
					exponent: match[5] // the exponent part without the letter, but with an optional "-" (again not "+"), or undefined
				};
			}
		},

		parseNumber: function(text) {
			this.numberData = {};
			// remove spaces since it is possible to have e.g. "-  5"
			this.numberData.value = parseFloat(text.replace(/[ ]+/g, ''));
			var split = jsmm.editor.editables.splitNumber(text);

			if (split === null || !isFinite(this.numberData.value)) {
				return false;
			} else {
				// if an exponent is defined, use the capitalisation already used in the value
				this.numberData.exponentLetter = split.exponentLetter || 'e';

				// calculate the delta for each offset pixel based on the number of decimals in the original number (and of course exponent)
				// the delta is inverted as this seems to reduce the number of rounding errors (e.g. 0.57 !== 57*0.01, but 0.57 === 57/100)
				this.numberData.invDelta = Math.pow(10, -(parseInt(split.exponent || '0', 10) - (split.decimals || '').length));

				// determine the number of significant digits by trimming leading zeros
				var significant = (split.integer + (split.decimals || '')).replace(/^0*/, '').length;

				// when zero, the number of significant digits is the number of decimals plus one
				if (this.numberData.value === 0) {
					significant = split.decimals.length+1;
				}

				// clamp the number
				if (significant > 8) significant = 8;
				else if (significant < 1) significant = 1;

				// the final number of decimals has to be based on the .toPrecision value with the calculated number of significant digits,
				// as this will be used when generating the number, and this function may alter the format of the number (e.g. different
				// number of digits and exponent, etc.)
				this.numberData.decimals = (jsmm.editor.editables.splitNumber(this.numberData.value.toPrecision(significant)).decimals || '').length;

				return true;
			}
		},

		makeNumber: function(offset) {
			// calculate new number with 8 significant digits and split it
			// for calculating the new number the function x^3/(x^2+200), which provides nice snapping to the original number and
			// lower sensitiveness near the original number
			var split = jsmm.editor.editables.splitNumber((this.numberData.value + (offset*offset*offset)/((offset*offset+200)*this.numberData.invDelta)).toPrecision(8));

			// start off with the integer part
			var newText = split.integer;

			// if we want any decimals, take all the decimals we get with 8 significant digits, and cap this off by the required amount
			if (this.numberData.decimals > 0) {
				newText += '.' + (split.decimals || '0').substring(0, this.numberData.decimals);
			}

			// add the exponent using the user-defined letter, if necessary
			if (split.exponent !== undefined) {
				newText += this.numberData.exponentLetter + split.exponent;
			}

			// finally add the negative sign if required, and if the rest of the number we have so far does not evaluate to zero
			if (split.sign === '-' && parseFloat(newText) !== 0) {
				newText = '-' + newText;
			}

			return newText;
		},

		splitColor: function(text) {
			var match = /^["]([#][0-9a-fA-F]{3}(?:[0-9a-fA-F]{3})?)|(?:(rgb|rgba|hsl|hsla)[(][ ]*(\d+(?:[.]\d+)?)([%]?)[ ]*,[ ]*(\d+(?:[.]\d+)?)([%]?)[ ]*,[ ]*(\d+(?:[.]\d+)?)([%]?)[ ]*(?:,[ ]*(\d+(?:[.]\d+)?)[ ]*)?[)])["]$/g.exec(text);
			if (match === null) {
				return null;
			} else {
				return {
					hex: match[1], // either "#xxx" or "#xxxxxx"
					format: match[2], // either "rgb", "rgba", "hsl", "hsla", or undefined
					part1: match[3], // number
					percent1: match[4], // either "" or "%"
					part2: match[5], // number
					percent2: match[6], // either "" or "%"
					part3: match[7], // number
					percent3: match[8], // either "" or "%"
					alpha: match[9] // alpha part or undefined
				};
			}
		},

		parseColor: function(text) {
			this.colorData = {};
			var split = jsmm.editor.editables.splitColor(text);
			if (split === null) {
				return false;
			} else {
				if (split.hex !== undefined) {
					this.colorData.value = split.hex;
					this.colorData.format = 'hex';
					return true;
				} else {
					var a;
					if (split.format === 'rgb' || split.format === 'rgba') {
						var r = parseFloat(split.part1);
						var g = parseFloat(split.part2);
						var b = parseFloat(split.part3);
						a = parseFloat(split.alpha || '1');
						if (split.percent1 === '%') {
							r = r*255/100;
						}
						if (split.percent2 === '%') {
							g = g*255/100;
						}
						if (split.percent3 === '%') {
							b = b*255/100;
						}
						if (r < 0 || r > 255 || g < 0 || g > 255 || b < 0 || b > 255 || a < 0 || a > 1) return false;
						this.colorData.value = 'rgba(' + r.toFixed(0) + ', ' + g.toFixed(0) + ', ' + b.toFixed(0) + ', ' + a.toFixed(2) + ')';
						this.colorData.format = 'rgba';
						return true;
					} else if (split.format === 'hsl' || split.format === 'hsla') {
						var h = parseInt(split.part1, 10);
						var s = parseInt(split.part2, 10);
						var l = parseInt(split.part3, 10);
						a = parseFloat(split.alpha || '1');
						if (h < 0 || h > 360 || split.percent1 === '%' || s < 0 || s > 100 || split.percent2 !== '%' ||
							l < 0 || l > 100 || split.percent3 !== '%' || a < 0 || a > 1) return false;
						this.colorData.value = 'hsla(' + h.toFixed(0) + ', ' + s.toFixed(2) + '%, ' + l.toFixed(2) + '%, ' + a.toFixed(2) + ')';
						this.colorData.format = 'hsla';
						return true;
					} else {
						return false;
					}
				}
			}
		},

		makeColor: function(color) {
			return '"' + color + '"';
		}
	};

	jsmm.editor.timeHighlights = {
		getTimeHighlights: function(tree) {
			var nodes = tree.getNodesByType('FunctionDeclaration');
			var result = {};
			for (var i=0; i<nodes.length; i++) {
				result[nodes[i].name] = nodes[i].blockLoc;
			}
			return result;
		}
	};
};<commit_msg>Fixed small bug<commit_after>/*jshint node:true*/
"use strict";

module.exports = function(jsmm) {
	jsmm.editor = {};

	jsmm.editor.autocompletion = {
		// expected text format: someObject.someProperty.startOfAFunction
		getExamples: function(scope, text) {
			var split = text.split('.');

			var obj = scope.find(split[0]);

			if (obj === undefined || typeof obj.value !== 'object' || obj.value.properties === undefined) return null;
			obj = obj.value;
			for (var i=1; i<split.length-1; i++) {
				obj = obj.properties[split[i]];
				if (typeof obj !== 'object' || obj.properties === undefined) return null;
			}

			var examples = [];
			var start = split[split.length-1].toLowerCase();
			for (var name in obj.properties) {
				var example;
				if (typeof obj.properties[name] === 'object' && obj.properties[name].example !== undefined) {
					example = obj.properties[name].example;
				} else {
					example = name;
				}

				if (start.length === 0 || example.substring(0, start.length).toLowerCase() === start) {
					// split into name part and "= 123" or "(100, 150)" part
					var splitExample = example.split(/( \=.*|\(.*)/);
					splitExample[1] = splitExample[1] || '';
					examples.push(splitExample);
				}
			}
			return {
				examples: examples,
				width: start.length,
				prefix: text.substring(0, text.length - start.length)
			};
		}
	};

	jsmm.editor.editables = {
		generate: function(tree, editorEditables, surface, editor) {
			var editables = [];
			var i;
			var booleanNodes = tree.getNodesByType('BooleanLiteral');
			if (booleanNodes !== undefined) {
				for (i=0; i<booleanNodes.length; i++) {
					editables.push(new editorEditables.CycleEditable(booleanNodes[i], surface, editor, this.parseBoolean, this.makeBoolean));
				}
			}
			var numberNodes = tree.getNodesByType('NumberLiteral');
			if (numberNodes !== undefined) {
				for (i=0; i<numberNodes.length; i++) {
					var node = numberNodes[i];
					if (node.parent.type === 'UnaryExpression') {
						node = node.parent;
					}
					editables.push(new editorEditables.NumberEditable(node, surface, editor, this.parseNumber, this.makeNumber));
				}
			}
			var stringNodes = tree.getNodesByType('StringLiteral');
			if (stringNodes !== undefined) {
				for (i=0; i<stringNodes.length; i++) {
					var str = stringNodes[i].str;
					if (jsmm.editor.editables.splitColor('"' + str + '"') !== null) {
						editables.push(new editorEditables.ColorEditable(stringNodes[i], surface, editor, this.parseColor, this.makeColor));
					}
				}
			}
			return editables;
		},

		parseBoolean: function(text) {
			this.value = text === 'true';
			return (text === 'true' || text === 'false');
		},

		makeBoolean: function() {
			return this.value ? 'false' : 'true';
		},

		splitNumber: function(text) {
			var match = /^[+]?([\-]?)[ ]*([0-9]+)(?:[.]([0-9]+))?(?:([eE])[+]?([\-]?[0-9]+))?$/g.exec(text);
			if (match === null) {
				return null;
			} else {
				return {
					sign: match[1], // either "-" or undefined ("+" is dropped)
					integer: match[2], // integer part, cannot be undefined (if the number is valid)
					decimals: match[3], // decimal part without ".", or undefined
					exponentLetter: match[4], // either "e", "E", or undefined
					exponent: match[5] // the exponent part without the letter, but with an optional "-" (again not "+"), or undefined
				};
			}
		},

		parseNumber: function(text) {
			this.numberData = {};
			// remove spaces since it is possible to have e.g. "-  5"
			this.numberData.value = parseFloat(text.replace(/[ ]+/g, ''));
			var split = jsmm.editor.editables.splitNumber(text);

			if (split === null || !isFinite(this.numberData.value)) {
				return false;
			} else {
				// if an exponent is defined, use the capitalisation already used in the value
				this.numberData.exponentLetter = split.exponentLetter || 'e';

				// calculate the delta for each offset pixel based on the number of decimals in the original number (and of course exponent)
				// the delta is inverted as this seems to reduce the number of rounding errors (e.g. 0.57 !== 57*0.01, but 0.57 === 57/100)
				this.numberData.invDelta = Math.pow(10, -(parseInt(split.exponent || '0', 10) - (split.decimals || '').length));

				// determine the number of significant digits by trimming leading zeros
				var significant = (split.integer + (split.decimals || '')).replace(/^0*/, '').length;

				// when zero, the number of significant digits is the number of decimals plus one
				if (this.numberData.value === 0 && split.decimals !== undefined) {
					significant = split.decimals.length+1;
				}

				// clamp the number
				if (significant > 8) significant = 8;
				else if (significant < 1) significant = 1;

				// the final number of decimals has to be based on the .toPrecision value with the calculated number of significant digits,
				// as this will be used when generating the number, and this function may alter the format of the number (e.g. different
				// number of digits and exponent, etc.)
				this.numberData.decimals = (jsmm.editor.editables.splitNumber(this.numberData.value.toPrecision(significant)).decimals || '').length;

				return true;
			}
		},

		makeNumber: function(offset) {
			// calculate new number with 8 significant digits and split it
			// for calculating the new number the function x^3/(x^2+200), which provides nice snapping to the original number and
			// lower sensitiveness near the original number
			var split = jsmm.editor.editables.splitNumber((this.numberData.value + (offset*offset*offset)/((offset*offset+200)*this.numberData.invDelta)).toPrecision(8));

			// start off with the integer part
			var newText = split.integer;

			// if we want any decimals, take all the decimals we get with 8 significant digits, and cap this off by the required amount
			if (this.numberData.decimals > 0) {
				newText += '.' + (split.decimals || '0').substring(0, this.numberData.decimals);
			}

			// add the exponent using the user-defined letter, if necessary
			if (split.exponent !== undefined) {
				newText += this.numberData.exponentLetter + split.exponent;
			}

			// finally add the negative sign if required, and if the rest of the number we have so far does not evaluate to zero
			if (split.sign === '-' && parseFloat(newText) !== 0) {
				newText = '-' + newText;
			}

			return newText;
		},

		splitColor: function(text) {
			var match = /^["]([#][0-9a-fA-F]{3}(?:[0-9a-fA-F]{3})?)|(?:(rgb|rgba|hsl|hsla)[(][ ]*(\d+(?:[.]\d+)?)([%]?)[ ]*,[ ]*(\d+(?:[.]\d+)?)([%]?)[ ]*,[ ]*(\d+(?:[.]\d+)?)([%]?)[ ]*(?:,[ ]*(\d+(?:[.]\d+)?)[ ]*)?[)])["]$/g.exec(text);
			if (match === null) {
				return null;
			} else {
				return {
					hex: match[1], // either "#xxx" or "#xxxxxx"
					format: match[2], // either "rgb", "rgba", "hsl", "hsla", or undefined
					part1: match[3], // number
					percent1: match[4], // either "" or "%"
					part2: match[5], // number
					percent2: match[6], // either "" or "%"
					part3: match[7], // number
					percent3: match[8], // either "" or "%"
					alpha: match[9] // alpha part or undefined
				};
			}
		},

		parseColor: function(text) {
			this.colorData = {};
			var split = jsmm.editor.editables.splitColor(text);
			if (split === null) {
				return false;
			} else {
				if (split.hex !== undefined) {
					this.colorData.value = split.hex;
					this.colorData.format = 'hex';
					return true;
				} else {
					var a;
					if (split.format === 'rgb' || split.format === 'rgba') {
						var r = parseFloat(split.part1);
						var g = parseFloat(split.part2);
						var b = parseFloat(split.part3);
						a = parseFloat(split.alpha || '1');
						if (split.percent1 === '%') {
							r = r*255/100;
						}
						if (split.percent2 === '%') {
							g = g*255/100;
						}
						if (split.percent3 === '%') {
							b = b*255/100;
						}
						if (r < 0 || r > 255 || g < 0 || g > 255 || b < 0 || b > 255 || a < 0 || a > 1) return false;
						this.colorData.value = 'rgba(' + r.toFixed(0) + ', ' + g.toFixed(0) + ', ' + b.toFixed(0) + ', ' + a.toFixed(2) + ')';
						this.colorData.format = 'rgba';
						return true;
					} else if (split.format === 'hsl' || split.format === 'hsla') {
						var h = parseInt(split.part1, 10);
						var s = parseInt(split.part2, 10);
						var l = parseInt(split.part3, 10);
						a = parseFloat(split.alpha || '1');
						if (h < 0 || h > 360 || split.percent1 === '%' || s < 0 || s > 100 || split.percent2 !== '%' ||
							l < 0 || l > 100 || split.percent3 !== '%' || a < 0 || a > 1) return false;
						this.colorData.value = 'hsla(' + h.toFixed(0) + ', ' + s.toFixed(2) + '%, ' + l.toFixed(2) + '%, ' + a.toFixed(2) + ')';
						this.colorData.format = 'hsla';
						return true;
					} else {
						return false;
					}
				}
			}
		},

		makeColor: function(color) {
			return '"' + color + '"';
		}
	};

	jsmm.editor.timeHighlights = {
		getTimeHighlights: function(tree) {
			var nodes = tree.getNodesByType('FunctionDeclaration');
			var result = {};
			for (var i=0; i<nodes.length; i++) {
				result[nodes[i].name] = nodes[i].blockLoc;
			}
			return result;
		}
	};
};<|endoftext|>


<commit_before>/*
 * jquery.flot.tooltip
 *
 * desc:	create tooltip with values of hovered point on the graph,
					support many series, time mode, stacking and pie charts
					you can set custom tip content (also with use of HTML tags) and precision of values
 * version:	0.4.4
 * author: 	Krzysztof Urbas @krzysu [myviews.pl] with help of @ismyrnow
 * website:	https://github.com/krzysu/flot.tooltip
 *
 * released under MIT License, 2012
*/

(function ($) {
	var options = {
		tooltip: false, //boolean
		tooltipOpts: {
			content: "%s | X: %x | Y: %y.2", //%s -> series label, %x -> X value, %y -> Y value, %x.2 -> precision of X value, %p -> percent
			dateFormat: "%y-%0m-%0d",
			shifts: {
				x: 10,
				y: 20
			},
			defaultTheme: true
		}
	};

	var init = function(plot) {

		var tipPosition = {x: 0, y: 0};
		var opts = plot.getOptions();

		var updateTooltipPosition = function(pos) {
			tipPosition.x = pos.x;
			tipPosition.y = pos.y;
		};

		var onMouseMove = function(e) {

			var pos = {x: 0, y: 0};

			pos.x = e.pageX;
			pos.y = e.pageY;

			updateTooltipPosition(pos);
		};

		var timestampToDate = function(tmst) {

			var theDate = new Date(tmst);

			return $.plot.formatDate(theDate, opts.tooltipOpts.dateFormat);
		};

		plot.hooks.bindEvents.push(function (plot, eventHolder) {

			var to = opts.tooltipOpts;
			var placeholder = plot.getPlaceholder();
			var $tip;

			if (opts.tooltip === false) return;

			if( $('#flotTip').length > 0 ){
				$tip = $('#flotTip');
			}
			else {
				$tip = $('<div />').attr('id', 'flotTip');
				$tip.appendTo('body').hide().css({position: 'absolute'});

				if(to.defaultTheme) {
					$tip.css({
						'background': '#fff',
						'z-index': '100',
						'padding': '0.4em 0.6em',
						'border-radius': '0.5em',
						'font-size': '0.8em',
						'border': '1px solid #111'
					});
				}
			}

			$(placeholder).bind("plothover", function (event, pos, item) {
				if (item) {
					var tipText;

					if(opts.xaxis.mode === "time" || opts.xaxes[0].mode === "time") {
						tipText = stringFormat(to.content, item, timestampToDate);
					}
					else {
						tipText = stringFormat(to.content, item);
					}

					$tip.html( tipText ).css({left: tipPosition.x + to.shifts.x, top: tipPosition.y + to.shifts.y}).show();
				}
				else {
					$tip.hide().html('');
				}
			});

			eventHolder.mousemove(onMouseMove);
		});

		var stringFormat = function(content, item, fnct) {

			var percentPattern = /%p\.{0,1}(\d{0,})/;
			var seriesPattern = /%s/;
			var xPattern = /%x\.{0,1}(\d{0,})/;
			var yPattern = /%y\.{0,1}(\d{0,})/;

			//percent match
			if( typeof (item.series.percent) !== 'undefined' ) {
				content = adjustValPrecision(percentPattern, content, item.series.percent);
			}
			//series match
			if( typeof(item.series.label) !== 'undefined' ) {
				content = content.replace(seriesPattern, item.series.label);
			}
			// xVal match
			if( typeof(fnct) === 'function' ) {
				content = content.replace(xPattern, fnct(item.series.data[item.dataIndex][0]) );
			}
			else if( typeof item.series.data[item.dataIndex][0] === 'number' ) {
				content = adjustValPrecision(xPattern, content, item.series.data[item.dataIndex][0]);
			}
			// yVal match
			if( typeof item.series.data[item.dataIndex][1] === 'number' ) {
				content = adjustValPrecision(yPattern, content, item.series.data[item.dataIndex][1]);
			}

			return content;
		};

		var adjustValPrecision = function(pattern, content, value) {

			var precision;
			if( content.match(pattern) !== 'null' ) {
				if(RegExp.$1 !== '') {
					precision = RegExp.$1;
					value = value.toFixed(precision)
				}
				content = content.replace(pattern, value);
			}

			return content;
		};
	}

	$.plot.plugins.push({
		init: init,
		options: options,
		name: 'tooltip',
		version: '0.4.4'
	});
})(jQuery);
<commit_msg>Update flot.tooltip<commit_after>/*
 * jquery.flot.tooltip
 *
 * description: easy-to-use tooltips for Flot charts
 * version: 0.6
 * author: Krzysztof Urbas @krzysu [myviews.pl]
 * website: https://github.com/krzysu/flot.tooltip
 *
 * build on 2013-01-07
 * released under MIT License, 2012
*/

(function ($) {

    // plugin options, default values
    var defaultOptions = {
        tooltip: false,
        tooltipOpts: {
            content: "%s | X: %x | Y: %y",
            // allowed templates are:
            // %s -> series label,
            // %x -> X value,
            // %y -> Y value,
            // %x.2 -> precision of X value,
            // %p -> percent
            xDateFormat: null,
            yDateFormat: null,
            shifts: {
                x: 10,
                y: 20
            },
            defaultTheme: true,

            // callbacks
            onHover: function(flotItem, $tooltipEl) {}
        }
    };

    // object
    var FlotTooltip = function(plot) {

        // variables
        this.tipPosition = {x: 0, y: 0};

        this.init(plot);
    };

    // main plugin function
    FlotTooltip.prototype.init = function(plot) {

        var that = this;

        plot.hooks.bindEvents.push(function (plot, eventHolder) {

            // get plot options
            that.plotOptions = plot.getOptions();

            // if not enabled return
            if (that.plotOptions.tooltip === false || typeof that.plotOptions.tooltip === 'undefined') return;

            // shortcut to access tooltip options
            that.tooltipOptions = that.plotOptions.tooltipOpts;

            // create tooltip DOM element
            var $tip = that.getDomElement();

            // bind event
            $( plot.getPlaceholder() ).bind("plothover", function (event, pos, item) {
                if (item) {
                    var tipText;

                    // convert tooltip content template to real tipText
                    tipText = that.stringFormat(that.tooltipOptions.content, item);

                    $tip.html( tipText )
                        .css({
                            left: that.tipPosition.x + that.tooltipOptions.shifts.x,
                            top: that.tipPosition.y + that.tooltipOptions.shifts.y
                        })
                        .show();

                    // run callback
                    if(typeof that.tooltipOptions.onHover === 'function') {
                        that.tooltipOptions.onHover(item, $tip);
                    }
                }
                else {
                    $tip.hide().html('');
                }
            });

            eventHolder.mousemove( function(e) {
                var pos = {};
                pos.x = e.pageX;
                pos.y = e.pageY;
                that.updateTooltipPosition(pos);
            });
        });
    };

    /**
     * get or create tooltip DOM element
     * @return jQuery object
     */
    FlotTooltip.prototype.getDomElement = function() {
        var $tip;

        if( $('#flotTip').length > 0 ){
            $tip = $('#flotTip');
        }
        else {
            $tip = $('<div />').attr('id', 'flotTip');
            $tip.appendTo('body').hide().css({position: 'absolute'});

            if(this.tooltipOptions.defaultTheme) {
                $tip.css({
                    'background': '#fff',
                    'z-index': '100',
                    'padding': '0.4em 0.6em',
                    'border-radius': '0.5em',
                    'font-size': '0.8em',
                    'border': '1px solid #111'
                });
            }
        }

        return $tip;
    };

    // as the name says
    FlotTooltip.prototype.updateTooltipPosition = function(pos) {
        this.tipPosition.x = pos.x;
        this.tipPosition.y = pos.y;
    };

    /**
     * core function, create tooltip content
     * @param  {string} content - template with tooltip content
     * @param  {object} item - Flot item
     * @return {string} real tooltip content for current item
     */
    FlotTooltip.prototype.stringFormat = function(content, item) {

        var percentPattern = /%p\.{0,1}(\d{0,})/;
        var seriesPattern = /%s/;
        var xPattern = /%x\.{0,1}(\d{0,})/;
        var yPattern = /%y\.{0,1}(\d{0,})/;

        // if it is a function callback get the content string
        if( typeof(content) === 'function' ) {
            content = content(item.series.data[item.dataIndex][0], item.series.data[item.dataIndex][1]);
        }

        // percent match for pie charts
        if( typeof (item.series.percent) !== 'undefined' ) {
            content = this.adjustValPrecision(percentPattern, content, item.series.percent);
        }

        // series match
        if( typeof(item.series.label) !== 'undefined' ) {
            content = content.replace(seriesPattern, item.series.label);
        }

        // time mode axes with custom dateFormat
        if(this.isTimeMode('xaxis', item) && this.isXDateFormat(item)) {
            content = content.replace(xPattern, this.timestampToDate(item.series.data[item.dataIndex][0], this.tooltipOptions.xDateFormat));
        }

        if(this.isTimeMode('yaxis', item) && this.isYDateFormat(item)) {
            content = content.replace(yPattern, this.timestampToDate(item.series.data[item.dataIndex][1], this.tooltipOptions.yDateFormat));
        }

        // set precision if defined
        if( typeof item.series.data[item.dataIndex][0] === 'number' ) {
            content = this.adjustValPrecision(xPattern, content, item.series.data[item.dataIndex][0]);
        }
        if( typeof item.series.data[item.dataIndex][1] === 'number' ) {
            content = this.adjustValPrecision(yPattern, content, item.series.data[item.dataIndex][1]);
        }

        // if no value customization, use tickFormatter by default
        if(typeof item.series.xaxis.tickFormatter !== 'undefined') {
            content = content.replace(xPattern, item.series.xaxis.tickFormatter(item.series.data[item.dataIndex][0], item.series.xaxis));
        }
        if(typeof item.series.yaxis.tickFormatter !== 'undefined') {
            content = content.replace(yPattern, item.series.yaxis.tickFormatter(item.series.data[item.dataIndex][1], item.series.yaxis));
        }

        return content;
    };

    // helpers just for readability
    FlotTooltip.prototype.isTimeMode = function(axisName, item) {
        return (typeof item.series[axisName].options.mode !== 'undefined' && item.series[axisName].options.mode === 'time');
    };

    FlotTooltip.prototype.isXDateFormat = function(item) {
        return (typeof this.tooltipOptions.xDateFormat !== 'undefined' && this.tooltipOptions.xDateFormat !== null);
    };

    FlotTooltip.prototype.isYDateFormat = function(item) {
        return (typeof this.tooltipOptions.yDateFormat !== 'undefined' && this.tooltipOptions.yDateFormat !== null);
    };

    //
    FlotTooltip.prototype.timestampToDate = function(tmst, dateFormat) {
        var theDate = new Date(tmst);
        return $.plot.formatDate(theDate, dateFormat);
    };

    //
    FlotTooltip.prototype.adjustValPrecision = function(pattern, content, value) {

        var precision;
        if( content.match(pattern) !== null ) {
            if(RegExp.$1 !== '') {
                precision = RegExp.$1;
                value = value.toFixed(precision);

                // only replace content if precision exists
                content = content.replace(pattern, value);
            }
        }

        return content;
    };

    // to have controll over existing instances
    var tooltipsCollection = [];

    //
    var init = function(plot) {
        tooltipsCollection.push( new FlotTooltip(plot) );
    };

    // define Flot plugin
    $.plot.plugins.push({
        init: init,
        options: defaultOptions,
        name: 'tooltip',
        version: '0.6'
    });

})(jQuery);
<|endoftext|>


<commit_before>"use strict";

const is = require("simple-is");

function traverse(root, options) {
    options = options || {};
    const pre = options.pre;
    const post = options.post;
    const cleanup = options.cleanup;

    function visit(node, parent) {
        if (!node || !is.string(node.type)) {
            return;
        }

        if (!cleanup && !is.own(node, "$parent")) {
            node.$parent = parent;
        }

        let res = undefined;
        if (pre) {
            res = pre(node);
        }

        if (res !== false) {
            for (let prop in node) {
                if (prop[0] === "$") {
                    if (cleanup) {
                        delete node[prop];
                    }
                    continue;
                }

                const child = node[prop];

                if (Array.isArray(child)) {
                    for (let i = 0; i < child.length; i++) {
                        visit(child[i], node);
                    }
                } else {
                    visit(child, node);
                }
            }
        }

        if (post) {
            post(node);
        }
    }

    visit(root, null);
};
module.exports = traverse;
<commit_msg>traverse: don't set , pass current and parent node to callback<commit_after>"use strict";

const is = require("simple-is");

function traverse(root, options) {
    options = options || {};
    const pre = options.pre;
    const post = options.post;
    const cleanup = options.cleanup;

    function visit(node, parent) {
        if (!node || !is.string(node.type)) {
            return;
        }

        let res = undefined;
        if (pre) {
            res = pre(node, parent);
        }

        if (res !== false) {
            for (let prop in node) {
                if (prop[0] === "$") {
                    if (cleanup) {
                        delete node[prop];
                    }
                    continue;
                }

                const child = node[prop];

                if (Array.isArray(child)) {
                    for (let i = 0; i < child.length; i++) {
                        visit(child[i], node);
                    }
                } else {
                    visit(child, node);
                }
            }
        }

        if (post) {
            post(node, parent);
        }
    }

    visit(root, null);
};
module.exports = traverse;
<|endoftext|>


<commit_before>//////////////////////////////////////////////////////////////////////////
// nptm - node project template manager
//////////////////////////////////////////////////////////////////////////
//
// Main script!
/* ----------------------------------------------------------------------
													Object Structures
-------------------------------------------------------------------------

*/
//////////////////////////////////////////////////////////////////////////
// Requires
var ares = require("ares").ares,
	async = require("async"),
	maker = require("maker").createMaker(),
	gitOps = require("./src/gitOperations");

//////////////////////////////////////////////////////////////////////////
// Namespace (lol)
var SHOW_DEBUG_PRINTS = true;
var log = function( a ) { if(SHOW_DEBUG_PRINTS) console.log(a); };				// A log function we can turn off

//////////////////////////////////////////////////////////////////////////
// Constructor
function spawner() {
	var _this = this;

} // end spawner()

//////////////////////////////////////////////////////////////////////////
// Spawns a project
spawner.prototype.spawn = function( options, contents, callback ) {
	var _this = this;

	options.tempRepoDest = __dirname + "/TempClones/";

	async.series([
		function( cb ) {
			// Clone the repo onto the local computer
			gitOps.cloneRepo( options, cb );
		},
		function( cb ) {
			var source = options.tempRepoDest + "/" + options.github.repo;

			maker.makeTemplatesFromDir( source, options.dest, options.keywords, options.keywords, options.extensions, contents, cb );
		},

	// Finished callback
	], function() {
		callback();
	});

} // end spawn()

exports.spawner = spawner;<commit_msg>Make sure we're spawning with correctly setup paths and some code cleanup<commit_after>//////////////////////////////////////////////////////////////////////////
// nptm - node project template manager
//////////////////////////////////////////////////////////////////////////
//
// Main script!
/* ----------------------------------------------------------------------
													Object Structures
-------------------------------------------------------------------------
	var options = {
		dest: "...path where we'll be dumping files...",
	}
*/
//////////////////////////////////////////////////////////////////////////
// Requires
var ares = require("ares").ares,
	async = require("async"),
	maker = require("maker").createMaker(),
	gitOps = require("./src/gitOperations");

//////////////////////////////////////////////////////////////////////////
// Namespace (lol)
var SHOW_DEBUG_PRINTS = true;
var log = function( a ) { if(SHOW_DEBUG_PRINTS) console.log(a); };	// A log function we can turn off

//////////////////////////////////////////////////////////////////////////
// Constructor
function spawner() {
	var _this = this;

} // end spawner()

//////////////////////////////////////////////////////////////////////////
// Spawns a project
spawner.prototype.spawn = function( dest, template, options, contents, callback ) {
	var _this = this;

	template.tempRepoDest = options.dest + "/_Temp/";

	async.series([
		function( cb ) {
			// Clone the repo onto the local computer
			gitOps.cloneRepo( template, cb );
		},
		function( cb ) {
			var source = template.tempRepoDest + "/" + template.github.repo;

			// Construct our filepath map by grabbing the end values for each
			// template from the contents we're given
			var filePathMap = {};
			for( var iKey in template.keywords ) {
				var keyword = template.keywords[iKey];
				filePathMap[iKey] = contents[keyword];
			}

			maker.makeTemplatesFromDir( source, options.dest + "/", template.keywords, filePathMap, template.extensions, contents, cb );
		}

	// We're finished, call back!
	], function() {
		callback();
	});

} // end spawn()

exports.spawner = spawner;<|endoftext|>


<commit_before>"use strict";

var puzzle = {
    tilesCount: 4,
    difficulty: 42,
    height: null,
    width: null,
    tiles: null,
    image: null,
    solved: null
};

function init() {
    var elm = document.getElementById('file');
    var label = document.getElementById('file_label');
    if (elm.type !== 'file') {
        /* For browsers without input=file, i.e. Firefox OS, we show the
           Web Activities "pick", restricting to images. We should probably do
           the test the other way around, so that browsers without input=file
           and without MozActvity don't throw errors, but Firefox Desktop has
           some issues when we test for MozActivity presence :( */
        elm.type = "button";
        elm.value = label.textContent;
        elm.addEventListener('click', function(e) {
            e.preventDefault();
            var pick = new MozActivity({
                name: "pick",
                data: {
                    type: ["image/png", "image/jpg", "image/jpeg"]
                }
            });
            pick.onsuccess = function() {
                // Create image and set the returned blob as the src
                puzzle.init(this.result.blob);
            };
        });
        label.style.display = 'none';
        document.getElementById('gui').appendChild(elm);
    } else {
        elm.addEventListener('change', function() {
            if (this.files.length) {
                puzzle.init(this.files[0]);
            }
        });
        document.getElementById('gui').appendChild(elm);
    }
    showGui();
}

function showGui() {
    document.getElementById('gui').classList.remove('hidden');
}

function Tile(x, y) {
    this.x = x;
    this.y = y;
    this.originalX = x;
    this.originalY = y;
    this.elm = null;
}

Tile.prototype.isEmpty = function() {
    // FIXME: use everywhere
    return (this === puzzle.tiles.empty);
};

Tile.prototype.debug = function(delay) {
    setTimeout((function() {
        if (this.elm) {
            this.elm.style.opacity = '0.5';
            this.elm.style.outline = '1px dashed yellow';
        }
    }).bind(this), delay || 20);
};

Tile.prototype.eventHandler = function(e) {
    if (!document.body.classList.contains('moving')) {
        this.move();
    }
    e.preventDefault();
};

Tile.prototype.move = function() {
    var empty = puzzle.tiles.empty;

    if (this.canMove()) {
        document.body.classList.add('moving');
        this.originalX = this.x;
        this.originalY = this.y;
        this.x = empty.x;
        this.y = empty.y;
        empty.x = this.originalX;
        empty.y = this.originalY;
        this.reposition();
    } else if (this.canLineMove('x')) {
        this.lineMove('x', 'y');
    } else if (this.canLineMove('y')) {
        this.lineMove('y', 'x');
    }
};

Tile.prototype.lineMove = function(mainAxis, secondaryAxis) {
    function sortTiles(a, b) {
        if (this[secondaryAxis] > puzzle.tiles.empty[secondaryAxis]) {
            return (a[secondaryAxis] - b[secondaryAxis]);
        } else {
            return (b[secondaryAxis] - a[secondaryAxis]);
        }
    }

    var tiles = [];
    for (var i = 0; i < puzzle.tiles.length; i++) {
        for (var j = 0; j < puzzle.tiles[i].length; j++) {
            var tile = puzzle.tiles[i][j];
            if (tile.isEmpty()) {
                continue;
            }
            if (tile[mainAxis] === this[mainAxis]) {
                if (this[secondaryAxis] > puzzle.tiles.empty[secondaryAxis]) {
                    if (tile[secondaryAxis] > puzzle.tiles.empty[secondaryAxis]
                        && tile[secondaryAxis] <= this[secondaryAxis]) {
                        tiles.push(tile);
                    }
                } else {
                    if (tile[secondaryAxis] < puzzle.tiles.empty[secondaryAxis]
                     && tile[secondaryAxis] >= this[secondaryAxis]) {
                        tiles.push(tile);
                    }
                }
            }
        }
    }
    tiles.sort(sortTiles.bind(this));
    puzzle.moving = tiles.length;
    tiles.forEach(function(item) {
        item.move();
    });
};

Tile.prototype.canLineMove = function(property) {
    var empty = puzzle.tiles.empty;
    return (this !== puzzle.tiles.empty /* don't consider empty tile as moveable */
         && empty[property] === this[property]);
};

Tile.prototype.canMove = function() {
    var empty = puzzle.tiles.empty;
    return (this !== puzzle.tiles.empty /* don't consider empty tile as moveable */
         && Math.abs(empty.x - this.x) + Math.abs(empty.y - this.y) === 1);
};

Tile.prototype.reposition = function() {
    function realReposition(e) {
        e.stopPropagation();
        this.classList.remove('peek');
        style.webkitTransform = 'translateX(' + x + 'px) translateY(' + y + 'px)';
        style.transform = 'translateX(' + x + 'px) translateY(' + y + 'px)';
        this.removeEventListener('transitionend', realReposition);
        this.removeEventListener('webkitTransitionEnd', realReposition);
    }

    var style = this.elm.style;
    var x = this.x * (puzzle.width / puzzle.tilesCount);
    var y = this.y * (puzzle.height / puzzle.tilesCount);
    this.elm.classList.add('peek');
    // move 1% in the right direction before doing the real move, to avoid
    // ugly flickering with Firefox OS
    this.elm.addEventListener('transitionend', realReposition);
    this.elm.addEventListener('webkitTransitionEnd', realReposition);
    var oldX = this.originalX * (puzzle.width / puzzle.tilesCount);
    var oldY = this.originalY * (puzzle.height / puzzle.tilesCount);
    var peekX = Math.round(oldX + (x - oldX) / 100);
    var peekY = Math.round(oldY + (y - oldY) / 100);
    style.webkitTransform = 'translateX(' + peekX + 'px) translateY(' + peekY + 'px)';
    style.transform = 'translateX(' + peekX + 'px) translateY(' + peekY + 'px)';
};

puzzle.init = function(file) {
    var container = document.createElement('div');
    container.id = 'container';
    document.body.appendChild(container);

    puzzle.solved = false;
    puzzle.height = window.innerHeight;
    puzzle.width = window.innerWidth;
    puzzle.createTiles();

    puzzle.img = new Image();
    puzzle.img.onload = puzzle.initialDraw;
    document.getElementById('gui').classList.add('hidden');

    puzzle.img.src = window.URL.createObjectURL(file);
};

puzzle.initialDraw = function() {
    function transitionEnd(e) {
        if (e.target.tagName.toLowerCase() !== 'canvas') {
            return;
        }
        if (puzzle.moving > 0) {
            puzzle.moving--;
        }
        if (!puzzle.moving) {
            document.body.classList.remove('moving');
            puzzle.checkSolved();
        }
    }
    puzzle.redraw();
    puzzle.shuffle();
    puzzle.moving = 0;
    document.body.addEventListener('transitionend', transitionEnd);
    document.body.addEventListener('webkitTransitionEnd', transitionEnd);
    document.defaultView.addEventListener('resize', puzzle.redraw);
    // FIXME: is "resize" enough ?
    //document.defaultView.addEventListener("deviceorientation", puzzle.redraw, true);
};

puzzle.redraw = function() {
    document.body.classList.add('moving');
    var img = puzzle.img;
    var canvas = document.createElement('canvas');
    var ctx = canvas.getContext('2d');
    var puzzleOrientation = null, imageOrientation = null;

    puzzle.height = window.innerHeight;
    puzzle.width = window.innerWidth;
    canvas.width = puzzle.width;
    canvas.height = puzzle.height;
    canvas.className = 'debug';
    if (puzzle.width !== puzzle.height) {
        puzzleOrientation = (puzzle.width / puzzle.height) > 1;
    }
    if (img.width !== img.height) {
        imageOrientation = (img.width / img.height) > 1;
    }
    if (puzzleOrientation !== null && imageOrientation !== null &&
        imageOrientation !== puzzleOrientation) {
        // If image orientation and puzzle orientation differ - and we are not
        // drawing to/from a square - we need to rotate the canvas 90 deg to
        // display the image.
        ctx.rotate(90 * Math.PI / 180);
        ctx.translate(0, -canvas.width);
        ctx.drawImage(img, 0, 0, canvas.height, canvas.width);
    } else {
        ctx.drawImage(img, 0, 0, canvas.width, canvas.height);
    }
    //document.body.appendChild(canvas); // for debugging

    for (var i = 0; i < puzzle.tiles.length; i++) {
        for (var j = 0; j < puzzle.tiles[i].length; j++) {
            if (i === puzzle.tiles.length -1 && j === puzzle.tiles[i].length -1) {
                continue;
            }
            var tile = puzzle.tiles[i][j];
            var elm = tile.elm;
            elm.width = puzzle.width / puzzle.tilesCount;
            elm.height = puzzle.height / puzzle.tilesCount;
            var sx = i * puzzle.width / puzzle.tilesCount;
            var sy = j * puzzle.height / puzzle.tilesCount;
            var sw = canvas.width / puzzle.tilesCount;
            var sh = canvas.height / puzzle.tilesCount;
            var dx = 0;
            var dy = 0;
            var dw = elm.width;
            var dh = elm.height;
            ctx = elm.getContext('2d');
            ctx.drawImage(canvas, sx, sy, sw, sh, dx, dy, dw, dh);
            ctx.strokeRect(0, 0, dw, dh);
            tile.reposition();
        }
    }
    document.body.classList.remove('moving');
};

puzzle.shuffle = function() {
    var current = null;

    function getAvailableTiles() {
        var availableTiles = [];
        for (var i = 0; i < puzzle.tiles.length; i++) {
            for (var j = 0; j < puzzle.tiles[i].length; j++) {
                var tile = puzzle.tiles[i][j];
                if (tile.canMove() && tile !== current) {
                    availableTiles.push(tile);
                }
            }
        }
        return availableTiles;
    }
    for (var d = 0; d < puzzle.difficulty; d++) {
        var available = getAvailableTiles();
        var i = Math.round(Math.random() * (available.length - 1));
        current = available[i];
        current.move(true);
    }
};

puzzle.checkSolved = function() {
    if (puzzle.solved) {
        return;
    }
    var solved = true;
    for (var i = 0; i < puzzle.tiles.length; i++) {
        for (var j = 0; j < puzzle.tiles[i].length; j++) {
            var tile = puzzle.tiles[i][j];
            if (i !== tile.x || j !== tile.y) {
                solved = false;
                break;
            }
        }
    }
    if (solved) {
        var playagain = confirm('Congratulations! Play again ?');
        if (playagain) {
            var container = document.getElementById('container');
            container.parentNode.removeChild(container, true);
            showGui();
        }
        puzzle.solved = solved;
    }
};

puzzle.createTiles = function() {
    var container = document.getElementById('container');

    puzzle.tiles = new Array(puzzle.tilesCount);
    puzzle.tiles.empty = new Tile(puzzle.tilesCount - 1, puzzle.tilesCount - 1);
    for (var i = 0; i < puzzle.tilesCount; i++) {
        puzzle.tiles[i] = new Array(puzzle.tilesCount);
        for (var j = 0; j < puzzle.tilesCount; j++) {
            if (i === puzzle.tiles.empty.x && j === puzzle.tiles.empty.y) {
                puzzle.tiles[i][j] = puzzle.tiles.empty;
            } else {
                var tile = new Tile(i, j);
                puzzle.tiles[i][j] = tile;
                if (!tile.elm) {
                    tile.elm = document.createElement('canvas');
                }
                tile.elm.className = 'tile';
                tile.elm.addEventListener('touchstart', tile.eventHandler.bind(tile));
                tile.elm.addEventListener('mousedown', tile.eventHandler.bind(tile));
                container.appendChild(tile.elm);
            }
        }
    }
};<commit_msg>Clarify comment about MozActivity<commit_after>"use strict";

var puzzle = {
    tilesCount: 4,
    difficulty: 42,
    height: null,
    width: null,
    tiles: null,
    image: null,
    solved: null
};

function init() {
    var elm = document.getElementById('file');
    var label = document.getElementById('file_label');
    if (elm.type !== 'file') {
        /* For browsers without input=file, i.e. Firefox OS, we show the
           Web Activities "pick", restricting to images.

           We should probably do the test the other way around, testing
           MozActivity presence first, so that browsers without input=file
           and without MozActvity don't throw errors, but testing for
           MozActivity presence doesn't garantee us it will work to pick images.
           ('MozActivity' in window is true in FF Desktop, but unusable, and we
            can only know that too late in the process, after the user has
            already clicked the button)
        */
        elm.type = "button";
        elm.value = label.textContent;
        elm.addEventListener('click', function(e) {
            e.preventDefault();
            var pick = new MozActivity({
                name: "pick",
                data: {
                    type: ["image/png", "image/jpg", "image/jpeg"]
                }
            });
            pick.onsuccess = function() {
                // Create image and set the returned blob as the src
                puzzle.init(this.result.blob);
            };
        });
        label.style.display = 'none';
        document.getElementById('gui').appendChild(elm);
    } else {
        elm.addEventListener('change', function() {
            if (this.files.length) {
                puzzle.init(this.files[0]);
            }
        });
        document.getElementById('gui').appendChild(elm);
    }
    showGui();
}

function showGui() {
    document.getElementById('gui').classList.remove('hidden');
}

function Tile(x, y) {
    this.x = x;
    this.y = y;
    this.originalX = x;
    this.originalY = y;
    this.elm = null;
}

Tile.prototype.isEmpty = function() {
    // FIXME: use everywhere
    return (this === puzzle.tiles.empty);
};

Tile.prototype.debug = function(delay) {
    setTimeout((function() {
        if (this.elm) {
            this.elm.style.opacity = '0.5';
            this.elm.style.outline = '1px dashed yellow';
        }
    }).bind(this), delay || 20);
};

Tile.prototype.eventHandler = function(e) {
    if (!document.body.classList.contains('moving')) {
        this.move();
    }
    e.preventDefault();
};

Tile.prototype.move = function() {
    var empty = puzzle.tiles.empty;

    if (this.canMove()) {
        document.body.classList.add('moving');
        this.originalX = this.x;
        this.originalY = this.y;
        this.x = empty.x;
        this.y = empty.y;
        empty.x = this.originalX;
        empty.y = this.originalY;
        this.reposition();
    } else if (this.canLineMove('x')) {
        this.lineMove('x', 'y');
    } else if (this.canLineMove('y')) {
        this.lineMove('y', 'x');
    }
};

Tile.prototype.lineMove = function(mainAxis, secondaryAxis) {
    function sortTiles(a, b) {
        if (this[secondaryAxis] > puzzle.tiles.empty[secondaryAxis]) {
            return (a[secondaryAxis] - b[secondaryAxis]);
        } else {
            return (b[secondaryAxis] - a[secondaryAxis]);
        }
    }

    var tiles = [];
    for (var i = 0; i < puzzle.tiles.length; i++) {
        for (var j = 0; j < puzzle.tiles[i].length; j++) {
            var tile = puzzle.tiles[i][j];
            if (tile.isEmpty()) {
                continue;
            }
            if (tile[mainAxis] === this[mainAxis]) {
                if (this[secondaryAxis] > puzzle.tiles.empty[secondaryAxis]) {
                    if (tile[secondaryAxis] > puzzle.tiles.empty[secondaryAxis]
                        && tile[secondaryAxis] <= this[secondaryAxis]) {
                        tiles.push(tile);
                    }
                } else {
                    if (tile[secondaryAxis] < puzzle.tiles.empty[secondaryAxis]
                     && tile[secondaryAxis] >= this[secondaryAxis]) {
                        tiles.push(tile);
                    }
                }
            }
        }
    }
    tiles.sort(sortTiles.bind(this));
    puzzle.moving = tiles.length;
    tiles.forEach(function(item) {
        item.move();
    });
};

Tile.prototype.canLineMove = function(property) {
    var empty = puzzle.tiles.empty;
    return (this !== puzzle.tiles.empty /* don't consider empty tile as moveable */
         && empty[property] === this[property]);
};

Tile.prototype.canMove = function() {
    var empty = puzzle.tiles.empty;
    return (this !== puzzle.tiles.empty /* don't consider empty tile as moveable */
         && Math.abs(empty.x - this.x) + Math.abs(empty.y - this.y) === 1);
};

Tile.prototype.reposition = function() {
    function realReposition(e) {
        e.stopPropagation();
        this.classList.remove('peek');
        style.webkitTransform = 'translateX(' + x + 'px) translateY(' + y + 'px)';
        style.transform = 'translateX(' + x + 'px) translateY(' + y + 'px)';
        this.removeEventListener('transitionend', realReposition);
        this.removeEventListener('webkitTransitionEnd', realReposition);
    }

    var style = this.elm.style;
    var x = this.x * (puzzle.width / puzzle.tilesCount);
    var y = this.y * (puzzle.height / puzzle.tilesCount);
    this.elm.classList.add('peek');
    // move 1% in the right direction before doing the real move, to avoid
    // ugly flickering with Firefox OS
    this.elm.addEventListener('transitionend', realReposition);
    this.elm.addEventListener('webkitTransitionEnd', realReposition);
    var oldX = this.originalX * (puzzle.width / puzzle.tilesCount);
    var oldY = this.originalY * (puzzle.height / puzzle.tilesCount);
    var peekX = Math.round(oldX + (x - oldX) / 100);
    var peekY = Math.round(oldY + (y - oldY) / 100);
    style.webkitTransform = 'translateX(' + peekX + 'px) translateY(' + peekY + 'px)';
    style.transform = 'translateX(' + peekX + 'px) translateY(' + peekY + 'px)';
};

puzzle.init = function(file) {
    var container = document.createElement('div');
    container.id = 'container';
    document.body.appendChild(container);

    puzzle.solved = false;
    puzzle.height = window.innerHeight;
    puzzle.width = window.innerWidth;
    puzzle.createTiles();

    puzzle.img = new Image();
    puzzle.img.onload = puzzle.initialDraw;
    document.getElementById('gui').classList.add('hidden');

    puzzle.img.src = window.URL.createObjectURL(file);
};

puzzle.initialDraw = function() {
    function transitionEnd(e) {
        if (e.target.tagName.toLowerCase() !== 'canvas') {
            return;
        }
        if (puzzle.moving > 0) {
            puzzle.moving--;
        }
        if (!puzzle.moving) {
            document.body.classList.remove('moving');
            puzzle.checkSolved();
        }
    }
    puzzle.redraw();
    puzzle.shuffle();
    puzzle.moving = 0;
    document.body.addEventListener('transitionend', transitionEnd);
    document.body.addEventListener('webkitTransitionEnd', transitionEnd);
    document.defaultView.addEventListener('resize', puzzle.redraw);
    // FIXME: is "resize" enough ?
    //document.defaultView.addEventListener("deviceorientation", puzzle.redraw, true);
};

puzzle.redraw = function() {
    document.body.classList.add('moving');
    var img = puzzle.img;
    var canvas = document.createElement('canvas');
    var ctx = canvas.getContext('2d');
    var puzzleOrientation = null, imageOrientation = null;

    puzzle.height = window.innerHeight;
    puzzle.width = window.innerWidth;
    canvas.width = puzzle.width;
    canvas.height = puzzle.height;
    canvas.className = 'debug';
    if (puzzle.width !== puzzle.height) {
        puzzleOrientation = (puzzle.width / puzzle.height) > 1;
    }
    if (img.width !== img.height) {
        imageOrientation = (img.width / img.height) > 1;
    }
    if (puzzleOrientation !== null && imageOrientation !== null &&
        imageOrientation !== puzzleOrientation) {
        // If image orientation and puzzle orientation differ - and we are not
        // drawing to/from a square - we need to rotate the canvas 90 deg to
        // display the image.
        ctx.rotate(90 * Math.PI / 180);
        ctx.translate(0, -canvas.width);
        ctx.drawImage(img, 0, 0, canvas.height, canvas.width);
    } else {
        ctx.drawImage(img, 0, 0, canvas.width, canvas.height);
    }
    //document.body.appendChild(canvas); // for debugging

    for (var i = 0; i < puzzle.tiles.length; i++) {
        for (var j = 0; j < puzzle.tiles[i].length; j++) {
            if (i === puzzle.tiles.length -1 && j === puzzle.tiles[i].length -1) {
                continue;
            }
            var tile = puzzle.tiles[i][j];
            var elm = tile.elm;
            elm.width = puzzle.width / puzzle.tilesCount;
            elm.height = puzzle.height / puzzle.tilesCount;
            var sx = i * puzzle.width / puzzle.tilesCount;
            var sy = j * puzzle.height / puzzle.tilesCount;
            var sw = canvas.width / puzzle.tilesCount;
            var sh = canvas.height / puzzle.tilesCount;
            var dx = 0;
            var dy = 0;
            var dw = elm.width;
            var dh = elm.height;
            ctx = elm.getContext('2d');
            ctx.drawImage(canvas, sx, sy, sw, sh, dx, dy, dw, dh);
            ctx.strokeRect(0, 0, dw, dh);
            tile.reposition();
        }
    }
    document.body.classList.remove('moving');
};

puzzle.shuffle = function() {
    var current = null;

    function getAvailableTiles() {
        var availableTiles = [];
        for (var i = 0; i < puzzle.tiles.length; i++) {
            for (var j = 0; j < puzzle.tiles[i].length; j++) {
                var tile = puzzle.tiles[i][j];
                if (tile.canMove() && tile !== current) {
                    availableTiles.push(tile);
                }
            }
        }
        return availableTiles;
    }
    for (var d = 0; d < puzzle.difficulty; d++) {
        var available = getAvailableTiles();
        var i = Math.round(Math.random() * (available.length - 1));
        current = available[i];
        current.move(true);
    }
};

puzzle.checkSolved = function() {
    if (puzzle.solved) {
        return;
    }
    var solved = true;
    for (var i = 0; i < puzzle.tiles.length; i++) {
        for (var j = 0; j < puzzle.tiles[i].length; j++) {
            var tile = puzzle.tiles[i][j];
            if (i !== tile.x || j !== tile.y) {
                solved = false;
                break;
            }
        }
    }
    if (solved) {
        var playagain = confirm('Congratulations! Play again ?');
        if (playagain) {
            var container = document.getElementById('container');
            container.parentNode.removeChild(container, true);
            showGui();
        }
        puzzle.solved = solved;
    }
};

puzzle.createTiles = function() {
    var container = document.getElementById('container');

    puzzle.tiles = new Array(puzzle.tilesCount);
    puzzle.tiles.empty = new Tile(puzzle.tilesCount - 1, puzzle.tilesCount - 1);
    for (var i = 0; i < puzzle.tilesCount; i++) {
        puzzle.tiles[i] = new Array(puzzle.tilesCount);
        for (var j = 0; j < puzzle.tilesCount; j++) {
            if (i === puzzle.tiles.empty.x && j === puzzle.tiles.empty.y) {
                puzzle.tiles[i][j] = puzzle.tiles.empty;
            } else {
                var tile = new Tile(i, j);
                puzzle.tiles[i][j] = tile;
                if (!tile.elm) {
                    tile.elm = document.createElement('canvas');
                }
                tile.elm.className = 'tile';
                tile.elm.addEventListener('touchstart', tile.eventHandler.bind(tile));
                tile.elm.addEventListener('mousedown', tile.eventHandler.bind(tile));
                container.appendChild(tile.elm);
            }
        }
    }
};<|endoftext|>


<commit_before>(function () {
	'use strict';

	// Create the client and the connection
	// yustClient.create({ id: 'mario', uri: 'http://54.247.168.152:3000' });
	yustClient.create({ id: 'mario', uri: 'http://localhost:3000' });

	// Using jQuery for the hackathon speed needs lol
	$(document).on('ready', function () {
		$('button').on('mousedown', function () {
			yustClient.emit('pressStart', $(this).attr('data-value'));
		});

		$('button').on('mouseup', function () {
			yustClient.emit('pressEnd', $(this).attr('data-value'));
		});

	});

}());<commit_msg>commented localhost socket<commit_after>(function () {
	'use strict';

	// Create the client and the connection
	yustClient.create({ id: 'mario', uri: 'http://54.247.168.152:3000' });
	// yustClient.create({ id: 'mario', uri: 'http://localhost:3000' });

	// Using jQuery for the hackathon speed needs lol
	$(document).on('ready', function () {
		$('button').on('mousedown', function () {
			yustClient.emit('pressStart', $(this).attr('data-value'));
		});

		$('button').on('mouseup', function () {
			yustClient.emit('pressEnd', $(this).attr('data-value'));
		});

	});

}());<|endoftext|>


<commit_before>// @flow
import * as Immutable from "immutable";
import * as React from "react";
import * as actions from "../../actions";
import * as extraHandlers from "./extra-handlers";
import * as selectors from "../../selectors";
import Menu, { SubMenu, Divider, MenuItem } from "rc-menu";
import type { KernelRef } from "../../state/refs";
import { MENU_ITEM_ACTIONS, MENUS } from "./constants";
import { MODAL_TYPES } from "../modal-controller";
import { connect } from "react-redux";
import { localCss } from "./styles";

// To allow actions that can take dynamic arguments (like selecting a kernel
// based on the host's kernelspecs), we have some simple utility functions to
// stringify/parse actions/arguments.
const createActionKey = (action, ...args) => [action, ...args].join(":");
const parseActionKey = key => key.split(":");

type Props = {
  defaultOpenKeys?: Array<string>,
  openKeys?: Array<string>,
  cellFocused: ?string,
  currentKernelRef: ?KernelRef,
  saveNotebook: ?() => void,
  executeCell: ?(cellId: ?string) => void,
  executeAllCells: ?() => void,
  executeAllCellsBelow: ?() => void,
  clearAllOutputs: ?() => void,
  unhideAllCells: ?(*) => void,
  cutCell: ?(cellId: ?string) => void,
  copyCell: ?(cellId: ?string) => void,
  mergeCellAfter: ?(cellId: ?string) => void,
  filename: ?string,
  notebook: Immutable.Map<string, *>,
  pasteCell: ?() => void,
  createCodeCell: ?(cellId: ?string) => void,
  createMarkdownCell: ?(cellId: ?string) => void,
  setCellTypeCode: ?(cellId: ?string) => void,
  setCellTypeMarkdown: ?(cellId: ?string) => void,
  setTheme: ?(theme: ?string) => void,
  openAboutModal: ?() => void,
  restartKernel: ?(payload: *) => void,
  restartKernelAndClearOutputs: ?(payload: *) => void,
  killKernel: ?(payload: *) => void,
  interruptKernel: ?(payload: *) => void
};

class PureNotebookMenu extends React.Component<Props> {
  static defaultProps = {
    cellFocused: null,
    saveNotebook: null,
    currentKernelRef: null,
    executeCell: null,
    executeAllCells: null,
    executeAllCellsBelow: null,
    clearAllOutputs: null,
    unhideAllCells: null,
    cutCell: null,
    copyCell: null,
    mergeCellAfter: null,
    notebook: null,
    pasteCell: null,
    createCodeCell: null,
    createMarkdownCell: null,
    setCellTypeCode: null,
    setCellTypeMarkdown: null,
    setTheme: null,
    openAboutModal: null,
    restartKernel: null,
    killKernel: null,
    interruptKernel: null
  };
  handleClick = ({ key }: { key: string }) => {
    const {
      saveNotebook,
      cellFocused,
      currentKernelRef,
      copyCell,
      createCodeCell,
      createMarkdownCell,
      cutCell,
      executeCell,
      executeAllCells,
      executeAllCellsBelow,
      clearAllOutputs,
      unhideAllCells,
      filename,
      mergeCellAfter,
      notebook,
      openAboutModal,
      pasteCell,
      setCellTypeCode,
      setCellTypeMarkdown,
      setTheme,
      restartKernel,
      restartKernelAndClearOutputs,
      killKernel,
      interruptKernel
    } = this.props;
    const [action, ...args] = parseActionKey(key);
    switch (action) {
      case MENU_ITEM_ACTIONS.SAVE_NOTEBOOK:
        if (saveNotebook) {
          saveNotebook();
        }
        break;
      case MENU_ITEM_ACTIONS.DOWNLOAD_NOTEBOOK:
        // This gets us around a Flow fail on document.body.
        const body = document.body;
        if (body) {
          extraHandlers.downloadNotebook(notebook, filename);
        }
        break;
      case MENU_ITEM_ACTIONS.COPY_CELL:
        if (copyCell) {
          copyCell(cellFocused);
        }
        break;
      case MENU_ITEM_ACTIONS.CUT_CELL:
        if (cutCell) {
          cutCell(cellFocused);
        }
        break;
      case MENU_ITEM_ACTIONS.PASTE_CELL:
        if (pasteCell) {
          pasteCell();
        }
        break;
      case MENU_ITEM_ACTIONS.MERGE_CELL_AFTER:
        if (mergeCellAfter) {
          mergeCellAfter(cellFocused);
        }
        break;
      case MENU_ITEM_ACTIONS.CREATE_CODE_CELL:
        if (createCodeCell) {
          createCodeCell(cellFocused);
        }
        break;
      case MENU_ITEM_ACTIONS.CREATE_MARKDOWN_CELL:
        if (createMarkdownCell) {
          createMarkdownCell(cellFocused);
        }
        break;
      case MENU_ITEM_ACTIONS.SET_CELL_TYPE_CODE:
        if (setCellTypeCode) {
          setCellTypeCode(cellFocused);
        }
        break;
      case MENU_ITEM_ACTIONS.SET_CELL_TYPE_MARKDOWN:
        if (setCellTypeMarkdown) {
          setCellTypeMarkdown(cellFocused);
        }
        break;
      case MENU_ITEM_ACTIONS.EXECUTE_ALL_CELLS:
        if (executeAllCells) {
          executeAllCells();
        }
        break;
      case MENU_ITEM_ACTIONS.EXECUTE_ALL_CELLS_BELOW:
        if (executeAllCellsBelow) {
          executeAllCellsBelow();
        }
        break;
      case MENU_ITEM_ACTIONS.UNHIDE_ALL_CELLS:
        if (unhideAllCells) {
          unhideAllCells({ outputHidden: false, inputHidden: false });
        }
        break;
      case MENU_ITEM_ACTIONS.CLEAR_ALL_OUTPUTS:
        if (clearAllOutputs) {
          clearAllOutputs();
        }
        break;
      case MENU_ITEM_ACTIONS.SET_THEME_DARK:
        if (setTheme) {
          setTheme("dark");
        }
        break;
      case MENU_ITEM_ACTIONS.SET_THEME_LIGHT:
        if (setTheme) {
          setTheme("light");
        }
        break;
      case MENU_ITEM_ACTIONS.OPEN_ABOUT:
        if (openAboutModal) {
          openAboutModal();
        }
        break;
      case MENU_ITEM_ACTIONS.INTERRUPT_KERNEL:
        if (interruptKernel) {
          interruptKernel({ ref: currentKernelRef });
        }
        break;
      case MENU_ITEM_ACTIONS.RESTART_KERNEL:
        if (restartKernel) {
          restartKernel({ ref: currentKernelRef });
        }
        break;
      case MENU_ITEM_ACTIONS.KILL_KERNEL:
        if (killKernel) {
          killKernel({ ref: currentKernelRef });
        }
        break;
      case MENU_ITEM_ACTIONS.RESTART_AND_CLEAR_OUTPUTS:
        if (restartKernelAndClearOutputs) {
          restartKernelAndClearOutputs({ ref: currentKernelRef });
        }
        break;

      default:
        console.log(`unhandled action: ${action}`);
    }
  };
  render() {
    const { defaultOpenKeys } = this.props;
    return (

        <Menu
          mode="horizontal"
          onClick={this.handleClick}
          defaultOpenKeys={defaultOpenKeys}
          selectable={false}
        >
          <SubMenu key={MENUS.FILE} title="File">
            <MenuItem key={createActionKey(MENU_ITEM_ACTIONS.SAVE_NOTEBOOK)}>
              Save
            </MenuItem>
            <MenuItem
              key={createActionKey(MENU_ITEM_ACTIONS.DOWNLOAD_NOTEBOOK)}
            >
              Download (.ipynb)
            </MenuItem>
          </SubMenu>
          <SubMenu key={MENUS.EDIT} title="Edit">
            <MenuItem key={createActionKey(MENU_ITEM_ACTIONS.CUT_CELL)}>
              Cut Cell
            </MenuItem>
            <MenuItem key={createActionKey(MENU_ITEM_ACTIONS.COPY_CELL)}>
              Copy Cell
            </MenuItem>
            <MenuItem key={createActionKey(MENU_ITEM_ACTIONS.PASTE_CELL)}>
              Paste Cell Below
            </MenuItem>
            <MenuItem key={createActionKey(MENU_ITEM_ACTIONS.MERGE_CELL_AFTER)}>
              Merge With Cell Below
            </MenuItem>
            <Divider />
            <SubMenu key={MENUS.EDIT_SET_CELL_TYPE} title="Cell Type">
              <MenuItem
                key={createActionKey(MENU_ITEM_ACTIONS.SET_CELL_TYPE_CODE)}
              >
                Code
              </MenuItem>
              <MenuItem
                key={createActionKey(MENU_ITEM_ACTIONS.SET_CELL_TYPE_MARKDOWN)}
              >
                Markdown
              </MenuItem>
            </SubMenu>
          </SubMenu>
          <SubMenu key={MENUS.VIEW} title="View">
            <SubMenu key={MENUS.VIEW_THEMES} title="themes">
              <MenuItem
                key={createActionKey(MENU_ITEM_ACTIONS.SET_THEME_LIGHT)}
              >
                light
              </MenuItem>
              <MenuItem key={createActionKey(MENU_ITEM_ACTIONS.SET_THEME_DARK)}>
                dark
              </MenuItem>
            </SubMenu>
          </SubMenu>
          <SubMenu key={MENUS.CELL} title="Cell">
            <MenuItem
              key={createActionKey(MENU_ITEM_ACTIONS.EXECUTE_ALL_CELLS)}
            >
              Run All Cells
            </MenuItem>
            <MenuItem
              key={createActionKey(MENU_ITEM_ACTIONS.EXECUTE_ALL_CELLS_BELOW)}
            >
              Run All Cells Below
            </MenuItem>
            <Divider />
            <SubMenu key={MENUS.CELL_CREATE_CELL} title="New Cell">
              <MenuItem
                key={createActionKey(MENU_ITEM_ACTIONS.CREATE_CODE_CELL)}
              >
                Code
              </MenuItem>
              <MenuItem
                key={createActionKey(MENU_ITEM_ACTIONS.CREATE_MARKDOWN_CELL)}
              >
                Markdown
              </MenuItem>
            </SubMenu>
            <Divider />

            <MenuItem
              key={createActionKey(MENU_ITEM_ACTIONS.CLEAR_ALL_OUTPUTS)}
            >
              Clear All Outputs
            </MenuItem>

            <MenuItem key={createActionKey(MENU_ITEM_ACTIONS.UNHIDE_ALL_CELLS)}>
              Unhide All Input and Output
            </MenuItem>
          </SubMenu>

          <SubMenu key={MENUS.RUNTIME} title="Runtime">
            <MenuItem key={createActionKey(MENU_ITEM_ACTIONS.INTERRUPT_KERNEL)}>
              Interrupt
            </MenuItem>
            <Divider />
            <MenuItem key={createActionKey(MENU_ITEM_ACTIONS.KILL_KERNEL)}>
              Halt
            </MenuItem>
            <MenuItem key={createActionKey(MENU_ITEM_ACTIONS.RESTART_KERNEL)}>
              Restart
            </MenuItem>
            <MenuItem
              key={createActionKey(MENU_ITEM_ACTIONS.RESTART_AND_CLEAR_OUTPUTS)}
            >
              Restart and Clear All Cells
            </MenuItem>
            {/*
              <Divider />
              <SwitchToKernelSpecsMenu />
            */}
          </SubMenu>

          <SubMenu key={MENUS.HELP} title="Help">
            <MenuItem key={createActionKey(MENU_ITEM_ACTIONS.OPEN_ABOUT)}>
              About
            </MenuItem>
          </SubMenu>
        </Menu>

    );
  }
}

// TODO: this forces the menu to re-render on cell focus. The better option is
// to alter how the actions work to just target the currently focused cell.
// That said, we *may* not have a great way of getting around this as we'll need
// information about the current document to decide which menu items are
// available...
const mapStateToProps = state => ({
  cellFocused: selectors.currentFocusedCellId(state),
  filename: selectors.currentFilename(state),
  notebook: selectors.currentNotebook(state),
  currentKernelRef: selectors.currentKernelRef(state)
});

const mapDispatchToProps = dispatch => ({
  saveNotebook: () => dispatch(actions.save()),
  executeCell: cellId => dispatch(actions.executeCell(cellId)),
  executeAllCells: () => dispatch(actions.executeAllCells()),
  executeAllCellsBelow: () => dispatch(actions.executeAllCellsBelow()),
  clearAllOutputs: () => dispatch(actions.clearAllOutputs()),
  unhideAllCells: payload => dispatch(actions.unhideAll(payload)),
  cutCell: cellId => dispatch(actions.cutCell(cellId)),
  copyCell: cellId => dispatch(actions.copyCell(cellId)),
  pasteCell: () => dispatch(actions.pasteCell()),
  mergeCellAfter: cellId => dispatch(actions.mergeCellAfter(cellId)),
  createCodeCell: cellId => dispatch(actions.createCellAfter("code", cellId)),
  createMarkdownCell: cellId =>
    dispatch(actions.createCellAfter("markdown", cellId)),
  setCellTypeCode: cellId => dispatch(actions.changeCellType(cellId, "code")),
  setCellTypeMarkdown: cellId =>
    dispatch(actions.changeCellType(cellId, "markdown")),
  setTheme: theme => dispatch(actions.setTheme(theme)),
  openAboutModal: () =>
    dispatch(actions.openModal({ modalType: MODAL_TYPES.ABOUT })),
  restartKernel: payload => dispatch(actions.restartKernel(payload)),
  restartKernelAndClearOutputs: payload =>
    dispatch(actions.restartKernel({ ...payload, clearOutputs: true })),
  killKernel: payload => dispatch(actions.killKernel(payload)),
  interruptKernel: payload => dispatch(actions.interruptKernel(payload))
});

const NotebookMenu = connect(mapStateToProps, mapDispatchToProps)(
  PureNotebookMenu
);

// We export this for testing purposes.
export { PureNotebookMenu };

export default NotebookMenu;
<commit_msg>switch to kernel refs in the core menu<commit_after>// @flow
import * as Immutable from "immutable";
import * as React from "react";
import * as actions from "../../actions";
import * as extraHandlers from "./extra-handlers";
import * as selectors from "../../selectors";
import Menu, { SubMenu, Divider, MenuItem } from "rc-menu";
import type { KernelRef } from "../../state/refs";
import { MENU_ITEM_ACTIONS, MENUS } from "./constants";
import { MODAL_TYPES } from "../modal-controller";
import { connect } from "react-redux";
import { localCss } from "./styles";

// To allow actions that can take dynamic arguments (like selecting a kernel
// based on the host's kernelspecs), we have some simple utility functions to
// stringify/parse actions/arguments.
const createActionKey = (action, ...args) => [action, ...args].join(":");
const parseActionKey = key => key.split(":");

type Props = {
  defaultOpenKeys?: Array<string>,
  openKeys?: Array<string>,
  cellFocused: ?string,
  currentKernelRef: ?KernelRef,
  saveNotebook: ?() => void,
  executeCell: ?(cellId: ?string) => void,
  executeAllCells: ?() => void,
  executeAllCellsBelow: ?() => void,
  clearAllOutputs: ?() => void,
  unhideAllCells: ?(*) => void,
  cutCell: ?(cellId: ?string) => void,
  copyCell: ?(cellId: ?string) => void,
  mergeCellAfter: ?(cellId: ?string) => void,
  filename: ?string,
  notebook: Immutable.Map<string, *>,
  pasteCell: ?() => void,
  createCodeCell: ?(cellId: ?string) => void,
  createMarkdownCell: ?(cellId: ?string) => void,
  setCellTypeCode: ?(cellId: ?string) => void,
  setCellTypeMarkdown: ?(cellId: ?string) => void,
  setTheme: ?(theme: ?string) => void,
  openAboutModal: ?() => void,
  restartKernel: ?(payload: *) => void,
  restartKernelAndClearOutputs: ?(payload: *) => void,
  killKernel: ?(payload: *) => void,
  interruptKernel: ?(payload: *) => void
};

class PureNotebookMenu extends React.Component<Props> {
  static defaultProps = {
    cellFocused: null,
    saveNotebook: null,
    currentKernelRef: null,
    executeCell: null,
    executeAllCells: null,
    executeAllCellsBelow: null,
    clearAllOutputs: null,
    unhideAllCells: null,
    cutCell: null,
    copyCell: null,
    mergeCellAfter: null,
    notebook: null,
    pasteCell: null,
    createCodeCell: null,
    createMarkdownCell: null,
    setCellTypeCode: null,
    setCellTypeMarkdown: null,
    setTheme: null,
    openAboutModal: null,
    restartKernel: null,
    killKernel: null,
    interruptKernel: null
  };
  handleClick = ({ key }: { key: string }) => {
    const {
      saveNotebook,
      cellFocused,
      currentKernelRef,
      copyCell,
      createCodeCell,
      createMarkdownCell,
      cutCell,
      executeCell,
      executeAllCells,
      executeAllCellsBelow,
      clearAllOutputs,
      unhideAllCells,
      filename,
      mergeCellAfter,
      notebook,
      openAboutModal,
      pasteCell,
      setCellTypeCode,
      setCellTypeMarkdown,
      setTheme,
      restartKernel,
      restartKernelAndClearOutputs,
      killKernel,
      interruptKernel
    } = this.props;
    const [action, ...args] = parseActionKey(key);
    switch (action) {
      case MENU_ITEM_ACTIONS.SAVE_NOTEBOOK:
        if (saveNotebook) {
          saveNotebook();
        }
        break;
      case MENU_ITEM_ACTIONS.DOWNLOAD_NOTEBOOK:
        // This gets us around a Flow fail on document.body.
        const body = document.body;
        if (body) {
          extraHandlers.downloadNotebook(notebook, filename);
        }
        break;
      case MENU_ITEM_ACTIONS.COPY_CELL:
        if (copyCell) {
          copyCell(cellFocused);
        }
        break;
      case MENU_ITEM_ACTIONS.CUT_CELL:
        if (cutCell) {
          cutCell(cellFocused);
        }
        break;
      case MENU_ITEM_ACTIONS.PASTE_CELL:
        if (pasteCell) {
          pasteCell();
        }
        break;
      case MENU_ITEM_ACTIONS.MERGE_CELL_AFTER:
        if (mergeCellAfter) {
          mergeCellAfter(cellFocused);
        }
        break;
      case MENU_ITEM_ACTIONS.CREATE_CODE_CELL:
        if (createCodeCell) {
          createCodeCell(cellFocused);
        }
        break;
      case MENU_ITEM_ACTIONS.CREATE_MARKDOWN_CELL:
        if (createMarkdownCell) {
          createMarkdownCell(cellFocused);
        }
        break;
      case MENU_ITEM_ACTIONS.SET_CELL_TYPE_CODE:
        if (setCellTypeCode) {
          setCellTypeCode(cellFocused);
        }
        break;
      case MENU_ITEM_ACTIONS.SET_CELL_TYPE_MARKDOWN:
        if (setCellTypeMarkdown) {
          setCellTypeMarkdown(cellFocused);
        }
        break;
      case MENU_ITEM_ACTIONS.EXECUTE_ALL_CELLS:
        if (executeAllCells) {
          executeAllCells();
        }
        break;
      case MENU_ITEM_ACTIONS.EXECUTE_ALL_CELLS_BELOW:
        if (executeAllCellsBelow) {
          executeAllCellsBelow();
        }
        break;
      case MENU_ITEM_ACTIONS.UNHIDE_ALL_CELLS:
        if (unhideAllCells) {
          unhideAllCells({ outputHidden: false, inputHidden: false });
        }
        break;
      case MENU_ITEM_ACTIONS.CLEAR_ALL_OUTPUTS:
        if (clearAllOutputs) {
          clearAllOutputs();
        }
        break;
      case MENU_ITEM_ACTIONS.SET_THEME_DARK:
        if (setTheme) {
          setTheme("dark");
        }
        break;
      case MENU_ITEM_ACTIONS.SET_THEME_LIGHT:
        if (setTheme) {
          setTheme("light");
        }
        break;
      case MENU_ITEM_ACTIONS.OPEN_ABOUT:
        if (openAboutModal) {
          openAboutModal();
        }
        break;
      case MENU_ITEM_ACTIONS.INTERRUPT_KERNEL:
        if (interruptKernel) {
          interruptKernel({ kernelRef: currentKernelRef });
        }
        break;
      case MENU_ITEM_ACTIONS.RESTART_KERNEL:
        if (restartKernel) {
          restartKernel({ kernelRef: currentKernelRef });
        }
        break;
      case MENU_ITEM_ACTIONS.KILL_KERNEL:
        if (killKernel) {
          killKernel({ kernelRef: currentKernelRef });
        }
        break;
      case MENU_ITEM_ACTIONS.RESTART_AND_CLEAR_OUTPUTS:
        if (restartKernelAndClearOutputs) {
          restartKernelAndClearOutputs({ kernelRef: currentKernelRef });
        }
        break;

      default:
        console.log(`unhandled action: ${action}`);
    }
  };
  render() {
    const { defaultOpenKeys } = this.props;
    return (

        <Menu
          mode="horizontal"
          onClick={this.handleClick}
          defaultOpenKeys={defaultOpenKeys}
          selectable={false}
        >
          <SubMenu key={MENUS.FILE} title="File">
            <MenuItem key={createActionKey(MENU_ITEM_ACTIONS.SAVE_NOTEBOOK)}>
              Save
            </MenuItem>
            <MenuItem
              key={createActionKey(MENU_ITEM_ACTIONS.DOWNLOAD_NOTEBOOK)}
            >
              Download (.ipynb)
            </MenuItem>
          </SubMenu>
          <SubMenu key={MENUS.EDIT} title="Edit">
            <MenuItem key={createActionKey(MENU_ITEM_ACTIONS.CUT_CELL)}>
              Cut Cell
            </MenuItem>
            <MenuItem key={createActionKey(MENU_ITEM_ACTIONS.COPY_CELL)}>
              Copy Cell
            </MenuItem>
            <MenuItem key={createActionKey(MENU_ITEM_ACTIONS.PASTE_CELL)}>
              Paste Cell Below
            </MenuItem>
            <MenuItem key={createActionKey(MENU_ITEM_ACTIONS.MERGE_CELL_AFTER)}>
              Merge With Cell Below
            </MenuItem>
            <Divider />
            <SubMenu key={MENUS.EDIT_SET_CELL_TYPE} title="Cell Type">
              <MenuItem
                key={createActionKey(MENU_ITEM_ACTIONS.SET_CELL_TYPE_CODE)}
              >
                Code
              </MenuItem>
              <MenuItem
                key={createActionKey(MENU_ITEM_ACTIONS.SET_CELL_TYPE_MARKDOWN)}
              >
                Markdown
              </MenuItem>
            </SubMenu>
          </SubMenu>
          <SubMenu key={MENUS.VIEW} title="View">
            <SubMenu key={MENUS.VIEW_THEMES} title="themes">
              <MenuItem
                key={createActionKey(MENU_ITEM_ACTIONS.SET_THEME_LIGHT)}
              >
                light
              </MenuItem>
              <MenuItem key={createActionKey(MENU_ITEM_ACTIONS.SET_THEME_DARK)}>
                dark
              </MenuItem>
            </SubMenu>
          </SubMenu>
          <SubMenu key={MENUS.CELL} title="Cell">
            <MenuItem
              key={createActionKey(MENU_ITEM_ACTIONS.EXECUTE_ALL_CELLS)}
            >
              Run All Cells
            </MenuItem>
            <MenuItem
              key={createActionKey(MENU_ITEM_ACTIONS.EXECUTE_ALL_CELLS_BELOW)}
            >
              Run All Cells Below
            </MenuItem>
            <Divider />
            <SubMenu key={MENUS.CELL_CREATE_CELL} title="New Cell">
              <MenuItem
                key={createActionKey(MENU_ITEM_ACTIONS.CREATE_CODE_CELL)}
              >
                Code
              </MenuItem>
              <MenuItem
                key={createActionKey(MENU_ITEM_ACTIONS.CREATE_MARKDOWN_CELL)}
              >
                Markdown
              </MenuItem>
            </SubMenu>
            <Divider />

            <MenuItem
              key={createActionKey(MENU_ITEM_ACTIONS.CLEAR_ALL_OUTPUTS)}
            >
              Clear All Outputs
            </MenuItem>

            <MenuItem key={createActionKey(MENU_ITEM_ACTIONS.UNHIDE_ALL_CELLS)}>
              Unhide All Input and Output
            </MenuItem>
          </SubMenu>

          <SubMenu key={MENUS.RUNTIME} title="Runtime">
            <MenuItem key={createActionKey(MENU_ITEM_ACTIONS.INTERRUPT_KERNEL)}>
              Interrupt
            </MenuItem>
            <Divider />
            <MenuItem key={createActionKey(MENU_ITEM_ACTIONS.KILL_KERNEL)}>
              Halt
            </MenuItem>
            <MenuItem key={createActionKey(MENU_ITEM_ACTIONS.RESTART_KERNEL)}>
              Restart
            </MenuItem>
            <MenuItem
              key={createActionKey(MENU_ITEM_ACTIONS.RESTART_AND_CLEAR_OUTPUTS)}
            >
              Restart and Clear All Cells
            </MenuItem>
            {/*
              <Divider />
              <SwitchToKernelSpecsMenu />
            */}
          </SubMenu>

          <SubMenu key={MENUS.HELP} title="Help">
            <MenuItem key={createActionKey(MENU_ITEM_ACTIONS.OPEN_ABOUT)}>
              About
            </MenuItem>
          </SubMenu>
        </Menu>

    );
  }
}

// TODO: this forces the menu to re-render on cell focus. The better option is
// to alter how the actions work to just target the currently focused cell.
// That said, we *may* not have a great way of getting around this as we'll need
// information about the current document to decide which menu items are
// available...
const mapStateToProps = state => ({
  cellFocused: selectors.currentFocusedCellId(state),
  filename: selectors.currentFilename(state),
  notebook: selectors.currentNotebook(state),
  currentKernelRef: selectors.currentKernelRef(state)
});

const mapDispatchToProps = dispatch => ({
  saveNotebook: () => dispatch(actions.save()),
  executeCell: cellId => dispatch(actions.executeCell(cellId)),
  executeAllCells: () => dispatch(actions.executeAllCells()),
  executeAllCellsBelow: () => dispatch(actions.executeAllCellsBelow()),
  clearAllOutputs: () => dispatch(actions.clearAllOutputs()),
  unhideAllCells: payload => dispatch(actions.unhideAll(payload)),
  cutCell: cellId => dispatch(actions.cutCell(cellId)),
  copyCell: cellId => dispatch(actions.copyCell(cellId)),
  pasteCell: () => dispatch(actions.pasteCell()),
  mergeCellAfter: cellId => dispatch(actions.mergeCellAfter(cellId)),
  createCodeCell: cellId => dispatch(actions.createCellAfter("code", cellId)),
  createMarkdownCell: cellId =>
    dispatch(actions.createCellAfter("markdown", cellId)),
  setCellTypeCode: cellId => dispatch(actions.changeCellType(cellId, "code")),
  setCellTypeMarkdown: cellId =>
    dispatch(actions.changeCellType(cellId, "markdown")),
  setTheme: theme => dispatch(actions.setTheme(theme)),
  openAboutModal: () =>
    dispatch(actions.openModal({ modalType: MODAL_TYPES.ABOUT })),
  restartKernel: payload => dispatch(actions.restartKernel(payload)),
  restartKernelAndClearOutputs: payload =>
    dispatch(actions.restartKernel({ ...payload, clearOutputs: true })),
  killKernel: payload => dispatch(actions.killKernel(payload)),
  interruptKernel: payload => dispatch(actions.interruptKernel(payload))
});

const NotebookMenu = connect(mapStateToProps, mapDispatchToProps)(
  PureNotebookMenu
);

// We export this for testing purposes.
export { PureNotebookMenu };

export default NotebookMenu;
<|endoftext|>