<commit_before>'use strict';

const
	$C = require('collection.js'),
	Sugar = require('sugar'),
	fs = require('fs-extra-promise'),
	vinyl = require('vinyl-fs'),
	path = require('path');

const
	Block = require('./block'),
	{entry, entryDependencies, lib, isNodeModule} = require('./resolve'),
	{blockName} = require('./validators');

const
	hasImport = /^import\s+(['"])(.*?)\1;?/,
	entriesDir = new RegExp(`^(?:${$C(entryDependencies).map(Sugar.RegExp.escape).join('|')})(?:[/\\\\]|$)`),
	insideEntry = /^\.\//,
	eol = /\r?\n|\r/;

/**
 * Returns a name for a common chunk by the specified id
 * @param {(string|number)} id
 */
function getCommonName(id) {
	return `common_${id}`;
}

/**
 * Returns a graph with dependencies and union entry points from the specified entries
 *
 * @param entries
 * @param {Map<string, !Block>=} [cache] - optional cache object with predefined blocks
 * @returns {!Promise<{dependencies: Object<string, !Set<string>>, entry: Object<string, !Map<string, !Block>>}>}
 */
async function getUnionEntryPoints(entries, {cache} = {}) {
	const
		packs = Object.create(null),
		weights = Object.create(null);

	await $C(entries).parallel().forEach(async (el) => {
		const
			deps = packs[path.basename(el.path, '.js')] = await getEntryRuntimeDependencies(path.dirname(el.path), el.source, {cache});

		const walk = (block) => {
			const
				name = block.name || block;

			if (name in weights) {
				const
					w = weights[name];

				w.i++;
				if (w.isParent) {
					w.isParent = deps.parents.has(name);
				}

			} else {
				weights[name] = {
					i: 0,
					name,
					isParent: deps.parents.has(name)
				};
			}
		};

		$C(deps.runtime).forEach(walk);
		$C(deps.libs).forEach(walk);
	});

	// Find common modules (weight > 1)

	const
		commonPacks = [];

	$C(weights).forEach((el, key) => {
		if (el.i > 1) {
			const pos = $C(entries).length() - el.i - 1;
			commonPacks[pos] = (commonPacks[pos] || new Map()).set(key, el);
		}
	});

	// Remove empty modules
	$C(commonPacks).remove((el) => !el);

	const
		dependencies = {};

	$C(packs).forEach((deps, name) => {
		const
			depList = new Set();

		$C(deps.runtime).forEach((el, block) => {
			const
				pos = $C(commonPacks).one.search((map) => map.has(block));

			if (pos !== null) {
				depList.add(pos);
				deps.runtime.delete(block);
			}
		});

		dependencies[name] = new Set([...depList].sort((a, b) => a - b).map((i) => getCommonName(i)));
	});

	const
		entry = {};

	$C(commonPacks).forEach((deps, i) => {
		entry[getCommonName(i)] = deps;
	});

	$C(packs).forEach((deps, name) => {
		entry[name] = $C(deps.runtime).to(new Map()).reduce((map, {name}) => map.set(name, {
			name,
			isParent: deps.parents.has(name)
		}));
	});

	return {dependencies, entry};
}

/**
 * Returns a list of imports from the specified entry file
 *
 * @param {string} dir - entry directory
 * @param {string} content - file content
 * @param {!Array} [arr]
 * @returns {!Array<string>}
 */
function getEntryImports(dir, content, arr = []) {
	$C(content.split(eol)).forEach((line) => {
		if (!hasImport.test(line)) {
			return;
		}

		const
			url = RegExp.$2,
			nodeModule = isNodeModule(url);

		if (nodeModule && entriesDir.test(url) || insideEntry.test(url)) {
			const
				d = nodeModule ? lib : dir;

			let
				f = path.join(d, `${url}.js`);

			if (!fs.existsSync(f)) {
				f = path.join(d, url, 'index.js');
			}

			getEntryImports(path.dirname(f), fs.readFileSync(f, 'utf-8'), arr);

		} else {
			arr.push(nodeModule ? url : path.join(dir, url));
		}
	});

	return arr;
}

/**
 * Returns a set of parent entries for the specified entry
 *
 * @param {string} content - file content
 * @returns {!Set<string>}
 */
function getEntryParents(content) {
	const
		parents = new Set(),
		clrfx = /\.\//;

	$C(content.split(eol)).forEach((line) => {
		if (!hasImport.test(line)) {
			return;
		}

		const
			url = RegExp.$2;

		if (isNodeModule(url) && entriesDir.test(url) || insideEntry.test(url)) {
			parents.add(url.replace(clrfx, ''));
		}
	});

	return parents;
}

/**
 * Returns a graph with dependencies for an entry file
 *
 * @param {string} dir - entry directory
 * @param {string} content - file content
 * @param {Map<string, !Block>=} [cache] - optional cache object with predefined blocks
 * @returns {!Promise<{runtime: !Map<string, !Block>, parents: !Map<string, !Block>, libs: !Set<string>}>}
 */
async function getEntryRuntimeDependencies(dir, content, {cache} = {}) {
	const deps = {
		runtime: new Map(),
		parents: new Map(),
		libs: new Set()
	};

	const
		runtime = new Set();

	await $C(getEntryImports(dir, content)).async.forEach(async (el) => {
		const
			name = path.basename(el, path.extname(el)),
			block = cache ? cache.get(name) : await Block.get(name);

		if (!blockName(name) || !block) {
			deps.runtime.set(el, el);
			return;
		}

		const
			blockDeps = await block.getRuntimeDependencies({cache});

		$C(blockDeps.runtime).forEach((obj, block) => {
			if (!blockDeps.parents.has(block)) {
				runtime.add(block);
			}
		});

		deps.runtime = new Map([...deps.runtime.entries(), ...blockDeps.runtime.entries()]);
		deps.parents = new Map(
			$C([...deps.parents.entries(), ...blockDeps.parents.entries()])
				.filter(([block]) => !runtime.has(block))
				.map()
		);

		deps.libs = new Set([...deps.libs, ...blockDeps.libs]);
	});

	return deps;
}

/**
 * Returns build config for entries
 * @returns {!Promise<!{entries, dependencies, commons}>}
 */
async function getBuildConfig() {
	const entries = await $C(vinyl.src(path.join(entry(), '*.js'), {read: false}))
		.async
		.to({})
		.reduce((res, el) => {
			const
				src = el.path,
				name = path.basename(src, '.js');

			let source;
			function getSource() {
				source = source || fs.readFileSync(src, 'utf-8');
				return source;
			}

			res[name] = {
				path: src,

				get source() {
					return getSource();
				},

				get parent() {
					return $C(getEntryParents(getSource())).one.get();
				},

				get parents() {
					return getEntryParents(getSource());
				},

				getRuntimeDependencies({cache} = {}) {
					return getEntryRuntimeDependencies(path.dirname(src), getSource(), {cache});
				}
			};

			return res;
		});

	function factory(entries) {
		function filter(cb) {
			return factory($C(entries).filter(cb).map());
		}

		return {
			entries,
			filter,
			getUnionEntryPoints({cache} = {}) {
				return getUnionEntryPoints(entries, {cache});
			}
		};
	}

	return factory(entries);
}

module.exports = {
	getCommonName,
	getBuildConfig
};
<commit_msg>:wrench:<commit_after>'use strict';

const
	$C = require('collection.js'),
	Sugar = require('sugar'),
	fs = require('fs-extra-promise'),
	vinyl = require('vinyl-fs'),
	path = require('path');

const
	Block = require('./block'),
	{entry, entryDependencies, lib, isNodeModule} = require('./resolve'),
	{blockName} = require('./validators');

const
	hasImport = /^import\s+(['"])(.*?)\1;?/,
	entriesDir = new RegExp(`^(?:${$C(entryDependencies).map(Sugar.RegExp.escape).join('|')})(?:[/\\\\]|$)`),
	insideEntry = /^\.\//,
	eol = /\r?\n|\r/;

/**
 * Returns a name for a common chunk by the specified id
 * @param {(string|number)} id
 */
function getCommonName(id) {
	return `common_${id}`;
}

/**
 * Returns a graph with dependencies and union entry points from the specified entries
 *
 * @param entries
 * @param {Map<string, !Block>=} [cache] - optional cache object with predefined blocks
 * @returns {!Promise<{dependencies: Object<string, !Set<string>>, entry: Object<string, !Map<string, !Block>>}>}
 */
async function getUnionEntryPoints(entries, {cache} = {}) {
	const
		packs = Object.create(null),
		weights = Object.create(null);

	await $C(entries).parallel().forEach(async (el) => {
		const
			deps = packs[path.basename(el.path, '.js')] = await getEntryRuntimeDependencies(path.dirname(el.path), el.source, {cache});

		const walk = (block) => {
			const
				name = block.name || block;

			if (name in weights) {
				const
					w = weights[name];

				w.i++;
				if (w.isParent) {
					w.isParent = deps.parents.has(name);
				}

			} else {
				weights[name] = {
					i: 0,
					name,
					isParent: deps.parents.has(name)
				};
			}
		};

		$C(deps.runtime).forEach(walk);
		$C(deps.libs).forEach(walk);
	});

	// Find common modules (weight > 1)

	const
		commonPacks = [];

	$C(weights).forEach((el, key) => {
		if (el.i > 1) {
			const pos = $C(entries).length() - el.i - 1;
			commonPacks[pos] = (commonPacks[pos] || new Map()).set(key, el);
		}
	});

	// Remove empty modules
	$C(commonPacks).remove((el) => !el);

	const
		dependencies = {};

	$C(packs).forEach((deps, name) => {
		const
			depList = new Set();

		$C(deps.runtime).forEach((el, block) => {
			const
				pos = $C(commonPacks).one.search((map) => map.has(block));

			if (pos !== null) {
				depList.add(pos);
				deps.runtime.delete(block);
			}
		});

		dependencies[name] = new Set([...depList].sort((a, b) => a - b).map((i) => getCommonName(i)));
	});

	const
		entry = {};

	$C(commonPacks).forEach((deps, i) => {
		entry[getCommonName(i)] = deps;
	});

	$C(packs).forEach((deps, name) => {
		entry[name] = $C(deps.runtime).to(new Map()).reduce((map, block, name) => map.set(name, {
			name,
			isParent: deps.parents.has(name)
		}));
	});

	return {dependencies, entry};
}

/**
 * Returns a list of imports from the specified entry file
 *
 * @param {string} dir - entry directory
 * @param {string} content - file content
 * @param {!Array} [arr]
 * @returns {!Array<string>}
 */
function getEntryImports(dir, content, arr = []) {
	$C(content.split(eol)).forEach((line) => {
		if (!hasImport.test(line)) {
			return;
		}

		const
			url = RegExp.$2,
			nodeModule = isNodeModule(url);

		if (nodeModule && entriesDir.test(url) || insideEntry.test(url)) {
			const
				d = nodeModule ? lib : dir;

			let
				f = path.join(d, `${url}.js`);

			if (!fs.existsSync(f)) {
				f = path.join(d, url, 'index.js');
			}

			getEntryImports(path.dirname(f), fs.readFileSync(f, 'utf-8'), arr);

		} else {
			arr.push(nodeModule ? url : path.join(dir, url));
		}
	});

	return arr;
}

/**
 * Returns a set of parent entries for the specified entry
 *
 * @param {string} content - file content
 * @returns {!Set<string>}
 */
function getEntryParents(content) {
	const
		parents = new Set(),
		clrfx = /\.\//;

	$C(content.split(eol)).forEach((line) => {
		if (!hasImport.test(line)) {
			return;
		}

		const
			url = RegExp.$2;

		if (isNodeModule(url) && entriesDir.test(url) || insideEntry.test(url)) {
			parents.add(url.replace(clrfx, ''));
		}
	});

	return parents;
}

/**
 * Returns a graph with dependencies for an entry file
 *
 * @param {string} dir - entry directory
 * @param {string} content - file content
 * @param {Map<string, !Block>=} [cache] - optional cache object with predefined blocks
 * @returns {!Promise<{runtime: !Map<string, !Block>, parents: !Map<string, !Block>, libs: !Set<string>}>}
 */
async function getEntryRuntimeDependencies(dir, content, {cache} = {}) {
	const deps = {
		runtime: new Map(),
		parents: new Map(),
		libs: new Set()
	};

	const
		runtime = new Set();

	await $C(getEntryImports(dir, content)).async.forEach(async (el) => {
		const
			name = path.basename(el, path.extname(el)),
			block = cache ? cache.get(name) : await Block.get(name);

		if (!blockName(name) || !block) {
			deps.runtime.set(el, el);
			return;
		}

		const
			blockDeps = await block.getRuntimeDependencies({cache});

		$C(blockDeps.runtime).forEach((obj, block) => {
			if (!blockDeps.parents.has(block)) {
				runtime.add(block);
			}
		});

		deps.runtime = new Map([...deps.runtime.entries(), ...blockDeps.runtime.entries()]);
		deps.parents = new Map(
			$C([...deps.parents.entries(), ...blockDeps.parents.entries()])
				.filter(([block]) => !runtime.has(block))
				.map()
		);

		deps.libs = new Set([...deps.libs, ...blockDeps.libs]);
	});

	return deps;
}

/**
 * Returns build config for entries
 * @returns {!Promise<!{entries, dependencies, commons}>}
 */
async function getBuildConfig() {
	const entries = await $C(vinyl.src(path.join(entry(), '*.js'), {read: false}))
		.async
		.to({})
		.reduce((res, el) => {
			const
				src = el.path,
				name = path.basename(src, '.js');

			let source;
			function getSource() {
				source = source || fs.readFileSync(src, 'utf-8');
				return source;
			}

			res[name] = {
				path: src,

				get source() {
					return getSource();
				},

				get parent() {
					return $C(getEntryParents(getSource())).one.get();
				},

				get parents() {
					return getEntryParents(getSource());
				},

				getRuntimeDependencies({cache} = {}) {
					return getEntryRuntimeDependencies(path.dirname(src), getSource(), {cache});
				}
			};

			return res;
		});

	function factory(entries) {
		function filter(cb) {
			return factory($C(entries).filter(cb).map());
		}

		return {
			entries,
			filter,
			getUnionEntryPoints({cache} = {}) {
				return getUnionEntryPoints(entries, {cache});
			}
		};
	}

	return factory(entries);
}

module.exports = {
	getCommonName,
	getBuildConfig
};
<|endoftext|>


<commit_before>var api = require("./api");
var db = require("./db");
var util = require("./util");
var time = require("./time");
var async = require("async");

/*
 * built-in event emitter for own events.
 */
var EventEmitter = require("events").EventEmitter,
	emitter = new EventEmitter();

/*
 * default function for handling events.
 */
function default_ev_handler(msg, rcpts){

	if( !(rcpts = rcpts || util.clone(msg.ev_ctx.config.rcpts) ||util.clone(msg.ev_ctx.doc.rcpts)) )//rcpts parameter takes precedence over document rcpts
		return;

	var	idx = rcpts.indexOf(msg.ev_ctx.params.uid);

	if( idx != -1)
		rcpts.splice(idx,1);

	//save event into journal and push event to subscribers.
	var ev_msg = {ev_type: msg.ev_type, ev_tstamp: msg.ev_tstamp, ev_data: msg.ev_ctx.payload};

	db.save("events",ev_msg,function(err){

		if(err)
			throw new Error(err);

		delete ev_msg._id;
		async.forEach( rcpts, function(rcpt, next){

				push( rcpt, ev_msg );
				next();
		});
	});

}

api.on("ev_api_create", function( msg, rcpts ){

	default_ev_handler( msg, rcpts );

});

/*
 * handler for ev_api_dispose event type.
 */
api.on("ev_api_dispose", function( msg, rcpts ){

	default_ev_handler( msg, rcpts );
});

/*
 * handler for ev_api_join event type.
 */
api.on("ev_api_join", function( msg, rcpts ){

	default_ev_handler( msg, rcpts );
});

/*
 * handler for ev_api_unjoin event type
 */
api.on("ev_api_unjoin", function( msg, rcpts ){

	default_ev_handler( msg, rcpts );
});

/*
 * handler for ev_api_rem event type.
 */
api.on("ev_api_remove", function( msg, rcpts ){

	default_ev_handler( msg, rcpts );
});

/*
 * handler for the ev_api_set event type.
 */
api.on("ev_api_set",function( msg, rcpts ){

	default_ev_handler( msg, rcpts );
});

/*
 * handler for the ev_api_push event type.
 */
api.on("ev_api_push",function( msg, rcpts ){

	default_ev_handler( msg, rcpts );
});

/*
 * handler for the ev_api_pop event type.
 */
api.on("ev_api_pop",function( msg, rcpts ){

	default_ev_handler( msg, rcpts );
});

/*
 * handler for the ev_api_pull event type.
 */
api.on("ev_api_shift",function( msg, rcpts ){

	default_ev_handler( msg, rcpts );
});

/*
 * subscribers list
 */
var subscribers = {};

var get_sub = exports.get_subscription = function ( uid ){

	if( uid == undefined )
		throw new Error("Invoke with a uid");

	return subscribers["" + uid];
}

/*
 * remove uid from the subscribers list.
 */
var rem_subscription = exports.rem_subscription  = function( uid ){

	if(uid == undefined)
		throw new Error("Must invoke with a uid");

	delete subscribers["" + uid];

}

/*
 * Add uid to the subscribers list.
 */
var add_subscription = exports.add_subscription = function( uid, http_resp ){

	subscribers["" + uid] = {http:http_resp};
}

/*
 * is subscriber uid connected?
 */
var is_subscribed = exports.is_subscribed = function( uid ){

	if(uid == undefined)
		throw new Error("Must invoke with a uid");

	return subscribers["" + uid] != undefined;
}

/*
 * Send message to subscriber.
 */
function push( rcpt, msg ){

	if( is_subscribed(rcpt) ){

		var http_resp = subscribers["" + rcpt].http;
		var msg_str = 	JSON.stringify(msg);
		http_resp.write( msg_str + "\n" );
		emit("ev_eq_push", {msg_str:msg_str}, rcpt );
	}

}

/*
 * Register listeners to the eq events using a delegation pattern for EventEmitter.
 *
 */
exports.on = function( ev_type, ret_handler ){

	emitter.on(ev_type, function( params, rcpts ){

		ret_handler( {
						ev_type:ev_type,
						ev_tstamp:time.now(),
						ev_data:params
					 }, rcpts );
	});
}

/*
 * emit an event as it was generated inside the eq.
 */
var emit = exports.emit =  function( ev_type, params, rcpts ){

	emitter.emit(ev_type, params, rcpts );
}

/*
 * Make evqueue listen for api events.
 */
exports.api = {listen : function( ev_type ){

		api.on(ev_type,function( msg, rcpts ){

			default_ev_handler( msg, rcpts );

		});
	}
}

exports.remote = {};

/*
 *
 * Remote procedure to subscribe to the event queue. The socket remains open
 * after this call to send events to the subscribed client.
 */
exports.remote.subscribe = function( http_resp, params ){

	if( !params || params.uid == undefined ){

		http_resp.end();
		return;
	}

	add_subscription(params.uid, http_resp);

	http_resp.on("close", function(){

		console.log('Client closed: [%d]', params.uid);
		rem_subscription(params.uid);
	});

	http_resp.connection.on("error", function(ex){

		http_resp.connection.destroy();

	});

	if( params.tstamp ){//create user feed

		var feed = [];
		db.criteria("users",{uid: params.uid},{},function(err,docs){

			async.forEach(docs[0].wids,function(wid,next){

				db.criteria("events",{"ev_data.wid":wid, ev_tstamp:{$gt:params.tstamp}},{ev_tstamp:1},function(err,events){

					feed = feed.concat(events);
					next(err);

				});
			},function(err){
				if(err){

				}else{
					//sort feed and push it back
					feed.sort(function(a,b){return a.ev_tstamp - b.ev_tstamp});

					for(i in feed){

						push(params.uid, feed[i]);
					}
				}
			});

		});
		/*
		db.criteria( "events", {ev_rcpt:params.uid, "ev_msg.ev_tstamp":{$gt:params.tstamp}}, {"ev_msg.ev_tstamp":1},function(err,results){

			for( i in results ){

				push( params.uid, results[i].ev_msg );
			}
		});
		*/
	}

}

<commit_msg>evqueue only saves events to db, the rest of the functionality is kept in evmngr.<commit_after>var db = require("./db");

/*
 * Store event into event catalog.
 */
exports.save  = function(ev_msg, ret_handler){

	db.save("events", ev_msg,function(err){

		delete ev_msg._id;
		ret_handler(err);
	});

}

<|endoftext|>


<commit_before>//
//   Copyright 2009-2014 Ilkka Oksanen <iao@iki.fi>
//
//   Licensed under the Apache License, Version 2.0 (the "License");
//   you may not use this file except in compliance with the License.
//   You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
//   Unless required by applicable law or agreed to in writing,
//   software distributed under the License is distributed on an "AS
//   IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
//   express or implied.  See the License for the specific language
//   governing permissions and limitations under the License.
//

'use strict';

/* globals URI, moment, emojify */

import Ember from 'ember';

export default Ember.Object.extend({
    store: null,

    gid: 0,
    body: null,
    cat: null,
    ts: null,
    userId: null,
    window: null,
    hideImages: false,

    mentionedRegEx: Ember.computed.alias('window.userNickHighlightRegex'),

    ircMotd: false,

    nick: Ember.computed('userId', 'window', function() {
        return this.get('store.users').getNick(this.get('userId'), this.get('window.network'));
    }),

    decoratedCat: Ember.computed('cat', 'nick', 'body', 'mentionedRegEx', function() {
        let cat = this.get('cat');
        let body = this.get('body');
        let userId = this.get('userId');
        let mentionedRegEx = this.get('mentionedRegEx');

        // TBD: Network === flowdock check is missing
        if (this.get('body').indexOf('Show in Flowdock:') > -1) {
            return 'flowdock-ignore';
        } else if (mentionedRegEx && mentionedRegEx.test(body) && cat === 'msg') {
            return 'mention';
        } else if (userId == this.get('store.userId')) {
            return 'mymsg';
        }

        return this.get('nick') === 'Flowdock' ? 'flowdock' : cat;
    }),

    decoratedTs: Ember.computed('ts', function() {
        return moment.unix(this.get('ts')).format('HH:mm');
    }),

    channelAction: Ember.computed('cat', function() {
        let category = this.get('cat');
        let nick = this.get('nick');
        let groupName = this.get('window.name');
        let body = this.get('body');

        switch (category) {
            case 'join':
                return `${nick} has joined ${groupName}.`;
            case 'part':
                return `${nick} has left ${groupName}. ${body}`;
            case 'quit':
                return `${nick} has quit irc. Reason: ${body}`;
            case 'kick':
                return `${nick} was kicked from ${groupName}. Reason: ${body}`;
        }
    }),

    bodyParts: Ember.computed('body', function() {
        let result = Ember.A([]);
        let pos = 0;
        let imgSuffixes = [ 'png', 'jpg', 'jpeg', 'gif' ];
        let body = this.get('body');
        let network = this.get('network');
        let cat = this.get('cat');

        // TBD: Do parsing in two stages, first images and rest. Then rest in detail.
        URI.withinString(body, function(url, start, end, source) {
            let urlObj = new URI(url);
            let visibleLink;
            let media = false;
            let type = '';
            let domain = urlObj.domain();

            if (start !== pos) {
                this._parseText(result, source.substring(pos, start), network, cat);
            }

            if (imgSuffixes.indexOf(urlObj.suffix().toLowerCase()) !== -1) {
                visibleLink = urlObj.filename();
                media = true;
                type = 'image';
            } else if ((domain === 'youtube.com' && urlObj.search(true).v) ||
                domain === 'youtu.be') {
                visibleLink = urlObj.toString();
                media = true;
                type = 'youtubelink';
            } else {
                visibleLink = urlObj.readable();
            }

            if (urlObj.protocol() === '') {
                urlObj.protocol('http');
            }

            this._pushPart(result, {
                link: true,
                text: visibleLink,
                url: urlObj.toString(),
                media: media,
                type: type,
                source: ''
            });

            pos = end;

            return url;
        }.bind(this));

        if (body && body.length !== pos) {
            this._parseText(result, body.substring(pos), network, cat);
        }

        return result;
    }),

    hasMedia: Ember.computed('bodyParts', function() {
        return this.get('bodyParts').isAny('media', true);
    }),

    hasImages: Ember.computed('bodyParts', function() {
        return this.get('bodyParts').isAny('type', 'image');
    }),

    hasYoutubeVideo: Ember.computed('bodyParts', function() {
        return this.get('bodyParts').isAny('type', 'youtubelink');
    }),

    videoId: Ember.computed('bodyParts', function() {
        let video = this.get('bodyParts').findBy('type', 'youtubelink');

        if (video) {
            let urlObj = new URI(video.url);
            // Format is https://www.youtube.com/watch?v=0P7O69GuCII or https://youtu.be/0P7O69GuCII
            return urlObj.search(true).v || urlObj.pathname().substring(1).split('/')[0];
        } else {
            return null;
        }
    }),

    images: Ember.computed('bodyParts', function() {
        return this.get('bodyParts').filterBy('type', 'image');
    }),

    _pushPart(array, params) {
        array.pushObject(Ember.Object.create(params));
    },

    _parseText(result, text, network, cat) {
        if (network === 'Flowdock') {
            text = text.replace(/^\[(.*?)\] << (.*)/, function(match, p1, p2) {
                return '[' + p1.substring(0, 9) + '] ' + p2;
            });
        }

        if (cat === 'banner') {
            text = text.replace(/ /g, 'ˑ'); // Preserve whitespace trick.
        }

        // Emoji and @mention separation, uses capturing parentheses to save the separators also
        let parts = text.split(/(:\S+:|(?:^| )@\S+ )/);

        parts.forEach(function(part) {
            let emojiMatch = /^:(.+):$/.exec(part);
            let isEmoji = emojiMatch && emojify.emojiNames.indexOf(emojiMatch[1]) > -1;

            if (part === '\n') {
                this._pushPart(result, {
                    linebreak: true
                });
                return;
            }

            if (isEmoji) {
                this._pushPart(result, {
                    link: false,
                    text: part,
                    emoji: emojiMatch[1]
                });
                return;
            }

            let mentionMatch = /^\s?(@\S+) $/.exec(part);

            if (mentionMatch) {
                this._pushPart(result, {
                    link: false,
                    mention: true,
                    text: mentionMatch[1]
                });
                return;
            }

            // Plain text
            this._pushPart(result, {
                link: false,
                text: part
            });
        }.bind(this));
    }
});
<commit_msg>Split message at newline<commit_after>//
//   Copyright 2009-2014 Ilkka Oksanen <iao@iki.fi>
//
//   Licensed under the Apache License, Version 2.0 (the "License");
//   you may not use this file except in compliance with the License.
//   You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
//   Unless required by applicable law or agreed to in writing,
//   software distributed under the License is distributed on an "AS
//   IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
//   express or implied.  See the License for the specific language
//   governing permissions and limitations under the License.
//

'use strict';

/* globals URI, moment, emojify */

import Ember from 'ember';

export default Ember.Object.extend({
    store: null,

    gid: 0,
    body: null,
    cat: null,
    ts: null,
    userId: null,
    window: null,
    hideImages: false,

    mentionedRegEx: Ember.computed.alias('window.userNickHighlightRegex'),

    ircMotd: false,

    nick: Ember.computed('userId', 'window', function() {
        return this.get('store.users').getNick(this.get('userId'), this.get('window.network'));
    }),

    decoratedCat: Ember.computed('cat', 'nick', 'body', 'mentionedRegEx', function() {
        let cat = this.get('cat');
        let body = this.get('body');
        let userId = this.get('userId');
        let mentionedRegEx = this.get('mentionedRegEx');

        // TBD: Network === flowdock check is missing
        if (this.get('body').indexOf('Show in Flowdock:') > -1) {
            return 'flowdock-ignore';
        } else if (mentionedRegEx && mentionedRegEx.test(body) && cat === 'msg') {
            return 'mention';
        } else if (userId == this.get('store.userId')) {
            return 'mymsg';
        }

        return this.get('nick') === 'Flowdock' ? 'flowdock' : cat;
    }),

    decoratedTs: Ember.computed('ts', function() {
        return moment.unix(this.get('ts')).format('HH:mm');
    }),

    channelAction: Ember.computed('cat', function() {
        let category = this.get('cat');
        let nick = this.get('nick');
        let groupName = this.get('window.name');
        let body = this.get('body');

        switch (category) {
            case 'join':
                return `${nick} has joined ${groupName}.`;
            case 'part':
                return `${nick} has left ${groupName}. ${body}`;
            case 'quit':
                return `${nick} has quit irc. Reason: ${body}`;
            case 'kick':
                return `${nick} was kicked from ${groupName}. Reason: ${body}`;
        }
    }),

    bodyParts: Ember.computed('body', function() {
        let result = Ember.A([]);
        let pos = 0;
        let imgSuffixes = [ 'png', 'jpg', 'jpeg', 'gif' ];
        let body = this.get('body');
        let network = this.get('network');
        let cat = this.get('cat');

        // TBD: Do parsing in two stages, first images and rest. Then rest in detail.
        URI.withinString(body, function(url, start, end, source) {
            let urlObj = new URI(url);
            let visibleLink;
            let media = false;
            let type = '';
            let domain = urlObj.domain();

            if (start !== pos) {
                this._parseText(result, source.substring(pos, start), network, cat);
            }

            if (imgSuffixes.indexOf(urlObj.suffix().toLowerCase()) !== -1) {
                visibleLink = urlObj.filename();
                media = true;
                type = 'image';
            } else if ((domain === 'youtube.com' && urlObj.search(true).v) ||
                domain === 'youtu.be') {
                visibleLink = urlObj.toString();
                media = true;
                type = 'youtubelink';
            } else {
                visibleLink = urlObj.readable();
            }

            if (urlObj.protocol() === '') {
                urlObj.protocol('http');
            }

            this._pushPart(result, {
                link: true,
                text: visibleLink,
                url: urlObj.toString(),
                media: media,
                type: type,
                source: ''
            });

            pos = end;

            return url;
        }.bind(this));

        if (body && body.length !== pos) {
            this._parseText(result, body.substring(pos), network, cat);
        }

        return result;
    }),

    hasMedia: Ember.computed('bodyParts', function() {
        return this.get('bodyParts').isAny('media', true);
    }),

    hasImages: Ember.computed('bodyParts', function() {
        return this.get('bodyParts').isAny('type', 'image');
    }),

    hasYoutubeVideo: Ember.computed('bodyParts', function() {
        return this.get('bodyParts').isAny('type', 'youtubelink');
    }),

    videoId: Ember.computed('bodyParts', function() {
        let video = this.get('bodyParts').findBy('type', 'youtubelink');

        if (video) {
            let urlObj = new URI(video.url);
            // Format is https://www.youtube.com/watch?v=0P7O69GuCII or https://youtu.be/0P7O69GuCII
            return urlObj.search(true).v || urlObj.pathname().substring(1).split('/')[0];
        } else {
            return null;
        }
    }),

    images: Ember.computed('bodyParts', function() {
        return this.get('bodyParts').filterBy('type', 'image');
    }),

    _pushPart(array, params) {
        array.pushObject(Ember.Object.create(params));
    },

    _parseText(result, text, network, cat) {
        if (network === 'Flowdock') {
            text = text.replace(/^\[(.*?)\] << (.*)/, function(match, p1, p2) {
                return '[' + p1.substring(0, 9) + '] ' + p2;
            });
        }

        if (cat === 'banner') {
            text = text.replace(/ /g, 'ˑ'); // Preserve whitespace trick.
        }

        // Emoji and @mention separation, uses capturing parentheses to save the separators also
        let parts = text.split(/(:\S+:|(?:^| )@\S+ |\n)/);

        parts.forEach(function(part) {
            let emojiMatch = /^:(.+):$/.exec(part);
            let isEmoji = emojiMatch && emojify.emojiNames.indexOf(emojiMatch[1]) > -1;

            if (part === '\n') {
                this._pushPart(result, {
                    linebreak: true
                });
                return;
            }

            if (isEmoji) {
                this._pushPart(result, {
                    link: false,
                    text: part,
                    emoji: emojiMatch[1]
                });
                return;
            }

            let mentionMatch = /^\s?(@\S+) $/.exec(part);

            if (mentionMatch) {
                this._pushPart(result, {
                    link: false,
                    mention: true,
                    text: mentionMatch[1]
                });
                return;
            }

            // Plain text
            this._pushPart(result, {
                link: false,
                text: part
            });
        }.bind(this));
    }
});
<|endoftext|>


<commit_before>import moment from "moment";
import debug from "debug";
import Mustache from "mustache";
import { axios } from "@/services/axios";
import {
  zipObject,
  isEmpty,
  map,
  filter,
  includes,
  union,
  uniq,
  has,
  identity,
  extend,
  each,
  some,
  clone,
  find,
} from "lodash";
import location from "@/services/location";

import { Parameter, createParameter } from "./parameters";
import { currentUser } from "./auth";
import QueryResult from "./query-result";

Mustache.escape = identity; // do not html-escape values

const logger = debug("redash:services:query");

function collectParams(parts) {
  let parameters = [];

  parts.forEach(part => {
    if (part[0] === "name" || part[0] === "&") {
      parameters.push(part[1].split(".")[0]);
    } else if (part[0] === "#") {
      parameters = union(parameters, collectParams(part[4]));
    }
  });

  return parameters;
}

export class Query {
  constructor(query) {
    extend(this, query);
  }

  isNew() {
    return this.id === undefined;
  }

  hasDailySchedule() {
    return this.schedule && this.schedule.match(/\d\d:\d\d/) !== null;
  }

  scheduleInLocalTime() {
    const parts = this.schedule.split(":");
    return moment
      .utc()
      .hour(parts[0])
      .minute(parts[1])
      .local()
      .format("HH:mm");
  }

  hasResult() {
    return !!(this.latest_query_data || this.latest_query_data_id);
  }

  paramsRequired() {
    return this.getParameters().isRequired();
  }

  hasParameters() {
    return this.getParametersDefs().length > 0;
  }

  prepareQueryResultExecution(execute, maxAge) {
    const parameters = this.getParameters();
    const missingParams = parameters.getMissing();

    if (missingParams.length > 0) {
      let paramsWord = "parameter";
      let valuesWord = "value";
      if (missingParams.length > 1) {
        paramsWord = "parameters";
        valuesWord = "values";
      }

      return new QueryResult({
        job: {
          error: `missing ${valuesWord} for ${missingParams.join(", ")} ${paramsWord}.`,
          status: 4,
        },
      });
    }

    if (parameters.isRequired()) {
      // Need to clear latest results, to make sure we don't use results for different params.
      this.latest_query_data = null;
      this.latest_query_data_id = null;
    }

    if (this.latest_query_data && maxAge !== 0) {
      if (!this.queryResult) {
        this.queryResult = new QueryResult({
          query_result: this.latest_query_data,
        });
      }
    } else if (this.latest_query_data_id && maxAge !== 0) {
      if (!this.queryResult) {
        this.queryResult = QueryResult.getById(this.id, this.latest_query_data_id);
      }
    } else {
      this.queryResult = execute();
    }

    return this.queryResult;
  }

  getQueryResult(maxAge) {
    const execute = () => QueryResult.getByQueryId(this.id, this.getParameters().getExecutionValues(), maxAge);
    return this.prepareQueryResultExecution(execute, maxAge);
  }

  getQueryResultByText(maxAge, selectedQueryText) {
    const queryText = selectedQueryText || this.query;
    if (!queryText) {
      return new QueryResultError("Can't execute empty query.");
    }

    const parameters = this.getParameters().getExecutionValues({ joinListValues: true });
    const execute = () => QueryResult.get(this.data_source_id, queryText, parameters, maxAge, this.id);
    return this.prepareQueryResultExecution(execute, maxAge);
  }

  getUrl(source, hash) {
    let url = `queries/${this.id}`;

    if (source) {
      url += "/source";
    }

    let params = {};
    if (this.getParameters().isRequired()) {
      this.getParametersDefs().forEach(param => {
        extend(params, param.toUrlParams());
      });
    }
    Object.keys(params).forEach(key => params[key] == null && delete params[key]);
    params = map(params, (value, name) => `${encodeURIComponent(name)}=${encodeURIComponent(value)}`).join("&");

    if (params !== "") {
      url += `?${params}`;
    }

    if (hash) {
      url += `#${hash}`;
    }

    return url;
  }

  getQueryResultPromise() {
    return this.getQueryResult().toPromise();
  }

  getParameters() {
    if (!this.$parameters) {
      this.$parameters = new Parameters(this, location.search);
    }

    return this.$parameters;
  }

  getParametersDefs(update = true) {
    return this.getParameters().get(update);
  }

  favorite() {
    return Query.favorite(this);
  }

  unfavorite() {
    return Query.unfavorite(this);
  }

  clone() {
    const newQuery = clone(this);
    newQuery.$parameters = null;
    newQuery.getParameters();
    return newQuery;
  }
}

class Parameters {
  constructor(query, queryString) {
    this.query = query;
    this.updateParameters();
    this.initFromQueryString(queryString);
  }

  parseQuery() {
    const fallback = () => map(this.query.options.parameters, i => i.name);

    let parameters = [];
    if (this.query.query !== undefined) {
      try {
        const parts = Mustache.parse(this.query.query);
        parameters = uniq(collectParams(parts));
      } catch (e) {
        logger("Failed parsing parameters: ", e);
        // Return current parameters so we don't reset the list
        parameters = fallback();
      }
    } else {
      parameters = fallback();
    }

    return parameters;
  }

  updateParameters(update) {
    if (this.query.query === this.cachedQueryText) {
      const parameters = this.query.options.parameters;
      const hasUnprocessedParameters = find(parameters, p => !(p instanceof Parameter));
      if (hasUnprocessedParameters) {
        this.query.options.parameters = map(parameters, p =>
          p instanceof Parameter ? p : createParameter(p, this.query.id)
        );
      }
      return;
    }

    this.cachedQueryText = this.query.query;
    const parameterNames = update ? this.parseQuery() : map(this.query.options.parameters, p => p.name);

    this.query.options.parameters = this.query.options.parameters || [];

    const parametersMap = {};
    this.query.options.parameters.forEach(param => {
      parametersMap[param.name] = param;
    });

    parameterNames.forEach(param => {
      if (!has(parametersMap, param)) {
        this.query.options.parameters.push(
          createParameter({
            title: param,
            name: param,
            type: "text",
            value: null,
            global: false,
          })
        );
      }
    });

    const parameterExists = p => includes(parameterNames, p.name);
    const parameters = this.query.options.parameters;
    this.query.options.parameters = parameters
      .filter(parameterExists)
      .map(p => (p instanceof Parameter ? p : createParameter(p, this.query.id)));
  }

  initFromQueryString(query) {
    this.get().forEach(param => {
      param.fromUrlParams(query);
    });
  }

  get(update = true) {
    this.updateParameters(update);
    return this.query.options.parameters;
  }

  add(parameterDef) {
    this.query.options.parameters = this.query.options.parameters.filter(p => p.name !== parameterDef.name);
    const param = createParameter(parameterDef);
    this.query.options.parameters.push(param);
    return param;
  }

  getMissing() {
    return map(
      filter(this.get(), p => p.isEmpty),
      i => i.title
    );
  }

  isRequired() {
    return !isEmpty(this.get());
  }

  getExecutionValues(extra = {}) {
    const params = this.get();
    return zipObject(
      map(params, i => i.name),
      map(params, i => i.getExecutionValue(extra))
    );
  }

  hasPendingValues() {
    return some(this.get(), p => p.hasPendingValue);
  }

  applyPendingValues() {
    each(this.get(), p => p.applyPendingValue());
  }

  toUrlParams() {
    if (this.get().length === 0) {
      return "";
    }

    const params = Object.assign(...this.get().map(p => p.toUrlParams()));
    Object.keys(params).forEach(key => params[key] == null && delete params[key]);
    return Object.keys(params)
      .map(k => `${encodeURIComponent(k)}=${encodeURIComponent(params[k])}`)
      .join("&");
  }
}

export class QueryResultError {
  constructor(errorMessage) {
    this.errorMessage = errorMessage;
    this.updatedAt = moment.utc();
  }

  getUpdatedAt() {
    return this.updatedAt;
  }

  getError() {
    return this.errorMessage;
  }

  toPromise() {
    return Promise.reject(this);
  }

  // eslint-disable-next-line class-methods-use-this
  getStatus() {
    return "failed";
  }

  // eslint-disable-next-line class-methods-use-this
  getData() {
    return null;
  }

  // eslint-disable-next-line class-methods-use-this
  getLog() {
    return null;
  }
}

const getQuery = query => new Query(query);
const saveOrCreateUrl = data => (data.id ? `api/queries/${data.id}` : "api/queries");
const mapResults = data => ({ ...data, results: map(data.results, getQuery) });

const QueryService = {
  query: params => axios.get("api/queries", { params }).then(mapResults),
  get: data => axios.get(`api/queries/${data.id}`, data).then(getQuery),
  save: data => axios.post(saveOrCreateUrl(data), data).then(getQuery),
  delete: data => axios.delete(`api/queries/${data.id}`),
  recent: params => axios.get(`api/queries/recent`, { params }).then(data => map(data, getQuery)),
  archive: params => axios.get(`api/queries/archive`, { params }).then(mapResults),
  myQueries: params => axios.get("api/queries/my", { params }).then(mapResults),
  fork: ({ id }) => axios.post(`api/queries/${id}/fork`, { id }).then(getQuery),
  resultById: data => axios.get(`api/queries/${data.id}/results.json`),
  asDropdown: data => axios.get(`api/queries/${data.id}/dropdown`),
  associatedDropdown: ({ queryId, dropdownQueryId }) =>
    axios.get(`api/queries/${queryId}/dropdowns/${dropdownQueryId}`),
  favorites: params => axios.get("api/queries/favorites", { params }).then(mapResults),
  favorite: data => axios.post(`api/queries/${data.id}/favorite`),
  unfavorite: data => axios.delete(`api/queries/${data.id}/favorite`),
};

QueryService.newQuery = function newQuery() {
  return new Query({
    query: "",
    name: "New Query",
    schedule: null,
    user: currentUser,
    options: {},
    tags: [],
    can_edit: true,
  });
};

QueryService.format = function formatQuery(syntax, query) {
  if (syntax === "json") {
    try {
      const formatted = JSON.stringify(JSON.parse(query), " ", 4);
      return Promise.resolve(formatted);
    } catch (err) {
      return Promise.reject(String(err));
    }
  } else if (syntax === "sql") {
    return axios.post("api/queries/format", { query }).then(data => data.query);
  } else {
    return Promise.reject("Query formatting is not supported for your data source syntax.");
  }
};

extend(Query, QueryService);
<commit_msg>Make sure queries have options and parameters (#4952)<commit_after>import moment from "moment";
import debug from "debug";
import Mustache from "mustache";
import { axios } from "@/services/axios";
import {
  zipObject,
  isEmpty,
  isArray,
  map,
  filter,
  includes,
  union,
  uniq,
  has,
  identity,
  extend,
  each,
  some,
  clone,
  find,
} from "lodash";
import location from "@/services/location";

import { Parameter, createParameter } from "./parameters";
import { currentUser } from "./auth";
import QueryResult from "./query-result";

Mustache.escape = identity; // do not html-escape values

const logger = debug("redash:services:query");

function collectParams(parts) {
  let parameters = [];

  parts.forEach(part => {
    if (part[0] === "name" || part[0] === "&") {
      parameters.push(part[1].split(".")[0]);
    } else if (part[0] === "#") {
      parameters = union(parameters, collectParams(part[4]));
    }
  });

  return parameters;
}

export class Query {
  constructor(query) {
    extend(this, query);

    if (!has(this, "options")) {
      this.options = {};
    }

    if (!isArray(this.options.parameters)) {
      this.options.parameters = [];
    }
  }

  isNew() {
    return this.id === undefined;
  }

  hasDailySchedule() {
    return this.schedule && this.schedule.match(/\d\d:\d\d/) !== null;
  }

  scheduleInLocalTime() {
    const parts = this.schedule.split(":");
    return moment
      .utc()
      .hour(parts[0])
      .minute(parts[1])
      .local()
      .format("HH:mm");
  }

  hasResult() {
    return !!(this.latest_query_data || this.latest_query_data_id);
  }

  paramsRequired() {
    return this.getParameters().isRequired();
  }

  hasParameters() {
    return this.getParametersDefs().length > 0;
  }

  prepareQueryResultExecution(execute, maxAge) {
    const parameters = this.getParameters();
    const missingParams = parameters.getMissing();

    if (missingParams.length > 0) {
      let paramsWord = "parameter";
      let valuesWord = "value";
      if (missingParams.length > 1) {
        paramsWord = "parameters";
        valuesWord = "values";
      }

      return new QueryResult({
        job: {
          error: `missing ${valuesWord} for ${missingParams.join(", ")} ${paramsWord}.`,
          status: 4,
        },
      });
    }

    if (parameters.isRequired()) {
      // Need to clear latest results, to make sure we don't use results for different params.
      this.latest_query_data = null;
      this.latest_query_data_id = null;
    }

    if (this.latest_query_data && maxAge !== 0) {
      if (!this.queryResult) {
        this.queryResult = new QueryResult({
          query_result: this.latest_query_data,
        });
      }
    } else if (this.latest_query_data_id && maxAge !== 0) {
      if (!this.queryResult) {
        this.queryResult = QueryResult.getById(this.id, this.latest_query_data_id);
      }
    } else {
      this.queryResult = execute();
    }

    return this.queryResult;
  }

  getQueryResult(maxAge) {
    const execute = () => QueryResult.getByQueryId(this.id, this.getParameters().getExecutionValues(), maxAge);
    return this.prepareQueryResultExecution(execute, maxAge);
  }

  getQueryResultByText(maxAge, selectedQueryText) {
    const queryText = selectedQueryText || this.query;
    if (!queryText) {
      return new QueryResultError("Can't execute empty query.");
    }

    const parameters = this.getParameters().getExecutionValues({ joinListValues: true });
    const execute = () => QueryResult.get(this.data_source_id, queryText, parameters, maxAge, this.id);
    return this.prepareQueryResultExecution(execute, maxAge);
  }

  getUrl(source, hash) {
    let url = `queries/${this.id}`;

    if (source) {
      url += "/source";
    }

    let params = {};
    if (this.getParameters().isRequired()) {
      this.getParametersDefs().forEach(param => {
        extend(params, param.toUrlParams());
      });
    }
    Object.keys(params).forEach(key => params[key] == null && delete params[key]);
    params = map(params, (value, name) => `${encodeURIComponent(name)}=${encodeURIComponent(value)}`).join("&");

    if (params !== "") {
      url += `?${params}`;
    }

    if (hash) {
      url += `#${hash}`;
    }

    return url;
  }

  getQueryResultPromise() {
    return this.getQueryResult().toPromise();
  }

  getParameters() {
    if (!this.$parameters) {
      this.$parameters = new Parameters(this, location.search);
    }

    return this.$parameters;
  }

  getParametersDefs(update = true) {
    return this.getParameters().get(update);
  }

  favorite() {
    return Query.favorite(this);
  }

  unfavorite() {
    return Query.unfavorite(this);
  }

  clone() {
    const newQuery = clone(this);
    newQuery.$parameters = null;
    newQuery.getParameters();
    return newQuery;
  }
}

class Parameters {
  constructor(query, queryString) {
    this.query = query;
    this.updateParameters();
    this.initFromQueryString(queryString);
  }

  parseQuery() {
    const fallback = () => map(this.query.options.parameters, i => i.name);

    let parameters = [];
    if (this.query.query !== undefined) {
      try {
        const parts = Mustache.parse(this.query.query);
        parameters = uniq(collectParams(parts));
      } catch (e) {
        logger("Failed parsing parameters: ", e);
        // Return current parameters so we don't reset the list
        parameters = fallback();
      }
    } else {
      parameters = fallback();
    }

    return parameters;
  }

  updateParameters(update) {
    if (this.query.query === this.cachedQueryText) {
      const parameters = this.query.options.parameters;
      const hasUnprocessedParameters = find(parameters, p => !(p instanceof Parameter));
      if (hasUnprocessedParameters) {
        this.query.options.parameters = map(parameters, p =>
          p instanceof Parameter ? p : createParameter(p, this.query.id)
        );
      }
      return;
    }

    this.cachedQueryText = this.query.query;
    const parameterNames = update ? this.parseQuery() : map(this.query.options.parameters, p => p.name);

    this.query.options.parameters = this.query.options.parameters || [];

    const parametersMap = {};
    this.query.options.parameters.forEach(param => {
      parametersMap[param.name] = param;
    });

    parameterNames.forEach(param => {
      if (!has(parametersMap, param)) {
        this.query.options.parameters.push(
          createParameter({
            title: param,
            name: param,
            type: "text",
            value: null,
            global: false,
          })
        );
      }
    });

    const parameterExists = p => includes(parameterNames, p.name);
    const parameters = this.query.options.parameters;
    this.query.options.parameters = parameters
      .filter(parameterExists)
      .map(p => (p instanceof Parameter ? p : createParameter(p, this.query.id)));
  }

  initFromQueryString(query) {
    this.get().forEach(param => {
      param.fromUrlParams(query);
    });
  }

  get(update = true) {
    this.updateParameters(update);
    return this.query.options.parameters;
  }

  add(parameterDef) {
    this.query.options.parameters = this.query.options.parameters.filter(p => p.name !== parameterDef.name);
    const param = createParameter(parameterDef);
    this.query.options.parameters.push(param);
    return param;
  }

  getMissing() {
    return map(
      filter(this.get(), p => p.isEmpty),
      i => i.title
    );
  }

  isRequired() {
    return !isEmpty(this.get());
  }

  getExecutionValues(extra = {}) {
    const params = this.get();
    return zipObject(
      map(params, i => i.name),
      map(params, i => i.getExecutionValue(extra))
    );
  }

  hasPendingValues() {
    return some(this.get(), p => p.hasPendingValue);
  }

  applyPendingValues() {
    each(this.get(), p => p.applyPendingValue());
  }

  toUrlParams() {
    if (this.get().length === 0) {
      return "";
    }

    const params = Object.assign(...this.get().map(p => p.toUrlParams()));
    Object.keys(params).forEach(key => params[key] == null && delete params[key]);
    return Object.keys(params)
      .map(k => `${encodeURIComponent(k)}=${encodeURIComponent(params[k])}`)
      .join("&");
  }
}

export class QueryResultError {
  constructor(errorMessage) {
    this.errorMessage = errorMessage;
    this.updatedAt = moment.utc();
  }

  getUpdatedAt() {
    return this.updatedAt;
  }

  getError() {
    return this.errorMessage;
  }

  toPromise() {
    return Promise.reject(this);
  }

  // eslint-disable-next-line class-methods-use-this
  getStatus() {
    return "failed";
  }

  // eslint-disable-next-line class-methods-use-this
  getData() {
    return null;
  }

  // eslint-disable-next-line class-methods-use-this
  getLog() {
    return null;
  }
}

const getQuery = query => new Query(query);
const saveOrCreateUrl = data => (data.id ? `api/queries/${data.id}` : "api/queries");
const mapResults = data => ({ ...data, results: map(data.results, getQuery) });

const QueryService = {
  query: params => axios.get("api/queries", { params }).then(mapResults),
  get: data => axios.get(`api/queries/${data.id}`, data).then(getQuery),
  save: data => axios.post(saveOrCreateUrl(data), data).then(getQuery),
  delete: data => axios.delete(`api/queries/${data.id}`),
  recent: params => axios.get(`api/queries/recent`, { params }).then(data => map(data, getQuery)),
  archive: params => axios.get(`api/queries/archive`, { params }).then(mapResults),
  myQueries: params => axios.get("api/queries/my", { params }).then(mapResults),
  fork: ({ id }) => axios.post(`api/queries/${id}/fork`, { id }).then(getQuery),
  resultById: data => axios.get(`api/queries/${data.id}/results.json`),
  asDropdown: data => axios.get(`api/queries/${data.id}/dropdown`),
  associatedDropdown: ({ queryId, dropdownQueryId }) =>
    axios.get(`api/queries/${queryId}/dropdowns/${dropdownQueryId}`),
  favorites: params => axios.get("api/queries/favorites", { params }).then(mapResults),
  favorite: data => axios.post(`api/queries/${data.id}/favorite`),
  unfavorite: data => axios.delete(`api/queries/${data.id}/favorite`),
};

QueryService.newQuery = function newQuery() {
  return new Query({
    query: "",
    name: "New Query",
    schedule: null,
    user: currentUser,
    options: {},
    tags: [],
    can_edit: true,
  });
};

QueryService.format = function formatQuery(syntax, query) {
  if (syntax === "json") {
    try {
      const formatted = JSON.stringify(JSON.parse(query), " ", 4);
      return Promise.resolve(formatted);
    } catch (err) {
      return Promise.reject(String(err));
    }
  } else if (syntax === "sql") {
    return axios.post("api/queries/format", { query }).then(data => data.query);
  } else {
    return Promise.reject("Query formatting is not supported for your data source syntax.");
  }
};

extend(Query, QueryService);
<|endoftext|>


<commit_before>import React from 'react';

import '../scss/_landing.scss';

const images = [
  'http://i.imgur.com/0uBzRJu.jpg', // chefs
  'http://i.imgur.com/KaHBbNU.jpg', // tomatoes
  'http://i.imgur.com/gBywUYs.jpg', // sandwich
  'http://i.imgur.com/3tPEYS9.jpg', // cookbook
  'http://i.imgur.com/U7yz4jB.jpg', // onion
  'http://i.imgur.com/P7HV4tq.jpg',
  'http://i.imgur.com/U7yPCPx.jpg',
  'http://i.imgur.com/MdsjkBd.jpg',
  'http://i.imgur.com/c8zNaLo.jpg',
  'http://i.imgur.com/YdLYJXS.jpg',
];

let randImage = Math.floor(Math.random() * images.length);
let heroImageStyle = {
  background: 'linear-gradient(rgba(50, 30, 45, 0.5), rgba(3, 79, 145, 0.5)), url(' + images[randImage] +')',
  // backgroundImage: '',
  backgroundSize: 'cover',
  backgroundRepeat: 'no-repeat',
  backgroundPosition: 'center center',
  // height: '500px',
  paddingTop: '100px',
};

const Landing = () => (

Cook. Iterate. Repeat.

Forkful helps you perfect your next meal by tracking different iterations of recipes.

            {/**/}

or, continue without signing in

Fork Recipes

"Fork" a recipe to modify it and make it your own.

Recipe History

See how recipes have changed over time.

Explore

Browse our most popular recipes and get cooking!

Made by Andrew, Dylan, Nemo, and Thomas.

);

export default Landing;
<commit_msg>Add links to GitHub for team members<commit_after>import React from 'react';

import '../scss/_landing.scss';

const images = [
  'http://i.imgur.com/0uBzRJu.jpg', // chefs
  'http://i.imgur.com/KaHBbNU.jpg', // tomatoes
  'http://i.imgur.com/gBywUYs.jpg', // sandwich
  'http://i.imgur.com/3tPEYS9.jpg', // cookbook
  'http://i.imgur.com/U7yz4jB.jpg', // onion
  'http://i.imgur.com/P7HV4tq.jpg',
  'http://i.imgur.com/U7yPCPx.jpg',
  'http://i.imgur.com/MdsjkBd.jpg',
  'http://i.imgur.com/c8zNaLo.jpg',
  'http://i.imgur.com/YdLYJXS.jpg',
];

let randImage = Math.floor(Math.random() * images.length);
let heroImageStyle = {
  background: 'linear-gradient(rgba(50, 30, 45, 0.5), rgba(3, 79, 145, 0.5)), url(' + images[randImage] +')',
  backgroundSize: 'cover',
  backgroundRepeat: 'no-repeat',
  backgroundPosition: 'center center',
  paddingTop: '100px',
};

const Landing = () => (

Cook. Iterate. Repeat.

Forkful helps you perfect your next meal by tracking different iterations of recipes.

            {/**/}

or, continue without signing in

Fork Recipes

"Fork" a recipe to modify it and make it your own.

Recipe History

See how recipes have changed over time.

Explore

Browse our most popular recipes and get cooking!

Made by Andrew, Dylan, Nemo, and Thomas.

);

export default Landing;
<|endoftext|>


<commit_before>'use strict';

const _ = require('lodash');

class Filters {

	constructor (cfg) {
		this.cfg = cfg.filters;

		// Filter sets
		this.following = new Set();
		this.blocked = new Set();
		this.muted = new Set();

		// Filter functions
		this.user = _.matchesProperty('id_str', cfg.user.id_str);
		this.followed = (x => _.has(x, 'id_str') && this.following.has(x.id_str));
		this.mentioned = (x => _.has(x, 'entities.user_mentions') && _.some(x.entities.user_mentions, this.user));
		this.reply_to_user = _.matchesProperty('in_reply_to_user_id_str', cfg.user.id_str);
		this.reply_to_followed = (x => _.has(x, 'in_reply_to_user_id_str') && this.following.has(x.in_reply_to_user_id_str));
		this.event_user_src = _.matchesProperty('source.id_str', cfg.user.id_str);
		this.event_user_tgt = _.matchesProperty('target.id_str', cfg.user.id_str);
	}

	static is_tweet (obj) {
		return ((_.has(obj, 'id_str') && _.isString(obj.id_str)) &&
				(_.has(obj, 'text') && _.isString(obj.text)));
	}

	static is_retweet (obj) {
		return (_.has(obj, 'retweeted_status') && _.isString(obj.retweeted_status));
	}

	static is_quote (obj) {
		return ((_.has(obj, 'is_quote_status') && obj.is_quote_status === true) &&
				(_.has(obj, 'quoted_status_id_str') && _.isString(obj.quoted_status_id_str)) &&
				(_.has(obj, 'quoted_status') && _.isObjectLike(obj.quoted_status)));
	}

	static is_reply (obj) {
		return (_.has(obj, 'in_reply_to_user_id_str') && _.isString(obj.in_reply_to_user_id_str));
	}

	static is_reply_to_tweet (obj) {
		return (_.has(obj, 'in_reply_to_status_id_str') && _.isString(obj.in_reply_to_status_id_str));
	}

	static is_event (obj) {
		return (_.has(obj, 'event') && _.isString(obj.event));
	}

	static is_delete (obj) {
		return (_.has(obj, 'delete') && _.isObjectLike(obj.delete));
	}

	static compareid (ida, idb) {
		// Unlike normal string sorting, longer means higher
		if (ida.length > idb.length) {
			return 1;
		}
		else if (ida.length < idb.length) {
			return -1;
		}
		else {
			// Equal length, compare char-by-char
			for (let i = 0; i < ida.length; i++) {
				let a = ida[i], b = idb[i];
				if (a > b) {
					return 1;
				}
				else if (a < b) {
					return -1;
				}
				// Chars equal, continue
			}
			// Strings equal
			return 0;
		}
	}

	static format (tweet, quote) {
		if (quote) {
			return `${this.format(tweet)} [${this.format(quote)}]`;
		}
		return `${tweet.user.screen_name || '~'}: ${_.unescape(tweet.text)}`;
	}

	// Warning: mutates original!
	static splituser (tweet) {
		let user;
		if (_.has(tweet.user, 'name') && _.has(tweet.user, 'description')) {
			user = tweet.user;
			tweet.user = {
				id_str: user.id_str,
			};
		}
		return [user, tweet];
	}

	static equaluser (user1, user2) {
		function comp (v1, v2, k, o1, o2) {
			// Skip missing keys, otherwise we'll get false positives
			// due to *slightly* different user objects with favs/rts
			if (v1 === undefined || v2 === undefined) {
				return true;
			}
			// Skip if both falsy - we don't need "" vs null diffs
			if (!v1 && !v2) {
				return true;
			}
			// Omit the '*_count' properties, since they'll be constantly changing
			if (_.endsWith(k, '_count')) {
				return true;
			}
			// DEBUG
			// console.log(`[${v1 == v2}] ${k} ${v1} ${v2}`);
			// END DEBUG
			return undefined;
		};

		return _.isEqualWith(user1, user2, comp);
	}

	tweetby (tweet) {
		if (this.user(tweet.user)) {
			return 'by_user';
		}
		else if (this.followed(tweet.user)) {
			return 'by_followed';
		}
		return 'by_other';
	}

	tweetof (tweet) {
		if (this.user(tweet.user)) {
			return 'of_user';
		}
		else if (this.followed(tweet.user)) {
			return 'of_followed';
		}
		return 'of_other';
	}

	tweetto (tweet) {
		if (this.reply_to_user(tweet)) {
			return 'to_user';
		}
		else if (this.reply_to_followed(tweet)) {
			return 'to_followed';
		}
		return 'to_other';
	}

	is_blocked (user) {
		return this.blocked.has(user.id_str);
	}

	is_muted (user) {
		return this.muted.has(user.id_str);
	}

	updateset (name, newdata) {
		let update = false;
		let oldset = this[name];
		let newset;

		// Check size first
		if (oldset.size !== newdata.length) {
			// Build replacement set, skip cross-check
			newset = new Set(newdata);
			update = true;
		}
		else {
			// Build new set
			newset = new Set();

			// Check for new ids and build new set
			for (let item of newdata) {
				// Add to new set (for reverse check and/or set replacement)
				newset.add(item);

				// Check if not in old set
				if (!oldset.has(item)) {
					update = true;
				}
			}

			// Reverse check (ids removed since last update)
			if (!update) {
				for (let item of oldset) {
					if (!newset.has(item)) {
						update = true;
						// This time we can break right away, since we've built our new set already
						break;
					}
				}
			}
		}

		if (update) {
			// Update set
			this[name] = newset;
		}

		return update;
	}

	on_rt (source, silent) {
		let src_type = null, tgt_type = null, quo_type = null;
		let target = source.retweeted_status;
		let quoted;
		// Strip target from source
		source.retweeted_status = {id_str: target.id_str};

		let src_user = this.tweetby(source);
		let tgt_user = this.tweetof(target);
		let quo_user;

		if (this.constructor.is_quote(target)) {
			quoted = target.quoted_status;
			delete target.quoted_status;
			quo_user = this.tweetof(quoted);
		}

		// Strip quoted from source, too
		if (this.constructor.is_quote(source)) {
			delete source.quoted_status;
		}

		if (this.cfg[src_user].retweet[tgt_user].source) {
			src_type = src_user === 'by_user' ? 'user_tweet' : 'other_tweet';
		}
		if (this.cfg[src_user].retweet[tgt_user].target) {
			tgt_type = tgt_user === 'of_user' ? 'user_tweet' : 'other_tweet';
		}
		if (quoted && this.cfg[src_user].retweet[tgt_user].quoted) {
			quo_type = quo_user === 'of_user' ? 'user_tweet' : 'other_tweet';
		}

		let ret = [];
		if (!silent && (src_type !== null || tgt_type !== null || quo_type !== null)) {
			ret.push({
				type: 'log',
				data: `[RT ${source.user.screen_name}] ${this.constructor.format(target, quoted)}`
			});
		}
		if (src_type !== null) {
			ret.push({
				type: src_type,
				data: source
			});
		}
		if (tgt_type !== null) {
			ret.push({
				type: tgt_type,
				data: target
			});
		}
		if (quo_type !== null) {
			ret.push({
				type: quo_type,
				data: quoted
			});
		}
		return ret;
	}

	on_quote (source, silent) {
		let src_type = null, quo_type = null;
		let quoted = source.quoted_status;
		delete source.quoted_status;

		let src_user = this.tweetby(source);
		let quo_user = this.tweetof(quoted);

		if (this.cfg[src_user].quote[quo_user].source) {
			src_type = src_user === 'by_user' ? 'user_tweet' : 'other_tweet';
		}
		if (this.cfg[src_user].quote[quo_user].quoted) {
			quo_type = quo_user === 'of_user' ? 'user_tweet' : 'other_tweet';
		}

		let ret = [];
		if (!silent && (src_type !== null || quo_type !== null)) {
			ret.push({
				type: 'log',
				data: `[Quote] ${this.constructor.format(source, quoted)}`
			});
		}
		if (src_type !== null) {
			ret.push({
				type: src_type,
				data: source
			});
		}
		if (quo_type !== null) {
			ret.push({
				type: quo_type,
				data: quoted
			});
		}
		return ret;
	}

	on_reply (source, silent) {
		let src_type = null, quo_type = null;
		let quoted;

		// Check source, target
		let src_user = this.tweetby(source);
		let tgt_user = this.tweetto(source);

		if (this.cfg[src_user].reply[tgt_user].source) {
			src_type = src_user === 'by_user' ? 'user_tweet' : 'other_tweet';
		}

		if (this.constructor.is_quote(source)) {
			quoted = source.quoted_status;
			delete source.quoted_status;

			if (this.cfg[src_user].reply[tgt_user].quoted) {
				quo_type = this.tweetof(quoted) === 'of_user' ? 'user_tweet' : 'other_tweet';
			}
		}

		let ret = [];
		if (!silent && (src_type !== null || quo_type !== null)) {
			ret.push({
				type: 'log',
				data: `[Reply] ${this.constructor.format(source, quoted)}`
			});
		}
		if (src_type !== null) {
			ret.push({
				type: src_type,
				data: source
			});
		}
		if (quo_type !== null) {
			ret.push({
				type: quo_type,
				data: quoted
			});
		}
		return ret;
	}

	on_user_tweet (tweet, silent) {
		let ret = [];
		if (this.cfg.by_user.standalone && this.user(tweet.user)) {
			if (!silent) {
				ret.push({
					type: 'log',
					data: `[User] ${this.constructor.format(tweet)}`
				});
			}
			ret.push({
				type: 'user_tweet',
				data: tweet
			});
		}
		return ret;
	}

	on_mention (source, silent) {
		let src_type = null, quo_type = null;
		let quoted;

		// Check source
		let src_user = this.tweetby(source);

		if (this.cfg[src_user].other_mention.source) {
			src_type = src_user === 'by_user' ? 'user_tweet' : 'other_tweet';
		}

		if (this.constructor.is_quote(source)) {
			quoted = source.quoted_status;
			delete source.quoted_status;

			if (this.cfg[src_user].other_mention.quoted) {
				quo_type = this.tweetof(quoted) === 'of_user' ? 'user_tweet' : 'other_tweet';
			}
		}

		let ret = [];
		if (!silent && (src_type !== null || quo_type !== null)) {
			ret.push({
				type: 'log',
				data: `[Mention] ${this.constructor.format(source, quoted)}`
			});
		}
		if (src_type !== null) {
			ret.push({
				type: src_type,
				data: source
			});
		}
		if (quo_type !== null) {
			ret.push({
				type: quo_type,
				data: quoted
			});
		}
		return ret;
	}

	on_favorite (source, silent) {
		let src_type = null, quo_type = null;
		let quoted;

		let src_user = this.tweetby(source);

		if (this.cfg[src_user].user_favorited.source) {
			src_type = src_user === 'by_user' ? 'user_tweet' : 'other_tweet';
		}

		if (this.constructor.is_quote(source)) {
			quoted = source.quoted_status;
			delete source.quoted_status;

			if (this.cfg[src_user].user_favorited.quoted) {
				quo_type = this.tweetby(quoted) === 'by_user' ? 'user_tweet' : 'other_tweet';
			}
		}

		let ret = [];
		if (!silent && (src_type !== null || quo_type !== null)) {
			ret.push({
				type: 'log',
				data: `[Favorite] ${this.constructor.format(source, quoted)}`
			});
		}
		if (src_type !== null) {
			ret.push({
				type: src_type,
				data: source
			});
		}
		if (quo_type !== null) {
			ret.push({
				type: quo_type,
				data: quoted
			});
		}
		return ret;
	}

	check_reply (tweet) {
		// Check source, target
		let src_user = this.tweetby(source);
		let tgt_user = this.tweetto(source);

		if (this.constructor.is_reply_to_tweet(tweet) && this.cfg[src_user].reply[tgt_user].target) {
			return tweet.in_reply_to_status_id_str;
		}
		// Otherwise return undefined
	}

}

module.exports = Filters;
<commit_msg>Fixed is_retweet() bug<commit_after>'use strict';

const _ = require('lodash');

class Filters {

	constructor (cfg) {
		this.cfg = cfg.filters;

		// Filter sets
		this.following = new Set();
		this.blocked = new Set();
		this.muted = new Set();

		// Filter functions
		this.user = _.matchesProperty('id_str', cfg.user.id_str);
		this.followed = (x => _.has(x, 'id_str') && this.following.has(x.id_str));
		this.mentioned = (x => _.has(x, 'entities.user_mentions') && _.some(x.entities.user_mentions, this.user));
		this.reply_to_user = _.matchesProperty('in_reply_to_user_id_str', cfg.user.id_str);
		this.reply_to_followed = (x => _.has(x, 'in_reply_to_user_id_str') && this.following.has(x.in_reply_to_user_id_str));
		this.event_user_src = _.matchesProperty('source.id_str', cfg.user.id_str);
		this.event_user_tgt = _.matchesProperty('target.id_str', cfg.user.id_str);
	}

	static is_tweet (obj) {
		return ((_.has(obj, 'id_str') && _.isString(obj.id_str)) &&
				(_.has(obj, 'text') && _.isString(obj.text)));
	}

	static is_retweet (obj) {
		return (_.has(obj, 'retweeted_status') && _.isObjectLike(obj.retweeted_status));
	}

	static is_quote (obj) {
		return ((_.has(obj, 'is_quote_status') && obj.is_quote_status === true) &&
				(_.has(obj, 'quoted_status_id_str') && _.isString(obj.quoted_status_id_str)) &&
				(_.has(obj, 'quoted_status') && _.isObjectLike(obj.quoted_status)));
	}

	static is_reply (obj) {
		return (_.has(obj, 'in_reply_to_user_id_str') && _.isString(obj.in_reply_to_user_id_str));
	}

	static is_reply_to_tweet (obj) {
		return (_.has(obj, 'in_reply_to_status_id_str') && _.isString(obj.in_reply_to_status_id_str));
	}

	static is_event (obj) {
		return (_.has(obj, 'event') && _.isString(obj.event));
	}

	static is_delete (obj) {
		return (_.has(obj, 'delete') && _.isObjectLike(obj.delete));
	}

	static compareid (ida, idb) {
		// Falsy checks
		if (!ida) {
			return (!idb) ? 0 : -1;
		}
		else if (!idb) {
			return 1;
		}
		// Unlike normal string sorting, longer means higher by definition
		if (ida.length > idb.length) {
			return 1;
		}
		else if (ida.length < idb.length) {
			return -1;
		}
		else {
			// Equal length, compare char-by-char
			for (let i = 0; i < ida.length; i++) {
				let a = ida[i], b = idb[i];
				if (a > b) {
					return 1;
				}
				else if (a < b) {
					return -1;
				}
				// Chars equal, continue
			}
			// Strings equal
			return 0;
		}
	}

	static format (tweet, quote) {
		if (quote) {
			return `${this.format(tweet)} [${this.format(quote)}]`;
		}
		return `${tweet.user.screen_name || '~'}: ${_.unescape(tweet.text)}`;
	}

	// Warning: mutates original!
	static splituser (tweet) {
		let user;
		if (_.has(tweet.user, 'name') && _.has(tweet.user, 'description')) {
			user = tweet.user;
			tweet.user = {
				id_str: user.id_str,
			};
		}
		return [user, tweet];
	}

	static equaluser (user1, user2) {
		function comp (v1, v2, k, o1, o2) {
			// Skip missing keys, otherwise we'll get false positives
			// due to *slightly* different user objects with favs/rts
			if (v1 === undefined || v2 === undefined) {
				return true;
			}
			// Skip if both falsy - we don't need "" vs null diffs
			if (!v1 && !v2) {
				return true;
			}
			// Omit the '*_count' properties, since they'll be constantly changing
			if (_.endsWith(k, '_count')) {
				return true;
			}
			// DEBUG
			// console.log(`[${v1 == v2}] ${k} ${v1} ${v2}`);
			// END DEBUG
			return undefined;
		};

		return _.isEqualWith(user1, user2, comp);
	}

	tweetby (tweet) {
		if (this.user(tweet.user)) {
			return 'by_user';
		}
		else if (this.followed(tweet.user)) {
			return 'by_followed';
		}
		return 'by_other';
	}

	tweetof (tweet) {
		if (this.user(tweet.user)) {
			return 'of_user';
		}
		else if (this.followed(tweet.user)) {
			return 'of_followed';
		}
		return 'of_other';
	}

	tweetto (tweet) {
		if (this.reply_to_user(tweet)) {
			return 'to_user';
		}
		else if (this.reply_to_followed(tweet)) {
			return 'to_followed';
		}
		return 'to_other';
	}

	is_blocked (user) {
		return this.blocked.has(user.id_str);
	}

	is_muted (user) {
		return this.muted.has(user.id_str);
	}

	updateset (name, newdata) {
		let update = false;
		let oldset = this[name];
		let newset;

		// Check size first
		if (oldset.size !== newdata.length) {
			// Build replacement set, skip cross-check
			newset = new Set(newdata);
			update = true;
		}
		else {
			// Build new set
			newset = new Set();

			// Check for new ids and build new set
			for (let item of newdata) {
				// Add to new set (for reverse check and/or set replacement)
				newset.add(item);

				// Check if not in old set
				if (!oldset.has(item)) {
					update = true;
				}
			}

			// Reverse check (ids removed since last update)
			if (!update) {
				for (let item of oldset) {
					if (!newset.has(item)) {
						update = true;
						// This time we can break right away, since we've built our new set already
						break;
					}
				}
			}
		}

		if (update) {
			// Update set
			this[name] = newset;
		}

		return update;
	}

	on_rt (source, silent) {
		let src_type = null, tgt_type = null, quo_type = null;
		let target = source.retweeted_status;
		let quoted;
		// Strip target from source
		source.retweeted_status = {id_str: target.id_str};

		let src_user = this.tweetby(source);
		let tgt_user = this.tweetof(target);
		let quo_user;

		if (this.constructor.is_quote(target)) {
			quoted = target.quoted_status;
			delete target.quoted_status;
			quo_user = this.tweetof(quoted);
		}

		// Strip quoted from source, too
		if (this.constructor.is_quote(source)) {
			delete source.quoted_status;
		}

		if (this.cfg[src_user].retweet[tgt_user].source) {
			src_type = src_user === 'by_user' ? 'user_tweet' : 'other_tweet';
		}
		if (this.cfg[src_user].retweet[tgt_user].target) {
			tgt_type = tgt_user === 'of_user' ? 'user_tweet' : 'other_tweet';
		}
		if (quoted && this.cfg[src_user].retweet[tgt_user].quoted) {
			quo_type = quo_user === 'of_user' ? 'user_tweet' : 'other_tweet';
		}

		let ret = [];
		if (!silent && (src_type !== null || tgt_type !== null || quo_type !== null)) {
			ret.push({
				type: 'log',
				data: `[RT ${source.user.screen_name}] ${this.constructor.format(target, quoted)}`
			});
		}
		if (src_type !== null) {
			ret.push({
				type: src_type,
				data: source
			});
		}
		if (tgt_type !== null) {
			ret.push({
				type: tgt_type,
				data: target
			});
		}
		if (quo_type !== null) {
			ret.push({
				type: quo_type,
				data: quoted
			});
		}
		return ret;
	}

	on_quote (source, silent) {
		let src_type = null, quo_type = null;
		let quoted = source.quoted_status;
		delete source.quoted_status;

		let src_user = this.tweetby(source);
		let quo_user = this.tweetof(quoted);

		if (this.cfg[src_user].quote[quo_user].source) {
			src_type = src_user === 'by_user' ? 'user_tweet' : 'other_tweet';
		}
		if (this.cfg[src_user].quote[quo_user].quoted) {
			quo_type = quo_user === 'of_user' ? 'user_tweet' : 'other_tweet';
		}

		let ret = [];
		if (!silent && (src_type !== null || quo_type !== null)) {
			ret.push({
				type: 'log',
				data: `[Quote] ${this.constructor.format(source, quoted)}`
			});
		}
		if (src_type !== null) {
			ret.push({
				type: src_type,
				data: source
			});
		}
		if (quo_type !== null) {
			ret.push({
				type: quo_type,
				data: quoted
			});
		}
		return ret;
	}

	on_reply (source, silent) {
		let src_type = null, quo_type = null;
		let quoted;

		// Check source, target
		let src_user = this.tweetby(source);
		let tgt_user = this.tweetto(source);

		if (this.cfg[src_user].reply[tgt_user].source) {
			src_type = src_user === 'by_user' ? 'user_tweet' : 'other_tweet';
		}

		if (this.constructor.is_quote(source)) {
			quoted = source.quoted_status;
			delete source.quoted_status;

			if (this.cfg[src_user].reply[tgt_user].quoted) {
				quo_type = this.tweetof(quoted) === 'of_user' ? 'user_tweet' : 'other_tweet';
			}
		}

		let ret = [];
		if (!silent && (src_type !== null || quo_type !== null)) {
			ret.push({
				type: 'log',
				data: `[Reply] ${this.constructor.format(source, quoted)}`
			});
		}
		if (src_type !== null) {
			ret.push({
				type: src_type,
				data: source
			});
		}
		if (quo_type !== null) {
			ret.push({
				type: quo_type,
				data: quoted
			});
		}
		return ret;
	}

	on_user_tweet (tweet, silent) {
		let ret = [];
		if (this.cfg.by_user.standalone && this.user(tweet.user)) {
			if (!silent) {
				ret.push({
					type: 'log',
					data: `[User] ${this.constructor.format(tweet)}`
				});
			}
			ret.push({
				type: 'user_tweet',
				data: tweet
			});
		}
		return ret;
	}

	on_mention (source, silent) {
		let src_type = null, quo_type = null;
		let quoted;

		// Check source
		let src_user = this.tweetby(source);

		if (this.cfg[src_user].other_mention.source) {
			src_type = src_user === 'by_user' ? 'user_tweet' : 'other_tweet';
		}

		if (this.constructor.is_quote(source)) {
			quoted = source.quoted_status;
			delete source.quoted_status;

			if (this.cfg[src_user].other_mention.quoted) {
				quo_type = this.tweetof(quoted) === 'of_user' ? 'user_tweet' : 'other_tweet';
			}
		}

		let ret = [];
		if (!silent && (src_type !== null || quo_type !== null)) {
			ret.push({
				type: 'log',
				data: `[Mention] ${this.constructor.format(source, quoted)}`
			});
		}
		if (src_type !== null) {
			ret.push({
				type: src_type,
				data: source
			});
		}
		if (quo_type !== null) {
			ret.push({
				type: quo_type,
				data: quoted
			});
		}
		return ret;
	}

	on_favorite (source, silent) {
		let src_type = null, quo_type = null;
		let quoted;

		let src_user = this.tweetby(source);

		if (this.cfg[src_user].user_favorited.source) {
			src_type = src_user === 'by_user' ? 'user_tweet' : 'other_tweet';
		}

		if (this.constructor.is_quote(source)) {
			quoted = source.quoted_status;
			delete source.quoted_status;

			if (this.cfg[src_user].user_favorited.quoted) {
				quo_type = this.tweetby(quoted) === 'by_user' ? 'user_tweet' : 'other_tweet';
			}
		}

		let ret = [];
		if (!silent && (src_type !== null || quo_type !== null)) {
			ret.push({
				type: 'log',
				data: `[Favorite] ${this.constructor.format(source, quoted)}`
			});
		}
		if (src_type !== null) {
			ret.push({
				type: src_type,
				data: source
			});
		}
		if (quo_type !== null) {
			ret.push({
				type: quo_type,
				data: quoted
			});
		}
		return ret;
	}

	check_reply (tweet) {
		// Check source, target
		let src_user = this.tweetby(source);
		let tgt_user = this.tweetto(source);

		if (this.constructor.is_reply_to_tweet(tweet) && this.cfg[src_user].reply[tgt_user].target) {
			return tweet.in_reply_to_status_id_str;
		}
		// Otherwise return undefined
	}

}

module.exports = Filters;
<|endoftext|>


<commit_before>// client.js
// Client side stuffs for greyhound web viewer
//

(function(w) {
	"use strict";

    // get URL parameters
    // for now, using an adaptation from: http://stackoverflow.com/a/2880929
    var getUrlParameters = function(query) {
        query = query.substring(1);

        var match,
            pl     = /\+/g,  // Regex for replacing addition symbol with a space
            search = /([^&=]+)=?([^&]*)/g,
            decode = function (s) { return decodeURIComponent(s.replace(pl, " ")); },
            urlParams = {};

        while (match = search.exec(query))
           urlParams[decode(match[1])] = decode(match[2]);

        return urlParams;
    };

	// show an error message to the user
	//
	var errorOut = function(msg) {
		$("#messages").html("
" + msg + "
");
		console.log('Error : ' + msg);
	};

	// show a status message to the user
	var message = function(msg) {
		$("#messages").html("
" + msg + "
");
		console.log('Status: ' + msg);
	};

	// download data over from the server and call the cb when done
	//
	var downloadData = function(status_cb, cb) {
		if (!w.WebSocket)
			return cb(new Error("Your browser doesn't seem to support websockets"));

		status_cb("Loading Point Cloud Data... Please Wait.");

		// prepare websocket URL and try to create a websocket
		//
		var wsURI = "ws://" + w.location.host + "/";
		var ws = new w.WebSocket(wsURI);

        console.log("DBG: ", w.location.search);
        console.log("GET: ", getUrlParameters(w.location.search));

		// get us data as array buffer
		//
		ws.binaryType = "arraybuffer";

		// Websocket open handler
		// Send a command to create a session, we will initiate an actual read
		// when we get confirmation that the connection was created
		ws.onopen = function() {
			status_cb("WebSocket connection established. Creating session...");

			ws.send(JSON.stringify({
				command: 'create',

                // TODO Get this from URL.
                pipelineId: 'd4f4cc08e63242a201de6132e5f54b08'
			}));
		};

		// General handler for all messages, our logic goes in here
		//
		var count;
		var session;
		var pointsCount;
		var dataBuffer = null; // buffer to collect recieved data

		ws.onmessage = function(evt) {
			if (typeof(evt.data) === "string") {
				var msg = JSON.parse(evt.data);
				console.log('Incoming:', msg);

				if (msg.command === "create") {
					if (msg.status === 0)
						return cb(new Error('Failed to create session, this is not good.'));

					// this is in response to our create request
					// we now request to recieve the data
					//
					ws.send(JSON.stringify({
						command: 'read',
						session: msg.session
					}));

					session	= msg.session;

					status_cb("Read initiated, waiting for response...");
				}
				else if (msg.command === "read") {
					if (msg.status !== 1)
						return cb(new Error("Failed to queue read request: " + (msg.reason || "Unspecified error")));

					status_cb("Reading data... Please wait.");

					count		= 0;
					pointsCount	= msg.pointsRead;
					console.log(msg.pointsRead, msg.bytesCount);
					dataBuffer	= new Int8Array(msg.bytesCount);
				}
				else if (msg.command === "destroy") {
					if (msg.status === 1) {
						ws.close();
						return status_cb("Data read successfully.");
					}

					cb(new Error("Session destroy command failed"));
				}
			}
			else {
				var a = new Int8Array(evt.data);
				dataBuffer.set(a, count);

				count += a.length;

				if (count >= dataBuffer.byteLength) {
					// we're done reading data, close connection
					ws.send(JSON.stringify({
						command: 'destroy',
						session: session
					}));
				}
			}
		};

		// close and cleanup data
		//
		ws.onclose = function() {
			if(dataBuffer !== null) {
				setTimeout(function() {
					cb(null, dataBuffer, pointsCount);
				}, 0); // so that we call the callback outside the context of ws.onclose
			}
		};
	}

	w.doIt = function() {
		$("#stats").hide();
		downloadData(message, function(err, data, count) {
			if (err)
				return errorOut(err.message);

			console.log('Got', data.byteLength, 'total bytes in', count, 'points');

			message("Data download complete, handing over to renderer.");
			try {
				renderPoints(data, count, message);
			}
			catch(e) {
				errorOut(e.message);
			}
		});
	};
})(window);

$(function() {
	doIt();
});
<commit_msg>Require web users to specify the ID of a pipeline in the database as a URL parameter.<commit_after>// client.js
// Client side stuffs for greyhound web viewer
//

(function(w) {
	"use strict";

    // get URL parameters
    // for now, using an adaptation from: http://stackoverflow.com/a/2880929
    var getUrlParameters = function(query) {
        query = query.substring(1);

        var match,
            pl     = /\+/g,  // Regex for replacing addition symbol with a space
            search = /([^&=]+)=?([^&]*)/g,
            decode = function (s) { return decodeURIComponent(s.replace(pl, " ")); },
            urlParams = {};

        while (match = search.exec(query))
           urlParams[decode(match[1])] = decode(match[2]);

        return urlParams;
    };

	// show an error message to the user
	//
	var errorOut = function(msg) {
		$("#messages").html("
" + msg + "
");
		console.log('Error : ' + msg);
	};

	// show a status message to the user
	var message = function(msg) {
		$("#messages").html("
" + msg + "
");
		console.log('Status: ' + msg);
	};

	// download data over from the server and call the cb when done
	//
	var downloadData = function(status_cb, cb) {
		if (!w.WebSocket)
			return cb(new Error("Your browser doesn't seem to support websockets"));

		status_cb("Loading Point Cloud Data... Please Wait.");

		// prepare websocket URL and try to create a websocket
		//
		var wsURI = "ws://" + w.location.host + "/";
		var ws = new w.WebSocket(wsURI);

		// get data as array buffer
		ws.binaryType = "arraybuffer";

		// Websocket open handler
		// Send a command to create a session, we will initiate an actual read
		// when we get confirmation that the connection was created
		ws.onopen = function() {
			status_cb("WebSocket connection established. Creating session...");

            var urlParams = getUrlParameters(w.location.search);

            // We must be provided a data parameter.
            if (urlParams.hasOwnProperty('data'))
            {
                ws.send(JSON.stringify({
                    command: 'create',
                    pipelineId: urlParams['data']
                }));
            }
            else
            {
                status_cb("No pipeline selected!");
            }
		};

		// General handler for all messages, our logic goes in here
		//
		var count;
		var session;
		var pointsCount;
		var dataBuffer = null; // buffer to collect recieved data

		ws.onmessage = function(evt) {
			if (typeof(evt.data) === "string") {
				var msg = JSON.parse(evt.data);
				console.log('Incoming:', msg);

				if (msg.command === "create") {
					if (msg.status === 0)
						return cb(new Error('Failed to create session, this is not good.'));

					// this is in response to our create request
					// we now request to recieve the data
					//
					ws.send(JSON.stringify({
						command: 'read',
						session: msg.session
					}));

					session	= msg.session;

					status_cb("Read initiated, waiting for response...");
				}
				else if (msg.command === "read") {
					if (msg.status !== 1)
						return cb(new Error("Failed to queue read request: " + (msg.reason || "Unspecified error")));

					status_cb("Reading data... Please wait.");

					count		= 0;
					pointsCount	= msg.pointsRead;
					console.log(msg.pointsRead, msg.bytesCount);
					dataBuffer	= new Int8Array(msg.bytesCount);
				}
				else if (msg.command === "destroy") {
					if (msg.status === 1) {
						ws.close();
						return status_cb("Data read successfully.");
					}

					cb(new Error("Session destroy command failed"));
				}
			}
			else {
				var a = new Int8Array(evt.data);
				dataBuffer.set(a, count);

				count += a.length;

				if (count >= dataBuffer.byteLength) {
					// we're done reading data, close connection
					ws.send(JSON.stringify({
						command: 'destroy',
						session: session
					}));
				}
			}
		};

		// close and cleanup data
		//
		ws.onclose = function() {
			if(dataBuffer !== null) {
				setTimeout(function() {
					cb(null, dataBuffer, pointsCount);
				}, 0); // so that we call the callback outside the context of ws.onclose
			}
		};
	}

	w.doIt = function() {
		$("#stats").hide();
		downloadData(message, function(err, data, count) {
			if (err)
				return errorOut(err.message);

			console.log('Got', data.byteLength, 'total bytes in', count, 'points');

			message("Data download complete, handing over to renderer.");
			try {
				renderPoints(data, count, message);
			}
			catch(e) {
				errorOut(e.message);
			}
		});
	};
})(window);

$(function() {
	doIt();
});
<|endoftext|>


<commit_before>var execSync = require('exec-sync');

function setLength (long){

  if (long === 0){
    return ' -s'
  } else if(long === 1){
    return ' -l'
  } else {
    return ''
  }

};

function setOffensiveness (dirty){

  if (dirty === 1){
    return ' -o';
  } else {
    return '';
  };

};

module.exports = function(long, dirty){

  var long    = parseInt(long),
      dirty   = parseInt(dirty);

  var methods = {
    options: function(){
      return setLength(long) + setOffensiveness(dirty);
    },
    text: function(){
      return execSync('fortune' + this.options());
    }
  };

  return methods;
};
<commit_msg>Adding fortune verification to fortune lib<commit_after>var execSync = require('exec-sync');

function setLength (long){

  if (long === 0){
    return ' -s'
  } else if(long === 1){
    return ' -l'
  } else {
    return ''
  }

};

function setOffensiveness (dirty){

  if (dirty === 1){
    return ' -o';
  } else {
    return '';
  };

};

module.exports = function(long, dirty){

  var long    = parseInt(long),
      dirty   = parseInt(dirty);

  var methods = {
    options: function(){
      return setLength(long) + setOffensiveness(dirty);
    },
    text: function(){

      if (execSync('command -v fortune >/dev/null 2>&1') !== ''){
        return execSync('fortune' + this.options());
      } else {
        return "It looks like you don't have the Fortune program. Check README.md to install instructions.";
      }

    }
  };

  return methods;
};
<|endoftext|>


<commit_before>'use strict';

let Promise = require('bluebird');

let FSUtils = {
  findup: Promise.promisify(require('findup')),
  fs: Promise.promisifyAll(require('fs'))
};

module.exports = FSUtils;
<commit_msg>removed unused fsutils module<commit_after><|endoftext|>