<commit_before>const Discord = require("discord.js");
var mysql = require('mysql');
var fs = require("fs");
var config = JSON.parse(fs.readFileSync("config.json", "utf8"));
var sqlConnection = mysql.createConnection(config.mysql);
var discordClient = null;
sqlConnection.connect();

var kue = require('kue'),
    queue = kue.createQueue({
        prefix: 'q',
        redis: {
            port: 6379,
            host: '127.0.0.1'
        }
    });
var triggerWords = {

};
var clientState = {
    "online": false,
    "loginName": null,
    "onlineSince": null
};
queue.on( 'error', function( err ) {
    console.log( 'Oops... ', err );
});
function biotmp(token) {
    function queueMSG(msg) {
        var job = queue.create('msg_process', {
            "title": "Discord Message Processing",
            "content": JSON.stringify({
                "message": msg.content,
                "channel": {
                    "type": msg.channel.type,
                    "id": msg.channel.id,
                    "name": msg.channel.name
                },
                "server": {
                    "id": msg.channel.guild.id,
                    "name": msg.channel.guild.name
                },
                "author": msg.author,
                "tts": msg.tts,
                "system": msg.system,
                "nonce": msg.nonce,
                "mentions": {
                    "everyone": msg.mentions.everyone,
                    "users": JSON.stringify(msg.mentions.users),
                    "roles": JSON.stringify(msg.mentions.roles)

                }
            })

        }).priority("low").save( function(err){
            if( !err ) console.log( job.id );
        });
    }
    discordClient = new Discord.Client();
    discordClient.on('ready', function () {
        console.log("Logged in as " + discordClient.user.tag);
        console.log("https://discordapp.com/oauth2/authorize?client_id=" + config.clientID + "&scope=bot&permissions=0")
        clientState.online = true;
        clientState.loginName = discordClient.user.tag;
        clientState.online = Date.now();
    });

    discordClient.on('message', function (msg) {
        if (triggerWords[msg.channel.guild.id] === undefined && msg.content.indexOf("biotmp ") === 0) {
            // triggered - default
            // add triggerword to msg object for further processing
            console.log("Triggered");
            msg.triggerWord = "biotmp ";
            //and queue processing
            queueMSG(msg);
        } else if (triggerWords[msg.channel.guild.id] !== undefined) {
            if (msg.content.indexOf(triggerWords[msg.channel.guild.id] + " ") === 0) {
                // triggered - custom triggerword
                // add triggerword to msg object for further processing
                msg.triggerWord = triggerWords[msg.channel.guild.id] + " ";
                //and queue processing
                queueMSG(msg);

            }
        }

    });

    discordClient.login(token);
}
function updateTriggerWords() {
    sqlConnection.query("SELECT id, server_id, triggerword FROM triggerword", function (err, results) {
       if (err) throw err;
       triggerWords = {};
       results.forEach(function (currentEntry) {
           triggerWords[currentEntry.server_id] = currentEntry.triggerword;
       });
    });
}
setInterval(updateTriggerWords, 1000);
biotmp(config.token);<commit_msg>lösch dich<commit_after>const Discord = require("discord.js");
var mysql = require('mysql');
var fs = require("fs");
var config = JSON.parse(fs.readFileSync("config.json", "utf8"));
var sqlConnection = mysql.createConnection(config.mysql);
var discordClient = null;
sqlConnection.connect();

var kue = require('kue'),
    queue = kue.createQueue({
        prefix: 'q',
        redis: {
            port: 6379,
            host: '127.0.0.1'
        }
    });
var triggerWords = {

};
var clientState = {
    "online": false,
    "loginName": null,
    "onlineSince": null
};
queue.on( 'error', function( err ) {
    console.log( 'Oops... ', err );
});
function biotmp(token) {
    function queueMSG(msg) {
        var job = queue.create('msg_process', {
            "title": "Discord Message Processing",
            "content": JSON.stringify({
                "message": msg.content,
                "channel": {
                    "type": msg.channel.type,
                    "id": msg.channel.id,
                    "name": msg.channel.name
                },
                "server": {
                    "id": msg.channel.guild.id,
                    "name": msg.channel.guild.name
                },
                "author": msg.author,
                "tts": msg.tts,
                "system": msg.system,
                "nonce": msg.nonce
            })

        }).priority("low").save( function(err){
            if( !err ) console.log( job.id );
        });
    }
    discordClient = new Discord.Client();
    discordClient.on('ready', function () {
        console.log("Logged in as " + discordClient.user.tag);
        console.log("https://discordapp.com/oauth2/authorize?client_id=" + config.clientID + "&scope=bot&permissions=0")
        clientState.online = true;
        clientState.loginName = discordClient.user.tag;
        clientState.online = Date.now();
    });

    discordClient.on('message', function (msg) {
        if (triggerWords[msg.channel.guild.id] === undefined && msg.content.indexOf("biotmp ") === 0) {
            // triggered - default
            // add triggerword to msg object for further processing
            console.log("Triggered");
            msg.triggerWord = "biotmp ";
            //and queue processing
            queueMSG(msg);
        } else if (triggerWords[msg.channel.guild.id] !== undefined) {
            if (msg.content.indexOf(triggerWords[msg.channel.guild.id] + " ") === 0) {
                // triggered - custom triggerword
                // add triggerword to msg object for further processing
                msg.triggerWord = triggerWords[msg.channel.guild.id] + " ";
                //and queue processing
                queueMSG(msg);

            }
        }

    });

    discordClient.login(token);
}
function updateTriggerWords() {
    sqlConnection.query("SELECT id, server_id, triggerword FROM triggerword", function (err, results) {
       if (err) throw err;
       triggerWords = {};
       results.forEach(function (currentEntry) {
           triggerWords[currentEntry.server_id] = currentEntry.triggerword;
       });
    });
}
setInterval(updateTriggerWords, 1000);
biotmp(config.token);<|endoftext|>


<commit_before>"use strict";

const fs      = require("fs");
const path    = require("path");
const utils   = require("./utils.js");
const {clean} = require("mocha/lib/utils.js");
const {addTo, nearest, New} = utils;

const cssPath = require.resolve("../test-runner.css");
const el      = Symbol("Atom-Mocha-Elements");

/**
 * TTY emulator to show spec results in interactive mode.
 */
class Reporter{

	constructor(runner, options){
		this.runner = runner;
		this.formatCode = options.formatCode;
		this.autoIt     = options.autoIt && options.ui === "bdd";

		/** Register Mocha handlers */
		const events = {
			start:      this.onStart,
			suite:      this.onSuite,
			pass:       this.onPass,
			fail:       this.onFail,
			end:        this.onEnd,
			pending:    this.onPending,
			"test end": this.onTestEnd
		};
		for(const event in events)
			runner.on(event, events[event].bind(this));

		/** Construct something to actually show stuff with */
		this.element = New("div", {id: "mocha"});
		this.report  = New("div", {id: "mocha-report"});
		this.element.appendChild(this.report);
		this.element.addEventListener("click", function(e){
			let title, stop, el;
			if(title = nearest(e.target, ".test-title")){
				el = title.nextSibling;
				if(el) el.hidden = !el.hidden;
			}

			else if(title = nearest(e.target, ".suite-title")){
				el = title.nextSibling;
				el.hidden = !el.hidden;
				el.parentNode.classList.toggle("collapsed", el.hidden);
			}

			if(el){
				e.preventDefault();
				e.stopPropagation();
				return false;
			}
		});

		/** Generate stat-bar */
		this.statBar = New("footer", {id: "mocha-stats"});
		this.element.appendChild(addTo(this.statBar)(
			this.statBar.passes   = New("div", {textContent: "0", id: "mocha-passes"}),
			this.statBar.duration = this.createDuration(...[,,{tagName: "div", id:"mocha-duration"}]),
			this.statBar.pending  = New("div", {textContent: "0", id: "mocha-pending"}),
			this.statBar.failures = New("div", {textContent: "0", id: "mocha-failures"})
		)[0]);

		document.title = "Mocha";
		document.body.style.tabSize = atom.config.get("editor.tabLength");
		document.body.appendChild(this.element);
		document.head.appendChild(New("link", {
			rel: "stylesheet",
			type: "text/css",
			href: "file://" + cssPath
		}));

		/** If we have the user's preferred editor settings on-hand, use 'em */
		if(Reporter.editorSettings){
			const ed = Reporter.editorSettings;
			const s  = this.element.style;

			if(ed.fontFamily) s.fontFamily = ed.fontFamily;
			if(ed.fontSize)   s.fontSize   = ed.fontSize + "px";
			if(ed.lineHeight) s.lineHeight = ed.lineHeight;
			if(ed.tabLength)  s.tabSize    = ed.tabLength;
		}
	}

	get passes(){ return +this.statBar.passes.dataset.value || 0 }
	set passes(input){
		input            = Math.max(0, +input || 0);
		const el         = this.statBar.passes;
		el.textContent   = `${input} passing`;
		el.dataset.value = input;
		el.classList.toggle("zero", input === 0);
	}

	get pending(){ return +this.statBar.pending.dataset.value || 0 }
	set pending(input){
		input            = Math.max(0, +input || 0);
		const el         = this.statBar.pending;
		el.textContent   = `${input} pending`;
		el.dataset.value = input;
		el.classList.toggle("zero", input === 0);
	}

	get failures(){ return +this.statBar.failures.dataset.value || 0 }
	set failures(input){
		input            = Math.max(0, +input || 0);
		const el         = this.statBar.failures;
		el.textContent   = `${input} failing`;
		el.dataset.value = input;
		el.classList.toggle("zero", input === 0);
	}

	get duration(){ return this.statBar.duration || 0 }
	set duration(input){
		const el = this.statBar.duration;
		el.value = input;
	}

	onStart(){
		this.passes     = 0;
		this.failures   = 0;
		this.total      = 0;
		this.pending    = 0;
		this.started    = performance.now();
	}

	onSuite(suite){

		/** We haven't created elements for this Mocha suite yet */
		if(!suite[el] && !suite.root)
			this.addSuite(suite);
	}

	onTestEnd(){
		++this.total;
		this.duration = performance.now() - this.started;
	}

	onPass(test){
		++this.passes;
		this.addResult(test);
	}

	onFail(test, reason){
		++this.failures;
		this.addResult(test, reason);
	}

	onPending(test){
		++this.pending;
		this.addResult(test);
	}

	onEnd(){
		this.finished = performance.now();
		this.duration = this.finished - this.started;
	}

	/**
	 * Add an HTML container for a suite of tests.
	 *
	 * @param {Suite} suite
	 */
	addSuite(suite){
		const container = New("article", {className: "suite"});
		const title     = New("h1", {className: "suite-title", textContent: suite.title});
		const results   = New("div", {className: "results"});

		container.appendChild(title);
		container.appendChild(results);
		suite[el] = {container, title, results};

		const parent = suite.parent[el];
		parent
			? parent.results.appendChild(container)
			: this.report.appendChild(container);
	}

	/**
	 * Display the result of a finished test in the spec-runner.
	 *
	 * @param {Runnable} test
	 * @param {Error} error
	 */
	addResult(test, error){
		const speed = this.rankSpeed(test);
		const state = error ? "fail" : (test.pending ? "pending" : "pass");

		const container = New("div", {className: `test ${state} ${speed}`});
		const title     = New("div", {className: "test-title"});
		const h2        = New("h2", {textContent: (this.autoIt? "It ":"") + test.title});
		addTo(title)(h2);

		if(!test.pending){
			const duration  = this.createDuration(test.duration, true);
			duration.title += ` (${speed === "okay" ? "medium" : speed})`;
			addTo(title)(duration);
		}

		container.appendChild(title);
		test.pending || container.appendChild(this.createCodeBlock(test.body));
		test.parent[el].results.appendChild(container);
	}

	/**
	 * Generate a chunk of (possibly) highlighted JavaScript source.
	 *
	 * @param {String} src
	 * @return {HTMLPreElement} pre
	 */
	createCodeBlock(src){
		const pre  = New("pre", {className: "code-block", hidden: true});
		const code = New("code", {textContent: src});

		if(this.formatCode){
			code.textContent = "";
			src = clean(src).replace(/^(\x20{2})+/gm, s => "\t".repeat(s.length / 2));
			for(let node of this.highlight(src))
				code.appendChild(node);
		}

		pre.appendChild(code);
		return pre;
	}

	/**
	 * Generate an HTML node for displaying a duration.
	 *
	 * @param {Number} input - Time expressed in milliseconds
	 * @param {Boolean} noUnits - Always display time in milliseconds
	 * @param {Object} attr - Additional HTML attributes to add to container
	 * @return {HTMLElement} el
	 */
	createDuration(input = 0, noUnits = false, attr = {}){
		const attrib  = Object.assign({}, attr, {className: "duration"});
		const tagName = attr.tagName || "span";
		delete attrib.tagName;

		const span = New(tagName, attrib);
		const data = New("data", {className: "amount"});
		const unit = New("span", {className: "unit"});

		Object.defineProperty(span, "value", {
			get(){ return +data.value || 0; },

			set(input){
				input = Math.max(0, +input || 0);
				data.value = input;

				/** Display a reader-friendly version of the duration */
				if(noUnits || input < 1000){
					input = parseFloat(input.toFixed(2));
					span.title = `${input} millisecond${input==1 ? "":"s"}`;
					data.textContent = input;
					unit.textContent = "ms";
				}

				else if(input >= 60000){
					input = parseFloat((input / 60000).toFixed(2));
					span.title = `${input} minute${input==1 ? "":"s"}`;
					data.textContent = input;
					unit.textContent = "m";
				}

				else{
					input = parseFloat((input / 1000).toFixed(2));
					span.title = `${input} second${input==1 ? "":"s"}`;
					data.textContent = input;
					unit.textContent = "s";
				}
			}
		});

		span.value = input;
		span.appendChild(data);
		span.appendChild(unit);
		return span;
	}

	/**
	 * Gauge the speed of a finished test.
	 *
	 * @param {Runnable} test
	 * @return {String} speed - "fast", "medium" or "slow"
	 */
	rankSpeed(test){
		const thresh = test.slow();
		if(test.duration > thresh)      return "slow";
		if(test.duration > thresh / 2)  return "okay";
		return "fast";
	}

	/**
	 * Highlight code chunks embedded in test results.
	 *
	 * @param  {Reporter} input - Mocha reporter object
	 * @param  {Object} atom - Reference to the Atom environment object
	 * @return {Array} nodes - An array of DOM nodes
	 */
	highlight(input, atom){
		const {grammars} = Reporter;
		const nodes = [];

		/** Highlighting is disabled/unavailable */
		if(grammars === undefined)
			return nodes;

		for(let line of grammars["source.js"].tokenizeLines(input)){
			for(let token of line){
				const tag       = document.createElement("span");
				tag.textContent = token.value;

				const scopes    = token.scopes.join(".").split(/\./g);
				const s         = boolBlob(scopes);
				tag.dataset.scopes = Array.from(new Set(scopes).values()).join(" ");

				/** Don't use scopes as classes; they'll be targeted by themes */
				if(s.punctuation) tag.className = s.string ? "js-quote" : "js-punct";
				else if(s.meta && s.brace || s.delimiter) tag.className = "js-punct";
				else if(s.keyword  || s.entity && s.name) tag.className = "js-key";
				else if(s.function || s.storage)          tag.className = "js-key";
				else if(s.constant || s.string)           tag.className = "js-value";
				else if(s.property || s.variable)         tag.className = "js-ident";
				else tag.className = "js";

				nodes.push(tag);
			}
			nodes.push(document.createTextNode("\n"));
		}
		return nodes;
	}

	/**
	 * Perform any asynchronous operations that need to complete before starting Mocha.
	 *
	 * @param {AtomMocha} main - Reference to the running AtomMocha instance.
	 * @return {Promise} promise
	 */
	static beforeStart(main){
		const promises = [this.loadEditorSettings()];

		/** Load JavaScript grammar to provide syntax highlighting */
		if(main.options.formatCode){
			this.grammars = {};

			promises.push(atom.packages.activatePackage("language-javascript").then(p => {
				p.grammars.forEach(g => this.grammars[g.scopeName] = g);

				/** Make sure we don't screw with specs */
				atom.packages.deactivatePackage("language-javascript");
			}));
		}

		return Promise.all(promises);
	}

	/**
	 * Attempt to load user's config to improve feedback quality.
	 *
	 * User configs are parsed statically. Because this feature is so cosmetic,
	 * it's not worth adding CSON/CoffeeScript as a hard dependency over.
	 *
	 * @return {Promise} promise
	 */
	static loadEditorSettings(){

		return new Promise((resolve, reject) => {
			const configPath = atom.config.configFilePath;

			fs.readFile(configPath, (error, data) => {
				if(error) return reject(error);

				/** Break the config's content apart by top-level scope */
				const scopes = {};
				data.toString().split(/^(?=\S)/gm).forEach(s => {
					const scope  = /^(?:"(?:[^"\\]|\\.)+"|'(?:[^'\\]|\\.)+'|\w+)/;
					const editor = /\n {2}editor:\n[^\x00]+?\n(?: {2}\S|$)/;
					const ed     = (s.match(editor) || [""])[0];

					/** Pinch a bunch of parsed editor settings */
					if(s = (s.match(scope) || [""])[0].replace(/["']/g, ""))
						scopes[s] = {
							fontFamily: (ed.match(/^ +fontFamily:\s*("|')(.+)\1/im) || []).pop(),
							fontSize:   (ed.match(/^ +fontSize:\s*([\d.]+)/im)      || []).pop(),
							lineHeight: (ed.match(/^ +lineHeight:\s*([\d.]+)/im)    || []).pop(),
							tabLength:  (ed.match(/^ +tabLength:\s*(\d+)/im)        || []).pop()
						};
				});

				/** Now collate them in ascending order of precedence */
				const ed = {};
				for(let scope of ["*", ".atom-mocha"]){
					for(let k in scopes[scope]){
						let v = scopes[scope][k]
						if(v != null) ed[k] = v
					}
				}
				Reporter.editorSettings = ed;
				resolve();
			});
		});
	}
}

module.exports = Reporter;

/** Sloppy alternative to "array.includes(string);" */
function boolBlob(list){
	let b = {};
	for(let i of list)
		b[i] = true;
	return b;
};
<commit_msg>Prevent editor-styles from cluttering dev tools<commit_after>"use strict";

const fs      = require("fs");
const path    = require("path");
const utils   = require("./utils.js");
const {clean} = require("mocha/lib/utils.js");
const {addTo, nearest, New} = utils;

const cssPath = require.resolve("../test-runner.css");
const el      = Symbol("Atom-Mocha-Elements");

/**
 * TTY emulator to show spec results in interactive mode.
 */
class Reporter{

	constructor(runner, options){
		this.runner = runner;
		this.formatCode = options.formatCode;
		this.autoIt     = options.autoIt && options.ui === "bdd";

		/** Register Mocha handlers */
		const events = {
			start:      this.onStart,
			suite:      this.onSuite,
			pass:       this.onPass,
			fail:       this.onFail,
			end:        this.onEnd,
			pending:    this.onPending,
			"test end": this.onTestEnd
		};
		for(const event in events)
			runner.on(event, events[event].bind(this));

		/** Construct something to actually show stuff with */
		this.element = New("div", {id: "mocha"});
		this.report  = New("div", {id: "mocha-report"});
		this.element.appendChild(this.report);
		this.element.addEventListener("click", function(e){
			let title, stop, el;
			if(title = nearest(e.target, ".test-title")){
				el = title.nextSibling;
				if(el) el.hidden = !el.hidden;
			}

			else if(title = nearest(e.target, ".suite-title")){
				el = title.nextSibling;
				el.hidden = !el.hidden;
				el.parentNode.classList.toggle("collapsed", el.hidden);
			}

			if(el){
				e.preventDefault();
				e.stopPropagation();
				return false;
			}
		});

		/** Generate stat-bar */
		this.statBar = New("footer", {id: "mocha-stats"});
		this.element.appendChild(addTo(this.statBar)(
			this.statBar.passes   = New("div", {textContent: "0", id: "mocha-passes"}),
			this.statBar.duration = this.createDuration(...[,,{tagName: "div", id:"mocha-duration"}]),
			this.statBar.pending  = New("div", {textContent: "0", id: "mocha-pending"}),
			this.statBar.failures = New("div", {textContent: "0", id: "mocha-failures"})
		)[0]);

		document.title = "Mocha";
		document.body.style.tabSize = atom.config.get("editor.tabLength");
		document.body.appendChild(this.element);
		document.head.appendChild(New("link", {
			rel: "stylesheet",
			type: "text/css",
			href: "file://" + cssPath
		}));

		/** If we have the user's preferred editor settings on-hand, use 'em */
		if(Reporter.editorSettings){
			const ed = Reporter.editorSettings;
			const editorStyle = New("style", {
				textContent: "#mocha{\n" + [
					ed.fontSize   ? `\tfont-size: ${ed.fontSize}px;`     : null,
					ed.lineHeight ? `\tline-height: ${ed.lineHeight};`   : null,
					ed.fontFamily ? `\tfont-family: "${ed.fontFamily}";` : null,
					ed.tabLength  ? `\ttab-size: ${ed.tabLength};`       : null
				].filter(Boolean).join("\n") + "\n}"
			});
			document.head.appendChild(editorStyle);
		}
	}

	get passes(){ return +this.statBar.passes.dataset.value || 0 }
	set passes(input){
		input            = Math.max(0, +input || 0);
		const el         = this.statBar.passes;
		el.textContent   = `${input} passing`;
		el.dataset.value = input;
		el.classList.toggle("zero", input === 0);
	}

	get pending(){ return +this.statBar.pending.dataset.value || 0 }
	set pending(input){
		input            = Math.max(0, +input || 0);
		const el         = this.statBar.pending;
		el.textContent   = `${input} pending`;
		el.dataset.value = input;
		el.classList.toggle("zero", input === 0);
	}

	get failures(){ return +this.statBar.failures.dataset.value || 0 }
	set failures(input){
		input            = Math.max(0, +input || 0);
		const el         = this.statBar.failures;
		el.textContent   = `${input} failing`;
		el.dataset.value = input;
		el.classList.toggle("zero", input === 0);
	}

	get duration(){ return this.statBar.duration || 0 }
	set duration(input){
		const el = this.statBar.duration;
		el.value = input;
	}

	onStart(){
		this.passes     = 0;
		this.failures   = 0;
		this.total      = 0;
		this.pending    = 0;
		this.started    = performance.now();
	}

	onSuite(suite){

		/** We haven't created elements for this Mocha suite yet */
		if(!suite[el] && !suite.root)
			this.addSuite(suite);
	}

	onTestEnd(){
		++this.total;
		this.duration = performance.now() - this.started;
	}

	onPass(test){
		++this.passes;
		this.addResult(test);
	}

	onFail(test, reason){
		++this.failures;
		this.addResult(test, reason);
	}

	onPending(test){
		++this.pending;
		this.addResult(test);
	}

	onEnd(){
		this.finished = performance.now();
		this.duration = this.finished - this.started;
	}

	/**
	 * Add an HTML container for a suite of tests.
	 *
	 * @param {Suite} suite
	 */
	addSuite(suite){
		const container = New("article", {className: "suite"});
		const title     = New("h1", {className: "suite-title", textContent: suite.title});
		const results   = New("div", {className: "results"});

		container.appendChild(title);
		container.appendChild(results);
		suite[el] = {container, title, results};

		const parent = suite.parent[el];
		parent
			? parent.results.appendChild(container)
			: this.report.appendChild(container);
	}

	/**
	 * Display the result of a finished test in the spec-runner.
	 *
	 * @param {Runnable} test
	 * @param {Error} error
	 */
	addResult(test, error){
		const speed = this.rankSpeed(test);
		const state = error ? "fail" : (test.pending ? "pending" : "pass");

		const container = New("div", {className: `test ${state} ${speed}`});
		const title     = New("div", {className: "test-title"});
		const h2        = New("h2", {textContent: (this.autoIt? "It ":"") + test.title});
		addTo(title)(h2);

		if(!test.pending){
			const duration  = this.createDuration(test.duration, true);
			duration.title += ` (${speed === "okay" ? "medium" : speed})`;
			addTo(title)(duration);
		}

		container.appendChild(title);
		test.pending || container.appendChild(this.createCodeBlock(test.body));
		test.parent[el].results.appendChild(container);
	}

	/**
	 * Generate a chunk of (possibly) highlighted JavaScript source.
	 *
	 * @param {String} src
	 * @return {HTMLPreElement} pre
	 */
	createCodeBlock(src){
		const pre  = New("pre", {className: "code-block", hidden: true});
		const code = New("code", {textContent: src});

		if(this.formatCode){
			code.textContent = "";
			src = clean(src).replace(/^(\x20{2})+/gm, s => "\t".repeat(s.length / 2));
			for(let node of this.highlight(src))
				code.appendChild(node);
		}

		pre.appendChild(code);
		return pre;
	}

	/**
	 * Generate an HTML node for displaying a duration.
	 *
	 * @param {Number} input - Time expressed in milliseconds
	 * @param {Boolean} noUnits - Always display time in milliseconds
	 * @param {Object} attr - Additional HTML attributes to add to container
	 * @return {HTMLElement} el
	 */
	createDuration(input = 0, noUnits = false, attr = {}){
		const attrib  = Object.assign({}, attr, {className: "duration"});
		const tagName = attr.tagName || "span";
		delete attrib.tagName;

		const span = New(tagName, attrib);
		const data = New("data", {className: "amount"});
		const unit = New("span", {className: "unit"});

		Object.defineProperty(span, "value", {
			get(){ return +data.value || 0; },

			set(input){
				input = Math.max(0, +input || 0);
				data.value = input;

				/** Display a reader-friendly version of the duration */
				if(noUnits || input < 1000){
					input = parseFloat(input.toFixed(2));
					span.title = `${input} millisecond${input==1 ? "":"s"}`;
					data.textContent = input;
					unit.textContent = "ms";
				}

				else if(input >= 60000){
					input = parseFloat((input / 60000).toFixed(2));
					span.title = `${input} minute${input==1 ? "":"s"}`;
					data.textContent = input;
					unit.textContent = "m";
				}

				else{
					input = parseFloat((input / 1000).toFixed(2));
					span.title = `${input} second${input==1 ? "":"s"}`;
					data.textContent = input;
					unit.textContent = "s";
				}
			}
		});

		span.value = input;
		span.appendChild(data);
		span.appendChild(unit);
		return span;
	}

	/**
	 * Gauge the speed of a finished test.
	 *
	 * @param {Runnable} test
	 * @return {String} speed - "fast", "medium" or "slow"
	 */
	rankSpeed(test){
		const thresh = test.slow();
		if(test.duration > thresh)      return "slow";
		if(test.duration > thresh / 2)  return "okay";
		return "fast";
	}

	/**
	 * Highlight code chunks embedded in test results.
	 *
	 * @param  {Reporter} input - Mocha reporter object
	 * @param  {Object} atom - Reference to the Atom environment object
	 * @return {Array} nodes - An array of DOM nodes
	 */
	highlight(input, atom){
		const {grammars} = Reporter;
		const nodes = [];

		/** Highlighting is disabled/unavailable */
		if(grammars === undefined)
			return nodes;

		for(let line of grammars["source.js"].tokenizeLines(input)){
			for(let token of line){
				const tag       = document.createElement("span");
				tag.textContent = token.value;

				const scopes    = token.scopes.join(".").split(/\./g);
				const s         = boolBlob(scopes);
				tag.dataset.scopes = Array.from(new Set(scopes).values()).join(" ");

				/** Don't use scopes as classes; they'll be targeted by themes */
				if(s.punctuation) tag.className = s.string ? "js-quote" : "js-punct";
				else if(s.meta && s.brace || s.delimiter) tag.className = "js-punct";
				else if(s.keyword  || s.entity && s.name) tag.className = "js-key";
				else if(s.function || s.storage)          tag.className = "js-key";
				else if(s.constant || s.string)           tag.className = "js-value";
				else if(s.property || s.variable)         tag.className = "js-ident";
				else tag.className = "js";

				nodes.push(tag);
			}
			nodes.push(document.createTextNode("\n"));
		}
		return nodes;
	}

	/**
	 * Perform any asynchronous operations that need to complete before starting Mocha.
	 *
	 * @param {AtomMocha} main - Reference to the running AtomMocha instance.
	 * @return {Promise} promise
	 */
	static beforeStart(main){
		const promises = [this.loadEditorSettings()];

		/** Load JavaScript grammar to provide syntax highlighting */
		if(main.options.formatCode){
			this.grammars = {};

			promises.push(atom.packages.activatePackage("language-javascript").then(p => {
				p.grammars.forEach(g => this.grammars[g.scopeName] = g);

				/** Make sure we don't screw with specs */
				atom.packages.deactivatePackage("language-javascript");
			}));
		}

		return Promise.all(promises);
	}

	/**
	 * Attempt to load user's config to improve feedback quality.
	 *
	 * User configs are parsed statically. Because this feature is so cosmetic,
	 * it's not worth adding CSON/CoffeeScript as a hard dependency over.
	 *
	 * @return {Promise} promise
	 */
	static loadEditorSettings(){

		return new Promise((resolve, reject) => {
			const configPath = atom.config.configFilePath;

			fs.readFile(configPath, (error, data) => {
				if(error) return reject(error);

				/** Break the config's content apart by top-level scope */
				const scopes = {};
				data.toString().split(/^(?=\S)/gm).forEach(s => {
					const scope  = /^(?:"(?:[^"\\]|\\.)+"|'(?:[^'\\]|\\.)+'|\w+)/;
					const editor = /\n {2}editor:\n[^\x00]+?\n(?: {2}\S|$)/;
					const ed     = (s.match(editor) || [""])[0];

					/** Pinch a bunch of parsed editor settings */
					if(s = (s.match(scope) || [""])[0].replace(/["']/g, ""))
						scopes[s] = {
							fontFamily: (ed.match(/^ +fontFamily:\s*("|')(.+)\1/im) || []).pop(),
							fontSize:   (ed.match(/^ +fontSize:\s*([\d.]+)/im)      || []).pop(),
							lineHeight: (ed.match(/^ +lineHeight:\s*([\d.]+)/im)    || []).pop(),
							tabLength:  (ed.match(/^ +tabLength:\s*(\d+)/im)        || []).pop()
						};
				});

				/** Now collate them in ascending order of precedence */
				const ed = {};
				for(let scope of ["*", ".atom-mocha"]){
					for(let k in scopes[scope]){
						let v = scopes[scope][k]
						if(v != null) ed[k] = v
					}
				}
				Reporter.editorSettings = ed;
				resolve();
			});
		});
	}
}

module.exports = Reporter;

/** Sloppy alternative to "array.includes(string);" */
function boolBlob(list){
	let b = {};
	for(let i of list)
		b[i] = true;
	return b;
};
<|endoftext|>


<commit_before>/**
 * Module dependencies.
 */
var EventEmitter = require('events').EventEmitter
  , http = require('http')
  , util = require('util');

/**
 * Creates an instance of `Response`.
 *
 * This class is used as a mock when testing Express handlers, substituted in
 * place of of a Node's `http.ServerResponse`.
 *
 * @constructor
 * @api protected
 */
function Response(cb) {
  EventEmitter.call(this);

  this.statusCode = 200;
  this._headers = {};
  this._data = '';
  this.__cb = cb;
}

util.inherits(Response, EventEmitter);

Response.prototype.getHeader = function(name) {
  return this._headers[name];
};

Response.prototype.setHeader = function(name, value) {
  this._headers[name] = value;
};

Response.prototype.writeHead = function(statusCode, reasonPhrase, headers) {
  if (typeof reasonPhrase == 'object') {
    headers = reasonPhrase;
    reasonPhrase = undefined;
  }

  this.statusCode = statusCode;

  var self = this;
  Object.keys(headers).forEach(function(key) {
    self.setHeader(key, headers[key]);
  });
}

Response.prototype.send = function(body) {
  // allow status / body
  if (2 == arguments.length) {
    // res.send(body, status) backwards compat
    if ('number' != typeof body && 'number' == typeof arguments[1]) {
      this.statusCode = arguments[1];
    } else {
      this.statusCode = body;
      body = arguments[1];
    }
  }

  switch (typeof body) {
    case 'number':
      if (!this.getHeader('Content-Type')) {
        this.setHeader('Content-Type', 'text/plain');
      }
      this.statusCode = body;
      body = http.STATUS_CODES[body];
      break;
    case 'object':
      return this.json(body);
      break;
  }

  this.end(body);
  return this;
};

Response.prototype.json = function(obj) {
  if (!this.getHeader('Content-Type')) {
    this.setHeader('Content-Type', 'application/json');
  }

  var body = JSON.stringify(obj);
  return this.send(body);
}

Response.prototype.redirect = function(url, status) {
  this.statusCode = status || 302;
  this.setHeader('Location', url);
  this.end();
};

Response.prototype.end = function(data, encoding) {
  if (data) { this._data += data; }
  if (this._data.length) {
    this.data = this._data;

    switch (this.getHeader('Content-Type')) {
    case 'text/plain':
      this.body = this.data;
      break;
    case 'application/json':
      this.body = JSON.parse(this.data);
      break;
    }
  }

  this.emit('finish');
  if (this.__cb) { this.__cb(); }
};

/**
 * Expose `Response`.
 */
module.exports = Response;
<commit_msg>Implement Response#location.<commit_after>/**
 * Module dependencies.
 */
var EventEmitter = require('events').EventEmitter
  , http = require('http')
  , util = require('util');

/**
 * Creates an instance of `Response`.
 *
 * This class is used as a mock when testing Express handlers, substituted in
 * place of of a Node's `http.ServerResponse`.
 *
 * @constructor
 * @api protected
 */
function Response(cb) {
  EventEmitter.call(this);

  this.statusCode = 200;
  this._headers = {};
  this._data = '';
  this.__cb = cb;
}

util.inherits(Response, EventEmitter);

Response.prototype.writeHead = function(statusCode, reasonPhrase, headers) {
  if (typeof reasonPhrase == 'object') {
    headers = reasonPhrase;
    reasonPhrase = undefined;
  }

  this.statusCode = statusCode;

  var self = this;
  Object.keys(headers).forEach(function(key) {
    self.setHeader(key, headers[key]);
  });
}

Response.prototype.getHeader = function(name) {
  return this._headers[name];
};

Response.prototype.setHeader = function(name, value) {
  this._headers[name] = value;
};

Response.prototype.location = function(url) {
  this.setHeader('Location', url);
  return this;
};

Response.prototype.send = function(body) {
  // allow status / body
  if (2 == arguments.length) {
    // res.send(body, status) backwards compat
    if ('number' != typeof body && 'number' == typeof arguments[1]) {
      this.statusCode = arguments[1];
    } else {
      this.statusCode = body;
      body = arguments[1];
    }
  }

  switch (typeof body) {
    case 'number':
      if (!this.getHeader('Content-Type')) {
        this.setHeader('Content-Type', 'text/plain');
      }
      this.statusCode = body;
      body = http.STATUS_CODES[body];
      break;
    case 'object':
      return this.json(body);
      break;
  }

  this.end(body);
  return this;
};

Response.prototype.json = function(obj) {
  if (!this.getHeader('Content-Type')) {
    this.setHeader('Content-Type', 'application/json');
  }

  var body = JSON.stringify(obj);
  return this.send(body);
}

Response.prototype.redirect = function(url, status) {
  this.statusCode = status || 302;
  this.setHeader('Location', url);
  this.end();
};

Response.prototype.end = function(data, encoding) {
  if (data) { this._data += data; }
  if (this._data.length) {
    this.data = this._data;

    switch (this.getHeader('Content-Type')) {
    case 'text/plain':
      this.body = this.data;
      break;
    case 'application/json':
      this.body = JSON.parse(this.data);
      break;
    }
  }

  this.emit('finish');
  if (this.__cb) { this.__cb(); }
};

/**
 * Expose `Response`.
 */
module.exports = Response;
<|endoftext|>


<commit_before>/*
 * NTwitBot - retrieve.js
 * Copyright (C) 2016-2017 Jordan Sne
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

/**
 * Data retriever class. Retrieves and pre-processes data from Twitter.
 */
module.exports = class Retrieve {

    /**
     * Initialize the class.
     * @param {Twitter} twitterHandler - The Twitter API object for making API calls.
     * @param {Util} utils - The utilities object needed for logging, etc.
     */
    constructor(twitterHandler, utils) {
        this.twitterHandler = twitterHandler;
        this.utils = utils;

        this.TWEETS_TO_TRACK = 3000;
    }

    /**
     * Retrieves new mentions of the bot, and saves the current state if needed.
     * @param {string} lastID - The last mention that was received.
     * @return {Promise} Resolves with any new mentions of the bot.
     */
    retrieveMentions(lastID) {
        const mentionData = {};

        // Retrieve since last mention or last 200
        if (lastID !== 0) {
            mentionData.since_id = lastID;
        } else {
            mentionData.count = 200;
        }

        return this.twitterHandler.getMentions(mentionData).then((mentions) => {
            return mentions;
        });
    }

    /**
     * Retrieves and processed new tweets of tracked users.
     * @param {Dictionary} trackedUsers - The tracked users data from the state.
     * @return {Promise} Resolves with the new tweets.
     */
    retrieveTweets(trackedUsers) {
        const tweetRetrievals = [];

        for (const userID in trackedUsers) {
            const lastTweetID = trackedUsers[userID];
            const requestData = {
                user_id: userID,
                trim_user: true
            };

            // If the user has been processed before
            if (lastTweetID !== 0) {
                requestData.count = 200;
                requestData.since_id = lastTweetID;
                tweetRetrievals.push(this.retrieveForExisting(requestData));
            // .. or the user has NOT been processed before
            } else {
                tweetRetrievals.push(this.retrieveForNew(requestData));
            }
        }

        // Wait until all retrievals are done
        return Promise.all(tweetRetrievals);
    }

    /**
     * Retrieves new tweets from an existing user. Only specify one argument.
     * @private
     * @param {Object} requestData - The request data to retrieve tweets with.
     * @return {Array} Resolves with an array of tweets.
     */
    retrieveForExisting(requestData, tweetsRetrieved) {
        const firstReq = !requestData.hasOwnProperty('max_id');

        // Initialize recursive variables
        if (firstReq) {
            tweetsRetrieved = [];
            this.utils.log('Retrieving most recent tweets from user with ID: "' + requestData.user_id + '"');
        }

        return this.twitterHandler.getTweets(requestData).then((tweets) => {
            // Do not count the last tweet as it might be the first tweet of the next request
            const numRetrieved = tweets.length - 1;

            if (tweets.length === 0) {
                this.utils.log('    No new tweets found');

                return {
                    done: true,
                    tweets: tweetsRetrieved,
                    totalRetrieved: 0
                };
            }

            // If the max ID from the request is the oldest tweet in the request (i.e. only one tweet returned)
            // or only one tweet was found on the first request..
            if (requestData.max_id === tweets[tweets.length - 1].id_str || firstReq && tweets.length === 1) {
                tweetsRetrieved.push(tweets[0]);

                this.utils.log('    Retrieved 1 tweet');
                this.utils.log('    Finished retrieving ' + tweetsRetrieved.length + ' tweets');

                return {
                    done: true,
                    tweets: tweetsRetrieved
                };
            }

            for (let i = 0; i < numRetrieved; i++) {
                tweetsRetrieved.push(tweets[i]);
            }

            this.utils.log('    Retrieved ' + numRetrieved + ' tweets');

            // Set the max ID of the next request to the oldest tweet in the previous request
            requestData.max_id = tweets[tweets.length - 1].id_str;

            return {
                done: false,
                requestData: requestData,
                tweetsRetrieved: tweetsRetrieved
            };
        }).then((next) => {
            if (!next.done) {
                return this.retrieveForExisting(next.requestData, next.tweetsRetrieved);
            } else {
                return next.tweets;
            }
        });
    }

    /**
     * Retrieves new tweets from a newly added user. Only specify one argument.
     * @private
     * @param {Object} requestData The request data to retrieve tweets with.
     * @return {Promise} Resolves with an object (The tweet data and total number of tweets retrieved).
     */
    retrieveForNew(requestData, tweetsLeft, tweetsRetrieved) {
        // If first request..
        if (!requestData.hasOwnProperty('count')) {
            requestData.count = this.TWEETS_TO_TRACK >= 200 ? 200 : this.TWEETS_TO_TRACK;
            tweetsLeft = this.TWEETS_TO_TRACK;
            tweetsRetrieved = [];

            this.utils.log('New user with ID: "' + requestData.user_id + '" added');
            this.utils.log('    Retrieving most recent ' + this.TWEETS_TO_TRACK + ' tweets');
        }

        return this.twitterHandler.getTweets(requestData).then((tweets) => {
            const numRetrieved = tweets.length - 1;
            tweetsLeft -= numRetrieved;

            // If the max ID from the request is the oldest tweet in the request (i.e. only one tweet returned)..
            if (requestData.max_id === tweets[tweets.length - 1].id_str) {
                tweetsRetrieved.push(tweets[0]);

                this.utils.log('    Retrieved ' + tweetsRetrieved.length + '/' + this.TWEETS_TO_TRACK + ' tweets');
                this.utils.log('    Finished retrieving tweets');
                if (requestData.count > 1) {
                    this.utils.log('    Reached max tweet retrieval limit.');
                }

                return {
                    done: true,
                    tweets: tweetsRetrieved
                };
            }

            for (let i = 0; i < numRetrieved; i++) {
                tweetsRetrieved.push(tweets[i]);
            }

            this.utils.log('    Retrieved ' + tweetsRetrieved.length + '/' + this.TWEETS_TO_TRACK + ' tweets');

            // Set newest ID for next request to oldest ID of the previous request
            requestData.max_id = tweets[tweets.length - 1].id_str;
            if (tweetsLeft < 200) {
                requestData.count = tweetsLeft;
            }

            return {
                done: false,
                requestData: requestData,
                tweetsLeft: tweetsLeft,
                tweetsRetrieved: tweetsRetrieved
            };
        }).then((next) => {
            if (!next.done) {
                return this.retrieveForNew(next.requestData, next.tweetsLeft, next.tweetsRetrieved);
            } else {
                return next.tweets;
            }
        });
    }

};
<commit_msg>Copy request object for each retrieval call<commit_after>/*
 * NTwitBot - retrieve.js
 * Copyright (C) 2016-2017 Jordan Sne
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

/**
 * Data retriever class. Retrieves and pre-processes data from Twitter.
 */
module.exports = class Retrieve {

    /**
     * Initialize the class.
     * @param {Twitter} twitterHandler - The Twitter API object for making API calls.
     * @param {Util} utils - The utilities object needed for logging, etc.
     */
    constructor(twitterHandler, utils) {
        this.twitterHandler = twitterHandler;
        this.utils = utils;

        this.TWEETS_TO_TRACK = 3000;
    }

    /**
     * Retrieves new mentions of the bot, and saves the current state if needed.
     * @param {string} lastID - The last mention that was received.
     * @return {Promise} Resolves with any new mentions of the bot.
     */
    retrieveMentions(lastID) {
        const mentionData = {};

        // Retrieve since last mention or last 200
        if (lastID !== 0) {
            mentionData.since_id = lastID;
        } else {
            mentionData.count = 200;
        }

        return this.twitterHandler.getMentions(mentionData).then((mentions) => {
            return mentions;
        });
    }

    /**
     * Retrieves and processed new tweets of tracked users.
     * @param {Dictionary} trackedUsers - The tracked users data from the state.
     * @return {Promise} Resolves with the new tweets.
     */
    retrieveTweets(trackedUsers) {
        const tweetRetrievals = [];

        for (const userID in trackedUsers) {
            const lastTweetID = trackedUsers[userID];
            const requestData = {
                user_id: userID,
                trim_user: true
            };

            // If the user has been processed before
            if (lastTweetID !== 0) {
                requestData.count = 200;
                requestData.since_id = lastTweetID;
                tweetRetrievals.push(this.retrieveForExisting(requestData));
            // .. or the user has NOT been processed before
            } else {
                tweetRetrievals.push(this.retrieveForNew(requestData));
            }
        }

        // Wait until all retrievals are done
        return Promise.all(tweetRetrievals);
    }

    /**
     * Retrieves new tweets from an existing user. Only specify one argument.
     * @private
     * @param {Object} params - The data to be used for making twitter requests.
     * @return {Array} Resolves with an array of tweets.
     */
    retrieveForExisting(params, tweetsRetrieved) {
        // Copy object to allow for proper unit testing
        const request = Object.assign({}, params);
        const firstReq = !request.hasOwnProperty('max_id');

        // Initialize recursive variables
        if (firstReq) {
            tweetsRetrieved = [];
            this.utils.log('Retrieving most recent tweets from user with ID: "' + request.user_id + '"');
        }

        return this.twitterHandler.getTweets(request).then((tweets) => {
            // Do not count the last tweet as it might be the first tweet of the next request
            const numRetrieved = tweets.length - 1;

            if (tweets.length === 0) {
                this.utils.log('    No new tweets found');

                return {
                    done: true,
                    tweets: tweetsRetrieved,
                    totalRetrieved: 0
                };
            }

            // If the max ID from the request is the oldest tweet in the request (i.e. only one tweet returned)
            // or only one tweet was found on the first request..
            if (request.max_id === tweets[tweets.length - 1].id_str || firstReq && tweets.length === 1) {
                tweetsRetrieved.push(tweets[0]);

                this.utils.log('    Retrieved 1 tweet');
                this.utils.log('    Finished retrieving ' + tweetsRetrieved.length + ' tweets');

                return {
                    done: true,
                    tweets: tweetsRetrieved
                };
            }

            for (let i = 0; i < numRetrieved; i++) {
                tweetsRetrieved.push(tweets[i]);
            }

            this.utils.log('    Retrieved ' + numRetrieved + ' tweets');

            // Set the max ID of the next request to the oldest tweet in the previous request
            const nextRequest = Object.assign({}, request);
            nextRequest.max_id = tweets[tweets.length - 1].id_str;

            return {
                done: false,
                request: nextRequest,
                tweetsRetrieved: tweetsRetrieved
            };
        }).then((next) => {
            if (!next.done) {
                return this.retrieveForExisting(next.request, next.tweetsRetrieved);
            } else {
                return next.tweets;
            }
        });
    }

    /**
     * Retrieves new tweets from a newly added user. Only specify one argument.
     * @private
     * @param {Object} params - The data to be used for making twitter requests.
     * @return {Promise} Resolves with an object (The tweet data and total number of tweets retrieved).
     */
    retrieveForNew(params, tweetsLeft, tweetsRetrieved) {
        // Copy object to allow for proper unit testing
        const request = Object.assign({}, params);

        // If first request..
        if (!request.hasOwnProperty('count')) {
            request.count = this.TWEETS_TO_TRACK >= 200 ? 200 : this.TWEETS_TO_TRACK;
            tweetsLeft = this.TWEETS_TO_TRACK;
            tweetsRetrieved = [];

            this.utils.log('New user with ID: "' + request.user_id + '" added');
            this.utils.log('    Retrieving most recent ' + this.TWEETS_TO_TRACK + ' tweets');
        }

        return this.twitterHandler.getTweets(request).then((tweets) => {
            const numRetrieved = tweets.length - 1;
            tweetsLeft -= numRetrieved;

            // If only one tweet was returned and it was the max ID tweet requested..
            if (request.max_id === tweets[tweets.length - 1].id_str) {
                tweetsRetrieved.push(tweets[0]);

                this.utils.log('    Retrieved ' + tweetsRetrieved.length + '/' + this.TWEETS_TO_TRACK + ' tweets');
                this.utils.log('    Finished retrieving tweets');
                if (request.count > 1) {
                    this.utils.log('    Reached max tweet retrieval limit.');
                }

                return {
                    done: true,
                    tweets: tweetsRetrieved
                };
            }

            // Otherwise add retrieved tweets
            for (let i = 0; i < numRetrieved; i++) {
                tweetsRetrieved.push(tweets[i]);
            }

            this.utils.log('    Retrieved ' + tweetsRetrieved.length + '/' + this.TWEETS_TO_TRACK + ' tweets');

            // Set newest ID for next request to oldest ID of the previous request
            const nextRequest = Object.assign({}, request);
            nextRequest.max_id = tweets[tweets.length - 1].id_str;
            if (tweetsLeft < 200) {
                nextRequest.count = tweetsLeft;
            }

            return {
                done: false,
                request: nextRequest,
                tweetsLeft: tweetsLeft,
                tweetsRetrieved: tweetsRetrieved
            };
        }).then((next) => {
            if (!next.done) {
                return this.retrieveForNew(next.request, next.tweetsLeft, next.tweetsRetrieved);
            } else {
                return next.tweets;
            }
        });
    }

};
<|endoftext|>


<commit_before>'use strict';

/*
  node-schedule
  A cron-like and not-cron-like job scheduler for Node.
*/

var events = require('events'),
  util = require('util'),
  increment = require('./increment.js'),
  cronParser = require('cron-parser'),
  lt = require('long-timeout');

/* Job object */
var anonJobCounter = 0;

function Job(name, job, callback) {
  // setup a private pendingInvocations variable
  var pendingInvocations = [];

  // Set scope vars
  var jobName = name && typeof name === 'string' ? name : '<Anonymous Job ' + (++anonJobCounter) + '>';
  this.job = name && typeof name === 'function' ? name : job;

  // Make sure callback is actually a callback
  if (this.job === name) {
    // Name wasn't provided and maybe a callback is there
    this.callback = typeof job === 'function' ? job : false;
  } else {
    // Name was provided, and maybe a callback is there
    this.callback = typeof callback === 'function' ? callback : false;
  }

  // define properties
  Object.defineProperty(this, 'name', {
    value: jobName,
    writable: false,
    enumerable: true
  });

  // method that require private access
  this.trackInvocation = function(invocation) {
    // add to our invocation list
    pendingInvocations.push(invocation);

    // and sort
    pendingInvocations.sort(sorter);

    return true;
  };
  this.stopTrackingInvocation = function(invocation) {
    var invIdx = pendingInvocations.indexOf(invocation);
    if (invIdx > -1) {
      pendingInvocations.splice(invIdx, 1);
      return true;
    }

    return false;
  };
  this.cancel = function(reschedule) {
    reschedule = (typeof reschedule == 'boolean') ? reschedule : false;

    var inv, newInv;
    var newInvs = [];
    for (var j = 0; j < pendingInvocations.length; j++) {
      inv = pendingInvocations[j];

      cancelInvocation(inv);

      if (reschedule && inv.recurrenceRule.recurs) {
        newInv = scheduleNextRecurrence(inv.recurrenceRule, this, inv.fireDate);
        if (newInv !== null) {
          newInvs.push(newInv);
        }
      }
    }

    pendingInvocations = [];

    for (var k = 0; k < newInvs.length; k++) {
      this.trackInvocation(newInvs[k]);
    }

    // remove from scheduledJobs if reschedule === false
    if (!reschedule) {
      if (this.name) {
        scheduledJobs[this.name] = null;
      }
    }

    return true;
  };
  this.cancelNext = function(reschedule) {
    reschedule = (typeof reschedule == 'boolean') ? reschedule : true;

    if (!pendingInvocations.length) {
      return false;
    }

    var newInv;
    var nextInv = pendingInvocations.shift();

    cancelInvocation(nextInv);

    if (reschedule && nextInv.recurrenceRule.recurs) {
      newInv = scheduleNextRecurrence(nextInv.recurrenceRule, this, nextInv.fireDate);
      if (newInv !== null) {
        this.trackInvocation(newInv);
      }
    }

    return true;
  };
  this.nextInvocation = function() {
    if (!pendingInvocations.length) {
      return null;
    }
    return pendingInvocations[0].fireDate;
  };
  this.pendingInvocations = function() {
    return pendingInvocations;
  };
}

util.inherits(Job, events.EventEmitter);

Job.prototype.invoke = function() {
  if (typeof this.job == 'function') {
    this.job();
  } else {
    this.job.execute();
  }
};

Job.prototype.runOnDate = function(date) {
  return this.schedule(date);
};

Job.prototype.schedule = function(spec) {
  var self = this;
  var success = false;
  var inv;
  try {
    var res = cronParser.parseExpression(spec);
    inv = scheduleNextRecurrence(res, self);
    if (inv !== null) {
      success = self.trackInvocation(inv);
    }

  } catch (err) {
    var type = typeof spec;
    if (type === 'string') {
      spec = new Date(spec);
    }

    if (spec instanceof Date) {
      inv = new Invocation(self, spec);
      scheduleInvocation(inv);
      success = self.trackInvocation(inv);
    } else if (type === 'object') {
      if (!(spec instanceof RecurrenceRule)) {
        var r = new RecurrenceRule();
        if ('year' in spec) {
          r.year = spec.year;
        }
        if ('month' in spec) {
          r.month = spec.month;
        }
        if ('date' in spec) {
          r.date = spec.date;
        }
        if ('dayOfWeek' in spec) {
          r.dayOfWeek = spec.dayOfWeek;
        }
        if ('hour' in spec) {
          r.hour = spec.hour;
        }
        if ('minute' in spec) {
          r.minute = spec.minute;
        }
        if ('second' in spec) {
          r.second = spec.second;
        }

        spec = r;
      }

      inv = scheduleNextRecurrence(spec, self);
      if (inv !== null) {
        success = self.trackInvocation(inv);
      }
    }
  }

  return success;
};

/* API
  invoke()
  runOnDate(date)
  schedule(date || recurrenceRule || cronstring)
  cancel(reschedule = false)
  cancelNext(reschedule = true)

   Property constraints
  name: readonly
  job: readwrite
*/

/* DoesntRecur rule */
var DoesntRecur = new RecurrenceRule();
DoesntRecur.recurs = false;

/* Invocation object */
function Invocation(job, fireDate, recurrenceRule) {
  this.job = job;
  this.fireDate = fireDate;
  this.recurrenceRule = recurrenceRule || DoesntRecur;

  this.timerID = null;
}

function sorter(a, b) {
  return (a.fireDate.getTime() - b.fireDate.getTime());
}

/* Range object */
function Range(start, end, step) {
  this.start = start || 0;
  this.end = end || 60;
  this.step = step || 1;
}

Range.prototype.contains = function(val) {
  if (this.step === null || this.step === 1) {
    return (val >= this.start && val <= this.end);
  } else {
    for (var i = this.start; i < this.end; i += this.step) {
      if (i === val) {
        return true;
      }
    }

    return false;
  }
};

/* RecurrenceRule object */
/*
  Interpreting each property:
  null - any value is valid
  number - fixed value
  Range - value must fall in range
  array - value must validate against any item in list

  NOTE: Cron months are 1-based, but RecurrenceRule months are 0-based.
*/
function RecurrenceRule(year, month, date, dayOfWeek, hour, minute, second) {
  this.recurs = true;

  this.year = (year == null) ? null : year;
  this.month = (month == null) ? null : month;
  this.date = (date == null) ? null : date;
  this.dayOfWeek = (dayOfWeek == null) ? null : dayOfWeek;
  this.hour = (hour == null) ? null : hour;
  this.minute = (minute == null) ? null : minute;
  this.second = (second == null) ? 0 : second;
}

RecurrenceRule.prototype.nextInvocationDate = function(base) {
  base = (base instanceof Date) ? base : (new Date());
  increment.addDateConvenienceMethods(base);
  if (!this.recurs) {
    return null;
  }

  var now = new Date();
  increment.addDateConvenienceMethods(now);
  if (this.year !== null && (typeof this.year == 'number') && this.year < now.getFullYear()) {
    return null;
  }

  var next = new Date(base.getTime());
  increment.addDateConvenienceMethods(next);
  next.addSecond();

  while (true) {
    if (this.year != null && !recurMatch(next.getFullYear(), this.year)) {
      next.addYear();
      next.setMonth(0);
      next.setDate(1);
      next.setHours(0);
      next.setMinutes(0);
      next.setSeconds(0);
      continue;
    }
    if (this.month != null && !recurMatch(next.getMonth(), this.month)) {
      next.addMonth();
      continue;
    }
    if (this.date != null && !recurMatch(next.getDate(), this.date)) {
      next.addDay();
      continue;
    }
    if (this.dayOfWeek != null && !recurMatch(next.getDay(), this.dayOfWeek)) {
      next.addDay();
      continue;
    }
    if (this.hour != null && !recurMatch(next.getHours(), this.hour)) {
      next.addHour();
      continue;
    }
    if (this.minute != null && !recurMatch(next.getMinutes(), this.minute)) {
      next.addMinute();
      continue;
    }
    if (this.second != null && !recurMatch(next.getSeconds(), this.second)) {
      next.addSecond();
      continue;
    }

    break;
  }

  return next;
};

function recurMatch(val, matcher) {
  if (matcher == null) {
    return true;
  }

  if (typeof matcher === 'number' || typeof matcher === 'string') {
    return (val === matcher);
  } else if (matcher instanceof Range) {
    return matcher.contains(val);
  } else if (Array.isArray(matcher) || (matcher instanceof Array)) {
    for (var i = 0; i < matcher.length; i++) {
      if (recurMatch(val, matcher[i])) {
        return true;
      }
    }
  }

  return false;
}

/* Date-based scheduler */
function runOnDate(date, job) {
  var now = (new Date()).getTime();
  var then = date.getTime();

  if (then < now) {
    process.nextTick(job);
    return null;
  }

  return lt.setTimeout(job, (then - now));
}

var invocations = [];
var currentInvocation = null;

function scheduleInvocation(invocation) {
  invocations.push(invocation);
  invocations.sort(sorter);
  prepareNextInvocation();
  invocation.job.emit('scheduled', invocation.fireDate);
}

function prepareNextInvocation() {
  if (invocations.length > 0 && currentInvocation !== invocations[0]) {
    if (currentInvocation !== null) {
      lt.clearTimeout(currentInvocation.timerID);
      currentInvocation.timerID = null;
      currentInvocation = null;
    }

    currentInvocation = invocations[0];

    var job = currentInvocation.job;
    var cinv = currentInvocation;
    currentInvocation.timerID = runOnDate(currentInvocation.fireDate, function() {
      currentInvocationFinished();

      if (job.callback) {
        job.callback();
      }

      if (cinv.recurrenceRule.recurs || cinv.recurrenceRule._endDate === null) {
        var inv = scheduleNextRecurrence(cinv.recurrenceRule, cinv.job, cinv.fireDate);
        if (inv !== null) {
          inv.job.trackInvocation(inv);
        }
      }

      job.stopTrackingInvocation(cinv);

      job.invoke();
      job.emit('run');
    });
  }
}

function currentInvocationFinished() {
  invocations.shift();
  currentInvocation = null;
  prepareNextInvocation();
}

function cancelInvocation(invocation) {
  var idx = invocations.indexOf(invocation);
  if (idx > -1) {
    invocations.splice(idx, 1);
    if (invocation.timerID !== null) {
      lt.clearTimeout(invocation.timerID);
    }

    if (currentInvocation === invocation) {
      currentInvocation = null;
    }

    invocation.job.emit('canceled', invocation.fireDate);
    prepareNextInvocation();
  }
}

/* Recurrence scheduler */
function scheduleNextRecurrence(rule, job, prevDate) {
  prevDate = (prevDate instanceof Date) ? prevDate : (new Date());

  var date = (rule instanceof RecurrenceRule) ? rule.nextInvocationDate(prevDate) : rule.next();
  if (date === null) {
    return null;
  }

  var inv = new Invocation(job, date, rule);
  scheduleInvocation(inv);

  return inv;
}

/* Convenience methods */
var scheduledJobs = {};

function scheduleJob() {
  if (arguments.length < 2) {
    return null;
  }

  var name = (arguments.length >= 4) ? arguments[0] : null;
  var spec = (arguments.length >= 4) ? arguments[1] : arguments[0];
  var method = (arguments.length >= 4) ? arguments[2] : arguments[1];
  var callback = (arguments.length >= 4) ? arguments[3] : arguments[2];

  var job = new Job(name, method, callback);

  if (job.schedule(spec)) {
    scheduledJobs[job.name] = job;
    return job;
  }

  return null;
}

function cancelJob(job) {
  var success = false;
  if (job instanceof Job) {
    success = job.cancel();
  } else if (typeof job == 'string' || job instanceof String) {
    if (job in scheduledJobs && scheduledJobs.hasOwnProperty(job)) {
      success = scheduledJobs[job].cancel();
    }
  }

  return success;
}

/* Public API */
exports.Job = Job;
exports.Range = Range;
exports.RecurrenceRule = RecurrenceRule;
exports.Invocation = Invocation;
exports.scheduleJob = scheduleJob;
exports.scheduledJobs = scheduledJobs;
exports.cancelJob = cancelJob;
exports.addDateConvenienceMethods = increment.addDateConvenienceMethods;
<commit_msg>Quickfix for #179<commit_after>'use strict';

/*
  node-schedule
  A cron-like and not-cron-like job scheduler for Node.
*/

var events = require('events'),
  util = require('util'),
  increment = require('./increment.js'),
  cronParser = require('cron-parser'),
  lt = require('long-timeout');

/* Job object */
var anonJobCounter = 0;

function Job(name, job, callback) {
  // setup a private pendingInvocations variable
  var pendingInvocations = [];

  // Set scope vars
  var jobName = name && typeof name === 'string' ? name : '<Anonymous Job ' + (++anonJobCounter) + '>';
  this.job = name && typeof name === 'function' ? name : job;

  // Make sure callback is actually a callback
  if (this.job === name) {
    // Name wasn't provided and maybe a callback is there
    this.callback = typeof job === 'function' ? job : false;
  } else {
    // Name was provided, and maybe a callback is there
    this.callback = typeof callback === 'function' ? callback : false;
  }

  // define properties
  Object.defineProperty(this, 'name', {
    value: jobName,
    writable: false,
    enumerable: true
  });

  // method that require private access
  this.trackInvocation = function(invocation) {
    // add to our invocation list
    pendingInvocations.push(invocation);

    // and sort
    pendingInvocations.sort(sorter);

    return true;
  };
  this.stopTrackingInvocation = function(invocation) {
    var invIdx = pendingInvocations.indexOf(invocation);
    if (invIdx > -1) {
      pendingInvocations.splice(invIdx, 1);
      return true;
    }

    return false;
  };
  this.cancel = function(reschedule) {
    reschedule = (typeof reschedule == 'boolean') ? reschedule : false;

    var inv, newInv;
    var newInvs = [];
    for (var j = 0; j < pendingInvocations.length; j++) {
      inv = pendingInvocations[j];

      cancelInvocation(inv);

      if (reschedule && inv.recurrenceRule.recurs) {
        newInv = scheduleNextRecurrence(inv.recurrenceRule, this, inv.fireDate);
        if (newInv !== null) {
          newInvs.push(newInv);
        }
      }
    }

    pendingInvocations = [];

    for (var k = 0; k < newInvs.length; k++) {
      this.trackInvocation(newInvs[k]);
    }

    // remove from scheduledJobs if reschedule === false
    if (!reschedule) {
      if (this.name) {
        scheduledJobs[this.name] = null;
      }
    }

    return true;
  };
  this.cancelNext = function(reschedule) {
    reschedule = (typeof reschedule == 'boolean') ? reschedule : true;

    if (!pendingInvocations.length) {
      return false;
    }

    var newInv;
    var nextInv = pendingInvocations.shift();

    cancelInvocation(nextInv);

    if (reschedule && nextInv.recurrenceRule.recurs) {
      newInv = scheduleNextRecurrence(nextInv.recurrenceRule, this, nextInv.fireDate);
      if (newInv !== null) {
        this.trackInvocation(newInv);
      }
    }

    return true;
  };
  this.nextInvocation = function() {
    if (!pendingInvocations.length) {
      return null;
    }
    return pendingInvocations[0].fireDate;
  };
  this.pendingInvocations = function() {
    return pendingInvocations;
  };
}

util.inherits(Job, events.EventEmitter);

Job.prototype.invoke = function() {
  if (typeof this.job == 'function') {
    this.job();
  } else {
    this.job.execute();
  }
};

Job.prototype.runOnDate = function(date) {
  return this.schedule(date);
};

Job.prototype.schedule = function(spec) {
  var self = this;
  var success = false;
  var inv;
  try {
    var res = cronParser.parseExpression(spec);
    inv = scheduleNextRecurrence(res, self);
    if (inv !== null) {
      success = self.trackInvocation(inv);
    }

  } catch (err) {
    var type = typeof spec;
    if (type === 'string') {
      spec = new Date(spec);
    }

    if (spec instanceof Date) {
      inv = new Invocation(self, spec);
      scheduleInvocation(inv);
      success = self.trackInvocation(inv);
    } else if (type === 'object') {
      if (!(spec instanceof RecurrenceRule)) {
        var r = new RecurrenceRule();
        if ('year' in spec) {
          r.year = spec.year;
        }
        if ('month' in spec) {
          r.month = spec.month;
        }
        if ('date' in spec) {
          r.date = spec.date;
        }
        if ('dayOfWeek' in spec) {
          r.dayOfWeek = spec.dayOfWeek;
        }
        if ('hour' in spec) {
          r.hour = spec.hour;
        }
        if ('minute' in spec) {
          r.minute = spec.minute;
        }
        if ('second' in spec) {
          r.second = spec.second;
        }

        spec = r;
      }

      inv = scheduleNextRecurrence(spec, self);
      if (inv !== null) {
        success = self.trackInvocation(inv);
      }
    }
  }

  return success;
};

/* API
  invoke()
  runOnDate(date)
  schedule(date || recurrenceRule || cronstring)
  cancel(reschedule = false)
  cancelNext(reschedule = true)

   Property constraints
  name: readonly
  job: readwrite
*/

/* DoesntRecur rule */
var DoesntRecur = new RecurrenceRule();
DoesntRecur.recurs = false;

/* Invocation object */
function Invocation(job, fireDate, recurrenceRule) {
  this.job = job;
  this.fireDate = fireDate;
  this.recurrenceRule = recurrenceRule || DoesntRecur;

  this.timerID = null;
}

function sorter(a, b) {
  return (a.fireDate.getTime() - b.fireDate.getTime());
}

/* Range object */
function Range(start, end, step) {
  this.start = start || 0;
  this.end = end || 60;
  this.step = step || 1;
}

Range.prototype.contains = function(val) {
  if (this.step === null || this.step === 1) {
    return (val >= this.start && val <= this.end);
  } else {
    for (var i = this.start; i < this.end; i += this.step) {
      if (i === val) {
        return true;
      }
    }

    return false;
  }
};

/* RecurrenceRule object */
/*
  Interpreting each property:
  null - any value is valid
  number - fixed value
  Range - value must fall in range
  array - value must validate against any item in list

  NOTE: Cron months are 1-based, but RecurrenceRule months are 0-based.
*/
function RecurrenceRule(year, month, date, dayOfWeek, hour, minute, second) {
  this.recurs = true;

  this.year = (year == null) ? null : year;
  this.month = (month == null) ? null : month;
  this.date = (date == null) ? null : date;
  this.dayOfWeek = (dayOfWeek == null) ? null : dayOfWeek;
  this.hour = (hour == null) ? null : hour;
  this.minute = (minute == null) ? null : minute;
  this.second = (second == null) ? 0 : second;
}

RecurrenceRule.prototype.nextInvocationDate = function(base) {
  base = (base instanceof Date) ? base : (new Date());
  increment.addDateConvenienceMethods(base);
  if (!this.recurs) {
    return null;
  }

  var now = new Date();
  increment.addDateConvenienceMethods(now);
  if (this.year !== null && (typeof this.year == 'number') && this.year < now.getFullYear()) {
    return null;
  }

  var next = new Date(base.getTime());
  increment.addDateConvenienceMethods(next);
  next.addSecond();

  while (true) {
    if (this.year != null && !recurMatch(next.getFullYear(), this.year)) {
      next.addYear();
      next.setMonth(0);
      next.setDate(1);
      next.setHours(0);
      next.setMinutes(0);
      next.setSeconds(0);
      continue;
    }
    if (this.month != null && !recurMatch(next.getMonth(), this.month)) {
      next.addMonth();
      continue;
    }
    if (this.date != null && !recurMatch(next.getDate(), this.date)) {
      next.addDay();
      continue;
    }
    if (this.dayOfWeek != null && !recurMatch(next.getDay(), this.dayOfWeek)) {
      next.addDay();
      continue;
    }
    if (this.hour != null && !recurMatch(next.getHours(), this.hour)) {
      next.addHour();
      continue;
    }
    if (this.minute != null && !recurMatch(next.getMinutes(), this.minute)) {
      next.addMinute();
      continue;
    }
    if (this.second != null && !recurMatch(next.getSeconds(), this.second)) {
      next.addSecond();
      continue;
    }

    break;
  }

  return next;
};

function recurMatch(val, matcher) {
  if (matcher == null) {
    return true;
  }

  if (typeof matcher === 'number' || typeof matcher === 'string') {
    return (val === matcher);
  } else if (matcher instanceof Range) {
    return matcher.contains(val);
  } else if (Array.isArray(matcher) || (matcher instanceof Array)) {
    for (var i = 0; i < matcher.length; i++) {
      if (recurMatch(val, matcher[i])) {
        return true;
      }
    }
  }

  return false;
}

/* Date-based scheduler */
function runOnDate(date, job) {
  var now = (new Date()).getTime();
  var then = date.getTime();

  if (then < now) {
    process.nextTick(job);
    return null;
  }

  return lt.setTimeout(job, (then - now));
}

var invocations = [];
var currentInvocation = null;

function scheduleInvocation(invocation) {
  invocations.push(invocation);
  invocations.sort(sorter);
  prepareNextInvocation();
  invocation.job.emit('scheduled', invocation.fireDate);
}

function prepareNextInvocation() {
  if (invocations.length > 0 && currentInvocation !== invocations[0]) {
    if (currentInvocation !== null) {
      lt.clearTimeout(currentInvocation.timerID);
      currentInvocation.timerID = null;
      currentInvocation = null;
    }

    currentInvocation = invocations[0];

    var job = currentInvocation.job;
    var cinv = currentInvocation;
    currentInvocation.timerID = runOnDate(currentInvocation.fireDate, function() {
      currentInvocationFinished();

      if (job.callback) {
        job.callback();
      }

      if (cinv.recurrenceRule.recurs || cinv.recurrenceRule._endDate === null) {
        var inv = scheduleNextRecurrence(cinv.recurrenceRule, cinv.job, cinv.fireDate);
        if (inv !== null) {
          inv.job.trackInvocation(inv);
        }
      }

      job.stopTrackingInvocation(cinv);

      job.invoke();
      job.emit('run');
    });
  }
}

function currentInvocationFinished() {
  invocations.shift();
  currentInvocation = null;
  prepareNextInvocation();
}

function cancelInvocation(invocation) {
  var idx = invocations.indexOf(invocation);
  if (idx > -1) {
    invocations.splice(idx, 1);
    if (invocation.timerID !== null) {
      lt.clearTimeout(invocation.timerID);
    }

    if (currentInvocation === invocation) {
      currentInvocation = null;
    }

    invocation.job.emit('canceled', invocation.fireDate);
    prepareNextInvocation();
  }
}

/* Recurrence scheduler */
function scheduleNextRecurrence(rule, job, prevDate) {
  prevDate = (prevDate instanceof Date) ? prevDate : (new Date());

  var date = (rule instanceof RecurrenceRule) ? rule.nextInvocationDate(prevDate) : rule.next();
  if (date === null) {
    return null;
  }

  var inv = new Invocation(job, date, rule);
  scheduleInvocation(inv);

  return inv;
}

/* Convenience methods */
var scheduledJobs = {};

function scheduleJob() {
  if (arguments.length < 2) {
    return null;
  }

  var name = (arguments.length >= 3 && typeof arguments[0] === 'string') ? arguments[0] : null;
  var spec = null;
  var method = null;
  var callback = false;

  if (name) {
    spec =  arguments[1];
    method = arguments[2];
    callback = arguments[3];
  } else {
    // For convienence methods
    spec = arguments[0];
    method = arguments[1];
    callback = arguments[2];
  }

  var job = new Job(name, method, callback);

  if (job.schedule(spec)) {
    scheduledJobs[job.name] = job;
    return job;
  }

  return null;
}

function cancelJob(job) {
  var success = false;
  if (job instanceof Job) {
    success = job.cancel();
  } else if (typeof job == 'string' || job instanceof String) {
    if (job in scheduledJobs && scheduledJobs.hasOwnProperty(job)) {
      success = scheduledJobs[job].cancel();
    }
  }

  return success;
}

/* Public API */
exports.Job = Job;
exports.Range = Range;
exports.RecurrenceRule = RecurrenceRule;
exports.Invocation = Invocation;
exports.scheduleJob = scheduleJob;
exports.scheduledJobs = scheduledJobs;
exports.cancelJob = cancelJob;
exports.addDateConvenienceMethods = increment.addDateConvenienceMethods;
<|endoftext|>


<commit_before>'use strict';

var fileScoper = require('./scoped-name');

var classRegex = /(\.)([^\s\.,{\[>+~#:]*)/g;
var keyframesRegex = /(@\S*keyframes\s*)([^{\s]*)/g;

module.exports = scopify;

function scopify(css, ignores) {
  var makeScopedName = fileScoper(css);
  var replacers = {
    'classes': classRegex,
    'keyframes': keyframesRegex
  };

  function scopeCss(result, key) {
    var replacer = replacers[key];
    function replaceFn(fullMatch, prefix, name) {
      var scopedName = ignores[name] ? name : makeScopedName(name);
      result[key][scopedName] = name;
      return prefix + scopedName;
    }
    return {
      css: result.css.replace(replacer, replaceFn),
      keyframes: result.keyframes,
      classes: result.classes
    };
  }

  var result = Object.keys(replacers).reduce(scopeCss, {
    css: css,
    keyframes: {},
    classes: {}
  });

  return replaceAnimations(result);
}

function replaceAnimations(result) {
  var animations = Object.keys(result.keyframes).reduce(function(acc, key) {
    acc[result.keyframes[key]] = key;
    return acc;
  }, {});
  var unscoped = Object.keys(animations);

  if (unscoped.length) {
    var regexStr = '((?:animation|animation-name)\\s*:[^};]*)('
      + unscoped.join('|') + ')';
    var regex = new RegExp(regexStr, 'g');

    var replacedCss = result.css.replace(regex, function(fullMatch, preamble, name) {
      return preamble + animations[name];
    });

    return {
      css: replacedCss,
      keyframes: result.keyframes,
      classes: result.classes
    }
  }

  return result;
}
<commit_msg>Minor cleanup<commit_after>'use strict';

var fileScoper = require('./scoped-name');

var classRegex = /(\.)([^\s\.,{\[>+~#:]*)/g;
var keyframesRegex = /(@\S*keyframes\s*)([^{\s]*)/g;

module.exports = scopify;

function scopify(css, ignores) {
  var makeScopedName = fileScoper(css);
  var replacers = {
    'classes': classRegex,
    'keyframes': keyframesRegex
  };

  function scopeCss(result, key) {
    var replacer = replacers[key];
    function replaceFn(fullMatch, prefix, name) {
      var scopedName = ignores[name] ? name : makeScopedName(name);
      result[key][scopedName] = name;
      return prefix + scopedName;
    }
    return {
      css: result.css.replace(replacer, replaceFn),
      keyframes: result.keyframes,
      classes: result.classes
    };
  }

  var result = Object.keys(replacers).reduce(scopeCss, {
    css: css,
    keyframes: {},
    classes: {}
  });

  return replaceAnimations(result);
}

function replaceAnimations(result) {
  var animations = Object.keys(result.keyframes).reduce(function(acc, key) {
    acc[result.keyframes[key]] = key;
    return acc;
  }, {});
  var unscoped = Object.keys(animations);

  if (unscoped.length) {
    var regexStr = '((?:animation|animation-name)\\s*:[^};]*)('
      + unscoped.join('|') + ')';
    var regex = new RegExp(regexStr, 'g');

    var replaced = result.css.replace(regex, function(match, preamble, name) {
      return preamble + animations[name];
    });

    return {
      css: replaced,
      keyframes: result.keyframes,
      classes: result.classes
    }
  }

  return result;
}
<|endoftext|>


<commit_before>'use strict';

(function() {
  function scramble(input, opts) {
    var objectType = Object.prototype.toString.call(input);

    if (objectType == '[object Array]') {
      return main(input, opts);
    } else if (objectType == '[object String]') {
      input = input.split('');
      return main(input, opts).join('');
    }
  }

  function main(array, opts) {
    // For now assume all inputs are arrays
    var index = array.length - 1;

    while (index) {
      //Generate a random number
      var rand = Math.floor((index + 1) * Math.random());

      //Swap
      var temp = array[rand];
      array[rand] = array[index];
      array[index] = temp;

      //Decrement index
      index -= 1;
    }

    return array;
  }

  function attach() {
    Array.prototype.scramble = function(opts) {
      return scramble(this, opts);
    };

    String.prototype.scramble = function(opts) {
      var input = this.split('');
      return scramble(input, opts).join('');
    }
  }

  scramble.attach = attach;

  if (typeof module !== 'undefined' && typeof exports === 'object') {
    module.exports = scramble;
  } else if (typeof define === 'function' && define.amd) {
    define(function() {
      return scramble;
    });
  } else {
    this.scramble = scramble;
  }
}).call(function() {
  return this || (typeof window !== 'undefined' ? window : global);
}());
<commit_msg>Add the option to preserve a portion of the input<commit_after>'use strict';

(function() {

  function between(x, min, max) {
    return x >= min && x <= max;
  }

  function scramble(input, opts) {
    var objectType = Object.prototype.toString.call(input);
    var rejoin = false;
    var output;

    if (objectType == '[object String]') {
      rejoin = true;
      input = input.split('');
    }

    if (opts && opts.preserve) {
      var start = opts.preserve[0];
      var end = opts.preserve[1];

      if (between(start, 0, input.length - 1) &&
        between(end, 0, input.length - 1) &&
        end > start) {

        var array1 = input.slice(start, end + 1);
        var array2 = input.slice(0, start)
          .concat(input.slice(end + 1, input.length));
        output = scramble(array2);
        // console.log(output);
        // var args = [start, array1.length].concat(array1);
        // console.log(args);
        output.splice.apply(output, [start, 0].concat(array1));
      } else {
        throw new Error('Provided range must be within bounds');
      }
    } else {
      output = main(input, opts);
    }

    if (rejoin) {
      output = output.join('');
    }

    return output;
  }

  function main(array) {
    // For now assume all inputs are arrays
    var index = array.length - 1;

    while (index) {
      //Generate a random number
      var rand = Math.floor((index + 1) * Math.random());

      //Swap
      var temp = array[rand];
      array[rand] = array[index];
      array[index] = temp;

      //Decrement index
      index -= 1;
    }

    return array;
  }

  function attach() {
    Array.prototype.scramble = function(opts) {
      return scramble(this, opts);
    };

    String.prototype.scramble = function(opts) {
      return scramble(this, opts);
    }
  }

  scramble.attach = attach;

  if (typeof module !== 'undefined' && typeof exports === 'object') {
    module.exports = scramble;
  } else if (typeof define === 'function' && define.amd) {
    define(function() {
      return scramble;
    });
  } else {
    this.scramble = scramble;
  }
}).call(function() {
  return this || (typeof window !== 'undefined' ? window : global);
}());
<|endoftext|>


<commit_before>"use strict";

var package_json    = require('./../package.json');
var _               = require('lodash');
var request         = require('request');
var Email           = require('./email');
var Sendgrid = function(api_user, api_key, options) {

  if( !(this instanceof Sendgrid) ) {
    return new Sendgrid(api_user, api_key, options);
  }

  var _this         = this;
  this.options      = options || {};

  // do this to mantain similarity to other libs
  var uriParts = {};
  uriParts.protocol = this.options.protocol || "https";
  uriParts.host = this.options.host || "api.sendgrid.com";
  uriParts.port = this.options.port || "";
  uriParts.endpoint = this.options.endpoint || "/api/mail.send.json";
  delete this.options.protocol;
  delete this.options.host;
  delete this.options.port;
  delete this.options.endpoint;
  this.options.uriParts = uriParts;

  this.options.uri = this.options.uri || uriParts.protocol + "://" + uriParts.host + (uriParts.port ? ":" + uriParts.port : "") + uriParts.endpoint;

  var send = function(email, callback) {
    var callback    = callback || function() { };
    if (email.constructor !== Email) {
      email = new Email(email);
    }

    _send.bind(this)(email, callback);
  };

  var _send = function(email, callback) {
    var postOptions = {
      method    : 'POST'
    };

    var options = _.merge(postOptions, this.options);

    var req   = request(options, function(err, resp, body) {
      var json;

      if(err) return callback(err, null);

      try {
        json = JSON.parse(body);
      } catch (e) {
        // be more granular with the error message
        e.message = e.message + " JSONPARSEERROR when parsing: " + body;
        return callback(new Error(e), null);
      }

      if (json.message !== 'success') {
        var error = 'sendgrid error';
        if (json.errors) { error = json.errors.shift(); }
        return callback(new Error(error), null);
      }

      return callback(null, json);
    });

    var form          = email.toWebFormat();
    form['api_user']  = api_user;
    form['api_key']   = api_key;

    var reqForm = req.form();

    var _reqFormAppend = function(field, value) {
      reqForm.append(field, value);
    };

    for (var field in form) {
      var value = form[field];
      if (value && value.filename) {
        if (value.cid) {
          reqForm.append("content["+value.filename+"]", value.cid);
        }
        reqForm.append("files["+value.filename+"]", value.content, {filename: value.filename, contentType: value.contentType});
      } else {
        try {
          if(!Array.isArray(value)){
            //reqForm.append(field, value);
            _reqFormAppend.bind(this)(field, value);
          } else {
            value.forEach(_reqFormAppend.bind(this, field));
          }
        } catch(err) {}
      }
    }
  };

  /*
   * Expose public API calls
   */
  this.version         = package_json.version;
  this.Email           = Email;
  this.api_user        = api_user;
  this.api_key         = api_key;
  this.send            = send;
  this.options         = this.options;
  return this;
};

module.exports = Sendgrid;
<commit_msg>Add useragent<commit_after>"use strict";

var package_json    = require('./../package.json');
var _               = require('lodash');
var request         = require('request');
var Email           = require('./email');
var Sendgrid = function(api_user, api_key, options) {

  if( !(this instanceof Sendgrid) ) {
    return new Sendgrid(api_user, api_key, options);
  }

  var _this         = this;
  this.options      = options || {};

  // do this to mantain similarity to other libs
  var uriParts = {};
  uriParts.protocol = this.options.protocol || "https";
  uriParts.host = this.options.host || "api.sendgrid.com";
  uriParts.port = this.options.port || "";
  uriParts.endpoint = this.options.endpoint || "/api/mail.send.json";
  delete this.options.protocol;
  delete this.options.host;
  delete this.options.port;
  delete this.options.endpoint;
  this.options.uriParts = uriParts;

  this.options.uri = this.options.uri || uriParts.protocol + "://" + uriParts.host + (uriParts.port ? ":" + uriParts.port : "") + uriParts.endpoint;

  var send = function(email, callback) {
    var callback    = callback || function() { };
    if (email.constructor !== Email) {
      email = new Email(email);
    }

    _send.bind(this)(email, callback);
  };

  var _send = function(email, callback) {
    var postOptions = {
      method    : 'POST',
      headers   : {
        'User-Agent': 'sendgrid/' + package_json.version + ';nodejs'
      }
    };

    var options = _.merge(postOptions, this.options);

    var req   = request(options, function(err, resp, body) {
      var json;

      if(err) return callback(err, null);

      try {
        json = JSON.parse(body);
      } catch (e) {
        // be more granular with the error message
        e.message = e.message + " JSONPARSEERROR when parsing: " + body;
        return callback(new Error(e), null);
      }

      if (json.message !== 'success') {
        var error = 'sendgrid error';
        if (json.errors) { error = json.errors.shift(); }
        return callback(new Error(error), null);
      }

      return callback(null, json);
    });

    var form          = email.toWebFormat();
    form['api_user']  = api_user;
    form['api_key']   = api_key;

    var reqForm = req.form();

    var _reqFormAppend = function(field, value) {
      reqForm.append(field, value);
    };

    for (var field in form) {
      var value = form[field];
      if (value && value.filename) {
        if (value.cid) {
          reqForm.append("content["+value.filename+"]", value.cid);
        }
        reqForm.append("files["+value.filename+"]", value.content, {filename: value.filename, contentType: value.contentType});
      } else {
        try {
          if(!Array.isArray(value)){
            //reqForm.append(field, value);
            _reqFormAppend.bind(this)(field, value);
          } else {
            value.forEach(_reqFormAppend.bind(this, field));
          }
        } catch(err) {}
      }
    }
  };

  /*
   * Expose public API calls
   */
  this.version         = package_json.version;
  this.Email           = Email;
  this.api_user        = api_user;
  this.api_key         = api_key;
  this.send            = send;
  this.options         = this.options;
  return this;
};

module.exports = Sendgrid;
<|endoftext|>


<commit_before>exports.init = function(grunt) {
    'use strict';

    var requirejs = require('requirejs');
    //project file paths
    var settingsFileName = 'settings.js';
    var requireConfigPath = options.projectPath + 'jam/require.config.js';

    //recursively searches the project folder for a matching settings file
    function getSettingsFilePath() {
        var settingsPath;
        // Guess settingsPath
        if (grunt.file.exists(settingsFileName)) {
            settingsPath = settingsFileName;
        } else {
            var found = grunt.file.expand('*/' + settingsFileName)
            if (found.length) {
                settingsPath = found[0];
            }
        }
        return settingsPath;
    }

    function getSettingsModule() {
        //get settings Path and check valid
        var settingsPath = getSettingsFilePath();
        if (grunt.file.exists(settingsPath)) {
            grunt.log.writeln('Using ' + settingsPath);
        } else {
            //exit with task error
            grunt.warn('Cant find ' + settingsFileName, 3);
        }
        var settings = requirejs(settingsPath);
        return settings;
    };

    function getModulePaths(settings) {
        // Get requireJS config to be able to resolve module paths.
        var config;
        if (grunt.file.exists(requireConfigPath)) {
            //use the require config generated by jam
            config = require(requireConfigPath);
            requirejs.config(config);
        } else {
            //exit with task error
            grunt.warn('Cant find ' + requireConfigPath, 3);
        }

        //Find the text files in all requireJS modules
        var root_len = requirejs.toUrl('').replace(/.js$/, '').length;
        //returns the relative module path to the requireJS root path
        function modulePath(moduleName) {
            var modulePath = requirejs.toUrl(moduleName).replace(/.js$/, '').substr(root_len);
            return modulePath;
        };

        //turn settings.DATA_DIRS into array of modules with paths
        var modulePaths = settings.DATA_DIRS.map(function (moduleName) {
            return {
                name: moduleName,
                path modulePaths(moduleName)
            }
        });

        return modulePaths;
    }

    var settings = getSettingsModule();
    settings.modulePaths = getModulePaths(settings);

    return settings;
};
<commit_msg>add default project path<commit_after>exports.init = function(grunt, options) {
    'use strict';

    defaultOptions = {
        projectPath: 'project/'
    };

    var requirejs = require('requirejs');
    //project file paths
    var settingsFileName = 'settings.js';
    var requireConfigPath = (options.projectPath || defaultOptions.projectPath) + 'jam/require.config.js';

    //recursively searches the project folder for a matching settings file
    function getSettingsFilePath() {
        var settingsPath;
        // Guess settingsPath
        if (grunt.file.exists(settingsFileName)) {
            settingsPath = settingsFileName;
        } else {
            var found = grunt.file.expand('*/' + settingsFileName)
            if (found.length) {
                settingsPath = found[0];
            }
        }
        return settingsPath;
    }

    function getSettingsModule() {
        //get settings Path and check valid
        var settingsPath = getSettingsFilePath();
        if (grunt.file.exists(settingsPath)) {
            grunt.log.writeln('Using ' + settingsPath);
        } else {
            //exit with task error
            grunt.warn('Cant find ' + settingsFileName, 3);
        }
        var settings = requirejs(settingsPath);
        return settings;
    };

    function getModulePaths(settings) {
        // Get requireJS config to be able to resolve module paths.
        var config;
        if (grunt.file.exists(requireConfigPath)) {
            //use the require config generated by jam
            config = require(requireConfigPath);
            requirejs.config(config);
        } else {
            //exit with task error
            grunt.warn('Cant find ' + requireConfigPath, 3);
        }

        //Find the text files in all requireJS modules
        var root_len = requirejs.toUrl('').replace(/.js$/, '').length;
        //returns the relative module path to the requireJS root path
        function modulePath(moduleName) {
            var modulePath = requirejs.toUrl(moduleName).replace(/.js$/, '').substr(root_len);
            return modulePath;
        };

        //turn settings.DATA_DIRS into array of modules with paths
        var modulePaths = settings.DATA_DIRS.map(function (moduleName) {
            return {
                name: moduleName,
                path modulePaths(moduleName)
            }
        });

        return modulePaths;
    }

    var settings = getSettingsModule();
    settings.modulePaths = getModulePaths(settings);

    return settings;
};
<|endoftext|>