<commit_before>/**
 * @copyright	Copyright 2010-2013, The Titon Project
 * @license		http://opensource.org/licenses/bsd-license.php
 * @link		http://titon.io
 */

(function(root) {
	'use strict';

	var vendor,
		slice = Array.prototype.slice,
		isNode = (typeof module !== 'undefined' && module.exports);

	/**
	 * Loop over each collection and extend the prototypes.
	 *
	 * @param {Object} vendor
	 * @param {Array} probes
	 */
	function mapPrototype(vendor, probes) {
		var a, b, c, probe, proto, func;

		for (a = 0; (probe = probes[a]); a++) {

			// Loop over each prototype
			for (b = 0; (proto = probe[0][b]); b++) {

				// Loop over each function
				for (c = 0; (func = probe[1][c]); c++) {

					// Skip if the function already exists on the prototype
					// We don't wont to cause collisions with built-ins or user defined
					if (!vendor[func] || proto[func] || proto.prototype[func]) {
						continue;
					}

					// Objects can only use static methods
					// Applying to the prototype disrupts object literals
					if (proto === Object) {
						proto[func] = vendor[func];
					} else {
						extendPrototype.call(this, vendor, proto, func);
					}
				}
			}
		}
	}

	/**
	 * Extend the prototype with the vendor function.
	 * Prepend the "this" value to the beginning of the arguments.
	 *
	 * @param {Object} vendor
	 * @param {Object} proto
	 * @param {Function} func
	 */
	function extendPrototype(vendor, proto, func) {
		proto.prototype[func] = function() {
			var args = slice.call(arguments) || [];
				args.unshift(this);

			return vendor[func].apply(this, args);
		};
	}

	/**
	 * Check if a node module exists.
	 *
	 * @param {Object} name
	 * @returns {bool}
	 */
	function moduleExists(name) {
		try {
			return require.resolve(name);
		} catch(e) {
			return false;
		}
	}

	/**
	 * ----------------------------------------
	 *		Lo-Dash / Underscore
	 * ----------------------------------------
	 */

	if (isNode) {
		if (moduleExists('lodash')) {
			exports._ = root._ = require('lodash');

		} else if (moduleExists('underscore')) {
			exports._ = root._ = require('underscore');
		}
	}

	if (typeof root._ !== 'undefined') {
		vendor = root._;

		// Extend with custom methods
		vendor.extend(vendor, {

			/**
			 * Empty the collection.
			 *
			 * @param {Array|Object|String} collection
			 * @returns {*}
			 */
			empty: function(collection) {
				if (_.isArray(collection)) {
					collection = [];
				} else if (_.isString(collection)) {
					collection = '';
				} else {
					collection = {};
				}

				return collection;
			},

			/**
			 * Convert a string to a camel case form by remove all non-alphanumeric characters
			 * and capitalizing the first character of each word.
			 *
			 * @param {String} string
			 * @returns {String}
			 */
			camelCase: function(string) {
				return string.replace(/\W\D/g, function(match){
					return match.charAt(1).toUpperCase();
				});
			},

			/**
			 * Capitalize the first character in all words, so long as that word follows a boundary.
			 *
			 * @param {String} string
			 * @returns {String}
			 */
			capitalize: function(string) {
				return string.replace(/\b[a-z]/g, function(match){
					return match.toUpperCase();
				});
			},

			/**
			 * Convert a camel cased or spaced out string to a slug based hyphenated form.
			 * This will remove all non-alphanumeric characters.
			 *
			 * @param {String} string
			 * @returns {String}
			 */
			slugify: function(string) {
				return string
					// Lowercase camel form
					.replace(/[A-Z]/g, function(match){
						return ' ' + match.charAt(0);
					})
					// Remove excess whitespace
					.replace(/\s{2,}/g, ' ')
					// Replace non-alphanumeric
					.replace(/[^A-Za-z0-9_-]/g, '-')
					// Trim dashes
					.replace(/^-+|-+$/, '')
					// Lowercase
					.toLowerCase();
			},

			/**
			 * Limit a number between minimum and maximum boundaries.
			 *
			 * @param {Number} number
			 * @param {Number} min
			 * @param {Number} max
			 * @returns {Number}
			 */
			limit: function(number, min, max){
				return Math.min(max, Math.max(min, number));
			},

			/**
			 * Round a number using a precision.
			 *
			 * @param {Number} number
			 * @param {Number} precision
			 * @returns {Number}
			 */
			round: function(number, precision) {
				precision = Math.pow(10, precision || 0).toFixed(precision < 0 ? -precision : 0);

				return Math.round(number * precision) / precision;
			},

			/**
			 * Convert a number to a float.
			 *
			 * @param {Number} number
			 * @returns {Number}
			 */
			toFloat: function(number) {
				return parseFloat(number);
			},

			/**
			 * Convert a float to a number.
			 *
			 * @param {Number} number
			 * @param {Number} base
			 * @returns {Number}
			 */
			toInt: function(number, base) {
				return parseInt(number, base || 10);
			}
		});

		// Mapping of functions and prototypes
		mapPrototype(vendor, [
			// Array
			[
				[Array],
				[
					'compact', 'difference', 'drop', 'findIndex', 'findLastIndex', 'first', 'flatten', 'head', 'indexOf', 'initial', 'intersection', 'last', 'lastIndexOf', 'pull',
					'range', 'remove', 'rest', 'sortedIndex', 'tail', 'take', 'union', 'uniq', 'unique', 'unzip', 'without', 'xor', 'zip', 'zipObject'
				]
			// Collections
			], [
				[Array, Object, String],
				[
					'all', 'any', 'at', 'collect', 'contains', 'countBy', 'detect', 'empty', 'each', 'eachRight', 'every', 'filter', 'find', 'findLast', 'findWhere', 'foldl', 'foldr', 'forEach', 'forEachRight',
					'groupBy', 'include', 'indexBy', 'inject', 'invoke', 'map', 'max', 'min', 'pluck', 'reduce', 'reduceRight', 'reject', 'sample', 'select', 'shuffle', 'size', 'some', 'sortBy', 'toArray', 'where',
					'constant'
				]
			// Functions
			// Unsupported: after, bindAll, bindKey, wrap
			], [
				[Function],
				[
					'bind', 'compose', 'curry', 'debounce', 'defer', 'delay', 'memoize', 'once', 'partial', 'partialRight', 'throttle',
					'createCallback'
				]
			// Objects
			// Unsupported: create
			], [
				[Object],
				[
					'assign', 'clone', 'cloneDeep', 'defaults', 'extend', 'findKey', 'findLastKey', 'forIn', 'forInRight', 'forOwn', 'forOwnRight', 'functions', 'has',
					'invert', 'keys', 'mapValues', 'merge', 'methods', 'mixin', 'omit', 'pairs', 'pick', 'property', 'result', 'transform', 'values'
				]
			// Strings
			], [
				[String],
				['escape', 'unescape', 'template', 'uniqueId', 'camelCase', 'slugify', 'capitalize']
			// Numbers
			], [
				[Number],
				['times', 'limit', 'round', 'toFloat', 'toInt', 'parseInt']
			// All
			], [
				[Array, Object, String, Number],
				['isEmpty', 'isEqual', 'isUndefined', 'isNull', 'toString', 'valueOf']
			]
		]);
	}
})(this);<commit_msg>Added underscore.string support<commit_after>/**
 * @copyright	Copyright 2010-2013, The Titon Project
 * @license		http://opensource.org/licenses/bsd-license.php
 * @link		http://titon.io
 */

(function(root) {
	'use strict';

	var slice = Array.prototype.slice,
		isNode = (typeof module !== 'undefined' && module.exports);

	/**
	 * Loop over each collection and extend the prototypes.
	 *
	 * @param {Object} vendor
	 * @param {Array} probes
	 */
	function mapPrototype(vendor, probes) {
		var a, b, c, probe, proto, func;

		for (a = 0; (probe = probes[a]); a++) {

			// Loop over each prototype
			for (b = 0; (proto = probe[0][b]); b++) {

				// Loop over each function
				for (c = 0; (func = probe[1][c]); c++) {

					// Skip if the function already exists on the prototype
					// We don't wont to cause collisions with built-ins or user defined
					if (!vendor[func] || proto[func] || proto.prototype[func]) {
						continue;
					}

					// Objects can only use static methods
					// Applying to the prototype disrupts object literals
					if (proto === Object) {
						proto[func] = vendor[func];
					} else {
						extendPrototype.call(this, vendor, proto, func);
					}
				}
			}
		}
	}

	/**
	 * Extend the prototype with the vendor function.
	 * Prepend the "this" value to the beginning of the arguments.
	 *
	 * @param {Object} vendor
	 * @param {Object} proto
	 * @param {Function} func
	 */
	function extendPrototype(vendor, proto, func) {
		proto.prototype[func] = function() {
			var args = slice.call(arguments) || [];
				args.unshift(this);

			return vendor[func].apply(this, args);
		};
	}

	/**
	 * Check if a node module exists.
	 *
	 * @param {Object} name
	 * @returns {bool}
	 */
	function moduleExists(name) {
		try {
			return require.resolve(name);
		} catch(e) {
			return false;
		}
	}

	/**
	 *----------------------------------------
	 *		Lo-Dash / Underscore
	 *----------------------------------------
	 */

	if (isNode) {
		if (moduleExists('lodash')) {
			exports._ = root._ = require('lodash');

		} else if (moduleExists('underscore')) {
			exports._ = root._ = require('underscore');
		}
	}

	var stringFunctions = [
		'escape', 'unescape', 'template', 'uniqueId', 'camelCase', 'slugify', 'capitalize',
		// underscore.string
		'isBlank', 'stripTags', 'capitalize', 'chop', 'clean', 'count', 'chars', 'swapCase', 'escapeHTML', 'unescapeHTML', 'escapeRegExp',
		'splice', 'insert', 'include', 'join', 'lines', 'reverse', 'startsWith', 'endsWith', 'succ', 'titleize', 'camelize', 'underscored',
		'dasherize', 'classify', 'humanize', 'trim', 'ltrim', 'rtrim', 'truncate', 'prune', 'words', 'pad', 'lpad', 'rpad', 'lrpad',
		'sprintf', 'vsprintf', 'toNumber', 'strRight', 'strRightBack', 'strLeft', 'strLeftBack', 'slugify', 'surround', 'quote', 'unquote',
		'repeat', 'naturalCmp', 'levenshtein', 'toBoolean'
	];

	if (typeof root._ !== 'undefined') {
		mapPrototype(root._, [
			// Array
			[
				[Array],
				[
					'compact', 'difference', 'drop', 'findIndex', 'findLastIndex', 'first', 'flatten', 'head', 'indexOf', 'initial', 'intersection', 'last', 'lastIndexOf', 'pull',
					'range', 'remove', 'rest', 'sortedIndex', 'tail', 'take', 'union', 'uniq', 'unique', 'unzip', 'without', 'xor', 'zip', 'zipObject'
				]
			// Collections
			], [
				[Array, Object, String],
				[
					'all', 'any', 'at', 'collect', 'contains', 'countBy', 'detect', 'empty', 'each', 'eachRight', 'every', 'filter', 'find', 'findLast', 'findWhere', 'foldl', 'foldr', 'forEach', 'forEachRight',
					'groupBy', 'include', 'indexBy', 'inject', 'invoke', 'map', 'max', 'min', 'pluck', 'reduce', 'reduceRight', 'reject', 'sample', 'select', 'shuffle', 'size', 'some', 'sortBy', 'toArray', 'where',
					'constant'
				]
			// Functions
			// Unsupported: after, bindAll, bindKey, wrap
			], [
				[Function],
				[
					'bind', 'compose', 'curry', 'debounce', 'defer', 'delay', 'memoize', 'once', 'partial', 'partialRight', 'throttle',
					'createCallback'
				]
			// Objects
			// Unsupported: create
			], [
				[Object],
				[
					'assign', 'clone', 'cloneDeep', 'defaults', 'extend', 'findKey', 'findLastKey', 'forIn', 'forInRight', 'forOwn', 'forOwnRight', 'functions', 'has',
					'invert', 'keys', 'mapValues', 'merge', 'methods', 'mixin', 'omit', 'pairs', 'pick', 'property', 'result', 'transform', 'values'
				]
			// Strings
			], [
				[String],
				stringFunctions
			// Numbers
			], [
				[Number],
				['times', 'limit', 'round', 'toFloat', 'toInt', 'parseInt']
			// All
			], [
				[Array, Object, String, Number],
				['isEmpty', 'isEqual', 'isUndefined', 'isNull', 'toString', 'valueOf']
			]
		]);
	}

	/**
	 *----------------------------------------
	 *		Underscore.String
	 *----------------------------------------
	 */

	if (isNode && moduleExists('underscore.string')) {
		exports._s = root._s = require('underscore.string');
	}

	if (typeof root._s !== 'undefined') {
		mapPrototype(root._s, [
			// String
			// Unsupported: toSentence, toSentenceSerial
			[
				[String],
				stringFunctions
			// Number
			], [
				[Number],
				['numberFormat']
			]
		]);
	}
})(this);<|endoftext|>


<commit_before>'use strict';

var Promise = require('bluebird'),
    Kind = require('./kind'),
    extend = require('extend'),
    util = require('./util');

var customOperators = {};

var defaultOptions = {
    skip: 0,
    limit: Infinity,
    plain: true,
    log: false
};

var Query = function Query(target, query, options) {

    this._targetModel = Kind.getSync(target);
    this._options = extend({}, defaultOptions, options);
    this._logs = [];

    this._executionPromise = null;
    this._target = target;
    this._query = query;
    this._executed = false;

};

Query.prototype._exec = function () {
    if(this._executed) {
        return;
    }
    this._executionPromise = handleQuery({
        target: this._target,
        query: this._query
    }, this._logs);
    this._executed = true;
};

Query.prototype._log = function (result) {
    if(this._options.log) {
        return {
            result: result,
            logs: this._logs
        };
    } else {
        return result;
    }
};

Query.prototype.count = function (callback) {

    this._exec();

    var self = this;
    var prom = this._executionPromise.then(function (result) {
        return self._log(result.length);
    });
    return util.bindPromise(prom, callback);

};

Query.prototype.all = function (callback) {

    this._exec();

    var self = this;
    var prom = this._executionPromise.then(function (result) {

        result = result.slice(self._options.skip, self._options.skip + self._options.limit);

        var query = self._targetModel.find({
            _id: {
                $in: result
            }
        });

        if(self._options.plain) {
            query.lean();
        }

        return query.exec().then(function(res) {
            return self._log(res);
        });

    });
    return util.bindPromise(prom, callback);

};

Query.prototype.exec = function(callback) {
    throw new Error('Unimplemented exec');
    // TODO iterator version
};

Query.createOperator = function(name, method) {
    if(name[0] !== '$') {
        name = '$'+name;
    }
    if(customOperators[name]) {
        throw new Error('There is already an operator with name '+name);
    }
    if(typeof method !== 'function') {
        throw new Error('Provided operator is not a function');
    }
    if(method.length < 2) {
        throw new Error('Operators require at least two parameters (target and query)');
    }
    customOperators[name] = method;
};

Query.apply = function(query, model, callback) {
    var mainPromise = new Promise(function (resolve, reject) {

        var prom;
        if(typeof model === 'string') {
            prom = Kind.get(model);
        } else {
            prom = Promise.resolve(model);
        }

        prom.then(function(kindModel) {

            kindModel.aggregate([
                {
                    $match: query
                },
                {
                    $project: {
                        _id: 1
                    }
                }
            ], function (err, result) {
                if (err) {
                    return reject(err);
                }
                resolve(result.map(extractId))
            });

        }, reject);

    });
    util.bindPromise(mainPromise, callback);
    return mainPromise;
};

/*
 Full complex query example

 {
 target: "entry",
 query: {
 $and: [
 {
 kind: "bp",
 query: {
 low: {$gt: 50, $lt: 100}
 }
 },
 {
 kind: "iupac",
 query: {
 value: {$regex: "benzen"}
 }
 },
 {
 target: "ir",
 query: {
 kind: "irLine",
 query: {
 value: {$gt: 3490, $lt: 3500}
 }
 }
 }
 ]
 },
 self: true,
 skip: 5,
 limit: 10
 };

 */

function handleQuery(fullQuery, logs) {
    return doQuery(fullQuery.target, fullQuery.query, logs);
}

function doQuery(target, query, logs) {

    /*
     Query can be one of :
     - basic (kind)
     - combination ($and, $or...)
     - projection (target)
     */

    var prom,
        type,
        logs2 = [];
    var time = Date.now();

    if (query.kind) {
        prom = basicQuery(target, query);
        type = 'basic';
    }

    if (query.target) {
        prom = handleQuery(query, logs2).then(function (projectionResult) {
            return basicQuery(target, {
                kind: query.target,
                query: {
                    _id: {
                        $in: projectionResult
                    }
                }
            }, true);
        });
        type = 'complex';
    }

    // Search for operator
    for(var i in query) {
        if(i[0] === '$' && customOperators[i]) {
            prom = customOperators[i](target, query[i], logs2);
            type = i;
        }
    }

    if(prom) {
        return prom.then(function(result) {
            logs.push({
                type: type,
                target: target,
                query: query,
                time: Date.now() - time,
                total: result.length,
                logs: logs2
            });
            return result;
        });
    }
    // Unable to process query
    return Promise.reject('Query could not be processed: ', JSON.stringify(query));

}

function basicQuery(target, query, forceMatch) {
    return new Promise(function (resolve, reject) {

        var kindModel = Kind.getSync(query.kind);

        var match = {
            '_an.kind': target
        };

        for (var i in query.query) {
            if (!forceMatch && i[0] === '_') {
                return reject('Forbidden field in projection query : ' + i)
            }
            match[i] = query.query[i];
        }

        kindModel.aggregate([
            {
                $match: match
            },
            {
                $project: {
                    _an: 1,
                    _id: 0
                }
            },
            {
                $unwind: '$_an'
            },
            {
                $match: {
                    '_an.kind': target
                }
            },
            {
                $group: {
                    _id: '$_an.id'
                }
            }
        ], function (err, result) {
            if (err) {
                return reject(err);
            }
            resolve(result.map(extractId))
        });

    });
}

function combineQueryAnd(target, query, logs) {
    return new Promise(function (resolve, reject) {

        var results = new Array(query.length);
        for (var i = 0; i < query.length; i++) {
            results[i] = doQuery(target, query[i], logs);
        }

        Promise.all(results).then(function (results) {
            results = results.map(toIdIn);
            var targetModel = Kind.getSync(target);
            targetModel.aggregate([
                {
                    $match: { $and: results }
                },
                {
                    $project: {
                        _id: 1
                    }
                }
            ], function (err, result) {
                if (err) {
                    return reject(err);
                }
                resolve(result.map(extractId));
            });
        }, reject);

    });
}

function combineQueryOr(target, query, logs) {
    return new Promise(function (resolve, reject) {

        var results = new Array(query.length);
        for (var i = 0; i < query.length; i++) {
            results[i] = doQuery(target, query[i], logs);
        }

        Promise.all(results).then(function (results) {
            results = results.map(toIdIn);
            var targetModel = Kind.getSync(target);
            targetModel.aggregate([
                {
                    $match: { $or: results }
                },
                {
                    $project: {
                        _id: 1
                    }
                }
            ], function (err, result) {
                if (err) {
                    return reject(err);
                }
                resolve(result.map(extractId));
            });
        }, reject);

    });
}

function combineQueryElseOr(target, query, logs) {
    return new Promise(function (resolve, reject) {

        function tryNextQuery() {
            var subQuery = query.shift();
            if (subQuery) {
                doQuery(target, subQuery, logs).then(function (result) {
                    if (result.length) {
                        resolve(result);
                    } else {
                        tryNextQuery();
                    }
                }, reject);
            } else {
                resolve([]);
            }
        }

        tryNextQuery();

    });
}

function projectQuery(target, query){throw new Error('Unimplemented project entry')} // TODO handle project query

function extractId(val) {
    return val._id;
}

function toIdIn(result) {
    return {
        _id: {$in: result}
    };
}

Query.createOperator('$and', combineQueryAnd);
Query.createOperator('$or', combineQueryOr);
Query.createOperator('$elseOr', combineQueryElseOr);
Query.createOperator('$project', projectQuery);

module.exports = Query;<commit_msg>add first level sort capability to queries<commit_after>'use strict';

var Promise = require('bluebird'),
    Kind = require('./kind'),
    extend = require('extend'),
    util = require('./util');

var customOperators = {};

var defaultOptions = {
    skip: 0,
    limit: Infinity,
    plain: true,
    log: false,
    sort: false
};

var Query = function Query(target, query, options) {

    this._targetModel = Kind.getSync(target);
    this._options = extend({}, defaultOptions, options);
    this._logs = [];

    this._executionPromise = null;
    this._target = target;
    this._query = query;
    this._executed = false;

};

Query.prototype._exec = function () {
    if(this._executed) {
        return;
    }
    this._executionPromise = handleQuery({
        target: this._target,
        query: this._query
    }, this._logs);
    this._executed = true;
};

Query.prototype._log = function (result) {
    if(this._options.log) {
        return {
            result: result,
            logs: this._logs
        };
    } else {
        return result;
    }
};

Query.prototype.count = function (callback) {

    this._exec();

    var self = this;
    var prom = this._executionPromise.then(function (result) {
        return self._log(result.length);
    });
    return util.bindPromise(prom, callback);

};

Query.prototype.all = function (callback) {

    this._exec();

    var self = this;
    var prom = this._executionPromise.then(function (result) {

        result = result.slice(self._options.skip, self._options.skip + self._options.limit);

        var query = self._targetModel.find({
            _id: {
                $in: result
            }
        });

        if(self._options.sort) {
            query.sort(self._options.sort);
        }

        if(self._options.plain) {
            query.lean();
        }

        return query.exec().then(function(res) {
            return self._log(res);
        });

    });
    return util.bindPromise(prom, callback);

};

Query.prototype.exec = function(callback) {
    throw new Error('Unimplemented exec');
    // TODO iterator version
};

Query.createOperator = function(name, method) {
    if(name[0] !== '$') {
        name = '$'+name;
    }
    if(customOperators[name]) {
        throw new Error('There is already an operator with name '+name);
    }
    if(typeof method !== 'function') {
        throw new Error('Provided operator is not a function');
    }
    if(method.length < 2) {
        throw new Error('Operators require at least two parameters (target and query)');
    }
    customOperators[name] = method;
};

Query.apply = function(query, model, callback) {
    var mainPromise = new Promise(function (resolve, reject) {

        var prom;
        if(typeof model === 'string') {
            prom = Kind.get(model);
        } else {
            prom = Promise.resolve(model);
        }

        prom.then(function(kindModel) {

            kindModel.aggregate([
                {
                    $match: query
                },
                {
                    $project: {
                        _id: 1
                    }
                }
            ], function (err, result) {
                if (err) {
                    return reject(err);
                }
                resolve(result.map(extractId))
            });

        }, reject);

    });
    util.bindPromise(mainPromise, callback);
    return mainPromise;
};

/*
 Full complex query example

 {
 target: "entry",
 query: {
 $and: [
 {
 kind: "bp",
 query: {
 low: {$gt: 50, $lt: 100}
 }
 },
 {
 kind: "iupac",
 query: {
 value: {$regex: "benzen"}
 }
 },
 {
 target: "ir",
 query: {
 kind: "irLine",
 query: {
 value: {$gt: 3490, $lt: 3500}
 }
 }
 }
 ]
 },
 self: true,
 skip: 5,
 limit: 10
 };

 */

function handleQuery(fullQuery, logs) {
    return doQuery(fullQuery.target, fullQuery.query, logs);
}

function doQuery(target, query, logs) {

    /*
     Query can be one of :
     - basic (kind)
     - combination ($and, $or...)
     - projection (target)
     */

    var prom,
        type,
        logs2 = [];
    var time = Date.now();

    if (query.kind) {
        prom = basicQuery(target, query);
        type = 'basic';
    }

    if (query.target) {
        prom = handleQuery(query, logs2).then(function (projectionResult) {
            return basicQuery(target, {
                kind: query.target,
                query: {
                    _id: {
                        $in: projectionResult
                    }
                }
            }, true);
        });
        type = 'complex';
    }

    // Search for operator
    for(var i in query) {
        if(i[0] === '$' && customOperators[i]) {
            prom = customOperators[i](target, query[i], logs2);
            type = i;
        }
    }

    if(prom) {
        return prom.then(function(result) {
            logs.push({
                type: type,
                target: target,
                query: query,
                time: Date.now() - time,
                total: result.length,
                logs: logs2
            });
            return result;
        });
    }
    // Unable to process query
    return Promise.reject('Query could not be processed: ', JSON.stringify(query));

}

function basicQuery(target, query, forceMatch) {
    return new Promise(function (resolve, reject) {

        var kindModel = Kind.getSync(query.kind);

        var match = {
            '_an.kind': target
        };

        for (var i in query.query) {
            if (!forceMatch && i[0] === '_') {
                return reject('Forbidden field in projection query : ' + i)
            }
            match[i] = query.query[i];
        }

        kindModel.aggregate([
            {
                $match: match
            },
            {
                $project: {
                    _an: 1,
                    _id: 0
                }
            },
            {
                $unwind: '$_an'
            },
            {
                $match: {
                    '_an.kind': target
                }
            },
            {
                $group: {
                    _id: '$_an.id'
                }
            }
        ], function (err, result) {
            if (err) {
                return reject(err);
            }
            resolve(result.map(extractId))
        });

    });
}

function combineQueryAnd(target, query, logs) {
    return new Promise(function (resolve, reject) {

        var results = new Array(query.length);
        for (var i = 0; i < query.length; i++) {
            results[i] = doQuery(target, query[i], logs);
        }

        Promise.all(results).then(function (results) {
            results = results.map(toIdIn);
            var targetModel = Kind.getSync(target);
            targetModel.aggregate([
                {
                    $match: { $and: results }
                },
                {
                    $project: {
                        _id: 1
                    }
                }
            ], function (err, result) {
                if (err) {
                    return reject(err);
                }
                resolve(result.map(extractId));
            });
        }, reject);

    });
}

function combineQueryOr(target, query, logs) {
    return new Promise(function (resolve, reject) {

        var results = new Array(query.length);
        for (var i = 0; i < query.length; i++) {
            results[i] = doQuery(target, query[i], logs);
        }

        Promise.all(results).then(function (results) {
            results = results.map(toIdIn);
            var targetModel = Kind.getSync(target);
            targetModel.aggregate([
                {
                    $match: { $or: results }
                },
                {
                    $project: {
                        _id: 1
                    }
                }
            ], function (err, result) {
                if (err) {
                    return reject(err);
                }
                resolve(result.map(extractId));
            });
        }, reject);

    });
}

function combineQueryElseOr(target, query, logs) {
    return new Promise(function (resolve, reject) {

        function tryNextQuery() {
            var subQuery = query.shift();
            if (subQuery) {
                doQuery(target, subQuery, logs).then(function (result) {
                    if (result.length) {
                        resolve(result);
                    } else {
                        tryNextQuery();
                    }
                }, reject);
            } else {
                resolve([]);
            }
        }

        tryNextQuery();

    });
}

function projectQuery(target, query){throw new Error('Unimplemented project entry')} // TODO handle project query

function extractId(val) {
    return val._id;
}

function toIdIn(result) {
    return {
        _id: {$in: result}
    };
}

Query.createOperator('$and', combineQueryAnd);
Query.createOperator('$or', combineQueryOr);
Query.createOperator('$elseOr', combineQueryElseOr);
Query.createOperator('$project', projectQuery);

module.exports = Query;<|endoftext|>


<commit_before>'use strict';

// First, check for JSON support
// If there is no JSON, we no-op the core features of Raven
// since JSON is required to encode the payload
var _Raven = window.Raven,
    hasJSON = !!(window.JSON && window.JSON.stringify),
    globalServer,
    globalUser,
    globalKey,
    globalProject,
    globalOptions = {
        logger: 'javascript',
        ignoreErrors: [],
        ignoreUrls: [],
        whitelistUrls: [],
        includePaths: [],
        tags: {},
        extra: {}
    };

var TK = TraceKit.noConflict();

// Disable Tracekit's remote fetching by default
TK.remoteFetching = false;

/*
 * The core Raven singleton
 *
 * @this {Raven}
 */
var Raven = {
    VERSION: '@VERSION',

    /*
     * Allow Raven to be configured as soon as it is loaded
     * It uses a global RavenConfig = {dsn: '...', config: {}}
     *
     * @return undefined
     */
    afterLoad: function() {
        var globalConfig = window.RavenConfig;
        if (globalConfig) {
            this.config(globalConfig.dsn, globalConfig.config).install();
        }
    },

    /*
     * Allow multiple versions of Raven to be installed.
     * Strip Raven from the global context and returns the instance.
     *
     * @return {Raven}
     */
    noConflict: function() {
        window.Raven = _Raven;
        return Raven;
    },

    /*
     * Configure Raven with a DSN and extra options
     *
     * @param {string} dsn The public Sentry DSN
     * @param {object} options Optional set of of global options [optional]
     * @return {Raven}
     */
    config: function(dsn, options) {
        var uri = parseUri(dsn),
            lastSlash = uri.path.lastIndexOf('/'),
            path = uri.path.substr(1, lastSlash);

        // merge in options
        if (options) {
            each(options, function(key, value){
                globalOptions[key] = value;
            });
        }

        // join regexp rules into one big rule
        globalOptions.ignoreUrls = globalOptions.ignoreUrls.length ? joinRegExp(globalOptions.ignoreUrls) : false;
        globalOptions.whitelistUrls = globalOptions.whitelistUrls.length ? joinRegExp(globalOptions.whitelistUrls) : false;
        globalOptions.includePaths = joinRegExp(globalOptions.includePaths);

        // "Script error." is hard coded into browsers for errors that it can't read.
        // this is the result of a script being pulled in from an external domain and CORS.
        globalOptions.ignoreErrors.push('Script error.');
        globalOptions.ignoreErrors.push('Script error');

        globalKey = uri.user;
        globalProject = ~~uri.path.substr(lastSlash + 1);

        // assemble the endpoint from the uri pieces
        globalServer = '//' + uri.host +
                      (uri.port ? ':' + uri.port : '') +
                      '/' + path + 'api/' + globalProject + '/store/';

        if (uri.protocol) {
            globalServer = uri.protocol + ':' + globalServer;
        }

        if (globalOptions.fetchContext) {
            TK.remoteFetching = true;
        }

        if (globalOptions.linesOfContext) {
            TK.linesOfContext = globalOptions.linesOfContext;
        }

        // return for chaining
        return Raven;
    },

    /*
     * Installs a global window.onerror error handler
     * to capture and report uncaught exceptions.
     * At this point, install() is required to be called due
     * to the way TraceKit is set up.
     *
     * @return {Raven}
     */
    install: function() {
        if (!isSetup()) return;

        TK.report.subscribe(handleStackInfo);

        return Raven;
    },

    /*
     * Wrap code within a context so Raven can capture errors
     * reliably across domains that is executed immediately.
     *
     * @param {object} options A specific set of options for this context [optional]
     * @param {function} func The callback to be immediately executed within the context
     * @param {array} args An array of arguments to be called with the callback [optional]
     */
    context: function(options, func, args) {
        if (isFunction(options)) {
            args = func || [];
            func = options;
            options = undefined;
        }

        Raven.wrap(options, func).apply(this, args);
    },

    /*
     * Wrap code within a context and returns back a new function to be executed
     *
     * @param {object} options A specific set of options for this context [optional]
     * @param {function} func The function to be wrapped in a new context
     * @return {function} The newly wrapped functions with a context
     */
    wrap: function(options, func) {
        // options is optional
        if (isFunction(options)) {
            func = options;
            options = undefined;
        }

        var property,
            wrappedFunction = function() {
                try {
                    return func.apply(this, arguments);
                } catch(e) {
                    Raven.captureException(e, options);
                    throw e;
                }
            };

        for (property in func) {
            if (func.hasOwnProperty(property)) {
                wrappedFunction[property] = func[property];
            }
        }

        return wrappedFunction;
    },

    /*
     * Uninstalls the global error handler.
     *
     * @return {Raven}
     */
    uninstall: function() {
        TK.report.unsubscribe(handleStackInfo);

        return Raven;
    },

    /*
     * Manually capture an exception and send it over to Sentry
     *
     * @param {error} ex An exception to be logged
     * @param {object} options A specific set of options for this error [optional]
     * @return {Raven}
     */
    captureException: function(ex, options) {
        // If a string is passed through, recall as a message
        if (typeof ex === 'string') {
            return Raven.captureMessage(ex, options);
        }

        // TraceKit.report will re-raise any exception passed to it,
        // which means you have to wrap it in try/catch. Instead, we
        // can wrap it here and only re-raise if TraceKit.report
        // raises an exception different from the one we asked to
        // report on.
        try {
            TK.report(ex, options);
        } catch(ex1) {
            if(ex !== ex1) {
                throw ex1;
            }
        }

        return Raven;
    },

    /*
     * Manually send a message to Sentry
     *
     * @param {string} msg A plain message to be captured in Sentry
     * @param {object} options A specific set of options for this message [optional]
     * @return {Raven}
     */
    captureMessage: function(msg, options) {
        // Fire away!
        send(
            arrayMerge({
                message: msg
            }, options)
        );

        return Raven;
    },

    /*
     * Set/clear a user to be sent along with the payload.
     *
     * @param {object} user An object representing user data [optional]
     * @return {Raven}
     */
    setUser: function(user) {
       globalUser = user;

       return Raven;
    }
};

function triggerEvent(eventType, options) {
    var event, key;

    eventType = 'raven' + eventType.substr(0,1).toUpperCase() + eventType.substr(1);

    if (document.createEvent) {
        event = document.createEvent('HTMLEvents');
        event.initEvent(eventType, true, true);
    } else {
        event = document.createEventObject();
        event.eventType = eventType;
    }

    if (typeof options !== 'object') {
        options = {};
    }

    for (key in options) if (options.hasOwnProperty(key)) {
        event[key] = options[key];
    }

    if (document.createEvent) {
        // IE9 if standards
        document.dispatchEvent(event);
    } else {
        // IE8 regardless of Quirks or Standards
        // IE9 if quirks
        try {
            document.fireEvent('on' + event.eventType.toLowerCase(), event);
        } catch(e) {}
    }
}

var uriKeys = 'source protocol authority userInfo user password host port relative path directory file query anchor'.split(' '),
    uriPattern = /^(?:(?![^:@]+:[^:@\/]*@)([^:\/?#.]+):)?(?:\/\/)?((?:(([^:@]*)(?::([^:@]*))?)?@)?([^:\/?#]*)(?::(\d*))?)(((\/(?:[^?#](?![^?#\/]*\.[^?#\/.]+(?:[?#]|$)))*\/?)?([^?#\/]*))(?:\?([^#]*))?(?:#(.*))?)/;

/**** Private functions ****/
function parseUri(str) {
    var m = uriPattern.exec(str),
        uri = {},
        i = 14;

    while (i--) uri[uriKeys[i]] = m[i] || '';

    return uri;
}

function isUndefined(what) {
    return typeof what === 'undefined';
}

function isFunction(what) {
    return typeof what === 'function';
}

function each(obj, callback) {
    var i, j;

    if (isUndefined(obj.length)) {
        for (i in obj) {
            if (obj.hasOwnProperty(i)) {
                callback.call(null, i, obj[i]);
            }
        }
    } else {
        j = obj.length;
        if (j) {
            for (i = 0; i < j; i++) {
                callback.call(null, i, obj[i]);
            }
        }
    }
}

var cachedAuth;

function getAuthQueryString() {
    if (cachedAuth) return cachedAuth;

    var qs = [
        'sentry_version=3',
        'sentry_client=raven-js/' + Raven.VERSION
    ];
    if (globalKey) {
        qs.push('sentry_key=' + globalKey);
    }

    cachedAuth = '?' + qs.join('&');
    return cachedAuth;
}

function handleStackInfo(stackInfo, options) {
    var frames = [];

    if (stackInfo.stack && stackInfo.stack.length) {
        each(stackInfo.stack, function(i, stack) {
            var frame = normalizeFrame(stack);
            if (frame) {
                frames.push(frame);
            }
        });
    }

    triggerEvent('handle', {
        stackInfo: stackInfo,
        options: options
    });

    processException(
        stackInfo.name,
        stackInfo.message,
        stackInfo.url,
        stackInfo.lineno,
        frames,
        options
    );
}

function normalizeFrame(frame) {
    if (!frame.url) return;

    // normalize the frames data
    var normalized = {
        filename:   frame.url,
        lineno:     frame.line,
        colno:      frame.column,
        'function': frame.func || '?'
    }, context = extractContextFromFrame(frame), i;

    if (context) {
        var keys = ['pre_context', 'context_line', 'post_context'];
        i = 3;
        while (i--) normalized[keys[i]] = context[i];
    }

    normalized.in_app = !( // determine if an exception came from outside of our app
        // first we check the global includePaths list.
        !globalOptions.includePaths.test(normalized.filename) ||
        // Now we check for fun, if the function name is Raven or TraceKit
        /(Raven|TraceKit)\./.test(normalized['function']) ||
        // finally, we do a last ditch effort and check for raven.min.js
        /raven\.(min\.)js$/.test(normalized.filename)
    );

    return normalized;
}

function extractContextFromFrame(frame) {
    // immediately check if we should even attempt to parse a context
    if (!frame.context || !globalOptions.fetchContext) return;

    var context = frame.context,
        pivot = ~~(context.length / 2),
        i = context.length, isMinified = false;

    while (i--) {
        // We're making a guess to see if the source is minified or not.
        // To do that, we make the assumption if *any* of the lines passed
        // in are greater than 300 characters long, we bail.
        // Sentry will see that there isn't a context
        if (context[i].length > 300) {
            isMinified = true;
            break;
        }
    }

    if (isMinified) {
        // The source is minified and we don't know which column. Fuck it.
        if (isUndefined(frame.column)) return;

        // If the source is minified and has a frame column
        // we take a chunk of the offending line to hopefully shed some light
        return [
            [],  // no pre_context
            context[pivot].substr(frame.column, 50), // grab 50 characters, starting at the offending column
            []   // no post_context
        ];
    }

    return [
        context.slice(0, pivot),    // pre_context
        context[pivot],             // context_line
        context.slice(pivot + 1)    // post_context
    ];
}

function processException(type, message, fileurl, lineno, frames, options) {
    var stacktrace, label, i;

    // IE8 really doesn't have Array.prototype.indexOf
    // Filter out a message that matches our ignore list
    i = globalOptions.ignoreErrors.length;
    while (i--) {
        if (message === globalOptions.ignoreErrors[i]) {
            return;
        }
    }

    if (frames && frames.length) {
        stacktrace = {frames: frames};
        fileurl = fileurl || frames[0].filename;
    } else if (fileurl) {
        stacktrace = {
            frames: [{
                filename: fileurl,
                lineno: lineno
            }]
        };
    }

    if (globalOptions.ignoreUrls && globalOptions.ignoreUrls.test(fileurl)) return;
    if (globalOptions.whitelistUrls && !globalOptions.whitelistUrls.test(fileurl)) return;

    label = lineno ? message + ' at ' + lineno : message;

    // Fire away!
    send(
        arrayMerge({
            'sentry.interfaces.Exception': {
                type: type,
                value: message
            },
            'sentry.interfaces.Stacktrace': stacktrace,
            culprit: fileurl,
            message: label
        }, options)
    );
}

function arrayMerge(arr1, arr2) {
    if (!arr2) {
        return arr1;
    }
    each(arr2, function(key, value){
        arr1[key] = value;
    });
    return arr1;
}

function getHttpData() {
    var http = {
        url: document.location.href,
        headers: {
            'User-Agent': navigator.userAgent
        }
    };

    if (document.referrer) {
        http.headers.Referer = document.referrer;
    }

    return http;
}

function send(data) {
    if (!isSetup()) return;

    data = arrayMerge({
        project: globalProject,
        logger: globalOptions.logger,
        site: globalOptions.site,
        platform: 'javascript',
        'sentry.interfaces.Http': getHttpData()
    }, data);

    // Merge in the tags and extra separately since arrayMerge doesn't handle a deep merge
    data.tags = arrayMerge(globalOptions.tags, data.tags);
    data.extra = arrayMerge(globalOptions.extra, data.extra);

    // If there are no tags/extra, strip the key from the payload alltogther.
    if (!data.tags) delete data.tags;
    if (!data.extra) delete data.extra;

    if (globalUser) data['sentry.interfaces.User'] = globalUser;

    if (isFunction(globalOptions.dataCallback)) {
        data = globalOptions.dataCallback(data);
    }

    // Check if the request should be filtered or not
    if (isFunction(globalOptions.shouldSendCallback) && !globalOptions.shouldSendCallback(data)) {
        return;
    }

    makeRequest(data);
}

function makeRequest(data) {
    var img, src;

    function success() {
        triggerEvent('success', {
            data: data,
            src: src
        });
    }

    function failure() {
        triggerEvent('failure', {
            data: data,
            src: src
        });
    }

    src = globalServer + getAuthQueryString() + '&sentry_data=' + encodeURIComponent(JSON.stringify(data));
    img = new Image();
    img.onload = success;
    img.onerror = failure;
    img.onabort = failure;
    img.src = src;
}

function isSetup() {
    if (!hasJSON) return false;  // needs JSON support
    if (!globalServer) {
        if (window.console && console.error) {
            console.error("Error: Raven has not been configured.");
        }
        return false;
    }
    return true;
}

function wrapArguments(what) {
    if (!isFunction(what)) return what;

    function wrapped() {
        var args = [], i = arguments.length, arg;
        while(i--) {
            arg = arguments[i];
            args[i] = isFunction(arg) ? Raven.wrap(arg) : arg;
        }
        what.apply(null, args);
    }
    // copy over properties of the old function
    for (var k in what) wrapped[k] = what[k];
    return wrapped;
}

function joinRegExp(patterns) {
    // Combine an array of regular expressions into one large regexp
    var sources = [], i = patterns.length;
    // lol, map
    while (i--) sources[i] = patterns[i].source;
    return new RegExp(sources.join('|'), 'i');
}

Raven.afterLoad();
<commit_msg>Return result of wrapped function from context<commit_after>'use strict';

// First, check for JSON support
// If there is no JSON, we no-op the core features of Raven
// since JSON is required to encode the payload
var _Raven = window.Raven,
    hasJSON = !!(window.JSON && window.JSON.stringify),
    globalServer,
    globalUser,
    globalKey,
    globalProject,
    globalOptions = {
        logger: 'javascript',
        ignoreErrors: [],
        ignoreUrls: [],
        whitelistUrls: [],
        includePaths: [],
        tags: {},
        extra: {}
    };

var TK = TraceKit.noConflict();

// Disable Tracekit's remote fetching by default
TK.remoteFetching = false;

/*
 * The core Raven singleton
 *
 * @this {Raven}
 */
var Raven = {
    VERSION: '@VERSION',

    /*
     * Allow Raven to be configured as soon as it is loaded
     * It uses a global RavenConfig = {dsn: '...', config: {}}
     *
     * @return undefined
     */
    afterLoad: function() {
        var globalConfig = window.RavenConfig;
        if (globalConfig) {
            this.config(globalConfig.dsn, globalConfig.config).install();
        }
    },

    /*
     * Allow multiple versions of Raven to be installed.
     * Strip Raven from the global context and returns the instance.
     *
     * @return {Raven}
     */
    noConflict: function() {
        window.Raven = _Raven;
        return Raven;
    },

    /*
     * Configure Raven with a DSN and extra options
     *
     * @param {string} dsn The public Sentry DSN
     * @param {object} options Optional set of of global options [optional]
     * @return {Raven}
     */
    config: function(dsn, options) {
        var uri = parseUri(dsn),
            lastSlash = uri.path.lastIndexOf('/'),
            path = uri.path.substr(1, lastSlash);

        // merge in options
        if (options) {
            each(options, function(key, value){
                globalOptions[key] = value;
            });
        }

        // join regexp rules into one big rule
        globalOptions.ignoreUrls = globalOptions.ignoreUrls.length ? joinRegExp(globalOptions.ignoreUrls) : false;
        globalOptions.whitelistUrls = globalOptions.whitelistUrls.length ? joinRegExp(globalOptions.whitelistUrls) : false;
        globalOptions.includePaths = joinRegExp(globalOptions.includePaths);

        // "Script error." is hard coded into browsers for errors that it can't read.
        // this is the result of a script being pulled in from an external domain and CORS.
        globalOptions.ignoreErrors.push('Script error.');
        globalOptions.ignoreErrors.push('Script error');

        globalKey = uri.user;
        globalProject = ~~uri.path.substr(lastSlash + 1);

        // assemble the endpoint from the uri pieces
        globalServer = '//' + uri.host +
                      (uri.port ? ':' + uri.port : '') +
                      '/' + path + 'api/' + globalProject + '/store/';

        if (uri.protocol) {
            globalServer = uri.protocol + ':' + globalServer;
        }

        if (globalOptions.fetchContext) {
            TK.remoteFetching = true;
        }

        if (globalOptions.linesOfContext) {
            TK.linesOfContext = globalOptions.linesOfContext;
        }

        // return for chaining
        return Raven;
    },

    /*
     * Installs a global window.onerror error handler
     * to capture and report uncaught exceptions.
     * At this point, install() is required to be called due
     * to the way TraceKit is set up.
     *
     * @return {Raven}
     */
    install: function() {
        if (!isSetup()) return;

        TK.report.subscribe(handleStackInfo);

        return Raven;
    },

    /*
     * Wrap code within a context so Raven can capture errors
     * reliably across domains that is executed immediately.
     *
     * @param {object} options A specific set of options for this context [optional]
     * @param {function} func The callback to be immediately executed within the context
     * @param {array} args An array of arguments to be called with the callback [optional]
     */
    context: function(options, func, args) {
        if (isFunction(options)) {
            args = func || [];
            func = options;
            options = undefined;
        }

        return Raven.wrap(options, func).apply(this, args);
    },

    /*
     * Wrap code within a context and returns back a new function to be executed
     *
     * @param {object} options A specific set of options for this context [optional]
     * @param {function} func The function to be wrapped in a new context
     * @return {function} The newly wrapped functions with a context
     */
    wrap: function(options, func) {
        // options is optional
        if (isFunction(options)) {
            func = options;
            options = undefined;
        }

        var property,
            wrappedFunction = function() {
                try {
                    return func.apply(this, arguments);
                } catch(e) {
                    Raven.captureException(e, options);
                    throw e;
                }
            };

        for (property in func) {
            if (func.hasOwnProperty(property)) {
                wrappedFunction[property] = func[property];
            }
        }

        return wrappedFunction;
    },

    /*
     * Uninstalls the global error handler.
     *
     * @return {Raven}
     */
    uninstall: function() {
        TK.report.unsubscribe(handleStackInfo);

        return Raven;
    },

    /*
     * Manually capture an exception and send it over to Sentry
     *
     * @param {error} ex An exception to be logged
     * @param {object} options A specific set of options for this error [optional]
     * @return {Raven}
     */
    captureException: function(ex, options) {
        // If a string is passed through, recall as a message
        if (typeof ex === 'string') {
            return Raven.captureMessage(ex, options);
        }

        // TraceKit.report will re-raise any exception passed to it,
        // which means you have to wrap it in try/catch. Instead, we
        // can wrap it here and only re-raise if TraceKit.report
        // raises an exception different from the one we asked to
        // report on.
        try {
            TK.report(ex, options);
        } catch(ex1) {
            if(ex !== ex1) {
                throw ex1;
            }
        }

        return Raven;
    },

    /*
     * Manually send a message to Sentry
     *
     * @param {string} msg A plain message to be captured in Sentry
     * @param {object} options A specific set of options for this message [optional]
     * @return {Raven}
     */
    captureMessage: function(msg, options) {
        // Fire away!
        send(
            arrayMerge({
                message: msg
            }, options)
        );

        return Raven;
    },

    /*
     * Set/clear a user to be sent along with the payload.
     *
     * @param {object} user An object representing user data [optional]
     * @return {Raven}
     */
    setUser: function(user) {
       globalUser = user;

       return Raven;
    }
};

function triggerEvent(eventType, options) {
    var event, key;

    eventType = 'raven' + eventType.substr(0,1).toUpperCase() + eventType.substr(1);

    if (document.createEvent) {
        event = document.createEvent('HTMLEvents');
        event.initEvent(eventType, true, true);
    } else {
        event = document.createEventObject();
        event.eventType = eventType;
    }

    if (typeof options !== 'object') {
        options = {};
    }

    for (key in options) if (options.hasOwnProperty(key)) {
        event[key] = options[key];
    }

    if (document.createEvent) {
        // IE9 if standards
        document.dispatchEvent(event);
    } else {
        // IE8 regardless of Quirks or Standards
        // IE9 if quirks
        try {
            document.fireEvent('on' + event.eventType.toLowerCase(), event);
        } catch(e) {}
    }
}

var uriKeys = 'source protocol authority userInfo user password host port relative path directory file query anchor'.split(' '),
    uriPattern = /^(?:(?![^:@]+:[^:@\/]*@)([^:\/?#.]+):)?(?:\/\/)?((?:(([^:@]*)(?::([^:@]*))?)?@)?([^:\/?#]*)(?::(\d*))?)(((\/(?:[^?#](?![^?#\/]*\.[^?#\/.]+(?:[?#]|$)))*\/?)?([^?#\/]*))(?:\?([^#]*))?(?:#(.*))?)/;

/**** Private functions ****/
function parseUri(str) {
    var m = uriPattern.exec(str),
        uri = {},
        i = 14;

    while (i--) uri[uriKeys[i]] = m[i] || '';

    return uri;
}

function isUndefined(what) {
    return typeof what === 'undefined';
}

function isFunction(what) {
    return typeof what === 'function';
}

function each(obj, callback) {
    var i, j;

    if (isUndefined(obj.length)) {
        for (i in obj) {
            if (obj.hasOwnProperty(i)) {
                callback.call(null, i, obj[i]);
            }
        }
    } else {
        j = obj.length;
        if (j) {
            for (i = 0; i < j; i++) {
                callback.call(null, i, obj[i]);
            }
        }
    }
}

var cachedAuth;

function getAuthQueryString() {
    if (cachedAuth) return cachedAuth;

    var qs = [
        'sentry_version=3',
        'sentry_client=raven-js/' + Raven.VERSION
    ];
    if (globalKey) {
        qs.push('sentry_key=' + globalKey);
    }

    cachedAuth = '?' + qs.join('&');
    return cachedAuth;
}

function handleStackInfo(stackInfo, options) {
    var frames = [];

    if (stackInfo.stack && stackInfo.stack.length) {
        each(stackInfo.stack, function(i, stack) {
            var frame = normalizeFrame(stack);
            if (frame) {
                frames.push(frame);
            }
        });
    }

    triggerEvent('handle', {
        stackInfo: stackInfo,
        options: options
    });

    processException(
        stackInfo.name,
        stackInfo.message,
        stackInfo.url,
        stackInfo.lineno,
        frames,
        options
    );
}

function normalizeFrame(frame) {
    if (!frame.url) return;

    // normalize the frames data
    var normalized = {
        filename:   frame.url,
        lineno:     frame.line,
        colno:      frame.column,
        'function': frame.func || '?'
    }, context = extractContextFromFrame(frame), i;

    if (context) {
        var keys = ['pre_context', 'context_line', 'post_context'];
        i = 3;
        while (i--) normalized[keys[i]] = context[i];
    }

    normalized.in_app = !( // determine if an exception came from outside of our app
        // first we check the global includePaths list.
        !globalOptions.includePaths.test(normalized.filename) ||
        // Now we check for fun, if the function name is Raven or TraceKit
        /(Raven|TraceKit)\./.test(normalized['function']) ||
        // finally, we do a last ditch effort and check for raven.min.js
        /raven\.(min\.)js$/.test(normalized.filename)
    );

    return normalized;
}

function extractContextFromFrame(frame) {
    // immediately check if we should even attempt to parse a context
    if (!frame.context || !globalOptions.fetchContext) return;

    var context = frame.context,
        pivot = ~~(context.length / 2),
        i = context.length, isMinified = false;

    while (i--) {
        // We're making a guess to see if the source is minified or not.
        // To do that, we make the assumption if *any* of the lines passed
        // in are greater than 300 characters long, we bail.
        // Sentry will see that there isn't a context
        if (context[i].length > 300) {
            isMinified = true;
            break;
        }
    }

    if (isMinified) {
        // The source is minified and we don't know which column. Fuck it.
        if (isUndefined(frame.column)) return;

        // If the source is minified and has a frame column
        // we take a chunk of the offending line to hopefully shed some light
        return [
            [],  // no pre_context
            context[pivot].substr(frame.column, 50), // grab 50 characters, starting at the offending column
            []   // no post_context
        ];
    }

    return [
        context.slice(0, pivot),    // pre_context
        context[pivot],             // context_line
        context.slice(pivot + 1)    // post_context
    ];
}

function processException(type, message, fileurl, lineno, frames, options) {
    var stacktrace, label, i;

    // IE8 really doesn't have Array.prototype.indexOf
    // Filter out a message that matches our ignore list
    i = globalOptions.ignoreErrors.length;
    while (i--) {
        if (message === globalOptions.ignoreErrors[i]) {
            return;
        }
    }

    if (frames && frames.length) {
        stacktrace = {frames: frames};
        fileurl = fileurl || frames[0].filename;
    } else if (fileurl) {
        stacktrace = {
            frames: [{
                filename: fileurl,
                lineno: lineno
            }]
        };
    }

    if (globalOptions.ignoreUrls && globalOptions.ignoreUrls.test(fileurl)) return;
    if (globalOptions.whitelistUrls && !globalOptions.whitelistUrls.test(fileurl)) return;

    label = lineno ? message + ' at ' + lineno : message;

    // Fire away!
    send(
        arrayMerge({
            'sentry.interfaces.Exception': {
                type: type,
                value: message
            },
            'sentry.interfaces.Stacktrace': stacktrace,
            culprit: fileurl,
            message: label
        }, options)
    );
}

function arrayMerge(arr1, arr2) {
    if (!arr2) {
        return arr1;
    }
    each(arr2, function(key, value){
        arr1[key] = value;
    });
    return arr1;
}

function getHttpData() {
    var http = {
        url: document.location.href,
        headers: {
            'User-Agent': navigator.userAgent
        }
    };

    if (document.referrer) {
        http.headers.Referer = document.referrer;
    }

    return http;
}

function send(data) {
    if (!isSetup()) return;

    data = arrayMerge({
        project: globalProject,
        logger: globalOptions.logger,
        site: globalOptions.site,
        platform: 'javascript',
        'sentry.interfaces.Http': getHttpData()
    }, data);

    // Merge in the tags and extra separately since arrayMerge doesn't handle a deep merge
    data.tags = arrayMerge(globalOptions.tags, data.tags);
    data.extra = arrayMerge(globalOptions.extra, data.extra);

    // If there are no tags/extra, strip the key from the payload alltogther.
    if (!data.tags) delete data.tags;
    if (!data.extra) delete data.extra;

    if (globalUser) data['sentry.interfaces.User'] = globalUser;

    if (isFunction(globalOptions.dataCallback)) {
        data = globalOptions.dataCallback(data);
    }

    // Check if the request should be filtered or not
    if (isFunction(globalOptions.shouldSendCallback) && !globalOptions.shouldSendCallback(data)) {
        return;
    }

    makeRequest(data);
}

function makeRequest(data) {
    var img, src;

    function success() {
        triggerEvent('success', {
            data: data,
            src: src
        });
    }

    function failure() {
        triggerEvent('failure', {
            data: data,
            src: src
        });
    }

    src = globalServer + getAuthQueryString() + '&sentry_data=' + encodeURIComponent(JSON.stringify(data));
    img = new Image();
    img.onload = success;
    img.onerror = failure;
    img.onabort = failure;
    img.src = src;
}

function isSetup() {
    if (!hasJSON) return false;  // needs JSON support
    if (!globalServer) {
        if (window.console && console.error) {
            console.error("Error: Raven has not been configured.");
        }
        return false;
    }
    return true;
}

function wrapArguments(what) {
    if (!isFunction(what)) return what;

    function wrapped() {
        var args = [], i = arguments.length, arg;
        while(i--) {
            arg = arguments[i];
            args[i] = isFunction(arg) ? Raven.wrap(arg) : arg;
        }
        what.apply(null, args);
    }
    // copy over properties of the old function
    for (var k in what) wrapped[k] = what[k];
    return wrapped;
}

function joinRegExp(patterns) {
    // Combine an array of regular expressions into one large regexp
    var sources = [], i = patterns.length;
    // lol, map
    while (i--) sources[i] = patterns[i].source;
    return new RegExp(sources.join('|'), 'i');
}

Raven.afterLoad();
<|endoftext|>


<commit_before>// Raven.js
//
// Originally based on the Arecibo JavaScript client.
//
// Requires:
//     * Either jQuery (>1.5) or Zepto.js (>0.8).
//     * parseUri (included in the full and minified distribution files)

(function(){
    // Save a reference to the global object (`window` in the browser, `global`
    // on the server).
    var root = this;

    var Raven;
    root.Raven = Raven = {};

    var self = Raven;

    Raven.VERSION = '@VERSION';

    // jQuery, Zepto, or Ender owns the `$` variable.
    var $ = root.jQuery || root.Zepto || root.ender;

    Raven.loaded = false;
    Raven.options = {
        secretKey: undefined,  // The global key if not using project auth
        publicKey: undefined,  // Leave as undefined if not using project auth
        servers: [],
        projectId: 1,
        logger: 'javascript',
        site: undefined,
        signatureUrl: undefined,
        fetchHeaders: false,  // Generates a synchronous request to your server
        testMode: false,  // Disables some things that randomize the signature
        ignoreErrors: [],
        ignoreUrls: []
    };

    Raven.funcNameRE = /function\s*([\w\-$]+)?\s*\(/i;

    Raven.config = function(config) {
        var servers = [];

        if (typeof(config) === "string") {
            if (config.indexOf('http') === 0) {
                // new-style DSN configuration
                config = Raven.parseDSN(config);
            } else {
                throw "Base64 encoded config is no longer supported - use DSN";
            }
        }

        $.each(config, function(key, option) {
            self.options[key] = option;
        });

        // Expand server base URLs into API URLs
        $.each(self.options['servers'], function(i, server) {
            // Add a trailing slash if one isn't provided
            if (server.slice(-1) !== '/') {
                server += '/';
            }
            servers.push(server + 'api/' + self.options['projectId'] + '/store/');
        });
        self.options['servers'] = servers;

    };

    Raven.parseDSN = function(dsn) {
        var uri = parseUri(dsn);
        var path_idx = uri.path.lastIndexOf('/');
        var project_id;
        var path;

        if (path_idx === -1) {
            project_id = uri.path.substr(1);
            path = '';
        } else {
            path = uri.path.substr(1, path_idx);
            project_id = uri.path.substr(path_idx + 1);
        }

        return {
            servers: [uri.protocol + '://' + uri.host + ':' + uri.port + '/' + path],
            publicKey: uri.user,
            secretKey: uri.password,
            projectId: project_id
        };
    };

    Raven.getHeaders = function() {
        var headers = {};

        if (self.options.fetchHeaders && !self.options.testMode) {
            headers = $.ajax({type: 'HEAD', url: root.location, async: false})
                       .getAllResponseHeaders();
        }

        headers["Referer"] = document.referrer;
        headers["User-Agent"] = navigator.userAgent;
        return headers;
    };

    Raven.parseHeaders = function(headers_string) {
        /*
         * Parse the header string returned from getAllResponseHeaders
         */
        var headers = {};
        $.each(headers_string.split('\n'), function(i, header) {
            var name = header.slice(0, header.indexOf(':')),
                value = header.slice(header.indexOf(':') + 2);
            headers[name] = value;
        });
        return headers;
    };

    Raven.getSignature = function(message, timestamp, callback) {
        if (self.options.signatureUrl) {
            $.post(self.options.signatureUrl, {
                message: message, timestamp: timestamp
            }, function(data) {
                callback(data.signature);
            });
        } else {
            callback();
        }
    };

    Raven.getAuthHeader = function(signature, timestamp) {
        var header = "Sentry sentry_version=2.0, ";
        header += "sentry_timestamp=" + timestamp + ", ";
        header += "sentry_client=raven-js/" + self.VERSION;
        if (self.options.publicKey) {
            header += ", sentry_key=" + self.options.publicKey;
        }
        if (signature) {
            header += ", sentry_signature=" + signature;
        }
        return header;
    };

    Raven.captureException = function(e) {
        var lineno, traceback, fileurl;

        if (e.line) {  // WebKit
            lineno = e.line;
        } else if (e.lineNumber) {  // Mozilla
            lineno = e.lineNumber;
        }

        if (e.sourceURL) {  // Webkit
            fileurl = e.sourceURL;
        } else if (e.fileName) {  // Mozilla
            fileurl = e.fileName;
        }

        if (e.arguments && e.stack) {
            traceback = this.chromeTraceback(e);
        } else if (e.stack) {
            // Detect edge cases where Chrome doesn't have 'arguments'
            if (e.stack.indexOf('@') == -1) {
                traceback = this.chromeTraceback(e);
            } else {
                traceback = this.firefoxOrSafariTraceback(e);
            }
        } else {
            traceback = [{"filename": fileurl, "lineno": lineno}];
            traceback = traceback.concat(this.otherTraceback(arguments.callee));
        }

        self.process(e, fileurl, lineno, traceback);
    };

    Raven.captureMessage = function(msg) {
        self.process(msg);
    };

    Raven.trimString = function(str) {
        return str.replace(/^\s+|\s+$/g, "");
    };

    Raven.chromeTraceback = function(e) {
        /*
         * First line is simply the repeated message:
         *   ReferenceError: aldfjalksdjf is not defined
         *
         * Following lines contain error context:
         *   at http://localhost:9000/1/group/306:41:5
         */
        var chunks, fn, filename, lineno, fileBits,
            traceback = [],
            lines = e.stack.split('\n');
        $.each(lines.slice(1), function(i, line) {
            // Trim the 'at ' from the beginning, and split by spaces
            chunks = Raven.trimString(line).slice(3);
            if (chunks == "unknown source") {
                return;  // Skip this one
            } else {
                chunks = chunks.split(' ');
            }

            if (chunks.length > 2) {
                // If there are more than 2 chunks, there are spaces in the
                // filename
                fn = chunks[0];
                filename = chunks.slice(1).join(' ');
                lineno = '(unknown)';
            } else if (chunks.length == 2) {
                // If there are two chunks, the first one is the function name
                fn = chunks[0];
                filename = chunks[1];
            } else {
                fn = '(unknown)';
                filename = chunks[0];
            }

            if (filename && filename !== '(unknown source)') {
                if (filename[0] === '(') {
                    // Remove parentheses
                    filename = filename.slice(1, -1);
                }
                // filename should be: <scheme>://<uri>:<line>:<column>
                // where :<column> is optional
                fileBits = filename.split(':');
                lineno = fileBits[2];
                filename = fileBits.slice(0, 2).join(':');
            }

            traceback.push({
                'function': fn,
                'filename': filename,
                'lineno': lineno
            });
        });
        return traceback;
    };

    Raven.firefoxOrSafariTraceback = function(e) {
        /*
         * Each line is a function with args and a filename, separated by an ampersand.
         *   unsubstantiatedClaim("I am Batman")@http://raven-js.com/test/exception.js:7
         *
         * Anonymous functions are presented without a name, but including args.
         *   (66)@http://raven-js.com/test/vendor/qunit.js:418
         *
         */
        var chunks, fn, args, filename, lineno,
            traceback = [],
            lines = e.stack.split('\n');
        $.each(lines, function(i, line) {
            if (line) {
                chunks = line.split('@');
                if (chunks[0]) {
                    fn = chunks[0].split('(');

                    if (fn.length > 1 && fn[1] != ')') {
                        args = fn[1].slice(0, -1).split(',');
                    } else {
                        args = undefined;
                    }

                    if (fn[0]) {
                        fn = fn[0];
                    } else {
                        fn = '(unknown)';
                    }
                } else {
                    fn = '(unknown)';
                }

                if (chunks.length > 1) {
                    filename = chunks[1].split(':');
                    lineno = filename.slice(-1)[0];
                    filename = filename.slice(0, -1).join(':');
                } else if (chunks[0] == '[native code]') {
                    fn = '(unknown)';
                    filename = '[native code]';
                    lineno = 0;
                    args = undefined;
                }

                traceback.push({
                    'function': fn,
                    'filename': filename,
                    'lineno': lineno,
                    'vars': {'arguments': args}
                });
            }
        });
        return traceback;
    };

    Raven.otherTraceback = function(callee) {
        /*
         * Generates best-efforts tracebacks for other browsers, such as Safari
         * or IE.
         */
        var fn, args,
            ANON = '<anonymous>',
            traceback = [],
            max = 9;
        while (callee && traceback.length < max) {
            fn = callee.name || (this.funcNameRE.test(callee.toString()) ? RegExp.$1 || ANON : ANON);
            if (callee.arguments) {
                args = this.stringifyArguments(callee.arguments);
            } else {
                args = undefined;
            }
            traceback.push({
                'filename': '(unknown source)',
                'lineno': '(unknown)',
                'function': fn,
                'post_context': callee.toString().split('\n'),
                'vars': {'arguments': args}
            });
            callee = callee.caller;
        }
        return traceback;
    };

    Raven.stringifyArguments = function(args) {
        /*
         * Converts a callee's arguments to strings
         */
        var fn,
            self = this,
            UNKNOWN = '<unknown>',
            results = [];

        $.each(args, function(i, arg) {
            if (arg === undefined) {
                results.push('undefined');
            } else if (arg === null) {
                results.push('null');
            } else if (arg instanceof Array) {
                results.push(self.stringifyArguments(arg));
            } else if (arg.constructor) {
                fn = arg.constructor.name || (self.funcNameRE.test(arg.constructor.toString()) ? RegExp.$1 || UNKNOWN : UNKNOWN);
                if (fn == 'String') {
                    results.push('"' + arg + '"');
                } else if (fn == 'Number' || fn == 'Date') {
                    results.push(arg);
                } else if (fn == 'Boolean') {
                    results.push(arg ? 'true' : 'false');
                } else {
                    results.push(fn);
                }
            } else {
                results.push(UNKNOWN);
            }
        });

        return results;
    };

    Raven.process = function(message, fileurl, lineno, traceback, timestamp) {
        var label, stacktrace, data, encoded_msg, type,
            url = root.location.protocol + '//' + root.location.host + root.location.pathname,
            querystring = root.location.search.slice(1);  // Remove the ?

        if (typeof(message) === 'object') {
            type = message.name;
            message = message.message;
        }
        if ($.inArray(message, self.options.ignoreErrors) >= 0) {
            return;
        }

        for (var i = 0; i < self.options.ignoreUrls.length; i++) {
            if (self.options.ignoreUrls[i].test(fileurl)) {
                return;
            }
        }

        label = lineno ? message + " at " + lineno : message;

        if (traceback) {
            stacktrace = {"frames": traceback};
            fileurl = fileurl || traceback[0].filename;
        } else if (fileurl) {
            stacktrace = {
                "frames": [{
                    "filename": fileurl,
                    "lineno": lineno
                }]
            };
        }

        data = {
            "message": label,
            "culprit": fileurl,
            "sentry.interfaces.Stacktrace": stacktrace,
            "sentry.interfaces.Exception": {
                "type": type,
                "value": message
            },
            "project": self.options.projectId,
            "logger": self.options.logger,
            "site": self.options.site
        };

        data["sentry.interfaces.Http"] = {
            "url": url,
            "querystring": querystring,
            "headers": self.getHeaders()
        };

        timestamp = timestamp || (new Date()).getTime();
        encoded_msg = JSON.stringify(data);
        self.getSignature(encoded_msg, timestamp, function(signature) {
            var header = self.getAuthHeader(signature, timestamp);
            $.each(self.options.servers, function (i, server) {
                $.ajax({
                    type: 'POST',
                    url: server,
                    data: encoded_msg,
                    headers: {
                        // We send both headers, since Authentication may be blocked,
                        // and custom headers arent supported in IE9
                        'X-Sentry-Auth': header,
                        'Authentication': header
                    }
                });
            });
        });
    };
}).call(this);
<commit_msg>fixed/improved parsing of chrome stack traces by replacing the string parsing with a regex to add support for urls with :port in the url, as well as supporting scenarios like 'new child' as the functio name. This also forces the lineno to be a number, because getsentry bombs out when you give it a non-number for the line number<commit_after>// Raven.js
//
// Originally based on the Arecibo JavaScript client.
//
// Requires:
//     * Either jQuery (>1.5) or Zepto.js (>0.8).
//     * parseUri (included in the full and minified distribution files)

(function(){
    // Save a reference to the global object (`window` in the browser, `global`
    // on the server).
    var root = this;

    var Raven;
    root.Raven = Raven = {};

    var self = Raven;

    Raven.VERSION = '@VERSION';

    // jQuery, Zepto, or Ender owns the `$` variable.
    var $ = root.jQuery || root.Zepto || root.ender;

    Raven.loaded = false;
    Raven.options = {
        secretKey: undefined,  // The global key if not using project auth
        publicKey: undefined,  // Leave as undefined if not using project auth
        servers: [],
        projectId: 1,
        logger: 'javascript',
        site: undefined,
        signatureUrl: undefined,
        fetchHeaders: false,  // Generates a synchronous request to your server
        testMode: false,  // Disables some things that randomize the signature
        ignoreErrors: [],
        ignoreUrls: []
    };

    Raven.funcNameRE = /function\s*([\w\-$]+)?\s*\(/i;

    Raven.config = function(config) {
        var servers = [];

        if (typeof(config) === "string") {
            if (config.indexOf('http') === 0) {
                // new-style DSN configuration
                config = Raven.parseDSN(config);
            } else {
                throw "Base64 encoded config is no longer supported - use DSN";
            }
        }

        $.each(config, function(key, option) {
            self.options[key] = option;
        });

        // Expand server base URLs into API URLs
        $.each(self.options['servers'], function(i, server) {
            // Add a trailing slash if one isn't provided
            if (server.slice(-1) !== '/') {
                server += '/';
            }
            servers.push(server + 'api/' + self.options['projectId'] + '/store/');
        });
        self.options['servers'] = servers;

    };

    Raven.parseDSN = function(dsn) {
        var uri = parseUri(dsn);
        var path_idx = uri.path.lastIndexOf('/');
        var project_id;
        var path;

        if (path_idx === -1) {
            project_id = uri.path.substr(1);
            path = '';
        } else {
            path = uri.path.substr(1, path_idx);
            project_id = uri.path.substr(path_idx + 1);
        }

        return {
            servers: [uri.protocol + '://' + uri.host + ':' + uri.port + '/' + path],
            publicKey: uri.user,
            secretKey: uri.password,
            projectId: project_id
        };
    };

    Raven.getHeaders = function() {
        var headers = {};

        if (self.options.fetchHeaders && !self.options.testMode) {
            headers = $.ajax({type: 'HEAD', url: root.location, async: false})
                       .getAllResponseHeaders();
        }

        headers["Referer"] = document.referrer;
        headers["User-Agent"] = navigator.userAgent;
        return headers;
    };

    Raven.parseHeaders = function(headers_string) {
        /*
         * Parse the header string returned from getAllResponseHeaders
         */
        var headers = {};
        $.each(headers_string.split('\n'), function(i, header) {
            var name = header.slice(0, header.indexOf(':')),
                value = header.slice(header.indexOf(':') + 2);
            headers[name] = value;
        });
        return headers;
    };

    Raven.getSignature = function(message, timestamp, callback) {
        if (self.options.signatureUrl) {
            $.post(self.options.signatureUrl, {
                message: message, timestamp: timestamp
            }, function(data) {
                callback(data.signature);
            });
        } else {
            callback();
        }
    };

    Raven.getAuthHeader = function(signature, timestamp) {
        var header = "Sentry sentry_version=2.0, ";
        header += "sentry_timestamp=" + timestamp + ", ";
        header += "sentry_client=raven-js/" + self.VERSION;
        if (self.options.publicKey) {
            header += ", sentry_key=" + self.options.publicKey;
        }
        if (signature) {
            header += ", sentry_signature=" + signature;
        }
        return header;
    };

    Raven.captureException = function(e) {
        var lineno, traceback, fileurl;

        if (e.line) {  // WebKit
            lineno = e.line;
        } else if (e.lineNumber) {  // Mozilla
            lineno = e.lineNumber;
        }

        if (e.sourceURL) {  // Webkit
            fileurl = e.sourceURL;
        } else if (e.fileName) {  // Mozilla
            fileurl = e.fileName;
        }

        if (e.arguments && e.stack) {
            traceback = this.chromeTraceback(e);
        } else if (e.stack) {
            // Detect edge cases where Chrome doesn't have 'arguments'
            if (e.stack.indexOf('@') == -1) {
                traceback = this.chromeTraceback(e);
            } else {
                traceback = this.firefoxOrSafariTraceback(e);
            }
        } else {
            traceback = [{"filename": fileurl, "lineno": lineno}];
            traceback = traceback.concat(this.otherTraceback(arguments.callee));
        }

        self.process(e, fileurl, lineno, traceback);
    };

    Raven.captureMessage = function(msg) {
        self.process(msg);
    };

    Raven.trimString = function(str) {
        return str.replace(/^\s+|\s+$/g, "");
    };

    Raven.chromeUrlRegex = /([\w\. ]+)\s+\((.*):(\d+):\d+\)$/;

    Raven.chromeTraceback = function(e) {
        /*
         * First line is simply the repeated message:
         *   ReferenceError: aldfjalksdjf is not defined
         *
         * Following lines contain error context:
         *   at http://localhost:9000/1/group/306:41:5
         */
        var chunks, fn, filename, lineno, idx,
            traceback = [],
            lines = e.stack.split('\n');
        $.each(lines.slice(1), function(i, line) {
            // Trim the 'at ' from the beginning, and split by spaces
            line = Raven.trimString(line).slice(3);
            if (line === "unknown source") {
                return;  // Skip this one
            }
            chunks = Raven.chromeUrlRegex.exec(line);
            if (chunks){
                fn = chunks[1];
                filename = chunks[2];
                lineno = parseInt(chunks[3], 10);
            }else{
                fn = '';
                filename = line;
                lineno = -1;
                // some lines are just a filename with a line number and a char number, let's try and parse that
                idx = line.lastIndexOf(':');
                if (idx !== -1){
                    line = line.substring(0, idx);
                    idx = line.lastIndexOf(':');
                    if (idx !== -1){
                        filename = line.substring(0, idx);
                        lineno = parseInt(line.substring(idx + 1), 10);
                    }
                }
            }

            traceback.push({
                'function': fn,
                'filename': filename,
                'lineno': isNaN(lineno) ? -1 : lineno
            });
        });
        return traceback;
    };

    Raven.firefoxOrSafariTraceback = function(e) {
        /*
         * Each line is a function with args and a filename, separated by an ampersand.
         *   unsubstantiatedClaim("I am Batman")@http://raven-js.com/test/exception.js:7
         *
         * Anonymous functions are presented without a name, but including args.
         *   (66)@http://raven-js.com/test/vendor/qunit.js:418
         *
         */
        var chunks, fn, args, filename, lineno,
            traceback = [],
            lines = e.stack.split('\n');
        $.each(lines, function(i, line) {
            if (line) {
                chunks = line.split('@');
                if (chunks[0]) {
                    fn = chunks[0].split('(');

                    if (fn.length > 1 && fn[1] != ')') {
                        args = fn[1].slice(0, -1).split(',');
                    } else {
                        args = undefined;
                    }

                    if (fn[0]) {
                        fn = fn[0];
                    } else {
                        fn = '(unknown)';
                    }
                } else {
                    fn = '(unknown)';
                }

                if (chunks.length > 1) {
                    filename = chunks[1].split(':');
                    lineno = filename.slice(-1)[0];
                    filename = filename.slice(0, -1).join(':');
                } else if (chunks[0] == '[native code]') {
                    fn = '(unknown)';
                    filename = '[native code]';
                    lineno = 0;
                    args = undefined;
                }

                traceback.push({
                    'function': fn,
                    'filename': filename,
                    'lineno': lineno,
                    'vars': {'arguments': args}
                });
            }
        });
        return traceback;
    };

    Raven.otherTraceback = function(callee) {
        /*
         * Generates best-efforts tracebacks for other browsers, such as Safari
         * or IE.
         */
        var fn, args,
            ANON = '<anonymous>',
            traceback = [],
            max = 9;
        while (callee && traceback.length < max) {
            fn = callee.name || (this.funcNameRE.test(callee.toString()) ? RegExp.$1 || ANON : ANON);
            if (callee.arguments) {
                args = this.stringifyArguments(callee.arguments);
            } else {
                args = undefined;
            }
            traceback.push({
                'filename': '(unknown source)',
                'lineno': '(unknown)',
                'function': fn,
                'post_context': callee.toString().split('\n'),
                'vars': {'arguments': args}
            });
            callee = callee.caller;
        }
        return traceback;
    };

    Raven.stringifyArguments = function(args) {
        /*
         * Converts a callee's arguments to strings
         */
        var fn,
            self = this,
            UNKNOWN = '<unknown>',
            results = [];

        $.each(args, function(i, arg) {
            if (arg === undefined) {
                results.push('undefined');
            } else if (arg === null) {
                results.push('null');
            } else if (arg instanceof Array) {
                results.push(self.stringifyArguments(arg));
            } else if (arg.constructor) {
                fn = arg.constructor.name || (self.funcNameRE.test(arg.constructor.toString()) ? RegExp.$1 || UNKNOWN : UNKNOWN);
                if (fn == 'String') {
                    results.push('"' + arg + '"');
                } else if (fn == 'Number' || fn == 'Date') {
                    results.push(arg);
                } else if (fn == 'Boolean') {
                    results.push(arg ? 'true' : 'false');
                } else {
                    results.push(fn);
                }
            } else {
                results.push(UNKNOWN);
            }
        });

        return results;
    };

    Raven.process = function(message, fileurl, lineno, traceback, timestamp) {
        var label, stacktrace, data, encoded_msg, type,
            url = root.location.protocol + '//' + root.location.host + root.location.pathname,
            querystring = root.location.search.slice(1);  // Remove the ?

        if (typeof(message) === 'object') {
            type = message.name;
            message = message.message;
        }
        if ($.inArray(message, self.options.ignoreErrors) >= 0) {
            return;
        }

        for (var i = 0; i < self.options.ignoreUrls.length; i++) {
            if (self.options.ignoreUrls[i].test(fileurl)) {
                return;
            }
        }

        label = lineno ? message + " at " + lineno : message;

        if (traceback) {
            stacktrace = {"frames": traceback};
            fileurl = fileurl || traceback[0].filename;
        } else if (fileurl) {
            stacktrace = {
                "frames": [{
                    "filename": fileurl,
                    "lineno": lineno
                }]
            };
        }

        data = {
            "message": label,
            "culprit": fileurl,
            "sentry.interfaces.Stacktrace": stacktrace,
            "sentry.interfaces.Exception": {
                "type": type,
                "value": message
            },
            "project": self.options.projectId,
            "logger": self.options.logger,
            "site": self.options.site
        };

        data["sentry.interfaces.Http"] = {
            "url": url,
            "querystring": querystring,
            "headers": self.getHeaders()
        };

        timestamp = timestamp || (new Date()).getTime();
        encoded_msg = JSON.stringify(data);
        self.getSignature(encoded_msg, timestamp, function(signature) {
            var header = self.getAuthHeader(signature, timestamp);
            $.each(self.options.servers, function (i, server) {
                $.ajax({
                    type: 'POST',
                    url: server,
                    data: encoded_msg,
                    headers: {
                        // We send both headers, since Authentication may be blocked,
                        // and custom headers arent supported in IE9
                        'X-Sentry-Auth': header,
                        'Authentication': header
                    }
                });
            });
        });
    };
}).call(this);
<|endoftext|>


<commit_before>var _ = require('_'),

    _cache = require('./cache'),

    _camelCache     = _cache(),
    _displayCache   = _cache(),
    _focusableCache = _cache(),
    _clickableCache = _cache(),
    _commandCache   = _cache(),
    _idCache        = _cache(),
    _tagCache       = _cache(),
    _classCache     = _cache();

    // Matches "-ms-" so that it can be changed to "ms-"
var _TRUNCATE_MS_PREFIX = /^-ms-/,

    // Matches dashed string for camelizing
    _DASH_CATCH = /-([\da-z])/gi,

    // Matches "none" or a table type e.g. "table",
    // "table-cell" etc...
    _NONE_OR_TABLE = /^(none|table(?!-c[ea]).+)/,

    _TYPE_TEST = {
        focusable: /^(?:input|select|textarea|button|object)$/i,
        clickable: /^(?:a|area)$/i
    },

    _SELECTOR = {
        // TODO: This regex fails on "a, #id, [some-attr], .class, [some-attr=a,b,c\],d,e,f]" (attribute value containing an escaped right square bracket
        commandSplit: /(?:\[.*?[^\\]\]|[^\[\],]+)(?=\s*(?:,|$))/g,

        id:    /^#([\w-]+)$/,
        tag:   /^[\w-]+$/,
        klass: /^\.([\w-]+)$/
    };

var _camelCase = function(match, letter) {
    return letter.toUpperCase();
};

module.exports = {
    alpha: /alpha\([^)]*\)/i,
    opacity: /opacity\s*=\s*([^)]*)/,

    camelCase: function(str) {
        return _camelCache.getOrSet(str, function() {
            return str.replace(_TRUNCATE_MS_PREFIX, 'ms-').replace(_DASH_CATCH, _camelCase);
        });
    },

    display: {
        isNoneOrTable: function(str) {
            return _displayCache.getOrSet(str, function() {
                return !!_NONE_OR_TABLE.exec(str);
            });
        }
    },

    type: {
        isFocusable: function(str) {
            return _focusableCache.getOrSet(str, function() {
                var result = _TYPE_TEST.focusable.exec(str);
                return !!result;
            });
        },
        isClickable: function(str) {
            return _clickableCache.getOrSet(str, function() {
                var result = _TYPE_TEST.clickable.exec(str);
                return !!result;
            });
        }
    },

    selector: {

        commandSplit: function(str) {
            return _commandCache.getOrSet(str, function() {
                return _.filter(_.fastmap(str.match(_SELECTOR.commandSplit), _.string.trim));
            });
        },
        isStrictId: function(str) {
            return _idCache.getOrSet(str, function() {
                var result = _SELECTOR.id.exec(str);
                return result ? !result[1] : false;
            });
        },
        isTag: function(str) {
            return _tagCache.getOrSet(str, function() {
                var result = _SELECTOR.tag.exec(str);
                return result ? !result[1] : false;
            });
        },
        isClass: function(str) {
            return _classCache.getOrSet(str, function() {
                var result = _SELECTOR.klass.exec(str);
                return result ? !result[1] : false;
            });
        }
    }
};
<commit_msg>Remove commandSplit regex<commit_after>var _ = require('_'),

    _cache = require('./cache'),

    _camelCache     = _cache(),
    _displayCache   = _cache(),
    _focusableCache = _cache(),
    _clickableCache = _cache(),
    _commandCache   = _cache(),
    _idCache        = _cache(),
    _tagCache       = _cache(),
    _classCache     = _cache();

    // Matches "-ms-" so that it can be changed to "ms-"
var _TRUNCATE_MS_PREFIX = /^-ms-/,

    // Matches dashed string for camelizing
    _DASH_CATCH = /-([\da-z])/gi,

    // Matches "none" or a table type e.g. "table",
    // "table-cell" etc...
    _NONE_OR_TABLE = /^(none|table(?!-c[ea]).+)/,

    _TYPE_TEST = {
        focusable: /^(?:input|select|textarea|button|object)$/i,
        clickable: /^(?:a|area)$/i
    },

    _SELECTOR = {
        id:    /^#([\w-]+)$/,
        tag:   /^[\w-]+$/,
        klass: /^\.([\w-]+)$/
    };

var _camelCase = function(match, letter) {
    return letter.toUpperCase();
};

module.exports = {
    alpha: /alpha\([^)]*\)/i,
    opacity: /opacity\s*=\s*([^)]*)/,

    camelCase: function(str) {
        return _camelCache.getOrSet(str, function() {
            return str.replace(_TRUNCATE_MS_PREFIX, 'ms-').replace(_DASH_CATCH, _camelCase);
        });
    },

    display: {
        isNoneOrTable: function(str) {
            return _displayCache.getOrSet(str, function() {
                return !!_NONE_OR_TABLE.exec(str);
            });
        }
    },

    type: {
        isFocusable: function(str) {
            return _focusableCache.getOrSet(str, function() {
                var result = _TYPE_TEST.focusable.exec(str);
                return !!result;
            });
        },
        isClickable: function(str) {
            return _clickableCache.getOrSet(str, function() {
                var result = _TYPE_TEST.clickable.exec(str);
                return !!result;
            });
        }
    },

    selector: {
        isStrictId: function(str) {
            return _idCache.getOrSet(str, function() {
                var result = _SELECTOR.id.exec(str);
                return result ? !result[1] : false;
            });
        },
        isTag: function(str) {
            return _tagCache.getOrSet(str, function() {
                var result = _SELECTOR.tag.exec(str);
                return result ? !result[1] : false;
            });
        },
        isClass: function(str) {
            return _classCache.getOrSet(str, function() {
                var result = _SELECTOR.klass.exec(str);
                return result ? !result[1] : false;
            });
        }
    }
};
<|endoftext|>