<commit_before>(function(){var _$=function(q){q=q||document;return this.find(q)};_$.extend=function(obj){var original=this.prototype;var extended=obj;for(var key in (extended||{})){original[key]=extended[key]}return original};_$.prototype={elements:[],find:function(q){var ele=[];var qlen=q.length;var list,size;var i,j;for(i=0;i<qlen;i++){if(typeof q[i]=="string"){list=document.querySelectorAll(q[i]);size=list.length;for(j=0;j<size;j++){ele.push(list[j])}}else{if(q[i] instanceof Array){for(var x=0;x<q[i].length;x++){list=document.querySelectorAll(q[i][x]);size=list.length;for(j=0;j<size;j++){ele.push(list[j])}}}else{ele.push(q[i])}}}this.elements=this.elements.concat(this.reduce(ele));return this},reduce:function(el,b){var a=[],i,l=el.length;for(i=0;i<l;i++){if(a.indexOf(el[i],0,b)<0){a.push(el[i])}}return a},removex:function(array,from,to){var rest=array.slice((to||from)+1||array.length);array.length=from<0?array.length+from:from;return array.push.apply(array,rest)},has:function(q){var t=[];this.each(function(el){x$(q).each(function(hel){if(hel==el){t.push(el)}})});this.elements=t;return this},not:function(q){var list=this.elements;for(var i=0;i<list.length;i++){x$(q).each(function(hel){if(list[i]==hel){this.elements=this.removex(list,list.indexOf(list[i]))}})}return this},add:function(q){this.find([q]);this.elements=this.reduce(this.elements);return this},first:function(){return this.get(0)},get:function(index){return this.elements[index]},eq:function(idx1,idx2){var idx2=idx2?idx2+1:idx1+1;this.elements=this.elements.slice(idx1,idx2);return this},size:function(){return this.elements.length},each:function(fn){for(var i=0,len=this.elements.length;i<len;++i){if(fn.call(this,this.elements[i])===false){break}}return this}};var libs=[];var Dom={inner:function(html){return this.html("inner",html)},outer:function(html){return this.html("outer",html)},top:function(html){return this.html("top",html)},bottom:function(html){return this.html("bottom",html)},remove:function(){return this.html("remove")},before:function(html){return this.html("before",html)},after:function(html){return this.html("after",html)},html:function(location,html){var getTag=function(el){if(el.firstChild===null){switch(el.tagName){case"UL":return"LI";case"DL":return"DT";case"TR":return"TD";default:return el.tagName}}return el.firstChild.tagName};var wrap=function(xhtml,tag){var attributes={};var re=/^<([A-Z][A-Z0-9]*)([^>]*)>(.*)<\/\1>/i;if(re.test(xhtml)){result=re.exec(xhtml);tag=result[1];if(result[2]!==""){var attrList=result[2].split(/([A-Z]*\s*=\s*['|"][A-Z0-9:;#\s]*['|"])/i);for(var i=0;i<attrList.length;i++){var attr=attrList[i].replace(/^\s*|\s*$/g,"");if(attr!==""&&attr!==" "){var node=attr.split("=");attributes[node[0]]=node[1].replace(/(["']?)/g,"")}}}xhtml=result[3]}var element=document.createElement(tag);for(var x in attributes){var a=document.createAttribute(x);a.nodeValue=attributes[x];element.setAttributeNode(a)}element.innerHTML=xhtml;return element};this.clean();if(arguments.length==0){return this.elements[0].innerHTML}if(arguments.length==1&&arguments[0]!="remove"){html=location;location="inner"}this.each(function(el){switch(location){case"inner":if(typeof html=="string"){el.innerHTML=html;var list=el.getElementsByTagName("SCRIPT");var len=list.length;for(var i=0;i<len;i++){eval(list[i].text)}}else{el.innerHTML="";el.appendChild(html)}break;case"outer":if(typeof html=="string"){html=wrap(html,getTag(el))}el.parentNode.replaceChild(html,el);break;case"top":if(typeof html=="string"){html=wrap(html,getTag(el))}el.insertBefore(html,el.firstChild);break;case"bottom":if(typeof html=="string"){html=wrap(html,getTag(el))}el.insertBefore(html,null);break;case"remove":var parent=el.parentNode;parent.removeChild(el);break;case"before":var parent=el.parentNode;if(typeof html=="string"){html=wrap(html,getTag(parent))}parent.insertBefore(html,el);break;case"after":var parent=el.parentNode;if(typeof html=="string"){html=wrap(html,getTag(parent))}parent.insertBefore(html,el.nextSibling);break}});return this},clean:function(){var ns=/\S/;this.each(function(el){var d=el,n=d.firstChild,ni=-1;while(n){var nx=n.nextSibling;if(n.nodeType==3&&!ns.test(n.nodeValue)){d.removeChild(n)}else{n.nodeIndex=++ni}n=nx}});return this},attr:function(attribute,val){if(arguments.length==2){this.each(function(el){el.setAttribute(attribute,val)});return this}else{return this.elements[0].getAttribute(attribute)}}};libs.push(Dom);var Event={click:function(fn){return this.on("click",fn)},load:function(fn){return this.on("load",fn)},touchstart:function(fn){return this.on("touchstart",fn)},touchmove:function(fn){return this.on("touchmove",fn)},touchend:function(fn){return this.on("touchend",fn)},touchcancel:function(fn){return this.on("touchcancel",fn)},gesturestart:function(fn){return this.on("gesturestart",fn)},gesturechange:function(fn){return this.on("gesturechange",fn)},gestureend:function(fn){return this.on("gestureend",fn)},orientationchange:function(fn){return this.on("orientationchange",fn)},on:function(type,fn){var listen=function(el){if(window.addEventListener){el.addEventListener(type,fn,false)}};this.each(function(el){listen(el)});return this}};libs.push(Event);var Style={setStyle:function(prop,val){this.each(function(el){el.style[prop]=val});return this},getStyle:function(prop,callback){var gs=function(el,p){return document.defaultView.getComputedStyle(el,"").getPropertyValue(p)};if(callback===undefined){return gs(this.first(),prop)}this.each(function(el){callback(gs(el,prop))});return this},addClass:function(className){var that=this;var hasClass=function(el,className){var re=that.getClassRegEx(className);return re.test(el.className)};this.each(function(el){if(hasClass(el,className)===false){el.className+=" "+className}});return this},removeClass:function(className){if(className===undefined){this.each(function(el){el.className=""})}else{var re=this.getClassRegEx(className);this.each(function(el){el.className=el.className.replace(re," ")})}return this},css:function(o){var that=this;this.each(function(el){for(var prop in o){that.setStyle(prop,o[prop])}});return this||that},reClassNameCache:{},getClassRegEx:function(className){var re=this.reClassNameCache[className];if(!re){re=new RegExp("(?:^|\\s+)"+className+"(?:\\s+|$)");this.reClassNameCache[className]=re}return re}};libs.push(Style);var Fx={tween:function(options,callback){if(options instanceof Array){for(var i=0;i<options.length;i++){this.animationStack.push(options[i])}}else{if(options instanceof Object){this.animationStack.push(options)}}this.start(callback);return this},animationStack:[],start:function(callback){var t=0;var len=this.animationStack.length;for(var i=0;i<this.animationStack.length;i++){var options=this.animationStack[i];var duration=options.duration==undefined?0.5:options.duration;setTimeout(function(s,o,i){s.animate(o);if((i==len-1)&&callback&&typeof (callback)=="function"){callback()}},t*1000*duration,this,options);t+=duration}this.animationStack=[];return this},animate:function(options){var that=this;var opt_after=options.after;var easing=(options.easing===undefined)?"ease-in":options.easing;var before=(options.before===undefined)?function(){}:options.before;var after=(opt_after===undefined)?function(){}:function(){opt_after.apply(that)};var duration=(options.duration===undefined)?0.5:options.duration;var translate=options.by;var rotate=options.rotate;options.easing=options.rotate=options.by=options.before=options.after=options.duration=undefined;before.apply(before.arguments);this.setStyle("-webkit-transition","all "+duration+"s "+easing);this.each(function(el){for(var prop in options){that.setStyle(prop,options[prop])}if(translate){that.setStyle("-webkit-transform",that.translateOp(translate[0],translate[1]))}});setTimeout(function(){that.setStyle("-webkit-transition","none")},duration*1000);setTimeout(function(){that.setStyle("-webkit-transform","none")},duration*1000);setTimeout(after,duration*1000);return this||that},translateOp:function(xPixels,yPixels){return"translate("+xPixels+"px, "+yPixels+"px)"},rotateOp:function(axis,degree){return"rotate"+axis+"("+degree+"deg)"}};libs.push(Fx);var Xhr={xhr:function(url,options){if(options===undefined){options={}}var that=this;var req=new XMLHttpRequest();var method=options.method||"get";var async=options.async||false;var params=options.data||null;req.open(method,url,async);req.onload=(options.callback!=null)?options.callback:function(){that.html(this.responseText)};req.send(params);return this},xhrjson:function(url,options){if(options===undefined){return this}var that=this;var cb=options.callback;if(typeof cb!="function"){cb=function(x){return x}}var callback=function(){var o=eval("("+this.responseText+")");for(var prop in o){x$(options.map[prop]).html(cb(o[prop]))}};options.callback=callback;this.xhr(url,options);return this}};libs.push(Xhr);for(var i=0,size=libs.length;i<size;i++){_$.extend(libs[i])}var xui=window.x$=function(){return new _$(arguments)}})();<commit_msg>regen xui-min.js<commit_after>(function(){var _$=function(q){q=q||document;return this.find(q)};_$.extend=function(obj){var original=this.prototype;var extended=obj;for(var key in (extended||{})){original[key]=extended[key]}return original};_$.prototype={elements:[],find:function(q){var ele=[];var qlen=q.length;var list,size;var i,j;for(i=0;i<qlen;i++){if(typeof q[i]=="string"){list=document.querySelectorAll(q[i]);size=list.length;for(j=0;j<size;j++){ele.push(list[j])}}else{if(q[i] instanceof Array){for(var x=0;x<q[i].length;x++){list=document.querySelectorAll(q[i][x]);size=list.length;for(j=0;j<size;j++){ele.push(list[j])}}}else{ele.push(q[i])}}}this.elements=this.elements.concat(this.reduce(ele));return this},reduce:function(el,b){var a=[],i,l=el.length;for(i=0;i<l;i++){if(a.indexOf(el[i],0,b)<0){a.push(el[i])}}return a},removex:function(array,from,to){var rest=array.slice((to||from)+1||array.length);array.length=from<0?array.length+from:from;return array.push.apply(array,rest)},has:function(q){var t=[];this.each(function(el){x$(q).each(function(hel){if(hel==el){t.push(el)}})});this.elements=t;return this},not:function(q){var list=this.elements;for(var i=0;i<list.length;i++){x$(q).each(function(hel){if(list[i]==hel){this.elements=this.removex(list,list.indexOf(list[i]))}})}return this},add:function(q){this.find([q]);this.elements=this.reduce(this.elements);return this},first:function(){return this.get(0)},get:function(index){return this.elements[index]},eq:function(idx1,idx2){var idx2=idx2?idx2+1:idx1+1;this.elements=this.elements.slice(idx1,idx2);return this},size:function(){return this.elements.length},each:function(fn){for(var i=0,len=this.elements.length;i<len;++i){if(fn.call(this,this.elements[i])===false){break}}return this}};var libs=[];var Dom={inner:function(html){return this.html("inner",html)},outer:function(html){return this.html("outer",html)},top:function(html){return this.html("top",html)},bottom:function(html){return this.html("bottom",html)},remove:function(){return this.html("remove")},before:function(html){return this.html("before",html)},after:function(html){return this.html("after",html)},html:function(location,html){var getTag=function(el){if(el.firstChild===null){switch(el.tagName){case"UL":return"LI";case"DL":return"DT";case"TR":return"TD";default:return el.tagName}}return el.firstChild.tagName};var wrap=function(xhtml,tag){var attributes={};var re=/^<([A-Z][A-Z0-9]*)([^>]*)>(.*)<\/\1>/i;if(re.test(xhtml)){result=re.exec(xhtml);tag=result[1];if(result[2]!==""){var attrList=result[2].split(/([A-Z]*\s*=\s*['|"][A-Z0-9:;#\s]*['|"])/i);for(var i=0;i<attrList.length;i++){var attr=attrList[i].replace(/^\s*|\s*$/g,"");if(attr!==""&&attr!==" "){var node=attr.split("=");attributes[node[0]]=node[1].replace(/(["']?)/g,"")}}}xhtml=result[3]}var element=document.createElement(tag);for(var x in attributes){var a=document.createAttribute(x);a.nodeValue=attributes[x];element.setAttributeNode(a)}element.innerHTML=xhtml;return element};this.clean();if(arguments.length==0){return this.elements[0].innerHTML}if(arguments.length==1&&arguments[0]!="remove"){html=location;location="inner"}this.each(function(el){switch(location){case"inner":if(typeof html=="string"){el.innerHTML=html;var list=el.getElementsByTagName("SCRIPT");var len=list.length;for(var i=0;i<len;i++){eval(list[i].text)}}else{el.innerHTML="";el.appendChild(html)}break;case"outer":if(typeof html=="string"){html=wrap(html,getTag(el))}el.parentNode.replaceChild(html,el);break;case"top":if(typeof html=="string"){html=wrap(html,getTag(el))}el.insertBefore(html,el.firstChild);break;case"bottom":if(typeof html=="string"){html=wrap(html,getTag(el))}el.insertBefore(html,null);break;case"remove":var parent=el.parentNode;parent.removeChild(el);break;case"before":var parent=el.parentNode;if(typeof html=="string"){html=wrap(html,getTag(parent))}parent.insertBefore(html,el);break;case"after":var parent=el.parentNode;if(typeof html=="string"){html=wrap(html,getTag(parent))}parent.insertBefore(html,el.nextSibling);break}});return this},clean:function(){var ns=/\S/;this.each(function(el){var d=el,n=d.firstChild,ni=-1;while(n){var nx=n.nextSibling;if(n.nodeType==3&&!ns.test(n.nodeValue)){d.removeChild(n)}else{n.nodeIndex=++ni}n=nx}});return this},attr:function(attribute,val){if(arguments.length==2){this.each(function(el){el.setAttribute(attribute,val)});return this}else{return this.elements[0].getAttribute(attribute)}}};libs.push(Dom);var Event={click:function(fn){return this.on("click",fn)},load:function(fn){return this.on("load",fn)},touchstart:function(fn){return this.on("touchstart",fn)},touchmove:function(fn){return this.on("touchmove",fn)},touchend:function(fn){return this.on("touchend",fn)},touchcancel:function(fn){return this.on("touchcancel",fn)},gesturestart:function(fn){return this.on("gesturestart",fn)},gesturechange:function(fn){return this.on("gesturechange",fn)},gestureend:function(fn){return this.on("gestureend",fn)},orientationchange:function(fn){return this.on("orientationchange",fn)},on:function(type,fn){var listen=function(el){if(window.addEventListener){el.addEventListener(type,fn,false)}};this.each(function(el){listen(el)});return this}};libs.push(Event);var Style={setStyle:function(prop,val){this.each(function(el){el.style[prop]=val});return this},getStyle:function(prop,callback){var gs=function(el,p){return document.defaultView.getComputedStyle(el,"").getPropertyValue(p)};if(callback===undefined){return gs(this.first(),prop)}this.each(function(el){callback(gs(el,prop))});return this},addClass:function(className){var that=this;var hasClass=function(el,className){var re=that.getClassRegEx(className);return re.test(el.className)};this.each(function(el){if(hasClass(el,className)===false){el.className+=" "+className}});return this},removeClass:function(className){if(className===undefined){this.each(function(el){el.className=""})}else{var re=this.getClassRegEx(className);this.each(function(el){el.className=el.className.replace(re," ")})}return this},css:function(o){var that=this;this.each(function(el){for(var prop in o){that.setStyle(prop,o[prop])}});return this||that},reClassNameCache:{},getClassRegEx:function(className){var re=this.reClassNameCache[className];if(!re){re=new RegExp("(?:^|\\s+)"+className+"(?:\\s+|$)");this.reClassNameCache[className]=re}return re}};libs.push(Style);var Fx={tween:function(options,callback){if(options instanceof Array){for(var i=0;i<options.length;i++){this.animationStack.push(options[i])}}else{if(options instanceof Object){this.animationStack.push(options)}}this.start(callback);return this},animationStack:[],start:function(callback){var t=0;var len=this.animationStack.length;for(var i=0;i<this.animationStack.length;i++){var options=this.animationStack[i];var duration=options.duration===undefined?0.5:options.duration;setTimeout(function(s,o,i){s.animate(o);if((i==len-1)&&callback&&typeof (callback)=="function"){callback()}},t*1000*duration,this,options);t+=duration}this.animationStack=[];return this},animate:function(options){var that=this;var opt_after=options.after;var easing=(options.easing===undefined)?"ease-in":options.easing;var before=(options.before===undefined)?function(){}:options.before;var after=(opt_after===undefined)?function(){}:function(){opt_after.apply(that)};var duration=(options.duration===undefined)?0.5:options.duration;var translate=options.by;var rotate=options.rotate;options.easing=options.rotate=options.by=options.before=options.after=options.duration=undefined;before.apply(before.arguments);this.setStyle("-webkit-transition","all "+duration+"s "+easing);this.each(function(el){for(var prop in options){that.setStyle(prop,options[prop])}if(translate){that.setStyle("-webkit-transform",that.translateOp(translate[0],translate[1]))}});setTimeout(function(){that.setStyle("-webkit-transition","none")},duration*1000);setTimeout(function(){that.setStyle("-webkit-transform","none")},duration*1000);setTimeout(after,duration*1000);return this||that},translateOp:function(xPixels,yPixels){return"translate("+xPixels+"px, "+yPixels+"px)"},rotateOp:function(axis,degree){return"rotate"+axis+"("+degree+"deg)"}};libs.push(Fx);var Xhr={xhr:function(url,options){if(options===undefined){options={}}var that=this;var req=new XMLHttpRequest();var method=options.method||"get";var async=options.async||false;var params=options.data||null;req.open(method,url,async);req.onload=(options.callback!=null)?options.callback:function(){that.html(this.responseText)};req.send(params);return this},xhrjson:function(url,options){if(options===undefined){return this}var that=this;var cb=options.callback;if(typeof cb!="function"){cb=function(x){return x}}var callback=function(){var o=eval("("+this.responseText+")");for(var prop in o){x$(options.map[prop]).html(cb(o[prop]))}};options.callback=callback;this.xhr(url,options);return this}};libs.push(Xhr);for(var i=0,size=libs.length;i<size;i++){_$.extend(libs[i])}var xui=window.x$=function(){return new _$(arguments)}})();<|endoftext|>


<commit_before>'use strict'

import {transformClassicStochasticProductions, transformClassicCSProduction, transformClassicParametricAxiom, testClassicParametricSyntax} from './classicLSystemSyntax';

export default function LSystem({axiom, productions, finals, branchSymbols, ignoredSymbols, classicParametricSyntax}) {
	// faking default values until better support lands in all browser
	axiom = typeof axiom !== 'undefined' ? axiom : '';
	branchSymbols = typeof branchSymbols !== 'undefined' ? branchSymbols : "";
	ignoredSymbols = typeof ignoredSymbols !== 'undefined' ? ignoredSymbols : "";
	classicParametricSyntax = typeof classicParametricSyntax !== 'undefined' ? classicParametricSyntax : 'false';

	// if using objects in axioms, as used in parametric L-Systems
	this.getString = function(onlySymbols = true) {
		if(typeof this.axiom === 'string') return this.axiom;
		if(onlySymbols === true) {
			return this.axiom.reduce( (prev, current) => {
				if(current.symbol === undefined){
					console.log('found:', current);
					throw new Error('L-Systems that use only objects as symbols (eg: {symbol: \'F\', params: []}), cant use string symbols (eg. \'F\')! Check if you always return objects in your productions and no strings.');
				}
				return prev + current.symbol;
			}, '');
		} else {
			return JSON.stringify(this.axiom);
		}
	};

	this.setAxiom = function (axiom) {
		this.axiom = axiom;
	};

	this.setProduction = function (A, B, doAppend = false) {
		let newProduction = [A, B];
		if(newProduction === undefined) throw	new Error('no production specified.');

		if(this.parameters.allowClassicSyntax === true) {
			newProduction = transformClassicCSProduction.bind(this)(newProduction, this.ignoredSymbols);
		}
		let symbol = newProduction[0];

		if(doAppend === true && this.productions.has(symbol)) {

			let existingProduction = this.productions.get(symbol);
			// If existing production results already in an array use this, otherwise
			// create new array to append to.
			let productionList = (existingProduction[Symbol.iterator] !== undefined && typeof existingProduction !== 'string' && !(existingProduction instanceof String)) ? this.productions.get(symbol) : [this.productions.get(symbol)];
			productionList.push(newProduction[1]);
			this.productions.set(symbol, productionList);
		} else {
			this.productions.set(newProduction[0], newProduction[1]);
		}

	};

	// set multiple productions from name:value Object
	this.setProductions = function (newProductions) {
		if(newProductions === undefined) throw new Error('no production specified.');
		this.clearProductions();

			// TODO: once Object.entries() (https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/entries) is stable, use that in combo instead of awkward for…in.
			for (let condition in newProductions) {
			  if( newProductions.hasOwnProperty( condition ) ) {
					this.setProduction(condition, newProductions[condition], true);
			  }
			}
	};

	this.clearProductions = function () {
		this.productions = new Map();
	};

	this.setFinal = function (symbol, final) {
		let newFinal = [symbol, final];
		if(newFinal === undefined) {
			throw	new Error('no final specified.');
		}
		this.finals.set(newFinal[0], newFinal[1]);
	};

	// set multiple finals from name:value Object
	this.setFinals = function (newFinals) {
		if(newFinals === undefined) throw new Error('no finals specified.');
		this.finals = new Map();
			for (let symbol in newFinals) {
			  if( newFinals.hasOwnProperty( symbol ) ) {
					this.setFinal(symbol, newFinals[symbol]);
			  }
			}
	};

	this.getProductionResult = function (p, index, part, params) {

		let result;

		// if p is a function, execute function and append return value
		if (typeof p === 'function') {
			result = p({index, currentAxiom: this.axiom, part, params});

			/* if p is no function and no iterable, then
			it should be a string (regular) or object
			directly return it then as result */
		} else if (typeof p === 'string' || p instanceof String || (typeof p === 'object' && p[Symbol.iterator] === undefined) ) {
			result = p;

			// if p is a list/iterable
		} else if (p[Symbol.iterator] !== undefined && typeof p !== 'string' && !(p instanceof String)) {
			/*
			go through the list and use
			the first valid production in that list. (that returns true)
			This assumes, it's a list of functions.
			*/
			for (let _p of p) {
				let _result;
				if (_p[Symbol.iterator] !== undefined && typeof _p !== 'string' && !(_p instanceof String)) {
					// If _p is itself also an Array, recursively get the result.
					_result = this.getProductionResult(_p);
				} else {
					_result = (typeof _p === 'function') ? _p({index, currentAxiom: this.axiom, part, params}) : _p;
				}

				if (_result !== undefined && _result !== false) {
					result = _result;
					break;
				}

			}
		}

		return (result === false) ? part : result;
	}

	this.applyProductions = function() {
		// a axiom can be a string or an array of objects that contain the key/value 'symbol'
		let newAxiom = (typeof this.axiom === 'string') ? '' : [];
		let index = 0;
		// iterate all symbols/characters of the axiom and lookup according productions
		for (let part of this.axiom) {
			let symbol = part;

			// Stuff for classic parametric L-Systems: get actual symbol and possible parameters
			// params will be given the production function, if applicable.
			let params = [];
			if(typeof part === 'object' && part.symbol) symbol = part.symbol;
			if(typeof part === 'object' && part.params) params = part.params;

			let result = part;
			if (this.productions.has(symbol)) {
				let p = this.productions.get(symbol);
				result = this.getProductionResult(p, index, part, params);
			}

			// finally add result to new axiom
			if(typeof newAxiom === 'string') {
				newAxiom += result;
			} else {
				// If result is an array, merge result into new axiom instead of pushing.
				if(result.constructor === Array) {
					Array.prototype.push.apply(newAxiom, result);
				} else {
					newAxiom.push(result);
				}
			}
			index++;
		}

		// finally set new axiom and also return for convenience
		this.axiom = newAxiom;
		return newAxiom;
	};

	// iterate n times
	this.iterate = function(n = 1) {
		this.iterations = n;
		let lastIteration;
		for (let iteration = 0; iteration < n; iteration++, this.iterationCount++) {
			lastIteration = this.applyProductions();
		}
		return lastIteration;
	};

	this.final = function() {
		for (let part of this.axiom) {

			// if we have objects for each symbol, (when using parametric L-Systems)
			// get actual identifiable symbol character
			let symbol = part
			if(typeof part === 'object' && part.symbol) symbol = part.symbol

			if (this.finals.has(symbol)) {
				var finalFunction = this.finals.get(symbol)
				var typeOfFinalFunction = typeof finalFunction
				if ((typeOfFinalFunction !== 'function')) {
					throw Error('\'' + symbol + '\'' + ' has an object for a final function. But it is __not a function__ but a ' + typeOfFinalFunction + '!')
				}
				// execute symbols function
				finalFunction()

			} else {
				// symbol has no final function
			}
		}
	}

/*
	how to use match():
 	-----------------------
	It is mainly a helper function for context sensitive productions.
	If you use the classic syntax, it will by default be automatically transformed to proper
	JS-Syntax.
	Howerver, you can use the match helper function in your on productions:

	index is the index of a production using `match`
	eg. in a classic L-System

	LSYS = ABCDE
	B<C>DE -> 'Z'

	the index of the `B<C>D -> 'Z'` production would be the index of C (which is 2) when the
	production would perform match(). so (if not using the ClassicLSystem class) you'd construction your context-sensitive production from C to Z like so:

	LSYS.setProduction('C', (index, axiom) => {
		(LSYS.match({index, match: 'B', direction: 'left'}) &&
		 LSYS.match({index, match: 'DE', direction: 'right'}) ? 'Z' : 'C')
	})

	You can just write match({index, ...} instead of match({index: index, ..}) because of new ES6 Object initialization, see: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Object_initializer#New_notations_in_ECMAScript_6
	*/

	this.match = function({axiom_, match, ignoredSymbols, branchSymbols, index, direction}) {
		let branchCount = 0;
		let explicitBranchCount = 0;
		axiom_ = axiom || this.axiom;
		if(branchSymbols === undefined) branchSymbols = (this.branchSymbols !== undefined) ? this.branchSymbols : [];
		if(ignoredSymbols === undefined) ignoredSymbols = (this.ignoredSymbols !== undefined) ? this.ignoredSymbols : [];
		let returnMatchIndices = [];

		let branchStart, branchEnd, axiomIndex, loopIndexChange, matchIndex, matchIndexChange, matchIndexOverflow;
		// set some variables depending on the direction to match
			if (direction === 'right') {
				loopIndexChange = matchIndexChange = +1;
				axiomIndex = index + 1;
				matchIndex = 0;
				matchIndexOverflow = match.length;
				if (branchSymbols.length > 0) [branchStart, branchEnd] = branchSymbols;
			} else if (direction === 'left') {
				loopIndexChange = matchIndexChange = -1;
				axiomIndex = index - 1;
				matchIndex = match.length - 1;
				matchIndexOverflow = -1;
				if (branchSymbols.length > 0) [branchEnd, branchStart] = branchSymbols;
			} else {
				throw Error(direction, 'is not a valid direction for matching.');
			}

		for (;axiomIndex < axiom_.length && axiomIndex >= 0; axiomIndex += loopIndexChange) {
			// FIXME: what about objects with .symbol

			let axiomSymbol = axiom_[axiomIndex];
			// For objects match for objects `symbol`
			if(typeof axiomSymbol === 'object') axiomSymbol = axiomSymbol.symbol;
			let matchSymbol = match[matchIndex];

			// compare current symbol of axiom with current symbol of match
			if (axiomSymbol === matchSymbol) {

				if(branchCount === 0 || explicitBranchCount > 0) {
					// if its a match and previously NOT inside branch (branchCount===0) or in explicitly wanted branch (explicitBranchCount > 0)

					// if a bracket was explicitly stated in match axiom
					if(axiomSymbol === branchStart){
						explicitBranchCount++;
						branchCount++;
						matchIndex += matchIndexChange;

					} else if (axiomSymbol === branchEnd) {
						explicitBranchCount = Math.max(0, explicitBranchCount - 1);
						branchCount = Math.max(0, branchCount - 1);
						// only increase match if we are out of explicit branch

						if(explicitBranchCount === 0){

							matchIndex += matchIndexChange;
						}

					} else {
						returnMatchIndices.push(axiomIndex);
						matchIndex += matchIndexChange;
					}
				}

				// overflowing matchIndices (matchIndex + 1 for right match, matchIndexEnd for left match )?
				// -> no more matches to do. return with true, as everything matched until here
				// *yay*
				if(matchIndex === matchIndexOverflow){
					return {result: true, matchIndices: returnMatchIndices};
				}

			} else if (axiomSymbol === branchStart) {
				branchCount++;
				if(explicitBranchCount > 0) explicitBranchCount++;

			} else if(axiomSymbol === branchEnd) {
				branchCount = Math.max(0, branchCount-1);
				if(explicitBranchCount > 0) explicitBranchCount = Math.max(0, explicitBranchCount-1);

			} else if((branchCount === 0 || (explicitBranchCount > 0 && matchSymbol !== branchEnd)) && ignoredSymbols.includes(axiomSymbol) === false) {
				// not in branchSymbols/branch? or if in explicit branch, and not at the very end of
				// condition (at the ]), and symbol not in ignoredSymbols ? then false
				return {result: false, matchIndices: returnMatchIndices};
			}
		}

		return {result: false, matchIndices: returnMatchIndices};

	};

	// finally init stuff
	this.parameters = {
		allowClassicSyntax: true
	};

	this.ignoredSymbols = ignoredSymbols;
	this.setAxiom(axiom);
	this.productions = new Map();

	this.branchSymbols = branchSymbols;

	this.classicParametricSyntax = classicParametricSyntax;

	if(productions) this.setProductions(productions);
	if (finals) this.setFinals(finals);

	this.iterationCount = 0;
	return this;
}

// Set classic syntax helpers to library scope to be used outside of library context
// for users eg.
LSystem.transformClassicStochasticProductions = transformClassicStochasticProductions;
LSystem.transformClassicCSProduction = transformClassicCSProduction;
LSystem.transformClassicParametricAxiom = transformClassicParametricAxiom;
LSystem.testClassicParametricSyntax = testClassicParametricSyntax;
<commit_msg>Closes #4<commit_after>'use strict'

import {transformClassicStochasticProductions, transformClassicCSProduction, transformClassicParametricAxiom, testClassicParametricSyntax} from './classicLSystemSyntax';

export default function LSystem({axiom, productions, finals, branchSymbols, ignoredSymbols, classicParametricSyntax}) {
	// faking default values until better support lands in all browser
	axiom = typeof axiom !== 'undefined' ? axiom : '';
	branchSymbols = typeof branchSymbols !== 'undefined' ? branchSymbols : "";
	ignoredSymbols = typeof ignoredSymbols !== 'undefined' ? ignoredSymbols : "";
	classicParametricSyntax = typeof classicParametricSyntax !== 'undefined' ? classicParametricSyntax : 'false';

	// if using objects in axioms, as used in parametric L-Systems
	this.getString = function(onlySymbols = true) {
		if(typeof this.axiom === 'string') return this.axiom;
		if(onlySymbols === true) {
			return this.axiom.reduce( (prev, current) => {
				if(current.symbol === undefined){
					console.log('found:', current);
					throw new Error('L-Systems that use only objects as symbols (eg: {symbol: \'F\', params: []}), cant use string symbols (eg. \'F\')! Check if you always return objects in your productions and no strings.');
				}
				return prev + current.symbol;
			}, '');
		} else {
			return JSON.stringify(this.axiom);
		}
	};

	this.setAxiom = function (axiom) {
		this.axiom = axiom;
	};

	this.setProduction = function (A, B, doAppend = false) {
		let newProduction = [A, B];
		if(newProduction === undefined) throw	new Error('no production specified.');

		if(this.parameters.allowClassicSyntax === true) {
			newProduction = transformClassicCSProduction.bind(this)(newProduction, this.ignoredSymbols);
		}
		let symbol = newProduction[0];

		if(doAppend === true && this.productions.has(symbol)) {

			let existingProduction = this.productions.get(symbol);
			// If existing production results already in an array use this, otherwise
			// create new array to append to.
			let productionList = (existingProduction[Symbol.iterator] !== undefined && typeof existingProduction !== 'string' && !(existingProduction instanceof String)) ? this.productions.get(symbol) : [this.productions.get(symbol)];
			productionList.push(newProduction[1]);
			this.productions.set(symbol, productionList);
		} else {
			this.productions.set(newProduction[0], newProduction[1]);
		}

	};

	// set multiple productions from name:value Object
	this.setProductions = function (newProductions) {
		if(newProductions === undefined) throw new Error('no production specified.');
		this.clearProductions();

			// TODO: once Object.entries() (https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/entries) is stable, use that in combo instead of awkward for…in.
			for (let condition in newProductions) {
			  if( newProductions.hasOwnProperty( condition ) ) {
					this.setProduction(condition, newProductions[condition], true);
			  }
			}
	};

	this.clearProductions = function () {
		this.productions = new Map();
	};

	this.setFinal = function (symbol, final) {
		let newFinal = [symbol, final];
		if(newFinal === undefined) {
			throw	new Error('no final specified.');
		}
		this.finals.set(newFinal[0], newFinal[1]);
	};

	// set multiple finals from name:value Object
	this.setFinals = function (newFinals) {
		if(newFinals === undefined) throw new Error('no finals specified.');
		this.finals = new Map();
			for (let symbol in newFinals) {
			  if( newFinals.hasOwnProperty( symbol ) ) {
					this.setFinal(symbol, newFinals[symbol]);
			  }
			}
	};

	this.getProductionResult = function (p, index, part, params) {

		let result;

		// if p is a function, execute function and append return value
		if (typeof p === 'function') {
			result = p({index, currentAxiom: this.axiom, part, params});

			/* if p is no function and no iterable, then
			it should be a string (regular) or object
			directly return it then as result */
		} else if (typeof p === 'string' || p instanceof String || (typeof p === 'object' && p[Symbol.iterator] === undefined) ) {
			result = p;

			// if p is a list/iterable
		} else if (p[Symbol.iterator] !== undefined && typeof p !== 'string' && !(p instanceof String)) {
			/*
			go through the list and use
			the first valid production in that list. (that returns true)
			This assumes, it's a list of functions.
			*/
			for (let _p of p) {
				let _result;
				if (_p[Symbol.iterator] !== undefined && typeof _p !== 'string' && !(_p instanceof String)) {
					// If _p is itself also an Array, recursively get the result.
					_result = this.getProductionResult(_p);
				} else {
					_result = (typeof _p === 'function') ? _p({index, currentAxiom: this.axiom, part, params}) : _p;
				}

				if (_result !== undefined && _result !== false) {
					result = _result;
					break;
				}

			}
		}

		return (result === false) ? part : result;
	}

	this.applyProductions = function() {
		// a axiom can be a string or an array of objects that contain the key/value 'symbol'
		let newAxiom = (typeof this.axiom === 'string') ? '' : [];
		let index = 0;
		// iterate all symbols/characters of the axiom and lookup according productions
		for (let part of this.axiom) {
			let symbol = part;

			// Stuff for classic parametric L-Systems: get actual symbol and possible parameters
			// params will be given the production function, if applicable.
			let params = [];
			if(typeof part === 'object' && part.symbol) symbol = part.symbol;
			if(typeof part === 'object' && part.params) params = part.params;

			let result = part;
			if (this.productions.has(symbol)) {
				let p = this.productions.get(symbol);
				result = this.getProductionResult(p, index, part, params);
			}

			// finally add result to new axiom
			if(typeof newAxiom === 'string') {
				newAxiom += result;
			} else {
				// If result is an array, merge result into new axiom instead of pushing.
				if(result.constructor === Array) {
					Array.prototype.push.apply(newAxiom, result);
				} else {
					newAxiom.push(result);
				}
			}
			index++;
		}

		// finally set new axiom and also return for convenience
		this.axiom = newAxiom;
		return newAxiom;
	};

	// iterate n times
	this.iterate = function(n = 1) {
		this.iterations = n;
		let lastIteration;
		for (let iteration = 0; iteration < n; iteration++, this.iterationCount++) {
			lastIteration = this.applyProductions();
		}
		return lastIteration;
	};

	this.final = function() {
		let index = 0;
		for (let part of this.axiom) {

			// if we have objects for each symbol, (when using parametric L-Systems)
			// get actual identifiable symbol character
			let symbol = part;
			if(typeof part === 'object' && part.symbol) symbol = part.symbol;

			if (this.finals.has(symbol)) {
				var finalFunction = this.finals.get(symbol);
				let typeOfFinalFunction = typeof finalFunction;
				if ((typeOfFinalFunction !== 'function')) {
					throw Error('\'' + symbol + '\'' + ' has an object for a final function. But it is __not a function__ but a ' + typeOfFinalFunction + '!');
				}
				// execute symbols function
				finalFunction({index: index, part: part});

			} else {
				// symbol has no final function
			}
			index++;
		}
	}

/*
	how to use match():
 	-----------------------
	It is mainly a helper function for context sensitive productions.
	If you use the classic syntax, it will by default be automatically transformed to proper
	JS-Syntax.
	Howerver, you can use the match helper function in your on productions:

	index is the index of a production using `match`
	eg. in a classic L-System

	LSYS = ABCDE
	B<C>DE -> 'Z'

	the index of the `B<C>D -> 'Z'` production would be the index of C (which is 2) when the
	production would perform match(). so (if not using the ClassicLSystem class) you'd construction your context-sensitive production from C to Z like so:

	LSYS.setProduction('C', (index, axiom) => {
		(LSYS.match({index, match: 'B', direction: 'left'}) &&
		 LSYS.match({index, match: 'DE', direction: 'right'}) ? 'Z' : 'C')
	})

	You can just write match({index, ...} instead of match({index: index, ..}) because of new ES6 Object initialization, see: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Object_initializer#New_notations_in_ECMAScript_6
	*/

	this.match = function({axiom_, match, ignoredSymbols, branchSymbols, index, direction}) {
		let branchCount = 0;
		let explicitBranchCount = 0;
		axiom_ = axiom || this.axiom;
		if(branchSymbols === undefined) branchSymbols = (this.branchSymbols !== undefined) ? this.branchSymbols : [];
		if(ignoredSymbols === undefined) ignoredSymbols = (this.ignoredSymbols !== undefined) ? this.ignoredSymbols : [];
		let returnMatchIndices = [];

		let branchStart, branchEnd, axiomIndex, loopIndexChange, matchIndex, matchIndexChange, matchIndexOverflow;
		// set some variables depending on the direction to match
			if (direction === 'right') {
				loopIndexChange = matchIndexChange = +1;
				axiomIndex = index + 1;
				matchIndex = 0;
				matchIndexOverflow = match.length;
				if (branchSymbols.length > 0) [branchStart, branchEnd] = branchSymbols;
			} else if (direction === 'left') {
				loopIndexChange = matchIndexChange = -1;
				axiomIndex = index - 1;
				matchIndex = match.length - 1;
				matchIndexOverflow = -1;
				if (branchSymbols.length > 0) [branchEnd, branchStart] = branchSymbols;
			} else {
				throw Error(direction, 'is not a valid direction for matching.');
			}

		for (;axiomIndex < axiom_.length && axiomIndex >= 0; axiomIndex += loopIndexChange) {
			// FIXME: what about objects with .symbol

			let axiomSymbol = axiom_[axiomIndex];
			// For objects match for objects `symbol`
			if(typeof axiomSymbol === 'object') axiomSymbol = axiomSymbol.symbol;
			let matchSymbol = match[matchIndex];

			// compare current symbol of axiom with current symbol of match
			if (axiomSymbol === matchSymbol) {

				if(branchCount === 0 || explicitBranchCount > 0) {
					// if its a match and previously NOT inside branch (branchCount===0) or in explicitly wanted branch (explicitBranchCount > 0)

					// if a bracket was explicitly stated in match axiom
					if(axiomSymbol === branchStart){
						explicitBranchCount++;
						branchCount++;
						matchIndex += matchIndexChange;

					} else if (axiomSymbol === branchEnd) {
						explicitBranchCount = Math.max(0, explicitBranchCount - 1);
						branchCount = Math.max(0, branchCount - 1);
						// only increase match if we are out of explicit branch

						if(explicitBranchCount === 0){

							matchIndex += matchIndexChange;
						}

					} else {
						returnMatchIndices.push(axiomIndex);
						matchIndex += matchIndexChange;
					}
				}

				// overflowing matchIndices (matchIndex + 1 for right match, matchIndexEnd for left match )?
				// -> no more matches to do. return with true, as everything matched until here
				// *yay*
				if(matchIndex === matchIndexOverflow){
					return {result: true, matchIndices: returnMatchIndices};
				}

			} else if (axiomSymbol === branchStart) {
				branchCount++;
				if(explicitBranchCount > 0) explicitBranchCount++;

			} else if(axiomSymbol === branchEnd) {
				branchCount = Math.max(0, branchCount-1);
				if(explicitBranchCount > 0) explicitBranchCount = Math.max(0, explicitBranchCount-1);

			} else if((branchCount === 0 || (explicitBranchCount > 0 && matchSymbol !== branchEnd)) && ignoredSymbols.includes(axiomSymbol) === false) {
				// not in branchSymbols/branch? or if in explicit branch, and not at the very end of
				// condition (at the ]), and symbol not in ignoredSymbols ? then false
				return {result: false, matchIndices: returnMatchIndices};
			}
		}

		return {result: false, matchIndices: returnMatchIndices};

	};

	// finally init stuff
	this.parameters = {
		allowClassicSyntax: true
	};

	this.ignoredSymbols = ignoredSymbols;
	this.setAxiom(axiom);
	this.productions = new Map();

	this.branchSymbols = branchSymbols;

	this.classicParametricSyntax = classicParametricSyntax;

	if(productions) this.setProductions(productions);
	if (finals) this.setFinals(finals);

	this.iterationCount = 0;
	return this;
}

// Set classic syntax helpers to library scope to be used outside of library context
// for users eg.
LSystem.transformClassicStochasticProductions = transformClassicStochasticProductions;
LSystem.transformClassicCSProduction = transformClassicCSProduction;
LSystem.transformClassicParametricAxiom = transformClassicParametricAxiom;
LSystem.testClassicParametricSyntax = testClassicParametricSyntax;
<|endoftext|>


<commit_before>this.LocalCache = (function constructor(localStorage) {
  var MS_IN_HOUR = 3600000,   // 60*60*1000
      MS_IN_DAY = 24*MS_IN_HOUR,
      PREFIX = 'LocalCache_',
      EXPIRES_KEY = 'Expires_',
      ACCESS_KEY = 'Access_',
      EXPIRES_REGEX = /^LocalCache_Expires_.*/,
      ALL_REGEX = /^LocalCache_.*/;

  var stubbedStorage;

  var TTL = {
    WEEK: function() {
      return TTL.DAY() + 6*MS_IN_DAY;
    },
    DAY: function() {
      var date = new Date(),
          originalDay = date.getDate(),
          originalHours = date.getHours();
      date.setUTCHours(10 /* 2am PST, 3am PDT */, 0, 0, 0);

      // If we are after midnight but before the expires we don't want to change anything
      if (date.getHours() <= originalHours) {
        date.setDate(originalDay + 1);
      }
      return date.getTime();
    },
    HOUR: function() {
      return (Math.floor(Date.now()/MS_IN_HOUR)+1)*MS_IN_HOUR;
    },
    hours: function(numHours) {
      return function() {
        return Date.now() + (MS_IN_HOUR * numHours);
      };
    }
  };

  // http://stackoverflow.com/questions/3027142/calculating-usage-of-localstorage-space
  function isQuotaError(error) {
    return error.code === 22
        || error.name === 'QUOTA_EXCEEDED_ERR'
        || error.name === 'QuotaExceededError'
        || error.name === 'NS_ERROR_DOM_QUOTA_REACHED';
  }

  function isStorageBug(error) {
    // IE stores localStorage as XML internally, as a result, some
    // characters cause this error to be thrown.
    return error.description === 'Invalid Argument.'
  }

  function checkStorage() {
    function tryStore() {
      localStorage.setItem('available-test', '1');
      localStorage.removeItem('available-test');
    }
    try {
      // iOS 7 betas throw a security exception when attempting to even read .localStorage
      // This is hopefully a bug but we're coding against it to be safe.
      localStorage = localStorage || window.localStorage;

      tryStore();
    } catch (err) {
      if (isQuotaError(err)) {
        try {
          // If we have a quota error, try one more time.
          flushExpired();

          tryStore();

          // We worked this time, full steam ahead.
          return;
        } catch (err) {
          /* NOP */
        }
      }

      stubStorage();
    }
  }

  function stubStorage() {
    stubbedStorage = true;
    var localStorageData = {};
    localStorage = {
      getItem: function(name) {
        return localStorageData[name];
      },
      setItem: function(name, value) {
        localStorageData[name] = value;
      },
      removeItem: function(name) {
        delete localStorageData[name];
      }
    };
  }

  function setKey(key, data, ttl) {
    if (ttl) {
      if (stubbedStorage) {
        // Do not store any data that has a TTL in stubbed mode as this data is already
        // not guaranteed, it can be quite large, and it will never be evicted.
        return;
      }

      localStorage.setItem(PREFIX+EXPIRES_KEY+key, ttl());
      localStorage.setItem(PREFIX+ACCESS_KEY+key, Date.now());
    }
    localStorage.setItem(PREFIX+key, data);
  }
  function removeKey(key) {
    localStorage.removeItem(PREFIX+EXPIRES_KEY+key);
    localStorage.removeItem(PREFIX+ACCESS_KEY+key);
    localStorage.removeItem(PREFIX+key);
  }
  function loadByAccess() {
    var ret = [];
    for (var i = 0, len = localStorage.length; i < len; i++) {
      var key = localStorage.key(i);
      if (EXPIRES_REGEX.test(key)) {
        key = key.substring(PREFIX.length + EXPIRES_KEY.length);
        ret.push({
          key: key,
          access: parseInt(localStorage.getItem(PREFIX+ACCESS_KEY+key) || 0, 10)
        });
      }
    }
    ret.sort(function(a, b) { return a.access-b.access; });
    return ret;
  }

  function flushExpired() {
    var now = Date.now(),
        toRemove = [];

    // Iterate over everything, scanning for anything that may be expired
    for (var i = 0, len = localStorage.length; i < len; i++) {
      var key = localStorage.key(i);
      if (EXPIRES_REGEX.test(key)) {
        if (parseInt(localStorage.getItem(key), 10) < now) {
          toRemove.push(key.substring(PREFIX.length + EXPIRES_KEY.length));
        }
      }
    }

    // Actually remove everything
    for (var i = 0, len = toRemove.length; i < len; i++) {
      removeKey(toRemove[i]);
    }
  }

  checkStorage();
  if (!stubbedStorage && (typeof $serverSide === 'undefined' || !$serverSide)) {
    // Kill any expired content that may be in the cache as soon as we get the chance
    setTimeout(flushExpired, 0);

    // And flush every hour, if we are on the same page for that long
    // No need to run more often than that as the shortest expiration time is 1 hour
    setInterval(flushExpired, MS_IN_HOUR);
  }

  return {
    // Exposed for unit testing purposes
    constructor: constructor,

    TTL: TTL,

    store: function(key, data, ttl) {
      // Iterate until we can store the data, we block out the cache, or we
      // hit an exception that is not a quote exception
      var cullList;
      while (!cullList || cullList.length) {
        cullList && cullList.shift();

        try {
          // Attempt to set once before loading the whole cache
          setKey(key, data, ttl);
          return true;
        } catch (err) {
          // If quota drop the things that are closest to expiration out of the cache
          if (isQuotaError(err)) {
            cullList = cullList || loadByAccess();
            if (cullList.length) {
              removeKey(cullList[0].key);
            }
          // We ignore if there is a ttl set - which indicates that it is not actual content
          } else if (!isStorageBug(err) || !ttl) {
            throw err;
          }
        }
      }
    },
    get: function(key) {
      var expires = localStorage.getItem(PREFIX+EXPIRES_KEY+key) || 0;
      if (expires && parseInt(expires, 10) < Date.now()) {
        // Kill anything that may have expired
        removeKey(key);
        return;
      }

      if (expires) {
        try {
          localStorage.setItem(PREFIX+ACCESS_KEY+key, Date.now());
        } catch (err) {
          /* NOP: This may occur in private browsing mode... Playing it safe in case cached data surivives there. */
        }
      }
      return localStorage.getItem(PREFIX+key);
    },
    remove: function(key) {
      removeKey(key);
    },
    invalidate: function(prefix, hard) {
      var toRemove = [];

      for (var i = 0, len = localStorage.length; i < len; i++) {
        var key = localStorage.key(i);
        if ((hard ? ALL_REGEX : EXPIRES_REGEX).test(key)) {
          var key = key.substring(PREFIX.length + (!hard ? EXPIRES_KEY.length : 0));
          if (!prefix || key.indexOf(prefix) === 0) {
            toRemove.push(key);
          }
        }
      }

      // Actually remove everything
      toRemove.forEach(removeKey);
    },
    reset: function(hard) {
      this.invalidate('', hard);
    },
    flushExpired: flushExpired
  };
}());
<commit_msg>Update comment wording<commit_after>this.LocalCache = (function constructor(localStorage) {
  var MS_IN_HOUR = 3600000,   // 60*60*1000
      MS_IN_DAY = 24*MS_IN_HOUR,
      PREFIX = 'LocalCache_',
      EXPIRES_KEY = 'Expires_',
      ACCESS_KEY = 'Access_',
      EXPIRES_REGEX = /^LocalCache_Expires_.*/,
      ALL_REGEX = /^LocalCache_.*/;

  var stubbedStorage;

  var TTL = {
    WEEK: function() {
      return TTL.DAY() + 6*MS_IN_DAY;
    },
    DAY: function() {
      var date = new Date(),
          originalDay = date.getDate(),
          originalHours = date.getHours();
      date.setUTCHours(10 /* 2am PST, 3am PDT */, 0, 0, 0);

      // If we are after midnight but before the expires we don't want to change anything
      if (date.getHours() <= originalHours) {
        date.setDate(originalDay + 1);
      }
      return date.getTime();
    },
    HOUR: function() {
      return (Math.floor(Date.now()/MS_IN_HOUR)+1)*MS_IN_HOUR;
    },
    hours: function(numHours) {
      return function() {
        return Date.now() + (MS_IN_HOUR * numHours);
      };
    }
  };

  // http://stackoverflow.com/questions/3027142/calculating-usage-of-localstorage-space
  function isQuotaError(error) {
    return error.code === 22
        || error.name === 'QUOTA_EXCEEDED_ERR'
        || error.name === 'QuotaExceededError'
        || error.name === 'NS_ERROR_DOM_QUOTA_REACHED';
  }

  function isStorageBug(error) {
    // IE stores localStorage as XML internally, as a result, some
    // characters cause this error to be thrown.
    return error.description === 'Invalid Argument.'
  }

  function checkStorage() {
    function tryStore() {
      localStorage.setItem('available-test', '1');
      localStorage.removeItem('available-test');
    }
    try {
      // iOS 7 betas throw a security exception when attempting to even read .localStorage
      // This is hopefully a bug but we're coding against it to be safe.
      localStorage = localStorage || window.localStorage;

      tryStore();
    } catch (err) {
      if (isQuotaError(err)) {
        try {
          // If we have a quota error, try one more time.
          flushExpired();

          tryStore();

          // We worked this time, full steam ahead.
          return;
        } catch (err) {
          /* NOP */
        }
      }

      stubStorage();
    }
  }

  function stubStorage() {
    stubbedStorage = true;
    var localStorageData = {};
    localStorage = {
      getItem: function(name) {
        return localStorageData[name];
      },
      setItem: function(name, value) {
        localStorageData[name] = value;
      },
      removeItem: function(name) {
        delete localStorageData[name];
      }
    };
  }

  function setKey(key, data, ttl) {
    if (ttl) {
      if (stubbedStorage) {
        // Do not store any data that has a TTL in stubbed mode as this data is already
        // not guaranteed, it can be quite large, and it will never be evicted.
        return;
      }

      localStorage.setItem(PREFIX+EXPIRES_KEY+key, ttl());
      localStorage.setItem(PREFIX+ACCESS_KEY+key, Date.now());
    }
    localStorage.setItem(PREFIX+key, data);
  }
  function removeKey(key) {
    localStorage.removeItem(PREFIX+EXPIRES_KEY+key);
    localStorage.removeItem(PREFIX+ACCESS_KEY+key);
    localStorage.removeItem(PREFIX+key);
  }
  function loadByAccess() {
    var ret = [];
    for (var i = 0, len = localStorage.length; i < len; i++) {
      var key = localStorage.key(i);
      if (EXPIRES_REGEX.test(key)) {
        key = key.substring(PREFIX.length + EXPIRES_KEY.length);
        ret.push({
          key: key,
          access: parseInt(localStorage.getItem(PREFIX+ACCESS_KEY+key) || 0, 10)
        });
      }
    }
    ret.sort(function(a, b) { return a.access-b.access; });
    return ret;
  }

  function flushExpired() {
    var now = Date.now(),
        toRemove = [];

    // Iterate over everything, scanning for anything that may be expired
    for (var i = 0, len = localStorage.length; i < len; i++) {
      var key = localStorage.key(i);
      if (EXPIRES_REGEX.test(key)) {
        if (parseInt(localStorage.getItem(key), 10) < now) {
          toRemove.push(key.substring(PREFIX.length + EXPIRES_KEY.length));
        }
      }
    }

    // Actually remove everything
    for (var i = 0, len = toRemove.length; i < len; i++) {
      removeKey(toRemove[i]);
    }
  }

  checkStorage();
  if (!stubbedStorage && (typeof $serverSide === 'undefined' || !$serverSide)) {
    // Kill any expired content that may be in the cache as soon as we get the chance
    setTimeout(flushExpired, 0);

    // And flush every hour, if we are on the same page for that long
    // No need to run more often than that as the shortest expiration time is 1 hour
    setInterval(flushExpired, MS_IN_HOUR);
  }

  return {
    // Exposed for unit testing purposes
    constructor: constructor,

    TTL: TTL,

    store: function(key, data, ttl) {
      // Iterate until we can store the data, we block out the cache, or we
      // hit an exception that is not a quote exception
      var cullList;
      while (!cullList || cullList.length) {
        cullList && cullList.shift();

        try {
          // Attempt to set once before loading the whole cache
          setKey(key, data, ttl);
          return true;
        } catch (err) {
          // If quota drop the things that are closest to expiration out of the cache
          if (isQuotaError(err)) {
            cullList = cullList || loadByAccess();
            if (cullList.length) {
              removeKey(cullList[0].key);
            }

          // We ignore if there is a ttl set - which indicates that it is not persistent content
          } else if (!isStorageBug(err) || !ttl) {
            throw err;
          }
        }
      }
    },
    get: function(key) {
      var expires = localStorage.getItem(PREFIX+EXPIRES_KEY+key) || 0;
      if (expires && parseInt(expires, 10) < Date.now()) {
        // Kill anything that may have expired
        removeKey(key);
        return;
      }

      if (expires) {
        try {
          localStorage.setItem(PREFIX+ACCESS_KEY+key, Date.now());
        } catch (err) {
          /* NOP: This may occur in private browsing mode... Playing it safe in case cached data surivives there. */
        }
      }
      return localStorage.getItem(PREFIX+key);
    },
    remove: function(key) {
      removeKey(key);
    },
    invalidate: function(prefix, hard) {
      var toRemove = [];

      for (var i = 0, len = localStorage.length; i < len; i++) {
        var key = localStorage.key(i);
        if ((hard ? ALL_REGEX : EXPIRES_REGEX).test(key)) {
          var key = key.substring(PREFIX.length + (!hard ? EXPIRES_KEY.length : 0));
          if (!prefix || key.indexOf(prefix) === 0) {
            toRemove.push(key);
          }
        }
      }

      // Actually remove everything
      toRemove.forEach(removeKey);
    },
    reset: function(hard) {
      this.invalidate('', hard);
    },
    flushExpired: flushExpired
  };
}());
<|endoftext|>


<commit_before>'use strict';
var fs = require('fs'),
    vm = require('vm'),
    vmInclude = function (code, path) {
        vm.runInThisContext(code, path);
    },
    vmAddFile = function (path) {
        vmInclude(fs.readFileSync(path), path);
    },
    vmRmFile = function (path) {
        vmInclude('', path);
    };

// workaround to 'require' works in vm.runInThisContext
global.require = require;

vmInclude('var window = global;');
vmAddFile(__dirname + '/nsapi-doc.js');

// load Netsuite functions and objects
var glob = require('glob'),
    files = glob.sync(__dirname + '/src/**/*.js');
for (let i = 0; i < files.length; vmAddFile(files[i++]));

var $db;

var $GLOBAL_VARS = Object.keys(global).concat(['$GLOBAL_VARS', '$db', '$GLOBAL_REM']),
    $GLOBAL_REM = [];

function loadDatabase(cb) {
    let base = '.nsmockup',
        tempDir = base + '/temp-' + (new Date().toJSON().replace(/[-:.Z]/g, '')),
        dbDir = tempDir + '/db',
        dbPath = dbDir + '/db.json';

    [base, tempDir, dbDir].forEach(f => {
        !fs.existsSync(f) && fs.mkdirSync(f);
    });

    !fs.existsSync(dbPath) && fs.writeFileSync(dbPath, '{}');

    let low = require('lowdb'),
        db = low(dbPath);

    // create metadata collection
    if (!db.object.__metadata) {
        db.object.__metadata = [];
        db.save();
    }
    db.$path = tempDir;
    db.$pathDB = dbDir;
    db.$pathCabinet = tempDir + '/cabinet';

    cb($db = global.$db = db);
}

exports.init = function (opts, cb) {
    function init(db) {
        let metadatas = opts.metadatas;
        if (typeof metadatas === 'string') metadatas = [require(opts.records)];
        else if (!Array.isArray(metadatas)) metadatas = [metadatas];

        if (metadatas && metadatas.length !== 0) {
            let _metadata = db('__metadata');
            for (let i = 0; i < metadatas.length; i++) {
                let metadata = metadatas[i];
                if (typeof metadata === 'string') metadata = require(metadata);

                if (!metadata || !metadata.code) continue;
                else {
                    _metadata.remove({code: metadata.code});
                    _metadata.push(metadata);
                    //console.log('import record-type metadata "' + metadata.code + '"');
                }
            }
            db.save();
        }

        let records = opts.records;
        if (typeof records === 'string') records = require(opts.records);

        if (!records) return cb();
        let recNames = Object.keys(records);

        let actual = 0,
            verifyDone = function () {
                if (++actual == recNames.length) return cb();
            };
        for (let i = 0; i < recNames.length; i++) {
            let recName = recNames[i],
                recVal = records[recName];

            if (typeof recVal === 'string') recVal = require(recVal);
            if (!recVal || !recVal.length) {
                verifyDone();
                continue;
            }

            recVal = recVal.map((val, i) => {
                if (!val.internalid) val.internalid = (i + 1);
                return val;
            });

            // save record type data in lowdb database
            db.object[recName] = recVal;
            db.save();

            //console.log('import record-type "' + recName + '" - total: ' + recVal.length);
            verifyDone();
        }
    }

    if ($db) init($db);
    else loadDatabase(init);
};

exports.destroy = function (cb) {
    var rmAllDirs = function (path) {
        if (fs.existsSync(path)) {
            fs.readdirSync(path).forEach(function (file) {
                var curPath = path + '/' + file;
                if (fs.lstatSync(curPath).isDirectory()) { // recurse
                    rmAllDirs(curPath);
                } else { // delete file
                    fs.unlinkSync(curPath);
                }
            });
            fs.rmdirSync(path);
        }
    };

    function destroy(db) {
        db.object = {};
        db.save();

        rmAllDirs(db.$pathCabinet);

        let globalVars = Object.keys(global);
        for (let k = 0; k < globalVars.length; k++) {
            let globalVar = globalVars[k];
            if ((~$GLOBAL_REM.indexOf(globalVar) || !~$GLOBAL_VARS.indexOf(globalVar)) && global[globalVar]) {
                global[globalVar] = undefined;
                !~$GLOBAL_REM.indexOf(globalVar) && $GLOBAL_REM.push(globalVar);
            }
        }
        return cb();
    }

    if ($db) destroy($db);
    else loadDatabase(destroy);
};

exports.addScript = vmAddFile;

let scripts = [];
function createScript(opt) {
    if (!opt || !opt.files || opt.files.length === 0) return;

    scripts.push(opt);
    for (let i = 0; i < opt.files.length; i++) {
        vmAddFile(opt.files[i]);
    }
    if (opt.params) {
        let pref = nlapiGetContext().preference,
            keys = Object.keys(opt.params);
        for (let i=0; i<keys.length;i++) {
            let param = keys[i];
            pref[param] = opt.params[param];
        }
    }
}

exports.createSuitelet = createScript;

exports.createRestlet = createScript;

exports.createSchedule = createScript;<commit_msg> - fix remove temporary global variables<commit_after>'use strict';
var fs = require('fs'),
    vm = require('vm'),
    vmInclude = function (code, path) {
        vm.runInThisContext(code, path);
    },
    vmAddFile = function (path) {
        vmInclude(fs.readFileSync(path), path);
    },
    vmRmFile = function (path) {
        vmInclude('', path);
    };

// workaround to 'require' works in vm.runInThisContext
global.require = require;

vmInclude('var window = global;');
vmAddFile(__dirname + '/nsapi-doc.js');

// load Netsuite functions and objects
var glob = require('glob'),
    files = glob.sync(__dirname + '/src/**/*.js');
for (let i = 0; i < files.length; vmAddFile(files[i++]));

var $db;

const $GLOBAL_VARS = Object.keys(global).concat(['$GLOBAL_VARS', '$db', '$GLOBAL_REM']);
global.$GLOBAL_REM = global.$GLOBAL_REM || [];

function loadDatabase(cb) {
    let base = '.nsmockup',
        tempDir = base + '/temp-' + (new Date().toJSON().replace(/[-:.Z]/g, '')),
        dbDir = tempDir + '/db',
        dbPath = dbDir + '/db.json';

    [base, tempDir, dbDir].forEach(f => {
        !fs.existsSync(f) && fs.mkdirSync(f);
    });

    !fs.existsSync(dbPath) && fs.writeFileSync(dbPath, '{}');

    let low = require('lowdb'),
        db = low(dbPath);

    // create metadata collection
    if (!db.object.__metadata) {
        db.object.__metadata = [];
        db.save();
    }
    db.$path = tempDir;
    db.$pathDB = dbDir;
    db.$pathCabinet = tempDir + '/cabinet';

    cb($db = global.$db = db);
}

exports.init = function (opts, cb) {
    function init(db) {
        let metadatas = opts.metadatas;
        if (typeof metadatas === 'string') metadatas = [require(opts.records)];
        else if (!Array.isArray(metadatas)) metadatas = [metadatas];

        if (metadatas && metadatas.length !== 0) {
            let _metadata = db('__metadata');
            for (let i = 0; i < metadatas.length; i++) {
                let metadata = metadatas[i];
                if (typeof metadata === 'string') metadata = require(metadata);

                if (!metadata || !metadata.code) continue;
                else {
                    _metadata.remove({code: metadata.code});
                    _metadata.push(metadata);
                    //console.log('import record-type metadata "' + metadata.code + '"');
                }
            }
            db.save();
        }

        let records = opts.records;
        if (typeof records === 'string') records = require(opts.records);

        if (!records) return cb();
        let recNames = Object.keys(records);

        let actual = 0,
            verifyDone = function () {
                if (++actual == recNames.length) return cb();
            };
        for (let i = 0; i < recNames.length; i++) {
            let recName = recNames[i],
                recVal = records[recName];

            if (typeof recVal === 'string') recVal = require(recVal);
            if (!recVal || !recVal.length) {
                verifyDone();
                continue;
            }

            recVal = recVal.map((val, i) => {
                if (!val.internalid) val.internalid = (i + 1);
                return val;
            });

            // save record type data in lowdb database
            db.object[recName] = recVal;
            db.save();

            //console.log('import record-type "' + recName + '" - total: ' + recVal.length);
            verifyDone();
        }
    }

    if ($db) init($db);
    else loadDatabase(init);
};

exports.destroy = function (cb) {

    var rmAllDirs = function (path) {
        if (fs.existsSync(path)) {
            fs.readdirSync(path).forEach(function (file) {
                var curPath = path + '/' + file;
                if (fs.lstatSync(curPath).isDirectory()) { // recurse
                    rmAllDirs(curPath);
                } else { // delete file
                    fs.unlinkSync(curPath);
                }
            });
            fs.rmdirSync(path);
        }
    };

    function destroy(db) {
        db.object = {};
        db.save();

        rmAllDirs(db.$pathCabinet);

        let globalVars = Object.keys(global),
            globalRem = global.$GLOBAL_REM;
        for (let k = 0; k < globalVars.length; k++) {
            let globalVar = globalVars[k];
            if ((~globalRem.indexOf(globalVar) || !~$GLOBAL_VARS.indexOf(globalVar)) && global[globalVar]) {
                global[globalVar] = undefined;
                !~globalRem.indexOf(globalVar) && globalRem.push(globalVar);
            }
        }
        return cb();
    }

    if ($db) destroy($db);
    else loadDatabase(destroy);
};

exports.addScript = vmAddFile;

let scripts = [];
function createScript(opt) {
    if (!opt || !opt.files || opt.files.length === 0) return;

    scripts.push(opt);
    for (let i = 0; i < opt.files.length; i++) {
        vmAddFile(opt.files[i]);
    }
    if (opt.params) {
        let pref = nlapiGetContext().preference,
            keys = Object.keys(opt.params);
        for (let i=0; i<keys.length;i++) {
            let param = keys[i];
            pref[param] = opt.params[param];
        }
    }
}

exports.createSuitelet = createScript;

exports.createRestlet = createScript;

exports.createSchedule = createScript;<|endoftext|>


<commit_before>/**
 * ニアスケイプwebモジュール
 *
 * HTTPに関わるところを処理する HTTPヘッダとかレスポンス処理とかHTTPのアクセスログとか
 * HTMLはコマンドラインでも使うのでns本体でやる
 *
 * node以外での利用は考慮しない
 */

/**
 * 設定
 */
//PENDING 動的な設定変更を設定ファルでやるか？DBの設定テーブルでやるか？
var config    = require('./config.json').ns;
var staticDir = config.staticDir || './static/';

/**
 * 定数的なの
 */
var URL_BASE = '/';

var httpStatus = {
	200 : ' OK',
	303 : ' See Other',             //他を参照せよ
	304 : ' Not Modified',          //未更新
	403 : ' Forbidden',             //禁止
	404 : ' NotFound',              //未検出
	405 : ' Method Not Allowed',    //許可されていないメソッド
	500 : ' Internal Server Error', //サーバ内部エラー
	503 : ' Service Unavailable',   //サービス利用不可
}
var contentType = {
	html : 'text/html;charset=UTF-8' ,
	htm  : 'text/html;charset=UTF-8' ,
	js   : 'text/javascript;charset=UTF-8' ,
	css  : 'text/css;charset=UTF-8' ,
	gif  : 'image/gif' ,
	jpeg : 'image/jpeg' ,
	jpg  : 'image/jpeg' ,
	jpe  : 'image/jpeg' ,
	png  : 'image/png' ,
	ico  : 'image/vnd.microsoft.icon' ,
	txt  : 'text/plain;charset=UTF-8' ,
};

var formHtml = (function () {/*
		<form action="__urlbase__" method="POST" enctype="multipart/form-data">

			<textarea id="box" style="width:100%;" rows="4" name="body" onKeyup="mojilenbody(value);"></textarea>
			tag<input type="text" name="tags" onKeyup="mojilentag(value);">
			<input id="btn" type="submit" name="submit" value="post" style="width: 100px;height: 60px;font-size: 2em;">
			<input type="file" name="file">
			<input type="hidden" name="user" value="__user__">
			文字数

		</form>
*/}).toString().match(/[^]*\/\*([^]*)\*\/\}$/)[1].trim();

/**
 * モジュール
 */

//nodeコアモジュール
var path = require('path');
var http = require('http');

//npm : Node Package Manager
var formidable = require('formidable');

//自作モジュール
var ns = require('./ns');

/**
 * モジュールローカル関数
 */
//GETリクエスト
function httpGet(request, response,logs){
	if (request.url == '/') { //トップへのアクセス
		//タイムライン
		ns.timeline('/','html',function(err,content){
			//TODO トップへのアクセス時の機能はサイト管理ユーザが設定できるようにする
			//PENDING 権限を持ったユーザがアクセスした時にHTMLには機能を付与して表示する その方法を考える

			//FIXME 投稿フォーム埋め込み
			var reshtml = content.split("__form__").join(formHtml).split("__urlbase__").join(URL_BASE);
			returnResponse(response,200,reshtml,'html',logs);
		});
	} else { //トップ以外
		//FIXME 拡張子関連
		ns.content(request.url,'txt',function(err,content,filePath){
			if (content != null){
				var extname = path.extname(request.url).replace(".", '');
				if(extname == ''){
					extname = 'txt';
				}
				if (filePath) {
					logs.msg += ' readFile:' + filePath;
				}
				returnResponse(response,200,content,extname,logs);
			} else {
				//コンテントがない場合404
				returnResponse(response,404,'404' + httpStatus[404],'txt',logs);
			}
		});
	}
}

//POSTリクエスト
function httpPost(request, response,logs){
	var form = new formidable.IncomingForm();
	var user = 'test'; //FIXME ユーザー取得
	//TODO 権限チェック

	form.parse(request, function( err, fields, files) {
		if(typeof fields.body != "undefined" && fields.body.trim() !== "" ){
			var body = fields.body.trim();
			var tags = fields.tags.trim().split(" ").filter(Boolean);
			ns.post(body,tags,'test',function(err){
				if (err){
					returnResponse(response,500,'err','txt',logs);
					errorLog({msg:'投稿失敗',err:err});
				} else {
					returnResponse(response,200,'posted','txt',logs);
				}
			});
		} else { //本文無しは転送
			redirect(response,303,'',logs);
		}
	});
}

//返送
function returnResponse(response,statusCode,content,type,logs){
	response.writeHead(statusCode, {'Content-Type': contentType[type]});
	response.end(content);

	logs.statusCode = statusCode;
	accessLog(logs);
}

//転送
function redirect(response,statusCode,url,logs){
	response.writeHead(statusCode, {"location": URL_BASE + url});
	response.end();

	logs.statusCode = statusCode;
	accessLog(logs);
}

//アクセスログ
function accessLog(logs){
	//TODO apache形式でファイルに書き出したりできるように
	console.log(logs.requestTime + ' ' + logs.host + ' ' + logs.httpVersion + ' ' + logs.method + ' ' + logs.url + ' ' + logs.statusCode + httpStatus[logs.statusCode] + ' ' + logs.userAgent + logs.msg);
	console.log(logs.statusCode + httpStatus[logs.statusCode]);
}

//エラーログ
function errorLog(log){
	//TODO オブジェクト受け取るようにする
	//TODO apache形式でファイルに書き出したりできるように
	console.log(log);
}

/** ニアスケイプwebモジュール本体
 *
 * node httpモジュールから呼ばれる
 * リクエストメソッドによって処理を振り分ける
 */
function nsweb(request, response) {

	var requestTime = new Date();

	var logs = {
		requestTime : requestTime ,
		host        : request.headers.host ,
		url         : request.url ,
		httpVersion : request.httpVersion ,
		method      : request.method ,
		statusCode  : 500 ,
		userAgent   : request.headers['user-agent'] ,
		msg         : '' ,
	}

 	//PENDING 先にURLで振り分けるべき？
	if (request.method == 'GET') {
		httpGet(request, response,logs);
	} else if (request.method == 'POST') {
		httpPost(request, response,logs);
	} else { //非許可HTTPメソッドは405
		returnResponse(response,405,'405' + httpStatus[403],'txt',logs);
	}

	//TODO あとで消す デバッグ用
	// /*
	console.log({
		request            : "######################################" ,
		requestTime        : requestTime ,
		url                : request.url ,
		headers            : request.headers ,
		http               : 'v' + request.httpVersion + ' ' + request.method ,
		idleStart          : Date(request.client._idleStart) ,
		monotonicStartTime : request.client._monotonicStartTime ,
	});
	// */
	//console.log(request);
}

/**
 * エクスポート
 */
module.exports = nsweb;
<commit_msg>改行を\nに統一 タグの空白文字列をスペース1個に<commit_after>/**
 * ニアスケイプwebモジュール
 *
 * HTTPに関わるところを処理する HTTPヘッダとかレスポンス処理とかHTTPのアクセスログとか
 * HTMLはコマンドラインでも使うのでns本体でやる
 *
 * node以外での利用は考慮しない
 */

/**
 * 設定
 */
//PENDING 動的な設定変更を設定ファルでやるか？DBの設定テーブルでやるか？
var config    = require('./config.json').ns;
var staticDir = config.staticDir || './static/';

/**
 * 定数的なの
 */
var URL_BASE = '/';

var httpStatus = {
	200 : ' OK',
	303 : ' See Other',             //他を参照せよ
	304 : ' Not Modified',          //未更新
	403 : ' Forbidden',             //禁止
	404 : ' NotFound',              //未検出
	405 : ' Method Not Allowed',    //許可されていないメソッド
	500 : ' Internal Server Error', //サーバ内部エラー
	503 : ' Service Unavailable',   //サービス利用不可
}
var contentType = {
	html : 'text/html;charset=UTF-8' ,
	htm  : 'text/html;charset=UTF-8' ,
	js   : 'text/javascript;charset=UTF-8' ,
	css  : 'text/css;charset=UTF-8' ,
	gif  : 'image/gif' ,
	jpeg : 'image/jpeg' ,
	jpg  : 'image/jpeg' ,
	jpe  : 'image/jpeg' ,
	png  : 'image/png' ,
	ico  : 'image/vnd.microsoft.icon' ,
	txt  : 'text/plain;charset=UTF-8' ,
};

var formHtml = (function () {/*
		<form action="__urlbase__" method="POST" enctype="multipart/form-data">

			<textarea id="box" style="width:100%;" rows="4" name="body" onKeyup="mojilenbody(value);"></textarea>
			tag<input type="text" name="tags" onKeyup="mojilentag(value);">
			<input id="btn" type="submit" name="submit" value="post" style="width: 100px;height: 60px;font-size: 2em;">
			<input type="file" name="file">
			<input type="hidden" name="user" value="__user__">
			文字数

		</form>
*/}).toString().match(/[^]*\/\*([^]*)\*\/\}$/)[1].trim();

/**
 * モジュール
 */

//nodeコアモジュール
var path = require('path');
var http = require('http');

//npm : Node Package Manager
var formidable = require('formidable');

//自作モジュール
var ns = require('./ns');

/**
 * モジュールローカル関数
 */
//GETリクエスト
function httpGet(request, response,logs){
	if (request.url == '/') { //トップへのアクセス
		//タイムライン
		ns.timeline('/','html',function(err,content){
			//TODO トップへのアクセス時の機能はサイト管理ユーザが設定できるようにする
			//PENDING 権限を持ったユーザがアクセスした時にHTMLには機能を付与して表示する その方法を考える

			//FIXME 投稿フォーム埋め込み
			var reshtml = content.split("__form__").join(formHtml).split("__urlbase__").join(URL_BASE);
			returnResponse(response,200,reshtml,'html',logs);
		});
	} else { //トップ以外
		//FIXME 拡張子関連
		ns.content(request.url,'txt',function(err,content,filePath){
			if (content != null){
				var extname = path.extname(request.url).replace(".", '');
				if(extname == ''){
					extname = 'txt';
				}
				if (filePath) {
					logs.msg += ' readFile:' + filePath;
				}
				returnResponse(response,200,content,extname,logs);
			} else {
				//コンテントがない場合404
				returnResponse(response,404,'404' + httpStatus[404],'txt',logs);
			}
		});
	}
}

//POSTリクエスト
function httpPost(request, response,logs){
	var form = new formidable.IncomingForm();
	var user = 'test'; //FIXME ユーザー取得
	//TODO 権限チェック

	form.parse(request, function( err, fields, files) {
		if(typeof fields.body != "undefined" && fields.body.trim() !== "" ){
			var body = fields.body.replace(/\r\n?/g,"\n").trim();
			var tags = fields.tags.trim().replace(/\s/, " ").replace(/\s{2,}/, " ").split(" ").filter(Boolean);
			ns.post(body,tags,'test',function(err){
				if (err){
					returnResponse(response,500,'err','txt',logs);
					errorLog({msg:'投稿失敗',err:err});
				} else {
					returnResponse(response,200,'posted','txt',logs);
				}
			});
		} else { //本文無しは転送
			redirect(response,303,'',logs);
		}
	});
}

//返送
function returnResponse(response,statusCode,content,type,logs){
	response.writeHead(statusCode, {'Content-Type': contentType[type]});
	response.end(content);

	logs.statusCode = statusCode;
	accessLog(logs);
}

//転送
function redirect(response,statusCode,url,logs){
	response.writeHead(statusCode, {"location": URL_BASE + url});
	response.end();

	logs.statusCode = statusCode;
	accessLog(logs);
}

//アクセスログ
function accessLog(logs){
	//TODO apache形式でファイルに書き出したりできるように
	console.log(logs.requestTime + ' ' + logs.host + ' ' + logs.httpVersion + ' ' + logs.method + ' ' + logs.url + ' ' + logs.statusCode + httpStatus[logs.statusCode] + ' ' + logs.userAgent + logs.msg);
	console.log(logs.statusCode + httpStatus[logs.statusCode]);
}

//エラーログ
function errorLog(log){
	//TODO オブジェクト受け取るようにする
	//TODO apache形式でファイルに書き出したりできるように
	console.log(log);
}

/** ニアスケイプwebモジュール本体
 *
 * node httpモジュールから呼ばれる
 * リクエストメソッドによって処理を振り分ける
 */
function nsweb(request, response) {

	var requestTime = new Date();

	var logs = {
		requestTime : requestTime ,
		host        : request.headers.host ,
		url         : request.url ,
		httpVersion : request.httpVersion ,
		method      : request.method ,
		statusCode  : 500 ,
		userAgent   : request.headers['user-agent'] ,
		msg         : '' ,
	}

 	//PENDING 先にURLで振り分けるべき？
	if (request.method == 'GET') {
		httpGet(request, response,logs);
	} else if (request.method == 'POST') {
		httpPost(request, response,logs);
	} else { //非許可HTTPメソッドは405
		returnResponse(response,405,'405' + httpStatus[403],'txt',logs);
	}

	//TODO あとで消す デバッグ用
	// /*
	console.log({
		request            : "######################################" ,
		requestTime        : requestTime ,
		url                : request.url ,
		headers            : request.headers ,
		http               : 'v' + request.httpVersion + ' ' + request.method ,
		idleStart          : Date(request.client._idleStart) ,
		monotonicStartTime : request.client._monotonicStartTime ,
	});
	// */
	//console.log(request);
}

/**
 * エクスポート
 */
module.exports = nsweb;
<|endoftext|>


<commit_before>import { Serializer } from 'ember-cli-mirage';

export default Serializer.extend({
    keyForCollection(modelName) {
        if (modelName === 'version-download') {
            return 'version_downloads';
        }

        return Serializer.prototype.keyForCollection.apply(this, arguments);
    }
});
<commit_msg>mirage/serializers/application: Use ActiveModelSerializer base class<commit_after>import { ActiveModelSerializer } from 'ember-cli-mirage';

export default ActiveModelSerializer.extend({
    keyForCollection(modelName) {
        if (modelName === 'version-download') {
            return 'version_downloads';
        }

        return ActiveModelSerializer.prototype.keyForCollection.apply(this, arguments);
    }
});
<|endoftext|>


<commit_before>/**
 * Link to the project's GitHub page:
 * https://github.com/pickhardt/coffeescript-codemirror-mode
 */
CodeMirror.defineMode("coffeescript", function(conf) {
  var ERRORCLASS = "error";

  function wordRegexp(words) {
    return new RegExp("^((" + words.join(")|(") + "))\\b");
  }

  var operators = /^(?:->|=>|\+[+=]?|-[\-=]?|\*[\*=]?|\/[\/=]?|[=!]=|<[><]?=?|>>?=?|%=?|&=?|\|=?|\^=?|\~|!|\?)/;
  var delimiters = /^(?:[()\[\]{},:`=;]|\.\.?\.?)/;
  var identifiers = /^[_A-Za-z$][_A-Za-z$0-9]*/;
  var properties = /^(@|this\.)[_A-Za-z$][_A-Za-z$0-9]*/;

  var wordOperators = wordRegexp(["and", "or", "not",
                                  "is", "isnt", "in",
                                  "instanceof", "typeof"]);
  var indentKeywords = ["for", "while", "loop", "if", "unless", "else",
                        "switch", "try", "catch", "finally", "class"];
  var commonKeywords = ["break", "by", "continue", "debugger", "delete",
                        "do", "in", "of", "new", "return", "then",
                        "this", "throw", "when", "until"];

  var keywords = wordRegexp(indentKeywords.concat(commonKeywords));

  indentKeywords = wordRegexp(indentKeywords);

  var stringPrefixes = /^('{3}|\"{3}|['\"])/;
  var regexPrefixes = /^(\/{3}|\/)/;
  var commonConstants = ["Infinity", "NaN", "undefined", "null", "true", "false", "on", "off", "yes", "no"];
  var constants = wordRegexp(commonConstants);

  // Tokenizers
  function tokenBase(stream, state) {
    // Handle scope changes
    if (stream.sol()) {
      if (state.scope.align === null) state.scope.align = false;
      var scopeOffset = state.scope.offset;
      if (stream.eatSpace()) {
        var lineOffset = stream.indentation();
        if (lineOffset > scopeOffset && state.scope.type == "coffee") {
          return "indent";
        } else if (lineOffset < scopeOffset) {
          return "dedent";
        }
        return null;
      } else {
        if (scopeOffset > 0) {
          dedent(stream, state);
        }
      }
    }
    if (stream.eatSpace()) {
      return null;
    }

    var ch = stream.peek();

    // Handle docco title comment (single line)
    if (stream.match("####")) {
      stream.skipToEnd();
      return "comment";
    }

    // Handle multi line comments
    if (stream.match("###")) {
      state.tokenize = longComment;
      return state.tokenize(stream, state);
    }

    // Single line comment
    if (ch === "#") {
      stream.skipToEnd();
      return "comment";
    }

    // Handle number literals
    if (stream.match(/^-?[0-9\.]/, false)) {
      var floatLiteral = false;
      // Floats
      if (stream.match(/^-?\d*\.\d+(e[\+\-]?\d+)?/i)) {
        floatLiteral = true;
      }
      if (stream.match(/^-?\d+\.\d*/)) {
        floatLiteral = true;
      }
      if (stream.match(/^-?\.\d+/)) {
        floatLiteral = true;
      }

      if (floatLiteral) {
        // prevent from getting extra . on 1..
        if (stream.peek() == "."){
          stream.backUp(1);
        }
        return "number";
      }
      // Integers
      var intLiteral = false;
      // Hex
      if (stream.match(/^-?0x[0-9a-f]+/i)) {
        intLiteral = true;
      }
      // Decimal
      if (stream.match(/^-?[1-9]\d*(e[\+\-]?\d+)?/)) {
        intLiteral = true;
      }
      // Zero by itself with no other piece of number.
      if (stream.match(/^-?0(?![\dx])/i)) {
        intLiteral = true;
      }
      if (intLiteral) {
        return "number";
      }
    }

    // Handle strings
    if (stream.match(stringPrefixes)) {
      state.tokenize = tokenFactory(stream.current(), "string");
      return state.tokenize(stream, state);
    }
    // Handle regex literals
    if (stream.match(regexPrefixes)) {
      if (stream.current() != "/" || stream.match(/^.*\//, false)) { // prevent highlight of division
        state.tokenize = tokenFactory(stream.current(), "string-2");
        return state.tokenize(stream, state);
      } else {
        stream.backUp(1);
      }
    }

    // Handle operators and delimiters
    if (stream.match(operators) || stream.match(wordOperators)) {
      return "operator";
    }
    if (stream.match(delimiters)) {
      return "punctuation";
    }

    if (stream.match(constants)) {
      return "atom";
    }

    if (stream.match(keywords)) {
      return "keyword";
    }

    if (stream.match(identifiers)) {
      return "variable";
    }

    if (stream.match(properties)) {
      return "property";
    }

    // Handle non-detected items
    stream.next();
    return ERRORCLASS;
  }

  function tokenFactory(delimiter, outclass) {
    var singleline = delimiter.length == 1;
    return function(stream, state) {
      while (!stream.eol()) {
        stream.eatWhile(/[^'"\/\\]/);
        if (stream.eat("\\")) {
          stream.next();
          if (singleline && stream.eol()) {
            return outclass;
          }
        } else if (stream.match(delimiter)) {
          state.tokenize = tokenBase;
          return outclass;
        } else {
          stream.eat(/['"\/]/);
        }
      }
      if (singleline) {
        if (conf.mode.singleLineStringErrors) {
          outclass = ERRORCLASS;
        } else {
          state.tokenize = tokenBase;
        }
      }
      return outclass;
    };
  }

  function longComment(stream, state) {
    while (!stream.eol()) {
      stream.eatWhile(/[^#]/);
      if (stream.match("###")) {
        state.tokenize = tokenBase;
        break;
      }
      stream.eatWhile("#");
    }
    return "comment";
  }

  function indent(stream, state, type) {
    type = type || "coffee";
    var offset = 0, align = false, alignOffset = null;
    for (var scope = state.scope; scope; scope = scope.prev) {
      if (scope.type === "coffee") {
        offset = scope.offset + conf.indentUnit;
        break;
      }
    }
    if (type !== "coffee") {
      align = null;
      alignOffset = stream.column() + stream.current().length;
    } else if (state.scope.align) {
      state.scope.align = false;
    }
    state.scope = {
      offset: offset,
      type: type,
      prev: state.scope,
      align: align,
      alignOffset: alignOffset
    };
  }

  function dedent(stream, state) {
    if (!state.scope.prev) return;
    if (state.scope.type === "coffee") {
      var _indent = stream.indentation();
      var matched = false;
      for (var scope = state.scope; scope; scope = scope.prev) {
        if (_indent === scope.offset) {
          matched = true;
          break;
        }
      }
      if (!matched) {
        return true;
      }
      while (state.scope.prev && state.scope.offset !== _indent) {
        state.scope = state.scope.prev;
      }
      return false;
    } else {
      state.scope = state.scope.prev;
      return false;
    }
  }

  function tokenLexer(stream, state) {
    var style = state.tokenize(stream, state);
    var current = stream.current();

    // Handle "." connected identifiers
    if (current === ".") {
      style = state.tokenize(stream, state);
      current = stream.current();
      if (/^\.[\w$]+$/.test(current)) {
        return "variable";
      } else {
        return ERRORCLASS;
      }
    }

    // Handle scope changes.
    if (current === "return") {
      state.dedent += 1;
    }
    if (((current === "->" || current === "=>") &&
         !state.lambda &&
         !stream.peek())
        || style === "indent") {
      indent(stream, state);
    }
    var delimiter_index = "[({".indexOf(current);
    if (delimiter_index !== -1) {
      indent(stream, state, "])}".slice(delimiter_index, delimiter_index+1));
    }
    if (indentKeywords.exec(current)){
      indent(stream, state);
    }
    if (current == "then"){
      dedent(stream, state);
    }

    if (style === "dedent") {
      if (dedent(stream, state)) {
        return ERRORCLASS;
      }
    }
    delimiter_index = "])}".indexOf(current);
    if (delimiter_index !== -1) {
      while (state.scope.type == "coffee" && state.scope.prev)
        state.scope = state.scope.prev;
      if (state.scope.type == current)
        state.scope = state.scope.prev;
    }
    if (state.dedent > 0 && stream.eol() && state.scope.type == "coffee") {
      if (state.scope.prev) state.scope = state.scope.prev;
      state.dedent -= 1;
    }

    return style;
  }

  var external = {
    startState: function(basecolumn) {
      return {
        tokenize: tokenBase,
        scope: {offset:basecolumn || 0, type:"coffee", prev: null, align: false},
        lastToken: null,
        lambda: false,
        dedent: 0
      };
    },

    token: function(stream, state) {
      var fillAlign = state.scope.align === null && state.scope;
      if (fillAlign && stream.sol()) fillAlign.align = false;

      var style = tokenLexer(stream, state);
      if (fillAlign && style && style != "comment") fillAlign.align = true;

      state.lastToken = {style:style, content: stream.current()};

      if (stream.eol() && stream.lambda) {
        state.lambda = false;
      }

      return style;
    },

    indent: function(state, text) {
      if (state.tokenize != tokenBase) return 0;
      var scope = state.scope;
      var closer = "])}".indexOf(text.charAt(0)) > -1;
      if (closer) while (scope.type == "coffee" && scope.prev) scope = scope.prev;
      var closes = scope.type === text.charAt(0);
      if (scope.align)
        return scope.alignOffset - (closes ? 1 : 0);
      else
        return (closes ? scope.prev : scope).offset;
    },

    lineComment: "#",
    fold: "indent"
  };
  return external;
});

CodeMirror.defineMIME("text/x-coffeescript", "coffeescript");
<commit_msg>[coffeescript mode] Fix bug introduced by 567a94b89cbe8b<commit_after>/**
 * Link to the project's GitHub page:
 * https://github.com/pickhardt/coffeescript-codemirror-mode
 */
CodeMirror.defineMode("coffeescript", function(conf) {
  var ERRORCLASS = "error";

  function wordRegexp(words) {
    return new RegExp("^((" + words.join(")|(") + "))\\b");
  }

  var operators = /^(?:->|=>|\+[+=]?|-[\-=]?|\*[\*=]?|\/[\/=]?|[=!]=|<[><]?=?|>>?=?|%=?|&=?|\|=?|\^=?|\~|!|\?)/;
  var delimiters = /^(?:[()\[\]{},:`=;]|\.\.?\.?)/;
  var identifiers = /^[_A-Za-z$][_A-Za-z$0-9]*/;
  var properties = /^(@|this\.)[_A-Za-z$][_A-Za-z$0-9]*/;

  var wordOperators = wordRegexp(["and", "or", "not",
                                  "is", "isnt", "in",
                                  "instanceof", "typeof"]);
  var indentKeywords = ["for", "while", "loop", "if", "unless", "else",
                        "switch", "try", "catch", "finally", "class"];
  var commonKeywords = ["break", "by", "continue", "debugger", "delete",
                        "do", "in", "of", "new", "return", "then",
                        "this", "throw", "when", "until"];

  var keywords = wordRegexp(indentKeywords.concat(commonKeywords));

  indentKeywords = wordRegexp(indentKeywords);

  var stringPrefixes = /^('{3}|\"{3}|['\"])/;
  var regexPrefixes = /^(\/{3}|\/)/;
  var commonConstants = ["Infinity", "NaN", "undefined", "null", "true", "false", "on", "off", "yes", "no"];
  var constants = wordRegexp(commonConstants);

  // Tokenizers
  function tokenBase(stream, state) {
    // Handle scope changes
    if (stream.sol()) {
      if (state.scope.align === null) state.scope.align = false;
      var scopeOffset = state.scope.offset;
      if (stream.eatSpace()) {
        var lineOffset = stream.indentation();
        if (lineOffset > scopeOffset && state.scope.type == "coffee") {
          return "indent";
        } else if (lineOffset < scopeOffset) {
          return "dedent";
        }
        return null;
      } else {
        if (scopeOffset > 0) {
          dedent(stream, state);
        }
      }
    }
    if (stream.eatSpace()) {
      return null;
    }

    var ch = stream.peek();

    // Handle docco title comment (single line)
    if (stream.match("####")) {
      stream.skipToEnd();
      return "comment";
    }

    // Handle multi line comments
    if (stream.match("###")) {
      state.tokenize = longComment;
      return state.tokenize(stream, state);
    }

    // Single line comment
    if (ch === "#") {
      stream.skipToEnd();
      return "comment";
    }

    // Handle number literals
    if (stream.match(/^-?[0-9\.]/, false)) {
      var floatLiteral = false;
      // Floats
      if (stream.match(/^-?\d*\.\d+(e[\+\-]?\d+)?/i)) {
        floatLiteral = true;
      }
      if (stream.match(/^-?\d+\.\d*/)) {
        floatLiteral = true;
      }
      if (stream.match(/^-?\.\d+/)) {
        floatLiteral = true;
      }

      if (floatLiteral) {
        // prevent from getting extra . on 1..
        if (stream.peek() == "."){
          stream.backUp(1);
        }
        return "number";
      }
      // Integers
      var intLiteral = false;
      // Hex
      if (stream.match(/^-?0x[0-9a-f]+/i)) {
        intLiteral = true;
      }
      // Decimal
      if (stream.match(/^-?[1-9]\d*(e[\+\-]?\d+)?/)) {
        intLiteral = true;
      }
      // Zero by itself with no other piece of number.
      if (stream.match(/^-?0(?![\dx])/i)) {
        intLiteral = true;
      }
      if (intLiteral) {
        return "number";
      }
    }

    // Handle strings
    if (stream.match(stringPrefixes)) {
      state.tokenize = tokenFactory(stream.current(), "string");
      return state.tokenize(stream, state);
    }
    // Handle regex literals
    if (stream.match(regexPrefixes)) {
      if (stream.current() != "/" || stream.match(/^.*\//, false)) { // prevent highlight of division
        state.tokenize = tokenFactory(stream.current(), "string-2");
        return state.tokenize(stream, state);
      } else {
        stream.backUp(1);
      }
    }

    // Handle operators and delimiters
    if (stream.match(operators) || stream.match(wordOperators)) {
      return "operator";
    }
    if (stream.match(delimiters)) {
      return "punctuation";
    }

    if (stream.match(constants)) {
      return "atom";
    }

    if (stream.match(keywords)) {
      return "keyword";
    }

    if (stream.match(identifiers)) {
      return "variable";
    }

    if (stream.match(properties)) {
      return "property";
    }

    // Handle non-detected items
    stream.next();
    return ERRORCLASS;
  }

  function tokenFactory(delimiter, outclass) {
    var singleline = delimiter.length == 1;
    return function(stream, state) {
      while (!stream.eol()) {
        stream.eatWhile(/[^'"\/\\]/);
        if (stream.eat("\\")) {
          stream.next();
          if (singleline && stream.eol()) {
            return outclass;
          }
        } else if (stream.match(delimiter)) {
          state.tokenize = tokenBase;
          return outclass;
        } else {
          stream.eat(/['"\/]/);
        }
      }
      if (singleline) {
        if (conf.mode.singleLineStringErrors) {
          outclass = ERRORCLASS;
        } else {
          state.tokenize = tokenBase;
        }
      }
      return outclass;
    };
  }

  function longComment(stream, state) {
    while (!stream.eol()) {
      stream.eatWhile(/[^#]/);
      if (stream.match("###")) {
        state.tokenize = tokenBase;
        break;
      }
      stream.eatWhile("#");
    }
    return "comment";
  }

  function indent(stream, state, type) {
    type = type || "coffee";
    var offset = 0, align = false, alignOffset = null;
    for (var scope = state.scope; scope; scope = scope.prev) {
      if (scope.type === "coffee") {
        offset = scope.offset + conf.indentUnit;
        break;
      }
    }
    if (type !== "coffee") {
      align = null;
      alignOffset = stream.column() + stream.current().length;
    } else if (state.scope.align) {
      state.scope.align = false;
    }
    state.scope = {
      offset: offset,
      type: type,
      prev: state.scope,
      align: align,
      alignOffset: alignOffset
    };
  }

  function dedent(stream, state) {
    if (!state.scope.prev) return;
    if (state.scope.type === "coffee") {
      var _indent = stream.indentation();
      var matched = false;
      for (var scope = state.scope; scope; scope = scope.prev) {
        if (_indent === scope.offset) {
          matched = true;
          break;
        }
      }
      if (!matched) {
        return true;
      }
      while (state.scope.prev && state.scope.offset !== _indent) {
        state.scope = state.scope.prev;
      }
      return false;
    } else {
      state.scope = state.scope.prev;
      return false;
    }
  }

  function tokenLexer(stream, state) {
    var style = state.tokenize(stream, state);
    var current = stream.current();

    // Handle "." connected identifiers
    if (current === ".") {
      style = state.tokenize(stream, state);
      current = stream.current();
      if (/^\.[\w$]+$/.test(current)) {
        return "variable";
      } else {
        return ERRORCLASS;
      }
    }

    // Handle scope changes.
    if (current === "return") {
      state.dedent += 1;
    }
    if (((current === "->" || current === "=>") &&
         !state.lambda &&
         !stream.peek())
        || style === "indent") {
      indent(stream, state);
    }
    var delimiter_index = "[({".indexOf(current);
    if (delimiter_index !== -1) {
      indent(stream, state, "])}".slice(delimiter_index, delimiter_index+1));
    }
    if (indentKeywords.exec(current)){
      indent(stream, state);
    }
    if (current == "then"){
      dedent(stream, state);
    }

    if (style === "dedent") {
      if (dedent(stream, state)) {
        return ERRORCLASS;
      }
    }
    delimiter_index = "])}".indexOf(current);
    if (delimiter_index !== -1) {
      while (state.scope.type == "coffee" && state.scope.prev)
        state.scope = state.scope.prev;
      if (state.scope.type == current)
        state.scope = state.scope.prev;
    }
    if (state.dedent > 0 && stream.eol() && state.scope.type == "coffee") {
      if (state.scope.prev) state.scope = state.scope.prev;
      state.dedent -= 1;
    }

    return style;
  }

  var external = {
    startState: function(basecolumn) {
      return {
        tokenize: tokenBase,
        scope: {offset:basecolumn || 0, type:"coffee", prev: null, align: false},
        lastToken: null,
        lambda: false,
        dedent: 0
      };
    },

    token: function(stream, state) {
      var fillAlign = state.scope.align === null && state.scope;
      if (fillAlign && stream.sol()) fillAlign.align = false;

      var style = tokenLexer(stream, state);
      if (fillAlign && style && style != "comment") fillAlign.align = true;

      state.lastToken = {style:style, content: stream.current()};

      if (stream.eol() && stream.lambda) {
        state.lambda = false;
      }

      return style;
    },

    indent: function(state, text) {
      if (state.tokenize != tokenBase) return 0;
      var scope = state.scope;
      var closer = text && "])}".indexOf(text.charAt(0)) > -1;
      if (closer) while (scope.type == "coffee" && scope.prev) scope = scope.prev;
      var closes = closer && scope.type === text.charAt(0);
      if (scope.align)
        return scope.alignOffset - (closes ? 1 : 0);
      else
        return (closes ? scope.prev : scope).offset;
    },

    lineComment: "#",
    fold: "indent"
  };
  return external;
});

CodeMirror.defineMIME("text/x-coffeescript", "coffeescript");
<|endoftext|>