<commit_before>var daisy = (function(Object, Array, Math, undefined){

    /**
     * Polyfills
     */
    (function(){

        // Array.isArray
        // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/isArray
        if (!Array.isArray) {
            Array.isArray = function(arg) {
                return Object.prototype.toString.call(arg) === '[object Array]';
            };
        }

        // Array.prototype.reduce
        // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/Reduce
        if (!Array.prototype.reduce) {
            Array.prototype.reduce = function(callback /*, initialValue*/) {
                'use strict';
                if (this == null) {
                    throw new TypeError('Array.prototype.reduce called on null or undefined');
                }
                if (typeof callback !== 'function') {
                    throw new TypeError(callback + ' is not a function');
                }
                var t = Object(this), len = t.length >>> 0, k = 0, value;
                if (arguments.length === 2) {
                    value = arguments[1];
                } else {
                    while (k < len && !(k in t)) {
                        k++;
                    }
                    if (k >= len) {
                        throw new TypeError('Reduce of empty array with no initial value');
                    }
                    value = t[k++];
                }
                for (; k < len; k++) {
                    if (k in t) {
                        value = callback(value, t[k], k, t);
                    }
                }
                return value;
            };
        }

    }());

    /**
     * DaisyException
     * @param {string} message Exception message
     */
    var DaisyException = function(message){
        this.name = 'DaisyException';
        this.message = message;
    };

    /**
     * tryParse - single point of number validation and coercion
     * @param  {number} i input value
     * @param  {object} o options object
     * @return {number}   sanitized number
     */
    var tryParse = function(i, o){
        var value = Number(i);
        if (isNaN(value)) {
            if (o.supressInvalidNumbers){ return 0; }
            throw new DaisyException('\'' + i + '\' is not a valid number');
        }
        return value;
    };

    /**
     * add - single representation of daisy addition
     * @param  {number} a
     * @param  {number} b
     * @param  {object} option options object
     * @return {number} result
     */
    var add = function(a, b, option){
        a = tryParse(a, option);
        b = tryParse(b, option);
        return a + b;
    };

    /**
     * subtract - single representation of daisy subtraction
     * @param  {number} a
     * @param  {number} b
     * @param  {object} option options object
     * @return {number} result
     */
    var subtract = function(a, b, option){
        a = tryParse(a, option);
        b = tryParse(b, option);
        return a - b;
    };

    /**
     * multiply - single representation of daisy multiplication
     * @param  {number} a
     * @param  {number} b
     * @param  {object} option options object
     * @return {number} result
     */
    var multiply = function(a, b, option){
        a = tryParse(a, option);
        b = tryParse(b, option);
        if (a === 0 || b === 0) { return 0; }
        if (a === 1) { return b; }
        if (b === 1) { return a; }
        return a * b;
    };

    /**
     * divide - single representation of daisy division
     * @param  {number} a
     * @param  {number} b
     * @param  {object} option options object
     * @return {number} result
     */
    var divide = function(a, b, option){
        b = tryParse(b);
        a = tryParse(a);
        if (b === 0 ) {
            if (!option.supressDivideByZero) {
                throw new DaisyException('divided \'' + a + '\' by zero');
            } else { return 0; }
        }
        if (b === 1) { return a; }
        if (a === 0) { return 0; }
        if (a === b) { return 1; }
        return a / b;
    };

    /**
     * round - single representation of daisy rounding
     * @param  {number}  val
     * @param  {decimal} place  decimal place rounding to (*1*)
     * @param  {string}  dir    "up", "down", or nothing
     * @param  {object}  options options object
     * @return {number}  result
     */
    var round = function(val, place, dir, options){
        var validatePlace = function(place){
            if (!place.toString){ return false; }
            var placeArray = place.toString().replace('.', '').split('');
            try {
                return placeArray.reduce(function(p, v){
                    return add(p, v, options);
                }) === 1;
            } catch (e) { return false; }
        };

        var getOperation = function(dir){
            switch(dir.toLowerCase()){
                case "up":
                    return Math.ceil;
                case "down":
                    return Math.floor;
                default:
                    return Math.round;
            }
        };

        if (!validatePlace(place)){ throw new DaisyException('\'' + place + '\' is not a valid decimal place'); }

        var op = getOperation(dir);
        return multiply(op(divide(val, place, options)), place, options);
    };

    /**
     * Computation Object - main daisy container
     * @param {number} initParam starting value
     * @param {object} options   options object
     */
    var Computation = function(initParam, options){
        this.currentVal = initParam + '';
        this.options = options || {};
    };

    Computation.prototype.equals = function(){
        var value = tryParse(this.currentVal, this.options).toFixed(2) + '';
        if (this.options.printDollarSign){ value = '$' + value; }
        return value;
    };

    Computation.prototype.plus = function(additionParam){
        this.currentVal = add(this.currentVal, additionParam, this.options);
        return this;
    };

    Computation.prototype.minus = function(substractionParam){
        this.currentVal = subtract(this.currentVal, substractionParam, this.options);
        return this;
    };

    Computation.prototype.times = function(multiplyParam){
        this.currentVal = multiply(this.currentVal, multiplyParam, this.options);
        return this;
    };

    Computation.prototype.dividedBy = function(divisiionParam){
        this.currentVal = divide(this.currentVal, divisiionParam, this.options);
        return this;
    };

    Computation.prototype.round = function(place, direction){
        direction = direction || '';
        this.currentVal = round(this.currentVal, place, direction, this.options);
        return this;
    };

    /**
     * NumberSet Object - daisy representation of series
     * @param {Array}  initParam starting array of values
     * @param {object} options   options object
     */
    var NumberSet = function(initParam, options){
        this.currentSet = initParam;
        this.options = options || {};
    };

    NumberSet.prototype.sum = function(){
        var self = this;
        if (self.currentSet.length === 0) { throw new DaisyException('cannot operate on empty set'); }
        return new Computation(self.currentSet.reduce(function(p, c){
            return add(p, c, self.options);
        }));
    };

    NumberSet.prototype.average = function(){
        var self = this;
        if (this.currentSet.length === 0) { throw new DaisyException('cannot operate on empty set'); }
        var value = this.currentSet.reduce(function(p, c){
            return add(p, c, self.options);
        }) / self.currentSet.length;
        return new Computation(value);
    };

    NumberSet.prototype.max = function(){
        var i, max, currentValue;
        if (this.currentSet.length === 0) { throw new DaisyException('cannot operate on empty set'); }
        if (!this.options.supressInvalidNumbers){
            return new Computation(Math.max.apply(null, this.currentSet));
        }
        max = tryParse(this.currentSet[0], this.options);
        for (i=0 ; i < this.currentSet.length ; i++){
            currentVal = tryParse(this.currentSet[i], this.options);
            if (max < currentVal){ max = currentVal; }
        }
        return new Computation(max);
    };

    NumberSet.prototype.min = function(){
        var i, min, currentValue;
        if (this.currentSet.length === 0) { throw new DaisyException('cannot operate on empty set'); }
        if (!this.options.supressInvalidNumbers){
            return new Computation(Math.min.apply(null, this.currentSet));
        }
        min = tryParse(this.currentSet[0], this.options);
        for (i=0 ; i < this.currentSet.length ; i++){
            currentVal = tryParse(this.currentSet[i], this.options);
            if (min > currentVal){ min = currentVal; }
        }
        return new Computation(min);
    };

    /**
     * daisy - exposed function
     * @param  {Array|number} initValue
     * @param  {object}       options
     * @return {Computation|Numberset}
     */
    return function(initValue, options){
        if (Array.isArray(initValue)){
            return new NumberSet(initValue, options);
        } else {
            return new Computation(initValue, options);
        }
    };

}(Object, Array, Math));<commit_msg>Added TODO comment for issue #14<commit_after>var daisy = (function(Object, Array, Math, undefined){

    /**
     * Polyfills
     */
    (function(){

        // Array.isArray
        // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/isArray
        if (!Array.isArray) {
            Array.isArray = function(arg) {
                return Object.prototype.toString.call(arg) === '[object Array]';
            };
        }

        // Array.prototype.reduce
        // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/Reduce
        if (!Array.prototype.reduce) {
            Array.prototype.reduce = function(callback /*, initialValue*/) {
                'use strict';
                if (this == null) {
                    throw new TypeError('Array.prototype.reduce called on null or undefined');
                }
                if (typeof callback !== 'function') {
                    throw new TypeError(callback + ' is not a function');
                }
                var t = Object(this), len = t.length >>> 0, k = 0, value;
                if (arguments.length === 2) {
                    value = arguments[1];
                } else {
                    while (k < len && !(k in t)) {
                        k++;
                    }
                    if (k >= len) {
                        throw new TypeError('Reduce of empty array with no initial value');
                    }
                    value = t[k++];
                }
                for (; k < len; k++) {
                    if (k in t) {
                        value = callback(value, t[k], k, t);
                    }
                }
                return value;
            };
        }

    }());

    /**
     * DaisyException
     * @param {string} message Exception message
     */
    var DaisyException = function(message){
        this.name = 'DaisyException';
        this.message = message;
    };

    /**
     * tryParse - single point of number validation and coercion
     * @param  {number} i input value
     * @param  {object} o options object
     * @return {number}   sanitized number
     */
    var tryParse = function(i, o){
        var value = Number(i);
        if (isNaN(value)) {
            if (o.supressInvalidNumbers){ return 0; }
            throw new DaisyException('\'' + i + '\' is not a valid number');
        }
        return value;
    };

    /**
     * add - single representation of daisy addition
     * @param  {number} a
     * @param  {number} b
     * @param  {object} option options object
     * @return {number} result
     */
    var add = function(a, b, option){
        a = tryParse(a, option);
        b = tryParse(b, option);
        return a + b;
    };

    /**
     * subtract - single representation of daisy subtraction
     * @param  {number} a
     * @param  {number} b
     * @param  {object} option options object
     * @return {number} result
     */
    var subtract = function(a, b, option){
        a = tryParse(a, option);
        b = tryParse(b, option);
        return a - b;
    };

    /**
     * multiply - single representation of daisy multiplication
     * @param  {number} a
     * @param  {number} b
     * @param  {object} option options object
     * @return {number} result
     */
    var multiply = function(a, b, option){
        a = tryParse(a, option);
        b = tryParse(b, option);
        if (a === 0 || b === 0) { return 0; }
        if (a === 1) { return b; }
        if (b === 1) { return a; }
        return a * b;
    };

    /**
     * divide - single representation of daisy division
     * @param  {number} a
     * @param  {number} b
     * @param  {object} option options object
     * @return {number} result
     */
    var divide = function(a, b, option){
        b = tryParse(b);
        a = tryParse(a);
        if (b === 0 ) {
            if (!option.supressDivideByZero) {
                throw new DaisyException('divided \'' + a + '\' by zero');
            } else { return 0; }
        }
        if (b === 1) { return a; }
        if (a === 0) { return 0; }
        if (a === b) { return 1; }
        return a / b;
    };

    /**
     * round - single representation of daisy rounding
     * @param  {number}  val
     * @param  {decimal} place  decimal place rounding to (*1*)
     * @param  {string}  dir    "up", "down", or nothing
     * @param  {object}  options options object
     * @return {number}  result
     */
    var round = function(val, place, dir, options){
        var validatePlace = function(place){
            // TODO: This is a convoluted way to validate
            // it seemed like a good idea at the time
            // See issue #14
            if (!place.toString){ return false; }
            var placeArray = place.toString().replace('.', '').split('');
            try {
                return placeArray.reduce(function(p, v){
                    return add(p, v, options);
                }) === 1;
            } catch (e) { return false; }
        };

        var getOperation = function(dir){
            switch(dir.toLowerCase()){
                case "up":
                    return Math.ceil;
                case "down":
                    return Math.floor;
                default:
                    return Math.round;
            }
        };

        if (!validatePlace(place)){ throw new DaisyException('\'' + place + '\' is not a valid decimal place'); }

        var op = getOperation(dir);
        return multiply(op(divide(val, place, options)), place, options);
    };

    /**
     * Computation Object - main daisy container
     * @param {number} initParam starting value
     * @param {object} options   options object
     */
    var Computation = function(initParam, options){
        this.currentVal = initParam + '';
        this.options = options || {};
    };

    Computation.prototype.equals = function(){
        var value = tryParse(this.currentVal, this.options).toFixed(2) + '';
        if (this.options.printDollarSign){ value = '$' + value; }
        return value;
    };

    Computation.prototype.plus = function(additionParam){
        this.currentVal = add(this.currentVal, additionParam, this.options);
        return this;
    };

    Computation.prototype.minus = function(substractionParam){
        this.currentVal = subtract(this.currentVal, substractionParam, this.options);
        return this;
    };

    Computation.prototype.times = function(multiplyParam){
        this.currentVal = multiply(this.currentVal, multiplyParam, this.options);
        return this;
    };

    Computation.prototype.dividedBy = function(divisiionParam){
        this.currentVal = divide(this.currentVal, divisiionParam, this.options);
        return this;
    };

    Computation.prototype.round = function(place, direction){
        direction = direction || '';
        this.currentVal = round(this.currentVal, place, direction, this.options);
        return this;
    };

    /**
     * NumberSet Object - daisy representation of series
     * @param {Array}  initParam starting array of values
     * @param {object} options   options object
     */
    var NumberSet = function(initParam, options){
        this.currentSet = initParam;
        this.options = options || {};
    };

    NumberSet.prototype.sum = function(){
        var self = this;
        if (self.currentSet.length === 0) { throw new DaisyException('cannot operate on empty set'); }
        return new Computation(self.currentSet.reduce(function(p, c){
            return add(p, c, self.options);
        }));
    };

    NumberSet.prototype.average = function(){
        var self = this;
        if (this.currentSet.length === 0) { throw new DaisyException('cannot operate on empty set'); }
        var value = this.currentSet.reduce(function(p, c){
            return add(p, c, self.options);
        }) / self.currentSet.length;
        return new Computation(value);
    };

    NumberSet.prototype.max = function(){
        var i, max, currentValue;
        if (this.currentSet.length === 0) { throw new DaisyException('cannot operate on empty set'); }
        if (!this.options.supressInvalidNumbers){
            return new Computation(Math.max.apply(null, this.currentSet));
        }
        max = tryParse(this.currentSet[0], this.options);
        for (i=0 ; i < this.currentSet.length ; i++){
            currentVal = tryParse(this.currentSet[i], this.options);
            if (max < currentVal){ max = currentVal; }
        }
        return new Computation(max);
    };

    NumberSet.prototype.min = function(){
        var i, min, currentValue;
        if (this.currentSet.length === 0) { throw new DaisyException('cannot operate on empty set'); }
        if (!this.options.supressInvalidNumbers){
            return new Computation(Math.min.apply(null, this.currentSet));
        }
        min = tryParse(this.currentSet[0], this.options);
        for (i=0 ; i < this.currentSet.length ; i++){
            currentVal = tryParse(this.currentSet[i], this.options);
            if (min > currentVal){ min = currentVal; }
        }
        return new Computation(min);
    };

    /**
     * daisy - exposed function
     * @param  {Array|number} initValue
     * @param  {object}       options
     * @return {Computation|Numberset}
     */
    return function(initValue, options){
        if (Array.isArray(initValue)){
            return new NumberSet(initValue, options);
        } else {
            return new Computation(initValue, options);
        }
    };

}(Object, Array, Math));<|endoftext|>


<commit_before>(function ($) {
  var c = require('categorizr')
  $.ender(c)
}(ender));
<commit_msg>more verbose $.X method names in ender integration<commit_after>(function ($) {
  var c = require('categorizr'), o = {}, k
  for (k in c) {
    if (Object.hasOwnProperty.call(c, k)) {
      o[k == 'test' ? 'testUserAgent' : k == 'is' ? 'isDeviceType' : k] = c[k]
    }
  }
  $.ender(o)
}(ender));<|endoftext|>


<commit_before>'use strict';

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

exports.create = function createEntry(kind, value, options) {

    if (arguments.length === 2) {
        options = value;
        value = null;
    }

    options = options || {};

    var kindModel = Kind.getSync(kind);
    var entry = new kindModel(value);
    // TODO handle options
    if (!options.owner) {
        throw new Error('cannot create an entry without owner');
    }
    entry._gr = [options.owner];
    return entry;

};

// Insert, update and/or delete multiple entries with a single instruction
/*
 Supported actions:
 - insert (default) : search entries with query. If no entry found create a new one, if one entry found modify its value, if more than one entry found error, then treat children
 - delete : remove all entries that match query then stop
 - add : insert new entry without searching, then treat children
 - replace : remove all entries that match query, then create entry with value, then treat children
 */
exports.batch = function (data, options, callback) {

    var prom;
    if (data instanceof Array) {
        prom = Promise.all(data.map(function (dataVal) {
            return exports.batch(dataVal, options);
        }));
    } else {
        prom = new Promise(function (resolve, reject) {

            if (!data.kind) {
                return reject(new Error('Batch method requires a kind for each element'));
            }

            var action = data.action || 'insert';

            options = options || {};
            if (!options.owner) {
                reject(new Error('Batch method requires an owner'));
            } else {
                if (options.parent) {
                    Kind.get(options.parent.kind).then(function (parentModel) {
                        parentModel.findById(options.parent.id, function (err, parent) {
                            if (err) {
                                reject(err);
                            } else if (parent) {
                                insertElement(parent);
                            } else {
                                reject(new Error('Parent ' + JSON.stringify(options.parent) + 'does not exist'));
                            }
                        });
                    }, reject);
                } else {
                    insertElement();
                }
            }

            function insertElement(parent) {
                Kind.get(data.kind).then(function (KindModel) {

                    var query = {
                        $and: [
                            extend({}, data.query, {
                                '_gr.0': options.owner
                            })
                        ]
                    };

                    if (parent) {
                        var parentQuery = {
                            kind: parent.getKind(),
                            id: parent.id
                        };
                        query.$and.unshift({ _an: { $elemMatch: parentQuery } });
                    } else {
                        query.$and.unshift({ _an: { $size: 0 } });
                    }

                    var finalCallback = getFinalCallback(resolve, reject),
                        nextCallback = getNextCallback(resolve, reject, data);

                    function createNewEntry(value, callback) {
                        var objData, obj;
                        if (value) {
                            objData = extend({}, value, { '_gr.0': data.owner });
                            obj = parent ? parent.createChild(data.kind, objData) : new KindModel(objData);
                            obj.save(function (err, entry) {
                                if (err) {
                                    callback(err);
                                } else {
                                    if (value.attachments) {
                                        async.each(value.attachments, addAttachment(entry), callback);
                                    } else {
                                        callback();
                                    }
                                }
                            });
                        } else {
                            objData = extend({}, data.query, data.value, { '_gr.0': data.owner });
                            obj = parent ? parent.createChild(data.kind, objData) : new KindModel(objData);
                            obj.save(function (err, entry) {
                                if (err) {
                                    nextCallback(err);
                                } else {
                                    if (data.attachments) {
                                        async.each(data.attachments, addAttachment(entry), function (err) {
                                            if (err) {
                                                nextCallback(err);
                                            } else {
                                                nextCallback(null, entry);
                                            }
                                        });
                                    } else {
                                        nextCallback(entry);
                                    }
                                }
                            });
                        }
                    }

                    function addNewEntries(err) {
                        if (err) {
                            return reject(err);
                        }
                        var values = data.values || [
                            { value: data.value, attachments: data.attachments }
                        ];
                        async.each(values, createNewEntry, finalCallback);
                    }

                    if (action === 'add') {
                        createNewEntry();
                    } else {
                        KindModel.find(query, function (err, res) {
                            if (err) {
                                reject(err);
                            } else if (res.length) {
                                if (action === 'delete') {
                                    async.each(res, removeEntry, finalCallback);
                                } else if (action === 'replace') {
                                    async.each(res, removeEntry, addNewEntries);
                                } else if (action === 'insert') {
                                    if (res.length > 1) {
                                        reject(new Error('Query lacks specificity. ' + res.length + ' matching documents found'));
                                    } else {
                                        res = res[0];
                                        for (var i in data.value) {
                                            if (data.value.hasOwnProperty(i)) {
                                                res[i] = data.value[i];
                                            }
                                        }
                                        res.save(nextCallback);
                                    }
                                } else {
                                    reject(new Error('Unknown action: ' + action));
                                }
                            } else {
                                if (action === 'delete') {
                                    reject(new Error('Entry not found, cannot remove'));
                                } else if (action === 'replace') {
                                    addNewEntries();
                                } else if (action === 'insert') {
                                    createNewEntry();
                                } else {
                                    reject(new Error('Unknown action: ' + action));
                                }
                            }
                        });
                    }
                });
            }

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

};

function addAttachment(entry) {
    return function addAttachmentToEntry(attachment, callback) {
        entry.createAttachment({
            data: new Buffer(attachment.value, attachment.encoding || 'utf8'),
            name: attachment.filename,
            contentType: attachment.contentType
        }, callback);
    };
}

function removeEntry(entry, callback) {
    entry.remove(callback);
}

function getFinalCallback(resolve, reject) {
    return function finalCallback(err) {
        if (err) {
            return reject(err);
        }
        resolve();
    }
}

function getNextCallback(resolve, reject, data) {
    return function nextCallback(err, entry) {
        if (err) {
            return reject(err);
        }
        // TODO attachments
        if (data.children) {
            var promises = data.children.map(function (child) {
                return _batch(child, entry);
            });
            Promise.all(promises).then(resolve, reject);
        } else {
            resolve();
        }
    }
}

function _batch(data, parent) {
    return new Promise(function (resolve, reject) {

        if (!data.kind) {
            return reject(new Error('Batch method requires a kind for each element'));
        }

        var action = data.action || 'insert';

        var parentQuery = {
            kind: parent.getKind(),
            id: ObjectID(parent.id)
        };

        var ancestors = parent._an.slice();
        ancestors.push(parentQuery);

        Kind.get(data.kind).then(function (KindModel) {

            var query = {
                $and: [
                    { _an: { $elemMatch: parentQuery } },
                    { '_gr.0': parent.owner, _an: ancestors }
                ]
            };
            if (data.query) {
                query.$and.push(extend({}, data.query))
            }

            var finalCallback = getFinalCallback(resolve, reject),
                nextCallback = getNextCallback(resolve, reject, data);

            function createNewEntry(value, callback) {
                var obj;
                if (value) {
                    obj = parent.createChild(data.kind, extend({}, value));
                    obj.save(function (err, entry) {
                        if (err) {
                            callback(err);
                        } else {
                            if (value.attachments) {
                                async.each(value.attachments, addAttachment(entry), callback);
                            } else {
                                callback();
                            }
                        }
                    });
                } else {
                    obj = parent.createChild(data.kind, extend({}, data.query, data.value));
                    obj.save(function (err, entry) {
                        if (err) {
                            nextCallback(err);
                        } else {
                            if (data.attachments) {
                                async.each(data.attachments, addAttachment(entry), function (err) {
                                    if (err) {
                                        nextCallback(err);
                                    } else {
                                        nextCallback(null, entry);
                                    }
                                });
                            } else {
                                nextCallback(entry);
                            }
                        }
                    });
                }
            }

            function addNewEntries(err) {
                if (err) {
                    return reject(err);
                }
                var values = data.values || [
                    { value: data.value, attachments: data.attachments }
                ];
                async.each(values, createNewEntry, finalCallback);
            }

            if (action === 'add') {
                createNewEntry();
            } else {
                KindModel.find(query, function (err, res) {
                    if (err) {
                        reject(err);
                    } else if (res.length) {
                        if (action === 'delete') {
                            async.each(res, removeEntry, finalCallback);
                        } else if (action === 'replace') {
                            async.each(res, removeEntry, addNewEntries);
                        } else if (action === 'insert') {
                            if (res.length > 1) {
                                reject(new Error('Query lacks specificity. ' + res.length + ' matching documents found'));
                            } else {
                                res = res[0];
                                for (var i in data.value) {
                                    if (data.value.hasOwnProperty(i)) {
                                        res[i] = data.value[i];
                                    }
                                }
                                res.save(nextCallback);
                            }
                        } else {
                            reject(new Error('Unknown action: ' + action));
                        }
                    } else {
                        if (action === 'delete') {
                            reject(new Error('Entry not found, cannot remove'));
                        } else if (action === 'replace') {
                            addNewEntries();
                        } else if (action === 'insert') {
                            createNewEntry();
                        } else {
                            reject(new Error('Unknown action: ' + action));
                        }
                    }
                });
            }
        });
    });
}

exports.insertTree = function (tree, options, callback) {

    options = options || {};
    if (!options.owner) {
        throw new Error('cannot create an entry without owner');
    }

    var prom = new Promise(function (resolve, reject) {

        if (!tree) {
            return reject(new Error('missing tree parameter'));
        }

        Kind.get(tree.kind).then(function (kindModel) {

            var rootVal = new kindModel(tree.value);
            rootVal.owner = options.owner;
            rootVal.save(function (err, rootVal) {

                if (err) {
                    return reject(err);
                }

                if (!tree.children) {
                    return resolve(rootVal);
                }

                appendChildren(rootVal, tree.children).then(function () {
                    resolve(rootVal);
                }, function (err) {
                    rootVal.remove(function () {
                        reject(err);
                    });
                });

            });

            // TODO hack mongoose bug !
            setTimeout(util.noop, 0);

        }, reject);

    });

    return util.bindPromise(prom, callback);

};

function appendChildren(parent, children) {

    return new Promise(function (resolve, reject) {

        if (!children instanceof Array) {
            return reject(new Error('children element must be an array'));
        }

        var childrenProm = [];
        for (var i = 0; i < children.length; i++) {
            childrenProm.push(appendChild(parent, children[i]));
        }

        Promise.all(childrenProm).then(resolve, reject);

    });

}

function appendChild(parent, child) {

    return new Promise(function (resolve, reject) {

        Kind.get(child.kind).then(function () {

            parent.createChild(child.kind, child.value).save(function (err, childObj) {
                if (err) {
                    return reject(err);
                }
                if (!child.children) {
                    resolve();
                } else {
                    appendChildren(childObj, child.children).then(resolve, reject);
                }

            });

        }, reject);

    });

}

exports.findOne = function findOneEntry(kind, conditions, fields, options, callback) {
    var kindModel = Kind.getSync(kind);
    return kindModel.findOne(conditions, fields, options, callback);
};

exports.find = function findEntries(kind, conditions, fields, options, callback) {
    var kindModel = Kind.getSync(kind);
    return kindModel.find(conditions, fields, options, callback);
};<commit_msg>fix wrong call of nextCallback<commit_after>'use strict';

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

exports.create = function createEntry(kind, value, options) {

    if (arguments.length === 2) {
        options = value;
        value = null;
    }

    options = options || {};

    var kindModel = Kind.getSync(kind);
    var entry = new kindModel(value);
    // TODO handle options
    if (!options.owner) {
        throw new Error('cannot create an entry without owner');
    }
    entry._gr = [options.owner];
    return entry;

};

// Insert, update and/or delete multiple entries with a single instruction
/*
 Supported actions:
 - insert (default) : search entries with query. If no entry found create a new one, if one entry found modify its value, if more than one entry found error, then treat children
 - delete : remove all entries that match query then stop
 - add : insert new entry without searching, then treat children
 - replace : remove all entries that match query, then create entry with value, then treat children
 */
exports.batch = function (data, options, callback) {

    var prom;
    if (data instanceof Array) {
        prom = Promise.all(data.map(function (dataVal) {
            return exports.batch(dataVal, options);
        }));
    } else {
        prom = new Promise(function (resolve, reject) {

            if (!data.kind) {
                return reject(new Error('Batch method requires a kind for each element'));
            }

            var action = data.action || 'insert';

            options = options || {};
            if (!options.owner) {
                reject(new Error('Batch method requires an owner'));
            } else {
                if (options.parent) {
                    Kind.get(options.parent.kind).then(function (parentModel) {
                        parentModel.findById(options.parent.id, function (err, parent) {
                            if (err) {
                                reject(err);
                            } else if (parent) {
                                insertElement(parent);
                            } else {
                                reject(new Error('Parent ' + JSON.stringify(options.parent) + 'does not exist'));
                            }
                        });
                    }, reject);
                } else {
                    insertElement();
                }
            }

            function insertElement(parent) {
                Kind.get(data.kind).then(function (KindModel) {

                    var query = {
                        $and: [
                            extend({}, data.query, {
                                '_gr.0': options.owner
                            })
                        ]
                    };

                    if (parent) {
                        var parentQuery = {
                            kind: parent.getKind(),
                            id: parent.id
                        };
                        query.$and.unshift({ _an: { $elemMatch: parentQuery } });
                    } else {
                        query.$and.unshift({ _an: { $size: 0 } });
                    }

                    var finalCallback = getFinalCallback(resolve, reject),
                        nextCallback = getNextCallback(resolve, reject, data);

                    function createNewEntry(value, callback) {
                        var objData, obj;
                        if (value) {
                            objData = extend({}, value, { '_gr.0': data.owner });
                            obj = parent ? parent.createChild(data.kind, objData) : new KindModel(objData);
                            obj.save(function (err, entry) {
                                if (err) {
                                    callback(err);
                                } else {
                                    if (value.attachments) {
                                        async.each(value.attachments, addAttachment(entry), callback);
                                    } else {
                                        callback();
                                    }
                                }
                            });
                        } else {
                            objData = extend({}, data.query, data.value, { '_gr.0': data.owner });
                            obj = parent ? parent.createChild(data.kind, objData) : new KindModel(objData);
                            obj.save(function (err, entry) {
                                if (err) {
                                    nextCallback(err);
                                } else {
                                    if (data.attachments) {
                                        async.each(data.attachments, addAttachment(entry), function (err) {
                                            if (err) {
                                                nextCallback(err);
                                            } else {
                                                nextCallback(null, entry);
                                            }
                                        });
                                    } else {
                                        nextCallback(null, entry);
                                    }
                                }
                            });
                        }
                    }

                    function addNewEntries(err) {
                        if (err) {
                            return reject(err);
                        }
                        var values = data.values || [
                            { value: data.value, attachments: data.attachments }
                        ];
                        async.each(values, createNewEntry, finalCallback);
                    }

                    if (action === 'add') {
                        createNewEntry();
                    } else {
                        KindModel.find(query, function (err, res) {
                            if (err) {
                                reject(err);
                            } else if (res.length) {
                                if (action === 'delete') {
                                    async.each(res, removeEntry, finalCallback);
                                } else if (action === 'replace') {
                                    async.each(res, removeEntry, addNewEntries);
                                } else if (action === 'insert') {
                                    if (res.length > 1) {
                                        reject(new Error('Query lacks specificity. ' + res.length + ' matching documents found'));
                                    } else {
                                        res = res[0];
                                        for (var i in data.value) {
                                            if (data.value.hasOwnProperty(i)) {
                                                res[i] = data.value[i];
                                            }
                                        }
                                        res.save(nextCallback);
                                    }
                                } else {
                                    reject(new Error('Unknown action: ' + action));
                                }
                            } else {
                                if (action === 'delete') {
                                    reject(new Error('Entry not found, cannot remove'));
                                } else if (action === 'replace') {
                                    addNewEntries();
                                } else if (action === 'insert') {
                                    createNewEntry();
                                } else {
                                    reject(new Error('Unknown action: ' + action));
                                }
                            }
                        });
                    }
                });
            }

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

};

function addAttachment(entry) {
    return function addAttachmentToEntry(attachment, callback) {
        entry.createAttachment({
            data: new Buffer(attachment.value, attachment.encoding || 'utf8'),
            name: attachment.filename,
            contentType: attachment.contentType
        }, callback);
    };
}

function removeEntry(entry, callback) {
    entry.remove(callback);
}

function getFinalCallback(resolve, reject) {
    return function finalCallback(err) {
        if (err) {
            return reject(err);
        }
        resolve();
    }
}

function getNextCallback(resolve, reject, data) {
    return function nextCallback(err, entry) {
        if (err) {
            return reject(err);
        }
        // TODO attachments
        if (data.children) {
            var promises = data.children.map(function (child) {
                return _batch(child, entry);
            });
            Promise.all(promises).then(resolve, reject);
        } else {
            resolve();
        }
    }
}

function _batch(data, parent) {
    return new Promise(function (resolve, reject) {

        if (!data.kind) {
            return reject(new Error('Batch method requires a kind for each element'));
        }

        var action = data.action || 'insert';

        var parentQuery = {
            kind: parent.getKind(),
            id: ObjectID(parent.id)
        };

        var ancestors = parent._an.slice();
        ancestors.push(parentQuery);

        Kind.get(data.kind).then(function (KindModel) {

            var query = {
                $and: [
                    { _an: { $elemMatch: parentQuery } },
                    { '_gr.0': parent.owner, _an: ancestors }
                ]
            };
            if (data.query) {
                query.$and.push(extend({}, data.query))
            }

            var finalCallback = getFinalCallback(resolve, reject),
                nextCallback = getNextCallback(resolve, reject, data);

            function createNewEntry(value, callback) {
                var obj;
                if (value) {
                    obj = parent.createChild(data.kind, extend({}, value));
                    obj.save(function (err, entry) {
                        if (err) {
                            callback(err);
                        } else {
                            if (value.attachments) {
                                async.each(value.attachments, addAttachment(entry), callback);
                            } else {
                                callback();
                            }
                        }
                    });
                } else {
                    obj = parent.createChild(data.kind, extend({}, data.query, data.value));
                    obj.save(function (err, entry) {
                        if (err) {
                            nextCallback(err);
                        } else {
                            if (data.attachments) {
                                async.each(data.attachments, addAttachment(entry), function (err) {
                                    if (err) {
                                        nextCallback(err);
                                    } else {
                                        nextCallback(null, entry);
                                    }
                                });
                            } else {
                                nextCallback(null, entry);
                            }
                        }
                    });
                }
            }

            function addNewEntries(err) {
                if (err) {
                    return reject(err);
                }
                var values = data.values || [
                    { value: data.value, attachments: data.attachments }
                ];
                async.each(values, createNewEntry, finalCallback);
            }

            if (action === 'add') {
                createNewEntry();
            } else {
                KindModel.find(query, function (err, res) {
                    if (err) {
                        reject(err);
                    } else if (res.length) {
                        if (action === 'delete') {
                            async.each(res, removeEntry, finalCallback);
                        } else if (action === 'replace') {
                            async.each(res, removeEntry, addNewEntries);
                        } else if (action === 'insert') {
                            if (res.length > 1) {
                                reject(new Error('Query lacks specificity. ' + res.length + ' matching documents found'));
                            } else {
                                res = res[0];
                                for (var i in data.value) {
                                    if (data.value.hasOwnProperty(i)) {
                                        res[i] = data.value[i];
                                    }
                                }
                                res.save(nextCallback);
                            }
                        } else {
                            reject(new Error('Unknown action: ' + action));
                        }
                    } else {
                        if (action === 'delete') {
                            reject(new Error('Entry not found, cannot remove'));
                        } else if (action === 'replace') {
                            addNewEntries();
                        } else if (action === 'insert') {
                            createNewEntry();
                        } else {
                            reject(new Error('Unknown action: ' + action));
                        }
                    }
                });
            }
        });
    });
}

exports.insertTree = function (tree, options, callback) {

    options = options || {};
    if (!options.owner) {
        throw new Error('cannot create an entry without owner');
    }

    var prom = new Promise(function (resolve, reject) {

        if (!tree) {
            return reject(new Error('missing tree parameter'));
        }

        Kind.get(tree.kind).then(function (kindModel) {

            var rootVal = new kindModel(tree.value);
            rootVal.owner = options.owner;
            rootVal.save(function (err, rootVal) {

                if (err) {
                    return reject(err);
                }

                if (!tree.children) {
                    return resolve(rootVal);
                }

                appendChildren(rootVal, tree.children).then(function () {
                    resolve(rootVal);
                }, function (err) {
                    rootVal.remove(function () {
                        reject(err);
                    });
                });

            });

            // TODO hack mongoose bug !
            setTimeout(util.noop, 0);

        }, reject);

    });

    return util.bindPromise(prom, callback);

};

function appendChildren(parent, children) {

    return new Promise(function (resolve, reject) {

        if (!children instanceof Array) {
            return reject(new Error('children element must be an array'));
        }

        var childrenProm = [];
        for (var i = 0; i < children.length; i++) {
            childrenProm.push(appendChild(parent, children[i]));
        }

        Promise.all(childrenProm).then(resolve, reject);

    });

}

function appendChild(parent, child) {

    return new Promise(function (resolve, reject) {

        Kind.get(child.kind).then(function () {

            parent.createChild(child.kind, child.value).save(function (err, childObj) {
                if (err) {
                    return reject(err);
                }
                if (!child.children) {
                    resolve();
                } else {
                    appendChildren(childObj, child.children).then(resolve, reject);
                }

            });

        }, reject);

    });

}

exports.findOne = function findOneEntry(kind, conditions, fields, options, callback) {
    var kindModel = Kind.getSync(kind);
    return kindModel.findOne(conditions, fields, options, callback);
};

exports.find = function findEntries(kind, conditions, fields, options, callback) {
    var kindModel = Kind.getSync(kind);
    return kindModel.find(conditions, fields, options, callback);
};<|endoftext|>


<commit_before>import { objectType } from "./core/utilities";

// Test for equality any JavaScript type.
// Author: Philippe Rathé <prathe@gmail.com>
export default ( function() {

	// Stack to decide between skip/abort functions
	var callers = [];

	// Stack to avoiding loops from circular referencing
	var parents = [];
	var parentsB = [];

	var getProto = Object.getPrototypeOf || function( obj ) {
		return obj.__proto__;
	};

	function useStrictEquality( b, a ) {

		// To catch short annotation VS 'new' annotation of a declaration. e.g.:
		// `var i = 1;`
		// `var j = new Number(1);`
		if ( typeof a === "object" ) {
			a = a.valueOf();
		}
		if ( typeof b === "object" ) {
			b = b.valueOf();
		}

		return a === b;
	}

	function compareConstructors( a, b ) {
		var protoA = getProto( a );
		var protoB = getProto( b );

		// Comparing constructors is more strict than using `instanceof`
		if ( a.constructor === b.constructor ) {
			return true;
		}

		// Ref #851
		// If the obj prototype descends from a null constructor, treat it
		// as a null prototype.
		if ( protoA && protoA.constructor === null ) {
			protoA = null;
		}
		if ( protoB && protoB.constructor === null ) {
			protoB = null;
		}

		// Allow objects with no prototype to be equivalent to
		// objects with Object as their constructor.
		if ( ( protoA === null && protoB === Object.prototype ) ||
				( protoB === null && protoA === Object.prototype ) ) {
			return true;
		}

		return false;
	}

	function getRegExpFlags( regexp ) {
		return "flags" in regexp ? regexp.flags : regexp.toString().match( /[gimuy]*$/ )[ 0 ];
	}

	var callbacks = {
		"string": useStrictEquality,
		"boolean": useStrictEquality,
		"number": useStrictEquality,
		"null": useStrictEquality,
		"undefined": useStrictEquality,
		"symbol": useStrictEquality,
		"date": useStrictEquality,

		"nan": function() {
			return true;
		},

		"regexp": function( b, a ) {
			return a.source === b.source &&

				// Include flags in the comparison
				getRegExpFlags( a ) === getRegExpFlags( b );
		},

		// - skip when the property is a method of an instance (OOP)
		// - abort otherwise,
		// initial === would have catch identical references anyway
		"function": function( b, a ) {

			var caller = callers[ callers.length - 1 ];
			return caller !== Object && typeof caller !== "undefined" &&
			a.toString() === b.toString();
		},

		"array": function( b, a ) {
			var i, j, len, loop, aCircular, bCircular;

			len = a.length;
			if ( len !== b.length ) {

				// Safe and faster
				return false;
			}

			// Track reference to avoid circular references
			parents.push( a );
			parentsB.push( b );
			for ( i = 0; i < len; i++ ) {
				loop = false;
				for ( j = 0; j < parents.length; j++ ) {
					aCircular = parents[ j ] === a[ i ];
					bCircular = parentsB[ j ] === b[ i ];
					if ( aCircular || bCircular ) {
						if ( a[ i ] === b[ i ] || aCircular && bCircular ) {
							loop = true;
						} else {
							parents.pop();
							parentsB.pop();
							return false;
						}
					}
				}
				if ( !loop && !innerEquiv( a[ i ], b[ i ] ) ) {
					parents.pop();
					parentsB.pop();
					return false;
				}
			}
			parents.pop();
			parentsB.pop();
			return true;
		},

		"set": function( b, a ) {
			var innerEq,
				outerEq = true;

			if ( a.size !== b.size ) {
				return false;
			}

			a.forEach( function( aVal ) {
				innerEq = false;

				b.forEach( function( bVal ) {
					if ( innerEquiv( bVal, aVal ) ) {
						innerEq = true;
					}
				} );

				if ( !innerEq ) {
					outerEq = false;
				}
			} );

			return outerEq;
		},

		"map": function( b, a ) {
			var innerEq,
				outerEq = true;

			if ( a.size !== b.size ) {
				return false;
			}

			a.forEach( function( aVal, aKey ) {
				innerEq = false;

				b.forEach( function( bVal, bKey ) {
					if ( innerEquiv( [ bVal, bKey ], [ aVal, aKey ] ) ) {
						innerEq = true;
					}
				} );

				if ( !innerEq ) {
					outerEq = false;
				}
			} );

			return outerEq;
		},

		"object": function( b, a ) {
			var i, j, loop, aCircular, bCircular;

			// Default to true
			var eq = true;
			var aProperties = [];
			var bProperties = [];

			if ( compareConstructors( a, b ) === false ) {
				return false;
			}

			// Stack constructor before traversing properties
			callers.push( a.constructor );

			// Track reference to avoid circular references
			parents.push( a );
			parentsB.push( b );

			// Be strict: don't ensure hasOwnProperty and go deep
			for ( i in a ) {
				loop = false;
				for ( j = 0; j < parents.length; j++ ) {
					aCircular = parents[ j ] === a[ i ];
					bCircular = parentsB[ j ] === b[ i ];
					if ( aCircular || bCircular ) {
						if ( a[ i ] === b[ i ] || aCircular && bCircular ) {
							loop = true;
						} else {
							eq = false;
							break;
						}
					}
				}
				aProperties.push( i );
				if ( !loop && !innerEquiv( a[ i ], b[ i ] ) ) {
					eq = false;
					break;
				}
			}

			parents.pop();
			parentsB.pop();

			// Unstack, we are done
			callers.pop();

			for ( i in b ) {

				// Collect b's properties
				bProperties.push( i );
			}

			// Ensures identical properties name
			return eq && innerEquiv( aProperties.sort(), bProperties.sort() );
		}
	};

	function typeEquiv( a, b ) {
		var type = objectType( a );
		return objectType( b ) === type && callbacks[ type ]( b, a );
	}

	// The real equiv function
	function innerEquiv( a, b ) {

		// We're done when there's nothing more to compare
		if ( arguments.length < 2 ) {
			return true;
		}

		// Require type-specific equality
		return ( a === b || typeEquiv( a, b ) ) &&

			// ...across all consecutive argument pairs
			( arguments.length === 2 || innerEquiv.apply( this, [].slice.call( arguments, 1 ) ) );
	}

	return innerEquiv;
}() );
<commit_msg>Core: Document useStrictEquality<commit_after>import { objectType } from "./core/utilities";

// Test for equality any JavaScript type.
// Author: Philippe Rathé <prathe@gmail.com>
export default ( function() {

	// Stack to decide between skip/abort functions
	var callers = [];

	// Stack to avoiding loops from circular referencing
	var parents = [];
	var parentsB = [];

	var getProto = Object.getPrototypeOf || function( obj ) {
		return obj.__proto__;
	};

	function useStrictEquality( b, a ) {

		// This only gets called if a and b are not strict equal, and is used to compare on
		// the primitive values inside object wrappers. For example:
		// `var i = 1;`
		// `var j = new Number(1);`
		// Neither a nor b can be null, as a !== b and they have the same type.
		if ( typeof a === "object" ) {
			a = a.valueOf();
		}
		if ( typeof b === "object" ) {
			b = b.valueOf();
		}

		return a === b;
	}

	function compareConstructors( a, b ) {
		var protoA = getProto( a );
		var protoB = getProto( b );

		// Comparing constructors is more strict than using `instanceof`
		if ( a.constructor === b.constructor ) {
			return true;
		}

		// Ref #851
		// If the obj prototype descends from a null constructor, treat it
		// as a null prototype.
		if ( protoA && protoA.constructor === null ) {
			protoA = null;
		}
		if ( protoB && protoB.constructor === null ) {
			protoB = null;
		}

		// Allow objects with no prototype to be equivalent to
		// objects with Object as their constructor.
		if ( ( protoA === null && protoB === Object.prototype ) ||
				( protoB === null && protoA === Object.prototype ) ) {
			return true;
		}

		return false;
	}

	function getRegExpFlags( regexp ) {
		return "flags" in regexp ? regexp.flags : regexp.toString().match( /[gimuy]*$/ )[ 0 ];
	}

	var callbacks = {
		"string": useStrictEquality,
		"boolean": useStrictEquality,
		"number": useStrictEquality,
		"null": useStrictEquality,
		"undefined": useStrictEquality,
		"symbol": useStrictEquality,
		"date": useStrictEquality,

		"nan": function() {
			return true;
		},

		"regexp": function( b, a ) {
			return a.source === b.source &&

				// Include flags in the comparison
				getRegExpFlags( a ) === getRegExpFlags( b );
		},

		// - skip when the property is a method of an instance (OOP)
		// - abort otherwise,
		// initial === would have catch identical references anyway
		"function": function( b, a ) {

			var caller = callers[ callers.length - 1 ];
			return caller !== Object && typeof caller !== "undefined" &&
			a.toString() === b.toString();
		},

		"array": function( b, a ) {
			var i, j, len, loop, aCircular, bCircular;

			len = a.length;
			if ( len !== b.length ) {

				// Safe and faster
				return false;
			}

			// Track reference to avoid circular references
			parents.push( a );
			parentsB.push( b );
			for ( i = 0; i < len; i++ ) {
				loop = false;
				for ( j = 0; j < parents.length; j++ ) {
					aCircular = parents[ j ] === a[ i ];
					bCircular = parentsB[ j ] === b[ i ];
					if ( aCircular || bCircular ) {
						if ( a[ i ] === b[ i ] || aCircular && bCircular ) {
							loop = true;
						} else {
							parents.pop();
							parentsB.pop();
							return false;
						}
					}
				}
				if ( !loop && !innerEquiv( a[ i ], b[ i ] ) ) {
					parents.pop();
					parentsB.pop();
					return false;
				}
			}
			parents.pop();
			parentsB.pop();
			return true;
		},

		"set": function( b, a ) {
			var innerEq,
				outerEq = true;

			if ( a.size !== b.size ) {
				return false;
			}

			a.forEach( function( aVal ) {
				innerEq = false;

				b.forEach( function( bVal ) {
					if ( innerEquiv( bVal, aVal ) ) {
						innerEq = true;
					}
				} );

				if ( !innerEq ) {
					outerEq = false;
				}
			} );

			return outerEq;
		},

		"map": function( b, a ) {
			var innerEq,
				outerEq = true;

			if ( a.size !== b.size ) {
				return false;
			}

			a.forEach( function( aVal, aKey ) {
				innerEq = false;

				b.forEach( function( bVal, bKey ) {
					if ( innerEquiv( [ bVal, bKey ], [ aVal, aKey ] ) ) {
						innerEq = true;
					}
				} );

				if ( !innerEq ) {
					outerEq = false;
				}
			} );

			return outerEq;
		},

		"object": function( b, a ) {
			var i, j, loop, aCircular, bCircular;

			// Default to true
			var eq = true;
			var aProperties = [];
			var bProperties = [];

			if ( compareConstructors( a, b ) === false ) {
				return false;
			}

			// Stack constructor before traversing properties
			callers.push( a.constructor );

			// Track reference to avoid circular references
			parents.push( a );
			parentsB.push( b );

			// Be strict: don't ensure hasOwnProperty and go deep
			for ( i in a ) {
				loop = false;
				for ( j = 0; j < parents.length; j++ ) {
					aCircular = parents[ j ] === a[ i ];
					bCircular = parentsB[ j ] === b[ i ];
					if ( aCircular || bCircular ) {
						if ( a[ i ] === b[ i ] || aCircular && bCircular ) {
							loop = true;
						} else {
							eq = false;
							break;
						}
					}
				}
				aProperties.push( i );
				if ( !loop && !innerEquiv( a[ i ], b[ i ] ) ) {
					eq = false;
					break;
				}
			}

			parents.pop();
			parentsB.pop();

			// Unstack, we are done
			callers.pop();

			for ( i in b ) {

				// Collect b's properties
				bProperties.push( i );
			}

			// Ensures identical properties name
			return eq && innerEquiv( aProperties.sort(), bProperties.sort() );
		}
	};

	function typeEquiv( a, b ) {
		var type = objectType( a );
		return objectType( b ) === type && callbacks[ type ]( b, a );
	}

	// The real equiv function
	function innerEquiv( a, b ) {

		// We're done when there's nothing more to compare
		if ( arguments.length < 2 ) {
			return true;
		}

		// Require type-specific equality
		return ( a === b || typeEquiv( a, b ) ) &&

			// ...across all consecutive argument pairs
			( arguments.length === 2 || innerEquiv.apply( this, [].slice.call( arguments, 1 ) ) );
	}

	return innerEquiv;
}() );
<|endoftext|>


<commit_before>var gbili = gbili || {};

gbili.toType = function(obj) {
    return ({}).toString.call(obj).match(/\s([a-zA-Z]+)/)[1].toLowerCase()
};

//
// gbili.event enables events, you can addListeners, trigger events and
// access the events list
//
gbili.event = function() {
    // every event has
    // {
    //     propagationIsStopped:false,
    //     name:?,
    //     params:{},
    //     target:{}
    // }
    var events = {};

    // Responses are sorted by event name as key
    // {
    //     'my.event.name' : [
    //         <listener1_return>,
    //         <listener2_return>,
    //         ...
    //     ]
    //     'my.event.name' : [
    //         <listener1_return>,
    //         <listener2_return>,
    //         ...
    //     ]
    // }
    var responses = {};

    var addListener = function (eventName, callback, priority) {
        if (!events.hasOwnProperty(eventName)) {
            events[eventName] = {
                listeners : []
            };
        }
        // Initialize the array with priority
        if (!(priority in events[eventName].listeners)) {
            events[eventName].listeners[priority] = [];
        }
        // Add the callback to the priority array
        events[eventName].listeners[priority].push(callback);
    },

    var trigger = function (eventName, params) {
        if (!events[eventName]) {
            return;
        }

        // Initialize to empty responses queue
        responses[eventName] = [];

        var triggeredEvent = events[eventName];
        var sortedListenerPriorities = triggeredEvent.listeners.sort();

        // Listeners can access event.params and event.target
        triggeredEvent.name = eventName;
        triggeredEvent.params = params.params || {};
        triggeredEvent.target = params.target || (params.targetGenerator && params.targetGenerator()) || {};
        triggeredEvent.lastListenerReturn = null;
        triggeredEvent.propagationIsStopped = false;

        var listenerRetval;
        execute_listeners:
        for (i in sortedListenerPriorities) {
            for (j in sortedListenerPriorities[i]) {
                var listenerCallback = sortedListenerPriorities[i][j];
                responses[eventName].push(listenerCallback(triggeredEvent));
                if (triggeredEvent.propagationIsStopped) {
                    break execute_listeners;
                }
            }
        }
        var returnDefaultResponse = params.hasOwnProperty('defaultReponse') && responses[eventName].length() == 0;
        console.log('returnDefaultResponse');
        console.log(returnDefaultResponse);
        var ret = (returnDefaultResponse && [params.defaultResponse]) || responses[eventName];
        console.log('ret');
        console.log(ret);
    },

    return {
        getEvents : function() {
            return events;
        },
        getResponses : function() {
            return responses;
        },
        addListener: addListener,
        trigger: trigger,
    };
}();
<commit_msg>changed object to function<commit_after>var gbili = gbili || {};

gbili.toType = function(obj) {
    return ({}).toString.call(obj).match(/\s([a-zA-Z]+)/)[1].toLowerCase()
};

//
// gbili.event enables events, you can addListeners, trigger events and
// access the events list
//
gbili.event = function() {
    // every event has
    // {
    //     propagationIsStopped:false,
    //     name:?,
    //     params:{},
    //     target:{}
    // }
    var events = {};

    // Responses are sorted by event name as key
    // {
    //     'my.event.name' : [
    //         <listener1_return>,
    //         <listener2_return>,
    //         ...
    //     ]
    //     'my.event.name' : [
    //         <listener1_return>,
    //         <listener2_return>,
    //         ...
    //     ]
    // }
    var responses = {};

    var addListener = function (eventName, callback, priority) {
        if (!events.hasOwnProperty(eventName)) {
            events[eventName] = {
                listeners : []
            };
        }
        // Initialize the array with priority
        if (!(priority in events[eventName].listeners)) {
            events[eventName].listeners[priority] = [];
        }
        // Add the callback to the priority array
        events[eventName].listeners[priority].push(callback);
    };

    var trigger = function (eventName, params) {
        if (!events[eventName]) {
            return;
        }

        // Initialize to empty responses queue
        responses[eventName] = [];

        var triggeredEvent = events[eventName];
        var sortedListenerPriorities = triggeredEvent.listeners.sort();

        // Listeners can access event.params and event.target
        triggeredEvent.name = eventName;
        triggeredEvent.params = params.params || {};
        triggeredEvent.target = params.target || (params.targetGenerator && params.targetGenerator()) || {};
        triggeredEvent.lastListenerReturn = null;
        triggeredEvent.propagationIsStopped = false;

        var listenerRetval;
        execute_listeners:
        for (i in sortedListenerPriorities) {
            for (j in sortedListenerPriorities[i]) {
                var listenerCallback = sortedListenerPriorities[i][j];
                responses[eventName].push(listenerCallback(triggeredEvent));
                if (triggeredEvent.propagationIsStopped) {
                    break execute_listeners;
                }
            }
        }
        var returnDefaultResponse = params.hasOwnProperty('defaultReponse') && responses[eventName].length() == 0;
        console.log('returnDefaultResponse');
        console.log(returnDefaultResponse);
        var ret = (returnDefaultResponse && [params.defaultResponse]) || responses[eventName];
        console.log('ret');
        console.log(ret);
    };

    return {
        getEvents : function() {
            return events;
        },
        getResponses : function() {
            return responses;
        },
        addListener: addListener,
        trigger: trigger,
    };
}();
<|endoftext|>


<commit_before>/*
 * A number of helper functions used for managing events.
 * Many of the ideas behind this code orignated from
 * Dean Edwards' addEvent library.
 */
jQuery.event = {

	// Bind an event to an element
	// Original by Dean Edwards
	add: function(element, type, handler, data) {
		// For whatever reason, IE has trouble passing the window object
		// around, causing it to be cloned in the process
		if ( jQuery.browser.msie && element.setInterval != undefined )
			element = window;

		// Make sure that the function being executed has a unique ID
		if ( !handler.guid )
			handler.guid = this.guid++;

		// if data is passed, bind to handler
		if( data != undefined ) {
        	// Create temporary function pointer to original handler
			var fn = handler;

			// Create unique handler function, wrapped around original handler
			handler = function() {
				// Pass arguments and context to original handler
				return fn.apply(this, arguments);
			};

			// Store data in unique handler
			handler.data = data;

			// Set the guid of unique handler to the same of original handler, so it can be removed
			handler.guid = fn.guid;
		}

		// Namespaced event handlers
		var parts = type.split(".");
		type = parts[0];
		handler.type = parts[1];

		// Init the element's event structure
		var events = jQuery.data(element, "events") || jQuery.data(element, "events", {});

		var handle = jQuery.data(element, "handle") || jQuery.data(element, "handle", function(){
			// returned undefined or false
			var val;

			// Handle the second event of a trigger and when
			// an event is called after a page has unloaded
			if ( typeof jQuery == "undefined" || jQuery.event.triggered )
				return val;

			val = jQuery.event.handle.apply(element, arguments);

			return val;
		});

		// Get the current list of functions bound to this event
		var handlers = events[type];

		// Init the event handler queue
		if (!handlers) {
			handlers = events[type] = {};

			// And bind the global event handler to the element
			if (element.addEventListener)
				element.addEventListener(type, handle, false);
			else
				element.attachEvent("on" + type, handle);
		}

		// Add the function to the element's handler list
		handlers[handler.guid] = handler;

		// Keep track of which events have been used, for global triggering
		this.global[type] = true;
	},

	guid: 1,
	global: {},

	// Detach an event or set of events from an element
	remove: function(element, type, handler) {
		var events = jQuery.data(element, "events"), ret, index;

		// Namespaced event handlers
		if ( typeof type == "string" ) {
			var parts = type.split(".");
			type = parts[0];
		}

		if ( events ) {
			// type is actually an event object here
			if ( type && type.type ) {
				handler = type.handler;
				type = type.type;
			}

			if ( !type ) {
				for ( type in events )
					this.remove( element, type );

			} else if ( events[type] ) {
				// remove the given handler for the given type
				if ( handler )
					delete events[type][handler.guid];

				// remove all handlers for the given type
				else
					for ( handler in events[type] )
						// Handle the removal of namespaced events
						if ( !parts[1] || events[type][handler].type == parts[1] )
							delete events[type][handler];

				// remove generic event handler if no more handlers exist
				for ( ret in events[type] ) break;
				if ( !ret ) {
					if (element.removeEventListener)
						element.removeEventListener(type, jQuery.data(element, "handle"), false);
					else
						element.detachEvent("on" + type, jQuery.data(element, "handle"));
					ret = null;
					delete events[type];
				}
			}

			// Remove the expando if it's no longer used
			for ( ret in events ) break;
			if ( !ret ) {
				jQuery.removeData( element, "events" );
				jQuery.removeData( element, "handle" );
			}
		}
	},

	trigger: function(type, data, element, donative, extra) {
		// Clone the incoming data, if any
		data = jQuery.makeArray(data || []);

		// Handle a global trigger
		if ( !element ) {
			// Only trigger if we've ever bound an event for it
			if ( this.global[type] )
				jQuery("*").add([window, document]).trigger(type, data);

		// Handle triggering a single element
		} else {
			var val, ret, fn = jQuery.isFunction( element[ type ] || null ),
				// Check to see if we need to provide a fake event, or not
				event = !data[0] || !data[0].preventDefault;

			// Pass along a fake event
			if ( event )
				data.unshift( this.fix({ type: type, target: element }) );

			// Enforce the right trigger type
			data[0].type = type;

			// Trigger the event
			if ( jQuery.isFunction( jQuery.data(element, "handle") ) )
				val = jQuery.data(element, "handle").apply( element, data );

			// Handle triggering native .onfoo handlers
			if ( !fn && element["on"+type] && element["on"+type].apply( element, data ) === false )
				val = false;

			// Extra functions don't get the custom event object
			if ( event )
				data.shift();

			// Handle triggering of extra function
			if ( extra && extra.apply( element, data ) === false )
				val = false;

			// Trigger the native events (except for clicks on links)
			if ( fn && donative !== false && val !== false && !(jQuery.nodeName(element, 'a') && type == "click") ) {
				this.triggered = true;
				element[ type ]();
			}

			this.triggered = false;
		}

		return val;
	},

	handle: function(event) {
		// returned undefined or false
		var val;

		// Empty object is for triggered events with no data
		event = jQuery.event.fix( event || window.event || {} );

		// Namespaced event handlers
		var parts = event.type.split(".");
		event.type = parts[0];

		var handlers = jQuery.data(this, "events") && jQuery.data(this, "events")[event.type], args = Array.prototype.slice.call( arguments, 1 );
		args.unshift( event );

		for ( var j in handlers ) {
			var handler = handlers[j];
			// Pass in a reference to the handler function itself
			// So that we can later remove it
			args[0].handler = handler;
			args[0].data = handler.data;

			// Filter the functions by class
			if ( !parts[1] || handler.type == parts[1] ) {
				var ret = handler.apply( this, args );

				if ( val !== false )
					val = ret;

				if ( ret === false ) {
					event.preventDefault();
					event.stopPropagation();
				}
			}
		}

		// Clean up added properties in IE to prevent memory leak
		if (jQuery.browser.msie)
			event.target = event.preventDefault = event.stopPropagation =
				event.handler = event.data = null;

		return val;
	},

	fix: function(event) {
		// store a copy of the original event object
		// and clone to set read-only properties
		var originalEvent = event;
		event = jQuery.extend({}, originalEvent);

		// add preventDefault and stopPropagation since
		// they will not work on the clone
		event.preventDefault = function() {
			// if preventDefault exists run it on the original event
			if (originalEvent.preventDefault)
				originalEvent.preventDefault();
			// otherwise set the returnValue property of the original event to false (IE)
			originalEvent.returnValue = false;
		};
		event.stopPropagation = function() {
			// if stopPropagation exists run it on the original event
			if (originalEvent.stopPropagation)
				originalEvent.stopPropagation();
			// otherwise set the cancelBubble property of the original event to true (IE)
			originalEvent.cancelBubble = true;
		};

		// Fix target property, if necessary
		if ( !event.target )
			event.target = event.srcElement || document; // Fixes #1925 where srcElement might not be defined either

		// check if target is a textnode (safari)
		if ( event.target.nodeType == 3 )
			event.target = originalEvent.target.parentNode;

		// Add relatedTarget, if necessary
		if ( !event.relatedTarget && event.fromElement )
			event.relatedTarget = event.fromElement == event.target ? event.toElement : event.fromElement;

		// Calculate pageX/Y if missing and clientX/Y available
		if ( event.pageX == null && event.clientX != null ) {
			var doc = document.documentElement, body = document.body;
			event.pageX = event.clientX + (doc && doc.scrollLeft || body && body.scrollLeft || 0) - (doc.clientLeft || 0);
			event.pageY = event.clientY + (doc && doc.scrollTop  || body && body.scrollTop  || 0) - (doc.clientLeft || 0);
		}

		// Add which for key events
		if ( !event.which && (event.charCode || event.keyCode) )
			event.which = event.charCode || event.keyCode;

		// Add metaKey to non-Mac browsers (use ctrl for PC's and Meta for Macs)
		if ( !event.metaKey && event.ctrlKey )
			event.metaKey = event.ctrlKey;

		// Add which for click: 1 == left; 2 == middle; 3 == right
		// Note: button is not normalized, so don't use it
		if ( !event.which && event.button )
			event.which = (event.button & 1 ? 1 : ( event.button & 2 ? 3 : ( event.button & 4 ? 2 : 0 ) ));

		return event;
	}
};

jQuery.fn.extend({
	bind: function( type, data, fn ) {
		return type == "unload" ? this.one(type, data, fn) : this.each(function(){
			jQuery.event.add( this, type, fn || data, fn && data );
		});
	},

	one: function( type, data, fn ) {
		return this.each(function(){
			jQuery.event.add( this, type, function(event) {
				jQuery(this).unbind(event);
				return (fn || data).apply( this, arguments);
			}, fn && data);
		});
	},

	unbind: function( type, fn ) {
		return this.each(function(){
			jQuery.event.remove( this, type, fn );
		});
	},

	trigger: function( type, data, fn ) {
		return this.each(function(){
			jQuery.event.trigger( type, data, this, true, fn );
		});
	},

	triggerHandler: function( type, data, fn ) {
		if ( this[0] )
			return jQuery.event.trigger( type, data, this[0], false, fn );
	},

	toggle: function() {
		// Save reference to arguments for access in closure
		var args = arguments;

		return this.click(function(event) {
			// Figure out which function to execute
			this.lastToggle = 0 == this.lastToggle ? 1 : 0;

			// Make sure that clicks stop
			event.preventDefault();

			// and execute the function
			return args[this.lastToggle].apply( this, [event] ) || false;
		});
	},

	hover: function(fnOver, fnOut) {

		// A private function for handling mouse 'hovering'
		function handleHover(event) {
			// Check if mouse(over|out) are still within the same parent element
			var parent = event.relatedTarget;

			// Traverse up the tree
			while ( parent && parent != this ) try { parent = parent.parentNode; } catch(error) { parent = this; };

			// If we actually just moused on to a sub-element, ignore it
			if ( parent == this ) return false;

			// Execute the right function
			return (event.type == "mouseover" ? fnOver : fnOut).apply(this, [event]);
		}

		// Bind the function to the two event listeners
		return this.mouseover(handleHover).mouseout(handleHover);
	},

	ready: function(fn) {
		// Attach the listeners
		bindReady();

		// If the DOM is already ready
		if ( jQuery.isReady )
			// Execute the function immediately
			fn.apply( document, [jQuery] );

		// Otherwise, remember the function for later
		else
			// Add the function to the wait list
			jQuery.readyList.push( function() { return fn.apply(this, [jQuery]); } );

		return this;
	}
});

jQuery.extend({
	/*
	 * All the code that makes DOM Ready work nicely.
	 */
	isReady: false,
	readyList: [],

	// Handle when the DOM is ready
	ready: function() {
		// Make sure that the DOM is not already loaded
		if ( !jQuery.isReady ) {
			// Remember that the DOM is ready
			jQuery.isReady = true;

			// If there are functions bound, to execute
			if ( jQuery.readyList ) {
				// Execute all of them
				jQuery.each( jQuery.readyList, function(){
					this.apply( document );
				});

				// Reset the list of functions
				jQuery.readyList = null;
			}
			// Remove event listener to avoid memory leak
			if ( document.removeEventListener )
				document.removeEventListener( "DOMContentLoaded", jQuery.ready, false );
		}
	}
});

jQuery.each( ("blur,focus,load,resize,scroll,unload,click,dblclick," +
	"mousedown,mouseup,mousemove,mouseover,mouseout,change,select," +
	"submit,keydown,keypress,keyup,error").split(","), function(i, name){

	// Handle event binding
	jQuery.fn[name] = function(fn){
		return fn ? this.bind(name, fn) : this.trigger(name);
	};
});

var readyBound = false;

function bindReady(){
	if ( readyBound ) return;
	readyBound = true;

	// Mozilla, Opera and webkit nightlies currently support this event
	if ( document.addEventListener )
		// Use the handy event callback
		document.addEventListener( "DOMContentLoaded", jQuery.ready, false );

	// If Safari or IE is used
	// Continually check to see if the document is ready
	if  (jQuery.browser.msie || jQuery.browser.safari ) (function(){
		try {
			// If IE is used, use the trick by Diego Perini
			// http://javascript.nwbox.com/IEContentLoaded/
			if ( jQuery.browser.msie || document.readyState != "loaded" && document.readyState != "complete" )
				document.documentElement.doScroll("left");
		} catch( error ) {
			return setTimeout( arguments.callee, 0 );
		}

		// and execute any waiting functions
		jQuery.ready();
	})();

	// A fallback to window.onload, that will always work
	jQuery.event.add( window, "load", jQuery.ready );
}

// Prevent memory leaks in IE
// And prevent errors on refresh with events like mouseover
// Window isn't included so as not to unbind existing unload events
jQuery(window).bind("unload", function() {
	$("*").add(document).unbind();
});
<commit_msg>Fix for #1933<commit_after>/*
 * A number of helper functions used for managing events.
 * Many of the ideas behind this code orignated from
 * Dean Edwards' addEvent library.
 */
jQuery.event = {

	// Bind an event to an element
	// Original by Dean Edwards
	add: function(element, type, handler, data) {
		// For whatever reason, IE has trouble passing the window object
		// around, causing it to be cloned in the process
		if ( jQuery.browser.msie && element.setInterval != undefined )
			element = window;

		// Make sure that the function being executed has a unique ID
		if ( !handler.guid )
			handler.guid = this.guid++;

		// if data is passed, bind to handler
		if( data != undefined ) {
        	// Create temporary function pointer to original handler
			var fn = handler;

			// Create unique handler function, wrapped around original handler
			handler = function() {
				// Pass arguments and context to original handler
				return fn.apply(this, arguments);
			};

			// Store data in unique handler
			handler.data = data;

			// Set the guid of unique handler to the same of original handler, so it can be removed
			handler.guid = fn.guid;
		}

		// Namespaced event handlers
		var parts = type.split(".");
		type = parts[0];
		handler.type = parts[1];

		// Init the element's event structure
		var events = jQuery.data(element, "events") || jQuery.data(element, "events", {});

		var handle = jQuery.data(element, "handle") || jQuery.data(element, "handle", function(){
			// returned undefined or false
			var val;

			// Handle the second event of a trigger and when
			// an event is called after a page has unloaded
			if ( typeof jQuery == "undefined" || jQuery.event.triggered )
				return val;

			val = jQuery.event.handle.apply(element, arguments);

			return val;
		});

		// Get the current list of functions bound to this event
		var handlers = events[type];

		// Init the event handler queue
		if (!handlers) {
			handlers = events[type] = {};

			// And bind the global event handler to the element
			if (element.addEventListener)
				element.addEventListener(type, handle, false);
			else
				element.attachEvent("on" + type, handle);
		}

		// Add the function to the element's handler list
		handlers[handler.guid] = handler;

		// Keep track of which events have been used, for global triggering
		this.global[type] = true;
	},

	guid: 1,
	global: {},

	// Detach an event or set of events from an element
	remove: function(element, type, handler) {
		var events = jQuery.data(element, "events"), ret, index;

		// Namespaced event handlers
		if ( typeof type == "string" ) {
			var parts = type.split(".");
			type = parts[0];
		}

		if ( events ) {
			// type is actually an event object here
			if ( type && type.type ) {
				handler = type.handler;
				type = type.type;
			}

			if ( !type ) {
				for ( type in events )
					this.remove( element, type );

			} else if ( events[type] ) {
				// remove the given handler for the given type
				if ( handler )
					delete events[type][handler.guid];

				// remove all handlers for the given type
				else
					for ( handler in events[type] )
						// Handle the removal of namespaced events
						if ( !parts[1] || events[type][handler].type == parts[1] )
							delete events[type][handler];

				// remove generic event handler if no more handlers exist
				for ( ret in events[type] ) break;
				if ( !ret ) {
					if (element.removeEventListener)
						element.removeEventListener(type, jQuery.data(element, "handle"), false);
					else
						element.detachEvent("on" + type, jQuery.data(element, "handle"));
					ret = null;
					delete events[type];
				}
			}

			// Remove the expando if it's no longer used
			for ( ret in events ) break;
			if ( !ret ) {
				jQuery.removeData( element, "events" );
				jQuery.removeData( element, "handle" );
			}
		}
	},

	trigger: function(type, data, element, donative, extra) {
		// Clone the incoming data, if any
		data = jQuery.makeArray(data || []);

		// Handle a global trigger
		if ( !element ) {
			// Only trigger if we've ever bound an event for it
			if ( this.global[type] )
				jQuery("*").add([window, document]).trigger(type, data);

		// Handle triggering a single element
		} else {
			var val, ret, fn = jQuery.isFunction( element[ type ] || null ),
				// Check to see if we need to provide a fake event, or not
				event = !data[0] || !data[0].preventDefault;

			// Pass along a fake event
			if ( event )
				data.unshift( this.fix({ type: type, target: element }) );

			// Enforce the right trigger type
			data[0].type = type;

			// Trigger the event
			if ( jQuery.isFunction( jQuery.data(element, "handle") ) )
				val = jQuery.data(element, "handle").apply( element, data );

			// Handle triggering native .onfoo handlers
			if ( !fn && element["on"+type] && element["on"+type].apply( element, data ) === false )
				val = false;

			// Extra functions don't get the custom event object
			if ( event )
				data.shift();

			// Handle triggering of extra function
			if ( extra && extra.apply( element, data ) === false )
				val = false;

			// Trigger the native events (except for clicks on links)
			if ( fn && donative !== false && val !== false && !(jQuery.nodeName(element, 'a') && type == "click") ) {
				this.triggered = true;
				element[ type ]();
			}

			this.triggered = false;
		}

		return val;
	},

	handle: function(event) {
		// returned undefined or false
		var val;

		// Empty object is for triggered events with no data
		event = jQuery.event.fix( event || window.event || {} );

		// Namespaced event handlers
		var parts = event.type.split(".");
		event.type = parts[0];

		var handlers = jQuery.data(this, "events") && jQuery.data(this, "events")[event.type], args = Array.prototype.slice.call( arguments, 1 );
		args.unshift( event );

		for ( var j in handlers ) {
			var handler = handlers[j];
			// Pass in a reference to the handler function itself
			// So that we can later remove it
			args[0].handler = handler;
			args[0].data = handler.data;

			// Filter the functions by class
			if ( !parts[1] || handler.type == parts[1] ) {
				var ret = handler.apply( this, args );

				if ( val !== false )
					val = ret;

				if ( ret === false ) {
					event.preventDefault();
					event.stopPropagation();
				}
			}
		}

		// Clean up added properties in IE to prevent memory leak
		if (jQuery.browser.msie)
			event.target = event.preventDefault = event.stopPropagation =
				event.handler = event.data = null;

		return val;
	},

	fix: function(event) {
		// store a copy of the original event object
		// and clone to set read-only properties
		var originalEvent = event;
		event = jQuery.extend({}, originalEvent);

		// add preventDefault and stopPropagation since
		// they will not work on the clone
		event.preventDefault = function() {
			// if preventDefault exists run it on the original event
			if (originalEvent.preventDefault)
				originalEvent.preventDefault();
			// otherwise set the returnValue property of the original event to false (IE)
			originalEvent.returnValue = false;
		};
		event.stopPropagation = function() {
			// if stopPropagation exists run it on the original event
			if (originalEvent.stopPropagation)
				originalEvent.stopPropagation();
			// otherwise set the cancelBubble property of the original event to true (IE)
			originalEvent.cancelBubble = true;
		};

		// Fix target property, if necessary
		if ( !event.target )
			event.target = event.srcElement || document; // Fixes #1925 where srcElement might not be defined either

		// check if target is a textnode (safari)
		if ( event.target.nodeType == 3 )
			event.target = originalEvent.target.parentNode;

		// Add relatedTarget, if necessary
		if ( !event.relatedTarget && event.fromElement )
			event.relatedTarget = event.fromElement == event.target ? event.toElement : event.fromElement;

		// Calculate pageX/Y if missing and clientX/Y available
		if ( event.pageX == null && event.clientX != null ) {
			var doc = document.documentElement, body = document.body;
			event.pageX = event.clientX + (doc && doc.scrollLeft || body && body.scrollLeft || 0) - (doc.clientLeft || 0);
			event.pageY = event.clientY + (doc && doc.scrollTop  || body && body.scrollTop  || 0) - (doc.clientLeft || 0);
		}

		// Add which for key events
		if ( !event.which && (event.charCode || event.keyCode) )
			event.which = event.charCode || event.keyCode;

		// Add metaKey to non-Mac browsers (use ctrl for PC's and Meta for Macs)
		if ( !event.metaKey && event.ctrlKey )
			event.metaKey = event.ctrlKey;

		// Add which for click: 1 == left; 2 == middle; 3 == right
		// Note: button is not normalized, so don't use it
		if ( !event.which && event.button )
			event.which = (event.button & 1 ? 1 : ( event.button & 2 ? 3 : ( event.button & 4 ? 2 : 0 ) ));

		return event;
	}
};

jQuery.fn.extend({
	bind: function( type, data, fn ) {
		return type == "unload" ? this.one(type, data, fn) : this.each(function(){
			jQuery.event.add( this, type, fn || data, fn && data );
		});
	},

	one: function( type, data, fn ) {
		return this.each(function(){
			jQuery.event.add( this, type, function(event) {
				jQuery(this).unbind(event);
				return (fn || data).apply( this, arguments);
			}, fn && data);
		});
	},

	unbind: function( type, fn ) {
		return this.each(function(){
			jQuery.event.remove( this, type, fn );
		});
	},

	trigger: function( type, data, fn ) {
		return this.each(function(){
			jQuery.event.trigger( type, data, this, true, fn );
		});
	},

	triggerHandler: function( type, data, fn ) {
		if ( this[0] )
			return jQuery.event.trigger( type, data, this[0], false, fn );
	},

	toggle: function() {
		// Save reference to arguments for access in closure
		var args = arguments;

		return this.click(function(event) {
			// Figure out which function to execute
			this.lastToggle = 0 == this.lastToggle ? 1 : 0;

			// Make sure that clicks stop
			event.preventDefault();

			// and execute the function
			return args[this.lastToggle].apply( this, [event] ) || false;
		});
	},

	hover: function(fnOver, fnOut) {

		// A private function for handling mouse 'hovering'
		function handleHover(event) {
			// Check if mouse(over|out) are still within the same parent element
			var parent = event.relatedTarget;

			// Traverse up the tree
			while ( parent && parent != this ) try { parent = parent.parentNode; } catch(error) { parent = this; };

			// If we actually just moused on to a sub-element, ignore it
			if ( parent == this ) return false;

			// Execute the right function
			return (event.type == "mouseover" ? fnOver : fnOut).apply(this, [event]);
		}

		// Bind the function to the two event listeners
		return this.mouseover(handleHover).mouseout(handleHover);
	},

	ready: function(fn) {
		// Attach the listeners
		bindReady();

		// If the DOM is already ready
		if ( jQuery.isReady )
			// Execute the function immediately
			fn.apply( document, [jQuery] );

		// Otherwise, remember the function for later
		else
			// Add the function to the wait list
			jQuery.readyList.push( function() { return fn.apply(this, [jQuery]); } );

		return this;
	}
});

jQuery.extend({
	/*
	 * All the code that makes DOM Ready work nicely.
	 */
	isReady: false,
	readyList: [],

	// Handle when the DOM is ready
	ready: function() {
		// Make sure that the DOM is not already loaded
		if ( !jQuery.isReady ) {
			// Remember that the DOM is ready
			jQuery.isReady = true;

			// If there are functions bound, to execute
			if ( jQuery.readyList ) {
				// Execute all of them
				jQuery.each( jQuery.readyList, function(){
					this.apply( document );
				});

				// Reset the list of functions
				jQuery.readyList = null;
			}
			// Remove event listener to avoid memory leak
			if ( document.removeEventListener )
				document.removeEventListener( "DOMContentLoaded", jQuery.ready, false );
		}
	}
});

jQuery.each( ("blur,focus,load,resize,scroll,unload,click,dblclick," +
	"mousedown,mouseup,mousemove,mouseover,mouseout,change,select," +
	"submit,keydown,keypress,keyup,error").split(","), function(i, name){

	// Handle event binding
	jQuery.fn[name] = function(fn){
		return fn ? this.bind(name, fn) : this.trigger(name);
	};
});

var readyBound = false;

function bindReady(){
	if ( readyBound ) return;
	readyBound = true;

	// Mozilla, Opera and webkit nightlies currently support this event
	if ( document.addEventListener )
		// Use the handy event callback
		document.addEventListener( "DOMContentLoaded", jQuery.ready, false );

	// If Safari or IE is used
	// Continually check to see if the document is ready
	if  (jQuery.browser.msie || jQuery.browser.safari ) (function(){
		try {
			// If IE is used, use the trick by Diego Perini
			// http://javascript.nwbox.com/IEContentLoaded/
			if ( jQuery.browser.msie || document.readyState != "loaded" && document.readyState != "complete" )
				document.documentElement.doScroll("left");
		} catch( error ) {
			return setTimeout( arguments.callee, 0 );
		}

		// and execute any waiting functions
		jQuery.ready();
	})();

	// A fallback to window.onload, that will always work
	jQuery.event.add( window, "load", jQuery.ready );
}

// Prevent memory leaks in IE
// And prevent errors on refresh with events like mouseover
// Window isn't included so as not to unbind existing unload events
jQuery(window).bind("unload", function() {
	jQuery("*").add(document).unbind();
});
<|endoftext|>