<commit_before>var mapzen = mapzen || {};
mapzen.whosonfirst = mapzen.whosonfirst || {};
mapzen.whosonfirst.leaflet = mapzen.whosonfirst.leaflet || {};

mapzen.whosonfirst.leaflet.tangram = (function(){

	var _scenefile = '/spelunker/static/tangram/refill.yaml'
	var _cache = {};

	var self = {

		'map_with_bbox': function(id, swlat, swlon, nelat, nelon){

			if ((swlat == nelat) && (swlon == nelon)){
				return self.map_with_latlon(id, swlat, swlon, 14);
			}

			var map = self.map(id);
			map.fitBounds([[swlat, swlon], [ nelat, nelon ]]);

			return map;
		},

		'map_with_latlon': function(id, lat, lon, zoom){

			var map = self.map(id);
			map.setView([ lat , lon ], zoom);

			return map;
		},

		'map': function(id){

			if (! _cache[id]){
				var map = L.map(id);
				map.scrollWheelZoom.disable();

				var tangram = self.tangram();
				tangram.addTo(map);

				_cache[id] = map;
			}

			return _cache[id];
		},

		'tangram': function(scene){

			var scenefile = self.scenefile();

			var attributions = self.attributions();
			var attribution = self.render_attributions(attributions);

			var tangram = Tangram.leafletLayer({
				scene: scenefile,
				numWorkers: 2,
        			unloadInvisibleTiles: false,
				updateWhenIdle: false,
				attribution: attribution,
			});

			return tangram;
		},

		'scenefile': function(url){

			if (url){
				_scenefile = url;
			}

			return _scenefile;
		},

		'attributions': function(){

			var attributions = {
				'Tangram': 'https://mapzen.com/tangram',
				'© OSM contributors': 'http://www.openstreetmap.org/',
				'Who\'s On First': 'http://whosonfirst.mapzen.com/',
				'Mapzen': 'https://mapzen.com/',
			};

			return attributions;
		},

		'render_attributions': function(attrs){

			var parts = [];

			for (var label in attrs){

				var link = attrs[label];

				var enc_label = mapzen.whosonfirst.php.htmlspecialchars(label);

				if (! link){
					parts.push(enc_label);
					continue;
				}

				var anchor = '' + enc_label + '';
				parts.push(anchor);
			}

			return parts.join(" | ");
		},

		'scene': function(id){

			var m = self.map(id);
			var s = undefined;

			m.eachLayer(function(l){

					if (s){
						return;
					}

					if (! l.scene){
						return;
					}

					if (l.scene.gl) {
						s = l.scene;
					}
			});

			return s;
		},

		// requires https://github.com/eligrey/FileSaver.js
		// so commented out for now until I can add suitable
		// checks and error handling (20160126/thisisaaronland)

		/*
		'screenshot_as_file': function(){

			var fname = 'tangram-' + (+new Date()) + '.png';

			var callback = function(sh){
				saveAs(sh.blob, fname);
			};

			self.screenshot(callback);
		},
		*/

		// requires https://github.com/tangrams/tangram/releases/tag/v0.5.0

		'screenshot': function(id, on_screenshot){

			if (! on_screenshot){

				on_screenshot = function(sh) {
					window.open(sh.url);
				};
			}

			var scene = self.scene(id);

			if (! scene){
				console.log("failed to retrieve scene");
				return false;
			}

			scene.screenshot().then(on_screenshot);
			return true;
		}
	};

	return self;
})();
<commit_msg>conditional hooks for screenshot_as_file<commit_after>var mapzen = mapzen || {};
mapzen.whosonfirst = mapzen.whosonfirst || {};
mapzen.whosonfirst.leaflet = mapzen.whosonfirst.leaflet || {};

mapzen.whosonfirst.leaflet.tangram = (function(){

	var _scenefile = '/spelunker/static/tangram/refill.yaml'
	var _cache = {};

	var self = {

		'map_with_bbox': function(id, swlat, swlon, nelat, nelon){

			if ((swlat == nelat) && (swlon == nelon)){
				return self.map_with_latlon(id, swlat, swlon, 14);
			}

			var map = self.map(id);
			map.fitBounds([[swlat, swlon], [ nelat, nelon ]]);

			return map;
		},

		'map_with_latlon': function(id, lat, lon, zoom){

			var map = self.map(id);
			map.setView([ lat , lon ], zoom);

			return map;
		},

		'map': function(id){

			if (! _cache[id]){
				var map = L.map(id);
				map.scrollWheelZoom.disable();

				var tangram = self.tangram();
				tangram.addTo(map);

				_cache[id] = map;
			}

			return _cache[id];
		},

		'tangram': function(scene){

			var scenefile = self.scenefile();

			var attributions = self.attributions();
			var attribution = self.render_attributions(attributions);

			var tangram = Tangram.leafletLayer({
				scene: scenefile,
				numWorkers: 2,
        			unloadInvisibleTiles: false,
				updateWhenIdle: false,
				attribution: attribution,
			});

			return tangram;
		},

		'scenefile': function(url){

			if (url){
				_scenefile = url;
			}

			return _scenefile;
		},

		'attributions': function(){

			var attributions = {
				'Tangram': 'https://mapzen.com/tangram',
				'© OSM contributors': 'http://www.openstreetmap.org/',
				'Who\'s On First': 'http://whosonfirst.mapzen.com/',
				'Mapzen': 'https://mapzen.com/',
			};

			return attributions;
		},

		'render_attributions': function(attrs){

			var parts = [];

			for (var label in attrs){

				var link = attrs[label];

				var enc_label = mapzen.whosonfirst.php.htmlspecialchars(label);

				if (! link){
					parts.push(enc_label);
					continue;
				}

				var anchor = '' + enc_label + '';
				parts.push(anchor);
			}

			return parts.join(" | ");
		},

		'scene': function(id){

			var m = self.map(id);
			var s = undefined;

			m.eachLayer(function(l){

					if (s){
						return;
					}

					if (! l.scene){
						return;
					}

					if (l.scene.gl) {
						s = l.scene;
					}
			});

			return s;
		},

		// requires https://github.com/eligrey/FileSaver.js

		'screenshot_as_file': function(id){

			if (typeof(saveAs) == "undefined"){
				console.log("missing 'saveAs' controls");
				return false
			}

			var fname = 'whosonfirst-spelunker-' + (+new Date()) + '.png';

			var callback = function(sh){
				saveAs(sh.blob, fname);
			};

			return self.screenshot(id, callback);
		},

		// requires https://github.com/tangrams/tangram/releases/tag/v0.5.0

		'screenshot': function(id, on_screenshot){

			if (! on_screenshot){

				on_screenshot = function(sh) {
					window.open(sh.url);
				};
			}

			var scene = self.scene(id);

			if (! scene){
				console.log("failed to retrieve scene");
				return false;
			}

			scene.screenshot().then(on_screenshot);
			return true;
		}
	};

	return self;
})();
<|endoftext|>


<commit_before>steal('can/util',
	  'can/view',
	  'can/view/scanner.js',
	  'can/observe/compute',
	  'can/view/render.js',
function( can ){

	can.view.ext = ".mustache";

	var extend = can.extend,
		CONTEXT = '___c0nt3xt',
		isObserve = function(obj) {
			return can.isFunction(obj.attr) && obj.constructor && obj.constructor.canMakeObserve;
		};
		Mustache = function( options ) {
			// Supports calling Mustache without the constructor
			// This returns a function that renders the template.
			if ( this.constructor != Mustache ) {
				var mustache = new Mustache(options);
				return function( data, helpers ) {
					 return mustache.render(data, helpers);
				};
			}

			// If we get a `function` directly, it probably is coming from
			// a `steal`-packaged view.
			if ( typeof options == "function" ) {
				this.template = {
					fn: options
				};
				return;
			}

			// Set options on self.
			extend(this, options);
			this.template = this.scanner.scan(this.text, this.name);
		};

	can.Mustache = window.Mustache = Mustache;

	/**
	 * @Prototype
	 */
	Mustache.prototype.
	/**
	 * Renders an object with view helpers attached to the view.
	 *
	 *		 new Mustache({text: "<%= message %>"}).render({
	 *			 message: "foo"
	 *		 },{helper: function(){ ... }})
	 *
	 * @param {Object} object data to be rendered
	 * @param {Object} [extraHelpers] an object with view helpers
	 * @return {String} returns the result of the string
	 */
	render = function( object, extraHelpers ) {
		object = object || {};
		return this.template.fn.call(object, object, new Mustache.Helpers(object, extraHelpers || {}));
	};

	extend(Mustache.prototype, {
		/**
		 * Singleton scanner instance for parsing templates.
		 */
		scanner: new can.view.Scanner({
			/**
			 * Inject additional start text.
			 */
			startTxt: 'var ' + CONTEXT + ' = {};',

			/**
			 * An ordered token registry for the scanner.
			 * This needs to be ordered by priority to prevent token parsing errors.
			 * Each token is defined as: ["token-name", "string representation", "optional regexp override"]
			 */
			tokens: [
				["templateLeft", "{{$"], // Template	 ---- Not supported
				["templateRight", "$}}"], // Right Template	---- Not supported
				["returnLeft", "{{{", "{{[{&]"], // Return Unescaped
				// ["commentFull", "{{!}}", "[\\s\\t]*{{!.+?}}\\n?"], // Comment
				["commentLeft", "{{!", "(\\n[\\s\\t]*{{!|{{!)"], // Comment
				["left", "{{~"], // Run
				["escapeLeft", "{{"], // Return Escaped
				["returnRight", "}}}"],
				["right", "}}"] // Right -> All have same FOR Mustache ...
			],

			helpers: [
				/**
				 * Partials
				 * Partials begin with a greater than sign, like {{> box}}.
				 *
				 * Partials are rendered at runtime (as opposed to compile time),
				 * so recursive partials are possible. Just avoid infinite loops.
				 *
				 * For example, this template and partial:
				 *
				 * 		base.mustache:
				 *
Names

				 * 			{{#names}}
				 * 				{{> user}}
				 * 			{{/names}}
				 *
				 * 		user.mustache:
				 * 			{{name}}
				 */
				{
					name: /^>[\s|\w]\w*/,
					fn:function(content, cmd){
						return "can.view.render('" + content.replace(/^>\s?/, '').trim() + "', can.extend({}, " + CONTEXT + ", this))";
					}
				},

				/**
				 * Convert the expression for use with interpolation/helpers.
				 */
				{
					name: /^.*$/,
					fn: function(content, cmd) {
						// Parse content
						var mode = false,
							result = [],
							content = content.trim();
						if (content.length && (mode = content.match(/^([#^/]|else$)/))) {
							mode = mode[0];
							switch (mode) {
								case '#':
								case '^':
									result.push(cmd.insert + 'can.view.txt(0,\'' + cmd.tagName + '\',' + cmd.status + ',this,function(){ return ');
									break;
								// Close section
								case '/':
									return { raw: '}}])}));' };
									break;
							}
							content = content.substring(1);
						}

						if (mode != 'else') {
							var args = content.replace(/^\s*/,'').replace(/\s*$/,'').split(/\s/),
								i = 0,
								arg, split;
							result.push('can.Mustache.txt(can.extend({}, ' + CONTEXT + ', this),' + (mode ? '"'+mode+'"' : 'null') + ',');

							// Append helper requests as a name string
							if (args.length > 1) {
								i = 1;
								result.push('"' + args[0] + '"');
							}

							// Iterate through the arguments
							for (; arg = args[i]; i++) {
								i > 0 && result.push(',');
								result.push('can.Mustache.get("' + arg.replace(/"/g,'\\"') + '",this,' + CONTEXT + ')');
							}
						}

						// Handle sections
						mode && mode != 'else' && result.push(',[{_:{');
						switch (mode) {
							// Truthy section
							case '#':
								result.push('}},{fn:function(' + CONTEXT + '){');
								break;
							// If/else section
							// Falsey section
							case 'else':
							case '^':
								result.push('}},{inverse:function(' + CONTEXT + '){');
								break;
							// Not a section
							default:
								result.push(');');
								break;
						}

						result = result.join('');
						return mode ? { raw: result } : result;
					}
				}
			]
		})
	});

	/**
	 * Addin default helpers first.  We could prob do this
	 * differently if we didn't 'break' on every match.
	 */
	var helpers = can.view.Scanner.prototype.helpers;
	for (var i = 0; i < helpers.length; i++) {
		Mustache.prototype.scanner.helpers.unshift(helpers[i]);
	};

	Mustache.Helpers = function( data, extras ) {
		this._data = data;
		this._extras = extras;
		extend(this, extras);
	};

	Mustache.registerHelper = function(name, fn){
		this._helpers.push({ name: name, fn: fn });
	};

	Mustache.registerPartial = function(id, text) {
		// Get the renderer function.
		var d = new can.Deferred(),
			type = can.view.types['.mustache'],
			func = type.renderer(id, text);

		// Cache if we are caching.
		if ( can.view.cache ) {
			can.view.cached[id] = d;
			d.__view_id = id;
			can.view.cachedRenderers[id] = func;
		}

		d.resolve(func);
	};

	/**
	 * @param {String} [mode]	The mode to evaluate the section with: # for truthy, ^ for falsey
	 */
	Mustache.txt = function(context, mode, name) {
		var args = Array.prototype.slice.call(arguments, 3),
			options = can.extend.apply(can, [{
					fn: function() {},
					inverse: function() {}
				}].concat(mode ? args.pop() : [])),
			validArgs = args.length > 0 ? args : [name],
			valid = true,
			result = [],
			i, helper;

		// Validate based on the mode if necessary
		if (mode) {
			for (i = 0; i < validArgs.length; i++) {
				if (can.isArray(validArgs[i])) {
					valid = mode == '#' ? valid && !!validArgs[i].length
						: mode == '^' ? valid && !validArgs[i].length
						: valid;
				}
				else {
					valid = mode == '#' ? valid && !!validArgs[i]
						: mode == '^' ? valid && !validArgs[i]
						: valid;
				}
			}
		}

		// If there is more than one argument, it's a helper
		if (args.length > 0) {
			for (i = 0; helper = this._helpers[i]; i++) {
				// Find the correct helper
				if (helper.name == name) {
					args.push({
						fn: can.proxy(options.fn, context),
						inverse: can.proxy(options.inverse, context)
					});
					return helper.fn.apply(context, args) || '';
				}
			}
			return '';
		}
		// Otherwise interpolate like normal
		else if (valid) {
			// Handle truthyness
			switch (mode) {
				case '#':
					// Iterate over arrays
					if (can.isArray(name)) {
						for (i = 0; i < name.length; i++) {
							result.push(options.fn.call(name[i] || {}, context) || '');
						}
						return result.join('');
					}
					// Normal case.
					else {
						return options.fn.call(name || {}, context) || '';
					}
					break;
				case '^':
					return options.inverse.call(name || {}, context) || '';
					break;
				default:
					return '' + (name || '');
					break;
			}
		}
	};

	/**
	 * Resolves a reference for a given object (and then a context if that fails).
	 *	obj = this
	 *	context = { a: true }
	 *	ref = 'a.b.c'
	 *		=> obj.a.b.c || context.a.b.c || ''
	 *
	 * This implements the following Mustache specs:
	 * 	Deeply Nested Contexts
	 *	All elements on the context stack should be accessible.
	 *		{{#bool}}B {{#bool}}C{{/bool}} D{{/bool}}
	 *		{ bool: true }
	 *		=> "B C D"
	 * 	Basic Context Miss Interpolation
	 * 	Failed context lookups should default to empty strings.
	 *  	{{cannot}}
	 *		=> ""
	 * 	Dotted Names - Broken Chains
	 * 	Any falsey value prior to the last part of the name should yield ''.
	 *		{{a.b.c}}
	 *		{ a: { d: 1 } }
	 *		=> ""
	 *
	 * @param {String} ref      The reference to check for on the obj/context.
	 * @param {Object} obj  		The object to use for checking for a reference.
	 * @param {Object} context  The context to use for checking for a reference if it doesn't exist in the object.
	 */
	Mustache.get = function(ref, obj, context) {
		var contexts = [obj, context],
			names = ref.split('.'),
			lastValue, value, name, i, j;

		// Handle "this" references for list iteration: {{.}} or {{this}}
		if (/^\.|this$/.test(ref)) {
			// If context isn't an object, then it was a value passed by a helper so use it as an override.
			if (!/^object|undefined$/.test(typeof context)) {
				return context || '';
			}
			// Otherwise just return the closest object.
			else {
				return obj || context || '';
			}
		}
		// Handle object resolution.
		else {
			for (i = 0; i < contexts.length; i++) {
				// Check the context for the reference
				value = contexts[i];

				// Make sure the context isn't a failed object before diving into it.
				if (value !== undefined) {
					for (j = 0; j < names.length; j++) {
						// Keep running up the tree while there are matches.
						if (typeof value[names[j]] != 'undefined') {
							lastValue = value;
							value = value[name = names[j]];
						}
						// If it's undefined, still match if the parent is an Observe.
						else if (isObserve(value)) {
							lastValue = value;
							name = names[j];
							break;
						}
						else {
							lastValue = value = undefined;
							break;
						}
					}
				}

				// Found a matched reference
				if (value !== undefined) {
					// Add support for observes
					if (isObserve(lastValue)) {
						return lastValue.attr(name);
					}
					// Support functions stored in objects
					else if (can.isFunction(lastValue[name])) {
						return lastValue[name]();
					}
					else {
						return value;
					}
				}
			}
		}

		return '';
	};

	Mustache._helpers = [
		/**
		 * {{#if expr}}
		 *   Do {{something}}
		 * {{else}}
		 *   Do {{nothing}}
		 * {{/if}}
		 */
		{
			name: 'if',
			fn: function(expr, options){
				if (!!expr) {
					return options.fn(this);
				}
				else {
					return options.inverse(this);
				}
			}
		},

		{
			name: 'unless',
			fn: function(expr, options){
				if (!expr) {
					return options.fn(this);
				}
			}
		},

		{
			name: 'each',
			fn: function(expr, options) {
				if (!!expr && expr.length) {
					var result = [];
					for (var i = 0; i < expr.length; i++) {
						result.push(options.fn(expr[i]));
					}
					return result.join('');
				}
			}
		},

		{
			name: 'with',
			fn: function(expr, options){
				if (!!expr) {
					return options.fn(expr);
				}
			}
		}
	];

	/**
	 * Register the view.
	 */
	can.view.register({
		suffix: "mustache",

		contentType: "x-mustache-template",

		// returns a `function` that renders the view.
		script: function( id, src ) {
			return "can.Mustache(function(_CONTEXT,_VIEW) { " + new Mustache({
				text: src,
				name: id
			}).template.out + " })";
		},

		renderer: function( id, text ) {
			return Mustache({
				text: text,
				name: id
			});
		}
	});

});<commit_msg>Fixed an issue where observes couldn't be used as a section in mustache (doesn't support nesting).<commit_after>steal('can/util',
	  'can/view',
	  'can/view/scanner.js',
	  'can/observe/compute',
	  'can/view/render.js',
function( can ){

	can.view.ext = ".mustache";

	var extend = can.extend,
		CONTEXT = '___c0nt3xt',
		isObserve = function(obj) {
			return can.isFunction(obj.attr) && obj.constructor && obj.constructor.canMakeObserve;
		};
		Mustache = function( options ) {
			// Supports calling Mustache without the constructor
			// This returns a function that renders the template.
			if ( this.constructor != Mustache ) {
				var mustache = new Mustache(options);
				return function( data, helpers ) {
					 return mustache.render(data, helpers);
				};
			}

			// If we get a `function` directly, it probably is coming from
			// a `steal`-packaged view.
			if ( typeof options == "function" ) {
				this.template = {
					fn: options
				};
				return;
			}

			// Set options on self.
			extend(this, options);
			this.template = this.scanner.scan(this.text, this.name);
		};

	can.Mustache = window.Mustache = Mustache;

	/**
	 * @Prototype
	 */
	Mustache.prototype.
	/**
	 * Renders an object with view helpers attached to the view.
	 *
	 *		 new Mustache({text: "<%= message %>"}).render({
	 *			 message: "foo"
	 *		 },{helper: function(){ ... }})
	 *
	 * @param {Object} object data to be rendered
	 * @param {Object} [extraHelpers] an object with view helpers
	 * @return {String} returns the result of the string
	 */
	render = function( object, extraHelpers ) {
		object = object || {};
		return this.template.fn.call(object, object, new Mustache.Helpers(object, extraHelpers || {}));
	};

	extend(Mustache.prototype, {
		/**
		 * Singleton scanner instance for parsing templates.
		 */
		scanner: new can.view.Scanner({
			/**
			 * Inject additional start text.
			 */
			startTxt: 'var ' + CONTEXT + ' = {};',

			/**
			 * An ordered token registry for the scanner.
			 * This needs to be ordered by priority to prevent token parsing errors.
			 * Each token is defined as: ["token-name", "string representation", "optional regexp override"]
			 */
			tokens: [
				["templateLeft", "{{$"], // Template	 ---- Not supported
				["templateRight", "$}}"], // Right Template	---- Not supported
				["returnLeft", "{{{", "{{[{&]"], // Return Unescaped
				// ["commentFull", "{{!}}", "[\\s\\t]*{{!.+?}}\\n?"], // Comment
				["commentLeft", "{{!", "(\\n[\\s\\t]*{{!|{{!)"], // Comment
				["left", "{{~"], // Run
				["escapeLeft", "{{"], // Return Escaped
				["returnRight", "}}}"],
				["right", "}}"] // Right -> All have same FOR Mustache ...
			],

			helpers: [
				/**
				 * Partials
				 * Partials begin with a greater than sign, like {{> box}}.
				 *
				 * Partials are rendered at runtime (as opposed to compile time),
				 * so recursive partials are possible. Just avoid infinite loops.
				 *
				 * For example, this template and partial:
				 *
				 * 		base.mustache:
				 *
Names

				 * 			{{#names}}
				 * 				{{> user}}
				 * 			{{/names}}
				 *
				 * 		user.mustache:
				 * 			{{name}}
				 */
				{
					name: /^>[\s|\w]\w*/,
					fn:function(content, cmd){
						return "can.view.render('" + content.replace(/^>\s?/, '').trim() + "', can.extend({}, " + CONTEXT + ", this))";
					}
				},

				/**
				 * Convert the expression for use with interpolation/helpers.
				 */
				{
					name: /^.*$/,
					fn: function(content, cmd) {
						// Parse content
						var mode = false,
							result = [],
							content = content.trim();
						if (content.length && (mode = content.match(/^([#^/]|else$)/))) {
							mode = mode[0];
							switch (mode) {
								case '#':
								case '^':
									result.push(cmd.insert + 'can.view.txt(0,\'' + cmd.tagName + '\',' + cmd.status + ',this,function(){ return ');
									break;
								// Close section
								case '/':
									return { raw: 'return ___v1ew.join("");}}])}));' };
									break;
							}
							content = content.substring(1);
						}

						if (mode != 'else') {
							var args = content.replace(/^\s*/,'').replace(/\s*$/,'').split(/\s/),
								i = 0,
								arg, split;
							result.push('can.Mustache.txt(can.extend({}, ' + CONTEXT + ', this),' + (mode ? '"'+mode+'"' : 'null') + ',');

							// Append helper requests as a name string
							if (args.length > 1) {
								i = 1;
								result.push('"' + args[0] + '"');
							}

							// Iterate through the arguments
							for (; arg = args[i]; i++) {
								i > 0 && result.push(',');
								result.push('can.Mustache.get("' + arg.replace(/"/g,'\\"') + '",this,' + CONTEXT + ')');
							}
						}

						// Handle sections
						mode && mode != 'else' && result.push(',[{_:function(){');
						switch (mode) {
							// Truthy section
							case '#':
								result.push('return ___v1ew.join("");}},{fn:function(' + CONTEXT + '){var ___v1ew = [];');
								break;
							// If/else section
							// Falsey section
							case 'else':
							case '^':
								result.push('return ___v1ew.join("");}},{inverse:function(' + CONTEXT + '){var ___v1ew = [];');
								break;
							// Not a section
							default:
								result.push(');');
								break;
						}

						result = result.join('');
						return mode ? { raw: result } : result;
					}
				}
			]
		})
	});

	/**
	 * Addin default helpers first.  We could prob do this
	 * differently if we didn't 'break' on every match.
	 */
	var helpers = can.view.Scanner.prototype.helpers;
	for (var i = 0; i < helpers.length; i++) {
		Mustache.prototype.scanner.helpers.unshift(helpers[i]);
	};

	Mustache.Helpers = function( data, extras ) {
		this._data = data;
		this._extras = extras;
		extend(this, extras);
	};

	Mustache.registerHelper = function(name, fn){
		this._helpers.push({ name: name, fn: fn });
	};

	Mustache.registerPartial = function(id, text) {
		// Get the renderer function.
		var d = new can.Deferred(),
			type = can.view.types['.mustache'],
			func = type.renderer(id, text);

		// Cache if we are caching.
		if ( can.view.cache ) {
			can.view.cached[id] = d;
			d.__view_id = id;
			can.view.cachedRenderers[id] = func;
		}

		d.resolve(func);
	};

	/**
	 * @param {String} [mode]	The mode to evaluate the section with: # for truthy, ^ for falsey
	 */
	Mustache.txt = function(context, mode, name) {
		var args = Array.prototype.slice.call(arguments, 3),
			options = can.extend.apply(can, [{
					fn: function() {},
					inverse: function() {}
				}].concat(mode ? args.pop() : [])),
			validArgs = args.length > 0 ? args : [name],
			valid = true,
			result = [],
			i, helper;

		// Validate based on the mode if necessary
		if (mode) {
			for (i = 0; i < validArgs.length; i++) {
				if (can.isArray(validArgs[i])) {
					valid = mode == '#' ? valid && !!validArgs[i].length
						: mode == '^' ? valid && !validArgs[i].length
						: valid;
				}
				else {
					valid = mode == '#' ? valid && !!validArgs[i]
						: mode == '^' ? valid && !validArgs[i]
						: valid;
				}
			}
		}

		// If there is more than one argument, it's a helper
		if (args.length > 0) {
			for (i = 0; helper = this._helpers[i]; i++) {
				// Find the correct helper
				if (helper.name == name) {
					args.push({
						fn: can.proxy(options.fn, context),
						inverse: can.proxy(options.inverse, context)
					});
					return helper.fn.apply(context, args) || '';
				}
			}
			return '';
		}
		// Otherwise interpolate like normal
		else if (valid) {
			// Handle truthyness
			switch (mode) {
				case '#':
					// Iterate over arrays
					if (can.isArray(name)) {
						for (i = 0; i < name.length; i++) {
							result.push(options.fn.call(name[i] || {}, context) || '');
						}
						return result.join('');
					}
					// Normal case.
					else {
						return options.fn.call(name || {}, context) || '';
					}
					break;
				case '^':
					return options.inverse.call(name || {}, context) || '';
					break;
				default:
					return '' + (name || '');
					break;
			}
		}
	};

	/**
	 * Resolves a reference for a given object (and then a context if that fails).
	 *	obj = this
	 *	context = { a: true }
	 *	ref = 'a.b.c'
	 *		=> obj.a.b.c || context.a.b.c || ''
	 *
	 * This implements the following Mustache specs:
	 * 	Deeply Nested Contexts
	 *	All elements on the context stack should be accessible.
	 *		{{#bool}}B {{#bool}}C{{/bool}} D{{/bool}}
	 *		{ bool: true }
	 *		=> "B C D"
	 * 	Basic Context Miss Interpolation
	 * 	Failed context lookups should default to empty strings.
	 *  	{{cannot}}
	 *		=> ""
	 * 	Dotted Names - Broken Chains
	 * 	Any falsey value prior to the last part of the name should yield ''.
	 *		{{a.b.c}}
	 *		{ a: { d: 1 } }
	 *		=> ""
	 *
	 * @param {String} ref      The reference to check for on the obj/context.
	 * @param {Object} obj  		The object to use for checking for a reference.
	 * @param {Object} context  The context to use for checking for a reference if it doesn't exist in the object.
	 */
	Mustache.get = function(ref, obj, context) {
		var contexts = [obj, context],
			names = ref.split('.'),
			lastValue, value, name, i, j;

		// Handle "this" references for list iteration: {{.}} or {{this}}
		if (/^\.|this$/.test(ref)) {
			// If context isn't an object, then it was a value passed by a helper so use it as an override.
			if (!/^object|undefined$/.test(typeof context)) {
				return context || '';
			}
			// Otherwise just return the closest object.
			else {
				return obj || context || '';
			}
		}
		// Handle object resolution.
		else {
			for (i = 0; i < contexts.length; i++) {
				// Check the context for the reference
				value = contexts[i];

				// Make sure the context isn't a failed object before diving into it.
				if (value !== undefined) {
					for (j = 0; j < names.length; j++) {
						// Keep running up the tree while there are matches.
						if (typeof value[names[j]] != 'undefined') {
							lastValue = value;
							value = value[name = names[j]];
						}
						// If it's undefined, still match if the parent is an Observe.
						else if (isObserve(value)) {
							lastValue = value;
							name = names[j];
							break;
						}
						else {
							lastValue = value = undefined;
							break;
						}
					}
				}

				// Found a matched reference
				if (value !== undefined) {
					// Add support for observes
					if (isObserve(lastValue)) {
						return lastValue.attr(name);
					}
					// Support functions stored in objects
					else if (can.isFunction(lastValue[name])) {
						return lastValue[name]();
					}
					else {
						return value;
					}
				}
			}
		}

		return '';
	};

	Mustache._helpers = [
		/**
		 * {{#if expr}}
		 *   Do {{something}}
		 * {{else}}
		 *   Do {{nothing}}
		 * {{/if}}
		 */
		{
			name: 'if',
			fn: function(expr, options){
				if (!!expr) {
					return options.fn(this);
				}
				else {
					return options.inverse(this);
				}
			}
		},

		{
			name: 'unless',
			fn: function(expr, options){
				if (!expr) {
					return options.fn(this);
				}
			}
		},

		{
			name: 'each',
			fn: function(expr, options) {
				if (!!expr && expr.length) {
					var result = [];
					for (var i = 0; i < expr.length; i++) {
						result.push(options.fn(expr[i]));
					}
					return result.join('');
				}
			}
		},

		{
			name: 'with',
			fn: function(expr, options){
				if (!!expr) {
					return options.fn(expr);
				}
			}
		}
	];

	/**
	 * Register the view.
	 */
	can.view.register({
		suffix: "mustache",

		contentType: "x-mustache-template",

		// returns a `function` that renders the view.
		script: function( id, src ) {
			return "can.Mustache(function(_CONTEXT,_VIEW) { " + new Mustache({
				text: src,
				name: id
			}).template.out + " })";
		},

		renderer: function( id, text ) {
			return Mustache({
				text: text,
				name: id
			});
		}
	});

});<|endoftext|>


<commit_before>odoo.define('website.backend', function (require) {
"use strict";

var core = require('web.core');
var form_common = require('web.form_common');
var form_widgets = require('web.form_widgets'); // required to guarantee that
    // the overrride of fieldtexthtml works

var WidgetWebsiteButton = form_common.AbstractField.extend({
    template: 'WidgetWebsiteButton',
    render_value: function() {
        // Hack to replace false value by unpublished because falsy form fields
        // have classname o_form_field_empty and are hidden
        if (!this.get_value()) {
            this.set_value('unpublished');
        }
        this._super();
        this.$el.toggleClass("published", this.get_value() === true);
        if (this.node.attrs.class) {
            this.$el.addClass(this.node.attrs.class);
        }
    },
});

core.form_widget_registry.add('website_button', WidgetWebsiteButton);

var FieldWidget = form_common.AbstractField.extend(form_common.ReinitializeFieldMixin);

var FieldTextHtmlFrame = FieldWidget.extend({
    template: 'FieldTextHtmlFrame',
    start: function () {
        var self = this;

        this.callback = _.uniqueId('FieldTextHtml_');
        odoo[this.callback+"_editor"] = function (EditorBar) {
            self.on_editor_loaded(EditorBar);
        };
        odoo[this.callback+"_content"] = function () {
            self.on_content_loaded();
        };
        odoo[this.callback+"_downup"] = function (value) {
            self.dirty = true;
            self.internal_set_value(value);
            self.trigger('changed_value');
            self.resize();
        };

        // init jqery objects
        this.$iframe = this.$('iframe');
        this.document = null;
        this.$body = $();
        this.$content = $();

        // init resize
        this.resize = function resize() {
            if (self.get('effective_readonly')) {
               $("body").removeClass("o_form_FieldTextHtml_fullscreen");
            }
            if ($("body").hasClass("o_form_FieldTextHtml_fullscreen")) {
                self.$iframe.css('height', $("body").hasClass('o_form_FieldTextHtml_fullscreen') ? (document.body.clientHeight - self.$iframe.offset().top) + 'px' : '');
            } else {
                self.$iframe.css("height", (self.$body.find("#oe_snippets").length ? 500 : 300) + "px");
            }
        };
        $(window).on('resize', self.resize);

        return this._super();
    },
    get_url: function () {
        var src = this.options.editor_url ? this.options.editor_url+"?" : "/website/field/html?";
        var datarecord = this.view.get_fields_values();

        var attr = {
            'model': this.view.model,
            'field': this.name,
            'res_id': datarecord.id || '',
            'callback': this.callback
        };
        if (this.options.snippets) {
            attr.snippets = this.options.snippets;
        }
        if (!this.get("effective_readonly")) {
            attr.enable_editor = 1;
        }
        if (core.debug) {
            attr.debug = 1;
        }

        for (var k in attr) {
            src += "&"+k+"="+attr[k];
        }

        delete datarecord[this.name];
        src += "&datarecord="+ encodeURIComponent(JSON.stringify(datarecord));

        return src;
    },
    initialize_content: function() {
        if (!this.get("effective_readonly")) {
            this.$iframe = this.$el.find('iframe');
            this.document = null;
            this.$body = $();
            this.$content = $();
            this.dirty = false;
            this.editor = false;
            odoo[this.callback+"_set_value"] = null;
            this.$iframe.attr("src", this.get_url());
        }
    },
    on_content_loaded: function () {
        this.document = this.$iframe.contents()[0];
        this.$body = $("body", this.document);
        this.$content = this.$body.find("#wrapwrap .o_editable:first");
        setTimeout(this.resize,0);
    },
    on_editor_loaded: function (EditorBar) {
        var self = this;
        this.editor = EditorBar;

        $("body").on('click', function (event) {
            if ($("body").hasClass('o_form_FieldTextHtml_fullscreen')) {
                $("body").removeClass("o_form_FieldTextHtml_fullscreen");
                self.$iframe.css('height', '');
                event.preventDefault();
                event.stopPropagation();
            }
        });

        if (this.get('value') && window.odoo[this.callback+"_set_value"] && !(this.$content.prop('innerHTML')||"").length) {
            this.render_value();
        }

        setTimeout(function () {
            var $fullscreen = $('');
            $("#website-top-navbar", self.document).append($fullscreen);
            $fullscreen.on('click', function () {
                $("body").toggleClass("o_form_FieldTextHtml_fullscreen");
                self.resize();
            });
            setTimeout(self.resize,0);
        }, 500);
    },
    render_value: function() {
        var value = (this.get('value') || "").replace(/^
]*>\s*<\/p>$/, '');
        if (!this.get("effective_readonly")) {
            if (!this.$content) {
                return;
            }
            if(window.odoo[this.callback+"_set_value"]) {
                window.odoo[this.callback+"_set_value"](value || '', this.view.get_fields_values(), this.name);
                this.resize();
            }
        } else {
            this.$el.html(value);
        }
    },
    is_false: function() {
        return this.get('value') === false || this.get('value') === "";
    },
    get_value: function(save_mode) {
        if (save_mode && this.editor && this.editor.snippets && this.dirty) {
            this.editor.snippets.clean_for_save();
            this.internal_set_value( this.$content.prop('innerHTML') );
        }
        return this.get('value');
    },
    destroy: function () {
        $("body").removeClass("o_form_FieldTextHtml_fullscreen");
        $(window).off('resize', self.resize);
        $(window).off('keydown', self.escape);
        delete window.odoo[this.callback+"_content"];
        delete window.odoo[this.callback+"_downup"];
        delete window.odoo[this.callback+"_set_value"];
    }
});

core.form_widget_registry.add('html_frame', FieldTextHtmlFrame);

});
<commit_msg>[FIX] : website : Remove hack that was unfortunately publishing all new record         created from the frontend (NB : This fix is compatible with the new webclient)<commit_after>odoo.define('website.backend', function (require) {
"use strict";

var core = require('web.core');
var form_common = require('web.form_common');
var form_widgets = require('web.form_widgets'); // required to guarantee that
    // the overrride of fieldtexthtml works

var WidgetWebsiteButton = form_common.AbstractField.extend({
    template: 'WidgetWebsiteButton',
    render_value: function() {
        this._super();
        this.$el.toggleClass("published", this.get_value() === true);
        if (this.node.attrs.class) {
            this.$el.addClass(this.node.attrs.class);
        }
    },
    is_false: function() {
        return false;
    },
});

core.form_widget_registry.add('website_button', WidgetWebsiteButton);

var FieldWidget = form_common.AbstractField.extend(form_common.ReinitializeFieldMixin);

var FieldTextHtmlFrame = FieldWidget.extend({
    template: 'FieldTextHtmlFrame',
    start: function () {
        var self = this;

        this.callback = _.uniqueId('FieldTextHtml_');
        odoo[this.callback+"_editor"] = function (EditorBar) {
            self.on_editor_loaded(EditorBar);
        };
        odoo[this.callback+"_content"] = function () {
            self.on_content_loaded();
        };
        odoo[this.callback+"_downup"] = function (value) {
            self.dirty = true;
            self.internal_set_value(value);
            self.trigger('changed_value');
            self.resize();
        };

        // init jqery objects
        this.$iframe = this.$('iframe');
        this.document = null;
        this.$body = $();
        this.$content = $();

        // init resize
        this.resize = function resize() {
            if (self.get('effective_readonly')) {
               $("body").removeClass("o_form_FieldTextHtml_fullscreen");
            }
            if ($("body").hasClass("o_form_FieldTextHtml_fullscreen")) {
                self.$iframe.css('height', $("body").hasClass('o_form_FieldTextHtml_fullscreen') ? (document.body.clientHeight - self.$iframe.offset().top) + 'px' : '');
            } else {
                self.$iframe.css("height", (self.$body.find("#oe_snippets").length ? 500 : 300) + "px");
            }
        };
        $(window).on('resize', self.resize);

        return this._super();
    },
    get_url: function () {
        var src = this.options.editor_url ? this.options.editor_url+"?" : "/website/field/html?";
        var datarecord = this.view.get_fields_values();

        var attr = {
            'model': this.view.model,
            'field': this.name,
            'res_id': datarecord.id || '',
            'callback': this.callback
        };
        if (this.options.snippets) {
            attr.snippets = this.options.snippets;
        }
        if (!this.get("effective_readonly")) {
            attr.enable_editor = 1;
        }
        if (core.debug) {
            attr.debug = 1;
        }

        for (var k in attr) {
            src += "&"+k+"="+attr[k];
        }

        delete datarecord[this.name];
        src += "&datarecord="+ encodeURIComponent(JSON.stringify(datarecord));

        return src;
    },
    initialize_content: function() {
        if (!this.get("effective_readonly")) {
            this.$iframe = this.$el.find('iframe');
            this.document = null;
            this.$body = $();
            this.$content = $();
            this.dirty = false;
            this.editor = false;
            odoo[this.callback+"_set_value"] = null;
            this.$iframe.attr("src", this.get_url());
        }
    },
    on_content_loaded: function () {
        this.document = this.$iframe.contents()[0];
        this.$body = $("body", this.document);
        this.$content = this.$body.find("#wrapwrap .o_editable:first");
        setTimeout(this.resize,0);
    },
    on_editor_loaded: function (EditorBar) {
        var self = this;
        this.editor = EditorBar;

        $("body").on('click', function (event) {
            if ($("body").hasClass('o_form_FieldTextHtml_fullscreen')) {
                $("body").removeClass("o_form_FieldTextHtml_fullscreen");
                self.$iframe.css('height', '');
                event.preventDefault();
                event.stopPropagation();
            }
        });

        if (this.get('value') && window.odoo[this.callback+"_set_value"] && !(this.$content.prop('innerHTML')||"").length) {
            this.render_value();
        }

        setTimeout(function () {
            var $fullscreen = $('');
            $("#website-top-navbar", self.document).append($fullscreen);
            $fullscreen.on('click', function () {
                $("body").toggleClass("o_form_FieldTextHtml_fullscreen");
                self.resize();
            });
            setTimeout(self.resize,0);
        }, 500);
    },
    render_value: function() {
        var value = (this.get('value') || "").replace(/^
]*>\s*<\/p>$/, '');
        if (!this.get("effective_readonly")) {
            if (!this.$content) {
                return;
            }
            if(window.odoo[this.callback+"_set_value"]) {
                window.odoo[this.callback+"_set_value"](value || '', this.view.get_fields_values(), this.name);
                this.resize();
            }
        } else {
            this.$el.html(value);
        }
    },
    is_false: function() {
        return this.get('value') === false || this.get('value') === "";
    },
    get_value: function(save_mode) {
        if (save_mode && this.editor && this.editor.snippets && this.dirty) {
            this.editor.snippets.clean_for_save();
            this.internal_set_value( this.$content.prop('innerHTML') );
        }
        return this.get('value');
    },
    destroy: function () {
        $("body").removeClass("o_form_FieldTextHtml_fullscreen");
        $(window).off('resize', self.resize);
        $(window).off('keydown', self.escape);
        delete window.odoo[this.callback+"_content"];
        delete window.odoo[this.callback+"_downup"];
        delete window.odoo[this.callback+"_set_value"];
    }
});

core.form_widget_registry.add('html_frame', FieldTextHtmlFrame);

});
<|endoftext|>


<commit_before>AUI.add('tooltip', function(A) {

var L = A.Lang,
	isString = L.isString,
	isUndefined = L.isUndefined,
	isBoolean = L.isBoolean,

	BL = 'bl',
	TR = 'tr',
	FOCUSED = 'focused',
	CANCEL_HIDE_ON_INTERACTION = 'cancelHideOnInteraction',
	BOUNDING_BOX = 'boundingBox',
	HIDE_DELAY = 'hideDelay',
	BLANK = '',
	ATTR = 'attr',
	TITLE = 'title',
	CURRENT_NODE = 'currentNode',
	SECTION = 'section',
	TRIGGER = 'trigger',
	BODY_CONTENT = 'bodyContent',
	TOOLTIP = 'tooltip';

function Tooltip(config) {
	Tooltip.superclass.constructor.apply(this, arguments);
}

A.mix(Tooltip, {
	NAME: TOOLTIP,

	ATTRS: {
		anim: {
			value: true
		},

		align: {
			value: { node: null, points: [ BL, TR ] }
		},

		cancelHideOnInteraction: {
			value: true,
			validador: isBoolean
		},

		showOn: {
			value: 'mouseover'
		},

		hideOn: {
			value: 'mouseout'
		},

		hideDelay: {
			value: 1500
		},

		title: {
			value: false,
			validador: isBoolean
		}
	}
});

A.extend(Tooltip, A.ContextPanel, {
	/*
	* Lifecycle
	*/
	bindUI: function() {
		var instance = this;
		var boudingBox = instance.get(BOUNDING_BOX);

		boudingBox.on('click', A.bind(instance._cancelHideOnInteraction, instance));
		boudingBox.on('mouseenter', A.bind(instance._cancelHideOnInteraction, instance));
		boudingBox.on('mouseleave', A.bind(instance._invokeHideTask, instance));
		instance.after('focusedChange', A.bind(instance._invokeHideTask, instance));

		Tooltip.superclass.bindUI.apply(instance, arguments);
	},

	/*
	* Methods
	*/
	show: function() {
		var instance = this;
		var bodyContent = instance.get(BODY_CONTENT);

		Tooltip.superclass.show.apply(instance, arguments);

		if (instance.get(TITLE)) {
			instance._loadBodyContentFromTitle( instance.get(CURRENT_NODE) );
		}
	},

	_loadBodyContentFromTitle: function(currentNode) {
		var instance = this;
		var trigger = instance.get(TRIGGER);

		if (!instance._titles) {
			instance._titles = trigger.attr(TITLE);

			// prevent default browser tooltip for title
			trigger.attr(TITLE, BLANK);
		}

		if (currentNode) {
			var index = trigger.indexOf(currentNode);
			var title = instance._titles[index];

			instance.set(BODY_CONTENT, title);
		}
	},

	_invokeHideTask: function() {
		var instance = this;

		if (!instance.get(FOCUSED)) {
			instance._hideTask.delay( instance.get(HIDE_DELAY) );
		}
	},

	/*
	* Listeners
	*/
	_afterBodyChange: function(e) {
		var instance = this;

		Tooltip.superclass._afterBodyChange.apply(this, arguments);

		instance.refreshAlign();
	},

	// cancel hide if the user does some interaction with the tooltip
	// interaction = focus, mouseover
	_cancelHideOnInteraction: function(event) {
		var instance = this;

		if (instance.get(CANCEL_HIDE_ON_INTERACTION)) {
			instance.clearIntervals();
		}

		event.halt();
	}
});

A.Tooltip = Tooltip;

}, '@VERSION', { requires: [ 'context-panel' ] });<commit_msg>Source formatting<commit_after>AUI.add('tooltip', function(A) {

var L = A.Lang,
	isString = L.isString,
	isUndefined = L.isUndefined,
	isBoolean = L.isBoolean,

	BL = 'bl',
	TR = 'tr',
	FOCUSED = 'focused',
	CANCEL_HIDE_ON_INTERACTION = 'cancelHideOnInteraction',
	BOUNDING_BOX = 'boundingBox',
	HIDE_DELAY = 'hideDelay',
	BLANK = '',
	ATTR = 'attr',
	TITLE = 'title',
	CURRENT_NODE = 'currentNode',
	SECTION = 'section',
	TRIGGER = 'trigger',
	BODY_CONTENT = 'bodyContent',
	TOOLTIP = 'tooltip';

function Tooltip(config) {
	Tooltip.superclass.constructor.apply(this, arguments);
}

A.mix(Tooltip, {
	NAME: TOOLTIP,

	ATTRS: {
		anim: {
			value: true
		},

		align: {
			value: { node: null, points: [ BL, TR ] }
		},

		cancelHideOnInteraction: {
			value: true,
			validador: isBoolean
		},

		showOn: {
			value: 'mouseover'
		},

		hideOn: {
			value: 'mouseout'
		},

		hideDelay: {
			value: 1500
		},

		title: {
			value: false,
			validador: isBoolean
		}
	}
});

A.extend(Tooltip, A.ContextPanel, {
	/*
	* Lifecycle
	*/
	bindUI: function() {
		var instance = this;
		var boudingBox = instance.get(BOUNDING_BOX);

		boudingBox.on('click', A.bind(instance._cancelHideOnInteraction, instance));
		boudingBox.on('mouseenter', A.bind(instance._cancelHideOnInteraction, instance));
		boudingBox.on('mouseleave', A.bind(instance._invokeHideTask, instance));
		instance.after('focusedChange', A.bind(instance._invokeHideTask, instance));

		Tooltip.superclass.bindUI.apply(instance, arguments);
	},

	/*
	* Methods
	*/
	show: function() {
		var instance = this;
		var bodyContent = instance.get(BODY_CONTENT);

		Tooltip.superclass.show.apply(instance, arguments);

		if (instance.get(TITLE)) {
			instance._loadBodyContentFromTitle( instance.get(CURRENT_NODE) );
		}
	},

	_loadBodyContentFromTitle: function(currentNode) {
		var instance = this;
		var trigger = instance.get(TRIGGER);

		if (!instance._titles) {
			instance._titles = trigger.attr(TITLE);

			// prevent default browser tooltip for title
			trigger.attr(TITLE, BLANK);
		}

		if (currentNode) {
			var index = trigger.indexOf(currentNode);
			var title = instance._titles[index];

			instance.set(BODY_CONTENT, title);
		}
	},

	_invokeHideTask: function() {
		var instance = this;

		if (!instance.get(FOCUSED)) {
			instance._hideTask.delay( instance.get(HIDE_DELAY) );
		}
	},

	/*
	* Listeners
	*/
	_afterBodyChange: function(e) {
		var instance = this;

		Tooltip.superclass._afterBodyChange.apply(this, arguments);

		// need to refreshAlign() after body change
		instance.refreshAlign();
	},

	// cancel hide if the user does some interaction with the tooltip
	// interaction = focus, mouseover
	_cancelHideOnInteraction: function(event) {
		var instance = this;

		if (instance.get(CANCEL_HIDE_ON_INTERACTION)) {
			instance.clearIntervals();
		}

		event.halt();
	}
});

A.Tooltip = Tooltip;

}, '@VERSION', { requires: [ 'context-panel' ] });<|endoftext|>


<commit_before>

var Jack = {
  /* the Rack env object */
  env : env,

  run : function(app){
    return ('function' == typeof(app)) ? app(Jack.env) : app.call(Jack.env)
  },

  lib : {
    jQuery : function(){
      return Johnson.runtime.readfile('lib/jquery-1.2.6.min.js')
    }
  }
}
<commit_msg>removing unused code.<commit_after>

var Jack = {
  /* the Rack env object */
  env : env,

  run : function(app){
    return ('function' == typeof(app)) ? app(Jack.env) : app.call(Jack.env)
  }
}
<|endoftext|>


<commit_before>
function deleteDouble(points) {
    let finalPoints = [];
    for (let i = 0; i < points.length; i++) {
        if (points[i][0] === points[(i + 1) % points.length][0] && points[i][1] === points[(i + 1) % points.length][1]) {
            continue;
        } else if (points[i][0] === points[(i - 1 + points.length) % points.length][0] && points[i][1] === points[(i - 1 + points.length) % points.length][1]) {
            continue;
        } else if (points[(i + 1) % points.length][0] === points[(i - 1 + points.length) % points.length][0] && points[(i - 1 + points.length) % points.length][1] === points[(i + 1) % points.length][1]) {
            continue; // we don't consider this point only
        } else {
            finalPoints.push(points[i]);
        }
    }
    return finalPoints;
}

function lineBetweenTwoPoints(p1, p2) {
    if (p1[0] === p2[0]) {
        return {a: 0, b: p1[0], vertical: true}; // we store the x of the vertical line into b
    } else {
        const coeffA = (p2[1] - p1[1]) / (p2[0] - p1[0]);
        const coeffB = p1[1] - coeffA * p1[0];
        return {a: coeffA, b: coeffB, vertical: false};
    }
}

function isAtTheRightOfTheLine(x, y, line, height) {
    if (line.vertical === true) {
        return (line.b <= x);
    } else {
        if (line.a === 0) {
            return false;
        } else {
            const xline = (y - line.b) / line.a;
            return (xline < x && xline >= 0 && xline <= height);
        }
    }
}

/**
 * Paint a polygon defined by an array of points.
 * @memberof Image
 * @instance
 * @param {Array<Array<number>>} points - Array of [x,y] points
 * @param {object} [options]
 * @param {Array<number>} [options.color=[max,0,0]] - Array of 3 elements (R, G, B), default is red.
 * @param {Array<number>} [options.filled=true] - If you want the polygon to be filled or not.
 * @return {this} The original painted image
 */
export default function paintPolygon(points, options = {}) {
    let {
        color = [this.maxValue, 0, 0],
        filled = false
    } = options;

    this.checkProcessable('paintPoints', {
        bitDepth: [8, 16]
    });

    options.closed = true;

    let filteredPoints = deleteDouble(points);
    if (filled === false) {
        return this.paintPolyline(points, options);
    } else {
        let matrixBinary = Array(this.height);
        for (let i = 0; i < this.height; i++) {
            matrixBinary[i] = [];
            for (let j = 0; j < this.width; j++) {
                matrixBinary[i].push(0);
            }
        }
        for (let p = 0; p < filteredPoints.length; p++) {
            const line = lineBetweenTwoPoints(filteredPoints[p], filteredPoints[(p + 1) % filteredPoints.length]);
            for (let y = 0; y < this.height; y++) {
                for (let x = 0; x < this.width; x++) {
                    if (isAtTheRightOfTheLine(x, y, line, this.height)) {
                        matrixBinary[y][x] = (matrixBinary[y][x] === 0) ? 1 : 0;
                    }
                }
            }
        }
        for (let y = 0; y < this.height; y++) {
            for (let x = 0; x < this.width; x++) {
                if (matrixBinary[y][x] === 1) {
                    let numberChannels = Math.min(this.channels, color.length);
                    let position = (x + y * this.width) * this.channels;
                    for (let channel = 0; channel < numberChannels; channel++) {
                        this.data[position + channel] = color[channel];
                    }
                }
            }
        }
        return this.paintPolyline(points, options);
    }

}
<commit_msg>fix: update doc polygon<commit_after>
function deleteDouble(points) {
    let finalPoints = [];
    for (let i = 0; i < points.length; i++) {
        if (points[i][0] === points[(i + 1) % points.length][0] && points[i][1] === points[(i + 1) % points.length][1]) {
            continue;
        } else if (points[i][0] === points[(i - 1 + points.length) % points.length][0] && points[i][1] === points[(i - 1 + points.length) % points.length][1]) {
            continue;
        } else if (points[(i + 1) % points.length][0] === points[(i - 1 + points.length) % points.length][0] && points[(i - 1 + points.length) % points.length][1] === points[(i + 1) % points.length][1]) {
            continue; // we don't consider this point only
        } else {
            finalPoints.push(points[i]);
        }
    }
    return finalPoints;
}

function lineBetweenTwoPoints(p1, p2) {
    if (p1[0] === p2[0]) {
        return {a: 0, b: p1[0], vertical: true}; // we store the x of the vertical line into b
    } else {
        const coeffA = (p2[1] - p1[1]) / (p2[0] - p1[0]);
        const coeffB = p1[1] - coeffA * p1[0];
        return {a: coeffA, b: coeffB, vertical: false};
    }
}

function isAtTheRightOfTheLine(x, y, line, height) {
    if (line.vertical === true) {
        return (line.b <= x);
    } else {
        if (line.a === 0) {
            return false;
        } else {
            const xline = (y - line.b) / line.a;
            return (xline < x && xline >= 0 && xline <= height);
        }
    }
}

/**
 * Paint a polygon defined by an array of points.
 * @memberof Image
 * @instance
 * @param {Array<Array<number>>} points - Array of [x,y] points
 * @param {object} [options]
 * @param {Array<number>} [options.color=[max,0,0]] - Array of 3 elements (R, G, B), default is red.
 * @param {Array<number>} [options.filled=false] - If you want the polygon to be filled or not.
 * @return {this} The original painted image
 */
export default function paintPolygon(points, options = {}) {
    let {
        color = [this.maxValue, 0, 0],
        filled = false
    } = options;

    this.checkProcessable('paintPoints', {
        bitDepth: [8, 16]
    });

    options.closed = true;

    let filteredPoints = deleteDouble(points);
    if (filled === false) {
        return this.paintPolyline(points, options);
    } else {
        let matrixBinary = Array(this.height);
        for (let i = 0; i < this.height; i++) {
            matrixBinary[i] = [];
            for (let j = 0; j < this.width; j++) {
                matrixBinary[i].push(0);
            }
        }
        for (let p = 0; p < filteredPoints.length; p++) {
            const line = lineBetweenTwoPoints(filteredPoints[p], filteredPoints[(p + 1) % filteredPoints.length]);
            for (let y = 0; y < this.height; y++) {
                for (let x = 0; x < this.width; x++) {
                    if (isAtTheRightOfTheLine(x, y, line, this.height)) {
                        matrixBinary[y][x] = (matrixBinary[y][x] === 0) ? 1 : 0;
                    }
                }
            }
        }
        for (let y = 0; y < this.height; y++) {
            for (let x = 0; x < this.width; x++) {
                if (matrixBinary[y][x] === 1) {
                    let numberChannels = Math.min(this.channels, color.length);
                    let position = (x + y * this.width) * this.channels;
                    for (let channel = 0; channel < numberChannels; channel++) {
                        this.data[position + channel] = color[channel];
                    }
                }
            }
        }
        return this.paintPolyline(points, options);
    }

}
<|endoftext|>


<commit_before>console.log('rq.js');

(function (win) {
	'use strict';

	var modules = {};

	var moduleOutput = {};

	function def(name, factory) {
		console.log('define', name);

		modules[name] = factory;
	}

	function req(name) {
		console.log('request', name);

		if (!moduleOutput[name]) {
			moduleOutput[name] = modules[name]();
		}

		return moduleOutput[name];
	}

	win.def = def;
	win.req = req;
}(window));
<commit_msg>Use Map<commit_after>console.log('rq.js');

(function (win) {
	'use strict';

	var modules = new Map();

	var moduleOutput = new Map();

	function def(name, factory) {
		console.log('define', name);

		modules.set(name, factory);
	}

	function req(name) {
		console.log('request', name);

		if (!moduleOutput.has(name)) {
			moduleOutput.set(name, modules.get(name)());
		}

		return moduleOutput.get(name);
	}

	win.def = def;
	win.req = req;
}(window));
<|endoftext|>


<commit_before>/*
	Masked Input plugin for jQuery
	Copyright (c) 2007-2010 Josh Bush (digitalbush.com)
	Licensed under the MIT license (http://digitalbush.com/projects/masked-input-plugin/#license)
	Version: 1.2.3
*/
(function($) {
	var pasteEventName = ($.browser.msie ? 'paste' : 'input') + ".mask";
	var iPhone = (window.orientation != undefined);

	$.mask = {
		//Predefined character definitions
		definitions: {
			'9': "[0-9]",
			'a': "[A-Za-z]",
			'*': "[A-Za-z0-9]"
		}
	};

	$.fn.extend({
		//Helper Function for Caret positioning
		caret: function(begin, end) {
			if (this.length == 0) return;
			if (typeof begin == 'number') {
				end = (typeof end == 'number') ? end : begin;
				return this.each(function() {
					if (this.setSelectionRange) {
						this.focus();
						this.setSelectionRange(begin, end);
					} else if (this.createTextRange) {
						var range = this.createTextRange();
						range.collapse(true);
						range.moveEnd('character', end);
						range.moveStart('character', begin);
						range.select();
					}
				});
			} else {
				if (this[0].setSelectionRange) {
					begin = this[0].selectionStart;
					end = this[0].selectionEnd;
				} else if (document.selection && document.selection.createRange) {
					var range = document.selection.createRange();
					begin = 0 - range.duplicate().moveStart('character', -100000);
					end = begin + range.text.length;
				}
				return { begin: begin, end: end };
			}
		},
		unmask: function() { return this.trigger("unmask"); },
		mask: function(mask, settings) {
			if (!mask && this.length > 0) {
				var input = $(this[0]);
				var tests = input.data("tests");
				return $.map(input.data("buffer"), function(c, i) {
					return tests[i] ? c : null;
				}).join('');
			}
			settings = $.extend({
				placeholder: "_",
				completed: null
			}, settings);

			var defs = $.mask.definitions;
			var tests = [];
			var partialPosition = mask.length;
			var firstNonMaskPos = null;
			var len = mask.length;

			$.each(mask.split(""), function(i, c) {
				if (c == '?') {
					len--;
					partialPosition = i;
				} else if (defs[c]) {
					tests.push(new RegExp(defs[c]));
					if(firstNonMaskPos==null)
						firstNonMaskPos =  tests.length - 1;
				} else {
					tests.push(null);
				}
			});

			return this.each(function() {
				var input = $(this);
				var buffer = $.map(mask.split(""), function(c, i) { if (c != '?') return defs[c] ? settings.placeholder : c });
				var ignore = false;  			//Variable for ignoring control keys
				var focusText = input.val();

				input.data("buffer", buffer).data("tests", tests);

				function seekNext(pos) {
					while (++pos <= len && !tests[pos]);
					return pos;
				};

				function shiftL(pos) {
					while (!tests[pos] && --pos >= 0);
					for (var i = pos; i < len; i++) {
						if (tests[i]) {
							buffer[i] = settings.placeholder;
							var j = seekNext(i);
							if (j < len && tests[i].test(buffer[j])) {
								buffer[i] = buffer[j];
							} else
								break;
						}
					}
					writeBuffer();
					input.caret(Math.max(firstNonMaskPos, pos));
				};

				function shiftR(pos) {
					for (var i = pos, c = settings.placeholder; i < len; i++) {
						if (tests[i]) {
							var j = seekNext(i);
							var t = buffer[i];
							buffer[i] = c;
							if (j < len && tests[j].test(t))
								c = t;
							else
								break;
						}
					}
				};

				function keydownEvent(e) {
					var pos = $(this).caret();
					var k = e.keyCode;
					ignore = (k < 16 || (k > 16 && k < 32) || (k > 32 && k < 41));

					//delete selection before proceeding
					if ((pos.begin - pos.end) != 0 && (!ignore || k == 8 || k == 46))
						clearBuffer(pos.begin, pos.end);

					//backspace, delete, and escape get special treatment
					if (k == 8 || k == 46 || (iPhone && k == 127)) {//backspace/delete
						shiftL(pos.begin + (k == 46 ? 0 : -1));
						return false;
					} else if (k == 27) {//escape
						input.val(focusText);
						input.caret(0, checkVal());
						return false;
					}
				};

				function keypressEvent(e) {
					if (ignore) {
						ignore = false;
						//Fixes Mac FF bug on backspace
						return (e.keyCode == 8) ? false : null;
					}
					e = e || window.event;
					var k = e.charCode || e.keyCode || e.which;
					var pos = $(this).caret();

					if (e.ctrlKey || e.altKey || e.metaKey) {//Ignore
						return true;
					} else if ((k >= 32 && k <= 125) || k > 186) {//typeable characters
						var p = seekNext(pos.begin - 1);
						if (p < len) {
							var c = String.fromCharCode(k);
							if (tests[p].test(c)) {
								shiftR(p);
								buffer[p] = c;
								writeBuffer();
								var next = seekNext(p);
								$(this).caret(next);
								if (settings.completed && next >= len)
									settings.completed.call(input);
							}
						}
					}
					return false;
				};

				function clearBuffer(start, end) {
					for (var i = start; i < end && i < len; i++) {
						if (tests[i])
							buffer[i] = settings.placeholder;
					}
				};

				function writeBuffer() { return input.val(buffer.join('')).val(); };

				function checkVal(allow) {
					//try to place characters where they belong
					var test = input.val();
					var lastMatch = -1;
					for (var i = 0, pos = 0; i < len; i++) {
						if (tests[i]) {
							buffer[i] = settings.placeholder;
							while (pos++ < test.length) {
								var c = test.charAt(pos - 1);
								if (tests[i].test(c)) {
									buffer[i] = c;
									lastMatch = i;
									break;
								}
							}
							if (pos > test.length)
								break;
						} else if (buffer[i] == test.charAt(pos) && i!=partialPosition) {
							pos++;
							lastMatch = i;
						}
					}
					if (!allow && lastMatch + 1 < partialPosition) {
						input.val("");
						clearBuffer(0, len);
					} else if (allow || lastMatch + 1 >= partialPosition) {
						writeBuffer();
						if (!allow) input.val(input.val().substring(0, lastMatch + 1));
					}
					return (partialPosition ? i : firstNonMaskPos);
				};

				if (!input.attr("readonly"))
					input
					.one("unmask", function() {
						input
							.unbind(".mask")
							.removeData("buffer")
							.removeData("tests");
					})
					.bind("focus.mask", function() {
						focusText = input.val();
						var pos = checkVal();
						writeBuffer();
						setTimeout(function() {
							if (pos == mask.length)
								input.caret(0, pos);
							else
								input.caret(pos);
						}, 0);
					})
					.bind("blur.mask", function() {
						checkVal();
						if (input.val() != focusText)
							input.change();
					})
					.bind("keydown.mask", keydownEvent)
					.bind("keypress.mask", keypressEvent)
					.bind(pasteEventName, function() {
						setTimeout(function() { input.caret(checkVal(true)); }, 0);
					});

				checkVal(); //Perform initial check for existing values
			});
		}
	});
})(jQuery);<commit_msg>Fixing bug when hitting delete key while cursor on top of a literal.<commit_after>/*
	Masked Input plugin for jQuery
	Copyright (c) 2007-2010 Josh Bush (digitalbush.com)
	Licensed under the MIT license (http://digitalbush.com/projects/masked-input-plugin/#license)
	Version: 1.2.3
*/
(function($) {
	var pasteEventName = ($.browser.msie ? 'paste' : 'input') + ".mask";
	var iPhone = (window.orientation != undefined);

	$.mask = {
		//Predefined character definitions
		definitions: {
			'9': "[0-9]",
			'a': "[A-Za-z]",
			'*': "[A-Za-z0-9]"
		}
	};

	$.fn.extend({
		//Helper Function for Caret positioning
		caret: function(begin, end) {
			if (this.length == 0) return;
			if (typeof begin == 'number') {
				end = (typeof end == 'number') ? end : begin;
				return this.each(function() {
					if (this.setSelectionRange) {
						this.focus();
						this.setSelectionRange(begin, end);
					} else if (this.createTextRange) {
						var range = this.createTextRange();
						range.collapse(true);
						range.moveEnd('character', end);
						range.moveStart('character', begin);
						range.select();
					}
				});
			} else {
				if (this[0].setSelectionRange) {
					begin = this[0].selectionStart;
					end = this[0].selectionEnd;
				} else if (document.selection && document.selection.createRange) {
					var range = document.selection.createRange();
					begin = 0 - range.duplicate().moveStart('character', -100000);
					end = begin + range.text.length;
				}
				return { begin: begin, end: end };
			}
		},
		unmask: function() { return this.trigger("unmask"); },
		mask: function(mask, settings) {
			if (!mask && this.length > 0) {
				var input = $(this[0]);
				var tests = input.data("tests");
				return $.map(input.data("buffer"), function(c, i) {
					return tests[i] ? c : null;
				}).join('');
			}
			settings = $.extend({
				placeholder: "_",
				completed: null
			}, settings);

			var defs = $.mask.definitions;
			var tests = [];
			var partialPosition = mask.length;
			var firstNonMaskPos = null;
			var len = mask.length;

			$.each(mask.split(""), function(i, c) {
				if (c == '?') {
					len--;
					partialPosition = i;
				} else if (defs[c]) {
					tests.push(new RegExp(defs[c]));
					if(firstNonMaskPos==null)
						firstNonMaskPos =  tests.length - 1;
				} else {
					tests.push(null);
				}
			});

			return this.each(function() {
				var input = $(this);
				var buffer = $.map(mask.split(""), function(c, i) { if (c != '?') return defs[c] ? settings.placeholder : c });
				var ignore = false;  			//Variable for ignoring control keys
				var focusText = input.val();

				input.data("buffer", buffer).data("tests", tests);

				function seekNext(pos) {
					while (++pos <= len && !tests[pos]);
					return pos;
				};

				function shiftL(pos) {
					while (!tests[pos] && --pos >= 0);
					for (var i = pos; i < len; i++) {
						if (tests[i]) {
							buffer[i] = settings.placeholder;
							var j = seekNext(i);
							if (j < len && tests[i].test(buffer[j])) {
								buffer[i] = buffer[j];
							} else
								break;
						}
					}
					writeBuffer();
					input.caret(Math.max(firstNonMaskPos, pos));
				};

				function shiftR(pos) {
					for (var i = pos, c = settings.placeholder; i < len; i++) {
						if (tests[i]) {
							var j = seekNext(i);
							var t = buffer[i];
							buffer[i] = c;
							if (j < len && tests[j].test(t))
								c = t;
							else
								break;
						}
					}
				};

				function keydownEvent(e) {
					var pos = $(this).caret();
					var k = e.keyCode;
					ignore = (k < 16 || (k > 16 && k < 32) || (k > 32 && k < 41));

					//delete selection before proceeding
					if ((pos.begin - pos.end) != 0 && (!ignore || k == 8 || k == 46))
						clearBuffer(pos.begin, pos.end);

					//backspace, delete, and escape get special treatment
					if (k == 8 || k == 46 || (iPhone && k == 127)) {//backspace/delete
						shiftL(pos.begin + (k == 46 ? (tests[pos.begin]?0:1) : -1));
						return false;
					} else if (k == 27) {//escape
						input.val(focusText);
						input.caret(0, checkVal());
						return false;
					}
				};

				function keypressEvent(e) {
					if (ignore) {
						ignore = false;
						//Fixes Mac FF bug on backspace
						return (e.keyCode == 8) ? false : null;
					}
					e = e || window.event;
					var k = e.charCode || e.keyCode || e.which;
					var pos = $(this).caret();

					if (e.ctrlKey || e.altKey || e.metaKey) {//Ignore
						return true;
					} else if ((k >= 32 && k <= 125) || k > 186) {//typeable characters
						var p = seekNext(pos.begin - 1);
						if (p < len) {
							var c = String.fromCharCode(k);
							if (tests[p].test(c)) {
								shiftR(p);
								buffer[p] = c;
								writeBuffer();
								var next = seekNext(p);
								$(this).caret(next);
								if (settings.completed && next >= len)
									settings.completed.call(input);
							}
						}
					}
					return false;
				};

				function clearBuffer(start, end) {
					for (var i = start; i < end && i < len; i++) {
						if (tests[i])
							buffer[i] = settings.placeholder;
					}
				};

				function writeBuffer() { return input.val(buffer.join('')).val(); };

				function checkVal(allow) {
					//try to place characters where they belong
					var test = input.val();
					var lastMatch = -1;
					for (var i = 0, pos = 0; i < len; i++) {
						if (tests[i]) {
							buffer[i] = settings.placeholder;
							while (pos++ < test.length) {
								var c = test.charAt(pos - 1);
								if (tests[i].test(c)) {
									buffer[i] = c;
									lastMatch = i;
									break;
								}
							}
							if (pos > test.length)
								break;
						} else if (buffer[i] == test.charAt(pos) && i!=partialPosition) {
							pos++;
							lastMatch = i;
						}
					}
					if (!allow && lastMatch + 1 < partialPosition) {
						input.val("");
						clearBuffer(0, len);
					} else if (allow || lastMatch + 1 >= partialPosition) {
						writeBuffer();
						if (!allow) input.val(input.val().substring(0, lastMatch + 1));
					}
					return (partialPosition ? i : firstNonMaskPos);
				};

				if (!input.attr("readonly"))
					input
					.one("unmask", function() {
						input
							.unbind(".mask")
							.removeData("buffer")
							.removeData("tests");
					})
					.bind("focus.mask", function() {
						focusText = input.val();
						var pos = checkVal();
						writeBuffer();
						setTimeout(function() {
							if (pos == mask.length)
								input.caret(0, pos);
							else
								input.caret(pos);
						}, 0);
					})
					.bind("blur.mask", function() {
						checkVal();
						if (input.val() != focusText)
							input.change();
					})
					.bind("keydown.mask", keydownEvent)
					.bind("keypress.mask", keypressEvent)
					.bind(pasteEventName, function() {
						setTimeout(function() { input.caret(checkVal(true)); }, 0);
					});

				checkVal(); //Perform initial check for existing values
			});
		}
	});
})(jQuery);<|endoftext|>


<commit_before>//var program = process.argv[2];

var converter = new Showdown.converter();

var ops = {
	'-' : function(args, env){
		    var total = parseInt(executeExpression(args[0], env));
			for(var i=1;i<args.length;i++){
				total -=  parseInt(executeExpression(args[i], env));
			}
			return total;
		},
	'=' : function(args, env){
			var arg1 = executeExpression(args[0], env)
			var arg2 = executeExpression(args[1], env)
			return (arg1 == arg2);
		},
	'+' : function(args, env){
		    var total = 0;
			for(var i in args){
				total +=  parseInt(executeExpression(args[i], env));
			}
			return total;
		},
	'length' : function(args, env){
		    return executeExpression(args[0], env).content.length;
		},
	'dotPath' : function(args, env){
			var dotPath = executeExpression(args[0], env)
			var json = executeExpression(args[1], env)
		    return json[dotPath];
		},
	'subseq' : function(args, env){
			var start = executeExpression(args[0], env);

			if(args.length == 2){
				var list = executeExpression(args[1], env);
				return list.slice(start);
			} else if(args.length == 3){
				var end = executeExpression(args[1], env);
				var list = executeExpression(args[2], env).content;
				return new List(list.slice(start, end));
			}
		},
	'nth' : function(args, env){
			var pos = executeExpression(args[0], env);
		    var list = executeExpression(args[1], env).content;

		    return list[pos];
		},
	'list' : function(args, env){
		    var list = [];
			for(var i in args){
				list.push(executeExpression(args[i], env));
			}
			return new List(list);
		},
	'begin' : function(args, env){
			for(var i=0;i<args.length-1;i++){
				executeExpression(args[i], env);
			}
			return executeExpression(args[args.length-1], env);
		},
	'if' : function(args, env){
			var bool = executeExpression(args[0], env);
			if(bool == true){
				return executeExpression(args[1], env);
			} else {
				return executeExpression(args[2], env);
			}
		},
	'json' : function(args, env){
			var url = executeExpression(args[0], env);
			var fn = executeExpression(args[1], env);
			$.ajax({
			        async: true,
			        type: "GET",
			        contentType: "application/json; charset=utf-8",
			        dataType: "json",
			        url: url,
			        success: function(jsonData) {
			            fn(new Array(jsonData), env);
			        },
			        error: function(){
			        	//
			        }
			    });

		},
	'import' : function(args, env){
		    var str = "";
			for(var i in args){
				var importedPage = executeExpression(args[i], env);
				var jsonStringData;

				var url = './'+importedPage+'/code';

				$.ajax({
			        async: false,
			        type: "GET",
			        contentType: "application/json; charset=utf-8",
			        dataType: "json",
			        url: url,
			        success: function(jsonData) {
			            jsonStringData = jsonData.data;
			        },
			        error: function(){
			        	jsonStringData = null;
			        }
			    });
				str += jsonStringData;
			}

			if(jsonStringData){
				evalLisp(str, function(){});
			}

			return null;
		},
	'concat' : function(args, env){
		    var str = "";
			for(i in args){
				str += executeExpression(args[i], env);
			}
			return str;
		},
	'defun' : function(args, env){
			var fnName = args.shift();
			var fnArgs = args.shift();
			var fnBody = args.shift();
			var fn = function(params, env){
				for(var i in fnArgs){
					var arg = fnArgs[i];
					var value = executeExpression(params[i], env);
					env[arg] = value;
				}
				return executeExpression(fnBody.slice(), env);
			}
		    ops[fnName] = fn;
		},
	'markdown' : function(args, env){
			return converter.makeHtml(ops['concat'](args, env));
		},
	'print' : function(args, env){
			if(args.length == 1){
				env.out(null, executeExpression(args[0], env));
			} else if(args.length == 2){
				env.out(
					executeExpression(args[0], env),
					executeExpression(args[1], env));
			}
		}
}

function getLambda(args, env){
	var fnArgs = args.shift();
	var fnBody = args.shift();
	return function(params, env){
		for(var i in fnArgs){
			var arg = fnArgs[i];
			var value = executeExpression(params[i], env);
			env[arg] = value;
		}
		return executeExpression(fnBody.slice(), env);
	}
}

function List(content) {
    this.content = content;
}

var trim = String.prototype.trim || function(){
	return (this + '').replace(/^\s+|\s+$/g, '');
};

function isBlank(str) {
    return (!str || /^\s*$/.test(str));
}

function evalLisp(program, callbck){
	return execute(parseExpressions(program), callbck);
}

function execute(expressions, callback){
	var env = {out:callback};
	for(var i in expressions){
		callback(null, executeExpression(expressions[i], env));
	}
}

function executeExpression(stack, env){
	if (stack instanceof List){
		return stack;
	}

	if (!Array.isArray(stack)){
		if(stack in env){
			return env[stack];
		} else {
			return stack;
		}
	}

	stack = stack.slice();
	if(env == null){
		throw new Error("Environment is NULL.");
	}

	var fn = null;
	var args = [];
	var operator = executeExpression(stack.shift(),env);

	if(operator == 'lambda'){
		return getLambda(stack, env);
	}

	var fn;
	if (typeof(operator) == "function"){
		fn = operator;
	} else {
		fn = ops[operator];
	}

	if(env[operator]){
		fn = env[operator];
	}

	if(!fn){
		return function(f, env){
			return f(stack, env);
		}
	}

	for(var i=0;i<stack.length;i++){
		var arg = stack[i];
		if(arg in env){
			arg = env[arg];
		}
		args.push(arg);
	}

	return fn(args, env);
}

function getArgName(arg){
	if(arg.indexOf('.') === -1){
		return arg;
	} else {
		return arg.substr(0, arg.indexOf('.'));
	}
}

function parseExpressions(text){
	text = trim.call(text);

	var token = text.charAt(0);

	if (token != '(') {
		//throw new Error('unexepected token: ' + token);
	}

	var inQuotes = false;

	var stack = [];
	var parens = 0;
	var expression = "";

	while(text.length > 0){
		var token = text.charAt(0);
		text = text.substring(1);

		if (token == '"') {
			inQuotes = !inQuotes;
		}

		if(!inQuotes){
			if (token == '(') {
				parens++;
			} else if (token == ')'){
				parens--;
			}
		}

		expression += token;

		if (parens == '0'){
			if(!isBlank(expression)){
				stack.push(parseExpression(expression));
			}
			expression = "";
		}

	}

	return stack;
}

function parseExpression(text){
	text = trim.call(text);

	if (text.charAt(0) != '(') return text;

	var stack = [];
	var token;
	var tokens = '';
	var comment = false;
	var i = 0;
	var expr;

	while (i < text.length){
		token = text.charAt(i++);

		if (comment & token != '"'){
			tokens += token;
			continue;
		}

		if (token == '(' || token == ')' || (token == ' ' && !comment)){
			if (expr && tokens.length){
				var n = +tokens;
				var stripped = tokens.replace(/^\"|\"$/g, "");
				expr.push(isNaN(n) ? stripped : n);
			}
			tokens = '';
		} else {
			if (token == '"') comment = !comment;
			if (!/\s/.test(token) || comment) tokens += token;
		}

		if (token == '('){

			var previous = expr;
			expr = [];

			if (previous){
				// push the previous expresion to the stack
				stack.push(previous);
				// if expr is not top-level, append the expression
				previous.push(expr);
			}

		} else if (token == ')'){

			// pop one from stack
			var pop = stack.pop();
			// stack is empty, so expr is the top-level expression
			if (!pop) return expr;
			expr = pop;

		}

	}

	//throw new Error('unbalanced parentheses');

};

//execute(parseExpressions(program));<commit_msg>Added sorting.<commit_after>//var program = process.argv[2];

var converter = new Showdown.converter();

function binaryNumOp(args, env, f){
	var arg1 = parseFloat(executeExpression(args[0], env));
	var arg2 = parseFloat(executeExpression(args[1], env));
	return f(arg1, arg2);
}

var ops = {
	'-' : function(args, env){
		    var total = parseInt(executeExpression(args[0], env));
			for(var i=1;i<args.length;i++){
				total -=  parseInt(executeExpression(args[i], env));
			}
			return total;
		},
	'+' : function(args, env){
		    var total = 0;
			for(var i in args){
				total +=  parseInt(executeExpression(args[i], env));
			}
			return total;
		},
	'=' : function(args, env){
			return binaryNumOp(args, env, function(a,b){return a == b});
		},
	'<' : function(args, env){
			return binaryNumOp(args, env, function(a,b){return a < b});
		},
	'<=' : function(args, env){
			return binaryNumOp(args, env, function(a,b){return a <= b});
		},
	'>' : function(args, env){
			return binaryNumOp(args, env, function(a,b){return a > b});
		},
	'>=' : function(args, env){
			return binaryNumOp(args, env, function(a,b){return a >= b});
		},
	'length' : function(args, env){
		    return executeExpression(args[0], env).content.length;
		},
	'dotPath' : function(args, env){
			var dotPath = executeExpression(args[0], env)
			var json = executeExpression(args[1], env)
		    return json[dotPath];
		},
	'subseq' : function(args, env){
			var start = executeExpression(args[0], env);

			if(args.length == 2){
				var list = executeExpression(args[1], env);
				return list.slice(start);
			} else if(args.length == 3){
				var end = executeExpression(args[1], env);
				var list = executeExpression(args[2], env).content;
				return new List(list.slice(start, end));
			}
		},
	'nth' : function(args, env){
			var pos = executeExpression(args[0], env);
		    var list = executeExpression(args[1], env).content;

		    return list[pos];
		},
	'list' : function(args, env){
		    var list = [];
			for(var i in args){
				list.push(executeExpression(args[i], env));
			}
			return new List(list);
		},
	'sort' : function(args, env){
		    var list = executeExpression(args[0], env).content.slice();
		    var sortFn = executeExpression(args[1], env);

			var sort = function(a, b){
				var result = parseInt(sortFn(new Array(a, b), env));
				return result;
			}

			var sortedContent = list.sort(sort);
			var sortedList = new List(sortedContent);

			return sortedList;
		},
	'begin' : function(args, env){
			for(var i=0;i<args.length-1;i++){
				executeExpression(args[i], env);
			}
			return executeExpression(args[args.length-1], env);
		},
	'if' : function(args, env){
			var bool = executeExpression(args[0], env);

			var ret;
			if(bool == true){
				ret = executeExpression(args[1], env);
			} else {
				ret = executeExpression(args[2], env);
			}
			return ret;
		},
	'json' : function(args, env){
			var url = executeExpression(args[0], env);
			var fn = executeExpression(args[1], env);
			$.ajax({
			        async: true,
			        type: "GET",
			        contentType: "application/json; charset=utf-8",
			        dataType: "json",
			        url: url,
			        success: function(jsonData) {
			            fn(new Array(jsonData), env);
			        },
			        error: function(){
			        	//
			        }
			    });

		},
	'import' : function(args, env){
		    var str = "";
			for(var i in args){
				var importedPage = executeExpression(args[i], env);
				var jsonStringData;

				var url = './'+importedPage+'/code';

				$.ajax({
			        async: false,
			        type: "GET",
			        contentType: "application/json; charset=utf-8",
			        dataType: "json",
			        url: url,
			        success: function(jsonData) {
			            jsonStringData = jsonData.data;
			        },
			        error: function(){
			        	jsonStringData = null;
			        }
			    });
				str += jsonStringData;
			}

			if(jsonStringData){
				evalLisp(str, function(){});
			}

			return null;
		},
	'concat' : function(args, env){
		    var str = "";
			for(i in args){
				str += executeExpression(args[i], env);
			}
			return str;
		},
	'defun' : function(args, env){
			var fnName = args.shift();
			var fnArgs = args.shift();
			var fnBody = args.shift();
			var fn = function(params, env){
				for(var i in fnArgs){
					var arg = fnArgs[i];
					var value = executeExpression(params[i], env);
					env[arg] = value;
				}
				return executeExpression(fnBody.slice(), env);
			}
		    ops[fnName] = fn;
		},
	'markdown' : function(args, env){
			return converter.makeHtml(ops['concat'](args, env));
		},
	'print' : function(args, env){
			if(args.length == 1){
				env.out(null, executeExpression(args[0], env));
			} else if(args.length == 2){
				env.out(
					executeExpression(args[0], env),
					executeExpression(args[1], env));
			}
		}
}

function Lambda(args, env){
	var fnArgs = args.shift();
	var fnBody = args.shift();
	return function(params, env){
		for(var i in fnArgs){
			var arg = fnArgs[i];
			var value = executeExpression(params[i], env);
			env[arg] = value;
		}
		return executeExpression(fnBody.slice(), env);
	}
}

function List(content) {
    this.content = content;
}

var trim = String.prototype.trim || function(){
	return (this + '').replace(/^\s+|\s+$/g, '');
};

function isBlank(str) {
    return (!str || /^\s*$/.test(str));
}

function evalLisp(program, callbck){
	return execute(parseExpressions(program), callbck);
}

function execute(expressions, callback){
	var env = {out:callback};
	for(var i in expressions){
		callback(null, executeExpression(expressions[i], env));
	}
}

function executeExpression(stack, env){
	if (stack instanceof List){
		return stack;
	}

	if (!Array.isArray(stack)){
		if(stack in env){
			return env[stack];
		} else {
			return stack;
		}
	}

	stack = stack.slice();
	if(env == null){
		throw new Error("Environment is NULL.");
	}

	var fn = null;
	var args = [];
	var operator = executeExpression(stack.shift(),env);

	if(operator == 'lambda'){
		return new Lambda(stack, env);
	}

	var fn;
	if (typeof(operator) == "function"){
		fn = operator;
	} else {
		fn = ops[operator];
	}

	if(env[operator]){
		fn = env[operator];
	}

	if(!fn){
		return function(f, env){
			return f(stack, env);
		}
	}

	for(var i=0;i<stack.length;i++){
		var arg = stack[i];
		if(arg in env){
			arg = env[arg];
		}
		args.push(arg);
	}

	return fn(args, env);
}

function getArgName(arg){
	if(arg.indexOf('.') === -1){
		return arg;
	} else {
		return arg.substr(0, arg.indexOf('.'));
	}
}

function parseExpressions(text){
	text = trim.call(text);

	var token = text.charAt(0);

	if (token != '(') {
		//throw new Error('unexepected token: ' + token);
	}

	var inQuotes = false;

	var stack = [];
	var parens = 0;
	var expression = "";

	while(text.length > 0){
		var token = text.charAt(0);
		text = text.substring(1);

		if (token == '"') {
			inQuotes = !inQuotes;
		}

		if(!inQuotes){
			if (token == '(') {
				parens++;
			} else if (token == ')'){
				parens--;
			}
		}

		expression += token;

		if (parens == '0'){
			if(!isBlank(expression)){
				stack.push(parseExpression(expression));
			}
			expression = "";
		}

	}

	return stack;
}

function parseExpression(text){
	text = trim.call(text);

	if (text.charAt(0) != '(') return text;

	var stack = [];
	var token;
	var tokens = '';
	var comment = false;
	var i = 0;
	var expr;

	while (i < text.length){
		token = text.charAt(i++);

		if (comment & token != '"'){
			tokens += token;
			continue;
		}

		if (token == '(' || token == ')' || (token == ' ' && !comment)){
			if (expr && tokens.length){
				var n = +tokens;
				var stripped = tokens.replace(/^\"|\"$/g, "");
				expr.push(isNaN(n) ? stripped : n);
			}
			tokens = '';
		} else {
			if (token == '"') comment = !comment;
			if (!/\s/.test(token) || comment) tokens += token;
		}

		if (token == '('){

			var previous = expr;
			expr = [];

			if (previous){
				// push the previous expresion to the stack
				stack.push(previous);
				// if expr is not top-level, append the expression
				previous.push(expr);
			}

		} else if (token == ')'){

			// pop one from stack
			var pop = stack.pop();
			// stack is empty, so expr is the top-level expression
			if (!pop) return expr;
			expr = pop;

		}

	}

	//throw new Error('unbalanced parentheses');

};

//execute(parseExpressions(program));<|endoftext|>