<commit_before>/*******************************************************************************
 * The MIT License (MIT)
 * Copyright © 2015 Inshua,inshua@gmail.com, All rights reserved.
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and
 * associated documentation files (the “Software”), to deal in the Software without restriction,
 * including without limitation the rights to use, copy, modify, merge, publish, distribute,
 * sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all copies or substantial
 * portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING
 * BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES
 * OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 *******************************************************************************/

/**
 * 所有收集器
 * @namespace
 */
d2js.Collectors = function(){}

/**
 * 所有收集器的管道函数
 * @namespace
 */
d2js.Collectors.Pipelines = function(){}

d2js.KNOWN_COLLECTORS = {};

d2js.KNOWN_COLLECT_PIPELINES = {};

/**
 * 发起收集函数。收集总入口。设置好数据路径(html attribute `data`)和收集器(html attribute `collector`)后，调用该函数，绘制所有相关元素。
 * 也可使用封装了该函数的 jQuery 函数:
 * ```js
 * $(htmlElement).collect(baseData, direct, customCollectors)
 * ```
 * @param [htmlElement=document.body] {HTMLElement} html 元素，从该元素及其子元素收集。
 * @param [baseData=d2js.dataset} {object} 数据，要渲染的数据
 * @param [direct=false] {bool} true - 直接收集  false - 使用绝对数据路径收集
 * @param [customCollectors] {object} 自定义收集器（含管道）。如
 * ```js
 * {
 * 	myCollector : function(el, value){return $(el).height()}
 * }
 * ```
 * 对 htmlElement 及其子元素有指定 collector="myCollector|s"，该函数会被套用。
 */
d2js.collect = function(htmlElement, pattern, customCollectors){

	d2js.travel(htmlElement, pattern, collectElement)

	function collectElement(e, crumb){
		if(e.hasAttribute('no-collect')) return 'stop';

		var collector = e.getAttribute('collector');
		if(collector){
			if(e.hasAttribute('trace-collect')) debugger;

			var pipelined = (collector.indexOf('|') != -1);
			if(pipelined){		// pipeline 函数仅翻译值，其有可能会加 html 效果
				var parr = collector.split('|');
				for(var i=0; i<parr.length - 1; i++){
					var fun = extractPipeline(parr[i].trim(), e);
					if(fun != null) {
						var piped = fun.apply(null, [e].concat(crumb));
						crumb[0] = piped;
					}
				}
				collector = parr[parr.length -1];
			}
			var fun = extractCollector(collector.trim(), e);
			if(fun){
				var r = fun.apply(null, [e].concat(crumb));
				$(e).trigger('d2js.collected', crumb, collector);
				//if(direct && r == 'break') return;
			} else {
				console.error(collector + ' not found');
			}
		}
	}

	function extractCollector(desc, e){
		if($.hasData(e)){
			var d = $.data(e)['d2js.collectors'];
			var fun = d && d[desc];
			if(fun) return fun;
		}
		if(customCollectors){
			var fun = customCollectors[desc];
			d2js._store(e, 'd2js.collectors', desc, fun);
			if(fun) return fun;
		}
		return d2js.extractCachedFunction(desc, d2js.Collectors, 'd2js.Collectors.', d2js.KNOWN_COLLECTORS);
	}

	function extractPipeline(pipelineDesc, e){
		if($.hasData(e)){
			var d = $.data(e)['d2js.collectors'];
			var fun = d && d[pipelineDesc];
			if(fun) return fun;
		}
		if(customCollectors){
			var fun = customCollectors[pipelineDesc];
			d2js._store(e, 'd2js.collectors', pipelineDesc, fun);
			if(fun) return fun;
		}
		return d2js.extractCachedFunction(pipelineDesc, d2js.Collectors.Pipelines, 'd2js.Collectors.Pipelines.', d2js.KNOWN_COLLECT_PIPELINES);
	}

};

+(function ( $ ) {
    $.fn.collect = function(pattern, customCollectors) {
    	this.each(function(){
    		d2js.collect(this, pattern, customCollectors);
    	});
    	return this;
    };
}( jQuery ));

/**
 * 常用 html 元素收集器管道函数。收集 html element 的 value 属性或 innerHTML 属性。
 * 通常收集器写为 c|s 前者由控件获取内容，后者设置到对象，中间可插入其它管道，如 c|n|s
 */
d2js.Collectors.Pipelines.c = d2js.KNOWN_COLLECT_PIPELINES['c'] = function(element, value, columnName, row, index, rows, _1, table){
	var newValue = null;
	if('value' in element){
		newValue = element.value;
	} else if('innerHTML' in element){
		newValue = element.innerHTML;
	} else {
		throw new Error('unsupported element type' + element.nodeName);
	}
	return newValue;
}

/**
 * 复选框勾中状态收集器。单独使用时，勾中为 true, 未勾中为 false
 */
d2js.Collectors.Pipelines.check = d2js.KNOWN_COLLECT_PIPELINES['check'] = function(element, value, columnName, row, index, rows, _1, table){
	return element.checked;
}

/**
 * 设置对象属性收集器。
 * s 是 set 或 std 的首字母。
 * 该函数设置新值到数据对象。用法 collector="c|s"。遇到普通对象，则使用 obj[attr] = value, 遇到 d2js.DataRow，则调用 row._set('attr', value)。
 * @param newValue {object} 从前面的管道函数获得
 */
d2js.Collectors.s = d2js.KNOWN_COLLECTORS['s'] = function(element, newValue, attr, obj, index, rows, _1, table){
	if(newValue === '') newValue = null;
	if(obj != null){	// dont test attr in obj
		if(obj.set){
			obj.set(attr, newValue);
		} else if(obj._set){
			obj._set(attr, newValue);
		} else {
			obj[attr] = newValue;
		}
	}
}

/**
 * 数字转换管道。将值转为数字类型。用法 collector="c|n|s"
 * @returns {number} 数字
 */
d2js.Collectors.Pipelines.n = d2js.KNOWN_COLLECT_PIPELINES['n'] = function(element, newValue, columnName, row, index, rows, _1, table){
	if(newValue instanceof String) newValue = newValue.trim();
	if(isNaN(newValue)){
		return null;
	} else {
		return newValue * 1;
	}
}

/**
 * 日期转换管道。将值转为 Date 类型。用法 collector="c|d|s"
 * @param element
 * @param newValue
 * @returns {Date}
 */
d2js.Collectors.Pipelines.d = d2js.KNOWN_COLLECT_PIPELINES['d'] = function(element, newValue, columnName, row, index, rows, _1, table){
	if(newValue instanceof String) newValue = newValue.trim();
	if(!newValue){
		return null;
	} else if(newValue instanceof Date){
		return newValue;
	} else {
		return Date.parse(newValue, element.getAttribute('format'));
	}
}

/**
 * molecule收集器管道。
 * 对支持 getValue 的 molecule 收集。
 * 用法: colector="m|s"
 */
d2js.Collectors.Pipelines.m = d2js.KNOWN_COLLECT_PIPELINES['m'] = function(element, value, columnName, row, index, rows, _1, table){
	var m = Molecule.of(element);
	if(m == null || !m.getValue){
		return;
	} else {
		return m.getValue();
	}
}

/**
 * repeater 收集器。对 repeater 渲染器产生的元素进行收集。
 * usage:
 * ```html
 * <div data="#authors,rows" renderer="repeater" collector="repeater">
		<h2 repeater="true"><span data="name" renderer="std"></span></h2>
		<h2 repeater-empty="true">no data found</h2>
	</div>
	```
 * @member
 */
d2js.Collectors.repeater = function(element, rows){
	var e = $(element);
	var copies = e.find('[repeater-copy]');
	copies.each(function(idx, c){
		if($(c).closest('[renderer=repeater]').is(e)){
			c.removeAttribute('no-collect');
			var row = $(c).data('repeater-obj');
			if(row) d2js.collect(c, row, true);
			c.setAttribute('no-collect', true);
		}
	});
}

/**
 * Object Column 值设置器
 * 当 DataRow['field'] 为对象时，如果 element 绑定的是对象的属性，应当使用该收集器。
 * 例如
 * ```html
 * <input data="#table,rows,0,contact,email">
 * ```
 * 该表的 contact 字段为一JSON字段，表现在前端为一个对象，其中 email 是一个属性，此时应使用如下收集方式
 * ```html
 * <input data="#table,rows,0,contact,email" collector="c|oc">
 * ```
 * 如使用
 * ```html
 * <input data="#table,rows,0,contact,email" collector="c|s">
 * ```
 * 则编辑时虽然值变化了，但行状态不会变化，将导致数据不提交。
 *
 * @param element
 * @param newValue
 */
d2js.Collectors.oc = d2js.KNOWN_COLLECTORS['oc'] = function(element, newValue){
	var row = null;
	for(var i=1; i<arguments.length-1; i++){
		var colname = arguments[i+1];
		if(arguments[i] instanceof d2js.DataRow){
			row = arguments[i];
			for(i+=2; i<arguments.length-1; i+=2){
				if(arguments[i] == null){
					var attr = arguments[i-1];
					arguments[i] = arguments[i-2][attr] = {};
				}
			}
			break;
		}
	}

	if(!row) return;

	if(row._state == 'none'){
		if(row._origin == null){
			row._origin = JSON.parse(JSON.stringify(row._toJson()));
		}
	}

	d2js.Collectors.s.apply(this, arguments);

	if(row._state == 'none'){
		if(JSON.stringify(row._toJson()) != JSON.stringify(row._origin)){
			row._state = 'edit';
		}
	}
}

<commit_msg>oc 收集器转换完成<commit_after>/*******************************************************************************
 * The MIT License (MIT)
 * Copyright © 2015 Inshua,inshua@gmail.com, All rights reserved.
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and
 * associated documentation files (the “Software”), to deal in the Software without restriction,
 * including without limitation the rights to use, copy, modify, merge, publish, distribute,
 * sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all copies or substantial
 * portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING
 * BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES
 * OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 *******************************************************************************/

/**
 * 所有收集器
 * @namespace
 */
d2js.Collectors = function(){}

/**
 * 所有收集器的管道函数
 * @namespace
 */
d2js.Collectors.Pipelines = function(){}

d2js.KNOWN_COLLECTORS = {};

d2js.KNOWN_COLLECT_PIPELINES = {};

/**
 * 发起收集函数。收集总入口。设置好数据路径(html attribute `data`)和收集器(html attribute `collector`)后，调用该函数，绘制所有相关元素。
 * 也可使用封装了该函数的 jQuery 函数:
 * ```js
 * $(htmlElement).collect(baseData, direct, customCollectors)
 * ```
 * @param [htmlElement=document.body] {HTMLElement} html 元素，从该元素及其子元素收集。
 * @param [baseData=d2js.dataset} {object} 数据，要渲染的数据
 * @param [direct=false] {bool} true - 直接收集  false - 使用绝对数据路径收集
 * @param [customCollectors] {object} 自定义收集器（含管道）。如
 * ```js
 * {
 * 	myCollector : function(el, value){return $(el).height()}
 * }
 * ```
 * 对 htmlElement 及其子元素有指定 collector="myCollector|s"，该函数会被套用。
 */
d2js.collect = function(htmlElement, pattern, customCollectors){

	d2js.travel(htmlElement, pattern, collectElement)

	function collectElement(e, crumb){
		if(e.hasAttribute('no-collect')) return 'stop';

		var collector = e.getAttribute('collector');
		if(collector){
			if(e.hasAttribute('trace-collect')) debugger;

			var pipelined = (collector.indexOf('|') != -1);
			if(pipelined){		// pipeline 函数仅翻译值，其有可能会加 html 效果
				var parr = collector.split('|');
				for(var i=0; i<parr.length - 1; i++){
					var fun = extractPipeline(parr[i].trim(), e);
					if(fun != null) {
						var piped = fun.apply(null, [e].concat(crumb));
						crumb[0] = piped;
					}
				}
				collector = parr[parr.length -1];
			}
			var fun = extractCollector(collector.trim(), e);
			if(fun){
				var r = fun.apply(null, [e].concat(crumb));
				$(e).trigger('d2js.collected', crumb, collector);
				//if(direct && r == 'break') return;
			} else {
				console.error(collector + ' not found');
			}
		}
	}

	function extractCollector(desc, e){
		if($.hasData(e)){
			var d = $.data(e)['d2js.collectors'];
			var fun = d && d[desc];
			if(fun) return fun;
		}
		if(customCollectors){
			var fun = customCollectors[desc];
			d2js._store(e, 'd2js.collectors', desc, fun);
			if(fun) return fun;
		}
		return d2js.extractCachedFunction(desc, d2js.Collectors, 'd2js.Collectors.', d2js.KNOWN_COLLECTORS);
	}

	function extractPipeline(pipelineDesc, e){
		if($.hasData(e)){
			var d = $.data(e)['d2js.collectors'];
			var fun = d && d[pipelineDesc];
			if(fun) return fun;
		}
		if(customCollectors){
			var fun = customCollectors[pipelineDesc];
			d2js._store(e, 'd2js.collectors', pipelineDesc, fun);
			if(fun) return fun;
		}
		return d2js.extractCachedFunction(pipelineDesc, d2js.Collectors.Pipelines, 'd2js.Collectors.Pipelines.', d2js.KNOWN_COLLECT_PIPELINES);
	}

};

+(function ( $ ) {
    $.fn.collect = function(pattern, customCollectors) {
    	this.each(function(){
    		d2js.collect(this, pattern, customCollectors);
    	});
    	return this;
    };
}( jQuery ));

/**
 * 常用 html 元素收集器管道函数。收集 html element 的 value 属性或 innerHTML 属性。
 * 通常收集器写为 c|s 前者由控件获取内容，后者设置到对象，中间可插入其它管道，如 c|n|s
 */
d2js.Collectors.Pipelines.c = d2js.KNOWN_COLLECT_PIPELINES['c'] = function(element, value, columnName, row, index, rows, _1, table){
	var newValue = null;
	if('value' in element){
		newValue = element.value;
	} else if('innerHTML' in element){
		newValue = element.innerHTML;
	} else {
		throw new Error('unsupported element type' + element.nodeName);
	}
	return newValue;
}

/**
 * 复选框勾中状态收集器。单独使用时，勾中为 true, 未勾中为 false
 */
d2js.Collectors.Pipelines.check = d2js.KNOWN_COLLECT_PIPELINES['check'] = function(element, value, columnName, row, index, rows, _1, table){
	return element.checked;
}

/**
 * 设置对象属性收集器。
 * s 是 set 或 std 的首字母。
 * 该函数设置新值到数据对象。用法 collector="c|s"。遇到普通对象，则使用 obj[attr] = value, 遇到 d2js.DataRow，则调用 row._set('attr', value)。
 * @param newValue {object} 从前面的管道函数获得
 */
d2js.Collectors.s = d2js.KNOWN_COLLECTORS['s'] = function(element, newValue, attr, obj, index, rows, _1, table){
	if(newValue === '') newValue = null;
	if(obj != null){	// dont test attr in obj
		if(obj.set){
			obj.set(attr, newValue);
		} else if(obj._set){
			obj._set(attr, newValue);
		} else {
			obj[attr] = newValue;
		}
	}
}

/**
 * 数字转换管道。将值转为数字类型。用法 collector="c|n|s"
 * @returns {number} 数字
 */
d2js.Collectors.Pipelines.n = d2js.KNOWN_COLLECT_PIPELINES['n'] = function(element, newValue, columnName, row, index, rows, _1, table){
	if(newValue instanceof String) newValue = newValue.trim();
	if(isNaN(newValue)){
		return null;
	} else {
		return newValue * 1;
	}
}

/**
 * 日期转换管道。将值转为 Date 类型。用法 collector="c|d|s"
 * @param element
 * @param newValue
 * @returns {Date}
 */
d2js.Collectors.Pipelines.d = d2js.KNOWN_COLLECT_PIPELINES['d'] = function(element, newValue, columnName, row, index, rows, _1, table){
	if(newValue instanceof String) newValue = newValue.trim();
	if(!newValue){
		return null;
	} else if(newValue instanceof Date){
		return newValue;
	} else {
		return Date.parse(newValue, element.getAttribute('format'));
	}
}

/**
 * molecule收集器管道。
 * 对支持 getValue 的 molecule 收集。
 * 用法: colector="m|s"
 */
d2js.Collectors.Pipelines.m = d2js.KNOWN_COLLECT_PIPELINES['m'] = function(element, value, columnName, row, index, rows, _1, table){
	var m = Molecule.of(element);
	if(m == null || !m.getValue){
		return;
	} else {
		return m.getValue();
	}
}

/**
 * repeater 收集器。对 repeater 渲染器产生的元素进行收集。
 * usage:
 * ```html
 * <div data="#authors,rows" renderer="repeater" collector="repeater">
		<h2 repeater="true"><span data="name" renderer="std"></span></h2>
		<h2 repeater-empty="true">no data found</h2>
	</div>
	```
 * @member
 */
d2js.Collectors.repeater = function(element, rows){
	var e = $(element);
	var copies = e.find('[repeater-copy]');
	copies.each(function(idx, c){
		if($(c).closest('[renderer=repeater]').is(e)){
			c.removeAttribute('no-collect');
			var row = $(c).data('repeater-obj');
			if(row) d2js.collect(c, row, true);
			c.setAttribute('no-collect', true);
		}
	});
}

/**
 * Object Column 值设置器
 * 当 DataRow['field'] 为对象时，如果 element 绑定的是对象的属性，应当使用该收集器。
 * 例如
 * ```html
 * <input data="#table,rows,0,contact,email">
 * ```
 * 该表的 contact 字段为一JSON字段，表现在前端为一个对象，其中 email 是一个属性，此时应使用如下收集方式
 * ```html
 * <input data="#table,rows,0,contact,email" collector="c|oc">
 * ```
 * 如使用
 * ```html
 * <input data="#table,rows,0,contact,email" collector="c|s">
 * ```
 * 则编辑时虽然值变化了，但行状态不会变化，将导致数据不提交。
 *
 * @param element
 * @param newValue
 */
d2js.Collectors.oc = d2js.KNOWN_COLLECTORS['oc'] = function(element, newValue){
	var row = null;
	for(var i = arguments.length-1; i>1; i--){
		var colname = arguments[i-1];
		if(arguments[i] instanceof d2js.DataRow){
			row = arguments[i];
			for(i-=2; i>1; i-=2){
				if(arguments[i] == null){
					var attr = arguments[i+1];
					arguments[i] = arguments[i+2][attr] = {};
				}
			}
			break;
		}
	}

	if(!row) return;

	if(row._state == 'none'){
		if(row._origin == null){
			row._origin = JSON.parse(JSON.stringify(row._toJson()));
		}
	}

	d2js.Collectors.s.apply(this, arguments);

	if(row._state == 'none'){
		if(JSON.stringify(row._toJson()) != JSON.stringify(row._origin)){
			row._state = 'edit';
		}
	}
}

<|endoftext|>


<commit_before><commit_msg>mixedmethod<commit_after><|endoftext|>


<commit_before>/*
 * a bot that help you get orientation at DevNet
 *
 */

var debug = require("debug")("samples");
var fine = require("debug")("samples:fine");

// Starts your Webhook with default configuration where the SPARK API access token is read from the SPARK_TOKEN env variable
var SparkBot = require("../../sparkbot/webhook");
var bot = new SparkBot();

var SparkClient = require("node-sparky");
var spark = new SparkClient({ token: process.env.SPARK_TOKEN });

var Events = require("./events.js");

bot.onCommand("about", function (command) {
    spark.messageSendRoom(command.message.roomId, {
        markdown: "```\n{\n   'author':'Brought to you by Cisco DevNet',\n   'code':'https://github.com/ObjectIsAdvantag/sparkbot-webhook-samples/blob/master/examples/devnet/bot.js',\n   'description':'shows upcoming DevNet events',\n   'healthcheck':'GET https://heroku/',\n   'webhook':'POST https://heroku/'\n}\n```"
    });
});

bot.onCommand("fallback", function (command) {
    // so happy to join
    spark.messageSendRoom(command.message.roomId, {
        text: "**sorry, I did not understand**"
    })
        .then(function (message) {
            // show how to use
            showHelp(command.message.roomId);
        });
});
bot.onCommand("help", function (command) {
    showHelp(command.message.roomId);
});
function showHelp(roomId) {
    spark.messageSendRoom(roomId, {
        markdown: "I can tell about DevNet events\n- /about\n- /help\n- /next [#max]: show upcoming #max events, defaults to 5\n- /now: show events happening now\n"
    });
}

bot.onCommand("next", function (command) {

    // let's acknowledge we received the order
    spark.messageSendRoom(command.message.roomId, {
        markdown: "_heard you! asking my crystal ball..._"
    });

    var limit = command.args[0];
    if (!limit) limit = 5;
    if (limit < 1) limit = 1;

    Events.fetchNext(limit, function (err, events) {
        if (err) {
            spark.messageSendRoom(command.message.roomId, {
                 markdown: "**sorry, ball seems broken :-(**"
            });
            return;
        }

        spark.messageSendRoom(command.message.roomId, {
            markdown: events
        });
    });
});

bot.onCommand("now", function (command) {
    // let's acknowledge we received the order
    spark.messageSendRoom(command.message.roomId, {
        markdown: "_heard you! let's check what's happening now..._"
    });

    Events.fetchCurrent(function (err, events) {
        if (err) {
            spark.messageSendRoom(command.message.roomId, {
                 markdown: "**sorry, could not contact the organizers :-(**"
            });
            return;
        }

        spark.messageSendRoom(command.message.roomId, {
            markdown: events
        });
    });
});

bot.onEvent("memberships", "created", function (trigger) {
    var newMembership = trigger.data; // see specs here: https://developer.ciscospark.com/endpoint-memberships-get.html
    if (newMembership.personId == bot.interpreter.person.id) {
        debug("bot's just added to room: " + trigger.data.roomId);

        // so happy to join
        spark.messageSendRoom(trigger.data.roomId, {
            text: "Hi, I am the DevNet Bot !"
        })
            .then(function (message) {
                if (message.roomType == "group") {
                    spark.messageSendRoom(message.roomId, {
                        markdown: "**Note: this is a 'Group' room,  I will wake up only when mentionned, ex: @" + bot.interpreter.nickName + " " +  bot.interpreter.prefix + "help**"
                    })
                        .then(function (message) {
                            showHelp(message.roomId);
                        });
                }
                else {
                    showHelp(message.roomId);
                }
            });
    }
});
<commit_msg>updated CiscoDevNet (bot) endpoint in /about<commit_after>/*
 * a bot that help you get orientation at DevNet
 *
 */

var debug = require("debug")("samples");
var fine = require("debug")("samples:fine");

// Starts your Webhook with default configuration where the SPARK API access token is read from the SPARK_TOKEN env variable
var SparkBot = require("../../sparkbot/webhook");
var bot = new SparkBot();

var SparkClient = require("node-sparky");
var spark = new SparkClient({ token: process.env.SPARK_TOKEN });

var Events = require("./events.js");

bot.onCommand("about", function (command) {
    spark.messageSendRoom(command.message.roomId, {
        markdown: "```\n{\n   'author':'Brought to you by Cisco DevNet',\n   'code':'https://github.com/ObjectIsAdvantag/sparkbot-webhook-samples/blob/master/examples/devnet/bot.js',\n   'description':'shows upcoming DevNet events',\n   'healthcheck':'GET http://devnet-events-sparkbot.herokuapp.com/',\n   'webhook':'POST http://devnet-events-sparkbot.herokuapp.com/'\n}\n```"
    });
});

bot.onCommand("fallback", function (command) {
    // so happy to join
    spark.messageSendRoom(command.message.roomId, {
        text: "**sorry, I did not understand**"
    })
        .then(function (message) {
            // show how to use
            showHelp(command.message.roomId);
        });
});
bot.onCommand("help", function (command) {
    showHelp(command.message.roomId);
});
function showHelp(roomId) {
    spark.messageSendRoom(roomId, {
        markdown: "I can tell about DevNet events\n- /about\n- /help\n- /next [#max]: show upcoming #max events, defaults to 5\n- /now: show events happening now\n"
    });
}

bot.onCommand("next", function (command) {

    // let's acknowledge we received the order
    spark.messageSendRoom(command.message.roomId, {
        markdown: "_heard you! asking my crystal ball..._"
    });

    var limit = command.args[0];
    if (!limit) limit = 5;
    if (limit < 1) limit = 1;

    Events.fetchNext(limit, function (err, events) {
        if (err) {
            spark.messageSendRoom(command.message.roomId, {
                 markdown: "**sorry, ball seems broken :-(**"
            });
            return;
        }

        spark.messageSendRoom(command.message.roomId, {
            markdown: events
        });
    });
});

bot.onCommand("now", function (command) {
    // let's acknowledge we received the order
    spark.messageSendRoom(command.message.roomId, {
        markdown: "_heard you! let's check what's happening now..._"
    });

    Events.fetchCurrent(function (err, events) {
        if (err) {
            spark.messageSendRoom(command.message.roomId, {
                 markdown: "**sorry, could not contact the organizers :-(**"
            });
            return;
        }

        spark.messageSendRoom(command.message.roomId, {
            markdown: events
        });
    });
});

bot.onEvent("memberships", "created", function (trigger) {
    var newMembership = trigger.data; // see specs here: https://developer.ciscospark.com/endpoint-memberships-get.html
    if (newMembership.personId == bot.interpreter.person.id) {
        debug("bot's just added to room: " + trigger.data.roomId);

        // so happy to join
        spark.messageSendRoom(trigger.data.roomId, {
            text: "Hi, I am the DevNet Bot !"
        })
            .then(function (message) {
                if (message.roomType == "group") {
                    spark.messageSendRoom(message.roomId, {
                        markdown: "**Note: this is a 'Group' room,  I will wake up only when mentionned, ex: @" + bot.interpreter.nickName + " " +  bot.interpreter.prefix + "help**"
                    })
                        .then(function (message) {
                            showHelp(message.roomId);
                        });
                }
                else {
                    showHelp(message.roomId);
                }
            });
    }
});
<|endoftext|>


<commit_before>!function($) {

  "use strict"; // jshint ;_;

  /* MODAL_FORM PUBLIC CLASS DEFINITION
   * =============================== */

  var ModalForm = function ( element, options ) {

    this.options = options;
    this.$element = $(element);

    this.$element
      .on('click.modal-form.close', '[data-dismiss="modal"]', $.proxy(this.hide, this))
      .on('click.modal-form.reset', 'input[type=reset], [data-dismiss="modal-reset"]', $.proxy(this.reset, this))
  //    .on('click.modal-form.submit', 'input[type=submit], [data-dismiss="modal-submit"]', $.proxy(this.submit, this));
      .on('ajax:success',  'form', $.proxy(this.on_success, this))
      .on('ajax:error',    'form', $.proxy(this.on_error, this))
      .on('flash:add',     $.proxy(this.on_flash, this))
      //.on('ajax:complete', 'form', $.proxy(this.on_complete, this))
  }

  /* NOTE: MODAL_FORM EXTENDS BOOTSTRAP-MODAL.js
   * ========================================== */

  ModalForm.prototype = $.extend({}, $.fn.modal.Constructor.prototype, {

    constructor: ModalForm

  , reset: function(e) {
      console.debug("reset");
      $(e.target).closest('form')[0].reset();
      this.hide(e);
    }

  , hide: function(e) {
      $.fn.modal.Constructor.prototype.hide.apply(this, [e]);
      this.$element.off('modal_form');
    }

  , submit: function(e) {
      var $form = $(e.target);
      e && e.preventDefault();
    }

  , on_success: function(ev, data, status, xhr) {
      // Hide, then display flash messages globally
      if (data.redirect) {
        window.location.replace(data.redirect);
      } else {
        this.hide();
        this.$element.trigger('flash', status);
      }
    }

  , on_error: function(xhr, status, error) {
      // Display flash messages
      this.$element.trigger('flash', error);
    }

  , on_flash: function(e, messages) {
      e.stopPropagation();
    }
  });

  $.fn.modal_form = function(option) {
    return this.each(function() {
      var $this = $(this)
        , data = $this.data('modal_form')
        , options = $.extend({}, $.fn.modal_form.defaults, $this.data(), typeof option == 'object' && option);
      if (!data) $this.data('modal_form', (data = new ModalForm(this, options)));
      if (typeof option == 'string') data[option]();
      else if (options.show) data.show();
    });
  }

  $.fn.modal_form.Constructor = ModalForm;

  $.fn.modal_form.defaults = $.extend({}, $.fn.modal.defaults, {

  });

  /* MODAL-FORM DATA-API
   * =================== */

  $(function () {
    $('body').on('click.modal.data-api', '[data-toggle="modal-form"]', function ( e ) {
      var $this = $(this), href
        , $target = $($this.attr('data-target') || (href = $this.attr('href')) && href.replace(/.*(?=#[^\s]+$)/, '')) //strip for ie7
        , option = $target.data('modal') ? 'toggle' : $.extend({}, $target.data(), $this.data());

      e.preventDefault();
      $target.modal_form(option);
    });
  });
}(window.jQuery);
<commit_msg>Fix $.fn.modal_form plugin<commit_after>!function($) {

  "use strict"; // jshint ;_;

  /* MODAL_FORM PUBLIC CLASS DEFINITION
   * =============================== */

  var ModalForm = function ( element, options ) {

    this.options = options;
    this.$element = $(element);

    this.$element
      .on('click.modal-form.close', '[data-dismiss="modal"]', $.proxy(this.hide, this))
      .on('click.modal-form.reset', 'input[type=reset], [data-dismiss="modal-reset"]', $.proxy(this.reset, this))
      .on('ajax:complete', 'form', $.proxy(this.on_complete, this))
      .on('ajax:flash',    'form', $.proxy(this.on_flash, this))
  }

  /* NOTE: MODAL_FORM EXTENDS BOOTSTRAP-MODAL.js
   * ========================================== */

  ModalForm.prototype = $.extend({}, $.fn.modal.Constructor.prototype, {

    constructor: ModalForm

  , reset: function(e) {
      $(e.target).closest('form')[0].reset();
      this.hide(e);
    }

  , hide: function(e) {
      $.fn.modal.Constructor.prototype.hide.apply(this, [e]);
      this.$element.off('modal_form');
    }

  , on_complete: function(ev, xhr, status) {
      // Replace form body
      this.$element.find('.modal-body').html(xhr.responseText);

      // Maybe handle AJAX/JSON redirect or refresh
      if (xhr.status == 278) {
        // Handle 278 redirect (AJAX redirect)
        window.location.assign(xhr.getResponseHeader('location'));
      } else if (xhr.status == 279) {
        window.location.assign(window.location.href);
      }

      // Maybe handle AJAX flash messages
      var flash_types = ["error", "alert", "notice", "warning"]
        , type_i, message
        , flash;

      for (type_i in flash_types) {
        message = xhr.getResponseHeader('x-flash-' + flash_types[type_i]);
        message = JSON.parse(message);
        if (message) {
          if (!flash)
            flash = {};
          flash[flash_types[type_i]] = message;
        }
      }

      if (flash)
        this.$element.find('form').trigger('ajax:flash', flash);
    }

  , on_flash: function(e, flash) {
      // Find or create the flash-message holder
      var $flash_holder = this.$element.find('.flash')
        , type, ucase_type
        , messages, message, message_i;

      if ($flash_holder.length == 0) {
        $flash_holder = $('

');
        this.$element.find('.modal-body').prepend($flash_holder);
      } else {
        $flash_holder.empty();
      }

      for (type in flash) {
        if (flash[type]) {
          if (typeof(flash[type]) == "string")
            flash[type] = [flash[type]];

          for (message_i in flash[type]) {
            $flash_holder.append('
' + flash[type][message_i] + '
');
          }
        }
      }
      e.stopPropagation();
    }
  });

  $.fn.modal_form = function(option) {
    return this.each(function() {
      var $this = $(this)
        , data = $this.data('modal_form')
        , options = $.extend({}, $.fn.modal_form.defaults, $this.data(), typeof option == 'object' && option);
      if (!data) $this.data('modal_form', (data = new ModalForm(this, options)));
      if (typeof option == 'string') data[option]();
      else if (options.show) data.show();
    });
  }

  $.fn.modal_form.Constructor = ModalForm;

  $.fn.modal_form.defaults = $.extend({}, $.fn.modal.defaults, {

  });

  /* MODAL-FORM DATA-API
   * =================== */

  $(function () {
    $('body').on('click.modal-form.data-api', '[data-toggle="modal-form"]', function ( e ) {
      var $this = $(this), href
        , $target = $($this.attr('data-target') || (href = $this.attr('href')) && href.replace(/.*(?=#[^\s]+$)/, '')) //strip for ie7
        , option = $target.data('modal-form') ? 'toggle' : $.extend({}, $target.data(), $this.data());

      e.preventDefault();
      $target.modal_form(option);
    });
  });
}(window.jQuery);
<|endoftext|>


<commit_before>"use strict";

var assert = require('assert');

var smush = function(w1,w2) {
    return w1 + w2;
};

describe('word smushing', function() {
    it('should join words with no overlap', function() {
	assert.equal('thecat', smush('the', 'cat'));
    });
});
<commit_msg>test that fails<commit_after>"use strict";

var assert = require('assert');

var smush = function(w1,w2) {
    return w1 + w2;
};

describe('word smushing', function() {
    it('should join words with no overlap', function() {
	assert.equal('thecat', smush('the', 'cat'));
    });

    it('should do single character overlaps', function() {
	assert.equal('henot', smush('hen', 'not'));
    });
});
<|endoftext|>


<commit_before>// ==UserScript==
// @name        MetaFilter User Makeup
// @namespace   https://github.com/roukaour/
// @description Assigns each MetaFilter user a random but consistent color and symbol.
// @include     *://*.metafilter.com/*
// @require     https://ajax.googleapis.com/ajax/libs/jquery/2.2.0/jquery.min.js
// @version     1.6
// @grant       none
// @run-at      document-end
// ==/UserScript==

// Inspired by valrus' Colorful Comments
// https://metatalk.metafilter.com/24039/Color-Comments-By-User
// https://github.com/valrus/mefi-comment-colors/

function hashCode(s) {
	// String#hashCode from Java
	var hash = 0;
	for (var i = 0; i < s.length; i++) {
		hash = s.charCodeAt(i) + ((hash << 5) - hash);
	}
	return hash;
}

function luma(r, g, b) {
	// sRGB to RGB and RGB to luma formulas from W3C accessibility guidelines
	// https://www.w3.org/TR/WCAG20/#relativeluminancedef
	var c = [r, g, b];
	for (var i = 0; i < 3; i++) {
		c[i] /= 255;
		if (c[i] <= 0.03928) {
			c[i] /= 12.92;
		} else {
			c[i] = Math.pow((c[i] + 0.055) / 1.055, 2.4);
		}
	}
	return 0.2126 * c[0] + 0.7152 * c[1] + 0.0722 * c[2];
}

function hashToColorSpan(x) {
	// Based on Cristian Sanchez's code:
	// http://stackoverflow.com/a/3426956/70175
	var color = (x & 0xFFFFFF).toString(16).toUpperCase();
	color = '00000'.substring(0, 6 - color.length) + color;
	var r = (x & 0xFF0000) >> 16, g = (x & 0xFF00) >> 8, b = x & 0xFF;
	var contrast = luma(r, g, b) > 0.179 ? '000' : 'FFF';
	return '';
}

function hashToSymbol(x) {
	var symbols = '●■▲▼◆▰★✪♠♣♥♦◈◉✠✿';
	var i = (x & 0xF000000) / 0x1000000;
	return symbols.substring(i, i + 1);
}

function applyMakeup() {
	var bylines = document.getElementsByClassName('smallcopy');
	var i = bylines.length;
	while (i--) {
		var byline = bylines[i];
		if (!byline.innerHTML.startsWith('posted by')) continue;
		var userlink = byline.getElementsByTagName('a')[0];
		var username = userlink.innerHTML;
		if (username.startsWith('<span style=')) continue;
		var hash = hashCode(username);
		userlink.innerHTML = hashToColorSpan(hash) +
			'<big>' + hashToSymbol(hash) + '</big> ' + username + '';
	}
}

applyMakeup();
$("#newcomments").on('mefi-comments', applyMakeup);
<commit_msg>Consistently use single quotes for strings.<commit_after>// ==UserScript==
// @name        MetaFilter User Makeup
// @namespace   https://github.com/roukaour/
// @description Assigns each MetaFilter user a random but consistent color and symbol.
// @include     *://*.metafilter.com/*
// @require     https://ajax.googleapis.com/ajax/libs/jquery/2.2.0/jquery.min.js
// @version     1.6
// @grant       none
// @run-at      document-end
// ==/UserScript==

// Inspired by valrus' Colorful Comments
// https://metatalk.metafilter.com/24039/Color-Comments-By-User
// https://github.com/valrus/mefi-comment-colors/

function hashCode(s) {
	// String#hashCode from Java
	var hash = 0;
	for (var i = 0; i < s.length; i++) {
		hash = s.charCodeAt(i) + ((hash << 5) - hash);
	}
	return hash;
}

function luma(r, g, b) {
	// sRGB to RGB and RGB to luma formulas from W3C accessibility guidelines
	// https://www.w3.org/TR/WCAG20/#relativeluminancedef
	var c = [r, g, b];
	for (var i = 0; i < 3; i++) {
		c[i] /= 255;
		if (c[i] <= 0.03928) {
			c[i] /= 12.92;
		} else {
			c[i] = Math.pow((c[i] + 0.055) / 1.055, 2.4);
		}
	}
	return 0.2126 * c[0] + 0.7152 * c[1] + 0.0722 * c[2];
}

function hashToColorSpan(x) {
	// Based on Cristian Sanchez's code:
	// http://stackoverflow.com/a/3426956/70175
	var color = (x & 0xFFFFFF).toString(16).toUpperCase();
	color = '00000'.substring(0, 6 - color.length) + color;
	var r = (x & 0xFF0000) >> 16, g = (x & 0xFF00) >> 8, b = x & 0xFF;
	var contrast = luma(r, g, b) > 0.179 ? '000' : 'FFF';
	return '';
}

function hashToSymbol(x) {
	var symbols = '●■▲▼◆▰★✪♠♣♥♦◈◉✠✿';
	var i = (x & 0xF000000) / 0x1000000;
	return symbols.substring(i, i + 1);
}

function applyMakeup() {
	var bylines = document.getElementsByClassName('smallcopy');
	var i = bylines.length;
	while (i--) {
		var byline = bylines[i];
		if (!byline.innerHTML.startsWith('posted by')) continue;
		var userlink = byline.getElementsByTagName('a')[0];
		var username = userlink.innerHTML;
		if (username.startsWith('<span style=')) continue;
		var hash = hashCode(username);
		userlink.innerHTML = hashToColorSpan(hash) +
			'<big>' + hashToSymbol(hash) + '</big> ' + username + '';
	}
}

applyMakeup();
$('#newcomments').on('mefi-comments', applyMakeup);
<|endoftext|>


<commit_before>//= require store/spree_core

$(function() {
  // when the #country field changes
  $("select#workshop_province").live("change", function() {
    // make a POST call and replace the content
    var province = $('select#workshop_province :selected').val();
    if(province == "") province="0";
    $.get('/workshops/update_town_select/' + province, function(data){
      $("#workshopTowns").html(data);
    });
    return false;
  });
});

$(function() {
  // when the #country field changes
  $("select#provincia").live("change", function() {
    // make a POST call and replace the content
    var province = $('select#provincia :selected').val();
    if(province == "") province="0";
    $.get('/workshops/update_workshop_list/' + province, function(data){
      $("#workshopsList").html(data);
    });
    return false;
  });
});
<commit_msg>New Workshop search<commit_after>//= require store/spree_core

$(function() {
  // when the #country field changes
  $("select#workshop_province").live("change", function() {
    // make a POST call and replace the content
    var province = $('select#workshop_province :selected').val();
    if(province == "") province="0";
    $.get('/workshops/update_town_select/' + province, function(data){
      $("#workshopTowns").html(data);
    });
    return false;
  });
});

$(function() {
  // when the #country field changes
  $("select#Provincia").live("change", function() {
    // make a POST call and replace the content
    var province = $('select#Provincia :selected').val();
    if(province == "") province="0";
    $.get('/workshops/update_workshop_list/' + province, function(data){
      $("#workshopsList").html(data);
    });
    return false;
  });
});
<|endoftext|>


<commit_before>process.env.DEBUG = '*';

const PluginLoader = require('../..');
const EventEmitter = require('events');

var app = new EventEmitter();
app.name = 'App';
app.on('plugins.ready', function(){
    app.logger.info('Application plugins loaded');
    app.debug('here we are!!');
});

const manager = new PluginLoader({
    context: app,
    basepath: __dirname,
    afterMount: (context) => context.emit('plugins.ready')
});

manager.find('./plugins').then((plugins) => {
    plugins.push({
        'debug': function(plugin, context, options){
        context.debug = plugin('in-load');
    }});

    return manager.load(plugins);
})
.then((plugins)=>{
    return manager.mount(plugins)
})
.catch(console.error);
<commit_msg>update examples<commit_after>'use strict';

process.env.DEBUG = '*';

const PluginLoader = require('../..');
const EventEmitter = require('events');

const app = new EventEmitter();

app.name = 'App';

app.on('plugins.ready', _ => {
    this.logger.info('Application plugins loaded');
    this.debug('here we are!!');
});

const manager = new PluginLoader({
    context: app,
    basepath: __dirname,
    afterMount: (context) => context.emit('plugins.ready')
});

manager.find('./plugins').then(plugins => {
    plugins.push({
        'debug': function(plugin, context, options) {
            context.debug = plugin('in-load');
        }
    });

    return manager.load(plugins);

}).then(plugins => {
    return manager.mount(plugins);
}).catch(console.error);<|endoftext|>


<commit_before>#!/usr/bin/env node

const { GitProcess } = require('dugite')
const childProcess = require('child_process')
const fs = require('fs')
const klaw = require('klaw')
const minimist = require('minimist')
const path = require('path')

const SOURCE_ROOT = path.normalize(path.dirname(__dirname))

const BLACKLIST = new Set([
  ['atom', 'browser', 'mac', 'atom_application.h'],
  ['atom', 'browser', 'mac', 'atom_application_delegate.h'],
  ['atom', 'browser', 'resources', 'win', 'resource.h'],
  ['atom', 'browser', 'notifications', 'mac', 'notification_center_delegate.h'],
  ['atom', 'browser', 'ui', 'cocoa', 'atom_menu_controller.h'],
  ['atom', 'browser', 'ui', 'cocoa', 'atom_ns_window.h'],
  ['atom', 'browser', 'ui', 'cocoa', 'atom_ns_window_delegate.h'],
  ['atom', 'browser', 'ui', 'cocoa', 'atom_preview_item.h'],
  ['atom', 'browser', 'ui', 'cocoa', 'atom_touch_bar.h'],
  ['atom', 'browser', 'ui', 'cocoa', 'atom_inspectable_web_contents_view.h'],
  ['atom', 'browser', 'ui', 'cocoa', 'event_dispatching_window.h'],
  ['atom', 'browser', 'ui', 'cocoa', 'touch_bar_forward_declarations.h'],
  ['atom', 'browser', 'ui', 'cocoa', 'NSColor+Hex.h'],
  ['atom', 'browser', 'ui', 'cocoa', 'NSString+ANSI.h'],
  ['atom', 'common', 'api', 'api_messages.h'],
  ['atom', 'common', 'common_message_generator.cc'],
  ['atom', 'common', 'common_message_generator.h'],
  ['atom', 'common', 'node_includes.h'],
  ['atom', 'node', 'osfhandle.cc'],
  ['spec', 'static', 'jquery-2.0.3.min.js']
].map(tokens => path.join(SOURCE_ROOT, ...tokens)))

function spawnAndCheckExitCode (cmd, args, opts) {
  opts = Object.assign({ stdio: 'inherit' }, opts)
  const status = childProcess.spawnSync(cmd, args, opts).status
  if (status) process.exit(status)
}

const LINTERS = [ {
  key: 'c++',
  roots: ['atom'],
  test: filename => filename.endsWith('.cc') || filename.endsWith('.h'),
  run: (opts, filenames) => {
    if (opts.fix) {
      spawnAndCheckExitCode('python', ['script/run-clang-format.py', '--fix', ...filenames])
    } else {
      spawnAndCheckExitCode('python', ['script/run-clang-format.py', ...filenames])
    }
    const result = childProcess.spawnSync('cpplint.py', filenames, { encoding: 'utf8' })
    // cpplint.py writes EVERYTHING to stderr, including status messages
    if (result.stderr) {
      for (const line of result.stderr.split(/[\r\n]+/)) {
        if (line.length && !line.startsWith('Done processing ') && line !== 'Total errors found: 0') {
          console.warn(line)
        }
      }
    }
    if (result.status) {
      process.exit(result.status)
    }
  }
}, {
  key: 'python',
  roots: ['script'],
  test: filename => filename.endsWith('.py'),
  run: (opts, filenames) => {
    const rcfile = path.normalize(path.join(SOURCE_ROOT, '..', 'third_party', 'depot_tools', 'pylintrc'))
    const args = ['--rcfile=' + rcfile, ...filenames]
    const env = Object.assign({ PYTHONPATH: path.join(SOURCE_ROOT, 'script') }, process.env)
    spawnAndCheckExitCode('pylint.py', args, { env })
  }
}, {
  key: 'javascript',
  roots: ['lib', 'spec', 'script', 'default_app'],
  ignoreRoots: ['spec/node_modules'],
  test: filename => filename.endsWith('.js'),
  run: (opts, filenames) => {
    const cmd = path.join(SOURCE_ROOT, 'node_modules', '.bin', 'eslint')
    const args = [ '--cache', ...filenames ]
    if (opts.fix) args.unshift('--fix')
    spawnAndCheckExitCode(cmd, args, { cwd: SOURCE_ROOT })
  }
}, {
  key: 'gn',
  roots: ['.'],
  test: filename => filename.endsWith('.gn') || filename.endsWith('.gni'),
  run: (opts, filenames) => {
    const allOk = filenames.map(filename => {
      const args = ['format', filename]
      if (!opts.fix) args.push('--dry-run')
      const result = childProcess.spawnSync('gn', args, { stdio: 'inherit', shell: true })
      if (result.status === 0) {
        return true
      } else if (result.status === 2) {
        console.log(`GN format errors in "${filename}". Run 'gn format "${filename}"' or rerun with --fix to fix them.`)
        return false
      } else {
        console.log(`Error running 'gn format --dry-run "${filename}"': exit code ${result.status}`)
        return false
      }
    }).every(x => x)
    if (!allOk) {
      process.exit(1)
    }
  }
}]

function parseCommandLine () {
  let help
  const opts = minimist(process.argv.slice(2), {
    boolean: [ 'c++', 'javascript', 'python', 'gn', 'help', 'changed', 'fix', 'verbose' ],
    alias: { 'c++': ['cc', 'cpp', 'cxx'], javascript: ['js', 'es'], python: 'py', changed: 'c', help: 'h', verbose: 'v' },
    unknown: arg => { help = true }
  })
  if (help || opts.help) {
    console.log('Usage: script/lint.js [--cc] [--js] [--py] [-c|--changed] [-h|--help] [-v|--verbose] [--fix]')
    process.exit(0)
  }
  return opts
}

async function findChangedFiles (top) {
  const result = await GitProcess.exec(['diff', '--name-only', '--cached'], top)
  if (result.exitCode !== 0) {
    console.log('Failed to find changed files', GitProcess.parseError(result.stderr))
    process.exit(1)
  }
  const relativePaths = result.stdout.split(/\r\n|\r|\n/g)
  const absolutePaths = relativePaths.map(x => path.join(top, x))
  return new Set(absolutePaths)
}

async function findMatchingFiles (top, test) {
  return new Promise((resolve, reject) => {
    const matches = []
    klaw(top)
      .on('end', () => resolve(matches))
      .on('data', item => {
        if (test(item.path)) {
          matches.push(item.path)
        }
      })
  })
}

async function findFiles (args, linter) {
  let filenames = []
  let whitelist = null

  // build the whitelist
  if (args.changed) {
    whitelist = await findChangedFiles(SOURCE_ROOT)
    if (!whitelist.size) {
      return []
    }
  }

  // accumulate the raw list of files
  for (const root of linter.roots) {
    const files = await findMatchingFiles(path.join(SOURCE_ROOT, root), linter.test)
    filenames.push(...files)
  }

  for (const ignoreRoot of (linter.ignoreRoots) || []) {
    const ignorePath = path.join(SOURCE_ROOT, ignoreRoot)
    if (!fs.existsSync(ignorePath)) continue

    const ignoreFiles = new Set(await findMatchingFiles(ignorePath, linter.test))
    filenames = filenames.filter(fileName => !ignoreFiles.has(fileName))
  }

  // remove blacklisted files
  filenames = filenames.filter(x => !BLACKLIST.has(x))

  // if a whitelist exists, remove anything not in it
  if (whitelist) {
    filenames = filenames.filter(x => whitelist.has(x))
  }

  // it's important that filenames be relative otherwise clang-format will
  // produce patches with absolute paths in them, which `git apply` will refuse
  // to apply.
  return filenames.map(x => path.relative(SOURCE_ROOT, x))
}

async function main () {
  const opts = parseCommandLine()

  // no mode specified? run 'em all
  if (!opts['c++'] && !opts.javascript && !opts.python && !opts.gn) {
    opts['c++'] = opts.javascript = opts.python = opts.gn = true
  }

  const linters = LINTERS.filter(x => opts[x.key])

  for (const linter of linters) {
    const filenames = await findFiles(opts, linter)
    if (filenames.length) {
      if (opts.verbose) { console.log(`linting ${filenames.length} ${linter.key} ${filenames.length === 1 ? 'file' : 'files'}`) }
      linter.run(opts, filenames)
    }
  }
}

if (process.mainModule === module) {
  main().catch((error) => {
    console.error(error)
    process.exit(1)
  })
}
<commit_msg>chore: set env when invoking gn (#15354)<commit_after>#!/usr/bin/env node

const { GitProcess } = require('dugite')
const childProcess = require('child_process')
const fs = require('fs')
const klaw = require('klaw')
const minimist = require('minimist')
const path = require('path')

const SOURCE_ROOT = path.normalize(path.dirname(__dirname))
const DEPOT_TOOLS = path.resolve(SOURCE_ROOT, '..', 'third_party', 'depot_tools')

const BLACKLIST = new Set([
  ['atom', 'browser', 'mac', 'atom_application.h'],
  ['atom', 'browser', 'mac', 'atom_application_delegate.h'],
  ['atom', 'browser', 'resources', 'win', 'resource.h'],
  ['atom', 'browser', 'notifications', 'mac', 'notification_center_delegate.h'],
  ['atom', 'browser', 'ui', 'cocoa', 'atom_menu_controller.h'],
  ['atom', 'browser', 'ui', 'cocoa', 'atom_ns_window.h'],
  ['atom', 'browser', 'ui', 'cocoa', 'atom_ns_window_delegate.h'],
  ['atom', 'browser', 'ui', 'cocoa', 'atom_preview_item.h'],
  ['atom', 'browser', 'ui', 'cocoa', 'atom_touch_bar.h'],
  ['atom', 'browser', 'ui', 'cocoa', 'atom_inspectable_web_contents_view.h'],
  ['atom', 'browser', 'ui', 'cocoa', 'event_dispatching_window.h'],
  ['atom', 'browser', 'ui', 'cocoa', 'touch_bar_forward_declarations.h'],
  ['atom', 'browser', 'ui', 'cocoa', 'NSColor+Hex.h'],
  ['atom', 'browser', 'ui', 'cocoa', 'NSString+ANSI.h'],
  ['atom', 'common', 'api', 'api_messages.h'],
  ['atom', 'common', 'common_message_generator.cc'],
  ['atom', 'common', 'common_message_generator.h'],
  ['atom', 'common', 'node_includes.h'],
  ['atom', 'node', 'osfhandle.cc'],
  ['spec', 'static', 'jquery-2.0.3.min.js']
].map(tokens => path.join(SOURCE_ROOT, ...tokens)))

function spawnAndCheckExitCode (cmd, args, opts) {
  opts = Object.assign({ stdio: 'inherit' }, opts)
  const status = childProcess.spawnSync(cmd, args, opts).status
  if (status) process.exit(status)
}

const LINTERS = [ {
  key: 'c++',
  roots: ['atom'],
  test: filename => filename.endsWith('.cc') || filename.endsWith('.h'),
  run: (opts, filenames) => {
    if (opts.fix) {
      spawnAndCheckExitCode('python', ['script/run-clang-format.py', '--fix', ...filenames])
    } else {
      spawnAndCheckExitCode('python', ['script/run-clang-format.py', ...filenames])
    }
    const result = childProcess.spawnSync('cpplint.py', filenames, { encoding: 'utf8' })
    // cpplint.py writes EVERYTHING to stderr, including status messages
    if (result.stderr) {
      for (const line of result.stderr.split(/[\r\n]+/)) {
        if (line.length && !line.startsWith('Done processing ') && line !== 'Total errors found: 0') {
          console.warn(line)
        }
      }
    }
    if (result.status) {
      process.exit(result.status)
    }
  }
}, {
  key: 'python',
  roots: ['script'],
  test: filename => filename.endsWith('.py'),
  run: (opts, filenames) => {
    const rcfile = path.join(DEPOT_TOOLS, 'pylintrc')
    const args = ['--rcfile=' + rcfile, ...filenames]
    const env = Object.assign({ PYTHONPATH: path.join(SOURCE_ROOT, 'script') }, process.env)
    spawnAndCheckExitCode('pylint.py', args, { env })
  }
}, {
  key: 'javascript',
  roots: ['lib', 'spec', 'script', 'default_app'],
  ignoreRoots: ['spec/node_modules'],
  test: filename => filename.endsWith('.js'),
  run: (opts, filenames) => {
    const cmd = path.join(SOURCE_ROOT, 'node_modules', '.bin', 'eslint')
    const args = [ '--cache', ...filenames ]
    if (opts.fix) args.unshift('--fix')
    spawnAndCheckExitCode(cmd, args, { cwd: SOURCE_ROOT })
  }
}, {
  key: 'gn',
  roots: ['.'],
  test: filename => filename.endsWith('.gn') || filename.endsWith('.gni'),
  run: (opts, filenames) => {
    const allOk = filenames.map(filename => {
      const env = Object.assign({
        CHROMIUM_BUILDTOOLS_PATH: path.resolve(SOURCE_ROOT, '..', 'buildtools'),
        DEPOT_TOOLS_WIN_TOOLCHAIN: '0'
      }, process.env)
      // Users may not have depot_tools in PATH.
      env.PATH = `${env.PATH}${path.delimiter}${DEPOT_TOOLS}`
      const args = ['format', filename]
      if (!opts.fix) args.push('--dry-run')
      const result = childProcess.spawnSync('gn', args, { env, stdio: 'inherit', shell: true })
      if (result.status === 0) {
        return true
      } else if (result.status === 2) {
        console.log(`GN format errors in "${filename}". Run 'gn format "${filename}"' or rerun with --fix to fix them.`)
        return false
      } else {
        console.log(`Error running 'gn format --dry-run "${filename}"': exit code ${result.status}`)
        return false
      }
    }).every(x => x)
    if (!allOk) {
      process.exit(1)
    }
  }
}]

function parseCommandLine () {
  let help
  const opts = minimist(process.argv.slice(2), {
    boolean: [ 'c++', 'javascript', 'python', 'gn', 'help', 'changed', 'fix', 'verbose' ],
    alias: { 'c++': ['cc', 'cpp', 'cxx'], javascript: ['js', 'es'], python: 'py', changed: 'c', help: 'h', verbose: 'v' },
    unknown: arg => { help = true }
  })
  if (help || opts.help) {
    console.log('Usage: script/lint.js [--cc] [--js] [--py] [-c|--changed] [-h|--help] [-v|--verbose] [--fix]')
    process.exit(0)
  }
  return opts
}

async function findChangedFiles (top) {
  const result = await GitProcess.exec(['diff', '--name-only', '--cached'], top)
  if (result.exitCode !== 0) {
    console.log('Failed to find changed files', GitProcess.parseError(result.stderr))
    process.exit(1)
  }
  const relativePaths = result.stdout.split(/\r\n|\r|\n/g)
  const absolutePaths = relativePaths.map(x => path.join(top, x))
  return new Set(absolutePaths)
}

async function findMatchingFiles (top, test) {
  return new Promise((resolve, reject) => {
    const matches = []
    klaw(top)
      .on('end', () => resolve(matches))
      .on('data', item => {
        if (test(item.path)) {
          matches.push(item.path)
        }
      })
  })
}

async function findFiles (args, linter) {
  let filenames = []
  let whitelist = null

  // build the whitelist
  if (args.changed) {
    whitelist = await findChangedFiles(SOURCE_ROOT)
    if (!whitelist.size) {
      return []
    }
  }

  // accumulate the raw list of files
  for (const root of linter.roots) {
    const files = await findMatchingFiles(path.join(SOURCE_ROOT, root), linter.test)
    filenames.push(...files)
  }

  for (const ignoreRoot of (linter.ignoreRoots) || []) {
    const ignorePath = path.join(SOURCE_ROOT, ignoreRoot)
    if (!fs.existsSync(ignorePath)) continue

    const ignoreFiles = new Set(await findMatchingFiles(ignorePath, linter.test))
    filenames = filenames.filter(fileName => !ignoreFiles.has(fileName))
  }

  // remove blacklisted files
  filenames = filenames.filter(x => !BLACKLIST.has(x))

  // if a whitelist exists, remove anything not in it
  if (whitelist) {
    filenames = filenames.filter(x => whitelist.has(x))
  }

  // it's important that filenames be relative otherwise clang-format will
  // produce patches with absolute paths in them, which `git apply` will refuse
  // to apply.
  return filenames.map(x => path.relative(SOURCE_ROOT, x))
}

async function main () {
  const opts = parseCommandLine()

  // no mode specified? run 'em all
  if (!opts['c++'] && !opts.javascript && !opts.python && !opts.gn) {
    opts['c++'] = opts.javascript = opts.python = opts.gn = true
  }

  const linters = LINTERS.filter(x => opts[x.key])

  for (const linter of linters) {
    const filenames = await findFiles(opts, linter)
    if (filenames.length) {
      if (opts.verbose) { console.log(`linting ${filenames.length} ${linter.key} ${filenames.length === 1 ? 'file' : 'files'}`) }
      linter.run(opts, filenames)
    }
  }
}

if (process.mainModule === module) {
  main().catch((error) => {
    console.error(error)
    process.exit(1)
  })
}
<|endoftext|>


<commit_before>/**
 * Main JS file for the /r/webdev collaborative site
 */

/**
* Entry function
*/
(function($) {
  'use strict';
   $(document).ready(initPage);
})(jQuery);

/**
* Main function. Every thing relating to page initialization should go here.
* Just attach initializing functions to the events you wish here
*/
function initPage() {
  $(document).scroll(setScrollEffectHeader);
  animateCode();
}

/**
* Sets the smooth scrolling effect on the fixed header.
*/
function setScrollEffectHeader() {
  var scroll_from_top = $(this).scrollTop(),
      $nav_container = $('header .nav-container'),
      nav_containerHeight = $nav_container.height(),
      osxWindow = $(".image-lockup"),
      limitOpacity = osxWindow.position().top - $nav_container.height() - 40;

  if (scroll_from_top > limitOpacity) {
    $nav_container.css('opacity', '0.7');
  }

  if (scroll_from_top > 315) {
    $nav_container.css('border-bottom', '1px solid #505050');
  }

  if (scroll_from_top <= limitOpacity) {
    $nav_container.css('opacity', '1');
    $nav_container.css('border-bottom', '0px');
  }
}

/**
* Animates the code inside the SVG window
*/
var code_counter = 1;

function animateCode(){
    var code =
" /** \n\
 * Sets the smooth scrolling effect on the fixed header.\n\
 */\n\n\
 function setScrollEffectHeader() {\n\
    var scroll_from_top = $(this).scrollTop(),\n\
        $nav_container = $('header .nav-container');\n\n\
    console.log(scroll_from_top);\n\n\
    if (scroll_from_top > 470) {\n\
        $nav_container.css('opacity', '0.7');\n\
    } else if (scroll_from_top > 318) {\n\
        $nav_container.css('border-bottom', '1px solid #505050');\n\
    } else {\n\
        $nav_container.css('opacity', '1');\n\
        $nav_container.css('border-bottom', '0px');\n\
    }\n\
}",
    $code_container = $('.animated-text'),
    code_words = code.split(' '),
    code_highlight = '',
    code_keyWords = ['var', 'function', 'if', 'else'],
    isColor = false;

    if (code_keyWords.indexOf(code_words[code_counter]) >= 0) {
      $code_container.append(code_highlight);
      $code_container = $('.animated-text span:last-child');
      isColor = true;
    };

  setTimeout(function() { typeCharacter($code_container, (' ' + code_words[code_counter]), 0, isColor, (code_words.length - 1) ); }, 60);
}

/**
* Simulates the text being typed
*/
function typeCharacter($element, word, index, isColor, length) {
  if (typeof word[index] === 'undefined') {
    if (code_counter !== length) {
      code_counter++;
      animateCode();
    } else {
      clearInterval(); // Stop animation
    }
  } else {
    $element.html($element.html() + word[index]);
    setTimeout(function() { typeCharacter($element, word, ++index, isColor, length);}, 60);
  }
};
<commit_msg>Fixed more jshint warnings<commit_after>/**
 * Main JS file for the /r/webdev collaborative site
 */

/**
* Entry function
*/
(function($) {
  'use strict';
   $(document).ready(initPage);
})(jQuery);

/**
* Main function. Every thing relating to page initialization should go here.
* Just attach initializing functions to the events you wish here
*/
function initPage() {
  $(document).scroll(setScrollEffectHeader);
  animateCode();
}

/**
* Sets the smooth scrolling effect on the fixed header.
*/
function setScrollEffectHeader() {
  var scroll_from_top = $(this).scrollTop(),
      $nav_container = $('header .nav-container'),
      nav_containerHeight = $nav_container.height(),
      osxWindow = $(".image-lockup"),
      limitOpacity = osxWindow.position().top - $nav_container.height() - 40;

  if (scroll_from_top > limitOpacity) {
    $nav_container.css('opacity', '0.7');
  }

  if (scroll_from_top > 315) {
    $nav_container.css('border-bottom', '1px solid #505050');
  }

  if (scroll_from_top <= limitOpacity) {
    $nav_container.css('opacity', '1');
    $nav_container.css('border-bottom', '0px');
  }
}

/**
* Animates the code inside the SVG window
*/

function animateCode(){
    var code =
    " /** \n\
     * Sets the smooth scrolling effect on the fixed header.\n\
     */\n\n\
     function setScrollEffectHeader() {\n\
        var scroll_from_top = $(this).scrollTop(),\n\
            $nav_container = $('header .nav-container');\n\n\
        console.log(scroll_from_top);\n\n\
        if (scroll_from_top > 470) {\n\
            $nav_container.css('opacity', '0.7');\n\
        } else if (scroll_from_top > 318) {\n\
            $nav_container.css('border-bottom', '1px solid #505050');\n\
        } else {\n\
            $nav_container.css('opacity', '1');\n\
            $nav_container.css('border-bottom', '0px');\n\
        }\n\
    }",
    code_counter = 1,
    $code_container = $('.animated-text'),
    code_words = code.split(' '),
    code_highlight = '',
    code_keyWords = ['var', 'function', 'if', 'else'],
    isColor = false;

    if (code_keyWords.indexOf(code_words[code_counter]) >= 0) {
      $code_container.append(code_highlight);
      $code_container = $('.animated-text span:last-child');
      isColor = true;
    }

  setTimeout(function() {typeCharacter($code_container, (' ' + code_words[code_counter]), 0, isColor, (code_words.length - 1) ); }, 60);
}

/**
* Simulates the text being typed
*/
function typeCharacter($element, word, index, isColor, length) {
  if (typeof word[index] === 'undefined') {
    if (code_counter !== length) {
      code_counter++;
      animateCode();
    } else {
      clearInterval(); // Stop animation
    }
  } else {
    $element.html($element.html() + word[index]);
    setTimeout(function() { typeCharacter($element, word, ++index, isColor, length);}, 60);
  }
}
<|endoftext|>


<commit_before>$(document).ready(function ($) {
  //navigation bar code
  $(document).load(function ($) {
    nav = $('.nav');
    navHeight = nav.height();
    navOffset = nav.offset();

    navSpacer = $('.navSpacer').css('height', navHeight);

    function checkScroll() {
      scroll = $(document).scrollTop();
      if (scroll > navOffset.top) {
        nav.css('position', 'fixed').css('top', '0');
        navSpacer.show();
      } else {
        nav.css('position', '').css('top', '');
        navSpacer.hide();
      }
      max = 0;
      current = undefined;
      $('.page').each(function(i) {
        offset = $(this).offset().top - navHeight * 2.5;
        if ((offset > max) && (scroll > offset)) {
          max = offset;
          current = $(this);
        }
      });
      $('.navButton').removeClass('active');
      if ((max > 0) && (current !== undefined)) {
        $('.navButton[href="#' + current.attr('id') + '"]').addClass('active');
      } else {
        $('.navButton[href="#top"]').addClass('active');
      }
    }

    $( window ).scroll(checkScroll);
    checkScroll();

    $('.nav .navButton, .intLink').each(function(i) {
        $(this).click(function(e) {
          e.preventDefault();
          scrollTo = 0;
          if ($(this).attr('href') != '#top') {
            scrollTo = $($(this).attr('href')).offset().top - navHeight + 2;
          }
          $('html, body').animate({
            scrollTop: scrollTo
          });
        });
    });
  });

  //slider code
  var _SlideshowTransitions = [
    //Fade
    { $Duration: 1200, $Opacity: 2 }
  ];

  var jssor_1_options = {
    $AutoPlay: true,
    $FillMode: 5,
    $SlideshowOptions: {
      $Class: $JssorSlideshowRunner$,
      Transitions: _SlideshowTransitions,
      $TransitionsOrder: 1
    },
    $BulletNavigatorOptions: {
      $Class: $JssorBulletNavigator$
    }
  };

  var jssor_1_slider = new $JssorSlider$("jssor_1", jssor_1_options);

  //responsive code begin
  //you can remove responsive code if you don't want the slider scales while window resizes
  function ScaleSlider() {
      var refSize = jssor_1_slider.$Elmt.parentNode.clientWidth;
      if (refSize) {
          refSize = Math.min(refSize, 600);
          jssor_1_slider.$ScaleWidth(refSize);
      }
      else {
          window.setTimeout(ScaleSlider, 30);
      }
  }
  ScaleSlider();
  $(window).bind("load", ScaleSlider);
  $(window).bind("resize", ScaleSlider);
  $(window).bind("orientationchange", ScaleSlider);
  //responsive code end
});
<commit_msg>Update main.js<commit_after>$(document).ready(function ($) {
  //navigation bar code
  $(document).load(function () {
    nav = $('.nav');
    navHeight = nav.height();
    navOffset = nav.offset();

    navSpacer = $('.navSpacer').css('height', navHeight);

    function checkScroll() {
      scroll = $(document).scrollTop();
      if (scroll > navOffset.top) {
        nav.css('position', 'fixed').css('top', '0');
        navSpacer.show();
      } else {
        nav.css('position', '').css('top', '');
        navSpacer.hide();
      }
      max = 0;
      current = undefined;
      $('.page').each(function(i) {
        offset = $(this).offset().top - navHeight * 2.5;
        if ((offset > max) && (scroll > offset)) {
          max = offset;
          current = $(this);
        }
      });
      $('.navButton').removeClass('active');
      if ((max > 0) && (current !== undefined)) {
        $('.navButton[href="#' + current.attr('id') + '"]').addClass('active');
      } else {
        $('.navButton[href="#top"]').addClass('active');
      }
    }

    $( window ).scroll(checkScroll);
    checkScroll();

    $('.nav .navButton, .intLink').each(function(i) {
        $(this).click(function(e) {
          e.preventDefault();
          scrollTo = 0;
          if ($(this).attr('href') != '#top') {
            scrollTo = $($(this).attr('href')).offset().top - navHeight + 2;
          }
          $('html, body').animate({
            scrollTop: scrollTo
          });
        });
    });
  });

  //slider code
  var _SlideshowTransitions = [
    //Fade
    { $Duration: 1200, $Opacity: 2 }
  ];

  var jssor_1_options = {
    $AutoPlay: true,
    $FillMode: 5,
    $SlideshowOptions: {
      $Class: $JssorSlideshowRunner$,
      Transitions: _SlideshowTransitions,
      $TransitionsOrder: 1
    },
    $BulletNavigatorOptions: {
      $Class: $JssorBulletNavigator$
    }
  };

  var jssor_1_slider = new $JssorSlider$("jssor_1", jssor_1_options);

  //responsive code begin
  //you can remove responsive code if you don't want the slider scales while window resizes
  function ScaleSlider() {
      var refSize = jssor_1_slider.$Elmt.parentNode.clientWidth;
      if (refSize) {
          refSize = Math.min(refSize, 600);
          jssor_1_slider.$ScaleWidth(refSize);
      }
      else {
          window.setTimeout(ScaleSlider, 30);
      }
  }
  ScaleSlider();
  $(window).bind("load", ScaleSlider);
  $(window).bind("resize", ScaleSlider);
  $(window).bind("orientationchange", ScaleSlider);
  //responsive code end
});
<|endoftext|>


<commit_before>// Simple uncertainty calculating helpers

// A base number class supporting uncertainties
function UNumber(value,uncertainty)
{
	this.value=parseFloat(value?Math.round(value*10e10)/10e10:0);
	// All uncertainties are considered as absolute
	this.uncertainty=Math.abs(parseFloat(uncertainty?uncertainty:0));
	this.toString = function()
	{
		return this.value+'\u00B1'+this.abs_uncertainty();
	}
	this.abs_uncertainty = function(sf)
	{
		sf=sf?sf:2;
		return sigFigs(this.uncertainty,sf);
	}
	this.rel_uncertainty = function(sf)
	{
		sf=sf?sf:2;
		if (this.value===0) {return Infinity;}
		return sigFigs(this.uncertainty/this.value,sf);
	}
	this.per_uncertainty = function(sf)
	{
		sf=sf?sf:2;
		if (this.value===0) {return Infinity;}
		return sigFigs(this.rel_uncertainty(sf)*100,sf);
	}
	this.pythag = function(n1,n2)
	{
		return Math.sqrt(Math.pow(n1,2)+Math.pow(n2,2));
	}
	this.plus = function(raw)
	{
		var number=raw;
		// convert number if needed
		if (typeof number === "number" )
		{
			number=new UNumber(raw);
		}
		// Add abs uncertainties
		var added_unc = this.pythag( number.abs_uncertainty(), this.abs_uncertainty() );
		var added_val = number.value+this.value;
		return new UNumber( added_val, added_unc );
	}
	this.minus = function(raw)
	{
		var number=raw;
		// convert number if needed
		if (typeof number === "number" )
		{
			number=new UNumber(raw);
		}
		// Add abs uncertainties
		var minus_unc = this.pythag( number.abs_uncertainty(), this.abs_uncertainty() );
		var minus_val = this.value-number.value;
		return new UNumber( minus_val, minus_unc );
	}
	this.times = function(raw)
	{
		var number=raw;
		// convert number if needed
		if (typeof number === "number" )
		{
			number=new UNumber(raw);
		}
		// Add rel uncertainties
		var times_unc = this.pythag( number.rel_uncertainty(), this.rel_uncertainty() );
		var times_val=this.value*number.value;
		return new UNumber( times_val, times_unc );
	}
	this.divide = function(raw)
	{
		var number=raw;
		// convert number if needed
		if (typeof number === "number" )
		{
			number=new UNumber(raw);
		}
		// Add rel uncertainties
		var div_unc = this.pythag( number.rel_uncertainty(), this.rel_uncertainty() );
		var div_val = this.value/number.value;
		return new UNumber( div_val, div_unc*div_val );
	}
	this.pow = function(raw)
	{
		var number=raw;
		// convert number if needed
		if (typeof number === "number" )
		{
			number=new UNumber(raw);
		}
		// times relative uncertianty by exponential
		var pow_unc = this.rel_uncertainty()*number.value;
		var pow_val = Math.pow(this.value,number.value);
		return new UNumber( pow_val, pow_unc*pow_val );
	}
	this.sqrt = function(raw)
	{
		return this.pow(new UNumber(0.5));
	}
	this.sin = function()
	{
		var val=Math.sin(this.value);
		var unc=Math.abs(Math.sin(this.value+this.uncertainty)-val);
		return new UNumber(val,unc);
	}
	this.cos = function()
	{
		var val=Math.cos(this.value);
		var unc=Math.abs(Math.cos(this.value+this.uncertainty)-val);
		return new UNumber(val,unc);
	}
	this.tan = function()
	{
		var val=Math.tan(this.value);
		var unc=Math.abs(Math.tan(this.value+this.uncertainty)-val);
		return new UNumber(val,unc);
	}
	this.log = function()
	{
		var val = Math.log(this.value)/Math.LN10;
		var unc = this.uncertainty/(this.value*Math.log(10));
		return new UNumber(val, unc);
	}
	this.ln = function()
	{
		return new UNumber(Math.log(this.value), this.uncertainty/this.value);
	}
}

// Helper function to convert between relative and absolute
function to_absolute(u,v)
{
	return u*v;
}
// Helper function to round to sig sf
function sigFigs(n, sig) {
	if(n===0){return 0;}
    var mult = Math.pow(10, sig - Math.floor(Math.log(n) / Math.LN10) - 1);
    var num = Math.round(n * mult) / mult;
    var str = num.toString();
    if (str.indexOf('.')==-1)
    {
		var sf = str.length;
		var extra = Math.max(0,sig-sf);
		if(extra!==0)
		{
			str+='.';
		}
		for (var i=0; i<extra; i++)
		{
			str+='0';
		}
		return str;
	} else { return num.toString() }
}

// Parse a UNum from a string
// - Take ± as the abs uncertainty
// - Take ±/ as the relative uncertainty
// - Take ±% as the percentage uncertainty
// Otherwise uncertainty=0
function parseUNum(string)
{
	if (string.indexOf('\u00B1')!==-1)
	{
		if (string.indexOf('\u00B1/')!==-1 )
		{
			var parts=string.split('\u00B1/');
			return new UNumber(parseFloat(parts[0]),to_absolute(parseFloat(parts[1]),parseFloat(parts[0])));
		}
		else if (string.indexOf('\u00B1%')!==-1 )
		{
			var parts=string.split('\u00B1%');
			return new UNumber(parseFloat(parts[0]),to_absolute(parseFloat(parts[1]/100),parseFloat(parts[0])));
		}
		else
		{
			var parts=string.split('\u00B1');
			return new UNumber(parseFloat(parts[0]),parseFloat(parts[1]));
		}
	} else {
		return new UNumber(parseFloat(string));
	}
}

// A nicer and more robust parsing algorithm
function parseExpression2(string)
{

	function operator_order(operator)
	{
		var ops=['sin','cos','tan','^','\u221A','\u00F7','\u00D7','+','-'].reverse();
		return ops.indexOf(operator)===-1?0:ops.indexOf(operator);
	}

	var operands=/\d|\.|\u00B1/;
	var operators=/\+|\-|\u00D7|\u00F7|\^|sin|cos|tan|\u221A|log|ln/;
	var functions=/sin|cos|tan|\u221A|log|ln/;

	// Split into tokens - either numbers or operators
	var tokens=[];
	var current='';
	var currently_number=true;
	for (var i=0; i<string.length; i++)
	{
		if(string[i]=='(' || string[i]==')' || string[i]=='\u03C0' || string[i]=='e')
		{
			tokens.push(current);
			current='';
			currently_number=false;
			tokens.push(string[i]);
			continue;
		}
		number=operands.test(string[i]);
		if(number===currently_number && string[i]!='-')
		{
			current+=string[i];
		} else {
			if(current.length)
				tokens.push(current);
			current=string[i];
			currently_number=number;
		}
	}
	if(current.length)
		tokens.push(current);
	// Massage in any negative signs after another operator
	for (var i=0; i<tokens.length; i++)
	{
		if (tokens[i]=='-' && !operands.test(tokens[i-1]))
		{
			// Remove from list
			tokens.splice(i,1);
			// Keep merging back the -ves until we get to a number
			sign='-';
			while (!operands.test(tokens[i])){
				if (i>tokens.length-1){throw 'Invalid Expression'}
				if (sign=='-'){sign='+';}
				else {sign='-';}
				tokens.splice(i,1);
			}
			// If needed apply the sign to the number now at tokens[i]
			if (sign=='-')
			{
				tokens[i]='-'+tokens[i];
			}
		}
		// Sub in pi or e
		if (tokens[i]=='\u03C0')
		{
			tokens[i]=Math.PI.toString();
		} else if (tokens[i]=='e') {
			tokens[i]=Math.E.toString();
		} else if (tokens[i].trim()=="") {
			tokens.splice(i,1);
			i--;	// Move back one index becuase we just removed one
		}
	}
	tokens=tokens.reverse();

	var operator_stack=[];
	var number_stack=[];

	while (tokens.length!==0)
	{
		var token = tokens.pop();

		if (operands.test(token))
		{
			number_stack.push(token);
		}

		else if (token=='(' || operator_stack.length===0 || (operator_order(token) > operator_order(operator_stack[operator_stack.length-1])) )
		{
			operator_stack.push(token);
		}

		else if (token==')')
		{
			while (operator_stack[operator_stack.length-1]!='(')
			{
				var operator = operator_stack.pop();
				if (functions.test(operator))
				{
					var right_operand = number_stack.pop();
					var operand = right_operand +' '+ operator;
				} else {
					var right_operand = number_stack.pop();
					var left_operand = number_stack.pop();
					var operand = left_operand +' '+ right_operand +' '+ operator;
				}
				number_stack.push(operand);
			}
			operator_stack.pop();
		}

		else if (operator_order(token) <= operator_order(operator_stack[operator_stack.length-1]))
		{
			while (operator_stack.length!==0 && operator_order(token) <= operator_order(operator_stack[operator_stack.length-1]))
			{
				var operator = operator_stack.pop();
				if (functions.test(operator))
				{
					var right_operand = number_stack.pop();
					var operand = right_operand +' '+ operator;
				} else {
					var right_operand = number_stack.pop();
					var left_operand = number_stack.pop();
					var operand = left_operand +' '+ right_operand +' '+ operator;
				}
				number_stack.push(operand);
			}
			operator_stack.push(token);
		}
	}

	while (operator_stack.length!==0 )
	{
		var operator = operator_stack.pop();
		if (functions.test(operator))
		{
			var right_operand = number_stack.pop();
			var operand = right_operand +' '+ operator;
		} else {
			var right_operand = number_stack.pop();
			var left_operand = number_stack.pop();
			var operand = left_operand +' '+ right_operand +' '+ operator;
		}
		number_stack.push(operand);
	}
	// Turn this into a parsable expression
	var exp=number_stack[0];
	var tokens=exp.split(' ');
	for (var i=0; i<tokens.length; i++)
	{
		if(operands.test(tokens[i]))
		{
			// Make UNum
			tokens[i]=parseUNum(tokens[i]);
		}
	}
	return evaluate_expression(tokens.reverse());

}

// Evalueates an expression in postfix notation
function evaluate_expression(exp)
{
	var operands=/\d|\.|\u00B1/;
	var functions=/sin|cos|tan|\u221A|log|ln/;
	var stack=[];
	while (exp.length)
	{
		token=exp.pop();
		if (operands.test(token))
		{
			stack.push(token);
		} else {
			if (functions.test(token))
			{
				var n1=stack.pop();
				stack.push(performOp(token,n1));
			} else {
				var n1=stack.pop();
				var n2=stack.pop();
				stack.push(performOp(token,n2,n1));
			}
		}
	}
	return stack.pop();
}

// do n1.op(n2)
function performOp(op, num1, num2)
{
	switch (op)
	{
		case ('+'):
			var result=num1.plus(num2);
			break;
		case ('-'):
			var result=num1.minus(num2);
			break;
		case ('\u00D7'):
			var result=num1.times(num2);
			break;
		case ('\u00F7'):
			var result=num1.divide(num2);
			break;
		case ('^'):
			var result=num1.pow(num2);
			break;
		// Here down are the functions (only need one number)
		case ('\u221A'):
			var result=num1.sqrt();
			break;
		case ('sin'):
			var result=num1.sin();
			break;
		case ('cos'):
			var result=num1.cos();
			break;
		case ('tan'):
			var result=num1.tan();
			break;
		case ('log'):
			var result=num1.log();
			break;
		case ('ln'):
			var result=num1.ln();
			break;
		default:
			throw 'Invalid Operator';
			break;
	}
	return result;
}
<commit_msg>Added even better rounding!<commit_after>// Simple uncertainty calculating helpers

// A base number class supporting uncertainties
function UNumber(value,uncertainty)
{
	this.value=parseFloat(value?value.toPrecision(12):0);
	// All uncertainties are considered as absolute
	this.uncertainty=Math.abs(parseFloat(uncertainty?uncertainty:0));
	this.toString = function()
	{
		return this.value+'\u00B1'+this.abs_uncertainty();
	}
	this.abs_uncertainty = function(sf)
	{
		sf=sf?sf:2;
		return sigFigs(this.uncertainty,sf);
	}
	this.rel_uncertainty = function(sf)
	{
		sf=sf?sf:2;
		if (this.value===0) {return Infinity;}
		return sigFigs(this.uncertainty/this.value,sf);
	}
	this.per_uncertainty = function(sf)
	{
		sf=sf?sf:2;
		if (this.value===0) {return Infinity;}
		return sigFigs(this.rel_uncertainty(sf)*100,sf);
	}
	this.pythag = function(n1,n2)
	{
		return Math.sqrt(Math.pow(n1,2)+Math.pow(n2,2));
	}
	this.plus = function(raw)
	{
		var number=raw;
		// convert number if needed
		if (typeof number === "number" )
		{
			number=new UNumber(raw);
		}
		// Add abs uncertainties
		var added_unc = this.pythag( number.abs_uncertainty(), this.abs_uncertainty() );
		var added_val = number.value+this.value;
		return new UNumber( added_val, added_unc );
	}
	this.minus = function(raw)
	{
		var number=raw;
		// convert number if needed
		if (typeof number === "number" )
		{
			number=new UNumber(raw);
		}
		// Add abs uncertainties
		var minus_unc = this.pythag( number.abs_uncertainty(), this.abs_uncertainty() );
		var minus_val = this.value-number.value;
		return new UNumber( minus_val, minus_unc );
	}
	this.times = function(raw)
	{
		var number=raw;
		// convert number if needed
		if (typeof number === "number" )
		{
			number=new UNumber(raw);
		}
		// Add rel uncertainties
		var times_unc = this.pythag( number.rel_uncertainty(), this.rel_uncertainty() );
		var times_val=this.value*number.value;
		return new UNumber( times_val, times_unc );
	}
	this.divide = function(raw)
	{
		var number=raw;
		// convert number if needed
		if (typeof number === "number" )
		{
			number=new UNumber(raw);
		}
		// Add rel uncertainties
		var div_unc = this.pythag( number.rel_uncertainty(), this.rel_uncertainty() );
		var div_val = this.value/number.value;
		return new UNumber( div_val, div_unc*div_val );
	}
	this.pow = function(raw)
	{
		var number=raw;
		// convert number if needed
		if (typeof number === "number" )
		{
			number=new UNumber(raw);
		}
		// times relative uncertianty by exponential
		var pow_unc = this.rel_uncertainty()*number.value;
		var pow_val = Math.pow(this.value,number.value);
		return new UNumber( pow_val, pow_unc*pow_val );
	}
	this.sqrt = function(raw)
	{
		return this.pow(new UNumber(0.5));
	}
	this.sin = function()
	{
		var val=Math.sin(this.value);
		var unc=Math.abs(Math.sin(this.value+this.uncertainty)-val);
		return new UNumber(val,unc);
	}
	this.cos = function()
	{
		var val=Math.cos(this.value);
		var unc=Math.abs(Math.cos(this.value+this.uncertainty)-val);
		return new UNumber(val,unc);
	}
	this.tan = function()
	{
		var val=Math.tan(this.value);
		var unc=Math.abs(Math.tan(this.value+this.uncertainty)-val);
		return new UNumber(val,unc);
	}
	this.log = function()
	{
		var val = Math.log(this.value)/Math.LN10;
		var unc = this.uncertainty/(this.value*Math.log(10));
		return new UNumber(val, unc);
	}
	this.ln = function()
	{
		return new UNumber(Math.log(this.value), this.uncertainty/this.value);
	}
}

// Helper function to convert between relative and absolute
function to_absolute(u,v)
{
	return u*v;
}
// Helper function to round to sig sf
function sigFigs(n, sig) {
	if(n===0){return 0;}
    var mult = Math.pow(10, sig - Math.floor(Math.log(n) / Math.LN10) - 1);
    var num = Math.round(n * mult) / mult;
    var str = num.toString();
    if (str.indexOf('.')==-1)
    {
		var sf = str.length;
		var extra = Math.max(0,sig-sf);
		if(extra!==0)
		{
			str+='.';
		}
		for (var i=0; i<extra; i++)
		{
			str+='0';
		}
		return str;
	} else { return num.toString() }
}

// Parse a UNum from a string
// - Take ± as the abs uncertainty
// - Take ±/ as the relative uncertainty
// - Take ±% as the percentage uncertainty
// Otherwise uncertainty=0
function parseUNum(string)
{
	if (string.indexOf('\u00B1')!==-1)
	{
		if (string.indexOf('\u00B1/')!==-1 )
		{
			var parts=string.split('\u00B1/');
			return new UNumber(parseFloat(parts[0]),to_absolute(parseFloat(parts[1]),parseFloat(parts[0])));
		}
		else if (string.indexOf('\u00B1%')!==-1 )
		{
			var parts=string.split('\u00B1%');
			return new UNumber(parseFloat(parts[0]),to_absolute(parseFloat(parts[1]/100),parseFloat(parts[0])));
		}
		else
		{
			var parts=string.split('\u00B1');
			return new UNumber(parseFloat(parts[0]),parseFloat(parts[1]));
		}
	} else {
		return new UNumber(parseFloat(string));
	}
}

// A nicer and more robust parsing algorithm
function parseExpression2(string)
{

	function operator_order(operator)
	{
		var ops=['sin','cos','tan','^','\u221A','\u00F7','\u00D7','+','-'].reverse();
		return ops.indexOf(operator)===-1?0:ops.indexOf(operator);
	}

	var operands=/\d|\.|\u00B1/;
	var operators=/\+|\-|\u00D7|\u00F7|\^|sin|cos|tan|\u221A|log|ln/;
	var functions=/sin|cos|tan|\u221A|log|ln/;

	// Split into tokens - either numbers or operators
	var tokens=[];
	var current='';
	var currently_number=true;
	for (var i=0; i<string.length; i++)
	{
		if(string[i]=='(' || string[i]==')' || string[i]=='\u03C0' || string[i]=='e')
		{
			tokens.push(current);
			current='';
			currently_number=false;
			tokens.push(string[i]);
			continue;
		}
		number=operands.test(string[i]);
		if(number===currently_number && string[i]!='-')
		{
			current+=string[i];
		} else {
			if(current.length)
				tokens.push(current);
			current=string[i];
			currently_number=number;
		}
	}
	if(current.length)
		tokens.push(current);
	// Massage in any negative signs after another operator
	for (var i=0; i<tokens.length; i++)
	{
		if (tokens[i]=='-' && !operands.test(tokens[i-1]))
		{
			// Remove from list
			tokens.splice(i,1);
			// Keep merging back the -ves until we get to a number
			sign='-';
			while (!operands.test(tokens[i])){
				if (i>tokens.length-1){throw 'Invalid Expression'}
				if (sign=='-'){sign='+';}
				else {sign='-';}
				tokens.splice(i,1);
			}
			// If needed apply the sign to the number now at tokens[i]
			if (sign=='-')
			{
				tokens[i]='-'+tokens[i];
			}
		}
		// Sub in pi or e
		if (tokens[i]=='\u03C0')
		{
			tokens[i]=Math.PI.toString();
		} else if (tokens[i]=='e') {
			tokens[i]=Math.E.toString();
		} else if (tokens[i].trim()=="") {
			tokens.splice(i,1);
			i--;	// Move back one index becuase we just removed one
		}
	}
	tokens=tokens.reverse();

	var operator_stack=[];
	var number_stack=[];

	while (tokens.length!==0)
	{
		var token = tokens.pop();

		if (operands.test(token))
		{
			number_stack.push(token);
		}

		else if (token=='(' || operator_stack.length===0 || (operator_order(token) > operator_order(operator_stack[operator_stack.length-1])) )
		{
			operator_stack.push(token);
		}

		else if (token==')')
		{
			while (operator_stack[operator_stack.length-1]!='(')
			{
				var operator = operator_stack.pop();
				if (functions.test(operator))
				{
					var right_operand = number_stack.pop();
					var operand = right_operand +' '+ operator;
				} else {
					var right_operand = number_stack.pop();
					var left_operand = number_stack.pop();
					var operand = left_operand +' '+ right_operand +' '+ operator;
				}
				number_stack.push(operand);
			}
			operator_stack.pop();
		}

		else if (operator_order(token) <= operator_order(operator_stack[operator_stack.length-1]))
		{
			while (operator_stack.length!==0 && operator_order(token) <= operator_order(operator_stack[operator_stack.length-1]))
			{
				var operator = operator_stack.pop();
				if (functions.test(operator))
				{
					var right_operand = number_stack.pop();
					var operand = right_operand +' '+ operator;
				} else {
					var right_operand = number_stack.pop();
					var left_operand = number_stack.pop();
					var operand = left_operand +' '+ right_operand +' '+ operator;
				}
				number_stack.push(operand);
			}
			operator_stack.push(token);
		}
	}

	while (operator_stack.length!==0 )
	{
		var operator = operator_stack.pop();
		if (functions.test(operator))
		{
			var right_operand = number_stack.pop();
			var operand = right_operand +' '+ operator;
		} else {
			var right_operand = number_stack.pop();
			var left_operand = number_stack.pop();
			var operand = left_operand +' '+ right_operand +' '+ operator;
		}
		number_stack.push(operand);
	}
	// Turn this into a parsable expression
	var exp=number_stack[0];
	var tokens=exp.split(' ');
	for (var i=0; i<tokens.length; i++)
	{
		if(operands.test(tokens[i]))
		{
			// Make UNum
			tokens[i]=parseUNum(tokens[i]);
		}
	}
	return evaluate_expression(tokens.reverse());

}

// Evalueates an expression in postfix notation
function evaluate_expression(exp)
{
	var operands=/\d|\.|\u00B1/;
	var functions=/sin|cos|tan|\u221A|log|ln/;
	var stack=[];
	while (exp.length)
	{
		token=exp.pop();
		if (operands.test(token))
		{
			stack.push(token);
		} else {
			if (functions.test(token))
			{
				var n1=stack.pop();
				stack.push(performOp(token,n1));
			} else {
				var n1=stack.pop();
				var n2=stack.pop();
				stack.push(performOp(token,n2,n1));
			}
		}
	}
	return stack.pop();
}

// do n1.op(n2)
function performOp(op, num1, num2)
{
	switch (op)
	{
		case ('+'):
			var result=num1.plus(num2);
			break;
		case ('-'):
			var result=num1.minus(num2);
			break;
		case ('\u00D7'):
			var result=num1.times(num2);
			break;
		case ('\u00F7'):
			var result=num1.divide(num2);
			break;
		case ('^'):
			var result=num1.pow(num2);
			break;
		// Here down are the functions (only need one number)
		case ('\u221A'):
			var result=num1.sqrt();
			break;
		case ('sin'):
			var result=num1.sin();
			break;
		case ('cos'):
			var result=num1.cos();
			break;
		case ('tan'):
			var result=num1.tan();
			break;
		case ('log'):
			var result=num1.log();
			break;
		case ('ln'):
			var result=num1.ln();
			break;
		default:
			throw 'Invalid Operator';
			break;
	}
	return result;
}
<|endoftext|>