UNPKG

fission

Version:
1,396 lines (1,217 loc) 2.73 MB
!function(e){if("object"==typeof exports&&"undefined"!=typeof module)module.exports=e();else if("function"==typeof define&&define.amd)define([],e);else{var f;"undefined"!=typeof window?f=window:"undefined"!=typeof global?f=global:"undefined"!=typeof self&&(f=self),f.fission=e()}}(function(){var define,module,exports;return (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(require,module,exports){ (function (process,global,Buffer,__argument0,__argument1,__argument2,__argument3,__filename,__dirname){ 'use strict'; var clone = require('lodash.clone'); var React = require('react'); // TODO: most of these have nothing to do with the app, dont expose them on the app function Fission(opts) { if (!(this instanceof Fission)) { return new Fission(opts); } this.options = opts ? clone(opts) : {}; } Fission.prototype.router = require('./lib/router'); Fission.prototype.model = require('./lib/model'); Fission.prototype.view = require('./lib/view'); Fission.prototype.modelView = require('./lib/modelView'); Fission.prototype.collectionView = require('./lib/collectionView'); Fission.prototype.createCollection = require('./lib/createCollection'); Fission.prototype.getSync = require('./lib/getSync'); Fission.prototype.alias = require('./lib/alias'); // TODO: remove all but log // TODO: ternary Fission.prototype.middleware = { auth: require('./lib/middleware/auth'), clearFB: require('./lib/middleware/clearFB'), log: require('./lib/middleware/log') }; Fission.prototype.mixins = { Listener: require('./lib/ListenerMixin') }; Fission.prototype.React = React; Fission.prototype.DOM = React.DOM; Fission.prototype.createFactory = React.createFactory; Fission.prototype.PropTypes = React.PropTypes; module.exports = Fission; }).call(this,require('_process'),typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {},require("buffer").Buffer,arguments[3],arguments[4],arguments[5],arguments[6],"/index.js","/") },{"./lib/ListenerMixin":2,"./lib/alias":3,"./lib/collectionView":4,"./lib/createCollection":5,"./lib/getSync":6,"./lib/middleware/auth":7,"./lib/middleware/clearFB":8,"./lib/middleware/log":9,"./lib/model":10,"./lib/modelView":11,"./lib/router":12,"./lib/view":13,"_process":59,"buffer":56,"lodash.clone":62,"react":256}],2:[function(require,module,exports){ (function (process,global,Buffer,__argument0,__argument1,__argument2,__argument3,__filename,__dirname){ 'use strict'; module.exports = { listenTo: function(ee, event, listener) { ee.on(event, function(){ listener(); }); // TODO: push to listeners return this; }, removeListener: function(ee, event, listener) { // TODO: remove from listeners if it exists // TODO: take an object too if (typeof ee.off === 'function') { ee.off(event, listener); } else if (typeof ee.removeListener === 'function') { ee.removeListener(event, listener); } return this; }, componentWillMount: function() { this.listeners = []; }, componentWillUnmount: function() { this.listeners.forEach(function(l){ this.removeListener(l.ee, l.event, l.listener); }); } }; }).call(this,require('_process'),typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {},require("buffer").Buffer,arguments[3],arguments[4],arguments[5],arguments[6],"/lib/ListenerMixin.js","/lib") },{"_process":59,"buffer":56}],3:[function(require,module,exports){ (function (process,global,Buffer,__argument0,__argument1,__argument2,__argument3,__filename,__dirname){ 'use strict'; var aliases = { init: 'getInitialState', mounting: 'componentWillMount', mounted: 'componentDidMount', unmounting: 'componentWillUnmount' }; var aliasKeys = Object.keys(aliases); // warning: mutates input module.exports = function(config) { aliasKeys.forEach(function(k){ var v = aliases[k]; // if config has the alias and no normal react name if (config[k] != null && config[v] == null) { config[v] = config[k]; } }); // always add a mixins array if (config.mixins == null) { config.mixins = []; } return config; }; }).call(this,require('_process'),typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {},require("buffer").Buffer,arguments[3],arguments[4],arguments[5],arguments[6],"/lib/alias.js","/lib") },{"_process":59,"buffer":56}],4:[function(require,module,exports){ (function (process,global,Buffer,__argument0,__argument1,__argument2,__argument3,__filename,__dirname){ 'use strict'; var ListenerMixin = require('./ListenerMixin'); var Collection = require('ampersand-collection'); var React = require('react'); module.exports = function(config) { if (config == null) { throw new Error('config parameter is required'); } if (config.model == null) { throw new Error('model attribute is required'); } if (config.itemView == null) { throw new Error('itemView attribute is required'); } var sync = this.getSync(config.model); var ctor = this.createCollection(config.model); // items = array of item views // collection = ampersand collection var CollectionViewMixin = { mixins: [ListenerMixin], propTypes: { collection: React.PropTypes.instanceOf(Collection), // TODO: id }, getDefaultProps: function(){ return { collection: config.collection }; }, renderItems: function() { // TODO: lazy loading this.items = this.collection.map(function(m) { return this.itemView({ model: m, key: m.cid }); }, this); if (this.isMounted()) { this.forceUpdate(); } }, componentWillMount: function() { // first mount, create items array if (this.items == null) { this.items = []; } // collection passed in if (this.props.collection != null) { this.collection = this.props.collection; this.renderItems(); } else { // collection needs to be constructed this.collection = new ctor(); this.collection.sync = sync; // if an id query is specified, append to url // [used for specifying collection's parent] // (obviously this needs to be more flexible) if (this.props.id != null) { this.collection.url += '?id=' + this.props.id; } this.collection.fetch({ success: this.renderItems, error: function(err) { // TODO: real error management on views console.log('fetch error', err); } }); } this.listenTo(this.collection, 'add change remove', this.renderItems); } }; return this.view(config, [CollectionViewMixin]); }; }).call(this,require('_process'),typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {},require("buffer").Buffer,arguments[3],arguments[4],arguments[5],arguments[6],"/lib/collectionView.js","/lib") },{"./ListenerMixin":2,"_process":59,"ampersand-collection":17,"buffer":56,"react":256}],5:[function(require,module,exports){ (function (process,global,Buffer,__argument0,__argument1,__argument2,__argument3,__filename,__dirname){ 'use strict'; var Collection = require('ampersand-collection'); var underscoreMixin = require('ampersand-collection-underscore-mixin'); var adapter = require('ampersand-sync-adapter'); module.exports = function(model) { // TODO: wtf is this for? if (model.isCollection) { return Collection.extend(model); } var inst = new model(); var conf = { model: model, // TODO: is this right? url is bound to an instance // shouldnt we just pass this straight through? url: inst.url() }; return Collection.extend(underscoreMixin, adapter, conf); }; }).call(this,require('_process'),typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {},require("buffer").Buffer,arguments[3],arguments[4],arguments[5],arguments[6],"/lib/createCollection.js","/lib") },{"_process":59,"ampersand-collection":17,"ampersand-collection-underscore-mixin":15,"ampersand-sync-adapter":41,"buffer":56}],6:[function(require,module,exports){ (function (process,global,Buffer,__argument0,__argument1,__argument2,__argument3,__filename,__dirname){ 'use strict'; var ampSync = require('ampersand-sync'); // TODO: dont rely on `this` anymore // bind in the options module.exports = function(model) { if (model.sync) { return model.sync; } if (this.options.sync == null) { return ampSync; } if (typeof this.options.sync === 'function') { return this.options.sync; } if (this.options.sync.plugin != null) { return this.options.sync.plugin; } throw new Error('Invalid sync option'); }; }).call(this,require('_process'),typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {},require("buffer").Buffer,arguments[3],arguments[4],arguments[5],arguments[6],"/lib/getSync.js","/lib") },{"_process":59,"ampersand-sync":42,"buffer":56}],7:[function(require,module,exports){ (function (process,global,Buffer,__argument0,__argument1,__argument2,__argument3,__filename,__dirname){ /* globals window */ 'use strict'; module.exports = function(me) { return function(ctx, next) { if (me != null) { return next(); } if (window.location.pathname === '/login') { return next(); } window.location = '/login'; return next(); }; }; }).call(this,require('_process'),typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {},require("buffer").Buffer,arguments[3],arguments[4],arguments[5],arguments[6],"/lib/middleware/auth.js","/lib/middleware") },{"_process":59,"buffer":56}],8:[function(require,module,exports){ (function (process,global,Buffer,__argument0,__argument1,__argument2,__argument3,__filename,__dirname){ /* globals window, document */ 'use strict'; module.exports = function(ctx, next) { var scroll; if (window.location.hash && window.location.hash === '#_=_') { if (window.history && window.history.replaceState) { window.history.replaceState(null, null, window.location.href.split('#')[0]); } else { scroll = { top: document.body.scrollTop, left: document.body.scrollLeft }; window.location.hash = ''; document.body.scrollTop = scroll.top; document.body.scrollLeft = scroll.left; } } return next(); }; }).call(this,require('_process'),typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {},require("buffer").Buffer,arguments[3],arguments[4],arguments[5],arguments[6],"/lib/middleware/clearFB.js","/lib/middleware") },{"_process":59,"buffer":56}],9:[function(require,module,exports){ (function (process,global,Buffer,__argument0,__argument1,__argument2,__argument3,__filename,__dirname){ 'use strict'; module.exports = function(ctx, next) { console.log('Routing called', ctx); return next(); }; }).call(this,require('_process'),typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {},require("buffer").Buffer,arguments[3],arguments[4],arguments[5],arguments[6],"/lib/middleware/log.js","/lib/middleware") },{"_process":59,"buffer":56}],10:[function(require,module,exports){ (function (process,global,Buffer,__argument0,__argument1,__argument2,__argument3,__filename,__dirname){ 'use strict'; var Model = require('ampersand-model'); var clone = require('lodash.clone'); module.exports = function(config) { var model = clone(config); if (model.url == null) { model.url = '/'; } model.urlRoot = model.url; delete model.url; model.sync = this.getSync(model); return Model.extend(model); }; }).call(this,require('_process'),typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {},require("buffer").Buffer,arguments[3],arguments[4],arguments[5],arguments[6],"/lib/model.js","/lib") },{"_process":59,"ampersand-model":20,"buffer":56,"lodash.clone":62}],11:[function(require,module,exports){ (function (process,global,Buffer,__argument0,__argument1,__argument2,__argument3,__filename,__dirname){ 'use strict'; var ListenerMixin = require('./ListenerMixin'); var Model = require('ampersand-model'); var React = require('react'); module.exports = function(config) { if (config == null) { throw new Error('config parameter is required'); } var ModelViewMixin = { mixins: [ListenerMixin], propTypes: { model: React.PropTypes.instanceOf(Model), }, componentWillMount: function() { // collection view passing in model if (this.props.model != null) { this.model = this.props.model; // router is passing in params } else if (this.props.params && this.props.params.id != null) { this.model = new config.model(); this.model.id = this.props.params.id; // set id attrib to id - needed for fetching if (this.model.idAttribute !== undefined) { this.model[this.model.idAttribute] = this.props.params.id; } this.model.fetch(); } // no model specified in props or config if (!this.model) { throw new Error('modelView never got a model'); } var update = function(){ if (this.isMounted()) { this.forceUpdate(); } }.bind(this); this.listenTo(this.model, 'change', update); return this.model; } }; return this.view(config, [ModelViewMixin]); }; }).call(this,require('_process'),typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {},require("buffer").Buffer,arguments[3],arguments[4],arguments[5],arguments[6],"/lib/modelView.js","/lib") },{"./ListenerMixin":2,"_process":59,"ampersand-model":20,"buffer":56,"react":256}],12:[function(require,module,exports){ (function (process,global,Buffer,__argument0,__argument1,__argument2,__argument3,__filename,__dirname){ /*globals document*/ 'use strict'; var page = require('page'); var React = require('react'); var clone = require('lodash.clone'); var router = {}; var renderView = function(opt, cb) { opt = opt ? clone(opt) : {}; // opt.view is a function if (typeof opt.view === 'function') { opt.view = opt.view(opt.args); return renderView(opt, cb); } // opt.view is already a view React.renderComponent(opt.view, opt.el); cb(); }; router.createRouteHandler = function(opt) { // require in a module if (typeof opt === 'string') { return function(ctx, next) { require([opt], function(fn) { fn(ctx, next); }); }; } // just use function (middleware) else if (typeof opt === 'function') { return function (ctx, next){ opt(ctx, next); }; } // use the options return router.createRenderHandler(opt); }; router.createRenderHandler = function(opt) { opt = opt ? clone(opt) : {}; if (opt.continue == null) { opt.continue = false; } // opt is a dom selector if (typeof opt.el === 'string') { opt.el = document.getElementById(opt.el); } var handler = function(ctx, next) { var nopt = { el: opt.el, view: opt.view, args: ctx }; renderView(nopt, function() { if (opt.title != null) { document.title = opt.title; } if (opt.continue) { next(); } }); }; return handler; }; router.route = function() { var args = Array.prototype.slice.call(arguments); var route = args.shift(); var handlers = args.map(router.createRouteHandler); page.apply(null, [route].concat(handlers)); return router; }; router.router = page; router.use = router.route.bind(null, '*'); router.start = page.start; router.show = page.show; module.exports = router; }).call(this,require('_process'),typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {},require("buffer").Buffer,arguments[3],arguments[4],arguments[5],arguments[6],"/lib/router.js","/lib") },{"_process":59,"buffer":56,"lodash.clone":62,"page":108,"react":256}],13:[function(require,module,exports){ (function (process,global,Buffer,__argument0,__argument1,__argument2,__argument3,__filename,__dirname){ 'use strict'; var React = require('react'); var clone = require('lodash.clone'); var alias = require('./alias'); module.exports = function(config, mixins) { var vu = alias(clone(config)); if (Array.isArray(mixins)) { vu.mixins = vu.mixins.concat(mixins); } return React.createClass(vu); }; }).call(this,require('_process'),typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {},require("buffer").Buffer,arguments[3],arguments[4],arguments[5],arguments[6],"/lib/view.js","/lib") },{"./alias":3,"_process":59,"buffer":56,"lodash.clone":62,"react":256}],14:[function(require,module,exports){ (function (process,global,Buffer,__argument0,__argument1,__argument2,__argument3,__filename,__dirname){ var objectExtend = require('extend-object'); /// Following code is largely pasted from Backbone.js // Helper function to correctly set up the prototype chain, for subclasses. // Similar to `goog.inherits`, but uses a hash of prototype properties and // class properties to be extended. var extend = function(protoProps) { var parent = this; var child; var args = [].slice.call(arguments); // The constructor function for the new subclass is either defined by you // (the "constructor" property in your `extend` definition), or defaulted // by us to simply call the parent's constructor. if (protoProps && protoProps.hasOwnProperty('constructor')) { child = protoProps.constructor; } else { child = function () { return parent.apply(this, arguments); }; } // Add static properties to the constructor function from parent objectExtend(child, parent); // Set the prototype chain to inherit from `parent`, without calling // `parent`'s constructor function. var Surrogate = function(){ this.constructor = child; }; Surrogate.prototype = parent.prototype; child.prototype = new Surrogate(); // Mix in all prototype properties to the subclass if supplied. if (protoProps) { args.unshift(child.prototype); objectExtend.apply(null, args); } // Set a convenience property in case the parent's prototype is needed // later. child.__super__ = parent.prototype; return child; }; // Expose the extend function module.exports = extend; }).call(this,require('_process'),typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {},require("buffer").Buffer,arguments[3],arguments[4],arguments[5],arguments[6],"/node_modules/ampersand-class-extend/ampersand-class-extend.js","/node_modules/ampersand-class-extend") },{"_process":59,"buffer":56,"extend-object":60}],15:[function(require,module,exports){ (function (process,global,Buffer,__argument0,__argument1,__argument2,__argument3,__filename,__dirname){ ;if (typeof window !== "undefined") { window.ampersand = window.ampersand || {}; window.ampersand["ampersand-collection-underscore-mixin"] = window.ampersand["ampersand-collection-underscore-mixin"] || []; window.ampersand["ampersand-collection-underscore-mixin"].push("1.0.3");} var _ = require('underscore'); var slice = [].slice; var mixins = {}; // Underscore methods that we want to implement on the Collection. var methods = ['forEach', 'each', 'map', 'collect', 'reduce', 'foldl', 'inject', 'reduceRight', 'foldr', 'find', 'detect', 'filter', 'select', 'reject', 'every', 'all', 'some', 'any', 'include', 'contains', 'invoke', 'max', 'min', 'toArray', 'size', 'first', 'head', 'take', 'initial', 'rest', 'tail', 'drop', 'last', 'without', 'difference', 'indexOf', 'shuffle', 'lastIndexOf', 'isEmpty', 'chain', 'sample', 'partition' ]; // Mix in each Underscore method as a proxy to `Collection#models`. _.each(methods, function (method) { if (!_[method]) return; mixins[method] = function () { var args = slice.call(arguments); args.unshift(this.models); return _[method].apply(_, args); }; }); // Underscore methods that take a property name as an argument. var attributeMethods = ['groupBy', 'countBy', 'sortBy', 'indexBy']; // Use attributes instead of properties. _.each(attributeMethods, function (method) { if (!_[method]) return; mixins[method] = function (value, context) { var iterator = _.isFunction(value) ? value : function (model) { return model.get ? model.get(value) : model[value]; }; return _[method](this.models, iterator, context); }; }); // Return models with matching attributes. Useful for simple cases of // `filter`. mixins.where = function (attrs, first) { if (_.isEmpty(attrs)) return first ? void 0 : []; return this[first ? 'find' : 'filter'](function (model) { var value; for (var key in attrs) { value = model.get ? model.get(key) : model[key]; if (attrs[key] !== value) return false; } return true; }); }; // Return the first model with matching attributes. Useful for simple cases // of `find`. mixins.findWhere = function (attrs) { return this.where(attrs, true); }; // Plucks an attribute from each model in the collection. mixins.pluck = function (attr) { return _.invoke(this.models, 'get', attr); }; module.exports = mixins; }).call(this,require('_process'),typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {},require("buffer").Buffer,arguments[3],arguments[4],arguments[5],arguments[6],"/node_modules/ampersand-collection-underscore-mixin/ampersand-collection-underscore-mixin.js","/node_modules/ampersand-collection-underscore-mixin") },{"_process":59,"buffer":56,"underscore":16}],16:[function(require,module,exports){ (function (process,global,Buffer,__argument0,__argument1,__argument2,__argument3,__filename,__dirname){ // Underscore.js 1.7.0 // http://underscorejs.org // (c) 2009-2014 Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors // Underscore may be freely distributed under the MIT license. (function() { // Baseline setup // -------------- // Establish the root object, `window` in the browser, or `exports` on the server. var root = this; // Save the previous value of the `_` variable. var previousUnderscore = root._; // Save bytes in the minified (but not gzipped) version: var ArrayProto = Array.prototype, ObjProto = Object.prototype, FuncProto = Function.prototype; // Create quick reference variables for speed access to core prototypes. var push = ArrayProto.push, slice = ArrayProto.slice, concat = ArrayProto.concat, toString = ObjProto.toString, hasOwnProperty = ObjProto.hasOwnProperty; // All **ECMAScript 5** native function implementations that we hope to use // are declared here. var nativeIsArray = Array.isArray, nativeKeys = Object.keys, nativeBind = FuncProto.bind; // Create a safe reference to the Underscore object for use below. var _ = function(obj) { if (obj instanceof _) return obj; if (!(this instanceof _)) return new _(obj); this._wrapped = obj; }; // Export the Underscore object for **Node.js**, with // backwards-compatibility for the old `require()` API. If we're in // the browser, add `_` as a global object. if (typeof exports !== 'undefined') { if (typeof module !== 'undefined' && module.exports) { exports = module.exports = _; } exports._ = _; } else { root._ = _; } // Current version. _.VERSION = '1.7.0'; // Internal function that returns an efficient (for current engines) version // of the passed-in callback, to be repeatedly applied in other Underscore // functions. var createCallback = function(func, context, argCount) { if (context === void 0) return func; switch (argCount == null ? 3 : argCount) { case 1: return function(value) { return func.call(context, value); }; case 2: return function(value, other) { return func.call(context, value, other); }; case 3: return function(value, index, collection) { return func.call(context, value, index, collection); }; case 4: return function(accumulator, value, index, collection) { return func.call(context, accumulator, value, index, collection); }; } return function() { return func.apply(context, arguments); }; }; // A mostly-internal function to generate callbacks that can be applied // to each element in a collection, returning the desired result — either // identity, an arbitrary callback, a property matcher, or a property accessor. _.iteratee = function(value, context, argCount) { if (value == null) return _.identity; if (_.isFunction(value)) return createCallback(value, context, argCount); if (_.isObject(value)) return _.matches(value); return _.property(value); }; // Collection Functions // -------------------- // The cornerstone, an `each` implementation, aka `forEach`. // Handles raw objects in addition to array-likes. Treats all // sparse array-likes as if they were dense. _.each = _.forEach = function(obj, iteratee, context) { if (obj == null) return obj; iteratee = createCallback(iteratee, context); var i, length = obj.length; if (length === +length) { for (i = 0; i < length; i++) { iteratee(obj[i], i, obj); } } else { var keys = _.keys(obj); for (i = 0, length = keys.length; i < length; i++) { iteratee(obj[keys[i]], keys[i], obj); } } return obj; }; // Return the results of applying the iteratee to each element. _.map = _.collect = function(obj, iteratee, context) { if (obj == null) return []; iteratee = _.iteratee(iteratee, context); var keys = obj.length !== +obj.length && _.keys(obj), length = (keys || obj).length, results = Array(length), currentKey; for (var index = 0; index < length; index++) { currentKey = keys ? keys[index] : index; results[index] = iteratee(obj[currentKey], currentKey, obj); } return results; }; var reduceError = 'Reduce of empty array with no initial value'; // **Reduce** builds up a single result from a list of values, aka `inject`, // or `foldl`. _.reduce = _.foldl = _.inject = function(obj, iteratee, memo, context) { if (obj == null) obj = []; iteratee = createCallback(iteratee, context, 4); var keys = obj.length !== +obj.length && _.keys(obj), length = (keys || obj).length, index = 0, currentKey; if (arguments.length < 3) { if (!length) throw new TypeError(reduceError); memo = obj[keys ? keys[index++] : index++]; } for (; index < length; index++) { currentKey = keys ? keys[index] : index; memo = iteratee(memo, obj[currentKey], currentKey, obj); } return memo; }; // The right-associative version of reduce, also known as `foldr`. _.reduceRight = _.foldr = function(obj, iteratee, memo, context) { if (obj == null) obj = []; iteratee = createCallback(iteratee, context, 4); var keys = obj.length !== + obj.length && _.keys(obj), index = (keys || obj).length, currentKey; if (arguments.length < 3) { if (!index) throw new TypeError(reduceError); memo = obj[keys ? keys[--index] : --index]; } while (index--) { currentKey = keys ? keys[index] : index; memo = iteratee(memo, obj[currentKey], currentKey, obj); } return memo; }; // Return the first value which passes a truth test. Aliased as `detect`. _.find = _.detect = function(obj, predicate, context) { var result; predicate = _.iteratee(predicate, context); _.some(obj, function(value, index, list) { if (predicate(value, index, list)) { result = value; return true; } }); return result; }; // Return all the elements that pass a truth test. // Aliased as `select`. _.filter = _.select = function(obj, predicate, context) { var results = []; if (obj == null) return results; predicate = _.iteratee(predicate, context); _.each(obj, function(value, index, list) { if (predicate(value, index, list)) results.push(value); }); return results; }; // Return all the elements for which a truth test fails. _.reject = function(obj, predicate, context) { return _.filter(obj, _.negate(_.iteratee(predicate)), context); }; // Determine whether all of the elements match a truth test. // Aliased as `all`. _.every = _.all = function(obj, predicate, context) { if (obj == null) return true; predicate = _.iteratee(predicate, context); var keys = obj.length !== +obj.length && _.keys(obj), length = (keys || obj).length, index, currentKey; for (index = 0; index < length; index++) { currentKey = keys ? keys[index] : index; if (!predicate(obj[currentKey], currentKey, obj)) return false; } return true; }; // Determine if at least one element in the object matches a truth test. // Aliased as `any`. _.some = _.any = function(obj, predicate, context) { if (obj == null) return false; predicate = _.iteratee(predicate, context); var keys = obj.length !== +obj.length && _.keys(obj), length = (keys || obj).length, index, currentKey; for (index = 0; index < length; index++) { currentKey = keys ? keys[index] : index; if (predicate(obj[currentKey], currentKey, obj)) return true; } return false; }; // Determine if the array or object contains a given value (using `===`). // Aliased as `include`. _.contains = _.include = function(obj, target) { if (obj == null) return false; if (obj.length !== +obj.length) obj = _.values(obj); return _.indexOf(obj, target) >= 0; }; // Invoke a method (with arguments) on every item in a collection. _.invoke = function(obj, method) { var args = slice.call(arguments, 2); var isFunc = _.isFunction(method); return _.map(obj, function(value) { return (isFunc ? method : value[method]).apply(value, args); }); }; // Convenience version of a common use case of `map`: fetching a property. _.pluck = function(obj, key) { return _.map(obj, _.property(key)); }; // Convenience version of a common use case of `filter`: selecting only objects // containing specific `key:value` pairs. _.where = function(obj, attrs) { return _.filter(obj, _.matches(attrs)); }; // Convenience version of a common use case of `find`: getting the first object // containing specific `key:value` pairs. _.findWhere = function(obj, attrs) { return _.find(obj, _.matches(attrs)); }; // Return the maximum element (or element-based computation). _.max = function(obj, iteratee, context) { var result = -Infinity, lastComputed = -Infinity, value, computed; if (iteratee == null && obj != null) { obj = obj.length === +obj.length ? obj : _.values(obj); for (var i = 0, length = obj.length; i < length; i++) { value = obj[i]; if (value > result) { result = value; } } } else { iteratee = _.iteratee(iteratee, context); _.each(obj, function(value, index, list) { computed = iteratee(value, index, list); if (computed > lastComputed || computed === -Infinity && result === -Infinity) { result = value; lastComputed = computed; } }); } return result; }; // Return the minimum element (or element-based computation). _.min = function(obj, iteratee, context) { var result = Infinity, lastComputed = Infinity, value, computed; if (iteratee == null && obj != null) { obj = obj.length === +obj.length ? obj : _.values(obj); for (var i = 0, length = obj.length; i < length; i++) { value = obj[i]; if (value < result) { result = value; } } } else { iteratee = _.iteratee(iteratee, context); _.each(obj, function(value, index, list) { computed = iteratee(value, index, list); if (computed < lastComputed || computed === Infinity && result === Infinity) { result = value; lastComputed = computed; } }); } return result; }; // Shuffle a collection, using the modern version of the // [Fisher-Yates shuffle](http://en.wikipedia.org/wiki/Fisher–Yates_shuffle). _.shuffle = function(obj) { var set = obj && obj.length === +obj.length ? obj : _.values(obj); var length = set.length; var shuffled = Array(length); for (var index = 0, rand; index < length; index++) { rand = _.random(0, index); if (rand !== index) shuffled[index] = shuffled[rand]; shuffled[rand] = set[index]; } return shuffled; }; // Sample **n** random values from a collection. // If **n** is not specified, returns a single random element. // The internal `guard` argument allows it to work with `map`. _.sample = function(obj, n, guard) { if (n == null || guard) { if (obj.length !== +obj.length) obj = _.values(obj); return obj[_.random(obj.length - 1)]; } return _.shuffle(obj).slice(0, Math.max(0, n)); }; // Sort the object's values by a criterion produced by an iteratee. _.sortBy = function(obj, iteratee, context) { iteratee = _.iteratee(iteratee, context); return _.pluck(_.map(obj, function(value, index, list) { return { value: value, index: index, criteria: iteratee(value, index, list) }; }).sort(function(left, right) { var a = left.criteria; var b = right.criteria; if (a !== b) { if (a > b || a === void 0) return 1; if (a < b || b === void 0) return -1; } return left.index - right.index; }), 'value'); }; // An internal function used for aggregate "group by" operations. var group = function(behavior) { return function(obj, iteratee, context) { var result = {}; iteratee = _.iteratee(iteratee, context); _.each(obj, function(value, index) { var key = iteratee(value, index, obj); behavior(result, value, key); }); return result; }; }; // Groups the object's values by a criterion. Pass either a string attribute // to group by, or a function that returns the criterion. _.groupBy = group(function(result, value, key) { if (_.has(result, key)) result[key].push(value); else result[key] = [value]; }); // Indexes the object's values by a criterion, similar to `groupBy`, but for // when you know that your index values will be unique. _.indexBy = group(function(result, value, key) { result[key] = value; }); // Counts instances of an object that group by a certain criterion. Pass // either a string attribute to count by, or a function that returns the // criterion. _.countBy = group(function(result, value, key) { if (_.has(result, key)) result[key]++; else result[key] = 1; }); // Use a comparator function to figure out the smallest index at which // an object should be inserted so as to maintain order. Uses binary search. _.sortedIndex = function(array, obj, iteratee, context) { iteratee = _.iteratee(iteratee, context, 1); var value = iteratee(obj); var low = 0, high = array.length; while (low < high) { var mid = low + high >>> 1; if (iteratee(array[mid]) < value) low = mid + 1; else high = mid; } return low; }; // Safely create a real, live array from anything iterable. _.toArray = function(obj) { if (!obj) return []; if (_.isArray(obj)) return slice.call(obj); if (obj.length === +obj.length) return _.map(obj, _.identity); return _.values(obj); }; // Return the number of elements in an object. _.size = function(obj) { if (obj == null) return 0; return obj.length === +obj.length ? obj.length : _.keys(obj).length; }; // Split a collection into two arrays: one whose elements all satisfy the given // predicate, and one whose elements all do not satisfy the predicate. _.partition = function(obj, predicate, context) { predicate = _.iteratee(predicate, context); var pass = [], fail = []; _.each(obj, function(value, key, obj) { (predicate(value, key, obj) ? pass : fail).push(value); }); return [pass, fail]; }; // Array Functions // --------------- // Get the first element of an array. Passing **n** will return the first N // values in the array. Aliased as `head` and `take`. The **guard** check // allows it to work with `_.map`. _.first = _.head = _.take = function(array, n, guard) { if (array == null) return void 0; if (n == null || guard) return array[0]; if (n < 0) return []; return slice.call(array, 0, n); }; // Returns everything but the last entry of the array. Especially useful on // the arguments object. Passing **n** will return all the values in // the array, excluding the last N. The **guard** check allows it to work with // `_.map`. _.initial = function(array, n, guard) { return slice.call(array, 0, Math.max(0, array.length - (n == null || guard ? 1 : n))); }; // Get the last element of an array. Passing **n** will return the last N // values in the array. The **guard** check allows it to work with `_.map`. _.last = function(array, n, guard) { if (array == null) return void 0; if (n == null || guard) return array[array.length - 1]; return slice.call(array, Math.max(array.length - n, 0)); }; // Returns everything but the first entry of the array. Aliased as `tail` and `drop`. // Especially useful on the arguments object. Passing an **n** will return // the rest N values in the array. The **guard** // check allows it to work with `_.map`. _.rest = _.tail = _.drop = function(array, n, guard) { return slice.call(array, n == null || guard ? 1 : n); }; // Trim out all falsy values from an array. _.compact = function(array) { return _.filter(array, _.identity); }; // Internal implementation of a recursive `flatten` function. var flatten = function(input, shallow, strict, output) { if (shallow && _.every(input, _.isArray)) { return concat.apply(output, input); } for (var i = 0, length = input.length; i < length; i++) { var value = input[i]; if (!_.isArray(value) && !_.isArguments(value)) { if (!strict) output.push(value); } else if (shallow) { push.apply(output, value); } else { flatten(value, shallow, strict, output); } } return output; }; // Flatten out an array, either recursively (by default), or just one level. _.flatten = function(array, shallow) { return flatten(array, shallow, false, []); }; // Return a version of the array that does not contain the specified value(s). _.without = function(array) { return _.difference(array, slice.call(arguments, 1)); }; // Produce a duplicate-free version of the array. If the array has already // been sorted, you have the option of using a faster algorithm. // Aliased as `unique`. _.uniq = _.unique = function(array, isSorted, iteratee, context) { if (array == null) return []; if (!_.isBoolean(isSorted)) { context = iteratee; iteratee = isSorted; isSorted = false; } if (iteratee != null) iteratee = _.iteratee(iteratee, context); var result = []; var seen = []; for (var i = 0, length = array.length; i < length; i++) { var value = array[i]; if (isSorted) { if (!i || seen !== value) result.push(value); seen = value; } else if (iteratee) { var computed = iteratee(value, i, array); if (_.indexOf(seen, computed) < 0) { seen.push(computed); result.push(value); } } else if (_.indexOf(result, value) < 0) { result.push(value); } } return result; }; // Produce an array that contains the union: each distinct element from all of // the passed-in arrays. _.union = function() { return _.uniq(flatten(arguments, true, true, [])); }; // Produce an array that contains every item shared between all the // passed-in arrays. _.intersection = function(array) { if (array == null) return []; var result = []; var argsLength = arguments.length; for (var i = 0, length = array.length; i < length; i++) { var item = array[i]; if (_.contains(result, item)) continue; for (var j = 1; j < argsLength; j++) { if (!_.contains(arguments[j], item)) break; } if (j === argsLength) result.push(item); } return result; }; // Take the difference between one array and a number of other arrays. // Only the elements present in just the first array will remain. _.difference = function(array) { var rest = flatten(slice.call(arguments, 1), true, true, []); return _.filter(array, function(value){ return !_.contains(rest, value); }); }; // Zip together multiple lists into a single array -- elements that share // an index go together. _.zip = function(array) { if (array == null) return []; var length = _.max(arguments, 'length').length; var results = Array(length); for (var i = 0; i < length; i++) { results[i] = _.pluck(arguments, i); } return results; }; // Converts lists into objects. Pass either a single array of `[key, value]` // pairs, or two parallel arrays of the same length -- one of keys, and one of // the corresponding values. _.object = function(list, values) { if (list == null) return {}; var result = {}; for (var i = 0, length = list.length; i < length; i++) { if (values) { result[list[i]] = values[i]; } else { result[list[i][0]] = list[i][1]; } } return result; }; // Return the position of the first occurrence of an item in an array, // or -1 if the item is not included in the array. // If the array is large and already in sort order, pass `true` // for **isSorted** to use binary search. _.indexOf = function(array, item, isSorted) { if (array == null) return -1; var i = 0, length = array.length; if (isSorted) { if (typeof isSorted == 'number') { i = isSorted < 0 ? Math.max(0, length + isSorted) : isSorted; } else { i = _.sortedIndex(array, item); return array[i] === item ? i : -1; } } for (; i < length; i++) if (array[i] === item) return i; return -1; }; _.lastIndexOf = function(array, item, from) { if (array == null) return -1; var idx = array.length; if (typeof from == 'number') { idx = from < 0 ? idx + from + 1 : Math.min(idx, from + 1); } while (--idx >= 0) if (array[idx] === item) return idx; return -1; }; // Generate an integer Array containing an arithmetic progression. A port of // the native Python `range()` function. See // [the Python documentation](http://docs.python.org/library/functions.html#range). _.range = function(start, stop, step) { if (arguments.length <= 1) { stop = start || 0; start = 0; } step = step || 1; var length = Math.max(Math.ceil((stop - start) / step), 0); var range = Array(length); for (var idx = 0; idx < length; idx++, start += step) { range[idx] = start; } return range; }; // Function (ahem) Functions // ------------------ // Reusable constructor function for prototype setting. var Ctor = function(){}; // Create a function bound to a given object (assigning `this`, and arguments, // optionally). Delegates to **ECMAScript 5**'s native `Function.bind` if // available. _.bind = function(func, context) { var args, bound; if (nativeBind && func.bind === nativeBind) return nativeBind.apply(func, slice.call(arguments, 1)); if (!_.isFunction(func)) throw new TypeError('Bind must be called on a function'); args = slice.call(arguments, 2); bound = function() { if (!(this instanceof bound)) return func.apply(context, args.concat(slice.call(arguments))); Ctor.prototype = func.prototype; var self = new Ctor; Ctor.prototype = null; var result = func.apply(self, args.concat(slice.call(arguments))); if (_.isObject(result)) return result; return self; }; return bound; }; // Partially apply a function by creating a version that has had some of its // arguments pre-filled, without changing its dynamic `this` context. _ acts // as a placeholder, allowing any combination of arguments to be pre-filled. _.partial = function(func) { var boundArgs = slice.call(arguments, 1); return function() { var position = 0; var args = boundArgs.slice(); for (var i = 0, length = args.length; i < length; i++) { if (args[i] === _) args[i] = arguments[position++]; } while (position < arguments.length) args.push(arguments[position++]); return func.apply(this, args); }; }; // Bind a number of an object's methods to that object. Remaining arguments // are the method names to be bound. Useful for ensuring that all callbacks // defined on an object belong to it. _.bindAll = function(obj) { var i, length = arguments.length, key; if (length <= 1) throw new Error('bindAll must be passed function names'); for (i = 1; i < length; i++) { key = arguments[i]; obj[key] = _.bind(obj[key], obj); } return obj; }; // Memoize an expensive function by storing its results. _.memoize = function(func, hasher) { var memoize = function(key) { var cache = memoize.cache; var address = hasher ? hasher.apply(this, arguments) : key; if (!_.has(cache, address)) cache[address] = func.apply(this, arguments); return cache[address]; }; memoize.cache = {}; return memoize; }; // Delays a function for the given number of milliseconds, and then calls // it with the arguments supplied. _.delay = function(func, wait) { var args = slice.call(arguments, 2); return setTimeout(function(){ return func.apply(null, args); }, wait); }; // Defers a function, scheduling it to run after the current call stack has // cleared. _.defer = function(func) { return _.delay.apply(_, [func, 1].concat(slice.call(arguments, 1))); }; // Returns a function, that, when invoked, will only be triggered at most once // during a given window of time. Normally, the throttled function will run // as much as it can, without ever going more than once per `wait` duration; // but if you'd like to disable the execution on the leading edge, pass // `{leading: false}`. To disable execution on the trailing edge, ditto. _.throttle = function(func, wait, options) { var context, args, result; var timeout = null; var previous = 0; if (!options) options = {}; var later = function() { previous = options.leading === false ? 0 : _.now(); timeout = null; result = func.apply(context, args); if (!timeout) context = args = null; }; return function() { var now = _.now(); if (!previous && options.leading === false) previous = now; var remaining = wait - (now - previous); context = this; args = arguments; if (remaining <= 0 || remaining > wait) { clearTimeout(timeout); timeout = null; previous = now; result = func.apply(context, args); if (!timeout) context = args = null; } else if (!timeout && options.trailing !== false) { timeout = setTimeout(later, remaining); } return result; }; }; // Returns a function, that, as long as it continues to be invoked, will not // be triggered. The function will be called after it stops being called for // N milliseconds. If `immediate` is passed, trigger the function on the // leading edge, instead of the trailing. _.debounce = function(func, wait, immediate) { var timeout, args, context, timestamp, result; var later = function() { var last = _.now() - timestamp; if (last < wait && last > 0) { timeout = setTimeout(later, wait - last); } else { timeout = null; if (!immediate) { result = func.apply(context, args); if (!timeout) context = args = null; } } }; return function() { context = this; args = arguments; timestamp = _.now(); var callNow = immediate && !timeout; if (!timeout) timeout = setTimeout(