Commit 873d1ad3
Changed files (8)
vendor
vendor/assets/javascripts/backbone.babysitter.js
@@ -0,0 +1,190 @@
+// Backbone.BabySitter
+// -------------------
+// v0.1.4
+//
+// Copyright (c)2014 Derick Bailey, Muted Solutions, LLC.
+// Distributed under MIT license
+//
+// http://github.com/marionettejs/backbone.babysitter
+
+(function(root, factory) {
+
+ if (typeof define === 'function' && define.amd) {
+ define(['backbone', 'underscore'], function(Backbone, _) {
+ return factory(Backbone, _);
+ });
+ } else if (typeof exports !== 'undefined') {
+ var Backbone = require('backbone');
+ var _ = require('underscore');
+ module.exports = factory(Backbone, _);
+ } else {
+ factory(root.Backbone, root._);
+ }
+
+}(this, function(Backbone, _) {
+ 'use strict';
+
+ var previousChildViewContainer = Backbone.ChildViewContainer;
+
+ // BabySitter.ChildViewContainer
+ // -----------------------------
+ //
+ // Provide a container to store, retrieve and
+ // shut down child views.
+
+ Backbone.ChildViewContainer = (function (Backbone, _) {
+
+ // Container Constructor
+ // ---------------------
+
+ var Container = function(views){
+ this._views = {};
+ this._indexByModel = {};
+ this._indexByCustom = {};
+ this._updateLength();
+
+ _.each(views, this.add, this);
+ };
+
+ // Container Methods
+ // -----------------
+
+ _.extend(Container.prototype, {
+
+ // Add a view to this container. Stores the view
+ // by `cid` and makes it searchable by the model
+ // cid (and model itself). Optionally specify
+ // a custom key to store an retrieve the view.
+ add: function(view, customIndex){
+ var viewCid = view.cid;
+
+ // store the view
+ this._views[viewCid] = view;
+
+ // index it by model
+ if (view.model){
+ this._indexByModel[view.model.cid] = viewCid;
+ }
+
+ // index by custom
+ if (customIndex){
+ this._indexByCustom[customIndex] = viewCid;
+ }
+
+ this._updateLength();
+ return this;
+ },
+
+ // Find a view by the model that was attached to
+ // it. Uses the model's `cid` to find it.
+ findByModel: function(model){
+ return this.findByModelCid(model.cid);
+ },
+
+ // Find a view by the `cid` of the model that was attached to
+ // it. Uses the model's `cid` to find the view `cid` and
+ // retrieve the view using it.
+ findByModelCid: function(modelCid){
+ var viewCid = this._indexByModel[modelCid];
+ return this.findByCid(viewCid);
+ },
+
+ // Find a view by a custom indexer.
+ findByCustom: function(index){
+ var viewCid = this._indexByCustom[index];
+ return this.findByCid(viewCid);
+ },
+
+ // Find by index. This is not guaranteed to be a
+ // stable index.
+ findByIndex: function(index){
+ return _.values(this._views)[index];
+ },
+
+ // retrieve a view by its `cid` directly
+ findByCid: function(cid){
+ return this._views[cid];
+ },
+
+ // Remove a view
+ remove: function(view){
+ var viewCid = view.cid;
+
+ // delete model index
+ if (view.model){
+ delete this._indexByModel[view.model.cid];
+ }
+
+ // delete custom index
+ _.any(this._indexByCustom, function(cid, key) {
+ if (cid === viewCid) {
+ delete this._indexByCustom[key];
+ return true;
+ }
+ }, this);
+
+ // remove the view from the container
+ delete this._views[viewCid];
+
+ // update the length
+ this._updateLength();
+ return this;
+ },
+
+ // Call a method on every view in the container,
+ // passing parameters to the call method one at a
+ // time, like `function.call`.
+ call: function(method){
+ this.apply(method, _.tail(arguments));
+ },
+
+ // Apply a method on every view in the container,
+ // passing parameters to the call method one at a
+ // time, like `function.apply`.
+ apply: function(method, args){
+ _.each(this._views, function(view){
+ if (_.isFunction(view[method])){
+ view[method].apply(view, args || []);
+ }
+ });
+ },
+
+ // Update the `.length` attribute on this container
+ _updateLength: function(){
+ this.length = _.size(this._views);
+ }
+ });
+
+ // Borrowing this code from Backbone.Collection:
+ // http://backbonejs.org/docs/backbone.html#section-106
+ //
+ // Mix in methods from Underscore, for iteration, and other
+ // collection related features.
+ var methods = ['forEach', 'each', 'map', 'find', 'detect', 'filter',
+ 'select', 'reject', 'every', 'all', 'some', 'any', 'include',
+ 'contains', 'invoke', 'toArray', 'first', 'initial', 'rest',
+ 'last', 'without', 'isEmpty', 'pluck'];
+
+ _.each(methods, function(method) {
+ Container.prototype[method] = function() {
+ var views = _.values(this._views);
+ var args = [views].concat(_.toArray(arguments));
+ return _[method].apply(_, args);
+ };
+ });
+
+ // return the public API
+ return Container;
+ })(Backbone, _);
+
+
+ Backbone.ChildViewContainer.VERSION = '0.1.4';
+
+ Backbone.ChildViewContainer.noConflict = function () {
+ Backbone.ChildViewContainer = previousChildViewContainer;
+ return this;
+ };
+
+ return Backbone.ChildViewContainer;
+
+}));
vendor/assets/javascripts/backbone.marionette.js
@@ -0,0 +1,3254 @@
+// MarionetteJS (Backbone.Marionette)
+// ----------------------------------
+// v2.0.1
+//
+// Copyright (c)2014 Derick Bailey, Muted Solutions, LLC.
+// Distributed under MIT license
+//
+// http://marionettejs.com
+
+
+/*!
+ * Includes BabySitter
+ * https://github.com/marionettejs/backbone.babysitter/
+ *
+ * Includes Wreqr
+ * https://github.com/marionettejs/backbone.wreqr/
+ */
+
+
+(function(root, factory) {
+
+ if (typeof define === 'function' && define.amd) {
+ define(['backbone', 'underscore'], function(Backbone, _) {
+ return (root.Marionette = factory(root, Backbone, _));
+ });
+ } else if (typeof exports !== 'undefined') {
+ var Backbone = require('backbone');
+ var _ = require('underscore');
+ module.exports = factory(root, Backbone, _);
+ } else {
+ root.Marionette = factory(root, root.Backbone, root._);
+ }
+
+}(this, function(root, Backbone, _) {
+ 'use strict';
+
+ // Backbone.BabySitter
+ // -------------------
+ // v0.1.4
+ //
+ // Copyright (c)2014 Derick Bailey, Muted Solutions, LLC.
+ // Distributed under MIT license
+ //
+ // http://github.com/marionettejs/backbone.babysitter
+ (function(Backbone, _) {
+ "use strict";
+ var previousChildViewContainer = Backbone.ChildViewContainer;
+ // BabySitter.ChildViewContainer
+ // -----------------------------
+ //
+ // Provide a container to store, retrieve and
+ // shut down child views.
+ Backbone.ChildViewContainer = function(Backbone, _) {
+ // Container Constructor
+ // ---------------------
+ var Container = function(views) {
+ this._views = {};
+ this._indexByModel = {};
+ this._indexByCustom = {};
+ this._updateLength();
+ _.each(views, this.add, this);
+ };
+ // Container Methods
+ // -----------------
+ _.extend(Container.prototype, {
+ // Add a view to this container. Stores the view
+ // by `cid` and makes it searchable by the model
+ // cid (and model itself). Optionally specify
+ // a custom key to store an retrieve the view.
+ add: function(view, customIndex) {
+ var viewCid = view.cid;
+ // store the view
+ this._views[viewCid] = view;
+ // index it by model
+ if (view.model) {
+ this._indexByModel[view.model.cid] = viewCid;
+ }
+ // index by custom
+ if (customIndex) {
+ this._indexByCustom[customIndex] = viewCid;
+ }
+ this._updateLength();
+ return this;
+ },
+ // Find a view by the model that was attached to
+ // it. Uses the model's `cid` to find it.
+ findByModel: function(model) {
+ return this.findByModelCid(model.cid);
+ },
+ // Find a view by the `cid` of the model that was attached to
+ // it. Uses the model's `cid` to find the view `cid` and
+ // retrieve the view using it.
+ findByModelCid: function(modelCid) {
+ var viewCid = this._indexByModel[modelCid];
+ return this.findByCid(viewCid);
+ },
+ // Find a view by a custom indexer.
+ findByCustom: function(index) {
+ var viewCid = this._indexByCustom[index];
+ return this.findByCid(viewCid);
+ },
+ // Find by index. This is not guaranteed to be a
+ // stable index.
+ findByIndex: function(index) {
+ return _.values(this._views)[index];
+ },
+ // retrieve a view by its `cid` directly
+ findByCid: function(cid) {
+ return this._views[cid];
+ },
+ // Remove a view
+ remove: function(view) {
+ var viewCid = view.cid;
+ // delete model index
+ if (view.model) {
+ delete this._indexByModel[view.model.cid];
+ }
+ // delete custom index
+ _.any(this._indexByCustom, function(cid, key) {
+ if (cid === viewCid) {
+ delete this._indexByCustom[key];
+ return true;
+ }
+ }, this);
+ // remove the view from the container
+ delete this._views[viewCid];
+ // update the length
+ this._updateLength();
+ return this;
+ },
+ // Call a method on every view in the container,
+ // passing parameters to the call method one at a
+ // time, like `function.call`.
+ call: function(method) {
+ this.apply(method, _.tail(arguments));
+ },
+ // Apply a method on every view in the container,
+ // passing parameters to the call method one at a
+ // time, like `function.apply`.
+ apply: function(method, args) {
+ _.each(this._views, function(view) {
+ if (_.isFunction(view[method])) {
+ view[method].apply(view, args || []);
+ }
+ });
+ },
+ // Update the `.length` attribute on this container
+ _updateLength: function() {
+ this.length = _.size(this._views);
+ }
+ });
+ // Borrowing this code from Backbone.Collection:
+ // http://backbonejs.org/docs/backbone.html#section-106
+ //
+ // Mix in methods from Underscore, for iteration, and other
+ // collection related features.
+ var methods = [ "forEach", "each", "map", "find", "detect", "filter", "select", "reject", "every", "all", "some", "any", "include", "contains", "invoke", "toArray", "first", "initial", "rest", "last", "without", "isEmpty", "pluck" ];
+ _.each(methods, function(method) {
+ Container.prototype[method] = function() {
+ var views = _.values(this._views);
+ var args = [ views ].concat(_.toArray(arguments));
+ return _[method].apply(_, args);
+ };
+ });
+ // return the public API
+ return Container;
+ }(Backbone, _);
+ Backbone.ChildViewContainer.VERSION = "0.1.4";
+ Backbone.ChildViewContainer.noConflict = function() {
+ Backbone.ChildViewContainer = previousChildViewContainer;
+ return this;
+ };
+ return Backbone.ChildViewContainer;
+ })(Backbone, _);
+ // Backbone.Wreqr (Backbone.Marionette)
+ // ----------------------------------
+ // v1.3.1
+ //
+ // Copyright (c)2014 Derick Bailey, Muted Solutions, LLC.
+ // Distributed under MIT license
+ //
+ // http://github.com/marionettejs/backbone.wreqr
+ (function(Backbone, _) {
+ "use strict";
+ var previousWreqr = Backbone.Wreqr;
+ var Wreqr = Backbone.Wreqr = {};
+ Backbone.Wreqr.VERSION = "1.3.1";
+ Backbone.Wreqr.noConflict = function() {
+ Backbone.Wreqr = previousWreqr;
+ return this;
+ };
+ // Handlers
+ // --------
+ // A registry of functions to call, given a name
+ Wreqr.Handlers = function(Backbone, _) {
+ "use strict";
+ // Constructor
+ // -----------
+ var Handlers = function(options) {
+ this.options = options;
+ this._wreqrHandlers = {};
+ if (_.isFunction(this.initialize)) {
+ this.initialize(options);
+ }
+ };
+ Handlers.extend = Backbone.Model.extend;
+ // Instance Members
+ // ----------------
+ _.extend(Handlers.prototype, Backbone.Events, {
+ // Add multiple handlers using an object literal configuration
+ setHandlers: function(handlers) {
+ _.each(handlers, function(handler, name) {
+ var context = null;
+ if (_.isObject(handler) && !_.isFunction(handler)) {
+ context = handler.context;
+ handler = handler.callback;
+ }
+ this.setHandler(name, handler, context);
+ }, this);
+ },
+ // Add a handler for the given name, with an
+ // optional context to run the handler within
+ setHandler: function(name, handler, context) {
+ var config = {
+ callback: handler,
+ context: context
+ };
+ this._wreqrHandlers[name] = config;
+ this.trigger("handler:add", name, handler, context);
+ },
+ // Determine whether or not a handler is registered
+ hasHandler: function(name) {
+ return !!this._wreqrHandlers[name];
+ },
+ // Get the currently registered handler for
+ // the specified name. Throws an exception if
+ // no handler is found.
+ getHandler: function(name) {
+ var config = this._wreqrHandlers[name];
+ if (!config) {
+ return;
+ }
+ return function() {
+ var args = Array.prototype.slice.apply(arguments);
+ return config.callback.apply(config.context, args);
+ };
+ },
+ // Remove a handler for the specified name
+ removeHandler: function(name) {
+ delete this._wreqrHandlers[name];
+ },
+ // Remove all handlers from this registry
+ removeAllHandlers: function() {
+ this._wreqrHandlers = {};
+ }
+ });
+ return Handlers;
+ }(Backbone, _);
+ // Wreqr.CommandStorage
+ // --------------------
+ //
+ // Store and retrieve commands for execution.
+ Wreqr.CommandStorage = function() {
+ "use strict";
+ // Constructor function
+ var CommandStorage = function(options) {
+ this.options = options;
+ this._commands = {};
+ if (_.isFunction(this.initialize)) {
+ this.initialize(options);
+ }
+ };
+ // Instance methods
+ _.extend(CommandStorage.prototype, Backbone.Events, {
+ // Get an object literal by command name, that contains
+ // the `commandName` and the `instances` of all commands
+ // represented as an array of arguments to process
+ getCommands: function(commandName) {
+ var commands = this._commands[commandName];
+ // we don't have it, so add it
+ if (!commands) {
+ // build the configuration
+ commands = {
+ command: commandName,
+ instances: []
+ };
+ // store it
+ this._commands[commandName] = commands;
+ }
+ return commands;
+ },
+ // Add a command by name, to the storage and store the
+ // args for the command
+ addCommand: function(commandName, args) {
+ var command = this.getCommands(commandName);
+ command.instances.push(args);
+ },
+ // Clear all commands for the given `commandName`
+ clearCommands: function(commandName) {
+ var command = this.getCommands(commandName);
+ command.instances = [];
+ }
+ });
+ return CommandStorage;
+ }();
+ // Wreqr.Commands
+ // --------------
+ //
+ // A simple command pattern implementation. Register a command
+ // handler and execute it.
+ Wreqr.Commands = function(Wreqr) {
+ "use strict";
+ return Wreqr.Handlers.extend({
+ // default storage type
+ storageType: Wreqr.CommandStorage,
+ constructor: function(options) {
+ this.options = options || {};
+ this._initializeStorage(this.options);
+ this.on("handler:add", this._executeCommands, this);
+ var args = Array.prototype.slice.call(arguments);
+ Wreqr.Handlers.prototype.constructor.apply(this, args);
+ },
+ // Execute a named command with the supplied args
+ execute: function(name, args) {
+ name = arguments[0];
+ args = Array.prototype.slice.call(arguments, 1);
+ if (this.hasHandler(name)) {
+ this.getHandler(name).apply(this, args);
+ } else {
+ this.storage.addCommand(name, args);
+ }
+ },
+ // Internal method to handle bulk execution of stored commands
+ _executeCommands: function(name, handler, context) {
+ var command = this.storage.getCommands(name);
+ // loop through and execute all the stored command instances
+ _.each(command.instances, function(args) {
+ handler.apply(context, args);
+ });
+ this.storage.clearCommands(name);
+ },
+ // Internal method to initialize storage either from the type's
+ // `storageType` or the instance `options.storageType`.
+ _initializeStorage: function(options) {
+ var storage;
+ var StorageType = options.storageType || this.storageType;
+ if (_.isFunction(StorageType)) {
+ storage = new StorageType();
+ } else {
+ storage = StorageType;
+ }
+ this.storage = storage;
+ }
+ });
+ }(Wreqr);
+ // Wreqr.RequestResponse
+ // ---------------------
+ //
+ // A simple request/response implementation. Register a
+ // request handler, and return a response from it
+ Wreqr.RequestResponse = function(Wreqr) {
+ "use strict";
+ return Wreqr.Handlers.extend({
+ request: function() {
+ var name = arguments[0];
+ var args = Array.prototype.slice.call(arguments, 1);
+ if (this.hasHandler(name)) {
+ return this.getHandler(name).apply(this, args);
+ }
+ }
+ });
+ }(Wreqr);
+ // Event Aggregator
+ // ----------------
+ // A pub-sub object that can be used to decouple various parts
+ // of an application through event-driven architecture.
+ Wreqr.EventAggregator = function(Backbone, _) {
+ "use strict";
+ var EA = function() {};
+ // Copy the `extend` function used by Backbone's classes
+ EA.extend = Backbone.Model.extend;
+ // Copy the basic Backbone.Events on to the event aggregator
+ _.extend(EA.prototype, Backbone.Events);
+ return EA;
+ }(Backbone, _);
+ // Wreqr.Channel
+ // --------------
+ //
+ // An object that wraps the three messaging systems:
+ // EventAggregator, RequestResponse, Commands
+ Wreqr.Channel = function(Wreqr) {
+ "use strict";
+ var Channel = function(channelName) {
+ this.vent = new Backbone.Wreqr.EventAggregator();
+ this.reqres = new Backbone.Wreqr.RequestResponse();
+ this.commands = new Backbone.Wreqr.Commands();
+ this.channelName = channelName;
+ };
+ _.extend(Channel.prototype, {
+ // Remove all handlers from the messaging systems of this channel
+ reset: function() {
+ this.vent.off();
+ this.vent.stopListening();
+ this.reqres.removeAllHandlers();
+ this.commands.removeAllHandlers();
+ return this;
+ },
+ // Connect a hash of events; one for each messaging system
+ connectEvents: function(hash, context) {
+ this._connect("vent", hash, context);
+ return this;
+ },
+ connectCommands: function(hash, context) {
+ this._connect("commands", hash, context);
+ return this;
+ },
+ connectRequests: function(hash, context) {
+ this._connect("reqres", hash, context);
+ return this;
+ },
+ // Attach the handlers to a given message system `type`
+ _connect: function(type, hash, context) {
+ if (!hash) {
+ return;
+ }
+ context = context || this;
+ var method = type === "vent" ? "on" : "setHandler";
+ _.each(hash, function(fn, eventName) {
+ this[type][method](eventName, _.bind(fn, context));
+ }, this);
+ }
+ });
+ return Channel;
+ }(Wreqr);
+ // Wreqr.Radio
+ // --------------
+ //
+ // An object that lets you communicate with many channels.
+ Wreqr.radio = function(Wreqr) {
+ "use strict";
+ var Radio = function() {
+ this._channels = {};
+ this.vent = {};
+ this.commands = {};
+ this.reqres = {};
+ this._proxyMethods();
+ };
+ _.extend(Radio.prototype, {
+ channel: function(channelName) {
+ if (!channelName) {
+ throw new Error("Channel must receive a name");
+ }
+ return this._getChannel(channelName);
+ },
+ _getChannel: function(channelName) {
+ var channel = this._channels[channelName];
+ if (!channel) {
+ channel = new Wreqr.Channel(channelName);
+ this._channels[channelName] = channel;
+ }
+ return channel;
+ },
+ _proxyMethods: function() {
+ _.each([ "vent", "commands", "reqres" ], function(system) {
+ _.each(messageSystems[system], function(method) {
+ this[system][method] = proxyMethod(this, system, method);
+ }, this);
+ }, this);
+ }
+ });
+ var messageSystems = {
+ vent: [ "on", "off", "trigger", "once", "stopListening", "listenTo", "listenToOnce" ],
+ commands: [ "execute", "setHandler", "setHandlers", "removeHandler", "removeAllHandlers" ],
+ reqres: [ "request", "setHandler", "setHandlers", "removeHandler", "removeAllHandlers" ]
+ };
+ var proxyMethod = function(radio, system, method) {
+ return function(channelName) {
+ var messageSystem = radio._getChannel(channelName)[system];
+ var args = Array.prototype.slice.call(arguments, 1);
+ return messageSystem[method].apply(messageSystem, args);
+ };
+ };
+ return new Radio();
+ }(Wreqr);
+ return Backbone.Wreqr;
+ })(Backbone, _);
+
+ var previousMarionette = root.Marionette;
+
+ var Marionette = Backbone.Marionette = {};
+
+ Marionette.VERSION = '2.0.1';
+
+ Marionette.noConflict = function() {
+ root.Marionette = previousMarionette;
+ return this;
+ };
+
+ Backbone.Marionette = Marionette;
+
+ // Get the Deferred creator for later use
+ Marionette.Deferred = Backbone.$.Deferred;
+
+ /* jshint unused: false */
+
+ // Helpers
+ // -------
+
+ // For slicing `arguments` in functions
+ var slice = Array.prototype.slice;
+
+ function throwError(message, name) {
+ var error = new Error(message);
+ error.name = name || 'Error';
+ throw error;
+ }
+
+ // Marionette.extend
+ // -----------------
+
+ // Borrow the Backbone `extend` method so we can use it as needed
+ Marionette.extend = Backbone.Model.extend;
+
+ // Marionette.getOption
+ // --------------------
+
+ // Retrieve an object, function or other value from a target
+ // object or its `options`, with `options` taking precedence.
+ Marionette.getOption = function(target, optionName) {
+ if (!target || !optionName) { return; }
+ var value;
+
+ if (target.options && (target.options[optionName] !== undefined)) {
+ value = target.options[optionName];
+ } else {
+ value = target[optionName];
+ }
+
+ return value;
+ };
+
+ // Proxy `Marionette.getOption`
+ Marionette.proxyGetOption = function(optionName) {
+ return Marionette.getOption(this, optionName);
+ };
+
+ // Marionette.normalizeMethods
+ // ----------------------
+
+ // Pass in a mapping of events => functions or function names
+ // and return a mapping of events => functions
+ Marionette.normalizeMethods = function(hash) {
+ var normalizedHash = {}, method;
+ _.each(hash, function(fn, name) {
+ method = fn;
+ if (!_.isFunction(method)) {
+ method = this[method];
+ }
+ if (!method) {
+ return;
+ }
+ normalizedHash[name] = method;
+ }, this);
+ return normalizedHash;
+ };
+
+
+ // allows for the use of the @ui. syntax within
+ // a given key for triggers and events
+ // swaps the @ui with the associated selector
+ Marionette.normalizeUIKeys = function(hash, ui) {
+ if (typeof(hash) === 'undefined') {
+ return;
+ }
+
+ _.each(_.keys(hash), function(v) {
+ var pattern = /@ui.[a-zA-Z_$0-9]*/g;
+ if (v.match(pattern)) {
+ hash[v.replace(pattern, function(r) {
+ return ui[r.slice(4)];
+ })] = hash[v];
+ delete hash[v];
+ }
+ });
+
+ return hash;
+ };
+
+ // Mix in methods from Underscore, for iteration, and other
+ // collection related features.
+ // Borrowing this code from Backbone.Collection:
+ // http://backbonejs.org/docs/backbone.html#section-106
+ Marionette.actAsCollection = function(object, listProperty) {
+ var methods = ['forEach', 'each', 'map', 'find', 'detect', 'filter',
+ 'select', 'reject', 'every', 'all', 'some', 'any', 'include',
+ 'contains', 'invoke', 'toArray', 'first', 'initial', 'rest',
+ 'last', 'without', 'isEmpty', 'pluck'];
+
+ _.each(methods, function(method) {
+ object[method] = function() {
+ var list = _.values(_.result(this, listProperty));
+ var args = [list].concat(_.toArray(arguments));
+ return _[method].apply(_, args);
+ };
+ });
+ };
+
+ // Trigger an event and/or a corresponding method name. Examples:
+ //
+ // `this.triggerMethod("foo")` will trigger the "foo" event and
+ // call the "onFoo" method.
+ //
+ // `this.triggerMethod("foo:bar")` will trigger the "foo:bar" event and
+ // call the "onFooBar" method.
+ Marionette.triggerMethod = (function() {
+
+ // split the event name on the ":"
+ var splitter = /(^|:)(\w)/gi;
+
+ // take the event section ("section1:section2:section3")
+ // and turn it in to uppercase name
+ function getEventName(match, prefix, eventName) {
+ return eventName.toUpperCase();
+ }
+
+ // actual triggerMethod implementation
+ var triggerMethod = function(event) {
+ // get the method name from the event name
+ var methodName = 'on' + event.replace(splitter, getEventName);
+ var method = this[methodName];
+ var result;
+
+ // call the onMethodName if it exists
+ if (_.isFunction(method)) {
+ // pass all arguments, except the event name
+ result = method.apply(this, _.tail(arguments));
+ }
+
+ // trigger the event, if a trigger method exists
+ if (_.isFunction(this.trigger)) {
+ this.trigger.apply(this, arguments);
+ }
+
+ return result;
+ };
+
+ return triggerMethod;
+ })();
+
+ // DOMRefresh
+ // ----------
+ //
+ // Monitor a view's state, and after it has been rendered and shown
+ // in the DOM, trigger a "dom:refresh" event every time it is
+ // re-rendered.
+
+ Marionette.MonitorDOMRefresh = (function(documentElement) {
+ // track when the view has been shown in the DOM,
+ // using a Marionette.Region (or by other means of triggering "show")
+ function handleShow(view) {
+ view._isShown = true;
+ triggerDOMRefresh(view);
+ }
+
+ // track when the view has been rendered
+ function handleRender(view) {
+ view._isRendered = true;
+ triggerDOMRefresh(view);
+ }
+
+ // Trigger the "dom:refresh" event and corresponding "onDomRefresh" method
+ function triggerDOMRefresh(view) {
+ if (view._isShown && view._isRendered && isInDOM(view)) {
+ if (_.isFunction(view.triggerMethod)) {
+ view.triggerMethod('dom:refresh');
+ }
+ }
+ }
+
+ function isInDOM(view) {
+ return documentElement.contains(view.el);
+ }
+
+ // Export public API
+ return function(view) {
+ view.listenTo(view, 'show', function() {
+ handleShow(view);
+ });
+
+ view.listenTo(view, 'render', function() {
+ handleRender(view);
+ });
+ };
+ })(document.documentElement);
+
+
+ /* jshint maxparams: 5 */
+
+ // Marionette.bindEntityEvents & unbindEntityEvents
+ // ---------------------------
+ //
+ // These methods are used to bind/unbind a backbone "entity" (collection/model)
+ // to methods on a target object.
+ //
+ // The first parameter, `target`, must have a `listenTo` method from the
+ // EventBinder object.
+ //
+ // The second parameter is the entity (Backbone.Model or Backbone.Collection)
+ // to bind the events from.
+ //
+ // The third parameter is a hash of { "event:name": "eventHandler" }
+ // configuration. Multiple handlers can be separated by a space. A
+ // function can be supplied instead of a string handler name.
+
+ (function(Marionette) {
+ 'use strict';
+
+ // Bind the event to handlers specified as a string of
+ // handler names on the target object
+ function bindFromStrings(target, entity, evt, methods) {
+ var methodNames = methods.split(/\s+/);
+
+ _.each(methodNames, function(methodName) {
+
+ var method = target[methodName];
+ if (!method) {
+ throwError('Method "' + methodName +
+ '" was configured as an event handler, but does not exist.');
+ }
+
+ target.listenTo(entity, evt, method);
+ });
+ }
+
+ // Bind the event to a supplied callback function
+ function bindToFunction(target, entity, evt, method) {
+ target.listenTo(entity, evt, method);
+ }
+
+ // Bind the event to handlers specified as a string of
+ // handler names on the target object
+ function unbindFromStrings(target, entity, evt, methods) {
+ var methodNames = methods.split(/\s+/);
+
+ _.each(methodNames, function(methodName) {
+ var method = target[methodName];
+ target.stopListening(entity, evt, method);
+ });
+ }
+
+ // Bind the event to a supplied callback function
+ function unbindToFunction(target, entity, evt, method) {
+ target.stopListening(entity, evt, method);
+ }
+
+
+ // generic looping function
+ function iterateEvents(target, entity, bindings, functionCallback, stringCallback) {
+ if (!entity || !bindings) { return; }
+
+ // allow the bindings to be a function
+ if (_.isFunction(bindings)) {
+ bindings = bindings.call(target);
+ }
+
+ // iterate the bindings and bind them
+ _.each(bindings, function(methods, evt) {
+
+ // allow for a function as the handler,
+ // or a list of event names as a string
+ if (_.isFunction(methods)) {
+ functionCallback(target, entity, evt, methods);
+ } else {
+ stringCallback(target, entity, evt, methods);
+ }
+
+ });
+ }
+
+ // Export Public API
+ Marionette.bindEntityEvents = function(target, entity, bindings) {
+ iterateEvents(target, entity, bindings, bindToFunction, bindFromStrings);
+ };
+
+ Marionette.unbindEntityEvents = function(target, entity, bindings) {
+ iterateEvents(target, entity, bindings, unbindToFunction, unbindFromStrings);
+ };
+
+ // Proxy `bindEntityEvents`
+ Marionette.proxyBindEntityEvents = function(entity, bindings) {
+ return Marionette.bindEntityEvents(this, entity, bindings);
+ };
+
+ // Proxy `unbindEntityEvents`
+ Marionette.proxyUnbindEntityEvents = function(entity, bindings) {
+ return Marionette.unbindEntityEvents(this, entity, bindings);
+ };
+ })(Marionette);
+
+
+ // Callbacks
+ // ---------
+
+ // A simple way of managing a collection of callbacks
+ // and executing them at a later point in time, using jQuery's
+ // `Deferred` object.
+ Marionette.Callbacks = function() {
+ this._deferred = Marionette.Deferred();
+ this._callbacks = [];
+ };
+
+ _.extend(Marionette.Callbacks.prototype, {
+
+ // Add a callback to be executed. Callbacks added here are
+ // guaranteed to execute, even if they are added after the
+ // `run` method is called.
+ add: function(callback, contextOverride) {
+ var promise = _.result(this._deferred, 'promise');
+
+ this._callbacks.push({cb: callback, ctx: contextOverride});
+
+ promise.then(function(args) {
+ if (contextOverride){ args.context = contextOverride; }
+ callback.call(args.context, args.options);
+ });
+ },
+
+ // Run all registered callbacks with the context specified.
+ // Additional callbacks can be added after this has been run
+ // and they will still be executed.
+ run: function(options, context) {
+ this._deferred.resolve({
+ options: options,
+ context: context
+ });
+ },
+
+ // Resets the list of callbacks to be run, allowing the same list
+ // to be run multiple times - whenever the `run` method is called.
+ reset: function() {
+ var callbacks = this._callbacks;
+ this._deferred = Marionette.Deferred();
+ this._callbacks = [];
+
+ _.each(callbacks, function(cb) {
+ this.add(cb.cb, cb.ctx);
+ }, this);
+ }
+ });
+
+ // Marionette Controller
+ // ---------------------
+ //
+ // A multi-purpose object to use as a controller for
+ // modules and routers, and as a mediator for workflow
+ // and coordination of other objects, views, and more.
+ Marionette.Controller = function(options) {
+ this.triggerMethod = Marionette.triggerMethod;
+ this.options = options || {};
+
+ if (_.isFunction(this.initialize)) {
+ this.initialize(this.options);
+ }
+ };
+
+ Marionette.Controller.extend = Marionette.extend;
+
+ // Controller Methods
+ // --------------
+
+ // Ensure it can trigger events with Backbone.Events
+ _.extend(Marionette.Controller.prototype, Backbone.Events, {
+ destroy: function() {
+ var args = Array.prototype.slice.call(arguments);
+ this.triggerMethod.apply(this, ['before:destroy'].concat(args));
+ this.triggerMethod.apply(this, ['destroy'].concat(args));
+
+ this.stopListening();
+ this.off();
+ },
+
+ // import the `triggerMethod` to trigger events with corresponding
+ // methods if the method exists
+ triggerMethod: Marionette.triggerMethod,
+
+ // Proxy `getOption` to enable getting options from this or this.options by name.
+ getOption: Marionette.proxyGetOption
+
+ });
+
+ /* jshint maxcomplexity: 10, maxstatements: 27 */
+
+ // Region
+ // ------
+ //
+ // Manage the visual regions of your composite application. See
+ // http://lostechies.com/derickbailey/2011/12/12/composite-js-apps-regions-and-region-managers/
+
+ Marionette.Region = function(options) {
+ this.options = options || {};
+ this.el = this.getOption('el');
+
+ // Handle when this.el is passed in as a $ wrapped element.
+ this.el = this.el instanceof Backbone.$ ? this.el[0] : this.el;
+
+ if (!this.el) {
+ throwError('An "el" must be specified for a region.', 'NoElError');
+ }
+
+ this.$el = this.getEl(this.el);
+
+ if (this.initialize) {
+ var args = Array.prototype.slice.apply(arguments);
+ this.initialize.apply(this, args);
+ }
+ };
+
+
+ // Region Class methods
+ // -------------------
+
+ _.extend(Marionette.Region, {
+
+ // Build an instance of a region by passing in a configuration object
+ // and a default region class to use if none is specified in the config.
+ //
+ // The config object should either be a string as a jQuery DOM selector,
+ // a Region class directly, or an object literal that specifies both
+ // a selector and regionClass:
+ //
+ // ```js
+ // {
+ // selector: "#foo",
+ // regionClass: MyCustomRegion
+ // }
+ // ```
+ //
+ buildRegion: function(regionConfig, defaultRegionClass) {
+ var regionIsString = _.isString(regionConfig);
+ var regionSelectorIsString = _.isString(regionConfig.selector);
+ var regionClassIsUndefined = _.isUndefined(regionConfig.regionClass);
+ var regionIsClass = _.isFunction(regionConfig);
+
+ if (!regionIsClass && !regionIsString && !regionSelectorIsString) {
+ throwError('Region must be specified as a Region class,' +
+ 'a selector string or an object with selector property');
+ }
+
+ var selector, RegionClass;
+
+ // get the selector for the region
+
+ if (regionIsString) {
+ selector = regionConfig;
+ }
+
+ if (regionConfig.selector) {
+ selector = regionConfig.selector;
+ delete regionConfig.selector;
+ }
+
+ // get the class for the region
+
+ if (regionIsClass) {
+ RegionClass = regionConfig;
+ }
+
+ if (!regionIsClass && regionClassIsUndefined) {
+ RegionClass = defaultRegionClass;
+ }
+
+ if (regionConfig.regionClass) {
+ RegionClass = regionConfig.regionClass;
+ delete regionConfig.regionClass;
+ }
+
+ if (regionIsString || regionIsClass) {
+ regionConfig = {};
+ }
+
+ regionConfig.el = selector;
+
+ // build the region instance
+ var region = new RegionClass(regionConfig);
+
+ // override the `getEl` function if we have a parentEl
+ // this must be overridden to ensure the selector is found
+ // on the first use of the region. if we try to assign the
+ // region's `el` to `parentEl.find(selector)` in the object
+ // literal to build the region, the element will not be
+ // guaranteed to be in the DOM already, and will cause problems
+ if (regionConfig.parentEl) {
+ region.getEl = function(el) {
+ if (_.isObject(el)) {
+ return Backbone.$(el);
+ }
+ var parentEl = regionConfig.parentEl;
+ if (_.isFunction(parentEl)) {
+ parentEl = parentEl();
+ }
+ return parentEl.find(el);
+ };
+ }
+
+ return region;
+ }
+
+ });
+
+ // Region Instance Methods
+ // -----------------------
+
+ _.extend(Marionette.Region.prototype, Backbone.Events, {
+
+ // Displays a backbone view instance inside of the region.
+ // Handles calling the `render` method for you. Reads content
+ // directly from the `el` attribute. Also calls an optional
+ // `onShow` and `onDestroy` method on your view, just after showing
+ // or just before destroying the view, respectively.
+ // The `preventDestroy` option can be used to prevent a view from
+ // the old view being destroyed on show.
+ // The `forceShow` option can be used to force a view to be
+ // re-rendered if it's already shown in the region.
+
+ show: function(view, options){
+ this._ensureElement();
+
+ var showOptions = options || {};
+ var isDifferentView = view !== this.currentView;
+ var preventDestroy = !!showOptions.preventDestroy;
+ var forceShow = !!showOptions.forceShow;
+
+ // we are only changing the view if there is a view to change to begin with
+ var isChangingView = !!this.currentView;
+
+ // only destroy the view if we don't want to preventDestroy and the view is different
+ var _shouldDestroyView = !preventDestroy && isDifferentView;
+
+ if (_shouldDestroyView) {
+ this.empty();
+ }
+
+ // show the view if the view is different or if you want to re-show the view
+ var _shouldShowView = isDifferentView || forceShow;
+
+ if (_shouldShowView) {
+ view.render();
+
+ if (isChangingView) {
+ this.triggerMethod('before:swap', view);
+ }
+
+ this.triggerMethod('before:show', view);
+ this.triggerMethod.call(view, 'before:show');
+
+ this.attachHtml(view);
+ this.currentView = view;
+
+ if (isChangingView) {
+ this.triggerMethod('swap', view);
+ }
+
+ this.triggerMethod('show', view);
+
+ if (_.isFunction(view.triggerMethod)) {
+ view.triggerMethod('show');
+ } else {
+ this.triggerMethod.call(view, 'show');
+ }
+
+ return this;
+ }
+
+ return this;
+ },
+
+ _ensureElement: function(){
+ if (!_.isObject(this.el)) {
+ this.$el = this.getEl(this.el);
+ this.el = this.$el[0];
+ }
+
+ if (!this.$el || this.$el.length === 0) {
+ throwError('An "el" ' + this.$el.selector + ' must exist in DOM');
+ }
+ },
+
+ // Override this method to change how the region finds the
+ // DOM element that it manages. Return a jQuery selector object.
+ getEl: function(el) {
+ return Backbone.$(el);
+ },
+
+ // Override this method to change how the new view is
+ // appended to the `$el` that the region is managing
+ attachHtml: function(view) {
+ // empty the node and append new view
+ this.el.innerHTML='';
+ this.el.appendChild(view.el);
+ },
+
+ // Destroy the current view, if there is one. If there is no
+ // current view, it does nothing and returns immediately.
+ empty: function() {
+ var view = this.currentView;
+ if (!view || view.isDestroyed) { return; }
+
+ this.triggerMethod('before:empty', view);
+
+ // call 'destroy' or 'remove', depending on which is found
+ if (view.destroy) { view.destroy(); }
+ else if (view.remove) { view.remove(); }
+
+ this.triggerMethod('empty', view);
+
+ delete this.currentView;
+ },
+
+ // Attach an existing view to the region. This
+ // will not call `render` or `onShow` for the new view,
+ // and will not replace the current HTML for the `el`
+ // of the region.
+ attachView: function(view) {
+ this.currentView = view;
+ },
+
+ // Reset the region by destroying any existing view and
+ // clearing out the cached `$el`. The next time a view
+ // is shown via this region, the region will re-query the
+ // DOM for the region's `el`.
+ reset: function() {
+ this.empty();
+
+ if (this.$el) {
+ this.el = this.$el.selector;
+ }
+
+ delete this.$el;
+ },
+
+ // Proxy `getOption` to enable getting options from this or this.options by name.
+ getOption: Marionette.proxyGetOption,
+
+ // import the `triggerMethod` to trigger events with corresponding
+ // methods if the method exists
+ triggerMethod: Marionette.triggerMethod
+ });
+
+ // Copy the `extend` function used by Backbone's classes
+ Marionette.Region.extend = Marionette.extend;
+
+ // Marionette.RegionManager
+ // ------------------------
+ //
+ // Manage one or more related `Marionette.Region` objects.
+ Marionette.RegionManager = (function(Marionette) {
+
+ var RegionManager = Marionette.Controller.extend({
+ constructor: function(options) {
+ this._regions = {};
+ Marionette.Controller.call(this, options);
+ },
+
+ // Add multiple regions using an object literal, where
+ // each key becomes the region name, and each value is
+ // the region definition.
+ addRegions: function(regionDefinitions, defaults) {
+ var regions = {};
+
+ _.each(regionDefinitions, function(definition, name) {
+ if (_.isString(definition)) {
+ definition = {selector: definition};
+ }
+
+ if (definition.selector) {
+ definition = _.defaults({}, definition, defaults);
+ }
+
+ var region = this.addRegion(name, definition);
+ regions[name] = region;
+ }, this);
+
+ return regions;
+ },
+
+ // Add an individual region to the region manager,
+ // and return the region instance
+ addRegion: function(name, definition) {
+ var region;
+
+ var isObject = _.isObject(definition);
+ var isString = _.isString(definition);
+ var hasSelector = !!definition.selector;
+
+ if (isString || (isObject && hasSelector)) {
+ region = Marionette.Region.buildRegion(definition, Marionette.Region);
+ } else if (_.isFunction(definition)) {
+ region = Marionette.Region.buildRegion(definition, Marionette.Region);
+ } else {
+ region = definition;
+ }
+
+ this.triggerMethod('before:add:region', name, region);
+
+ this._store(name, region);
+
+ this.triggerMethod('add:region', name, region);
+ return region;
+ },
+
+ // Get a region by name
+ get: function(name) {
+ return this._regions[name];
+ },
+
+ // Gets all the regions contained within
+ // the `regionManager` instance.
+ getRegions: function(){
+ return _.clone(this._regions);
+ },
+
+ // Remove a region by name
+ removeRegion: function(name) {
+ var region = this._regions[name];
+ this._remove(name, region);
+ },
+
+ // Empty all regions in the region manager, and
+ // remove them
+ removeRegions: function() {
+ _.each(this._regions, function(region, name) {
+ this._remove(name, region);
+ }, this);
+ },
+
+ // Empty all regions in the region manager, but
+ // leave them attached
+ emptyRegions: function() {
+ _.each(this._regions, function(region) {
+ region.empty();
+ }, this);
+ },
+
+ // Destroy all regions and shut down the region
+ // manager entirely
+ destroy: function() {
+ this.removeRegions();
+ Marionette.Controller.prototype.destroy.apply(this, arguments);
+ },
+
+ // internal method to store regions
+ _store: function(name, region) {
+ this._regions[name] = region;
+ this._setLength();
+ },
+
+ // internal method to remove a region
+ _remove: function(name, region) {
+ this.triggerMethod('before:remove:region', name, region);
+ region.empty();
+ region.stopListening();
+ delete this._regions[name];
+ this._setLength();
+ this.triggerMethod('remove:region', name, region);
+ },
+
+ // set the number of regions current held
+ _setLength: function() {
+ this.length = _.size(this._regions);
+ }
+
+ });
+
+ Marionette.actAsCollection(RegionManager.prototype, '_regions');
+
+ return RegionManager;
+ })(Marionette);
+
+
+ // Template Cache
+ // --------------
+
+ // Manage templates stored in `<script>` blocks,
+ // caching them for faster access.
+ Marionette.TemplateCache = function(templateId) {
+ this.templateId = templateId;
+ };
+
+ // TemplateCache object-level methods. Manage the template
+ // caches from these method calls instead of creating
+ // your own TemplateCache instances
+ _.extend(Marionette.TemplateCache, {
+ templateCaches: {},
+
+ // Get the specified template by id. Either
+ // retrieves the cached version, or loads it
+ // from the DOM.
+ get: function(templateId) {
+ var cachedTemplate = this.templateCaches[templateId];
+
+ if (!cachedTemplate) {
+ cachedTemplate = new Marionette.TemplateCache(templateId);
+ this.templateCaches[templateId] = cachedTemplate;
+ }
+
+ return cachedTemplate.load();
+ },
+
+ // Clear templates from the cache. If no arguments
+ // are specified, clears all templates:
+ // `clear()`
+ //
+ // If arguments are specified, clears each of the
+ // specified templates from the cache:
+ // `clear("#t1", "#t2", "...")`
+ clear: function() {
+ var i;
+ var args = slice.call(arguments);
+ var length = args.length;
+
+ if (length > 0) {
+ for (i = 0; i < length; i++) {
+ delete this.templateCaches[args[i]];
+ }
+ } else {
+ this.templateCaches = {};
+ }
+ }
+ });
+
+ // TemplateCache instance methods, allowing each
+ // template cache object to manage its own state
+ // and know whether or not it has been loaded
+ _.extend(Marionette.TemplateCache.prototype, {
+
+ // Internal method to load the template
+ load: function() {
+ // Guard clause to prevent loading this template more than once
+ if (this.compiledTemplate) {
+ return this.compiledTemplate;
+ }
+
+ // Load the template and compile it
+ var template = this.loadTemplate(this.templateId);
+ this.compiledTemplate = this.compileTemplate(template);
+
+ return this.compiledTemplate;
+ },
+
+ // Load a template from the DOM, by default. Override
+ // this method to provide your own template retrieval
+ // For asynchronous loading with AMD/RequireJS, consider
+ // using a template-loader plugin as described here:
+ // https://github.com/marionettejs/backbone.marionette/wiki/Using-marionette-with-requirejs
+ loadTemplate: function(templateId) {
+ var template = Backbone.$(templateId).html();
+
+ if (!template || template.length === 0) {
+ throwError('Could not find template: "' + templateId + '"', 'NoTemplateError');
+ }
+
+ return template;
+ },
+
+ // Pre-compile the template before caching it. Override
+ // this method if you do not need to pre-compile a template
+ // (JST / RequireJS for example) or if you want to change
+ // the template engine used (Handebars, etc).
+ compileTemplate: function(rawTemplate) {
+ return _.template(rawTemplate);
+ }
+ });
+
+ // Renderer
+ // --------
+
+ // Render a template with data by passing in the template
+ // selector and the data to render.
+ Marionette.Renderer = {
+
+ // Render a template with data. The `template` parameter is
+ // passed to the `TemplateCache` object to retrieve the
+ // template function. Override this method to provide your own
+ // custom rendering and template handling for all of Marionette.
+ render: function(template, data) {
+ if (!template) {
+ throwError('Cannot render the template since its false, null or undefined.',
+ 'TemplateNotFoundError');
+ }
+
+ var templateFunc;
+ if (typeof template === 'function') {
+ templateFunc = template;
+ } else {
+ templateFunc = Marionette.TemplateCache.get(template);
+ }
+
+ return templateFunc(data);
+ }
+ };
+
+
+ /* jshint maxlen: 114, nonew: false */
+ // Marionette.View
+ // ---------------
+
+ // The core view class that other Marionette views extend from.
+ Marionette.View = Backbone.View.extend({
+
+ constructor: function(options) {
+ _.bindAll(this, 'render');
+
+ // this exposes view options to the view initializer
+ // this is a backfill since backbone removed the assignment
+ // of this.options
+ // at some point however this may be removed
+ this.options = _.extend({}, _.result(this, 'options'), _.isFunction(options) ? options.call(this) : options);
+ // parses out the @ui DSL for events
+ this.events = this.normalizeUIKeys(_.result(this, 'events'));
+
+ if (_.isObject(this.behaviors)) {
+ new Marionette.Behaviors(this);
+ }
+
+ Backbone.View.apply(this, arguments);
+
+ Marionette.MonitorDOMRefresh(this);
+ this.listenTo(this, 'show', this.onShowCalled);
+ },
+
+ // Get the template for this view
+ // instance. You can set a `template` attribute in the view
+ // definition or pass a `template: "whatever"` parameter in
+ // to the constructor options.
+ getTemplate: function() {
+ return this.getOption('template');
+ },
+
+ // Mix in template helper methods. Looks for a
+ // `templateHelpers` attribute, which can either be an
+ // object literal, or a function that returns an object
+ // literal. All methods and attributes from this object
+ // are copies to the object passed in.
+ mixinTemplateHelpers: function(target) {
+ target = target || {};
+ var templateHelpers = this.getOption('templateHelpers');
+ if (_.isFunction(templateHelpers)) {
+ templateHelpers = templateHelpers.call(this);
+ }
+ return _.extend(target, templateHelpers);
+ },
+
+
+ normalizeUIKeys: function(hash) {
+ var ui = _.result(this, 'ui');
+ var uiBindings = _.result(this, '_uiBindings');
+ return Marionette.normalizeUIKeys(hash, uiBindings || ui);
+ },
+
+ // Configure `triggers` to forward DOM events to view
+ // events. `triggers: {"click .foo": "do:foo"}`
+ configureTriggers: function() {
+ if (!this.triggers) { return; }
+
+ var triggerEvents = {};
+
+ // Allow `triggers` to be configured as a function
+ var triggers = this.normalizeUIKeys(_.result(this, 'triggers'));
+
+ // Configure the triggers, prevent default
+ // action and stop propagation of DOM events
+ _.each(triggers, function(value, key) {
+
+ var hasOptions = _.isObject(value);
+ var eventName = hasOptions ? value.event : value;
+
+ // build the event handler function for the DOM event
+ triggerEvents[key] = function(e) {
+
+ // stop the event in its tracks
+ if (e) {
+ var prevent = e.preventDefault;
+ var stop = e.stopPropagation;
+
+ var shouldPrevent = hasOptions ? value.preventDefault : prevent;
+ var shouldStop = hasOptions ? value.stopPropagation : stop;
+
+ if (shouldPrevent && prevent) { prevent.apply(e); }
+ if (shouldStop && stop) { stop.apply(e); }
+ }
+
+ // build the args for the event
+ var args = {
+ view: this,
+ model: this.model,
+ collection: this.collection
+ };
+
+ // trigger the event
+ this.triggerMethod(eventName, args);
+ };
+
+ }, this);
+
+ return triggerEvents;
+ },
+
+ // Overriding Backbone.View's delegateEvents to handle
+ // the `triggers`, `modelEvents`, and `collectionEvents` configuration
+ delegateEvents: function(events) {
+ this._delegateDOMEvents(events);
+ this.bindEntityEvents(this.model, this.getOption('modelEvents'));
+ this.bindEntityEvents(this.collection, this.getOption('collectionEvents'));
+ },
+
+ // internal method to delegate DOM events and triggers
+ _delegateDOMEvents: function(events) {
+ events = events || this.events;
+ if (_.isFunction(events)) { events = events.call(this); }
+
+ // normalize ui keys
+ events = this.normalizeUIKeys(events);
+
+ var combinedEvents = {};
+
+ // look up if this view has behavior events
+ var behaviorEvents = _.result(this, 'behaviorEvents') || {};
+ var triggers = this.configureTriggers();
+
+ // behavior events will be overriden by view events and or triggers
+ _.extend(combinedEvents, behaviorEvents, events, triggers);
+
+ Backbone.View.prototype.delegateEvents.call(this, combinedEvents);
+ },
+
+ // Overriding Backbone.View's undelegateEvents to handle unbinding
+ // the `triggers`, `modelEvents`, and `collectionEvents` config
+ undelegateEvents: function() {
+ var args = Array.prototype.slice.call(arguments);
+ Backbone.View.prototype.undelegateEvents.apply(this, args);
+ this.unbindEntityEvents(this.model, this.getOption('modelEvents'));
+ this.unbindEntityEvents(this.collection, this.getOption('collectionEvents'));
+ },
+
+ // Internal method, handles the `show` event.
+ onShowCalled: function() {},
+
+ // Internal helper method to verify whether the view hasn't been destroyed
+ _ensureViewIsIntact: function() {
+ if (this.isDestroyed) {
+ var err = new Error('Cannot use a view thats already been destroyed.');
+ err.name = 'ViewDestroyedError';
+ throw err;
+ }
+ },
+
+ // Default `destroy` implementation, for removing a view from the
+ // DOM and unbinding it. Regions will call this method
+ // for you. You can specify an `onDestroy` method in your view to
+ // add custom code that is called after the view is destroyed.
+ destroy: function() {
+ if (this.isDestroyed) { return; }
+
+ var args = Array.prototype.slice.call(arguments);
+
+ this.triggerMethod.apply(this, ['before:destroy'].concat(args));
+
+ // mark as destroyed before doing the actual destroy, to
+ // prevent infinite loops within "destroy" event handlers
+ // that are trying to destroy other views
+ this.isDestroyed = true;
+ this.triggerMethod.apply(this, ['destroy'].concat(args));
+
+ // unbind UI elements
+ this.unbindUIElements();
+
+ // remove the view from the DOM
+ this.remove();
+ },
+
+ // This method binds the elements specified in the "ui" hash inside the view's code with
+ // the associated jQuery selectors.
+ bindUIElements: function() {
+ if (!this.ui) { return; }
+
+ // store the ui hash in _uiBindings so they can be reset later
+ // and so re-rendering the view will be able to find the bindings
+ if (!this._uiBindings) {
+ this._uiBindings = this.ui;
+ }
+
+ // get the bindings result, as a function or otherwise
+ var bindings = _.result(this, '_uiBindings');
+
+ // empty the ui so we don't have anything to start with
+ this.ui = {};
+
+ // bind each of the selectors
+ _.each(_.keys(bindings), function(key) {
+ var selector = bindings[key];
+ this.ui[key] = this.$(selector);
+ }, this);
+ },
+
+ // This method unbinds the elements specified in the "ui" hash
+ unbindUIElements: function() {
+ if (!this.ui || !this._uiBindings) { return; }
+
+ // delete all of the existing ui bindings
+ _.each(this.ui, function($el, name) {
+ delete this.ui[name];
+ }, this);
+
+ // reset the ui element to the original bindings configuration
+ this.ui = this._uiBindings;
+ delete this._uiBindings;
+ },
+
+ // import the `triggerMethod` to trigger events with corresponding
+ // methods if the method exists
+ triggerMethod: Marionette.triggerMethod,
+
+ // Imports the "normalizeMethods" to transform hashes of
+ // events=>function references/names to a hash of events=>function references
+ normalizeMethods: Marionette.normalizeMethods,
+
+ // Proxy `getOption` to enable getting options from this or this.options by name.
+ getOption: Marionette.proxyGetOption,
+
+ // Proxy `unbindEntityEvents` to enable binding view's events from another entity.
+ bindEntityEvents: Marionette.proxyBindEntityEvents,
+
+ // Proxy `unbindEntityEvents` to enable unbinding view's events from another entity.
+ unbindEntityEvents: Marionette.proxyUnbindEntityEvents
+ });
+
+ // Item View
+ // ---------
+
+ // A single item view implementation that contains code for rendering
+ // with underscore.js templates, serializing the view's model or collection,
+ // and calling several methods on extended views, such as `onRender`.
+ Marionette.ItemView = Marionette.View.extend({
+
+ // Setting up the inheritance chain which allows changes to
+ // Marionette.View.prototype.constructor which allows overriding
+ constructor: function() {
+ Marionette.View.apply(this, arguments);
+ },
+
+ // Serialize the model or collection for the view. If a model is
+ // found, `.toJSON()` is called. If a collection is found, `.toJSON()`
+ // is also called, but is used to populate an `items` array in the
+ // resulting data. If both are found, defaults to the model.
+ // You can override the `serializeData` method in your own view
+ // definition, to provide custom serialization for your view's data.
+ serializeData: function() {
+ var data = {};
+
+ if (this.model) {
+ data = this.model.toJSON();
+ }
+ else if (this.collection) {
+ data = {items: this.collection.toJSON()};
+ }
+
+ return data;
+ },
+
+ // Render the view, defaulting to underscore.js templates.
+ // You can override this in your view definition to provide
+ // a very specific rendering for your view. In general, though,
+ // you should override the `Marionette.Renderer` object to
+ // change how Marionette renders views.
+ render: function() {
+ this._ensureViewIsIntact();
+
+ this.triggerMethod('before:render', this);
+
+ var data = this.serializeData();
+ data = this.mixinTemplateHelpers(data);
+
+ var template = this.getTemplate();
+ var html = Marionette.Renderer.render(template, data);
+ this.attachElContent(html);
+ this.bindUIElements();
+
+ this.triggerMethod('render', this);
+
+ return this;
+ },
+
+ // Attaches the content of a given view.
+ // This method can be overriden to optimize rendering,
+ // or to render in a non standard way.
+ //
+ // For example, using `innerHTML` instead of `$el.html`
+ //
+ // ```js
+ // attachElContent: function(html) {
+ // this.el.innerHTML = html;
+ // return this;
+ // }
+ // ```
+ attachElContent: function(html) {
+ this.$el.html(html);
+
+ return this;
+ },
+
+ // Override the default destroy event to add a few
+ // more events that are triggered.
+ destroy: function() {
+ if (this.isDestroyed) { return; }
+
+ Marionette.View.prototype.destroy.apply(this, arguments);
+ }
+ });
+
+ /* jshint maxstatements: 14 */
+
+ // Collection View
+ // ---------------
+
+ // A view that iterates over a Backbone.Collection
+ // and renders an individual child view for each model.
+ Marionette.CollectionView = Marionette.View.extend({
+
+ // used as the prefix for child view events
+ // that are forwarded through the collectionview
+ childViewEventPrefix: 'childview',
+
+ // constructor
+ // option to pass `{sort: false}` to prevent the `CollectionView` from
+ // maintaining the sorted order of the collection.
+ // This will fallback onto appending childView's to the end.
+ constructor: function(options){
+ var initOptions = options || {};
+ this.sort = _.isUndefined(initOptions.sort) ? true : initOptions.sort;
+
+ this._initChildViewStorage();
+
+ Marionette.View.apply(this, arguments);
+
+ this._initialEvents();
+ this.initRenderBuffer();
+ },
+
+ // Instead of inserting elements one by one into the page,
+ // it's much more performant to insert elements into a document
+ // fragment and then insert that document fragment into the page
+ initRenderBuffer: function() {
+ this.elBuffer = document.createDocumentFragment();
+ this._bufferedChildren = [];
+ },
+
+ startBuffering: function() {
+ this.initRenderBuffer();
+ this.isBuffering = true;
+ },
+
+ endBuffering: function() {
+ this.isBuffering = false;
+ this._triggerBeforeShowBufferedChildren();
+ this.attachBuffer(this, this.elBuffer);
+ this._triggerShowBufferedChildren();
+ this.initRenderBuffer();
+ },
+
+ _triggerBeforeShowBufferedChildren: function() {
+ if (this._isShown) {
+ _.invoke(this._bufferedChildren, 'triggerMethod', 'before:show');
+ }
+ },
+
+ _triggerShowBufferedChildren: function() {
+ if (this._isShown) {
+ _.each(this._bufferedChildren, function (child) {
+ if (_.isFunction(child.triggerMethod)) {
+ child.triggerMethod('show');
+ } else {
+ Marionette.triggerMethod.call(child, 'show');
+ }
+ });
+ this._bufferedChildren = [];
+ }
+ },
+
+ // Configured the initial events that the collection view
+ // binds to.
+ _initialEvents: function() {
+ if (this.collection) {
+ this.listenTo(this.collection, 'add', this._onCollectionAdd);
+ this.listenTo(this.collection, 'remove', this._onCollectionRemove);
+ this.listenTo(this.collection, 'reset', this.render);
+
+ if (this.sort) {
+ this.listenTo(this.collection, 'sort', this._sortViews);
+ }
+ }
+ },
+
+ // Handle a child added to the collection
+ _onCollectionAdd: function(child, collection, options) {
+ this.destroyEmptyView();
+ var ChildView = this.getChildView(child);
+ var index = this.collection.indexOf(child);
+ this.addChild(child, ChildView, index);
+ },
+
+ // get the child view by model it holds, and remove it
+ _onCollectionRemove: function(model) {
+ var view = this.children.findByModel(model);
+ this.removeChildView(view);
+ this.checkEmpty();
+ },
+
+ // Override from `Marionette.View` to trigger show on child views
+ onShowCalled: function(){
+ this.children.each(function(child){
+ if (_.isFunction(child.triggerMethod)) {
+ child.triggerMethod('show');
+ } else {
+ Marionette.triggerMethod.call(child, 'show');
+ }
+ });
+ },
+
+ // Render children views. Override this method to
+ // provide your own implementation of a render function for
+ // the collection view.
+ render: function() {
+ this._ensureViewIsIntact();
+ this.triggerMethod('before:render', this);
+ this._renderChildren();
+ this.triggerMethod('render', this);
+ return this;
+ },
+
+ // Internal method. This checks for any changes in the order of the collection.
+ // If the index of any view doesn't match, it will render.
+ _sortViews: function(){
+ // check for any changes in sort order of views
+ var orderChanged = this.collection.find(function(item, index){
+ var view = this.children.findByModel(item);
+ return view && view._index !== index;
+ }, this);
+
+ if (orderChanged) {
+ this.render();
+ }
+ },
+
+ // Internal method. Separated so that CompositeView can have
+ // more control over events being triggered, around the rendering
+ // process
+ _renderChildren: function() {
+ this.startBuffering();
+
+ this.destroyEmptyView();
+ this.destroyChildren();
+
+ if (!this.isEmpty(this.collection)) {
+ this.triggerMethod('before:render:collection', this);
+ this.showCollection();
+ this.triggerMethod('render:collection', this);
+ } else {
+ this.showEmptyView();
+ }
+
+ this.endBuffering();
+ },
+
+ // Internal method to loop through collection and show each child view.
+ showCollection: function() {
+ var ChildView;
+ this.collection.each(function(child, index) {
+ ChildView = this.getChildView(child);
+ this.addChild(child, ChildView, index);
+ }, this);
+ },
+
+ // Internal method to show an empty view in place of
+ // a collection of child views, when the collection is empty
+ showEmptyView: function() {
+ var EmptyView = this.getEmptyView();
+
+ if (EmptyView && !this._showingEmptyView) {
+ this.triggerMethod('before:render:empty');
+
+ this._showingEmptyView = true;
+ var model = new Backbone.Model();
+ this.addEmptyView(model, EmptyView);
+
+ this.triggerMethod('render:empty');
+ }
+ },
+
+ // Internal method to destroy an existing emptyView instance
+ // if one exists. Called when a collection view has been
+ // rendered empty, and then a child is added to the collection.
+ destroyEmptyView: function() {
+ if (this._showingEmptyView) {
+ this.destroyChildren();
+ delete this._showingEmptyView;
+ }
+ },
+
+ // Retrieve the empty view class
+ getEmptyView: function() {
+ return this.getOption('emptyView');
+ },
+
+ // Render and show the emptyView. Similar to addChild method
+ // but "child:added" events are not fired, and the event from
+ // emptyView are not forwarded
+ addEmptyView: function(child, EmptyView){
+
+ // get the emptyViewOptions, falling back to childViewOptions
+ var emptyViewOptions = this.getOption('emptyViewOptions') ||
+ this.getOption('childViewOptions');
+
+ if (_.isFunction(emptyViewOptions)){
+ emptyViewOptions = emptyViewOptions.call(this);
+ }
+
+ // build the empty view
+ var view = this.buildChildView(child, EmptyView, emptyViewOptions);
+
+ // trigger the 'before:show' event on `view` if the collection view
+ // has already been shown
+ if (this._isShown){
+ this.triggerMethod.call(view, 'before:show');
+ }
+
+ // Store the `emptyView` like a `childView` so we can properly
+ // remove and/or close it later
+ this.children.add(view);
+
+ // Render it and show it
+ this.renderChildView(view, -1);
+
+ // call the 'show' method if the collection view
+ // has already been shown
+ if (this._isShown){
+ this.triggerMethod.call(view, 'show');
+ }
+ },
+
+ // Retrieve the childView class, either from `this.options.childView`
+ // or from the `childView` in the object definition. The "options"
+ // takes precedence.
+ getChildView: function(child) {
+ var childView = this.getOption('childView');
+
+ if (!childView) {
+ throwError('A "childView" must be specified', 'NoChildViewError');
+ }
+
+ return childView;
+ },
+
+ // Render the child's view and add it to the
+ // HTML for the collection view at a given index.
+ // This will also update the indices of later views in the collection
+ // in order to keep the children in sync with the collection.
+ addChild: function(child, ChildView, index) {
+ var childViewOptions = this.getOption('childViewOptions');
+ if (_.isFunction(childViewOptions)) {
+ childViewOptions = childViewOptions.call(this, child, index);
+ }
+
+ var view = this.buildChildView(child, ChildView, childViewOptions);
+
+ // increment indices of views after this one
+ this._updateIndices(view, true, index);
+
+ this._addChildView(view, index);
+
+ return view;
+ },
+
+ // Internal method. This decrements or increments the indices of views after the
+ // added/removed view to keep in sync with the collection.
+ _updateIndices: function(view, increment, index) {
+ if (!this.sort) {
+ return;
+ }
+
+ if (increment) {
+ // assign the index to the view
+ view._index = index;
+
+ // increment the index of views after this one
+ this.children.each(function (laterView) {
+ if (laterView._index >= view._index) {
+ laterView._index++;
+ }
+ });
+ }
+ else {
+ // decrement the index of views after this one
+ this.children.each(function (laterView) {
+ if (laterView._index >= view._index) {
+ laterView._index--;
+ }
+ });
+ }
+ },
+
+
+ // Internal Method. Add the view to children and render it at
+ // the given index.
+ _addChildView: function(view, index) {
+ // set up the child view event forwarding
+ this.proxyChildEvents(view);
+
+ this.triggerMethod('before:add:child', view);
+
+ // Store the child view itself so we can properly
+ // remove and/or destroy it later
+ this.children.add(view);
+ this.renderChildView(view, index);
+
+ if (this._isShown && !this.isBuffering){
+ if (_.isFunction(view.triggerMethod)) {
+ view.triggerMethod('show');
+ } else {
+ Marionette.triggerMethod.call(view, 'show');
+ }
+ }
+
+ this.triggerMethod('add:child', view);
+ },
+
+ // render the child view
+ renderChildView: function(view, index) {
+ view.render();
+ this.attachHtml(this, view, index);
+ },
+
+ // Build a `childView` for a model in the collection.
+ buildChildView: function(child, ChildViewClass, childViewOptions) {
+ var options = _.extend({model: child}, childViewOptions);
+ return new ChildViewClass(options);
+ },
+
+ // Remove the child view and destroy it.
+ // This function also updates the indices of
+ // later views in the collection in order to keep
+ // the children in sync with the collection.
+ removeChildView: function(view) {
+
+ if (view) {
+ this.triggerMethod('before:remove:child', view);
+ // call 'destroy' or 'remove', depending on which is found
+ if (view.destroy) { view.destroy(); }
+ else if (view.remove) { view.remove(); }
+
+ this.stopListening(view);
+ this.children.remove(view);
+ this.triggerMethod('remove:child', view);
+
+ // decrement the index of views after this one
+ this._updateIndices(view, false);
+ }
+
+ },
+
+ // check if the collection is empty
+ isEmpty: function(collection) {
+ return !this.collection || this.collection.length === 0;
+ },
+
+ // If empty, show the empty view
+ checkEmpty: function() {
+ if (this.isEmpty(this.collection)) {
+ this.showEmptyView();
+ }
+ },
+
+ // You might need to override this if you've overridden attachHtml
+ attachBuffer: function(collectionView, buffer) {
+ collectionView.$el.append(buffer);
+ },
+
+ // Append the HTML to the collection's `el`.
+ // Override this method to do something other
+ // than `.append`.
+ attachHtml: function(collectionView, childView, index) {
+ if (collectionView.isBuffering) {
+ // buffering happens on reset events and initial renders
+ // in order to reduce the number of inserts into the
+ // document, which are expensive.
+ collectionView.elBuffer.appendChild(childView.el);
+ collectionView._bufferedChildren.push(childView);
+ }
+ else {
+ // If we've already rendered the main collection, append
+ // the new child into the correct order if we need to. Otherwise
+ // append to the end.
+ if (!collectionView._insertBefore(childView, index)){
+ collectionView._insertAfter(childView);
+ }
+ }
+ },
+
+ // Internal method. Check whether we need to insert the view into
+ // the correct position.
+ _insertBefore: function(childView, index) {
+ var currentView;
+ var findPosition = this.sort && (index < this.children.length - 1);
+ if (findPosition) {
+ // Find the view after this one
+ currentView = this.children.find(function (view) {
+ return view._index === index + 1;
+ });
+ }
+
+ if (currentView) {
+ currentView.$el.before(childView.el);
+ return true;
+ }
+
+ return false;
+ },
+
+ // Internal method. Append a view to the end of the $el
+ _insertAfter: function(childView) {
+ this.$el.append(childView.el);
+ },
+
+ // Internal method to set up the `children` object for
+ // storing all of the child views
+ _initChildViewStorage: function() {
+ this.children = new Backbone.ChildViewContainer();
+ },
+
+ // Handle cleanup and other destroying needs for the collection of views
+ destroy: function() {
+ if (this.isDestroyed) { return; }
+
+ this.triggerMethod('before:destroy:collection');
+ this.destroyChildren();
+ this.triggerMethod('destroy:collection');
+
+ Marionette.View.prototype.destroy.apply(this, arguments);
+ },
+
+ // Destroy the child views that this collection view
+ // is holding on to, if any
+ destroyChildren: function() {
+ this.children.each(this.removeChildView, this);
+ this.checkEmpty();
+ },
+
+ // Set up the child view event forwarding. Uses a "childview:"
+ // prefix in front of all forwarded events.
+ proxyChildEvents: function(view) {
+ var prefix = this.getOption('childViewEventPrefix');
+
+ // Forward all child view events through the parent,
+ // prepending "childview:" to the event name
+ this.listenTo(view, 'all', function() {
+ var args = Array.prototype.slice.call(arguments);
+ var rootEvent = args[0];
+ var childEvents = this.normalizeMethods(_.result(this, 'childEvents'));
+
+ args[0] = prefix + ':' + rootEvent;
+ args.splice(1, 0, view);
+
+ // call collectionView childEvent if defined
+ if (typeof childEvents !== 'undefined' && _.isFunction(childEvents[rootEvent])) {
+ childEvents[rootEvent].apply(this, args.slice(1));
+ }
+
+ this.triggerMethod.apply(this, args);
+ }, this);
+ }
+ });
+
+ /* jshint maxstatements: 17, maxlen: 117 */
+
+ // Composite View
+ // --------------
+
+ // Used for rendering a branch-leaf, hierarchical structure.
+ // Extends directly from CollectionView and also renders an
+ // a child view as `modelView`, for the top leaf
+ Marionette.CompositeView = Marionette.CollectionView.extend({
+
+ // Setting up the inheritance chain which allows changes to
+ // Marionette.CollectionView.prototype.constructor which allows overriding
+ // option to pass '{sort: false}' to prevent the CompositeView from
+ // maintaining the sorted order of the collection.
+ // This will fallback onto appending childView's to the end.
+ constructor: function() {
+ Marionette.CollectionView.apply(this, arguments);
+ },
+
+ // Configured the initial events that the composite view
+ // binds to. Override this method to prevent the initial
+ // events, or to add your own initial events.
+ _initialEvents: function() {
+
+ // Bind only after composite view is rendered to avoid adding child views
+ // to nonexistent childViewContainer
+ this.once('render', function() {
+ if (this.collection) {
+ this.listenTo(this.collection, 'add', this._onCollectionAdd);
+ this.listenTo(this.collection, 'remove', this._onCollectionRemove);
+ this.listenTo(this.collection, 'reset', this._renderChildren);
+
+ if (this.sort) {
+ this.listenTo(this.collection, 'sort', this._sortViews);
+ }
+ }
+ });
+
+ },
+
+ // Retrieve the `childView` to be used when rendering each of
+ // the items in the collection. The default is to return
+ // `this.childView` or Marionette.CompositeView if no `childView`
+ // has been defined
+ getChildView: function(child) {
+ var childView = this.getOption('childView') || this.constructor;
+
+ if (!childView) {
+ throwError('A "childView" must be specified', 'NoChildViewError');
+ }
+
+ return childView;
+ },
+
+ // Serialize the collection for the view.
+ // You can override the `serializeData` method in your own view
+ // definition, to provide custom serialization for your view's data.
+ serializeData: function() {
+ var data = {};
+
+ if (this.model) {
+ data = this.model.toJSON();
+ }
+
+ return data;
+ },
+
+ // Renders the model once, and the collection once. Calling
+ // this again will tell the model's view to re-render itself
+ // but the collection will not re-render.
+ render: function() {
+ this._ensureViewIsIntact();
+ this.isRendered = true;
+ this.resetChildViewContainer();
+
+ this.triggerMethod('before:render', this);
+
+ this._renderRoot();
+ this._renderChildren();
+
+ this.triggerMethod('render', this);
+ return this;
+ },
+
+ _renderChildren: function() {
+ if (this.isRendered) {
+ Marionette.CollectionView.prototype._renderChildren.call(this);
+ }
+ },
+
+ // Render the root template that the children
+ // views are appended to
+ _renderRoot: function() {
+ var data = {};
+ data = this.serializeData();
+ data = this.mixinTemplateHelpers(data);
+
+ this.triggerMethod('before:render:template');
+
+ var template = this.getTemplate();
+ var html = Marionette.Renderer.render(template, data);
+ this.attachElContent(html);
+
+ // the ui bindings is done here and not at the end of render since they
+ // will not be available until after the model is rendered, but should be
+ // available before the collection is rendered.
+ this.bindUIElements();
+ this.triggerMethod('render:template');
+ },
+
+ // Attaches the content of the root.
+ // This method can be overriden to optimize rendering,
+ // or to render in a non standard way.
+ //
+ // For example, using `innerHTML` instead of `$el.html`
+ //
+ // ```js
+ // attachElContent: function(html) {
+ // this.el.innerHTML = html;
+ // return this;
+ // }
+ // ```
+ attachElContent: function(html) {
+ this.$el.html(html);
+
+ return this;
+ },
+
+ // You might need to override this if you've overridden attachHtml
+ attachBuffer: function(compositeView, buffer) {
+ var $container = this.getChildViewContainer(compositeView);
+ $container.append(buffer);
+ },
+
+ // Internal method. Append a view to the end of the $el.
+ // Overidden from CollectionView to ensure view is appended to
+ // childViewContainer
+ _insertAfter: function (childView) {
+ var $container = this.getChildViewContainer(this);
+ $container.append(childView.el);
+ },
+
+ // Internal method to ensure an `$childViewContainer` exists, for the
+ // `attachHtml` method to use.
+ getChildViewContainer: function(containerView) {
+ if ('$childViewContainer' in containerView) {
+ return containerView.$childViewContainer;
+ }
+
+ var container;
+ var childViewContainer = Marionette.getOption(containerView, 'childViewContainer');
+ if (childViewContainer) {
+
+ var selector = _.isFunction(childViewContainer) ? childViewContainer.call(containerView) : childViewContainer;
+
+ if (selector.charAt(0) === '@' && containerView.ui) {
+ container = containerView.ui[selector.substr(4)];
+ } else {
+ container = containerView.$(selector);
+ }
+
+ if (container.length <= 0) {
+ throwError('The specified "childViewContainer" was not found: ' +
+ containerView.childViewContainer, 'ChildViewContainerMissingError');
+ }
+
+ } else {
+ container = containerView.$el;
+ }
+
+ containerView.$childViewContainer = container;
+ return container;
+ },
+
+ // Internal method to reset the `$childViewContainer` on render
+ resetChildViewContainer: function() {
+ if (this.$childViewContainer) {
+ delete this.$childViewContainer;
+ }
+ }
+ });
+
+ // LayoutView
+ // ----------
+
+ // Used for managing application layoutViews, nested layoutViews and
+ // multiple regions within an application or sub-application.
+ //
+ // A specialized view class that renders an area of HTML and then
+ // attaches `Region` instances to the specified `regions`.
+ // Used for composite view management and sub-application areas.
+ Marionette.LayoutView = Marionette.ItemView.extend({
+ regionClass: Marionette.Region,
+
+ // Ensure the regions are available when the `initialize` method
+ // is called.
+ constructor: function(options) {
+ options = options || {};
+
+ this._firstRender = true;
+ this._initializeRegions(options);
+
+ Marionette.ItemView.call(this, options);
+ },
+
+ // LayoutView's render will use the existing region objects the
+ // first time it is called. Subsequent calls will destroy the
+ // views that the regions are showing and then reset the `el`
+ // for the regions to the newly rendered DOM elements.
+ render: function() {
+ this._ensureViewIsIntact();
+
+ if (this._firstRender) {
+ // if this is the first render, don't do anything to
+ // reset the regions
+ this._firstRender = false;
+ } else {
+ // If this is not the first render call, then we need to
+ // re-initialize the `el` for each region
+ this._reInitializeRegions();
+ }
+
+ return Marionette.ItemView.prototype.render.apply(this, arguments);
+ },
+
+ // Handle destroying regions, and then destroy the view itself.
+ destroy: function() {
+ if (this.isDestroyed) { return; }
+
+ this.regionManager.destroy();
+ Marionette.ItemView.prototype.destroy.apply(this, arguments);
+ },
+
+ // Add a single region, by name, to the layoutView
+ addRegion: function(name, definition) {
+ this.triggerMethod('before:region:add', name);
+ var regions = {};
+ regions[name] = definition;
+ return this._buildRegions(regions)[name];
+ },
+
+ // Add multiple regions as a {name: definition, name2: def2} object literal
+ addRegions: function(regions) {
+ this.regions = _.extend({}, this.regions, regions);
+ return this._buildRegions(regions);
+ },
+
+ // Remove a single region from the LayoutView, by name
+ removeRegion: function(name) {
+ this.triggerMethod('before:region:remove', name);
+ delete this.regions[name];
+ return this.regionManager.removeRegion(name);
+ },
+
+ // Provides alternative access to regions
+ // Accepts the region name
+ // getRegion('main')
+ getRegion: function(region) {
+ return this.regionManager.get(region);
+ },
+
+ // Get all regions
+ getRegions: function(){
+ return this.regionManager.getRegions();
+ },
+
+ // internal method to build regions
+ _buildRegions: function(regions) {
+ var that = this;
+
+ var defaults = {
+ regionClass: this.getOption('regionClass'),
+ parentEl: function() { return that.$el; }
+ };
+
+ return this.regionManager.addRegions(regions, defaults);
+ },
+
+ // Internal method to initialize the regions that have been defined in a
+ // `regions` attribute on this layoutView.
+ _initializeRegions: function(options) {
+ var regions;
+ this._initRegionManager();
+
+ if (_.isFunction(this.regions)) {
+ regions = this.regions(options);
+ } else {
+ regions = this.regions || {};
+ }
+
+ // Enable users to define `regions` as instance options.
+ var regionOptions = this.getOption.call(options, 'regions');
+
+ // enable region options to be a function
+ if (_.isFunction(regionOptions)) {
+ regionOptions = regionOptions.call(this, options);
+ }
+
+ _.extend(regions, regionOptions);
+
+ this.addRegions(regions);
+ },
+
+ // Internal method to re-initialize all of the regions by updating the `el` that
+ // they point to
+ _reInitializeRegions: function() {
+ this.regionManager.emptyRegions();
+ this.regionManager.each(function(region) {
+ region.reset();
+ });
+ },
+
+ // Enable easy overiding of the default `RegionManager`
+ // for customized region interactions and buisness specific
+ // view logic for better control over single regions.
+ getRegionManager: function() {
+ return new Marionette.RegionManager();
+ },
+
+ // Internal method to initialize the region manager
+ // and all regions in it
+ _initRegionManager: function() {
+ this.regionManager = this.getRegionManager();
+
+ this.listenTo(this.regionManager, 'before:add:region', function(name) {
+ this.triggerMethod('before:add:region', name);
+ });
+
+ this.listenTo(this.regionManager, 'add:region', function(name, region) {
+ this[name] = region;
+ this.triggerMethod('add:region', name, region);
+ });
+
+ this.listenTo(this.regionManager, 'before:remove:region', function(name) {
+ this.triggerMethod('before:remove:region', name);
+ });
+
+ this.listenTo(this.regionManager, 'remove:region', function(name, region) {
+ delete this[name];
+ this.triggerMethod('remove:region', name, region);
+ });
+ }
+ });
+
+
+ // Behavior
+ // -----------
+
+ // A Behavior is an isolated set of DOM /
+ // user interactions that can be mixed into any View.
+ // Behaviors allow you to blackbox View specific interactions
+ // into portable logical chunks, keeping your views simple and your code DRY.
+
+ Marionette.Behavior = (function(_, Backbone) {
+ function Behavior(options, view) {
+ // Setup reference to the view.
+ // this comes in handle when a behavior
+ // wants to directly talk up the chain
+ // to the view.
+ this.view = view;
+ this.defaults = _.result(this, 'defaults') || {};
+ this.options = _.extend({}, this.defaults, options);
+
+ // proxy behavior $ method to the view
+ // this is useful for doing jquery DOM lookups
+ // scoped to behaviors view.
+ this.$ = function() {
+ return this.view.$.apply(this.view, arguments);
+ };
+
+ // Call the initialize method passing
+ // the arguments from the instance constructor
+ this.initialize.apply(this, arguments);
+ }
+
+ _.extend(Behavior.prototype, Backbone.Events, {
+ initialize: function() {},
+
+ // stopListening to behavior `onListen` events.
+ destroy: function() {
+ this.stopListening();
+ },
+
+ // import the `triggerMethod` to trigger events with corresponding
+ // methods if the method exists
+ triggerMethod: Marionette.triggerMethod,
+
+ // Proxy `getOption` to enable getting options from this or this.options by name.
+ getOption: Marionette.proxyGetOption,
+
+ // Proxy `unbindEntityEvents` to enable binding view's events from another entity.
+ bindEntityEvents: Marionette.proxyBindEntityEvents,
+
+ // Proxy `unbindEntityEvents` to enable unbinding view's events from another entity.
+ unbindEntityEvents: Marionette.proxyUnbindEntityEvents
+ });
+
+ // Borrow Backbones extend implementation
+ // this allows us to setup a proper
+ // inheritence pattern that follow in suite
+ // with the rest of Marionette views.
+ Behavior.extend = Marionette.extend;
+
+ return Behavior;
+ })(_, Backbone);
+
+ /* jshint maxlen: 143, nonew: false */
+ // Marionette.Behaviors
+ // --------
+
+ // Behaviors is a utility class that takes care of
+ // glueing your behavior instances to their given View.
+ // The most important part of this class is that you
+ // **MUST** override the class level behaviorsLookup
+ // method for things to work properly.
+
+ Marionette.Behaviors = (function(Marionette, _) {
+
+ function Behaviors(view, behaviors) {
+ // Behaviors defined on a view can be a flat object literal
+ // or it can be a function that returns an object.
+ behaviors = Behaviors.parseBehaviors(view, behaviors || _.result(view, 'behaviors'));
+
+ // Wraps several of the view's methods
+ // calling the methods first on each behavior
+ // and then eventually calling the method on the view.
+ Behaviors.wrap(view, behaviors, [
+ 'bindUIElements', 'unbindUIElements',
+ 'delegateEvents', 'undelegateEvents',
+ 'behaviorEvents', 'triggerMethod',
+ 'setElement', 'destroy'
+ ]);
+ }
+
+ var methods = {
+ setElement: function(setElement, behaviors) {
+ setElement.apply(this, _.tail(arguments, 2));
+
+ // proxy behavior $el to the view's $el.
+ // This is needed because a view's $el proxy
+ // is not set until after setElement is called.
+ _.each(behaviors, function(b) {
+ b.$el = this.$el;
+ }, this);
+ },
+
+ destroy: function(destroy, behaviors) {
+ var args = _.tail(arguments, 2);
+ destroy.apply(this, args);
+
+ // Call destroy on each behavior after
+ // destroying the view.
+ // This unbinds event listeners
+ // that behaviors have registerd for.
+ _.invoke(behaviors, 'destroy', args);
+ },
+
+ bindUIElements: function(bindUIElements, behaviors) {
+ bindUIElements.apply(this);
+ _.invoke(behaviors, bindUIElements);
+ },
+
+ unbindUIElements: function(unbindUIElements, behaviors) {
+ unbindUIElements.apply(this);
+ _.invoke(behaviors, unbindUIElements);
+ },
+
+ triggerMethod: function(triggerMethod, behaviors) {
+ var args = _.tail(arguments, 2);
+ triggerMethod.apply(this, args);
+
+ _.each(behaviors, function(b) {
+ triggerMethod.apply(b, args);
+ });
+ },
+
+ delegateEvents: function(delegateEvents, behaviors) {
+ var args = _.tail(arguments, 2);
+ delegateEvents.apply(this, args);
+
+ _.each(behaviors, function(b) {
+ Marionette.bindEntityEvents(b, this.model, Marionette.getOption(b, 'modelEvents'));
+ Marionette.bindEntityEvents(b, this.collection, Marionette.getOption(b, 'collectionEvents'));
+ }, this);
+ },
+
+ undelegateEvents: function(undelegateEvents, behaviors) {
+ var args = _.tail(arguments, 2);
+ undelegateEvents.apply(this, args);
+
+ _.each(behaviors, function(b) {
+ Marionette.unbindEntityEvents(b, this.model, Marionette.getOption(b, 'modelEvents'));
+ Marionette.unbindEntityEvents(b, this.collection, Marionette.getOption(b, 'collectionEvents'));
+ }, this);
+ },
+
+ behaviorEvents: function(behaviorEvents, behaviors) {
+ var _behaviorsEvents = {};
+ var viewUI = _.result(this, 'ui');
+
+ _.each(behaviors, function(b, i) {
+ var _events = {};
+ var behaviorEvents = _.clone(_.result(b, 'events')) || {};
+ var behaviorUI = _.result(b, 'ui');
+
+ // Construct an internal UI hash first using
+ // the views UI hash and then the behaviors UI hash.
+ // This allows the user to use UI hash elements
+ // defined in the parent view as well as those
+ // defined in the given behavior.
+ var ui = _.extend({}, viewUI, behaviorUI);
+
+ // Normalize behavior events hash to allow
+ // a user to use the @ui. syntax.
+ behaviorEvents = Marionette.normalizeUIKeys(behaviorEvents, ui);
+
+ _.each(_.keys(behaviorEvents), function(key) {
+ // Append white-space at the end of each key to prevent behavior key collisions.
+ // This is relying on the fact that backbone events considers "click .foo" the same as
+ // "click .foo ".
+
+ // +2 is used because new Array(1) or 0 is "" and not " "
+ var whitespace = (new Array(i + 2)).join(' ');
+ var eventKey = key + whitespace;
+ var handler = _.isFunction(behaviorEvents[key]) ? behaviorEvents[key] : b[behaviorEvents[key]];
+
+ _events[eventKey] = _.bind(handler, b);
+ });
+
+ _behaviorsEvents = _.extend(_behaviorsEvents, _events);
+ });
+
+ return _behaviorsEvents;
+ }
+ };
+
+ _.extend(Behaviors, {
+
+ // Placeholder method to be extended by the user.
+ // The method should define the object that stores the behaviors.
+ // i.e.
+ //
+ // ```js
+ // Marionette.Behaviors.behaviorsLookup: function() {
+ // return App.Behaviors
+ // }
+ // ```
+ behaviorsLookup: function() {
+ throw new Error('You must define where your behaviors are stored.' +
+ 'See https://github.com/marionettejs/backbone.marionette' +
+ '/blob/master/docs/marionette.behaviors.md#behaviorslookup');
+ },
+
+ // Takes care of getting the behavior class
+ // given options and a key.
+ // If a user passes in options.behaviorClass
+ // default to using that. Otherwise delegate
+ // the lookup to the users `behaviorsLookup` implementation.
+ getBehaviorClass: function(options, key) {
+ if (options.behaviorClass) {
+ return options.behaviorClass;
+ }
+
+ // Get behavior class can be either a flat object or a method
+ return _.isFunction(Behaviors.behaviorsLookup) ? Behaviors.behaviorsLookup.apply(this, arguments)[key] : Behaviors.behaviorsLookup[key];
+ },
+
+ // Iterate over the behaviors object, for each behavior
+ // instantiate it and get its grouped behaviors.
+ parseBehaviors: function(view, behaviors) {
+ return _.chain(behaviors).map(function(options, key) {
+ var BehaviorClass = Behaviors.getBehaviorClass(options, key);
+
+ var behavior = new BehaviorClass(options, view);
+ var nestedBehaviors = Behaviors.parseBehaviors(view, _.result(behavior, 'behaviors'));
+
+ return [behavior].concat(nestedBehaviors);
+ }).flatten().value();
+ },
+
+ // Wrap view internal methods so that they delegate to behaviors. For example,
+ // `onDestroy` should trigger destroy on all of the behaviors and then destroy itself.
+ // i.e.
+ //
+ // `view.delegateEvents = _.partial(methods.delegateEvents, view.delegateEvents, behaviors);`
+ wrap: function(view, behaviors, methodNames) {
+ _.each(methodNames, function(methodName) {
+ view[methodName] = _.partial(methods[methodName], view[methodName], behaviors);
+ });
+ }
+ });
+
+ return Behaviors;
+
+ })(Marionette, _);
+
+
+ // AppRouter
+ // ---------
+
+ // Reduce the boilerplate code of handling route events
+ // and then calling a single method on another object.
+ // Have your routers configured to call the method on
+ // your object, directly.
+ //
+ // Configure an AppRouter with `appRoutes`.
+ //
+ // App routers can only take one `controller` object.
+ // It is recommended that you divide your controller
+ // objects in to smaller pieces of related functionality
+ // and have multiple routers / controllers, instead of
+ // just one giant router and controller.
+ //
+ // You can also add standard routes to an AppRouter.
+
+ Marionette.AppRouter = Backbone.Router.extend({
+
+ constructor: function(options) {
+ Backbone.Router.apply(this, arguments);
+
+ this.options = options || {};
+
+ var appRoutes = this.getOption('appRoutes');
+ var controller = this._getController();
+ this.processAppRoutes(controller, appRoutes);
+ this.on('route', this._processOnRoute, this);
+ },
+
+ // Similar to route method on a Backbone Router but
+ // method is called on the controller
+ appRoute: function(route, methodName) {
+ var controller = this._getController();
+ this._addAppRoute(controller, route, methodName);
+ },
+
+ // process the route event and trigger the onRoute
+ // method call, if it exists
+ _processOnRoute: function(routeName, routeArgs) {
+ // find the path that matched
+ var routePath = _.invert(this.appRoutes)[routeName];
+
+ // make sure an onRoute is there, and call it
+ if (_.isFunction(this.onRoute)) {
+ this.onRoute(routeName, routePath, routeArgs);
+ }
+ },
+
+ // Internal method to process the `appRoutes` for the
+ // router, and turn them in to routes that trigger the
+ // specified method on the specified `controller`.
+ processAppRoutes: function(controller, appRoutes) {
+ if (!appRoutes) { return; }
+
+ var routeNames = _.keys(appRoutes).reverse(); // Backbone requires reverted order of routes
+
+ _.each(routeNames, function(route) {
+ this._addAppRoute(controller, route, appRoutes[route]);
+ }, this);
+ },
+
+ _getController: function() {
+ return this.getOption('controller');
+ },
+
+ _addAppRoute: function(controller, route, methodName) {
+ var method = controller[methodName];
+
+ if (!method) {
+ throwError('Method "' + methodName + '" was not found on the controller');
+ }
+
+ this.route(route, methodName, _.bind(method, controller));
+ },
+
+ // Proxy `getOption` to enable getting options from this or this.options by name.
+ getOption: Marionette.proxyGetOption
+ });
+
+ // Application
+ // -----------
+
+ // Contain and manage the composite application as a whole.
+ // Stores and starts up `Region` objects, includes an
+ // event aggregator as `app.vent`
+ Marionette.Application = function(options) {
+ this._initRegionManager();
+ this._initCallbacks = new Marionette.Callbacks();
+ var globalCh = Backbone.Wreqr.radio.channel('global');
+ this.vent = globalCh.vent;
+ this.commands = globalCh.commands;
+ this.reqres = globalCh.reqres;
+ this.submodules = {};
+
+ _.extend(this, options);
+ };
+
+ _.extend(Marionette.Application.prototype, Backbone.Events, {
+ // Command execution, facilitated by Backbone.Wreqr.Commands
+ execute: function() {
+ this.commands.execute.apply(this.commands, arguments);
+ },
+
+ // Request/response, facilitated by Backbone.Wreqr.RequestResponse
+ request: function() {
+ return this.reqres.request.apply(this.reqres, arguments);
+ },
+
+ // Add an initializer that is either run at when the `start`
+ // method is called, or run immediately if added after `start`
+ // has already been called.
+ addInitializer: function(initializer) {
+ this._initCallbacks.add(initializer);
+ },
+
+ // kick off all of the application's processes.
+ // initializes all of the regions that have been added
+ // to the app, and runs all of the initializer functions
+ start: function(options) {
+ this.triggerMethod('before:start', options);
+ this._initCallbacks.run(options, this);
+ this.triggerMethod('start', options);
+ },
+
+ // Add regions to your app.
+ // Accepts a hash of named strings or Region objects
+ // addRegions({something: "#someRegion"})
+ // addRegions({something: Region.extend({el: "#someRegion"}) });
+ addRegions: function(regions) {
+ return this._regionManager.addRegions(regions);
+ },
+
+ // Empty all regions in the app, without removing them
+ emptyRegions: function() {
+ this._regionManager.emptyRegions();
+ },
+
+ // Removes a region from your app, by name
+ // Accepts the regions name
+ // removeRegion('myRegion')
+ removeRegion: function(region) {
+ this._regionManager.removeRegion(region);
+ },
+
+ // Provides alternative access to regions
+ // Accepts the region name
+ // getRegion('main')
+ getRegion: function(region) {
+ return this._regionManager.get(region);
+ },
+
+ // Get all the regions from the region manager
+ getRegions: function(){
+ return this._regionManager.getRegions();
+ },
+
+ // Create a module, attached to the application
+ module: function(moduleNames, moduleDefinition) {
+
+ // Overwrite the module class if the user specifies one
+ var ModuleClass = Marionette.Module.getClass(moduleDefinition);
+
+ // slice the args, and add this application object as the
+ // first argument of the array
+ var args = slice.call(arguments);
+ args.unshift(this);
+
+ // see the Marionette.Module object for more information
+ return ModuleClass.create.apply(ModuleClass, args);
+ },
+
+ // Internal method to set up the region manager
+ _initRegionManager: function() {
+ this._regionManager = new Marionette.RegionManager();
+
+ this.listenTo(this._regionManager, 'before:add:region', function(name) {
+ this.triggerMethod('before:add:region', name);
+ });
+
+ this.listenTo(this._regionManager, 'add:region', function(name, region) {
+ this[name] = region;
+ this.triggerMethod('add:region', name, region);
+ });
+
+ this.listenTo(this._regionManager, 'before:remove:region', function(name) {
+ this.triggerMethod('before:remove:region', name);
+ });
+
+ this.listenTo(this._regionManager, 'remove:region', function(name, region) {
+ delete this[name];
+ this.triggerMethod('remove:region', name, region);
+ });
+ },
+
+ // import the `triggerMethod` to trigger events with corresponding
+ // methods if the method exists
+ triggerMethod: Marionette.triggerMethod
+ });
+
+ // Copy the `extend` function used by Backbone's classes
+ Marionette.Application.extend = Marionette.extend;
+
+ /* jshint maxparams: 9 */
+
+ // Module
+ // ------
+
+ // A simple module system, used to create privacy and encapsulation in
+ // Marionette applications
+ Marionette.Module = function(moduleName, app, options) {
+ this.moduleName = moduleName;
+ this.options = _.extend({}, this.options, options);
+ // Allow for a user to overide the initialize
+ // for a given module instance.
+ this.initialize = options.initialize || this.initialize;
+
+ // Set up an internal store for sub-modules.
+ this.submodules = {};
+
+ this._setupInitializersAndFinalizers();
+
+ // Set an internal reference to the app
+ // within a module.
+ this.app = app;
+
+ // By default modules start with their parents.
+ this.startWithParent = true;
+
+ if (_.isFunction(this.initialize)) {
+ this.initialize(moduleName, app, this.options);
+ }
+ };
+
+ Marionette.Module.extend = Marionette.extend;
+
+ // Extend the Module prototype with events / listenTo, so that the module
+ // can be used as an event aggregator or pub/sub.
+ _.extend(Marionette.Module.prototype, Backbone.Events, {
+
+ // Initialize is an empty function by default. Override it with your own
+ // initialization logic when extending Marionette.Module.
+ initialize: function() {},
+
+ // Initializer for a specific module. Initializers are run when the
+ // module's `start` method is called.
+ addInitializer: function(callback) {
+ this._initializerCallbacks.add(callback);
+ },
+
+ // Finalizers are run when a module is stopped. They are used to teardown
+ // and finalize any variables, references, events and other code that the
+ // module had set up.
+ addFinalizer: function(callback) {
+ this._finalizerCallbacks.add(callback);
+ },
+
+ // Start the module, and run all of its initializers
+ start: function(options) {
+ // Prevent re-starting a module that is already started
+ if (this._isInitialized) { return; }
+
+ // start the sub-modules (depth-first hierarchy)
+ _.each(this.submodules, function(mod) {
+ // check to see if we should start the sub-module with this parent
+ if (mod.startWithParent) {
+ mod.start(options);
+ }
+ });
+
+ // run the callbacks to "start" the current module
+ this.triggerMethod('before:start', options);
+
+ this._initializerCallbacks.run(options, this);
+ this._isInitialized = true;
+
+ this.triggerMethod('start', options);
+ },
+
+ // Stop this module by running its finalizers and then stop all of
+ // the sub-modules for this module
+ stop: function() {
+ // if we are not initialized, don't bother finalizing
+ if (!this._isInitialized) { return; }
+ this._isInitialized = false;
+
+ this.triggerMethod('before:stop');
+
+ // stop the sub-modules; depth-first, to make sure the
+ // sub-modules are stopped / finalized before parents
+ _.each(this.submodules, function(mod) { mod.stop(); });
+
+ // run the finalizers
+ this._finalizerCallbacks.run(undefined, this);
+
+ // reset the initializers and finalizers
+ this._initializerCallbacks.reset();
+ this._finalizerCallbacks.reset();
+
+ this.triggerMethod('stop');
+ },
+
+ // Configure the module with a definition function and any custom args
+ // that are to be passed in to the definition function
+ addDefinition: function(moduleDefinition, customArgs) {
+ this._runModuleDefinition(moduleDefinition, customArgs);
+ },
+
+ // Internal method: run the module definition function with the correct
+ // arguments
+ _runModuleDefinition: function(definition, customArgs) {
+ // If there is no definition short circut the method.
+ if (!definition) { return; }
+
+ // build the correct list of arguments for the module definition
+ var args = _.flatten([
+ this,
+ this.app,
+ Backbone,
+ Marionette,
+ Backbone.$, _,
+ customArgs
+ ]);
+
+ definition.apply(this, args);
+ },
+
+ // Internal method: set up new copies of initializers and finalizers.
+ // Calling this method will wipe out all existing initializers and
+ // finalizers.
+ _setupInitializersAndFinalizers: function() {
+ this._initializerCallbacks = new Marionette.Callbacks();
+ this._finalizerCallbacks = new Marionette.Callbacks();
+ },
+
+ // import the `triggerMethod` to trigger events with corresponding
+ // methods if the method exists
+ triggerMethod: Marionette.triggerMethod
+ });
+
+ // Class methods to create modules
+ _.extend(Marionette.Module, {
+
+ // Create a module, hanging off the app parameter as the parent object.
+ create: function(app, moduleNames, moduleDefinition) {
+ var module = app;
+
+ // get the custom args passed in after the module definition and
+ // get rid of the module name and definition function
+ var customArgs = slice.call(arguments);
+ customArgs.splice(0, 3);
+
+ // Split the module names and get the number of submodules.
+ // i.e. an example module name of `Doge.Wow.Amaze` would
+ // then have the potential for 3 module definitions.
+ moduleNames = moduleNames.split('.');
+ var length = moduleNames.length;
+
+ // store the module definition for the last module in the chain
+ var moduleDefinitions = [];
+ moduleDefinitions[length - 1] = moduleDefinition;
+
+ // Loop through all the parts of the module definition
+ _.each(moduleNames, function(moduleName, i) {
+ var parentModule = module;
+ module = this._getModule(parentModule, moduleName, app, moduleDefinition);
+ this._addModuleDefinition(parentModule, module, moduleDefinitions[i], customArgs);
+ }, this);
+
+ // Return the last module in the definition chain
+ return module;
+ },
+
+ _getModule: function(parentModule, moduleName, app, def, args) {
+ var options = _.extend({}, def);
+ var ModuleClass = this.getClass(def);
+
+ // Get an existing module of this name if we have one
+ var module = parentModule[moduleName];
+
+ if (!module) {
+ // Create a new module if we don't have one
+ module = new ModuleClass(moduleName, app, options);
+ parentModule[moduleName] = module;
+ // store the module on the parent
+ parentModule.submodules[moduleName] = module;
+ }
+
+ return module;
+ },
+
+ // ## Module Classes
+ //
+ // Module classes can be used as an alternative to the define pattern.
+ // The extend function of a Module is identical to the extend functions
+ // on other Backbone and Marionette classes.
+ // This allows module lifecyle events like `onStart` and `onStop` to be called directly.
+ getClass: function(moduleDefinition) {
+ var ModuleClass = Marionette.Module;
+
+ if (!moduleDefinition) {
+ return ModuleClass;
+ }
+
+ // If all of the module's functionality is defined inside its class,
+ // then the class can be passed in directly. `MyApp.module("Foo", FooModule)`.
+ if (moduleDefinition.prototype instanceof ModuleClass) {
+ return moduleDefinition;
+ }
+
+ return moduleDefinition.moduleClass || ModuleClass;
+ },
+
+ // Add the module definition and add a startWithParent initializer function.
+ // This is complicated because module definitions are heavily overloaded
+ // and support an anonymous function, module class, or options object
+ _addModuleDefinition: function(parentModule, module, def, args) {
+ var fn = this._getDefine(def);
+ var startWithParent = this._getStartWithParent(def, module);
+
+ if (fn) {
+ module.addDefinition(fn, args);
+ }
+
+ this._addStartWithParent(parentModule, module, startWithParent);
+ },
+
+ _getStartWithParent: function(def, module) {
+ var swp;
+
+ if (_.isFunction(def) && (def.prototype instanceof Marionette.Module)) {
+ swp = module.constructor.prototype.startWithParent;
+ return _.isUndefined(swp) ? true : swp;
+ }
+
+ if (_.isObject(def)) {
+ swp = def.startWithParent;
+ return _.isUndefined(swp) ? true : swp;
+ }
+
+ return true;
+ },
+
+ _getDefine: function(def) {
+ if (_.isFunction(def) && !(def.prototype instanceof Marionette.Module)) {
+ return def;
+ }
+
+ if (_.isObject(def)) {
+ return def.define;
+ }
+
+ return null;
+ },
+
+ _addStartWithParent: function(parentModule, module, startWithParent) {
+ module.startWithParent = module.startWithParent && startWithParent;
+
+ if (!module.startWithParent || !!module.startWithParentIsConfigured) {
+ return;
+ }
+
+ module.startWithParentIsConfigured = true;
+
+ parentModule.addInitializer(function(options) {
+ if (module.startWithParent) {
+ module.start(options);
+ }
+ });
+ }
+ });
+
+
+ return Marionette;
+}));
vendor/assets/javascripts/backbone.marionette.map
@@ -0,0 +1,1 @@
+{"version":3,"file":"lib/backbone.marionette.min.js","sources":["?"],"names":["root","factory","define","amd","Backbone","_","Marionette","exports","require","module","this","throwError","message","name","error","Error","previousChildViewContainer","ChildViewContainer","Container","views","_views","_indexByModel","_indexByCustom","_updateLength","each","add","extend","prototype","view","customIndex","viewCid","cid","model","findByModel","findByModelCid","modelCid","findByCid","findByCustom","index","findByIndex","values","remove","any","key","call","method","apply","tail","arguments","args","isFunction","length","size","methods","concat","toArray","VERSION","noConflict","previousWreqr","Wreqr","Handlers","options","_wreqrHandlers","initialize","Model","Events","setHandlers","handlers","handler","context","isObject","callback","setHandler","config","trigger","hasHandler","getHandler","Array","slice","removeHandler","removeAllHandlers","CommandStorage","_commands","getCommands","commandName","commands","command","instances","addCommand","push","clearCommands","Commands","storageType","constructor","_initializeStorage","on","_executeCommands","execute","storage","StorageType","RequestResponse","request","EventAggregator","EA","Channel","channelName","vent","reqres","reset","off","stopListening","connectEvents","hash","_connect","connectCommands","connectRequests","type","fn","eventName","bind","radio","Radio","_channels","_proxyMethods","channel","_getChannel","system","messageSystems","proxyMethod","messageSystem","previousMarionette","Deferred","$","getOption","target","optionName","value","undefined","proxyGetOption","normalizeMethods","normalizedHash","normalizeUIKeys","ui","keys","v","pattern","match","replace","r","actAsCollection","object","listProperty","list","result","triggerMethod","getEventName","prefix","toUpperCase","splitter","event","methodName","MonitorDOMRefresh","documentElement","handleShow","_isShown","triggerDOMRefresh","handleRender","_isRendered","isInDOM","contains","el","listenTo","document","bindFromStrings","entity","evt","methodNames","split","bindToFunction","unbindFromStrings","unbindToFunction","iterateEvents","bindings","functionCallback","stringCallback","bindEntityEvents","unbindEntityEvents","proxyBindEntityEvents","proxyUnbindEntityEvents","Callbacks","_deferred","_callbacks","contextOverride","promise","cb","ctx","then","run","resolve","callbacks","Controller","destroy","Region","$el","getEl","buildRegion","regionConfig","defaultRegionClass","regionIsString","isString","regionSelectorIsString","selector","regionClassIsUndefined","isUndefined","regionClass","regionIsClass","RegionClass","region","parentEl","find","show","_ensureElement","showOptions","isDifferentView","currentView","preventDestroy","forceShow","isChangingView","_shouldDestroyView","empty","_shouldShowView","render","attachHtml","innerHTML","appendChild","isDestroyed","attachView","RegionManager","_regions","addRegions","regionDefinitions","defaults","regions","definition","addRegion","hasSelector","_store","get","getRegions","clone","removeRegion","_remove","removeRegions","emptyRegions","_setLength","TemplateCache","templateId","templateCaches","cachedTemplate","load","clear","i","compiledTemplate","template","loadTemplate","compileTemplate","html","rawTemplate","Renderer","data","templateFunc","View","bindAll","events","behaviors","Behaviors","onShowCalled","getTemplate","mixinTemplateHelpers","templateHelpers","uiBindings","configureTriggers","triggers","triggerEvents","hasOptions","e","prevent","preventDefault","stop","stopPropagation","shouldPrevent","shouldStop","collection","delegateEvents","_delegateDOMEvents","combinedEvents","behaviorEvents","undelegateEvents","_ensureViewIsIntact","err","unbindUIElements","bindUIElements","_uiBindings","ItemView","serializeData","toJSON","items","attachElContent","CollectionView","childViewEventPrefix","initOptions","sort","_initChildViewStorage","_initialEvents","initRenderBuffer","elBuffer","createDocumentFragment","_bufferedChildren","startBuffering","isBuffering","endBuffering","_triggerBeforeShowBufferedChildren","attachBuffer","_triggerShowBufferedChildren","invoke","child","_onCollectionAdd","_onCollectionRemove","_sortViews","destroyEmptyView","ChildView","getChildView","indexOf","addChild","children","removeChildView","checkEmpty","_renderChildren","orderChanged","item","_index","destroyChildren","isEmpty","showEmptyView","showCollection","EmptyView","getEmptyView","_showingEmptyView","addEmptyView","emptyViewOptions","buildChildView","renderChildView","childView","childViewOptions","_updateIndices","_addChildView","increment","laterView","proxyChildEvents","ChildViewClass","collectionView","buffer","append","_insertBefore","_insertAfter","findPosition","before","rootEvent","childEvents","splice","CompositeView","once","isRendered","resetChildViewContainer","_renderRoot","compositeView","$container","getChildViewContainer","containerView","$childViewContainer","container","childViewContainer","charAt","substr","LayoutView","_firstRender","_initializeRegions","_reInitializeRegions","regionManager","_buildRegions","getRegion","that","_initRegionManager","regionOptions","getRegionManager","Behavior","parseBehaviors","wrap","setElement","b","_behaviorsEvents","viewUI","_events","behaviorUI","whitespace","join","eventKey","behaviorsLookup","getBehaviorClass","behaviorClass","chain","map","BehaviorClass","behavior","nestedBehaviors","flatten","partial","AppRouter","Router","appRoutes","controller","_getController","processAppRoutes","_processOnRoute","appRoute","route","_addAppRoute","routeName","routeArgs","routePath","invert","onRoute","routeNames","reverse","Application","_initCallbacks","globalCh","submodules","addInitializer","initializer","start","_regionManager","moduleNames","moduleDefinition","ModuleClass","Module","getClass","unshift","create","moduleName","app","_setupInitializersAndFinalizers","startWithParent","_initializerCallbacks","addFinalizer","_finalizerCallbacks","_isInitialized","mod","addDefinition","customArgs","_runModuleDefinition","moduleDefinitions","parentModule","_getModule","_addModuleDefinition","def","moduleClass","_getDefine","_getStartWithParent","_addStartWithParent","swp","startWithParentIsConfigured"],"mappings":"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"}
\ No newline at end of file
vendor/assets/javascripts/backbone.wreqr.js
@@ -0,0 +1,440 @@
+// Backbone.Wreqr (Backbone.Marionette)
+// ----------------------------------
+// v1.3.1
+//
+// Copyright (c)2014 Derick Bailey, Muted Solutions, LLC.
+// Distributed under MIT license
+//
+// http://github.com/marionettejs/backbone.wreqr
+
+
+(function(root, factory) {
+
+ if (typeof define === 'function' && define.amd) {
+ define(['backbone', 'underscore'], function(Backbone, _) {
+ return factory(Backbone, _);
+ });
+ } else if (typeof exports !== 'undefined') {
+ var Backbone = require('backbone');
+ var _ = require('underscore');
+ module.exports = factory(Backbone, _);
+ } else {
+ factory(root.Backbone, root._);
+ }
+
+}(this, function(Backbone, _) {
+ "use strict";
+
+ var previousWreqr = Backbone.Wreqr;
+
+ var Wreqr = Backbone.Wreqr = {};
+
+ Backbone.Wreqr.VERSION = '1.3.1';
+
+ Backbone.Wreqr.noConflict = function () {
+ Backbone.Wreqr = previousWreqr;
+ return this;
+ };
+
+ // Handlers
+ // --------
+ // A registry of functions to call, given a name
+
+ Wreqr.Handlers = (function(Backbone, _){
+ "use strict";
+
+ // Constructor
+ // -----------
+
+ var Handlers = function(options){
+ this.options = options;
+ this._wreqrHandlers = {};
+
+ if (_.isFunction(this.initialize)){
+ this.initialize(options);
+ }
+ };
+
+ Handlers.extend = Backbone.Model.extend;
+
+ // Instance Members
+ // ----------------
+
+ _.extend(Handlers.prototype, Backbone.Events, {
+
+ // Add multiple handlers using an object literal configuration
+ setHandlers: function(handlers){
+ _.each(handlers, function(handler, name){
+ var context = null;
+
+ if (_.isObject(handler) && !_.isFunction(handler)){
+ context = handler.context;
+ handler = handler.callback;
+ }
+
+ this.setHandler(name, handler, context);
+ }, this);
+ },
+
+ // Add a handler for the given name, with an
+ // optional context to run the handler within
+ setHandler: function(name, handler, context){
+ var config = {
+ callback: handler,
+ context: context
+ };
+
+ this._wreqrHandlers[name] = config;
+
+ this.trigger("handler:add", name, handler, context);
+ },
+
+ // Determine whether or not a handler is registered
+ hasHandler: function(name){
+ return !! this._wreqrHandlers[name];
+ },
+
+ // Get the currently registered handler for
+ // the specified name. Throws an exception if
+ // no handler is found.
+ getHandler: function(name){
+ var config = this._wreqrHandlers[name];
+
+ if (!config){
+ return;
+ }
+
+ return function(){
+ var args = Array.prototype.slice.apply(arguments);
+ return config.callback.apply(config.context, args);
+ };
+ },
+
+ // Remove a handler for the specified name
+ removeHandler: function(name){
+ delete this._wreqrHandlers[name];
+ },
+
+ // Remove all handlers from this registry
+ removeAllHandlers: function(){
+ this._wreqrHandlers = {};
+ }
+ });
+
+ return Handlers;
+ })(Backbone, _);
+
+ // Wreqr.CommandStorage
+ // --------------------
+ //
+ // Store and retrieve commands for execution.
+ Wreqr.CommandStorage = (function(){
+ "use strict";
+
+ // Constructor function
+ var CommandStorage = function(options){
+ this.options = options;
+ this._commands = {};
+
+ if (_.isFunction(this.initialize)){
+ this.initialize(options);
+ }
+ };
+
+ // Instance methods
+ _.extend(CommandStorage.prototype, Backbone.Events, {
+
+ // Get an object literal by command name, that contains
+ // the `commandName` and the `instances` of all commands
+ // represented as an array of arguments to process
+ getCommands: function(commandName){
+ var commands = this._commands[commandName];
+
+ // we don't have it, so add it
+ if (!commands){
+
+ // build the configuration
+ commands = {
+ command: commandName,
+ instances: []
+ };
+
+ // store it
+ this._commands[commandName] = commands;
+ }
+
+ return commands;
+ },
+
+ // Add a command by name, to the storage and store the
+ // args for the command
+ addCommand: function(commandName, args){
+ var command = this.getCommands(commandName);
+ command.instances.push(args);
+ },
+
+ // Clear all commands for the given `commandName`
+ clearCommands: function(commandName){
+ var command = this.getCommands(commandName);
+ command.instances = [];
+ }
+ });
+
+ return CommandStorage;
+ })();
+
+ // Wreqr.Commands
+ // --------------
+ //
+ // A simple command pattern implementation. Register a command
+ // handler and execute it.
+ Wreqr.Commands = (function(Wreqr){
+ "use strict";
+
+ return Wreqr.Handlers.extend({
+ // default storage type
+ storageType: Wreqr.CommandStorage,
+
+ constructor: function(options){
+ this.options = options || {};
+
+ this._initializeStorage(this.options);
+ this.on("handler:add", this._executeCommands, this);
+
+ var args = Array.prototype.slice.call(arguments);
+ Wreqr.Handlers.prototype.constructor.apply(this, args);
+ },
+
+ // Execute a named command with the supplied args
+ execute: function(name, args){
+ name = arguments[0];
+ args = Array.prototype.slice.call(arguments, 1);
+
+ if (this.hasHandler(name)){
+ this.getHandler(name).apply(this, args);
+ } else {
+ this.storage.addCommand(name, args);
+ }
+
+ },
+
+ // Internal method to handle bulk execution of stored commands
+ _executeCommands: function(name, handler, context){
+ var command = this.storage.getCommands(name);
+
+ // loop through and execute all the stored command instances
+ _.each(command.instances, function(args){
+ handler.apply(context, args);
+ });
+
+ this.storage.clearCommands(name);
+ },
+
+ // Internal method to initialize storage either from the type's
+ // `storageType` or the instance `options.storageType`.
+ _initializeStorage: function(options){
+ var storage;
+
+ var StorageType = options.storageType || this.storageType;
+ if (_.isFunction(StorageType)){
+ storage = new StorageType();
+ } else {
+ storage = StorageType;
+ }
+
+ this.storage = storage;
+ }
+ });
+
+ })(Wreqr);
+
+ // Wreqr.RequestResponse
+ // ---------------------
+ //
+ // A simple request/response implementation. Register a
+ // request handler, and return a response from it
+ Wreqr.RequestResponse = (function(Wreqr){
+ "use strict";
+
+ return Wreqr.Handlers.extend({
+ request: function(){
+ var name = arguments[0];
+ var args = Array.prototype.slice.call(arguments, 1);
+ if (this.hasHandler(name)) {
+ return this.getHandler(name).apply(this, args);
+ }
+ }
+ });
+
+ })(Wreqr);
+
+ // Event Aggregator
+ // ----------------
+ // A pub-sub object that can be used to decouple various parts
+ // of an application through event-driven architecture.
+
+ Wreqr.EventAggregator = (function(Backbone, _){
+ "use strict";
+ var EA = function(){};
+
+ // Copy the `extend` function used by Backbone's classes
+ EA.extend = Backbone.Model.extend;
+
+ // Copy the basic Backbone.Events on to the event aggregator
+ _.extend(EA.prototype, Backbone.Events);
+
+ return EA;
+ })(Backbone, _);
+
+ // Wreqr.Channel
+ // --------------
+ //
+ // An object that wraps the three messaging systems:
+ // EventAggregator, RequestResponse, Commands
+ Wreqr.Channel = (function(Wreqr){
+ "use strict";
+
+ var Channel = function(channelName) {
+ this.vent = new Backbone.Wreqr.EventAggregator();
+ this.reqres = new Backbone.Wreqr.RequestResponse();
+ this.commands = new Backbone.Wreqr.Commands();
+ this.channelName = channelName;
+ };
+
+ _.extend(Channel.prototype, {
+
+ // Remove all handlers from the messaging systems of this channel
+ reset: function() {
+ this.vent.off();
+ this.vent.stopListening();
+ this.reqres.removeAllHandlers();
+ this.commands.removeAllHandlers();
+ return this;
+ },
+
+ // Connect a hash of events; one for each messaging system
+ connectEvents: function(hash, context) {
+ this._connect('vent', hash, context);
+ return this;
+ },
+
+ connectCommands: function(hash, context) {
+ this._connect('commands', hash, context);
+ return this;
+ },
+
+ connectRequests: function(hash, context) {
+ this._connect('reqres', hash, context);
+ return this;
+ },
+
+ // Attach the handlers to a given message system `type`
+ _connect: function(type, hash, context) {
+ if (!hash) {
+ return;
+ }
+
+ context = context || this;
+ var method = (type === 'vent') ? 'on' : 'setHandler';
+
+ _.each(hash, function(fn, eventName) {
+ this[type][method](eventName, _.bind(fn, context));
+ }, this);
+ }
+ });
+
+
+ return Channel;
+ })(Wreqr);
+
+ // Wreqr.Radio
+ // --------------
+ //
+ // An object that lets you communicate with many channels.
+ Wreqr.radio = (function(Wreqr){
+ "use strict";
+
+ var Radio = function() {
+ this._channels = {};
+ this.vent = {};
+ this.commands = {};
+ this.reqres = {};
+ this._proxyMethods();
+ };
+
+ _.extend(Radio.prototype, {
+
+ channel: function(channelName) {
+ if (!channelName) {
+ throw new Error('Channel must receive a name');
+ }
+
+ return this._getChannel( channelName );
+ },
+
+ _getChannel: function(channelName) {
+ var channel = this._channels[channelName];
+
+ if(!channel) {
+ channel = new Wreqr.Channel(channelName);
+ this._channels[channelName] = channel;
+ }
+
+ return channel;
+ },
+
+ _proxyMethods: function() {
+ _.each(['vent', 'commands', 'reqres'], function(system) {
+ _.each( messageSystems[system], function(method) {
+ this[system][method] = proxyMethod(this, system, method);
+ }, this);
+ }, this);
+ }
+ });
+
+
+ var messageSystems = {
+ vent: [
+ 'on',
+ 'off',
+ 'trigger',
+ 'once',
+ 'stopListening',
+ 'listenTo',
+ 'listenToOnce'
+ ],
+
+ commands: [
+ 'execute',
+ 'setHandler',
+ 'setHandlers',
+ 'removeHandler',
+ 'removeAllHandlers'
+ ],
+
+ reqres: [
+ 'request',
+ 'setHandler',
+ 'setHandlers',
+ 'removeHandler',
+ 'removeAllHandlers'
+ ]
+ };
+
+ var proxyMethod = function(radio, system, method) {
+ return function(channelName) {
+ var messageSystem = radio._getChannel(channelName)[system];
+ var args = Array.prototype.slice.call(arguments, 1);
+
+ return messageSystem[method].apply(messageSystem, args);
+ };
+ };
+
+ return new Radio();
+
+ })(Wreqr);
+
+
+ return Backbone.Wreqr;
+
+}));
vendor/assets/javascripts/json2.js
@@ -0,0 +1,489 @@
+/*
+ json2.js
+ 2014-02-04
+
+ Public Domain.
+
+ NO WARRANTY EXPRESSED OR IMPLIED. USE AT YOUR OWN RISK.
+
+ See http://www.JSON.org/js.html
+
+
+ This code should be minified before deployment.
+ See http://javascript.crockford.com/jsmin.html
+
+ USE YOUR OWN COPY. IT IS EXTREMELY UNWISE TO LOAD CODE FROM SERVERS YOU DO
+ NOT CONTROL.
+
+
+ This file creates a global JSON object containing two methods: stringify
+ and parse.
+
+ JSON.stringify(value, replacer, space)
+ value any JavaScript value, usually an object or array.
+
+ replacer an optional parameter that determines how object
+ values are stringified for objects. It can be a
+ function or an array of strings.
+
+ space an optional parameter that specifies the indentation
+ of nested structures. If it is omitted, the text will
+ be packed without extra whitespace. If it is a number,
+ it will specify the number of spaces to indent at each
+ level. If it is a string (such as '\t' or ' '),
+ it contains the characters used to indent at each level.
+
+ This method produces a JSON text from a JavaScript value.
+
+ When an object value is found, if the object contains a toJSON
+ method, its toJSON method will be called and the result will be
+ stringified. A toJSON method does not serialize: it returns the
+ value represented by the name/value pair that should be serialized,
+ or undefined if nothing should be serialized. The toJSON method
+ will be passed the key associated with the value, and this will be
+ bound to the value
+
+ For example, this would serialize Dates as ISO strings.
+
+ Date.prototype.toJSON = function (key) {
+ function f(n) {
+ // Format integers to have at least two digits.
+ return n < 10 ? '0' + n : n;
+ }
+
+ return this.getUTCFullYear() + '-' +
+ f(this.getUTCMonth() + 1) + '-' +
+ f(this.getUTCDate()) + 'T' +
+ f(this.getUTCHours()) + ':' +
+ f(this.getUTCMinutes()) + ':' +
+ f(this.getUTCSeconds()) + 'Z';
+ };
+
+ You can provide an optional replacer method. It will be passed the
+ key and value of each member, with this bound to the containing
+ object. The value that is returned from your method will be
+ serialized. If your method returns undefined, then the member will
+ be excluded from the serialization.
+
+ If the replacer parameter is an array of strings, then it will be
+ used to select the members to be serialized. It filters the results
+ such that only members with keys listed in the replacer array are
+ stringified.
+
+ Values that do not have JSON representations, such as undefined or
+ functions, will not be serialized. Such values in objects will be
+ dropped; in arrays they will be replaced with null. You can use
+ a replacer function to replace those with JSON values.
+ JSON.stringify(undefined) returns undefined.
+
+ The optional space parameter produces a stringification of the
+ value that is filled with line breaks and indentation to make it
+ easier to read.
+
+ If the space parameter is a non-empty string, then that string will
+ be used for indentation. If the space parameter is a number, then
+ the indentation will be that many spaces.
+
+ Example:
+
+ text = JSON.stringify(['e', {pluribus: 'unum'}]);
+ // text is '["e",{"pluribus":"unum"}]'
+
+
+ text = JSON.stringify(['e', {pluribus: 'unum'}], null, '\t');
+ // text is '[\n\t"e",\n\t{\n\t\t"pluribus": "unum"\n\t}\n]'
+
+ text = JSON.stringify([new Date()], function (key, value) {
+ return this[key] instanceof Date ?
+ 'Date(' + this[key] + ')' : value;
+ });
+ // text is '["Date(---current time---)"]'
+
+
+ JSON.parse(text, reviver)
+ This method parses a JSON text to produce an object or array.
+ It can throw a SyntaxError exception.
+
+ The optional reviver parameter is a function that can filter and
+ transform the results. It receives each of the keys and values,
+ and its return value is used instead of the original value.
+ If it returns what it received, then the structure is not modified.
+ If it returns undefined then the member is deleted.
+
+ Example:
+
+ // Parse the text. Values that look like ISO date strings will
+ // be converted to Date objects.
+
+ myData = JSON.parse(text, function (key, value) {
+ var a;
+ if (typeof value === 'string') {
+ a =
+/^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2}(?:\.\d*)?)Z$/.exec(value);
+ if (a) {
+ return new Date(Date.UTC(+a[1], +a[2] - 1, +a[3], +a[4],
+ +a[5], +a[6]));
+ }
+ }
+ return value;
+ });
+
+ myData = JSON.parse('["Date(09/09/2001)"]', function (key, value) {
+ var d;
+ if (typeof value === 'string' &&
+ value.slice(0, 5) === 'Date(' &&
+ value.slice(-1) === ')') {
+ d = new Date(value.slice(5, -1));
+ if (d) {
+ return d;
+ }
+ }
+ return value;
+ });
+
+
+ This is a reference implementation. You are free to copy, modify, or
+ redistribute.
+*/
+
+/*jslint evil: true, regexp: true */
+
+/*members "", "\b", "\t", "\n", "\f", "\r", "\"", JSON, "\\", apply,
+ call, charCodeAt, getUTCDate, getUTCFullYear, getUTCHours,
+ getUTCMinutes, getUTCMonth, getUTCSeconds, hasOwnProperty, join,
+ lastIndex, length, parse, prototype, push, replace, slice, stringify,
+ test, toJSON, toString, valueOf
+*/
+
+
+// Create a JSON object only if one does not already exist. We create the
+// methods in a closure to avoid creating global variables.
+
+if (typeof JSON !== 'object') {
+ JSON = {};
+}
+
+(function () {
+ 'use strict';
+
+ function f(n) {
+ // Format integers to have at least two digits.
+ return n < 10 ? '0' + n : n;
+ }
+
+ if (typeof Date.prototype.toJSON !== 'function') {
+
+ Date.prototype.toJSON = function () {
+
+ return isFinite(this.valueOf())
+ ? this.getUTCFullYear() + '-' +
+ f(this.getUTCMonth() + 1) + '-' +
+ f(this.getUTCDate()) + 'T' +
+ f(this.getUTCHours()) + ':' +
+ f(this.getUTCMinutes()) + ':' +
+ f(this.getUTCSeconds()) + 'Z'
+ : null;
+ };
+
+ String.prototype.toJSON =
+ Number.prototype.toJSON =
+ Boolean.prototype.toJSON = function () {
+ return this.valueOf();
+ };
+ }
+
+ var cx,
+ escapable,
+ gap,
+ indent,
+ meta,
+ rep;
+
+
+ function quote(string) {
+
+// If the string contains no control characters, no quote characters, and no
+// backslash characters, then we can safely slap some quotes around it.
+// Otherwise we must also replace the offending characters with safe escape
+// sequences.
+
+ escapable.lastIndex = 0;
+ return escapable.test(string) ? '"' + string.replace(escapable, function (a) {
+ var c = meta[a];
+ return typeof c === 'string'
+ ? c
+ : '\\u' + ('0000' + a.charCodeAt(0).toString(16)).slice(-4);
+ }) + '"' : '"' + string + '"';
+ }
+
+
+ function str(key, holder) {
+
+// Produce a string from holder[key].
+
+ var i, // The loop counter.
+ k, // The member key.
+ v, // The member value.
+ length,
+ mind = gap,
+ partial,
+ value = holder[key];
+
+// If the value has a toJSON method, call it to obtain a replacement value.
+
+ if (value && typeof value === 'object' &&
+ typeof value.toJSON === 'function') {
+ value = value.toJSON(key);
+ }
+
+// If we were called with a replacer function, then call the replacer to
+// obtain a replacement value.
+
+ if (typeof rep === 'function') {
+ value = rep.call(holder, key, value);
+ }
+
+// What happens next depends on the value's type.
+
+ switch (typeof value) {
+ case 'string':
+ return quote(value);
+
+ case 'number':
+
+// JSON numbers must be finite. Encode non-finite numbers as null.
+
+ return isFinite(value) ? String(value) : 'null';
+
+ case 'boolean':
+ case 'null':
+
+// If the value is a boolean or null, convert it to a string. Note:
+// typeof null does not produce 'null'. The case is included here in
+// the remote chance that this gets fixed someday.
+
+ return String(value);
+
+// If the type is 'object', we might be dealing with an object or an array or
+// null.
+
+ case 'object':
+
+// Due to a specification blunder in ECMAScript, typeof null is 'object',
+// so watch out for that case.
+
+ if (!value) {
+ return 'null';
+ }
+
+// Make an array to hold the partial results of stringifying this object value.
+
+ gap += indent;
+ partial = [];
+
+// Is the value an array?
+
+ if (Object.prototype.toString.apply(value) === '[object Array]') {
+
+// The value is an array. Stringify every element. Use null as a placeholder
+// for non-JSON values.
+
+ length = value.length;
+ for (i = 0; i < length; i += 1) {
+ partial[i] = str(i, value) || 'null';
+ }
+
+// Join all of the elements together, separated with commas, and wrap them in
+// brackets.
+
+ v = partial.length === 0
+ ? '[]'
+ : gap
+ ? '[\n' + gap + partial.join(',\n' + gap) + '\n' + mind + ']'
+ : '[' + partial.join(',') + ']';
+ gap = mind;
+ return v;
+ }
+
+// If the replacer is an array, use it to select the members to be stringified.
+
+ if (rep && typeof rep === 'object') {
+ length = rep.length;
+ for (i = 0; i < length; i += 1) {
+ if (typeof rep[i] === 'string') {
+ k = rep[i];
+ v = str(k, value);
+ if (v) {
+ partial.push(quote(k) + (gap ? ': ' : ':') + v);
+ }
+ }
+ }
+ } else {
+
+// Otherwise, iterate through all of the keys in the object.
+
+ for (k in value) {
+ if (Object.prototype.hasOwnProperty.call(value, k)) {
+ v = str(k, value);
+ if (v) {
+ partial.push(quote(k) + (gap ? ': ' : ':') + v);
+ }
+ }
+ }
+ }
+
+// Join all of the member texts together, separated with commas,
+// and wrap them in braces.
+
+ v = partial.length === 0
+ ? '{}'
+ : gap
+ ? '{\n' + gap + partial.join(',\n' + gap) + '\n' + mind + '}'
+ : '{' + partial.join(',') + '}';
+ gap = mind;
+ return v;
+ }
+ }
+
+// If the JSON object does not yet have a stringify method, give it one.
+
+ if (typeof JSON.stringify !== 'function') {
+ escapable = /[\\\"\x00-\x1f\x7f-\x9f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g;
+ meta = { // table of character substitutions
+ '\b': '\\b',
+ '\t': '\\t',
+ '\n': '\\n',
+ '\f': '\\f',
+ '\r': '\\r',
+ '"' : '\\"',
+ '\\': '\\\\'
+ };
+ JSON.stringify = function (value, replacer, space) {
+
+// The stringify method takes a value and an optional replacer, and an optional
+// space parameter, and returns a JSON text. The replacer can be a function
+// that can replace values, or an array of strings that will select the keys.
+// A default replacer method can be provided. Use of the space parameter can
+// produce text that is more easily readable.
+
+ var i;
+ gap = '';
+ indent = '';
+
+// If the space parameter is a number, make an indent string containing that
+// many spaces.
+
+ if (typeof space === 'number') {
+ for (i = 0; i < space; i += 1) {
+ indent += ' ';
+ }
+
+// If the space parameter is a string, it will be used as the indent string.
+
+ } else if (typeof space === 'string') {
+ indent = space;
+ }
+
+// If there is a replacer, it must be a function or an array.
+// Otherwise, throw an error.
+
+ rep = replacer;
+ if (replacer && typeof replacer !== 'function' &&
+ (typeof replacer !== 'object' ||
+ typeof replacer.length !== 'number')) {
+ throw new Error('JSON.stringify');
+ }
+
+// Make a fake root object containing our value under the key of ''.
+// Return the result of stringifying the value.
+
+ return str('', {'': value});
+ };
+ }
+
+
+// If the JSON object does not yet have a parse method, give it one.
+
+ if (typeof JSON.parse !== 'function') {
+ cx = /[\u0000\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g;
+ JSON.parse = function (text, reviver) {
+
+// The parse method takes a text and an optional reviver function, and returns
+// a JavaScript value if the text is a valid JSON text.
+
+ var j;
+
+ function walk(holder, key) {
+
+// The walk method is used to recursively walk the resulting structure so
+// that modifications can be made.
+
+ var k, v, value = holder[key];
+ if (value && typeof value === 'object') {
+ for (k in value) {
+ if (Object.prototype.hasOwnProperty.call(value, k)) {
+ v = walk(value, k);
+ if (v !== undefined) {
+ value[k] = v;
+ } else {
+ delete value[k];
+ }
+ }
+ }
+ }
+ return reviver.call(holder, key, value);
+ }
+
+
+// Parsing happens in four stages. In the first stage, we replace certain
+// Unicode characters with escape sequences. JavaScript handles many characters
+// incorrectly, either silently deleting them, or treating them as line endings.
+
+ text = String(text);
+ cx.lastIndex = 0;
+ if (cx.test(text)) {
+ text = text.replace(cx, function (a) {
+ return '\\u' +
+ ('0000' + a.charCodeAt(0).toString(16)).slice(-4);
+ });
+ }
+
+// In the second stage, we run the text against regular expressions that look
+// for non-JSON patterns. We are especially concerned with '()' and 'new'
+// because they can cause invocation, and '=' because it can cause mutation.
+// But just to be safe, we want to reject all unexpected forms.
+
+// We split the second stage into 4 regexp operations in order to work around
+// crippling inefficiencies in IE's and Safari's regexp engines. First we
+// replace the JSON backslash pairs with '@' (a non-JSON character). Second, we
+// replace all simple value tokens with ']' characters. Third, we delete all
+// open brackets that follow a colon or comma or that begin the text. Finally,
+// we look to see that the remaining characters are only whitespace or ']' or
+// ',' or ':' or '{' or '}'. If that is so, then the text is safe for eval.
+
+ if (/^[\],:{}\s]*$/
+ .test(text.replace(/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g, '@')
+ .replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g, ']')
+ .replace(/(?:^|:|,)(?:\s*\[)+/g, ''))) {
+
+// In the third stage we use the eval function to compile the text into a
+// JavaScript structure. The '{' operator is subject to a syntactic ambiguity
+// in JavaScript: it can begin a block or an object literal. We wrap the text
+// in parens to eliminate the ambiguity.
+
+ j = eval('(' + text + ')');
+
+// In the optional fourth stage, we recursively walk the new structure, passing
+// each name/value pair to a reviver function for possible transformation.
+
+ return typeof reviver === 'function'
+ ? walk({'': j}, '')
+ : j;
+ }
+
+// If the text is not JSON parseable, then a SyntaxError is thrown.
+
+ throw new SyntaxError('JSON.parse');
+ };
+ }
+}());
vendor/assets/javascripts/underscore.js
@@ -31,6 +31,15 @@
// All **ECMAScript 5** native function implementations that we hope to use
// are declared here.
var
+ nativeForEach = ArrayProto.forEach,
+ nativeMap = ArrayProto.map,
+ nativeReduce = ArrayProto.reduce,
+ nativeReduceRight = ArrayProto.reduceRight,
+ nativeFilter = ArrayProto.filter,
+ nativeEvery = ArrayProto.every,
+ nativeSome = ArrayProto.some,
+ nativeIndexOf = ArrayProto.indexOf,
+ nativeLastIndexOf = ArrayProto.lastIndexOf,
nativeIsArray = Array.isArray,
nativeKeys = Object.keys,
nativeBind = FuncProto.bind;
@@ -58,66 +67,37 @@
// Current version.
_.VERSION = '1.6.0';
- // Internal function: creates a callback bound to its context if supplied
- 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);
- };
- };
-
- // An internal function to generate lookup iterators.
- var lookupIterator = 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, iterator, context) {
- var i, length;
+ // Handles objects with the built-in `forEach`, arrays, and raw objects.
+ // Delegates to **ECMAScript 5**'s native `forEach` if available.
+ var each = _.each = _.forEach = function(obj, iterator, context) {
if (obj == null) return obj;
- iterator = createCallback(iterator, context);
- if (obj.length === +obj.length) {
- for (i = 0, length = obj.length; i < length; i++) {
- if (iterator(obj[i], i, obj) === breaker) break;
+ if (nativeForEach && obj.forEach === nativeForEach) {
+ obj.forEach(iterator, context);
+ } else if (obj.length === +obj.length) {
+ for (var i = 0, length = obj.length; i < length; i++) {
+ if (iterator.call(context, obj[i], i, obj) === breaker) return;
}
} else {
var keys = _.keys(obj);
- for (i = 0, length = keys.length; i < length; i++) {
- if (iterator(obj[keys[i]], keys[i], obj) === breaker) break;
+ for (var i = 0, length = keys.length; i < length; i++) {
+ if (iterator.call(context, obj[keys[i]], keys[i], obj) === breaker) return;
}
}
return obj;
};
// Return the results of applying the iterator to each element.
+ // Delegates to **ECMAScript 5**'s native `map` if available.
_.map = _.collect = function(obj, iterator, context) {
var results = [];
if (obj == null) return results;
- iterator = lookupIterator(iterator, context);
- _.each(obj, function(value, index, list) {
- results.push(iterator(value, index, list));
+ if (nativeMap && obj.map === nativeMap) return obj.map(iterator, context);
+ each(obj, function(value, index, list) {
+ results.push(iterator.call(context, value, index, list));
});
return results;
};
@@ -125,52 +105,58 @@
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`.
+ // or `foldl`. Delegates to **ECMAScript 5**'s native `reduce` if available.
_.reduce = _.foldl = _.inject = function(obj, iterator, memo, context) {
var initial = arguments.length > 2;
if (obj == null) obj = [];
- iterator = createCallback(iterator, context, 4);
- _.each(obj, function(value, index, list) {
+ if (nativeReduce && obj.reduce === nativeReduce) {
+ if (context) iterator = _.bind(iterator, context);
+ return initial ? obj.reduce(iterator, memo) : obj.reduce(iterator);
+ }
+ each(obj, function(value, index, list) {
if (!initial) {
memo = value;
initial = true;
} else {
- memo = iterator(memo, value, index, list);
+ memo = iterator.call(context, memo, value, index, list);
}
});
- if (!initial) throw TypeError(reduceError);
+ if (!initial) throw new TypeError(reduceError);
return memo;
};
// The right-associative version of reduce, also known as `foldr`.
+ // Delegates to **ECMAScript 5**'s native `reduceRight` if available.
_.reduceRight = _.foldr = function(obj, iterator, memo, context) {
var initial = arguments.length > 2;
if (obj == null) obj = [];
+ if (nativeReduceRight && obj.reduceRight === nativeReduceRight) {
+ if (context) iterator = _.bind(iterator, context);
+ return initial ? obj.reduceRight(iterator, memo) : obj.reduceRight(iterator);
+ }
var length = obj.length;
- iterator = createCallback(iterator, context, 4);
if (length !== +length) {
var keys = _.keys(obj);
length = keys.length;
}
- _.each(obj, function(value, index, list) {
+ each(obj, function(value, index, list) {
index = keys ? keys[--length] : --length;
if (!initial) {
memo = obj[index];
initial = true;
} else {
- memo = iterator(memo, obj[index], index, list);
+ memo = iterator.call(context, memo, obj[index], index, list);
}
});
- if (!initial) throw TypeError(reduceError);
+ if (!initial) throw new TypeError(reduceError);
return memo;
};
// Return the first value which passes a truth test. Aliased as `detect`.
_.find = _.detect = function(obj, predicate, context) {
var result;
- predicate = lookupIterator(predicate, context);
- _.some(obj, function(value, index, list) {
- if (predicate(value, index, list)) {
+ any(obj, function(value, index, list) {
+ if (predicate.call(context, value, index, list)) {
result = value;
return true;
}
@@ -179,44 +165,49 @@
};
// Return all the elements that pass a truth test.
+ // Delegates to **ECMAScript 5**'s native `filter` if available.
// Aliased as `select`.
_.filter = _.select = function(obj, predicate, context) {
var results = [];
if (obj == null) return results;
- predicate = lookupIterator(predicate, context);
- _.each(obj, function(value, index, list) {
- if (predicate(value, index, list)) results.push(value);
+ if (nativeFilter && obj.filter === nativeFilter) return obj.filter(predicate, context);
+ each(obj, function(value, index, list) {
+ if (predicate.call(context, 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(lookupIterator(predicate)), context);
+ return _.filter(obj, function(value, index, list) {
+ return !predicate.call(context, value, index, list);
+ }, context);
};
// Determine whether all of the elements match a truth test.
+ // Delegates to **ECMAScript 5**'s native `every` if available.
// Aliased as `all`.
_.every = _.all = function(obj, predicate, context) {
+ predicate || (predicate = _.identity);
var result = true;
if (obj == null) return result;
- predicate = lookupIterator(predicate, context);
- _.each(obj, function(value, index, list) {
- result = predicate(value, index, list);
- if (!result) return breaker;
+ if (nativeEvery && obj.every === nativeEvery) return obj.every(predicate, context);
+ each(obj, function(value, index, list) {
+ if (!(result = result && predicate.call(context, value, index, list))) return breaker;
});
return !!result;
};
// Determine if at least one element in the object matches a truth test.
+ // Delegates to **ECMAScript 5**'s native `some` if available.
// Aliased as `any`.
- _.some = _.any = function(obj, predicate, context) {
+ var any = _.some = _.any = function(obj, predicate, context) {
+ predicate || (predicate = _.identity);
var result = false;
if (obj == null) return result;
- predicate = lookupIterator(predicate, context);
- _.each(obj, function(value, index, list) {
- result = predicate(value, index, list);
- if (result) return breaker;
+ if (nativeSome && obj.some === nativeSome) return obj.some(predicate, context);
+ each(obj, function(value, index, list) {
+ if (result || (result = predicate.call(context, value, index, list))) return breaker;
});
return !!result;
};
@@ -225,8 +216,10 @@
// 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;
+ if (nativeIndexOf && obj.indexOf === nativeIndexOf) return obj.indexOf(target) != -1;
+ return any(obj, function(value) {
+ return value === target;
+ });
};
// Invoke a method (with arguments) on every item in a collection.
@@ -255,51 +248,37 @@
return _.find(obj, _.matches(attrs));
};
- // Return the maximum element (or element-based computation).
+ // Return the maximum element or (element-based computation).
+ // Can't optimize arrays of integers longer than 65,535 elements.
+ // See [WebKit Bug 80797](https://bugs.webkit.org/show_bug.cgi?id=80797)
_.max = function(obj, iterator, context) {
- var result = -Infinity, lastComputed = -Infinity,
- value, computed;
- if (!iterator && _.isArray(obj)) {
- for (var i = 0, length = obj.length; i < length; i++) {
- value = obj[i];
- if (value > result) {
- result = value;
- }
- }
- } else {
- iterator = lookupIterator(iterator, context);
- _.each(obj, function(value, index, list) {
- computed = iterator(value, index, list);
- if (computed > lastComputed || computed === -Infinity && result === -Infinity) {
- result = value;
- lastComputed = computed;
- }
- });
+ if (!iterator && _.isArray(obj) && obj[0] === +obj[0] && obj.length < 65535) {
+ return Math.max.apply(Math, obj);
}
+ var result = -Infinity, lastComputed = -Infinity;
+ each(obj, function(value, index, list) {
+ var computed = iterator ? iterator.call(context, value, index, list) : value;
+ if (computed > lastComputed) {
+ result = value;
+ lastComputed = computed;
+ }
+ });
return result;
};
// Return the minimum element (or element-based computation).
_.min = function(obj, iterator, context) {
- var result = Infinity, lastComputed = Infinity,
- value, computed;
- if (!iterator && _.isArray(obj)) {
- for (var i = 0, length = obj.length; i < length; i++) {
- value = obj[i];
- if (value < result) {
- result = value;
- }
- }
- } else {
- iterator = lookupIterator(iterator, context);
- _.each(obj, function(value, index, list) {
- computed = iterator(value, index, list);
- if (computed < lastComputed || computed === Infinity && result === Infinity) {
- result = value;
- lastComputed = computed;
- }
- });
+ if (!iterator && _.isArray(obj) && obj[0] === +obj[0] && obj.length < 65535) {
+ return Math.min.apply(Math, obj);
}
+ var result = Infinity, lastComputed = Infinity;
+ each(obj, function(value, index, list) {
+ var computed = iterator ? iterator.call(context, value, index, list) : value;
+ if (computed < lastComputed) {
+ result = value;
+ lastComputed = computed;
+ }
+ });
return result;
};
@@ -309,7 +288,7 @@
var rand;
var index = 0;
var shuffled = [];
- _.each(obj, function(value) {
+ each(obj, function(value) {
rand = _.random(index++);
shuffled[index - 1] = shuffled[rand];
shuffled[rand] = value;
@@ -328,14 +307,21 @@
return _.shuffle(obj).slice(0, Math.max(0, n));
};
+ // An internal function to generate lookup iterators.
+ var lookupIterator = function(value) {
+ if (value == null) return _.identity;
+ if (_.isFunction(value)) return value;
+ return _.property(value);
+ };
+
// Sort the object's values by a criterion produced by an iterator.
_.sortBy = function(obj, iterator, context) {
- iterator = lookupIterator(iterator, context);
+ iterator = lookupIterator(iterator);
return _.pluck(_.map(obj, function(value, index, list) {
return {
value: value,
index: index,
- criteria: iterator(value, index, list)
+ criteria: iterator.call(context, value, index, list)
};
}).sort(function(left, right) {
var a = left.criteria;
@@ -352,10 +338,10 @@
var group = function(behavior) {
return function(obj, iterator, context) {
var result = {};
- iterator = lookupIterator(iterator, context);
- _.each(obj, function(value, index) {
- var key = iterator(value, index, obj);
- behavior(result, value, key);
+ iterator = lookupIterator(iterator);
+ each(obj, function(value, index) {
+ var key = iterator.call(context, value, index, obj);
+ behavior(result, key, value);
});
return result;
};
@@ -363,32 +349,32 @@
// 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];
+ _.groupBy = group(function(result, key, value) {
+ _.has(result, key) ? result[key].push(value) : 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) {
+ _.indexBy = group(function(result, key, value) {
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;
+ _.countBy = group(function(result, key) {
+ _.has(result, key) ? result[key]++ : 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, iterator, context) {
- iterator = lookupIterator(iterator, context, 1);
- var value = iterator(obj);
+ iterator = lookupIterator(iterator);
+ var value = iterator.call(context, obj);
var low = 0, high = array.length;
while (low < high) {
var mid = (low + high) >>> 1;
- if (iterator(array[mid]) < value) low = mid + 1; else high = mid;
+ iterator.call(context, array[mid]) < value ? low = mid + 1 : high = mid;
}
return low;
};
@@ -404,18 +390,7 @@
// 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 = lookupIterator(predicate, context);
- var pass = [], fail = [];
- _.each(obj, function(value, key, obj) {
- (predicate(value, key, obj) ? pass : fail).push(value);
- });
- return [pass, fail];
+ return (obj.length === +obj.length) ? obj.length : _.keys(obj).length;
};
// Array Functions
@@ -426,7 +401,7 @@
// 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 == null) || guard) return array[0];
if (n < 0) return [];
return slice.call(array, 0, n);
};
@@ -436,14 +411,14 @@
// 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)));
+ return slice.call(array, 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];
+ if ((n == null) || guard) return array[array.length - 1];
return slice.call(array, Math.max(array.length - n, 0));
};
@@ -452,7 +427,7 @@
// 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);
+ return slice.call(array, (n == null) || guard ? 1 : n);
};
// Trim out all falsy values from an array.
@@ -461,26 +436,23 @@
};
// Internal implementation of a recursive `flatten` function.
- var flatten = function(input, shallow, strict, output) {
+ var flatten = function(input, shallow, 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);
+ each(input, function(value) {
+ if (_.isArray(value) || _.isArguments(value)) {
+ shallow ? push.apply(output, value) : flatten(value, shallow, output);
} else {
- flatten(value, shallow, strict, output);
+ output.push(value);
}
- }
+ });
return output;
};
// Flatten out an array, either recursively (by default), or just one level.
_.flatten = function(array, shallow) {
- return flatten(array, shallow, false, []);
+ return flatten(array, shallow, []);
};
// Return a version of the array that does not contain the specified value(s).
@@ -488,71 +460,68 @@
return _.difference(array, slice.call(arguments, 1));
};
+ // Split an array into two arrays: one whose elements all satisfy the given
+ // predicate, and one whose elements all do not satisfy the predicate.
+ _.partition = function(array, predicate) {
+ var pass = [], fail = [];
+ each(array, function(elem) {
+ (predicate(elem) ? pass : fail).push(elem);
+ });
+ return [pass, fail];
+ };
+
// 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, iterator, context) {
- if (array == null) return [];
if (_.isFunction(isSorted)) {
context = iterator;
iterator = isSorted;
isSorted = false;
}
- if (iterator) iterator = lookupIterator(iterator, context);
- var result = [];
+ var initial = iterator ? _.map(array, iterator, context) : array;
+ var results = [];
var seen = [];
- for (var i = 0, length = array.length; i < length; i++) {
- var value = array[i];
- if (iterator) value = iterator(value, i, array);
- if (isSorted ? !i || seen !== value : !_.contains(seen, value)) {
- if (isSorted) seen = value;
- else seen.push(value);
- result.push(array[i]);
+ each(initial, function(value, index) {
+ if (isSorted ? (!index || seen[seen.length - 1] !== value) : !_.contains(seen, value)) {
+ seen.push(value);
+ results.push(array[index]);
}
- }
- return result;
+ });
+ return results;
};
// 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, []));
+ return _.uniq(_.flatten(arguments, 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;
+ var rest = slice.call(arguments, 1);
+ return _.filter(_.uniq(array), function(item) {
+ return _.every(rest, function(other) {
+ return _.contains(other, item);
+ });
+ });
};
// 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);
- });
+ var rest = concat.apply(ArrayProto, slice.call(arguments, 1));
+ 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);
+ _.zip = function() {
+ var length = _.max(_.pluck(arguments, 'length').concat(0));
+ var results = new Array(length);
for (var i = 0; i < length; i++) {
- results[i] = _.pluck(arguments, i);
+ results[i] = _.pluck(arguments, '' + i);
}
return results;
};
@@ -573,8 +542,10 @@
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 browser doesn't supply us with indexOf (I'm looking at you, **MSIE**),
+ // we need this function. Return the position of the first occurrence of an
+ // item in an array, or -1 if the item is not included in the array.
+ // Delegates to **ECMAScript 5**'s native `indexOf` if available.
// If the array is large and already in sort order, pass `true`
// for **isSorted** to use binary search.
_.indexOf = function(array, item, isSorted) {
@@ -582,23 +553,26 @@
var i = 0, length = array.length;
if (isSorted) {
if (typeof isSorted == 'number') {
- i = isSorted < 0 ? Math.max(0, length + isSorted) : isSorted;
+ i = (isSorted < 0 ? Math.max(0, length + isSorted) : isSorted);
} else {
i = _.sortedIndex(array, item);
return array[i] === item ? i : -1;
}
}
+ if (nativeIndexOf && array.indexOf === nativeIndexOf) return array.indexOf(item, isSorted);
for (; i < length; i++) if (array[i] === item) return i;
return -1;
};
+ // Delegates to **ECMAScript 5**'s native `lastIndexOf` if available.
_.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);
+ var hasIndex = from != null;
+ if (nativeLastIndexOf && array.lastIndexOf === nativeLastIndexOf) {
+ return hasIndex ? array.lastIndexOf(item, from) : array.lastIndexOf(item);
}
- while (--idx >= 0) if (array[idx] === item) return idx;
+ var i = (hasIndex ? from : array.length);
+ while (i--) if (array[i] === item) return i;
return -1;
};
@@ -614,9 +588,9 @@
var length = Math.max(Math.ceil((stop - start) / step), 0);
var idx = 0;
- var range = Array(length);
+ var range = new Array(length);
- while (idx < length) {
+ while(idx < length) {
range[idx++] = start;
start += step;
}
@@ -628,7 +602,7 @@
// ------------------
// Reusable constructor function for prototype setting.
- var Ctor = function(){};
+ 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
@@ -636,18 +610,17 @@
_.bind = function(func, context) {
var args, bound;
if (nativeBind && func.bind === nativeBind) return nativeBind.apply(func, slice.call(arguments, 1));
- if (!_.isFunction(func)) throw TypeError('Bind must be called on a function');
+ if (!_.isFunction(func)) throw new TypeError;
args = slice.call(arguments, 2);
- bound = function() {
+ return 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;
+ ctor.prototype = func.prototype;
+ var self = new ctor;
+ ctor.prototype = null;
var result = func.apply(self, args.concat(slice.call(arguments)));
if (Object(result) === result) return result;
return self;
};
- return bound;
};
// Partially apply a function by creating a version that has had some of its
@@ -670,34 +643,27 @@
// 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 = 1, length = arguments.length, key;
- if (length <= 1) throw Error('bindAll must be passed function names');
- for (; i < length; i++) {
- key = arguments[i];
- obj[key] = _.bind(obj[key], obj);
- }
+ var funcs = slice.call(arguments, 1);
+ if (funcs.length === 0) throw new Error('bindAll must be passed function names');
+ each(funcs, function(f) { obj[f] = _.bind(obj[f], 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[key];
+ var memo = {};
+ hasher || (hasher = _.identity);
+ return function() {
+ var key = hasher.apply(this, arguments);
+ return _.has(memo, key) ? memo[key] : (memo[key] = func.apply(this, arguments));
};
- 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);
+ return setTimeout(function(){ return func.apply(null, args); }, wait);
};
// Defers a function, scheduling it to run after the current call stack has
@@ -715,12 +681,12 @@
var context, args, result;
var timeout = null;
var previous = 0;
- if (!options) options = {};
+ options || (options = {});
var later = function() {
previous = options.leading === false ? 0 : _.now();
timeout = null;
result = func.apply(context, args);
- if (!timeout) context = args = null;
+ context = args = null;
};
return function() {
var now = _.now();
@@ -728,12 +694,12 @@
var remaining = wait - (now - previous);
context = this;
args = arguments;
- if (remaining <= 0 || remaining > wait) {
+ if (remaining <= 0) {
clearTimeout(timeout);
timeout = null;
previous = now;
result = func.apply(context, args);
- if (!timeout) context = args = null;
+ context = args = null;
} else if (!timeout && options.trailing !== false) {
timeout = setTimeout(later, remaining);
}
@@ -750,14 +716,13 @@
var later = function() {
var last = _.now() - timestamp;
-
- if (last < wait && last > 0) {
+ if (last < wait) {
timeout = setTimeout(later, wait - last);
} else {
timeout = null;
if (!immediate) {
result = func.apply(context, args);
- if (!timeout) context = args = null;
+ context = args = null;
}
}
};
@@ -767,7 +732,9 @@
args = arguments;
timestamp = _.now();
var callNow = immediate && !timeout;
- if (!timeout) timeout = setTimeout(later, wait);
+ if (!timeout) {
+ timeout = setTimeout(later, wait);
+ }
if (callNow) {
result = func.apply(context, args);
context = args = null;
@@ -797,23 +764,16 @@
return _.partial(wrapper, func);
};
- // Returns a negated version of the passed-in predicate.
- _.negate = function(predicate) {
- return function() {
- return !predicate.apply(this, arguments);
- };
- };
-
// Returns a function that is the composition of a list of functions, each
// consuming the return value of the function that follows.
_.compose = function() {
- var args = arguments;
- var start = args.length - 1;
+ var funcs = arguments;
return function() {
- var i = start;
- var result = args[start].apply(this, arguments);
- while (i--) result = args[i].call(this, result);
- return result;
+ var args = arguments;
+ for (var i = funcs.length - 1; i >= 0; i--) {
+ args = [funcs[i].apply(this, args)];
+ }
+ return args[0];
};
};
@@ -843,7 +803,7 @@
_.values = function(obj) {
var keys = _.keys(obj);
var length = keys.length;
- var values = Array(length);
+ var values = new Array(length);
for (var i = 0; i < length; i++) {
values[i] = obj[keys[i]];
}
@@ -854,7 +814,7 @@
_.pairs = function(obj) {
var keys = _.keys(obj);
var length = keys.length;
- var pairs = Array(length);
+ var pairs = new Array(length);
for (var i = 0; i < length; i++) {
pairs[i] = [keys[i], obj[keys[i]]];
}
@@ -883,60 +843,45 @@
// Extend a given object with all the properties in passed-in object(s).
_.extend = function(obj) {
- if (!_.isObject(obj)) return obj;
- var source, prop;
- for (var i = 1, length = arguments.length; i < length; i++) {
- source = arguments[i];
- for (prop in source) {
- obj[prop] = source[prop];
+ each(slice.call(arguments, 1), function(source) {
+ if (source) {
+ for (var prop in source) {
+ obj[prop] = source[prop];
+ }
}
- }
+ });
return obj;
};
// Return a copy of the object only containing the whitelisted properties.
- _.pick = function(obj, iterator, context) {
- var result = {}, key;
- if (obj == null) return result;
- if (_.isFunction(iterator)) {
- iterator = createCallback(iterator, context);
- for (key in obj) {
- var value = obj[key];
- if (iterator(value, key, obj)) result[key] = value;
- }
- } else {
- var keys = concat.apply([], slice.call(arguments, 1));
- obj = Object(obj);
- for (var i = 0, length = keys.length; i < length; i++) {
- key = keys[i];
- if (key in obj) result[key] = obj[key];
- }
- }
- return result;
+ _.pick = function(obj) {
+ var copy = {};
+ var keys = concat.apply(ArrayProto, slice.call(arguments, 1));
+ each(keys, function(key) {
+ if (key in obj) copy[key] = obj[key];
+ });
+ return copy;
};
// Return a copy of the object without the blacklisted properties.
- _.omit = function(obj, iterator, context) {
- if (_.isFunction(iterator)) {
- iterator = _.negate(iterator);
- } else {
- var keys = _.map(concat.apply([], slice.call(arguments, 1)), String);
- iterator = function(value, key) {
- return !_.contains(keys, key);
- };
+ _.omit = function(obj) {
+ var copy = {};
+ var keys = concat.apply(ArrayProto, slice.call(arguments, 1));
+ for (var key in obj) {
+ if (!_.contains(keys, key)) copy[key] = obj[key];
}
- return _.pick(obj, iterator, context);
+ return copy;
};
// Fill in a given object with default properties.
_.defaults = function(obj) {
- if (!_.isObject(obj)) return obj;
- for (var i = 1, length = arguments.length; i < length; i++) {
- var source = arguments[i];
- for (var prop in source) {
- if (obj[prop] === void 0) obj[prop] = source[prop];
+ each(slice.call(arguments, 1), function(source) {
+ if (source) {
+ for (var prop in source) {
+ if (obj[prop] === void 0) obj[prop] = source[prop];
+ }
}
- }
+ });
return obj;
};
@@ -958,7 +903,7 @@
var eq = function(a, b, aStack, bStack) {
// Identical objects are equal. `0 === -0`, but they aren't identical.
// See the [Harmony `egal` proposal](http://wiki.ecmascript.org/doku.php?id=harmony:egal).
- if (a === b) return a !== 0 || 1 / a === 1 / b;
+ if (a === b) return a !== 0 || 1 / a == 1 / b;
// A strict comparison is necessary because `null == undefined`.
if (a == null || b == null) return a === b;
// Unwrap any wrapped objects.
@@ -966,27 +911,29 @@
if (b instanceof _) b = b._wrapped;
// Compare `[[Class]]` names.
var className = toString.call(a);
- if (className !== toString.call(b)) return false;
+ if (className != toString.call(b)) return false;
switch (className) {
- // Strings, numbers, regular expressions, dates, and booleans are compared by value.
- case '[object RegExp]':
- // RegExps are coerced to strings for comparison (Note: '' + /a/i === '/a/i')
+ // Strings, numbers, dates, and booleans are compared by value.
case '[object String]':
// Primitives and their corresponding object wrappers are equivalent; thus, `"5"` is
// equivalent to `new String("5")`.
- return '' + a === '' + b;
+ return a == String(b);
case '[object Number]':
- // `NaN`s are equivalent, but non-reflexive.
- // Object(NaN) is equivalent to NaN
- if (a != +a) return b != +b;
- // An `egal` comparison is performed for other numeric values.
- return a == 0 ? 1 / a == 1 / b : a == +b;
+ // `NaN`s are equivalent, but non-reflexive. An `egal` comparison is performed for
+ // other numeric values.
+ return a != +a ? b != +b : (a == 0 ? 1 / a == 1 / b : a == +b);
case '[object Date]':
case '[object Boolean]':
// Coerce dates and booleans to numeric primitive values. Dates are compared by their
// millisecond representations. Note that invalid dates with millisecond representations
// of `NaN` are not equivalent.
- return +a === +b;
+ return +a == +b;
+ // RegExps are compared by their source patterns and flags.
+ case '[object RegExp]':
+ return a.source == b.source &&
+ a.global == b.global &&
+ a.multiline == b.multiline &&
+ a.ignoreCase == b.ignoreCase;
}
if (typeof a != 'object' || typeof b != 'object') return false;
// Assume equality for cyclic structures. The algorithm for detecting cyclic
@@ -995,29 +942,25 @@
while (length--) {
// Linear search. Performance is inversely proportional to the number of
// unique nested structures.
- if (aStack[length] === a) return bStack[length] === b;
+ if (aStack[length] == a) return bStack[length] == b;
}
// Objects with different constructors are not equivalent, but `Object`s
// from different frames are.
var aCtor = a.constructor, bCtor = b.constructor;
- if (
- aCtor !== bCtor &&
- // Handle Object.create(x) cases
- 'constructor' in a && 'constructor' in b &&
- !(_.isFunction(aCtor) && aCtor instanceof aCtor &&
- _.isFunction(bCtor) && bCtor instanceof bCtor)
- ) {
+ if (aCtor !== bCtor && !(_.isFunction(aCtor) && (aCtor instanceof aCtor) &&
+ _.isFunction(bCtor) && (bCtor instanceof bCtor))
+ && ('constructor' in a && 'constructor' in b)) {
return false;
}
// Add the first object to the stack of traversed objects.
aStack.push(a);
bStack.push(b);
- var size, result;
+ var size = 0, result = true;
// Recursively compare objects and arrays.
- if (className === '[object Array]') {
+ if (className == '[object Array]') {
// Compare array lengths to determine if a deep comparison is necessary.
size = a.length;
- result = size === b.length;
+ result = size == b.length;
if (result) {
// Deep compare the contents, ignoring non-numeric properties.
while (size--) {
@@ -1026,17 +969,21 @@
}
} else {
// Deep compare objects.
- var keys = _.keys(a), key;
- size = keys.length;
- // Ensure that both objects contain the same number of properties before comparing deep equality.
- result = _.keys(b).length == size;
- if (result) {
- while (size--) {
- // Deep compare each member
- key = keys[size];
+ for (var key in a) {
+ if (_.has(a, key)) {
+ // Count the expected number of properties.
+ size++;
+ // Deep compare each member.
if (!(result = _.has(b, key) && eq(a[key], b[key], aStack, bStack))) break;
}
}
+ // Ensure that both objects contain the same number of properties.
+ if (result) {
+ for (key in b) {
+ if (_.has(b, key) && !(size--)) break;
+ }
+ result = !size;
+ }
}
// Remove the first object from the stack of traversed objects.
aStack.pop();
@@ -1053,7 +1000,7 @@
// An "empty" object has no enumerable own-properties.
_.isEmpty = function(obj) {
if (obj == null) return true;
- if (_.isArray(obj) || _.isString(obj) || _.isArguments(obj)) return obj.length === 0;
+ if (_.isArray(obj) || _.isString(obj)) return obj.length === 0;
for (var key in obj) if (_.has(obj, key)) return false;
return true;
};
@@ -1066,7 +1013,7 @@
// Is a given value an array?
// Delegates to ECMA5's native Array.isArray
_.isArray = nativeIsArray || function(obj) {
- return toString.call(obj) === '[object Array]';
+ return toString.call(obj) == '[object Array]';
};
// Is a given variable an object?
@@ -1075,9 +1022,9 @@
};
// Add some isType methods: isArguments, isFunction, isString, isNumber, isDate, isRegExp.
- _.each(['Arguments', 'Function', 'String', 'Number', 'Date', 'RegExp'], function(name) {
+ each(['Arguments', 'Function', 'String', 'Number', 'Date', 'RegExp'], function(name) {
_['is' + name] = function(obj) {
- return toString.call(obj) === '[object ' + name + ']';
+ return toString.call(obj) == '[object ' + name + ']';
};
});
@@ -1085,12 +1032,12 @@
// there isn't any inspectable "Arguments" type.
if (!_.isArguments(arguments)) {
_.isArguments = function(obj) {
- return _.has(obj, 'callee');
+ return !!(obj && _.has(obj, 'callee'));
};
}
// Optimize `isFunction` if appropriate.
- if (typeof /./ !== 'function') {
+ if (typeof (/./) !== 'function') {
_.isFunction = function(obj) {
return typeof obj === 'function';
};
@@ -1103,12 +1050,12 @@
// Is the given value `NaN`? (NaN is the only number which does not equal itself).
_.isNaN = function(obj) {
- return _.isNumber(obj) && obj !== +obj;
+ return _.isNumber(obj) && obj != +obj;
};
// Is a given value a boolean?
_.isBoolean = function(obj) {
- return obj === true || obj === false || toString.call(obj) === '[object Boolean]';
+ return obj === true || obj === false || toString.call(obj) == '[object Boolean]';
};
// Is a given value equal to null?
@@ -1124,7 +1071,7 @@
// Shortcut function for checking if an object has a given property directly
// on itself (in other words, not on a prototype).
_.has = function(obj, key) {
- return obj != null && hasOwnProperty.call(obj, key);
+ return hasOwnProperty.call(obj, key);
};
// Utility Functions
@@ -1143,13 +1090,11 @@
};
_.constant = function(value) {
- return function() {
+ return function () {
return value;
};
};
- _.noop = function(){};
-
_.property = function(key) {
return function(obj) {
return obj[key];
@@ -1159,18 +1104,19 @@
// Returns a predicate for checking whether an object has a given set of `key:value` pairs.
_.matches = function(attrs) {
return function(obj) {
- if (obj == null) return _.isEmpty(attrs);
- if (obj === attrs) return true;
- for (var key in attrs) if (attrs[key] !== obj[key]) return false;
+ if (obj === attrs) return true; //avoid comparing an object to itself.
+ for (var key in attrs) {
+ if (attrs[key] !== obj[key])
+ return false;
+ }
return true;
- };
+ }
};
// Run a function **n** times.
_.times = function(n, iterator, context) {
var accum = Array(Math.max(0, n));
- iterator = createCallback(iterator, context, 1);
- for (var i = 0; i < n; i++) accum[i] = iterator(i);
+ for (var i = 0; i < n; i++) accum[i] = iterator.call(context, i);
return accum;
};
@@ -1184,9 +1130,7 @@
};
// A (possibly faster) way to get the current timestamp as an integer.
- _.now = Date.now || function() {
- return new Date().getTime();
- };
+ _.now = Date.now || function() { return new Date().getTime(); };
// List of HTML entities for escaping.
var entityMap = {
@@ -1202,8 +1146,8 @@
// Regexes containing the keys and values listed immediately above.
var entityRegexes = {
- escape: RegExp('[' + _.keys(entityMap.escape).join('') + ']', 'g'),
- unescape: RegExp('(' + _.keys(entityMap.unescape).join('|') + ')', 'g')
+ escape: new RegExp('[' + _.keys(entityMap.escape).join('') + ']', 'g'),
+ unescape: new RegExp('(' + _.keys(entityMap.unescape).join('|') + ')', 'g')
};
// Functions for escaping and unescaping strings to/from HTML interpolation.
@@ -1221,7 +1165,19 @@
_.result = function(object, property) {
if (object == null) return void 0;
var value = object[property];
- return _.isFunction(value) ? object[property]() : value;
+ return _.isFunction(value) ? value.call(object) : value;
+ };
+
+ // Add your own custom functions to the Underscore object.
+ _.mixin = function(obj) {
+ each(_.functions(obj), function(name) {
+ var func = _[name] = obj[name];
+ _.prototype[name] = function() {
+ var args = [this._wrapped];
+ push.apply(args, arguments);
+ return result.call(this, func.apply(_, args));
+ };
+ });
};
// Generate a unique integer id (unique within the entire client session).
@@ -1252,24 +1208,22 @@
'\\': '\\',
'\r': 'r',
'\n': 'n',
+ '\t': 't',
'\u2028': 'u2028',
'\u2029': 'u2029'
};
- var escaper = /\\|'|\r|\n|\u2028|\u2029/g;
-
- var escapeChar = function(match) {
- return '\\' + escapes[match];
- };
+ var escaper = /\\|'|\r|\n|\t|\u2028|\u2029/g;
// JavaScript micro-templating, similar to John Resig's implementation.
// Underscore templating handles arbitrary delimiters, preserves whitespace,
// and correctly escapes quotes within interpolated code.
_.template = function(text, data, settings) {
+ var render;
settings = _.defaults({}, settings, _.templateSettings);
// Combine delimiters into one regular expression via alternation.
- var matcher = RegExp([
+ var matcher = new RegExp([
(settings.escape || noMatch).source,
(settings.interpolate || noMatch).source,
(settings.evaluate || noMatch).source
@@ -1279,18 +1233,19 @@
var index = 0;
var source = "__p+='";
text.replace(matcher, function(match, escape, interpolate, evaluate, offset) {
- source += text.slice(index, offset).replace(escaper, escapeChar);
- index = offset + match.length;
+ source += text.slice(index, offset)
+ .replace(escaper, function(match) { return '\\' + escapes[match]; });
if (escape) {
source += "'+\n((__t=(" + escape + "))==null?'':_.escape(__t))+\n'";
- } else if (interpolate) {
+ }
+ if (interpolate) {
source += "'+\n((__t=(" + interpolate + "))==null?'':__t)+\n'";
- } else if (evaluate) {
+ }
+ if (evaluate) {
source += "';\n" + evaluate + "\n__p+='";
}
-
- // Adobe VMs need the match returned to produce the correct offest.
+ index = offset + match.length;
return match;
});
source += "';\n";
@@ -1300,10 +1255,10 @@
source = "var __t,__p='',__j=Array.prototype.join," +
"print=function(){__p+=__j.call(arguments,'');};\n" +
- source + 'return __p;\n';
+ source + "return __p;\n";
try {
- var render = Function(settings.variable || 'obj', '_', source);
+ render = new Function(settings.variable || 'obj', '_', source);
} catch (e) {
e.source = source;
throw e;
@@ -1314,18 +1269,15 @@
return render.call(this, data, _);
};
- // Provide the compiled source as a convenience for precompilation.
- var argument = settings.variable || 'obj';
- template.source = 'function(' + argument + '){\n' + source + '}';
+ // Provide the compiled function source as a convenience for precompilation.
+ template.source = 'function(' + (settings.variable || 'obj') + '){\n' + source + '}';
return template;
};
- // Add a "chain" function. Start chaining a wrapped Underscore object.
+ // Add a "chain" function, which will delegate to the wrapper.
_.chain = function(obj) {
- var instance = _(obj);
- instance._chain = true;
- return instance;
+ return _(obj).chain();
};
// OOP
@@ -1339,44 +1291,42 @@
return this._chain ? _(obj).chain() : obj;
};
- // Add your own custom functions to the Underscore object.
- _.mixin = function(obj) {
- _.each(_.functions(obj), function(name) {
- var func = _[name] = obj[name];
- _.prototype[name] = function() {
- var args = [this._wrapped];
- push.apply(args, arguments);
- return result.call(this, func.apply(_, args));
- };
- });
- };
-
// Add all of the Underscore functions to the wrapper object.
_.mixin(_);
// Add all mutator Array functions to the wrapper.
- _.each(['pop', 'push', 'reverse', 'shift', 'sort', 'splice', 'unshift'], function(name) {
+ each(['pop', 'push', 'reverse', 'shift', 'sort', 'splice', 'unshift'], function(name) {
var method = ArrayProto[name];
_.prototype[name] = function() {
var obj = this._wrapped;
method.apply(obj, arguments);
- if ((name === 'shift' || name === 'splice') && obj.length === 0) delete obj[0];
+ if ((name == 'shift' || name == 'splice') && obj.length === 0) delete obj[0];
return result.call(this, obj);
};
});
// Add all accessor Array functions to the wrapper.
- _.each(['concat', 'join', 'slice'], function(name) {
+ each(['concat', 'join', 'slice'], function(name) {
var method = ArrayProto[name];
_.prototype[name] = function() {
return result.call(this, method.apply(this._wrapped, arguments));
};
});
- // Extracts the result from a wrapped and chained object.
- _.prototype.value = function() {
- return this._wrapped;
- };
+ _.extend(_.prototype, {
+
+ // Start chaining a wrapped Underscore object.
+ chain: function() {
+ this._chain = true;
+ return this;
+ },
+
+ // Extracts the result from a wrapped and chained object.
+ value: function() {
+ return this._wrapped;
+ }
+
+ });
// AMD registration happens at the end for compatibility with AMD loaders
// that may not enforce next-turn semantics on modules. Even though general
@@ -1390,4 +1340,4 @@
return _;
});
}
-}.call(this));
+}).call(this);
Gemfile
@@ -36,7 +36,6 @@ gem 'exifr'
gem 'aws-sdk'
gem 'mime-types'
gem 'ejs'
-gem 'marionette-rails'
gem 'js-routes'
group :development do
Gemfile.lock
@@ -177,8 +177,6 @@ GEM
mail (2.5.4)
mime-types (~> 1.16)
treetop (~> 1.4.8)
- marionette-rails (2.0.0)
- railties (>= 3.1)
memoizable (0.4.2)
thread_safe (~> 0.3, >= 0.3.1)
mime-types (1.25.1)
@@ -355,7 +353,6 @@ DEPENDENCIES
js-routes
kaminari (~> 0.15.0)
lol_dba
- marionette-rails
mime-types
mini_magick
newrelic_rpm