File manager - Edit - /home/monara/public_html/test.athavaneng.com/backbone.tar
Back
backbone.radio.min.js 0000644 00000007251 15073237727 0010547 0 ustar 00 /*! elementor - v0.7.1 - 18-08-2016 */ // Backbone.Radio v1.0.4 !function(e,n){"object"==typeof exports&&"undefined"!=typeof module?module.exports=n(require("underscore"),require("backbone")):"function"==typeof define&&define.amd?define(["underscore","backbone"],n):(e.Backbone=e.Backbone||{},e.Backbone.Radio=n(e._,e.Backbone))}(this,function(e,n){"use strict";function t(e,n,t,r){var o=e[n];return t&&t!==o.callback&&t!==o.callback._callback||r&&r!==o.context?void 0:(delete e[n],!0)}function r(n,r,o,i){n||(n={});for(var s=r?[r]:e.keys(n),u=!1,a=0,c=s.length;c>a;a++)r=s[a],n[r]&&t(n,r,o,i)&&(u=!0);return u}function o(n){return l[n]||(l[n]=e.partial(a.log,n))}function i(n){return e.isFunction(n)?n:function(){return n}}e="default"in e?e["default"]:e,n="default"in n?n["default"]:n;var s={};s["typeof"]="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol?"symbol":typeof e};var u=n.Radio,a=n.Radio={};a.VERSION="1.0.4",a.noConflict=function(){return n.Radio=u,this},a.DEBUG=!1,a._debugText=function(e,n,t){return e+(t?" on the "+t+" channel":"")+': "'+n+'"'},a.debugLog=function(e,n,t){a.DEBUG&&console&&console.warn&&console.warn(a._debugText(e,n,t))};var c=/\s+/;a._eventsApi=function(n,t,r,o){if(!r)return!1;var i={};if("object"===("undefined"==typeof r?"undefined":s["typeof"](r))){for(var u in r){var a=n[t].apply(n,[u,r[u]].concat(o));c.test(u)?e.extend(i,a):i[u]=a}return i}if(c.test(r)){for(var l=r.split(c),f=0,h=l.length;h>f;f++)i[l[f]]=n[t].apply(n,[l[f]].concat(o));return i}return!1},a._callHandler=function(e,n,t){var r=t[0],o=t[1],i=t[2];switch(t.length){case 0:return e.call(n);case 1:return e.call(n,r);case 2:return e.call(n,r,o);case 3:return e.call(n,r,o,i);default:return e.apply(n,t)}};var l={};e.extend(a,{log:function(n,t){if("undefined"!=typeof console){var r=e.drop(arguments,2);console.log("["+n+'] "'+t+'"',r)}},tuneIn:function(e){var n=a.channel(e);return n._tunedIn=!0,n.on("all",o(e)),this},tuneOut:function(e){var n=a.channel(e);return n._tunedIn=!1,n.off("all",o(e)),delete l[e],this}}),a.Requests={request:function(n){var t=e.rest(arguments),r=a._eventsApi(this,"request",n,t);if(r)return r;var o=this.channelName,i=this._requests;if(o&&this._tunedIn&&a.log.apply(this,[o,n].concat(t)),i&&(i[n]||i["default"])){var s=i[n]||i["default"];return t=i[n]?t:arguments,a._callHandler(s.callback,s.context,t)}a.debugLog("An unhandled request was fired",n,o)},reply:function(e,n,t){return a._eventsApi(this,"reply",e,[n,t])?this:(this._requests||(this._requests={}),this._requests[e]&&a.debugLog("A request was overwritten",e,this.channelName),this._requests[e]={callback:i(n),context:t||this},this)},replyOnce:function(n,t,r){if(a._eventsApi(this,"replyOnce",n,[t,r]))return this;var o=this,s=e.once(function(){return o.stopReplying(n),i(t).apply(this,arguments)});return this.reply(n,s,r)},stopReplying:function(e,n,t){return a._eventsApi(this,"stopReplying",e)?this:(e||n||t?r(this._requests,e,n,t)||a.debugLog("Attempted to remove the unregistered request",e,this.channelName):delete this._requests,this)}},a._channels={},a.channel=function(e){if(!e)throw new Error("You must provide a name for the channel.");return a._channels[e]?a._channels[e]:a._channels[e]=new a.Channel(e)},a.Channel=function(e){this.channelName=e},e.extend(a.Channel.prototype,n.Events,a.Requests,{reset:function(){return this.off(),this.stopListening(),this.stopReplying(),this}});var f,h,d=[n.Events,a.Requests];return e.each(d,function(n){e.each(n,function(n,t){a[t]=function(n){return h=e.rest(arguments),f=this.channel(n),f[t].apply(f,h)}})}),a.reset=function(n){var t=n?[this._channels[n]]:this._channels;e.invoke(t,"reset")},a}); backbone.marionette.js 0000644 00000404044 15073237727 0011037 0 ustar 00 /*! elementor - v3.0.15 - 2020-12-20 */ // MarionetteJS (Backbone.Marionette) // ---------------------------------- // v2.4.5.e1 // Change Log: // e1: Fix - Compatibility with jQuery 3. (`Marionette.Region.reset`). // // Copyright (c)2016 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) { /* istanbul ignore next */ if (typeof define === 'function' && define.amd) { define(['backbone', 'underscore'], function(Backbone, _) { return (root.Marionette = root.Mn = factory(root, Backbone, _)); }); } else if (typeof exports !== 'undefined') { var Backbone = require('backbone'); var _ = require('underscore'); module.exports = factory(root, Backbone, _); } else { root.Marionette = root.Mn = factory(root, root.Backbone, root._); } }(this, function(root, Backbone, _) { 'use strict'; /* istanbul ignore next */ // Backbone.BabySitter // ------------------- // v0.1.11 // // Copyright (c)2016 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", "reduce" ]; _.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.11"; Backbone.ChildViewContainer.noConflict = function() { Backbone.ChildViewContainer = previousChildViewContainer; return this; }; return Backbone.ChildViewContainer; })(Backbone, _); /* istanbul ignore next */ // Backbone.Wreqr (Backbone.Marionette) // ---------------------------------- // v1.3.6 // // Copyright (c)2016 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.6"; 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() { return config.callback.apply(config.context, arguments); }; }, // 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); Wreqr.Handlers.prototype.constructor.apply(this, arguments); }, // Execute a named command with the supplied args execute: function(name) { name = arguments[0]; var args = _.rest(arguments); 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(name) { if (this.hasHandler(name)) { return this.getHandler(name).apply(this, _.rest(arguments)); } } }); }(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]; return messageSystem[method].apply(messageSystem, _.rest(arguments)); }; }; return new Radio(); }(Wreqr, _); return Backbone.Wreqr; })(Backbone, _); var previousMarionette = root.Marionette; var previousMn = root.Mn; var Marionette = Backbone.Marionette = {}; Marionette.VERSION = '2.4.5'; Marionette.noConflict = function() { root.Marionette = previousMarionette; root.Mn = previousMn; return this; }; Backbone.Marionette = Marionette; // Get the Deferred creator for later use Marionette.Deferred = Backbone.$.Deferred; /* jshint unused: false *//* global console */ // Helpers // ------- // Marionette.extend // ----------------- // Borrow the Backbone `extend` method so we can use it as needed Marionette.extend = Backbone.Model.extend; // Marionette.isNodeAttached // ------------------------- // Determine if `el` is a child of the document Marionette.isNodeAttached = function(el) { return Backbone.$.contains(document.documentElement, el); }; // Merge `keys` from `options` onto `this` Marionette.mergeOptions = function(options, keys) { if (!options) { return; } _.extend(this, _.pick(options, keys)); }; // 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; } if (target.options && (target.options[optionName] !== undefined)) { return target.options[optionName]; } else { return target[optionName]; } }; // Proxy `Marionette.getOption` Marionette.proxyGetOption = function(optionName) { return Marionette.getOption(this, optionName); }; // Similar to `_.result`, this is a simple helper // If a function is provided we call it with context // otherwise just return the value. If the value is // undefined return a default value Marionette._getValue = function(value, context, params) { if (_.isFunction(value)) { value = params ? value.apply(context, params) : value.call(context); } return value; }; // Marionette.normalizeMethods // ---------------------- // Pass in a mapping of events => functions or function names // and return a mapping of events => functions Marionette.normalizeMethods = function(hash) { return _.reduce(hash, function(normalizedHash, method, name) { if (!_.isFunction(method)) { method = this[method]; } if (method) { normalizedHash[name] = method; } return normalizedHash; }, {}, this); }; // utility method for parsing @ui. syntax strings // into associated selector Marionette.normalizeUIString = function(uiString, ui) { return uiString.replace(/@ui\.[a-zA-Z-_$0-9]*/g, function(r) { return ui[r.slice(4)]; }); }; // allows for the use of the @ui. syntax within // a given key for triggers and events // swaps the @ui with the associated selector. // Returns a new, non-mutated, parsed events hash. Marionette.normalizeUIKeys = function(hash, ui) { return _.reduce(hash, function(memo, val, key) { var normalizedKey = Marionette.normalizeUIString(key, ui); memo[normalizedKey] = val; return memo; }, {}); }; // allows for the use of the @ui. syntax within // a given value for regions // swaps the @ui with the associated selector Marionette.normalizeUIValues = function(hash, ui, properties) { _.each(hash, function(val, key) { if (_.isString(val)) { hash[key] = Marionette.normalizeUIString(val, ui); } else if (_.isObject(val) && _.isArray(properties)) { _.extend(val, Marionette.normalizeUIValues(_.pick(val, properties), ui)); /* Value is an object, and we got an array of embedded property names to normalize. */ _.each(properties, function(property) { var propertyVal = val[property]; if (_.isString(propertyVal)) { val[property] = Marionette.normalizeUIString(propertyVal, ui); } }); } }); 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-121 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); }; }); }; var deprecate = Marionette.deprecate = function(message, test) { if (_.isObject(message)) { message = ( message.prev + ' is going to be removed in the future. ' + 'Please use ' + message.next + ' instead.' + (message.url ? ' See: ' + message.url : '') ); } if ((test === undefined || !test) && !deprecate._cache[message]) { deprecate._warn('Deprecation warning: ' + message); deprecate._cache[message] = true; } }; deprecate._console = typeof console !== 'undefined' ? console : {}; deprecate._warn = function() { var warn = deprecate._console.warn || deprecate._console.log || function() {}; return warn.apply(deprecate._console, arguments); }; deprecate._cache = {}; /* jshint maxstatements: 14, maxcomplexity: 7 */ // Trigger 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(); } return function(context, event, args) { var noEventArg = arguments.length < 3; if (noEventArg) { args = event; event = args[0]; } // get the method name from the event name var methodName = 'on' + event.replace(splitter, getEventName); var method = context[methodName]; var result; // call the onMethodName if it exists if (_.isFunction(method)) { // pass all args, except the event name result = method.apply(context, noEventArg ? _.rest(args) : args); } // trigger the event, if a trigger method exists if (_.isFunction(context.trigger)) { if (noEventArg + args.length > 1) { context.trigger.apply(context, noEventArg ? args : [event].concat(_.drop(args, 0))); } else { context.trigger(event); } } return result; }; })(); // 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(event) { return Marionette._triggerMethod(this, arguments); }; // triggerMethodOn invokes triggerMethod on a specific context // // e.g. `Marionette.triggerMethodOn(view, 'show')` // will trigger a "show" event or invoke onShow the view. Marionette.triggerMethodOn = function(context) { var fnc = _.isFunction(context.triggerMethod) ? context.triggerMethod : Marionette.triggerMethod; return fnc.apply(context, _.rest(arguments)); }; // DOM Refresh // ----------- // 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(view) { if (view._isDomRefreshMonitored) { return; } view._isDomRefreshMonitored = true; // track when the view has been shown in the DOM, // using a Marionette.Region (or by other means of triggering "show") function handleShow() { view._isShown = true; triggerDOMRefresh(); } // track when the view has been rendered function handleRender() { view._isRendered = true; triggerDOMRefresh(); } // Trigger the "dom:refresh" event and corresponding "onDomRefresh" method function triggerDOMRefresh() { if (view._isShown && view._isRendered && Marionette.isNodeAttached(view.el)) { Marionette.triggerMethodOn(view, 'dom:refresh', view); } } view.on({ show: handleShow, render: handleRender }); }; /* jshint maxparams: 5 */ // Bind Entity Events & Unbind Entity Events // ----------------------------------------- // // These methods are used to bind/unbind a backbone "entity" (e.g. collection/model) // to methods on a target object. // // The first parameter, `target`, must have the Backbone.Events module mixed in. // // The second parameter is the `entity` (Backbone.Model, Backbone.Collection or // any object that has Backbone.Events mixed in) 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) { throw new Marionette.Error('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; } // type-check bindings if (!_.isObject(bindings)) { throw new Marionette.Error({ message: 'Bindings must be an object or function.', url: 'marionette.functions.html#marionettebindentityevents' }); } // allow the bindings to be a function bindings = Marionette._getValue(bindings, 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); // Error // ----- var errorProps = ['description', 'fileName', 'lineNumber', 'name', 'message', 'number']; Marionette.Error = Marionette.extend.call(Error, { urlRoot: 'http://marionettejs.com/docs/v' + Marionette.VERSION + '/', constructor: function(message, options) { if (_.isObject(message)) { options = message; message = options.message; } else if (!options) { options = {}; } var error = Error.call(this, message); _.extend(this, _.pick(error, errorProps), _.pick(options, errorProps)); this.captureStackTrace(); if (options.url) { this.url = this.urlRoot + options.url; } }, captureStackTrace: function() { if (Error.captureStackTrace) { Error.captureStackTrace(this, Marionette.Error); } }, toString: function() { return this.name + ': ' + this.message + (this.url ? ' See: ' + this.url : ''); } }); Marionette.Error.extend = Marionette.extend; // 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); } }); // 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.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() { Marionette._triggerMethod(this, 'before:destroy', arguments); Marionette._triggerMethod(this, 'destroy', arguments); this.stopListening(); this.off(); return this; }, // import the `triggerMethod` to trigger events with corresponding // methods if the method exists triggerMethod: Marionette.triggerMethod, // A handy way to merge options onto the instance mergeOptions: Marionette.mergeOptions, // Proxy `getOption` to enable getting options from this or this.options by name. getOption: Marionette.proxyGetOption }); // Object // ------ // A Base Class that other Classes should descend from. // Object borrows many conventions and utilities from Backbone. Marionette.Object = function(options) { this.options = _.extend({}, _.result(this, 'options'), options); this.initialize.apply(this, arguments); }; Marionette.Object.extend = Marionette.extend; // Object Methods // -------------- // Ensure it can trigger events with Backbone.Events _.extend(Marionette.Object.prototype, Backbone.Events, { //this is a noop method intended to be overridden by classes that extend from this base initialize: function() {}, destroy: function(options) { options = options || {}; this.triggerMethod('before:destroy', options); this.triggerMethod('destroy', options); this.stopListening(); return this; }, // Import the `triggerMethod` to trigger events with corresponding // methods if the method exists triggerMethod: Marionette.triggerMethod, // A handy way to merge options onto the instance mergeOptions: Marionette.mergeOptions, // Proxy `getOption` to enable getting options from this or this.options by name. getOption: Marionette.proxyGetOption, // Proxy `bindEntityEvents` 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 }); /* jshint maxcomplexity: 16, maxstatements: 45, maxlen: 120 */ // 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 = Marionette.Object.extend({ constructor: function(options) { // set options temporarily so that we can get `el`. // options will be overriden by Object.constructor 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) { throw new Marionette.Error({ name: 'NoElError', message: 'An "el" must be specified for a region.' }); } this.$el = this.getEl(this.el); Marionette.Object.call(this, options); }, // 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) { if (!this._ensureElement()) { return; } this._ensureViewIsIntact(view); Marionette.MonitorDOMRefresh(view); 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 current view to change to begin with var isChangingView = !!this.currentView; // Only destroy the current view if we don't want to `preventDestroy` and if // the view given in the first argument is different than `currentView` var _shouldDestroyView = isDifferentView && !preventDestroy; // Only show the view given in the first argument if it is different than // the current view or if we want to re-show the view. Note that if // `_shouldDestroyView` is true, then `_shouldShowView` is also necessarily true. var _shouldShowView = isDifferentView || forceShow; if (isChangingView) { this.triggerMethod('before:swapOut', this.currentView, this, options); } if (this.currentView && isDifferentView) { delete this.currentView._parent; } if (_shouldDestroyView) { this.empty(); // A `destroy` event is attached to the clean up manually removed views. // We need to detach this event when a new view is going to be shown as it // is no longer relevant. } else if (isChangingView && _shouldShowView) { this.currentView.off('destroy', this.empty, this); } if (_shouldShowView) { // We need to listen for if a view is destroyed // in a way other than through the region. // If this happens we need to remove the reference // to the currentView since once a view has been destroyed // we can not reuse it. view.once('destroy', this.empty, this); // make this region the view's parent, // It's important that this parent binding happens before rendering // so that any events the child may trigger during render can also be // triggered on the child's ancestor views view._parent = this; this._renderView(view); if (isChangingView) { this.triggerMethod('before:swap', view, this, options); } this.triggerMethod('before:show', view, this, options); Marionette.triggerMethodOn(view, 'before:show', view, this, options); if (isChangingView) { this.triggerMethod('swapOut', this.currentView, this, options); } // An array of views that we're about to display var attachedRegion = Marionette.isNodeAttached(this.el); // The views that we're about to attach to the document // It's important that we prevent _getNestedViews from being executed unnecessarily // as it's a potentially-slow method var displayedViews = []; var attachOptions = _.extend({ triggerBeforeAttach: this.triggerBeforeAttach, triggerAttach: this.triggerAttach }, showOptions); if (attachedRegion && attachOptions.triggerBeforeAttach) { displayedViews = this._displayedViews(view); this._triggerAttach(displayedViews, 'before:'); } this.attachHtml(view); this.currentView = view; if (attachedRegion && attachOptions.triggerAttach) { displayedViews = this._displayedViews(view); this._triggerAttach(displayedViews); } if (isChangingView) { this.triggerMethod('swap', view, this, options); } this.triggerMethod('show', view, this, options); Marionette.triggerMethodOn(view, 'show', view, this, options); return this; } return this; }, triggerBeforeAttach: true, triggerAttach: true, _triggerAttach: function(views, prefix) { var eventName = (prefix || '') + 'attach'; _.each(views, function(view) { Marionette.triggerMethodOn(view, eventName, view, this); }, this); }, _displayedViews: function(view) { return _.union([view], _.result(view, '_getNestedViews') || []); }, _renderView: function(view) { if (!view.supportsRenderLifecycle) { Marionette.triggerMethodOn(view, 'before:render', view); } view.render(); if (!view.supportsRenderLifecycle) { Marionette.triggerMethodOn(view, 'render', view); } }, _ensureElement: function() { if (!_.isObject(this.el)) { this.$el = this.getEl(this.el); this.el = this.$el[0]; } if (!this.$el || this.$el.length === 0) { if (this.getOption('allowMissingEl')) { return false; } else { throw new Marionette.Error('An "el" ' + this.$el.selector + ' must exist in DOM'); } } return true; }, _ensureViewIsIntact: function(view) { if (!view) { throw new Marionette.Error({ name: 'ViewNotValid', message: 'The view passed is undefined and therefore invalid. You must pass a view instance to show.' }); } if (view.isDestroyed) { throw new Marionette.Error({ name: 'ViewDestroyedError', message: 'View (cid: "' + view.cid + '") has already been destroyed and cannot be used.' }); } }, // Override this method to change how the region finds the DOM // element that it manages. Return a jQuery selector object scoped // to a provided parent el or the document if none exists. getEl: function(el) { return Backbone.$(el, Marionette._getValue(this.options.parentEl, this)); }, // Override this method to change how the new view is // appended to the `$el` that the region is managing attachHtml: function(view) { this.$el.contents().detach(); 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(options) { var view = this.currentView; var emptyOptions = options || {}; var preventDestroy = !!emptyOptions.preventDestroy; // If there is no view in the region // we should not remove anything if (!view) { return this; } view.off('destroy', this.empty, this); this.triggerMethod('before:empty', view); if (!preventDestroy) { this._destroyView(); } this.triggerMethod('empty', view); // Remove region pointer to the currentView delete this.currentView; if (preventDestroy) { this.$el.contents().detach(); } return this; }, // call 'destroy' or 'remove', depending on which is found // on the view (if showing a raw Backbone view or a Marionette View) _destroyView: function() { var view = this.currentView; if (view.isDestroyed) { return; } if (!view.supportsDestroyLifecycle) { Marionette.triggerMethodOn(view, 'before:destroy', view); } if (view.destroy) { view.destroy(); } else { view.remove(); // appending isDestroyed to raw Backbone View allows regions // to throw a ViewDestroyedError for this view view.isDestroyed = true; } if (!view.supportsDestroyLifecycle) { Marionette.triggerMethodOn(view, 'destroy', view); } }, // 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) { if (this.currentView) { delete this.currentView._parent; } view._parent = this; this.currentView = view; return this; }, // Checks whether a view is currently present within // the region. Returns `true` if there is and `false` if // no view is present. hasView: function() { return !!this.currentView; }, // 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) { // 2020-12-20 Changed for compatibility with jQuery 3. this.el = this.options.el; } delete this.$el; return this; } }, // Static Methods { // 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 a selector, // a custom regionClass, and any options to be supplied to the region: // // ```js // { // selector: "#foo", // regionClass: MyCustomRegion, // allowMissingEl: false // } // ``` // buildRegion: function(regionConfig, DefaultRegionClass) { if (_.isString(regionConfig)) { return this._buildRegionFromSelector(regionConfig, DefaultRegionClass); } if (regionConfig.selector || regionConfig.el || regionConfig.regionClass) { return this._buildRegionFromObject(regionConfig, DefaultRegionClass); } if (_.isFunction(regionConfig)) { return this._buildRegionFromRegionClass(regionConfig); } throw new Marionette.Error({ message: 'Improper region configuration type.', url: 'marionette.region.html#region-configuration-types' }); }, // Build the region from a string selector like '#foo-region' _buildRegionFromSelector: function(selector, DefaultRegionClass) { return new DefaultRegionClass({el: selector}); }, // Build the region from a configuration object // ```js // { selector: '#foo', regionClass: FooRegion, allowMissingEl: false } // ``` _buildRegionFromObject: function(regionConfig, DefaultRegionClass) { var RegionClass = regionConfig.regionClass || DefaultRegionClass; var options = _.omit(regionConfig, 'selector', 'regionClass'); if (regionConfig.selector && !options.el) { options.el = regionConfig.selector; } return new RegionClass(options); }, // Build the region directly from a given `RegionClass` _buildRegionFromRegionClass: function(RegionClass) { return new RegionClass(); } }); // Region Manager // -------------- // Manage one or more related `Marionette.Region` objects. Marionette.RegionManager = Marionette.Controller.extend({ constructor: function(options) { this._regions = {}; this.length = 0; Marionette.Controller.call(this, options); this.addRegions(this.getOption('regions')); }, // Add multiple regions using an object literal or a // function that returns an object literal, where // each key becomes the region name, and each value is // the region definition. addRegions: function(regionDefinitions, defaults) { regionDefinitions = Marionette._getValue(regionDefinitions, this, arguments); return _.reduce(regionDefinitions, function(regions, definition, name) { if (_.isString(definition)) { definition = {selector: definition}; } if (definition.selector) { definition = _.defaults({}, definition, defaults); } regions[name] = this.addRegion(name, definition); return regions; }, {}, this); }, // Add an individual region to the region manager, // and return the region instance addRegion: function(name, definition) { var region; if (definition instanceof Marionette.Region) { region = definition; } else { region = Marionette.Region.buildRegion(definition, Marionette.Region); } this.triggerMethod('before:add:region', name, region); region._parent = this; 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); return region; }, // Empty all regions in the region manager, and // remove them removeRegions: function() { var regions = this.getRegions(); _.each(this._regions, function(region, name) { this._remove(name, region); }, this); return regions; }, // Empty all regions in the region manager, but // leave them attached emptyRegions: function() { var regions = this.getRegions(); _.invoke(regions, 'empty'); return regions; }, // Destroy all regions and shut down the region // manager entirely destroy: function() { this.removeRegions(); return Marionette.Controller.prototype.destroy.apply(this, arguments); }, // internal method to store regions _store: function(name, region) { if (!this._regions[name]) { this.length++; } this._regions[name] = region; }, // internal method to remove a region _remove: function(name, region) { this.triggerMethod('before:remove:region', name, region); region.empty(); region.stopListening(); delete region._parent; delete this._regions[name]; this.length--; this.triggerMethod('remove:region', name, region); } }); Marionette.actAsCollection(Marionette.RegionManager.prototype, '_regions'); // 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, options) { var cachedTemplate = this.templateCaches[templateId]; if (!cachedTemplate) { cachedTemplate = new Marionette.TemplateCache(templateId); this.templateCaches[templateId] = cachedTemplate; } return cachedTemplate.load(options); }, // 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 = _.toArray(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(options) { // 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, options); this.compiledTemplate = this.compileTemplate(template, options); 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, options) { var $template = Backbone.$(templateId); if (!$template.length) { throw new Marionette.Error({ name: 'NoTemplateError', message: 'Could not find template: "' + templateId + '"' }); } return $template.html(); }, // 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, options) { return _.template(rawTemplate, options); } }); // 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) { throw new Marionette.Error({ name: 'TemplateNotFoundError', message: 'Cannot render the template since its false, null or undefined.' }); } var templateFunc = _.isFunction(template) ? template : Marionette.TemplateCache.get(template); return templateFunc(data); } }; /* jshint maxlen: 114, nonew: false */ // View // ---- // The core view class that other Marionette views extend from. Marionette.View = Backbone.View.extend({ isDestroyed: false, supportsRenderLifecycle: true, supportsDestroyLifecycle: true, constructor: function(options) { this.render = _.bind(this.render, this); options = Marionette._getValue(options, this); // 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'), options); this._behaviors = Marionette.Behaviors(this); Backbone.View.call(this, this.options); Marionette.MonitorDOMRefresh(this); }, // 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'); }, // Serialize a model by returning its attributes. Clones // the attributes to allow modification. serializeModel: function(model) { return model.toJSON.apply(model, _.rest(arguments)); }, // 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'); templateHelpers = Marionette._getValue(templateHelpers, this); return _.extend(target, templateHelpers); }, // normalize the keys of passed hash with the views `ui` selectors. // `{"@ui.foo": "bar"}` normalizeUIKeys: function(hash) { var uiBindings = _.result(this, '_uiBindings'); return Marionette.normalizeUIKeys(hash, uiBindings || _.result(this, 'ui')); }, // normalize the values of passed hash with the views `ui` selectors. // `{foo: "@ui.bar"}` normalizeUIValues: function(hash, properties) { var ui = _.result(this, 'ui'); var uiBindings = _.result(this, '_uiBindings'); return Marionette.normalizeUIValues(hash, uiBindings || ui, properties); }, // Configure `triggers` to forward DOM events to view // events. `triggers: {"click .foo": "do:foo"}` configureTriggers: function() { if (!this.triggers) { return; } // 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 return _.reduce(triggers, function(events, value, key) { events[key] = this._buildViewTrigger(value); return events; }, {}, this); }, // 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')); _.each(this._behaviors, function(behavior) { behavior.bindEntityEvents(this.model, behavior.getOption('modelEvents')); behavior.bindEntityEvents(this.collection, behavior.getOption('collectionEvents')); }, this); return this; }, // internal method to delegate DOM events and triggers _delegateDOMEvents: function(eventsArg) { var events = Marionette._getValue(eventsArg || this.events, this); // normalize ui keys events = this.normalizeUIKeys(events); if (_.isUndefined(eventsArg)) {this.events = events;} var combinedEvents = {}; // look up if this view has behavior events var behaviorEvents = _.result(this, 'behaviorEvents') || {}; var triggers = this.configureTriggers(); var behaviorTriggers = _.result(this, 'behaviorTriggers') || {}; // behavior events will be overriden by view events and or triggers _.extend(combinedEvents, behaviorEvents, events, triggers, behaviorTriggers); Backbone.View.prototype.delegateEvents.call(this, combinedEvents); }, // Overriding Backbone.View's undelegateEvents to handle unbinding // the `triggers`, `modelEvents`, and `collectionEvents` config undelegateEvents: function() { Backbone.View.prototype.undelegateEvents.apply(this, arguments); this.unbindEntityEvents(this.model, this.getOption('modelEvents')); this.unbindEntityEvents(this.collection, this.getOption('collectionEvents')); _.each(this._behaviors, function(behavior) { behavior.unbindEntityEvents(this.model, behavior.getOption('modelEvents')); behavior.unbindEntityEvents(this.collection, behavior.getOption('collectionEvents')); }, this); return this; }, // Internal helper method to verify whether the view hasn't been destroyed _ensureViewIsIntact: function() { if (this.isDestroyed) { throw new Marionette.Error({ name: 'ViewDestroyedError', message: 'View (cid: "' + this.cid + '") has already been destroyed and cannot be used.' }); } }, // 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 this; } var args = _.toArray(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(); this.isRendered = false; // remove the view from the DOM this.remove(); // Call destroy on each behavior after // destroying the view. // This unbinds event listeners // that behaviors have registered for. _.invoke(this._behaviors, 'destroy', args); return this; }, bindUIElements: function() { this._bindUIElements(); _.invoke(this._behaviors, this._bindUIElements); }, // 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(bindings, function(selector, key) { this.ui[key] = this.$(selector); }, this); }, // This method unbinds the elements specified in the "ui" hash unbindUIElements: function() { this._unbindUIElements(); _.invoke(this._behaviors, this._unbindUIElements); }, _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; }, // Internal method to create an event handler for a given `triggerDef` like // 'click:foo' _buildViewTrigger: function(triggerDef) { var options = _.defaults({}, triggerDef, { preventDefault: true, stopPropagation: true }); var eventName = _.isObject(triggerDef) ? options.event : triggerDef; return function(e) { if (e) { if (e.preventDefault && options.preventDefault) { e.preventDefault(); } if (e.stopPropagation && options.stopPropagation) { e.stopPropagation(); } } var args = { view: this, model: this.model, collection: this.collection }; this.triggerMethod(eventName, args); }; }, setElement: function() { var ret = Backbone.View.prototype.setElement.apply(this, arguments); // 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. _.invoke(this._behaviors, 'proxyViewProperties', this); return ret; }, // import the `triggerMethod` to trigger events with corresponding // methods if the method exists triggerMethod: function() { var ret = Marionette._triggerMethod(this, arguments); this._triggerEventOnBehaviors(arguments); this._triggerEventOnParentLayout(arguments[0], _.rest(arguments)); return ret; }, _triggerEventOnBehaviors: function(args) { var triggerMethod = Marionette._triggerMethod; var behaviors = this._behaviors; // Use good ol' for as this is a very hot function for (var i = 0, length = behaviors && behaviors.length; i < length; i++) { triggerMethod(behaviors[i], args); } }, _triggerEventOnParentLayout: function(eventName, args) { var layoutView = this._parentLayoutView(); if (!layoutView) { return; } // invoke triggerMethod on parent view var eventPrefix = Marionette.getOption(layoutView, 'childViewEventPrefix'); var prefixedEventName = eventPrefix + ':' + eventName; var callArgs = [this].concat(args); Marionette._triggerMethod(layoutView, prefixedEventName, callArgs); // call the parent view's childEvents handler var childEvents = Marionette.getOption(layoutView, 'childEvents'); // since childEvents can be an object or a function use Marionette._getValue // to handle the abstaction for us. childEvents = Marionette._getValue(childEvents, layoutView); var normalizedChildEvents = layoutView.normalizeMethods(childEvents); if (normalizedChildEvents && _.isFunction(normalizedChildEvents[eventName])) { normalizedChildEvents[eventName].apply(layoutView, callArgs); } }, // This method returns any views that are immediate // children of this view _getImmediateChildren: function() { return []; }, // Returns an array of every nested view within this view _getNestedViews: function() { var children = this._getImmediateChildren(); if (!children.length) { return children; } return _.reduce(children, function(memo, view) { if (!view._getNestedViews) { return memo; } return memo.concat(view._getNestedViews()); }, children); }, // Walk the _parent tree until we find a layout view (if one exists). // Returns the parent layout view hierarchically closest to this view. _parentLayoutView: function() { var parent = this._parent; while (parent) { if (parent instanceof Marionette.LayoutView) { return parent; } parent = parent._parent; } }, // Imports the "normalizeMethods" to transform hashes of // events=>function references/names to a hash of events=>function references normalizeMethods: Marionette.normalizeMethods, // A handy way to merge passed-in options onto the instance mergeOptions: Marionette.mergeOptions, // Proxy `getOption` to enable getting options from this or this.options by name. getOption: Marionette.proxyGetOption, // Proxy `bindEntityEvents` 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, the view's `serializeModel` is called. If a collection is found, // each model in the collection is serialized by calling // the view's `serializeCollection` and put into 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() { if (!this.model && !this.collection) { return {}; } var args = [this.model || this.collection]; if (arguments.length) { args.push.apply(args, arguments); } if (this.model) { return this.serializeModel.apply(this, args); } else { return { items: this.serializeCollection.apply(this, args) }; } }, // Serialize a collection by serializing each of its models. serializeCollection: function(collection) { return collection.toJSON.apply(collection, _.rest(arguments)); }, // 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); this._renderTemplate(); this.isRendered = true; this.bindUIElements(); this.triggerMethod('render', this); return this; }, // Internal method to render the template with the serialized data // and template helpers via the `Marionette.Renderer` object. // Throws an `UndefinedTemplateError` error if the template is // any falsely value but literal `false`. _renderTemplate: function() { var template = this.getTemplate(); // Allow template-less item views if (template === false) { return; } if (!template) { throw new Marionette.Error({ name: 'UndefinedTemplateError', message: 'Cannot render the template since it is null or undefined.' }); } // Add in entity data and template helpers var data = this.mixinTemplateHelpers(this.serializeData()); // Render and add to el var html = Marionette.Renderer.render(template, data, this); this.attachElContent(html); return this; }, // Attaches the content of a given view. // This method can be overridden 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; } }); /* jshint maxstatements: 20, maxcomplexity: 7 */ // 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', // flag for maintaining the sorted order of the collection sort: true, // 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. // // option to pass `{comparator: compFunction()}` to allow the `CollectionView` // to use a custom sort order for the collection. constructor: function(options) { this.once('render', this._initialEvents); this._initChildViewStorage(); Marionette.View.apply(this, arguments); this.on({ 'before:show': this._onBeforeShowCalled, 'show': this._onShowCalled, 'before:attach': this._onBeforeAttachCalled, 'attach': this._onAttachCalled }); 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._bufferedChildren = []; }, startBuffering: function() { this.initRenderBuffer(); this.isBuffering = true; }, endBuffering: function() { // Only trigger attach if already shown and attached, otherwise Region#show() handles this. var canTriggerAttach = this._isShown && Marionette.isNodeAttached(this.el); var nestedViews; this.isBuffering = false; if (this._isShown) { this._triggerMethodMany(this._bufferedChildren, this, 'before:show'); } if (canTriggerAttach && this._triggerBeforeAttach) { nestedViews = this._getNestedViews(); this._triggerMethodMany(nestedViews, this, 'before:attach'); } this.attachBuffer(this, this._createBuffer()); if (canTriggerAttach && this._triggerAttach) { nestedViews = this._getNestedViews(); this._triggerMethodMany(nestedViews, this, 'attach'); } if (this._isShown) { this._triggerMethodMany(this._bufferedChildren, this, 'show'); } this.initRenderBuffer(); }, _triggerMethodMany: function(targets, source, eventName) { var args = _.drop(arguments, 3); _.each(targets, function(target) { Marionette.triggerMethodOn.apply(target, [target, eventName, target, source].concat(args)); }); }, // 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.getOption('sort')) { this.listenTo(this.collection, 'sort', this._sortViews); } } }, // Handle a child added to the collection _onCollectionAdd: function(child, collection, opts) { // `index` is present when adding with `at` since BB 1.2; indexOf fallback for < 1.2 var index = opts.at !== undefined && (opts.index || collection.indexOf(child)); // When filtered or when there is no initial index, calculate index. if (this.getOption('filter') || index === false) { index = _.indexOf(this._filteredSortedModels(index), child); } if (this._shouldAddChild(child, index)) { this.destroyEmptyView(); var ChildView = this.getChildView(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(); }, _onBeforeShowCalled: function() { // Reset attach event flags at the top of the Region#show() event lifecycle; if the Region's // show() options permit onBeforeAttach/onAttach events, these flags will be set true again. this._triggerBeforeAttach = this._triggerAttach = false; this.children.each(function(childView) { Marionette.triggerMethodOn(childView, 'before:show', childView); }); }, _onShowCalled: function() { this.children.each(function(childView) { Marionette.triggerMethodOn(childView, 'show', childView); }); }, // If during Region#show() onBeforeAttach was fired, continue firing it for child views _onBeforeAttachCalled: function() { this._triggerBeforeAttach = true; }, // If during Region#show() onAttach was fired, continue firing it for child views _onAttachCalled: function() { this._triggerAttach = true; }, // 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.isRendered = true; this.triggerMethod('render', this); return this; }, // Reorder DOM after sorting. When your element's rendering // do not use their index, you can pass reorderOnSort: true // to only reorder the DOM after a sort instead of rendering // all the collectionView reorder: function() { var children = this.children; var models = this._filteredSortedModels(); var anyModelsAdded = _.some(models, function(model) { return !children.findByModel(model); }); // If there are any new models added due to filtering // We need to add child views // So render as normal if (anyModelsAdded) { this.render(); } else { // get the DOM nodes in the same order as the models var elsToReorder = _.map(models, function(model, index) { var view = children.findByModel(model); view._index = index; return view.el; }); // find the views that were children before but arent in this new ordering var filteredOutViews = children.filter(function(view) { return !_.contains(elsToReorder, view.el); }); this.triggerMethod('before:reorder'); // since append moves elements that are already in the DOM, // appending the elements will effectively reorder them this._appendReorderedChildren(elsToReorder); // remove any views that have been filtered out _.each(filteredOutViews, this.removeChildView, this); this.checkEmpty(); this.triggerMethod('reorder'); } }, // Render view after sorting. Override this method to // change how the view renders after a `sort` on the collection. // An example of this would be to only `renderChildren` in a `CompositeView` // rather than the full view. resortView: function() { if (Marionette.getOption(this, 'reorderOnSort')) { this.reorder(); } else { this.render(); } }, // 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() { var models = this._filteredSortedModels(); // check for any changes in sort order of views var orderChanged = _.find(models, function(item, index) { var view = this.children.findByModel(item); return !view || view._index !== index; }, this); if (orderChanged) { this.resortView(); } }, // Internal reference to what index a `emptyView` is. _emptyViewIndex: -1, // Internal method. Separated so that CompositeView can append to the childViewContainer // if necessary _appendReorderedChildren: function(children) { this.$el.append(children); }, // Internal method. Separated so that CompositeView can have // more control over events being triggered, around the rendering // process _renderChildren: function() { this.destroyEmptyView(); this.destroyChildren({checkEmpty: false}); if (this.isEmpty(this.collection)) { this.showEmptyView(); } else { this.triggerMethod('before:render:collection', this); this.startBuffering(); this.showCollection(); this.endBuffering(); this.triggerMethod('render:collection', this); // If we have shown children and none have passed the filter, show the empty view if (this.children.isEmpty() && this.getOption('filter')) { this.showEmptyView(); } } }, // Internal method to loop through collection and show each child view. showCollection: function() { var ChildView; var models = this._filteredSortedModels(); _.each(models, function(child, index) { ChildView = this.getChildView(child); this.addChild(child, ChildView, index); }, this); }, // Allow the collection to be sorted by a custom view comparator _filteredSortedModels: function(addedAt) { var viewComparator = this.getViewComparator(); var models = this.collection.models; addedAt = Math.min(Math.max(addedAt, 0), models.length - 1); if (viewComparator) { var addedModel; // Preserve `at` location, even for a sorted view if (addedAt) { addedModel = models[addedAt]; models = models.slice(0, addedAt).concat(models.slice(addedAt + 1)); } models = this._sortModelsBy(models, viewComparator); if (addedModel) { models.splice(addedAt, 0, addedModel); } } // Filter after sorting in case the filter uses the index if (this.getOption('filter')) { models = _.filter(models, function(model, index) { return this._shouldAddChild(model, index); }, this); } return models; }, _sortModelsBy: function(models, comparator) { if (typeof comparator === 'string') { return _.sortBy(models, function(model) { return model.get(comparator); }, this); } else if (comparator.length === 1) { return _.sortBy(models, comparator, this); } else { return models.sort(_.bind(comparator, 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.triggerMethod('before:remove:empty'); this.destroyChildren(); delete this._showingEmptyView; this.triggerMethod('remove:empty'); } }, // Retrieve the empty view class getEmptyView: function() { return this.getOption('emptyView'); }, // Render and show the emptyView. Similar to addChild method // but "add:child" events are not fired, and the event from // emptyView are not forwarded addEmptyView: function(child, EmptyView) { // Only trigger attach if already shown, attached, and not buffering, otherwise endBuffer() or // Region#show() handles this. var canTriggerAttach = this._isShown && !this.isBuffering && Marionette.isNodeAttached(this.el); var nestedViews; // get the emptyViewOptions, falling back to childViewOptions var emptyViewOptions = this.getOption('emptyViewOptions') || this.getOption('childViewOptions'); if (_.isFunction(emptyViewOptions)) { emptyViewOptions = emptyViewOptions.call(this, child, this._emptyViewIndex); } // build the empty view var view = this.buildChildView(child, EmptyView, emptyViewOptions); view._parent = this; // Proxy emptyView events this.proxyChildEvents(view); view.once('render', function() { // trigger the 'before:show' event on `view` if the collection view has already been shown if (this._isShown) { Marionette.triggerMethodOn(view, 'before:show', view); } // Trigger `before:attach` following `render` to avoid adding logic and event triggers // to public method `renderChildView()`. if (canTriggerAttach && this._triggerBeforeAttach) { nestedViews = this._getViewAndNested(view); this._triggerMethodMany(nestedViews, this, 'before:attach'); } }, this); // Store the `emptyView` like a `childView` so we can properly remove and/or close it later this.children.add(view); this.renderChildView(view, this._emptyViewIndex); // Trigger `attach` if (canTriggerAttach && this._triggerAttach) { nestedViews = this._getViewAndNested(view); this._triggerMethodMany(nestedViews, this, 'attach'); } // call the 'show' method if the collection view has already been shown if (this._isShown) { Marionette.triggerMethodOn(view, 'show', view); } }, // Retrieve the `childView` class, either from `this.options.childView` // or from the `childView` in the object definition. The "options" // takes precedence. // This method receives the model that will be passed to the instance // created from this `childView`. Overriding methods may use the child // to determine what `childView` class to return. getChildView: function(child) { var childView = this.getOption('childView'); if (!childView) { throw new Marionette.Error({ name: 'NoChildViewError', message: 'A "childView" must be specified' }); } 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'); childViewOptions = Marionette._getValue(childViewOptions, this, [child, index]); var view = this.buildChildView(child, ChildView, childViewOptions); // increment indices of views after this one this._updateIndices(view, true, index); this.triggerMethod('before:add:child', view); this._addChildView(view, index); this.triggerMethod('add:child', view); view._parent = this; 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.getOption('sort')) { return; } if (increment) { // assign the index to the view view._index = index; } // update the indexes of views after this one this.children.each(function(laterView) { if (laterView._index >= view._index) { laterView._index += increment ? 1 : -1; } }); }, // Internal Method. Add the view to children and render it at // the given index. _addChildView: function(view, index) { // Only trigger attach if already shown, attached, and not buffering, otherwise endBuffer() or // Region#show() handles this. var canTriggerAttach = this._isShown && !this.isBuffering && Marionette.isNodeAttached(this.el); var nestedViews; // set up the child view event forwarding this.proxyChildEvents(view); view.once('render', function() { // trigger the 'before:show' event on `view` if the collection view has already been shown if (this._isShown && !this.isBuffering) { Marionette.triggerMethodOn(view, 'before:show', view); } // Trigger `before:attach` following `render` to avoid adding logic and event triggers // to public method `renderChildView()`. if (canTriggerAttach && this._triggerBeforeAttach) { nestedViews = this._getViewAndNested(view); this._triggerMethodMany(nestedViews, this, 'before:attach'); } }, this); // Store the child view itself so we can properly remove and/or destroy it later this.children.add(view); this.renderChildView(view, index); // Trigger `attach` if (canTriggerAttach && this._triggerAttach) { nestedViews = this._getViewAndNested(view); this._triggerMethodMany(nestedViews, this, 'attach'); } // Trigger `show` if (this._isShown && !this.isBuffering) { Marionette.triggerMethodOn(view, 'show', view); } }, // render the child view renderChildView: function(view, index) { if (!view.supportsRenderLifecycle) { Marionette.triggerMethodOn(view, 'before:render', view); } view.render(); if (!view.supportsRenderLifecycle) { Marionette.triggerMethodOn(view, 'render', view); } this.attachHtml(this, view, index); return view; }, // Build a `childView` for a model in the collection. buildChildView: function(child, ChildViewClass, childViewOptions) { var options = _.extend({model: child}, childViewOptions); var childView = new ChildViewClass(options); Marionette.MonitorDOMRefresh(childView); return childView; }, // 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) { return view; } this.triggerMethod('before:remove:child', view); if (!view.supportsDestroyLifecycle) { Marionette.triggerMethodOn(view, 'before:destroy', view); } // call 'destroy' or 'remove', depending on which is found if (view.destroy) { view.destroy(); } else { view.remove(); } if (!view.supportsDestroyLifecycle) { Marionette.triggerMethodOn(view, 'destroy', view); } delete view._parent; this.stopListening(view); this.children.remove(view); this.triggerMethod('remove:child', view); // decrement the index of views after this one this._updateIndices(view, false); return view; }, // check if the collection is empty isEmpty: function() { 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); }, // Create a fragment buffer from the currently buffered children _createBuffer: function() { var elBuffer = document.createDocumentFragment(); _.each(this._bufferedChildren, function(b) { elBuffer.appendChild(b.el); }); return elBuffer; }, // 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._bufferedChildren.splice(index, 0, 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.getOption('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; } this.triggerMethod('before:destroy:collection'); this.destroyChildren({checkEmpty: false}); this.triggerMethod('destroy:collection'); return Marionette.View.prototype.destroy.apply(this, arguments); }, // Destroy the child views that this collection view // is holding on to, if any destroyChildren: function(options) { var destroyOptions = options || {}; var shouldCheckEmpty = true; var childViews = this.children.map(_.identity); if (!_.isUndefined(destroyOptions.checkEmpty)) { shouldCheckEmpty = destroyOptions.checkEmpty; } this.children.each(this.removeChildView, this); if (shouldCheckEmpty) { this.checkEmpty(); } return childViews; }, // Return true if the given child should be shown // Return false otherwise // The filter will be passed (child, index, collection) // Where // 'child' is the given model // 'index' is the index of that model in the collection // 'collection' is the collection referenced by this CollectionView _shouldAddChild: function(child, index) { var filter = this.getOption('filter'); return !_.isFunction(filter) || filter.call(this, child, index, this.collection); }, // 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 = _.toArray(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); }); }, _getImmediateChildren: function() { return _.values(this.children._views); }, _getViewAndNested: function(view) { // This will not fail on Backbone.View which does not have #_getNestedViews. return [view].concat(_.result(view, '_getNestedViews') || []); }, getViewComparator: function() { return this.getOption('viewComparator'); } }); /* 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 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.getOption('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; return childView; }, // Serialize the model 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 = _.partial(this.serializeModel, this.model).apply(this, arguments); } return data; }, // Renders the model and the collection. render: function() { this._ensureViewIsIntact(); this._isRendering = true; this.resetChildViewContainer(); this.triggerMethod('before:render', this); this._renderTemplate(); this._renderChildren(); this._isRendering = false; this.isRendered = true; this.triggerMethod('render', this); return this; }, _renderChildren: function() { if (this.isRendered || this._isRendering) { Marionette.CollectionView.prototype._renderChildren.call(this); } }, // Render the root template that the children // views are appended to _renderTemplate: 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); 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 overridden 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, childView); $container.append(childView.el); }, // Internal method. Append reordered childView'. // Overidden from CollectionView to ensure reordered views // are appended to childViewContainer _appendReorderedChildren: function(children) { var $container = this.getChildViewContainer(this); $container.append(children); }, // Internal method to ensure an `$childViewContainer` exists, for the // `attachHtml` method to use. getChildViewContainer: function(containerView, childView) { if (!!containerView.$childViewContainer) { return containerView.$childViewContainer; } var container; var childViewContainer = Marionette.getOption(containerView, 'childViewContainer'); if (childViewContainer) { var selector = Marionette._getValue(childViewContainer, containerView); if (selector.charAt(0) === '@' && containerView.ui) { container = containerView.ui[selector.substr(4)]; } else { container = containerView.$(selector); } if (container.length <= 0) { throw new Marionette.Error({ name: 'ChildViewContainerMissingError', message: 'The specified "childViewContainer" was not found: ' + containerView.childViewContainer }); } } else { container = containerView.$el; } containerView.$childViewContainer = container; return container; }, // Internal method to reset the `$childViewContainer` on render resetChildViewContainer: function() { if (this.$childViewContainer) { this.$childViewContainer = undefined; } } }); // Layout View // ----------- // 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, options: { destroyImmediate: false }, // used as the prefix for child view events // that are forwarded through the layoutview childViewEventPrefix: 'childview', // 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; } // #2134: remove parent element before destroying the child views, so // removing the child views doesn't retrigger repaints if (this.getOption('destroyImmediate') === true) { this.$el.remove(); } this.regionManager.destroy(); return Marionette.ItemView.prototype.destroy.apply(this, arguments); }, showChildView: function(regionName, view, options) { var region = this.getRegion(regionName); return region.show.apply(region, _.rest(arguments)); }, getChildView: function(regionName) { return this.getRegion(regionName).currentView; }, // Add a single region, by name, to the layoutView addRegion: function(name, definition) { 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) { 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 defaults = { regionClass: this.getOption('regionClass'), parentEl: _.partial(_.result, this, '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(); regions = Marionette._getValue(this.regions, this, [options]) || {}; // Enable users to define `regions` as instance options. var regionOptions = this.getOption.call(options, 'regions'); // enable region options to be a function regionOptions = Marionette._getValue(regionOptions, this, [options]); _.extend(regions, regionOptions); // Normalize region selectors hash to allow // a user to use the @ui. syntax. regions = this.normalizeUIValues(regions, ['selector', 'el']); this.addRegions(regions); }, // Internal method to re-initialize all of the regions by updating the `el` that // they point to _reInitializeRegions: function() { this.regionManager.invoke('reset'); }, // Enable easy overriding of the default `RegionManager` // for customized region interactions and business 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.regionManager._parent = this; 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); }); }, _getImmediateChildren: function() { return _.chain(this.regionManager.getRegions()) .pluck('currentView') .compact() .value(); } }); // 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 = Marionette.Object.extend({ constructor: function(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); // Construct an internal UI hash 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. this.ui = _.extend({}, _.result(view, 'ui'), _.result(this, 'ui')); Marionette.Object.apply(this, arguments); }, // proxy behavior $ method to the view // this is useful for doing jquery DOM lookups // scoped to behaviors view. $: function() { return this.view.$.apply(this.view, arguments); }, // Stops the behavior from listening to events. // Overrides Object#destroy to prevent additional events from being triggered. destroy: function() { this.stopListening(); return this; }, proxyViewProperties: function(view) { this.$el = view.$el; this.el = view.el; } }); /* jshint maxlen: 143 */ // Behaviors // --------- // Behaviors is a utility class that takes care of // gluing 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, _) { // Borrow event splitter from Backbone var delegateEventSplitter = /^(\S+)\s*(.*)$/; function Behaviors(view, behaviors) { if (!_.isObject(view.behaviors)) { return {}; } // 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, _.keys(methods)); return behaviors; } var methods = { behaviorTriggers: function(behaviorTriggers, behaviors) { var triggerBuilder = new BehaviorTriggersBuilder(this, behaviors); return triggerBuilder.buildBehaviorTriggers(); }, behaviorEvents: function(behaviorEvents, behaviors) { var _behaviorsEvents = {}; _.each(behaviors, function(b, i) { var _events = {}; var behaviorEvents = _.clone(_.result(b, 'events')) || {}; // Normalize behavior events hash to allow // a user to use the @ui. syntax. behaviorEvents = Marionette.normalizeUIKeys(behaviorEvents, getBehaviorsUI(b)); var j = 0; _.each(behaviorEvents, function(behaviour, key) { var match = key.match(delegateEventSplitter); // Set event name to be namespaced using the view cid, // the behavior index, and the behavior event index // to generate a non colliding event namespace // http://api.jquery.com/event.namespace/ var eventName = match[1] + '.' + [this.cid, i, j++, ' '].join(''); var selector = match[2]; var eventKey = eventName + selector; var handler = _.isFunction(behaviour) ? behaviour : b[behaviour]; if (!handler) { return; } _events[eventKey] = _.bind(handler, b); }, this); _behaviorsEvents = _.extend(_behaviorsEvents, _events); }, this); 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 Marionette.Error({ message: 'You must define where your behaviors are stored.', url: 'marionette.behaviors.html#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 Marionette._getValue(Behaviors.behaviorsLookup, this, [options, key])[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); }); } }); // Class to build handlers for `triggers` on behaviors // for views function BehaviorTriggersBuilder(view, behaviors) { this._view = view; this._behaviors = behaviors; this._triggers = {}; } _.extend(BehaviorTriggersBuilder.prototype, { // Main method to build the triggers hash with event keys and handlers buildBehaviorTriggers: function() { _.each(this._behaviors, this._buildTriggerHandlersForBehavior, this); return this._triggers; }, // Internal method to build all trigger handlers for a given behavior _buildTriggerHandlersForBehavior: function(behavior, i) { var triggersHash = _.clone(_.result(behavior, 'triggers')) || {}; triggersHash = Marionette.normalizeUIKeys(triggersHash, getBehaviorsUI(behavior)); _.each(triggersHash, _.bind(this._setHandlerForBehavior, this, behavior, i)); }, // Internal method to create and assign the trigger handler for a given // behavior _setHandlerForBehavior: function(behavior, i, eventName, trigger) { // Unique identifier for the `this._triggers` hash var triggerKey = trigger.replace(/^\S+/, function(triggerName) { return triggerName + '.' + 'behaviortriggers' + i; }); this._triggers[triggerKey] = this._view._buildViewTrigger(eventName); } }); function getBehaviorsUI(behavior) { return behavior._uiBindings || behavior.ui; } return Behaviors; })(Marionette, _); // App Router // ---------- // 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) { this.options = options || {}; Backbone.Router.apply(this, arguments); 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) { // make sure an onRoute before trying to call it if (_.isFunction(this.onRoute)) { // find the path that matches the current route var routePath = _.invert(this.getOption('appRoutes'))[routeName]; 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) { throw new Marionette.Error('Method "' + methodName + '" was not found on the controller'); } this.route(route, methodName, _.bind(method, controller)); }, mergeOptions: Marionette.mergeOptions, // Proxy `getOption` to enable getting options from this or this.options by name. getOption: Marionette.proxyGetOption, triggerMethod: Marionette.triggerMethod, bindEntityEvents: Marionette.proxyBindEntityEvents, unbindEntityEvents: Marionette.proxyUnbindEntityEvents }); // 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 = Marionette.Object.extend({ constructor: function(options) { this._initializeRegions(options); this._initCallbacks = new Marionette.Callbacks(); this.submodules = {}; _.extend(this, options); this._initChannel(); Marionette.Object.apply(this, arguments); }, // 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() { return this._regionManager.emptyRegions(); }, // Removes a region from your app, by name // Accepts the regions name // removeRegion('myRegion') removeRegion: function(region) { return 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); var args = _.toArray(arguments); args.unshift(this); // see the Marionette.Module object for more information return ModuleClass.create.apply(ModuleClass, args); }, // Enable easy overriding of the default `RegionManager` // for customized region interactions and business-specific // view logic for better control over single regions. getRegionManager: function() { return new Marionette.RegionManager(); }, // Internal method to initialize the regions that have been defined in a // `regions` attribute on the application instance _initializeRegions: function(options) { var regions = _.isFunction(this.regions) ? this.regions(options) : this.regions || {}; this._initRegionManager(); // Enable users to define `regions` in instance options. var optionRegions = Marionette.getOption(options, 'regions'); // Enable region options to be a function if (_.isFunction(optionRegions)) { optionRegions = optionRegions.call(this, options); } // Overwrite current regions with those passed in options _.extend(regions, optionRegions); this.addRegions(regions); return this; }, // Internal method to set up the region manager _initRegionManager: function() { this._regionManager = this.getRegionManager(); this._regionManager._parent = this; this.listenTo(this._regionManager, 'before:add:region', function() { Marionette._triggerMethod(this, 'before:add:region', arguments); }); this.listenTo(this._regionManager, 'add:region', function(name, region) { this[name] = region; Marionette._triggerMethod(this, 'add:region', arguments); }); this.listenTo(this._regionManager, 'before:remove:region', function() { Marionette._triggerMethod(this, 'before:remove:region', arguments); }); this.listenTo(this._regionManager, 'remove:region', function(name) { delete this[name]; Marionette._triggerMethod(this, 'remove:region', arguments); }); }, // Internal method to setup the Wreqr.radio channel _initChannel: function() { this.channelName = _.result(this, 'channelName') || 'global'; this.channel = _.result(this, 'channel') || Backbone.Wreqr.radio.channel(this.channelName); this.vent = _.result(this, 'vent') || this.channel.vent; this.commands = _.result(this, 'commands') || this.channel.commands; this.reqres = _.result(this, 'reqres') || this.channel.reqres; } }); /* 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; 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, { // By default modules start with their parents. startWithParent: true, // 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 _.invoke(this.submodules, '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 = _.drop(arguments, 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; })); backbone.radio.js 0000644 00000024173 15073237727 0007767 0 ustar 00 /*! elementor - v0.7.1 - 18-08-2016 */ // Backbone.Radio v1.0.4 (function (global, factory) { typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory(require('underscore'), require('backbone')) : typeof define === 'function' && define.amd ? define(['underscore', 'backbone'], factory) : (global.Backbone = global.Backbone || {}, global.Backbone.Radio = factory(global._,global.Backbone)); }(this, function (_,Backbone) { 'use strict'; _ = 'default' in _ ? _['default'] : _; Backbone = 'default' in Backbone ? Backbone['default'] : Backbone; var babelHelpers = {}; babelHelpers.typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol ? "symbol" : typeof obj; }; babelHelpers; var previousRadio = Backbone.Radio; var Radio = Backbone.Radio = {}; Radio.VERSION = '1.0.4'; // This allows you to run multiple instances of Radio on the same // webapp. After loading the new version, call `noConflict()` to // get a reference to it. At the same time the old version will be // returned to Backbone.Radio. Radio.noConflict = function () { Backbone.Radio = previousRadio; return this; }; // Whether or not we're in DEBUG mode or not. DEBUG mode helps you // get around the issues of lack of warnings when events are mis-typed. Radio.DEBUG = false; // Format debug text. Radio._debugText = function (warning, eventName, channelName) { return warning + (channelName ? ' on the ' + channelName + ' channel' : '') + ': "' + eventName + '"'; }; // This is the method that's called when an unregistered event was called. // By default, it logs warning to the console. By overriding this you could // make it throw an Error, for instance. This would make firing a nonexistent event // have the same consequence as firing a nonexistent method on an Object. Radio.debugLog = function (warning, eventName, channelName) { if (Radio.DEBUG && console && console.warn) { console.warn(Radio._debugText(warning, eventName, channelName)); } }; var eventSplitter = /\s+/; // An internal method used to handle Radio's method overloading for Requests. // It's borrowed from Backbone.Events. It differs from Backbone's overload // API (which is used in Backbone.Events) in that it doesn't support space-separated // event names. Radio._eventsApi = function (obj, action, name, rest) { if (!name) { return false; } var results = {}; // Handle event maps. if ((typeof name === 'undefined' ? 'undefined' : babelHelpers.typeof(name)) === 'object') { for (var key in name) { var result = obj[action].apply(obj, [key, name[key]].concat(rest)); eventSplitter.test(key) ? _.extend(results, result) : results[key] = result; } return results; } // Handle space separated event names. if (eventSplitter.test(name)) { var names = name.split(eventSplitter); for (var i = 0, l = names.length; i < l; i++) { results[names[i]] = obj[action].apply(obj, [names[i]].concat(rest)); } return results; } return false; }; // An optimized way to execute callbacks. Radio._callHandler = function (callback, context, args) { var a1 = args[0], a2 = args[1], a3 = args[2]; switch (args.length) { case 0: return callback.call(context); case 1: return callback.call(context, a1); case 2: return callback.call(context, a1, a2); case 3: return callback.call(context, a1, a2, a3); default: return callback.apply(context, args); } }; // A helper used by `off` methods to the handler from the store function removeHandler(store, name, callback, context) { var event = store[name]; if ((!callback || callback === event.callback || callback === event.callback._callback) && (!context || context === event.context)) { delete store[name]; return true; } } function removeHandlers(store, name, callback, context) { store || (store = {}); var names = name ? [name] : _.keys(store); var matched = false; for (var i = 0, length = names.length; i < length; i++) { name = names[i]; // If there's no event by this name, log it and continue // with the loop if (!store[name]) { continue; } if (removeHandler(store, name, callback, context)) { matched = true; } } return matched; } /* * tune-in * ------- * Get console logs of a channel's activity * */ var _logs = {}; // This is to produce an identical function in both tuneIn and tuneOut, // so that Backbone.Events unregisters it. function _partial(channelName) { return _logs[channelName] || (_logs[channelName] = _.partial(Radio.log, channelName)); } _.extend(Radio, { // Log information about the channel and event log: function log(channelName, eventName) { if (typeof console === 'undefined') { return; } var args = _.drop(arguments, 2); console.log('[' + channelName + '] "' + eventName + '"', args); }, // Logs all events on this channel to the console. It sets an // internal value on the channel telling it we're listening, // then sets a listener on the Backbone.Events tuneIn: function tuneIn(channelName) { var channel = Radio.channel(channelName); channel._tunedIn = true; channel.on('all', _partial(channelName)); return this; }, // Stop logging all of the activities on this channel to the console tuneOut: function tuneOut(channelName) { var channel = Radio.channel(channelName); channel._tunedIn = false; channel.off('all', _partial(channelName)); delete _logs[channelName]; return this; } }); /* * Backbone.Radio.Requests * ----------------------- * A messaging system for requesting data. * */ function makeCallback(callback) { return _.isFunction(callback) ? callback : function () { return callback; }; } Radio.Requests = { // Make a request request: function request(name) { var args = _.rest(arguments); var results = Radio._eventsApi(this, 'request', name, args); if (results) { return results; } var channelName = this.channelName; var requests = this._requests; // Check if we should log the request, and if so, do it if (channelName && this._tunedIn) { Radio.log.apply(this, [channelName, name].concat(args)); } // If the request isn't handled, log it in DEBUG mode and exit if (requests && (requests[name] || requests['default'])) { var handler = requests[name] || requests['default']; args = requests[name] ? args : arguments; return Radio._callHandler(handler.callback, handler.context, args); } else { Radio.debugLog('An unhandled request was fired', name, channelName); } }, // Set up a handler for a request reply: function reply(name, callback, context) { if (Radio._eventsApi(this, 'reply', name, [callback, context])) { return this; } this._requests || (this._requests = {}); if (this._requests[name]) { Radio.debugLog('A request was overwritten', name, this.channelName); } this._requests[name] = { callback: makeCallback(callback), context: context || this }; return this; }, // Set up a handler that can only be requested once replyOnce: function replyOnce(name, callback, context) { if (Radio._eventsApi(this, 'replyOnce', name, [callback, context])) { return this; } var self = this; var once = _.once(function () { self.stopReplying(name); return makeCallback(callback).apply(this, arguments); }); return this.reply(name, once, context); }, // Remove handler(s) stopReplying: function stopReplying(name, callback, context) { if (Radio._eventsApi(this, 'stopReplying', name)) { return this; } // Remove everything if there are no arguments passed if (!name && !callback && !context) { delete this._requests; } else if (!removeHandlers(this._requests, name, callback, context)) { Radio.debugLog('Attempted to remove the unregistered request', name, this.channelName); } return this; } }; /* * Backbone.Radio.channel * ---------------------- * Get a reference to a channel by name. * */ Radio._channels = {}; Radio.channel = function (channelName) { if (!channelName) { throw new Error('You must provide a name for the channel.'); } if (Radio._channels[channelName]) { return Radio._channels[channelName]; } else { return Radio._channels[channelName] = new Radio.Channel(channelName); } }; /* * Backbone.Radio.Channel * ---------------------- * A Channel is an object that extends from Backbone.Events, * and Radio.Requests. * */ Radio.Channel = function (channelName) { this.channelName = channelName; }; _.extend(Radio.Channel.prototype, Backbone.Events, Radio.Requests, { // Remove all handlers from the messaging systems of this channel reset: function reset() { this.off(); this.stopListening(); this.stopReplying(); return this; } }); /* * Top-level API * ------------- * Supplies the 'top-level API' for working with Channels directly * from Backbone.Radio. * */ var channel; var args; var systems = [Backbone.Events, Radio.Requests]; _.each(systems, function (system) { _.each(system, function (method, methodName) { Radio[methodName] = function (channelName) { args = _.rest(arguments); channel = this.channel(channelName); return channel[methodName].apply(channel, args); }; }); }); Radio.reset = function (channelName) { var channels = !channelName ? this._channels : [this._channels[channelName]]; _.invoke(channels, 'reset'); }; return Radio; })); backbone.marionette.min.js 0000644 00000130144 15073237727 0011616 0 ustar 00 /*! elementor - v3.0.15 - 2020-12-20 */ // MarionetteJS (Backbone.Marionette) // ---------------------------------- // v2.4.5.e1 // Change Log: // e1: Fix - Compatibility with jQuery 3. (`Marionette.Region.reset`). // // Copyright (c)2016 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(a,b){if("function"==typeof define&&define.amd)define(["backbone","underscore"],function(c,d){return a.Marionette=a.Mn=b(a,c,d)});else if("undefined"!=typeof exports){var c=require("backbone"),d=require("underscore");module.exports=b(a,c,d)}else a.Marionette=a.Mn=b(a,a.Backbone,a._)}(this,function(a,b,c){"use strict";!function(a,b){var c=a.ChildViewContainer;return a.ChildViewContainer=function(a,b){var c=function(a){this._views={},this._indexByModel={},this._indexByCustom={},this._updateLength(),b.each(a,this.add,this)};b.extend(c.prototype,{add:function(a,b){var c=a.cid;return this._views[c]=a,a.model&&(this._indexByModel[a.model.cid]=c),b&&(this._indexByCustom[b]=c),this._updateLength(),this},findByModel:function(a){return this.findByModelCid(a.cid)},findByModelCid:function(a){var b=this._indexByModel[a];return this.findByCid(b)},findByCustom:function(a){var b=this._indexByCustom[a];return this.findByCid(b)},findByIndex:function(a){return b.values(this._views)[a]},findByCid:function(a){return this._views[a]},remove:function(a){var c=a.cid;return a.model&&delete this._indexByModel[a.model.cid],b.any(this._indexByCustom,function(a,b){return a===c?(delete this._indexByCustom[b],!0):void 0},this),delete this._views[c],this._updateLength(),this},call:function(a){this.apply(a,b.tail(arguments))},apply:function(a,c){b.each(this._views,function(d){b.isFunction(d[a])&&d[a].apply(d,c||[])})},_updateLength:function(){this.length=b.size(this._views)}});var d=["forEach","each","map","find","detect","filter","select","reject","every","all","some","any","include","contains","invoke","toArray","first","initial","rest","last","without","isEmpty","pluck","reduce"];return b.each(d,function(a){c.prototype[a]=function(){var c=b.values(this._views),d=[c].concat(b.toArray(arguments));return b[a].apply(b,d)}}),c}(a,b),a.ChildViewContainer.VERSION="0.1.11",a.ChildViewContainer.noConflict=function(){return a.ChildViewContainer=c,this},a.ChildViewContainer}(b,c),function(a,b){var c=a.Wreqr,d=a.Wreqr={};return a.Wreqr.VERSION="1.3.6",a.Wreqr.noConflict=function(){return a.Wreqr=c,this},d.Handlers=function(a,b){var c=function(a){this.options=a,this._wreqrHandlers={},b.isFunction(this.initialize)&&this.initialize(a)};return c.extend=a.Model.extend,b.extend(c.prototype,a.Events,{setHandlers:function(a){b.each(a,function(a,c){var d=null;b.isObject(a)&&!b.isFunction(a)&&(d=a.context,a=a.callback),this.setHandler(c,a,d)},this)},setHandler:function(a,b,c){var d={callback:b,context:c};this._wreqrHandlers[a]=d,this.trigger("handler:add",a,b,c)},hasHandler:function(a){return!!this._wreqrHandlers[a]},getHandler:function(a){var b=this._wreqrHandlers[a];if(b)return function(){return b.callback.apply(b.context,arguments)}},removeHandler:function(a){delete this._wreqrHandlers[a]},removeAllHandlers:function(){this._wreqrHandlers={}}}),c}(a,b),d.CommandStorage=function(){var c=function(a){this.options=a,this._commands={},b.isFunction(this.initialize)&&this.initialize(a)};return b.extend(c.prototype,a.Events,{getCommands:function(a){var b=this._commands[a];return b||(b={command:a,instances:[]},this._commands[a]=b),b},addCommand:function(a,b){var c=this.getCommands(a);c.instances.push(b)},clearCommands:function(a){var b=this.getCommands(a);b.instances=[]}}),c}(),d.Commands=function(a,b){return a.Handlers.extend({storageType:a.CommandStorage,constructor:function(b){this.options=b||{},this._initializeStorage(this.options),this.on("handler:add",this._executeCommands,this),a.Handlers.prototype.constructor.apply(this,arguments)},execute:function(a){a=arguments[0];var c=b.rest(arguments);this.hasHandler(a)?this.getHandler(a).apply(this,c):this.storage.addCommand(a,c)},_executeCommands:function(a,c,d){var e=this.storage.getCommands(a);b.each(e.instances,function(a){c.apply(d,a)}),this.storage.clearCommands(a)},_initializeStorage:function(a){var c,d=a.storageType||this.storageType;c=b.isFunction(d)?new d:d,this.storage=c}})}(d,b),d.RequestResponse=function(a,b){return a.Handlers.extend({request:function(a){return this.hasHandler(a)?this.getHandler(a).apply(this,b.rest(arguments)):void 0}})}(d,b),d.EventAggregator=function(a,b){var c=function(){};return c.extend=a.Model.extend,b.extend(c.prototype,a.Events),c}(a,b),d.Channel=function(c){var d=function(b){this.vent=new a.Wreqr.EventAggregator,this.reqres=new a.Wreqr.RequestResponse,this.commands=new a.Wreqr.Commands,this.channelName=b};return b.extend(d.prototype,{reset:function(){return this.vent.off(),this.vent.stopListening(),this.reqres.removeAllHandlers(),this.commands.removeAllHandlers(),this},connectEvents:function(a,b){return this._connect("vent",a,b),this},connectCommands:function(a,b){return this._connect("commands",a,b),this},connectRequests:function(a,b){return this._connect("reqres",a,b),this},_connect:function(a,c,d){if(c){d=d||this;var e="vent"===a?"on":"setHandler";b.each(c,function(c,f){this[a][e](f,b.bind(c,d))},this)}}}),d}(d),d.radio=function(a,b){var c=function(){this._channels={},this.vent={},this.commands={},this.reqres={},this._proxyMethods()};b.extend(c.prototype,{channel:function(a){if(!a)throw new Error("Channel must receive a name");return this._getChannel(a)},_getChannel:function(b){var c=this._channels[b];return c||(c=new a.Channel(b),this._channels[b]=c),c},_proxyMethods:function(){b.each(["vent","commands","reqres"],function(a){b.each(d[a],function(b){this[a][b]=e(this,a,b)},this)},this)}});var d={vent:["on","off","trigger","once","stopListening","listenTo","listenToOnce"],commands:["execute","setHandler","setHandlers","removeHandler","removeAllHandlers"],reqres:["request","setHandler","setHandlers","removeHandler","removeAllHandlers"]},e=function(a,c,d){return function(e){var f=a._getChannel(e)[c];return f[d].apply(f,b.rest(arguments))}};return new c}(d,b),a.Wreqr}(b,c);var d=a.Marionette,e=a.Mn,f=b.Marionette={};f.VERSION="2.4.5",f.noConflict=function(){return a.Marionette=d,a.Mn=e,this},b.Marionette=f,f.Deferred=b.$.Deferred,f.extend=b.Model.extend,f.isNodeAttached=function(a){return b.$.contains(document.documentElement,a)},f.mergeOptions=function(a,b){a&&c.extend(this,c.pick(a,b))},f.getOption=function(a,b){return a&&b?a.options&&void 0!==a.options[b]?a.options[b]:a[b]:void 0},f.proxyGetOption=function(a){return f.getOption(this,a)},f._getValue=function(a,b,d){return c.isFunction(a)&&(a=d?a.apply(b,d):a.call(b)),a},f.normalizeMethods=function(a){return c.reduce(a,function(a,b,d){return c.isFunction(b)||(b=this[b]),b&&(a[d]=b),a},{},this)},f.normalizeUIString=function(a,b){return a.replace(/@ui\.[a-zA-Z-_$0-9]*/g,function(a){return b[a.slice(4)]})},f.normalizeUIKeys=function(a,b){return c.reduce(a,function(a,c,d){var e=f.normalizeUIString(d,b);return a[e]=c,a},{})},f.normalizeUIValues=function(a,b,d){return c.each(a,function(e,g){c.isString(e)?a[g]=f.normalizeUIString(e,b):c.isObject(e)&&c.isArray(d)&&(c.extend(e,f.normalizeUIValues(c.pick(e,d),b)),c.each(d,function(a){var d=e[a];c.isString(d)&&(e[a]=f.normalizeUIString(d,b))}))}),a},f.actAsCollection=function(a,b){var d=["forEach","each","map","find","detect","filter","select","reject","every","all","some","any","include","contains","invoke","toArray","first","initial","rest","last","without","isEmpty","pluck"];c.each(d,function(d){a[d]=function(){var a=c.values(c.result(this,b)),e=[a].concat(c.toArray(arguments));return c[d].apply(c,e)}})};var g=f.deprecate=function(a,b){c.isObject(a)&&(a=a.prev+" is going to be removed in the future. Please use "+a.next+" instead."+(a.url?" See: "+a.url:"")),void 0!==b&&b||g._cache[a]||(g._warn("Deprecation warning: "+a),g._cache[a]=!0)};g._console="undefined"!=typeof console?console:{},g._warn=function(){var a=g._console.warn||g._console.log||function(){};return a.apply(g._console,arguments)},g._cache={},f._triggerMethod=function(){function a(a,b,c){return c.toUpperCase()}var b=/(^|:)(\w)/gi;return function(d,e,f){var g=arguments.length<3;g&&(f=e,e=f[0]);var h,i="on"+e.replace(b,a),j=d[i];return c.isFunction(j)&&(h=j.apply(d,g?c.rest(f):f)),c.isFunction(d.trigger)&&(g+f.length>1?d.trigger.apply(d,g?f:[e].concat(c.drop(f,0))):d.trigger(e)),h}}(),f.triggerMethod=function(a){return f._triggerMethod(this,arguments)},f.triggerMethodOn=function(a){var b=c.isFunction(a.triggerMethod)?a.triggerMethod:f.triggerMethod;return b.apply(a,c.rest(arguments))},f.MonitorDOMRefresh=function(a){function b(){a._isShown=!0,d()}function c(){a._isRendered=!0,d()}function d(){a._isShown&&a._isRendered&&f.isNodeAttached(a.el)&&f.triggerMethodOn(a,"dom:refresh",a)}a._isDomRefreshMonitored||(a._isDomRefreshMonitored=!0,a.on({show:b,render:c}))},function(a){function b(b,d,e,f){var g=f.split(/\s+/);c.each(g,function(c){var f=b[c];if(!f)throw new a.Error('Method "'+c+'" was configured as an event handler, but does not exist.');b.listenTo(d,e,f)})}function d(a,b,c,d){a.listenTo(b,c,d)}function e(a,b,d,e){var f=e.split(/\s+/);c.each(f,function(c){var e=a[c];a.stopListening(b,d,e)})}function f(a,b,c,d){a.stopListening(b,c,d)}function g(b,d,e,f,g){if(d&&e){if(!c.isObject(e))throw new a.Error({message:"Bindings must be an object or function.",url:"marionette.functions.html#marionettebindentityevents"});e=a._getValue(e,b),c.each(e,function(a,e){c.isFunction(a)?f(b,d,e,a):g(b,d,e,a)})}}a.bindEntityEvents=function(a,c,e){g(a,c,e,d,b)},a.unbindEntityEvents=function(a,b,c){g(a,b,c,f,e)},a.proxyBindEntityEvents=function(b,c){return a.bindEntityEvents(this,b,c)},a.proxyUnbindEntityEvents=function(b,c){return a.unbindEntityEvents(this,b,c)}}(f);var h=["description","fileName","lineNumber","name","message","number"];return f.Error=f.extend.call(Error,{urlRoot:"http://marionettejs.com/docs/v"+f.VERSION+"/",constructor:function(a,b){c.isObject(a)?(b=a,a=b.message):b||(b={});var d=Error.call(this,a);c.extend(this,c.pick(d,h),c.pick(b,h)),this.captureStackTrace(),b.url&&(this.url=this.urlRoot+b.url)},captureStackTrace:function(){Error.captureStackTrace&&Error.captureStackTrace(this,f.Error)},toString:function(){return this.name+": "+this.message+(this.url?" See: "+this.url:"")}}),f.Error.extend=f.extend,f.Callbacks=function(){this._deferred=f.Deferred(),this._callbacks=[]},c.extend(f.Callbacks.prototype,{add:function(a,b){var d=c.result(this._deferred,"promise");this._callbacks.push({cb:a,ctx:b}),d.then(function(c){b&&(c.context=b),a.call(c.context,c.options)})},run:function(a,b){this._deferred.resolve({options:a,context:b})},reset:function(){var a=this._callbacks;this._deferred=f.Deferred(),this._callbacks=[],c.each(a,function(a){this.add(a.cb,a.ctx)},this)}}),f.Controller=function(a){this.options=a||{},c.isFunction(this.initialize)&&this.initialize(this.options)},f.Controller.extend=f.extend,c.extend(f.Controller.prototype,b.Events,{destroy:function(){return f._triggerMethod(this,"before:destroy",arguments),f._triggerMethod(this,"destroy",arguments),this.stopListening(),this.off(),this},triggerMethod:f.triggerMethod,mergeOptions:f.mergeOptions,getOption:f.proxyGetOption}),f.Object=function(a){this.options=c.extend({},c.result(this,"options"),a),this.initialize.apply(this,arguments)},f.Object.extend=f.extend,c.extend(f.Object.prototype,b.Events,{initialize:function(){},destroy:function(a){return a=a||{},this.triggerMethod("before:destroy",a),this.triggerMethod("destroy",a),this.stopListening(),this},triggerMethod:f.triggerMethod,mergeOptions:f.mergeOptions,getOption:f.proxyGetOption,bindEntityEvents:f.proxyBindEntityEvents,unbindEntityEvents:f.proxyUnbindEntityEvents}),f.Region=f.Object.extend({constructor:function(a){if(this.options=a||{},this.el=this.getOption("el"),this.el=this.el instanceof b.$?this.el[0]:this.el,!this.el)throw new f.Error({name:"NoElError",message:'An "el" must be specified for a region.'});this.$el=this.getEl(this.el),f.Object.call(this,a)},show:function(a,b){if(this._ensureElement()){this._ensureViewIsIntact(a),f.MonitorDOMRefresh(a);var d=b||{},e=a!==this.currentView,g=!!d.preventDestroy,h=!!d.forceShow,i=!!this.currentView,j=e&&!g,k=e||h;if(i&&this.triggerMethod("before:swapOut",this.currentView,this,b),this.currentView&&e&&delete this.currentView._parent,j?this.empty():i&&k&&this.currentView.off("destroy",this.empty,this),k){a.once("destroy",this.empty,this),a._parent=this,this._renderView(a),i&&this.triggerMethod("before:swap",a,this,b),this.triggerMethod("before:show",a,this,b),f.triggerMethodOn(a,"before:show",a,this,b),i&&this.triggerMethod("swapOut",this.currentView,this,b);var l=f.isNodeAttached(this.el),m=[],n=c.extend({triggerBeforeAttach:this.triggerBeforeAttach,triggerAttach:this.triggerAttach},d);return l&&n.triggerBeforeAttach&&(m=this._displayedViews(a),this._triggerAttach(m,"before:")),this.attachHtml(a),this.currentView=a,l&&n.triggerAttach&&(m=this._displayedViews(a),this._triggerAttach(m)),i&&this.triggerMethod("swap",a,this,b),this.triggerMethod("show",a,this,b),f.triggerMethodOn(a,"show",a,this,b),this}return this}},triggerBeforeAttach:!0,triggerAttach:!0,_triggerAttach:function(a,b){var d=(b||"")+"attach";c.each(a,function(a){f.triggerMethodOn(a,d,a,this)},this)},_displayedViews:function(a){return c.union([a],c.result(a,"_getNestedViews")||[])},_renderView:function(a){a.supportsRenderLifecycle||f.triggerMethodOn(a,"before:render",a),a.render(),a.supportsRenderLifecycle||f.triggerMethodOn(a,"render",a)},_ensureElement:function(){if(c.isObject(this.el)||(this.$el=this.getEl(this.el),this.el=this.$el[0]),!this.$el||0===this.$el.length){if(this.getOption("allowMissingEl"))return!1;throw new f.Error('An "el" '+this.$el.selector+" must exist in DOM")}return!0},_ensureViewIsIntact:function(a){if(!a)throw new f.Error({name:"ViewNotValid",message:"The view passed is undefined and therefore invalid. You must pass a view instance to show."});if(a.isDestroyed)throw new f.Error({name:"ViewDestroyedError",message:'View (cid: "'+a.cid+'") has already been destroyed and cannot be used.'})},getEl:function(a){return b.$(a,f._getValue(this.options.parentEl,this))},attachHtml:function(a){this.$el.contents().detach(),this.el.appendChild(a.el)},empty:function(a){var b=this.currentView,c=a||{},d=!!c.preventDestroy;return b?(b.off("destroy",this.empty,this),this.triggerMethod("before:empty",b),d||this._destroyView(),this.triggerMethod("empty",b),delete this.currentView,d&&this.$el.contents().detach(),this):this},_destroyView:function(){var a=this.currentView;a.isDestroyed||(a.supportsDestroyLifecycle||f.triggerMethodOn(a,"before:destroy",a),a.destroy?a.destroy():(a.remove(),a.isDestroyed=!0),a.supportsDestroyLifecycle||f.triggerMethodOn(a,"destroy",a))},attachView:function(a){return this.currentView&&delete this.currentView._parent,a._parent=this,this.currentView=a,this},hasView:function(){return!!this.currentView},reset:function(){return this.empty(),this.$el&&(this.el=this.options.el),delete this.$el,this}},{buildRegion:function(a,b){if(c.isString(a))return this._buildRegionFromSelector(a,b);if(a.selector||a.el||a.regionClass)return this._buildRegionFromObject(a,b);if(c.isFunction(a))return this._buildRegionFromRegionClass(a);throw new f.Error({message:"Improper region configuration type.",url:"marionette.region.html#region-configuration-types"})},_buildRegionFromSelector:function(a,b){return new b({el:a})},_buildRegionFromObject:function(a,b){var d=a.regionClass||b,e=c.omit(a,"selector","regionClass");return a.selector&&!e.el&&(e.el=a.selector),new d(e)},_buildRegionFromRegionClass:function(a){return new a}}),f.RegionManager=f.Controller.extend({constructor:function(a){this._regions={},this.length=0,f.Controller.call(this,a),this.addRegions(this.getOption("regions"))},addRegions:function(a,b){return a=f._getValue(a,this,arguments),c.reduce(a,function(a,d,e){return c.isString(d)&&(d={selector:d}),d.selector&&(d=c.defaults({},d,b)),a[e]=this.addRegion(e,d),a},{},this)},addRegion:function(a,b){var c;return c=b instanceof f.Region?b:f.Region.buildRegion(b,f.Region),this.triggerMethod("before:add:region",a,c),c._parent=this,this._store(a,c),this.triggerMethod("add:region",a,c),c},get:function(a){return this._regions[a]},getRegions:function(){return c.clone(this._regions)},removeRegion:function(a){var b=this._regions[a];return this._remove(a,b),b},removeRegions:function(){var a=this.getRegions();return c.each(this._regions,function(a,b){this._remove(b,a)},this),a},emptyRegions:function(){var a=this.getRegions();return c.invoke(a,"empty"),a},destroy:function(){return this.removeRegions(),f.Controller.prototype.destroy.apply(this,arguments)},_store:function(a,b){this._regions[a]||this.length++,this._regions[a]=b},_remove:function(a,b){this.triggerMethod("before:remove:region",a,b),b.empty(),b.stopListening(),delete b._parent,delete this._regions[a],this.length--,this.triggerMethod("remove:region",a,b)}}),f.actAsCollection(f.RegionManager.prototype,"_regions"),f.TemplateCache=function(a){this.templateId=a},c.extend(f.TemplateCache,{templateCaches:{},get:function(a,b){var c=this.templateCaches[a];return c||(c=new f.TemplateCache(a),this.templateCaches[a]=c),c.load(b)},clear:function(){var a,b=c.toArray(arguments),d=b.length;if(d>0)for(a=0;d>a;a++)delete this.templateCaches[b[a]];else this.templateCaches={}}}),c.extend(f.TemplateCache.prototype,{load:function(a){if(this.compiledTemplate)return this.compiledTemplate;var b=this.loadTemplate(this.templateId,a);return this.compiledTemplate=this.compileTemplate(b,a),this.compiledTemplate},loadTemplate:function(a,c){var d=b.$(a);if(!d.length)throw new f.Error({name:"NoTemplateError",message:'Could not find template: "'+a+'"'});return d.html()},compileTemplate:function(a,b){return c.template(a,b)}}),f.Renderer={render:function(a,b){if(!a)throw new f.Error({name:"TemplateNotFoundError",message:"Cannot render the template since its false, null or undefined."});var d=c.isFunction(a)?a:f.TemplateCache.get(a);return d(b)}},f.View=b.View.extend({isDestroyed:!1,supportsRenderLifecycle:!0,supportsDestroyLifecycle:!0,constructor:function(a){this.render=c.bind(this.render,this),a=f._getValue(a,this),this.options=c.extend({},c.result(this,"options"),a),this._behaviors=f.Behaviors(this),b.View.call(this,this.options),f.MonitorDOMRefresh(this)},getTemplate:function(){return this.getOption("template")},serializeModel:function(a){return a.toJSON.apply(a,c.rest(arguments))},mixinTemplateHelpers:function(a){a=a||{};var b=this.getOption("templateHelpers");return b=f._getValue(b,this),c.extend(a,b)},normalizeUIKeys:function(a){var b=c.result(this,"_uiBindings");return f.normalizeUIKeys(a,b||c.result(this,"ui"))},normalizeUIValues:function(a,b){var d=c.result(this,"ui"),e=c.result(this,"_uiBindings");return f.normalizeUIValues(a,e||d,b)},configureTriggers:function(){if(this.triggers){var a=this.normalizeUIKeys(c.result(this,"triggers"));return c.reduce(a,function(a,b,c){return a[c]=this._buildViewTrigger(b),a},{},this)}},delegateEvents:function(a){return this._delegateDOMEvents(a),this.bindEntityEvents(this.model,this.getOption("modelEvents")),this.bindEntityEvents(this.collection,this.getOption("collectionEvents")),c.each(this._behaviors,function(a){a.bindEntityEvents(this.model,a.getOption("modelEvents")),a.bindEntityEvents(this.collection,a.getOption("collectionEvents"))},this),this},_delegateDOMEvents:function(a){var d=f._getValue(a||this.events,this);d=this.normalizeUIKeys(d),c.isUndefined(a)&&(this.events=d);var e={},g=c.result(this,"behaviorEvents")||{},h=this.configureTriggers(),i=c.result(this,"behaviorTriggers")||{};c.extend(e,g,d,h,i),b.View.prototype.delegateEvents.call(this,e)},undelegateEvents:function(){return b.View.prototype.undelegateEvents.apply(this,arguments),this.unbindEntityEvents(this.model,this.getOption("modelEvents")),this.unbindEntityEvents(this.collection,this.getOption("collectionEvents")),c.each(this._behaviors,function(a){a.unbindEntityEvents(this.model,a.getOption("modelEvents")),a.unbindEntityEvents(this.collection,a.getOption("collectionEvents"))},this),this},_ensureViewIsIntact:function(){if(this.isDestroyed)throw new f.Error({name:"ViewDestroyedError",message:'View (cid: "'+this.cid+'") has already been destroyed and cannot be used.'})},destroy:function(){if(this.isDestroyed)return this;var a=c.toArray(arguments);return this.triggerMethod.apply(this,["before:destroy"].concat(a)),this.isDestroyed=!0,this.triggerMethod.apply(this,["destroy"].concat(a)),this.unbindUIElements(),this.isRendered=!1,this.remove(),c.invoke(this._behaviors,"destroy",a),this},bindUIElements:function(){this._bindUIElements(),c.invoke(this._behaviors,this._bindUIElements)},_bindUIElements:function(){if(this.ui){this._uiBindings||(this._uiBindings=this.ui);var a=c.result(this,"_uiBindings");this.ui={},c.each(a,function(a,b){this.ui[b]=this.$(a)},this)}},unbindUIElements:function(){this._unbindUIElements(),c.invoke(this._behaviors,this._unbindUIElements)},_unbindUIElements:function(){this.ui&&this._uiBindings&&(c.each(this.ui,function(a,b){delete this.ui[b]},this),this.ui=this._uiBindings,delete this._uiBindings)},_buildViewTrigger:function(a){var b=c.defaults({},a,{preventDefault:!0,stopPropagation:!0}),d=c.isObject(a)?b.event:a;return function(a){a&&(a.preventDefault&&b.preventDefault&&a.preventDefault(),a.stopPropagation&&b.stopPropagation&&a.stopPropagation());var c={view:this,model:this.model,collection:this.collection};this.triggerMethod(d,c)}},setElement:function(){var a=b.View.prototype.setElement.apply(this,arguments);return c.invoke(this._behaviors,"proxyViewProperties",this),a},triggerMethod:function(){var a=f._triggerMethod(this,arguments);return this._triggerEventOnBehaviors(arguments),this._triggerEventOnParentLayout(arguments[0],c.rest(arguments)),a},_triggerEventOnBehaviors:function(a){for(var b=f._triggerMethod,c=this._behaviors,d=0,e=c&&c.length;e>d;d++)b(c[d],a)},_triggerEventOnParentLayout:function(a,b){var d=this._parentLayoutView();if(d){var e=f.getOption(d,"childViewEventPrefix"),g=e+":"+a,h=[this].concat(b);f._triggerMethod(d,g,h);var i=f.getOption(d,"childEvents");i=f._getValue(i,d);var j=d.normalizeMethods(i);j&&c.isFunction(j[a])&&j[a].apply(d,h)}},_getImmediateChildren:function(){return[]},_getNestedViews:function(){var a=this._getImmediateChildren();return a.length?c.reduce(a,function(a,b){return b._getNestedViews?a.concat(b._getNestedViews()):a},a):a},_parentLayoutView:function(){for(var a=this._parent;a;){if(a instanceof f.LayoutView)return a;a=a._parent}},normalizeMethods:f.normalizeMethods,mergeOptions:f.mergeOptions,getOption:f.proxyGetOption,bindEntityEvents:f.proxyBindEntityEvents,unbindEntityEvents:f.proxyUnbindEntityEvents}),f.ItemView=f.View.extend({constructor:function(){f.View.apply(this,arguments)},serializeData:function(){if(!this.model&&!this.collection)return{};var a=[this.model||this.collection];return arguments.length&&a.push.apply(a,arguments),this.model?this.serializeModel.apply(this,a):{items:this.serializeCollection.apply(this,a)}},serializeCollection:function(a){return a.toJSON.apply(a,c.rest(arguments))},render:function(){return this._ensureViewIsIntact(),this.triggerMethod("before:render",this),this._renderTemplate(),this.isRendered=!0,this.bindUIElements(),this.triggerMethod("render",this),this},_renderTemplate:function(){var a=this.getTemplate();if(a!==!1){if(!a)throw new f.Error({name:"UndefinedTemplateError",message:"Cannot render the template since it is null or undefined."});var b=this.mixinTemplateHelpers(this.serializeData()),c=f.Renderer.render(a,b,this);return this.attachElContent(c),this}},attachElContent:function(a){return this.$el.html(a),this}}),f.CollectionView=f.View.extend({childViewEventPrefix:"childview",sort:!0,constructor:function(a){this.once("render",this._initialEvents),this._initChildViewStorage(),f.View.apply(this,arguments),this.on({"before:show":this._onBeforeShowCalled,show:this._onShowCalled,"before:attach":this._onBeforeAttachCalled,attach:this._onAttachCalled}),this.initRenderBuffer()},initRenderBuffer:function(){this._bufferedChildren=[]},startBuffering:function(){this.initRenderBuffer(),this.isBuffering=!0},endBuffering:function(){var a,b=this._isShown&&f.isNodeAttached(this.el);this.isBuffering=!1,this._isShown&&this._triggerMethodMany(this._bufferedChildren,this,"before:show"),b&&this._triggerBeforeAttach&&(a=this._getNestedViews(),this._triggerMethodMany(a,this,"before:attach")),this.attachBuffer(this,this._createBuffer()),b&&this._triggerAttach&&(a=this._getNestedViews(),this._triggerMethodMany(a,this,"attach")),this._isShown&&this._triggerMethodMany(this._bufferedChildren,this,"show"),this.initRenderBuffer()},_triggerMethodMany:function(a,b,d){var e=c.drop(arguments,3);c.each(a,function(a){f.triggerMethodOn.apply(a,[a,d,a,b].concat(e))})},_initialEvents:function(){this.collection&&(this.listenTo(this.collection,"add",this._onCollectionAdd),this.listenTo(this.collection,"remove",this._onCollectionRemove),this.listenTo(this.collection,"reset",this.render),this.getOption("sort")&&this.listenTo(this.collection,"sort",this._sortViews))},_onCollectionAdd:function(a,b,d){var e=void 0!==d.at&&(d.index||b.indexOf(a));if((this.getOption("filter")||e===!1)&&(e=c.indexOf(this._filteredSortedModels(e),a)),this._shouldAddChild(a,e)){this.destroyEmptyView();var f=this.getChildView(a);this.addChild(a,f,e)}},_onCollectionRemove:function(a){var b=this.children.findByModel(a);this.removeChildView(b),this.checkEmpty()},_onBeforeShowCalled:function(){this._triggerBeforeAttach=this._triggerAttach=!1,this.children.each(function(a){f.triggerMethodOn(a,"before:show",a)})},_onShowCalled:function(){this.children.each(function(a){f.triggerMethodOn(a,"show",a)})},_onBeforeAttachCalled:function(){this._triggerBeforeAttach=!0},_onAttachCalled:function(){this._triggerAttach=!0},render:function(){return this._ensureViewIsIntact(),this.triggerMethod("before:render",this),this._renderChildren(),this.isRendered=!0,this.triggerMethod("render",this),this},reorder:function(){var a=this.children,b=this._filteredSortedModels(),d=c.some(b,function(b){return!a.findByModel(b)});if(d)this.render();else{var e=c.map(b,function(b,c){var d=a.findByModel(b);return d._index=c,d.el}),f=a.filter(function(a){return!c.contains(e,a.el)});this.triggerMethod("before:reorder"),this._appendReorderedChildren(e),c.each(f,this.removeChildView,this),this.checkEmpty(),this.triggerMethod("reorder")}},resortView:function(){f.getOption(this,"reorderOnSort")?this.reorder():this.render()},_sortViews:function(){var a=this._filteredSortedModels(),b=c.find(a,function(a,b){var c=this.children.findByModel(a);return!c||c._index!==b},this);b&&this.resortView()},_emptyViewIndex:-1,_appendReorderedChildren:function(a){this.$el.append(a)},_renderChildren:function(){this.destroyEmptyView(),this.destroyChildren({checkEmpty:!1}),this.isEmpty(this.collection)?this.showEmptyView():(this.triggerMethod("before:render:collection",this),this.startBuffering(),this.showCollection(),this.endBuffering(),this.triggerMethod("render:collection",this),this.children.isEmpty()&&this.getOption("filter")&&this.showEmptyView())},showCollection:function(){var a,b=this._filteredSortedModels();c.each(b,function(b,c){a=this.getChildView(b),this.addChild(b,a,c)},this)},_filteredSortedModels:function(a){var b=this.getViewComparator(),d=this.collection.models;if(a=Math.min(Math.max(a,0),d.length-1),b){var e;a&&(e=d[a],d=d.slice(0,a).concat(d.slice(a+1))),d=this._sortModelsBy(d,b),e&&d.splice(a,0,e)}return this.getOption("filter")&&(d=c.filter(d,function(a,b){return this._shouldAddChild(a,b)},this)),d},_sortModelsBy:function(a,b){return"string"==typeof b?c.sortBy(a,function(a){return a.get(b)},this):1===b.length?c.sortBy(a,b,this):a.sort(c.bind(b,this))},showEmptyView:function(){var a=this.getEmptyView();if(a&&!this._showingEmptyView){this.triggerMethod("before:render:empty"),this._showingEmptyView=!0;var c=new b.Model;this.addEmptyView(c,a),this.triggerMethod("render:empty")}},destroyEmptyView:function(){this._showingEmptyView&&(this.triggerMethod("before:remove:empty"),this.destroyChildren(),delete this._showingEmptyView,this.triggerMethod("remove:empty"))},getEmptyView:function(){return this.getOption("emptyView")},addEmptyView:function(a,b){var d,e=this._isShown&&!this.isBuffering&&f.isNodeAttached(this.el),g=this.getOption("emptyViewOptions")||this.getOption("childViewOptions");c.isFunction(g)&&(g=g.call(this,a,this._emptyViewIndex));var h=this.buildChildView(a,b,g);h._parent=this,this.proxyChildEvents(h),h.once("render",function(){this._isShown&&f.triggerMethodOn(h,"before:show",h),e&&this._triggerBeforeAttach&&(d=this._getViewAndNested(h),this._triggerMethodMany(d,this,"before:attach"))},this),this.children.add(h),this.renderChildView(h,this._emptyViewIndex),e&&this._triggerAttach&&(d=this._getViewAndNested(h),this._triggerMethodMany(d,this,"attach")),this._isShown&&f.triggerMethodOn(h,"show",h)},getChildView:function(a){var b=this.getOption("childView");if(!b)throw new f.Error({name:"NoChildViewError",message:'A "childView" must be specified'});return b},addChild:function(a,b,c){var d=this.getOption("childViewOptions");d=f._getValue(d,this,[a,c]);var e=this.buildChildView(a,b,d);return this._updateIndices(e,!0,c),this.triggerMethod("before:add:child",e),this._addChildView(e,c),this.triggerMethod("add:child",e),e._parent=this,e},_updateIndices:function(a,b,c){this.getOption("sort")&&(b&&(a._index=c),this.children.each(function(c){c._index>=a._index&&(c._index+=b?1:-1)}))},_addChildView:function(a,b){var c,d=this._isShown&&!this.isBuffering&&f.isNodeAttached(this.el);this.proxyChildEvents(a),a.once("render",function(){this._isShown&&!this.isBuffering&&f.triggerMethodOn(a,"before:show",a),d&&this._triggerBeforeAttach&&(c=this._getViewAndNested(a),this._triggerMethodMany(c,this,"before:attach"))},this),this.children.add(a),this.renderChildView(a,b),d&&this._triggerAttach&&(c=this._getViewAndNested(a),this._triggerMethodMany(c,this,"attach")),this._isShown&&!this.isBuffering&&f.triggerMethodOn(a,"show",a)},renderChildView:function(a,b){return a.supportsRenderLifecycle||f.triggerMethodOn(a,"before:render",a),a.render(),a.supportsRenderLifecycle||f.triggerMethodOn(a,"render",a),this.attachHtml(this,a,b),a},buildChildView:function(a,b,d){var e=c.extend({model:a},d),g=new b(e);return f.MonitorDOMRefresh(g),g},removeChildView:function(a){return a?(this.triggerMethod("before:remove:child",a),a.supportsDestroyLifecycle||f.triggerMethodOn(a,"before:destroy",a),a.destroy?a.destroy():a.remove(),a.supportsDestroyLifecycle||f.triggerMethodOn(a,"destroy",a),delete a._parent,this.stopListening(a),this.children.remove(a),this.triggerMethod("remove:child",a),this._updateIndices(a,!1),a):a},isEmpty:function(){return!this.collection||0===this.collection.length},checkEmpty:function(){this.isEmpty(this.collection)&&this.showEmptyView()},attachBuffer:function(a,b){a.$el.append(b)},_createBuffer:function(){var a=document.createDocumentFragment();return c.each(this._bufferedChildren,function(b){a.appendChild(b.el)}),a},attachHtml:function(a,b,c){a.isBuffering?a._bufferedChildren.splice(c,0,b):a._insertBefore(b,c)||a._insertAfter(b)},_insertBefore:function(a,b){var c,d=this.getOption("sort")&&b<this.children.length-1;return d&&(c=this.children.find(function(a){return a._index===b+1})),c?(c.$el.before(a.el),!0):!1},_insertAfter:function(a){this.$el.append(a.el)},_initChildViewStorage:function(){this.children=new b.ChildViewContainer},destroy:function(){return this.isDestroyed?this:(this.triggerMethod("before:destroy:collection"),this.destroyChildren({checkEmpty:!1}),this.triggerMethod("destroy:collection"),f.View.prototype.destroy.apply(this,arguments))},destroyChildren:function(a){var b=a||{},d=!0,e=this.children.map(c.identity);return c.isUndefined(b.checkEmpty)||(d=b.checkEmpty),this.children.each(this.removeChildView,this),d&&this.checkEmpty(),e},_shouldAddChild:function(a,b){var d=this.getOption("filter");return!c.isFunction(d)||d.call(this,a,b,this.collection)},proxyChildEvents:function(a){var b=this.getOption("childViewEventPrefix");this.listenTo(a,"all",function(){var d=c.toArray(arguments),e=d[0],f=this.normalizeMethods(c.result(this,"childEvents")); d[0]=b+":"+e,d.splice(1,0,a),"undefined"!=typeof f&&c.isFunction(f[e])&&f[e].apply(this,d.slice(1)),this.triggerMethod.apply(this,d)})},_getImmediateChildren:function(){return c.values(this.children._views)},_getViewAndNested:function(a){return[a].concat(c.result(a,"_getNestedViews")||[])},getViewComparator:function(){return this.getOption("viewComparator")}}),f.CompositeView=f.CollectionView.extend({constructor:function(){f.CollectionView.apply(this,arguments)},_initialEvents:function(){this.collection&&(this.listenTo(this.collection,"add",this._onCollectionAdd),this.listenTo(this.collection,"remove",this._onCollectionRemove),this.listenTo(this.collection,"reset",this._renderChildren),this.getOption("sort")&&this.listenTo(this.collection,"sort",this._sortViews))},getChildView:function(a){var b=this.getOption("childView")||this.constructor;return b},serializeData:function(){var a={};return this.model&&(a=c.partial(this.serializeModel,this.model).apply(this,arguments)),a},render:function(){return this._ensureViewIsIntact(),this._isRendering=!0,this.resetChildViewContainer(),this.triggerMethod("before:render",this),this._renderTemplate(),this._renderChildren(),this._isRendering=!1,this.isRendered=!0,this.triggerMethod("render",this),this},_renderChildren:function(){(this.isRendered||this._isRendering)&&f.CollectionView.prototype._renderChildren.call(this)},_renderTemplate:function(){var a={};a=this.serializeData(),a=this.mixinTemplateHelpers(a),this.triggerMethod("before:render:template");var b=this.getTemplate(),c=f.Renderer.render(b,a,this);this.attachElContent(c),this.bindUIElements(),this.triggerMethod("render:template")},attachElContent:function(a){return this.$el.html(a),this},attachBuffer:function(a,b){var c=this.getChildViewContainer(a);c.append(b)},_insertAfter:function(a){var b=this.getChildViewContainer(this,a);b.append(a.el)},_appendReorderedChildren:function(a){var b=this.getChildViewContainer(this);b.append(a)},getChildViewContainer:function(a,b){if(a.$childViewContainer)return a.$childViewContainer;var c,d=f.getOption(a,"childViewContainer");if(d){var e=f._getValue(d,a);if(c="@"===e.charAt(0)&&a.ui?a.ui[e.substr(4)]:a.$(e),c.length<=0)throw new f.Error({name:"ChildViewContainerMissingError",message:'The specified "childViewContainer" was not found: '+a.childViewContainer})}else c=a.$el;return a.$childViewContainer=c,c},resetChildViewContainer:function(){this.$childViewContainer&&(this.$childViewContainer=void 0)}}),f.LayoutView=f.ItemView.extend({regionClass:f.Region,options:{destroyImmediate:!1},childViewEventPrefix:"childview",constructor:function(a){a=a||{},this._firstRender=!0,this._initializeRegions(a),f.ItemView.call(this,a)},render:function(){return this._ensureViewIsIntact(),this._firstRender?this._firstRender=!1:this._reInitializeRegions(),f.ItemView.prototype.render.apply(this,arguments)},destroy:function(){return this.isDestroyed?this:(this.getOption("destroyImmediate")===!0&&this.$el.remove(),this.regionManager.destroy(),f.ItemView.prototype.destroy.apply(this,arguments))},showChildView:function(a,b,d){var e=this.getRegion(a);return e.show.apply(e,c.rest(arguments))},getChildView:function(a){return this.getRegion(a).currentView},addRegion:function(a,b){var c={};return c[a]=b,this._buildRegions(c)[a]},addRegions:function(a){return this.regions=c.extend({},this.regions,a),this._buildRegions(a)},removeRegion:function(a){return delete this.regions[a],this.regionManager.removeRegion(a)},getRegion:function(a){return this.regionManager.get(a)},getRegions:function(){return this.regionManager.getRegions()},_buildRegions:function(a){var b={regionClass:this.getOption("regionClass"),parentEl:c.partial(c.result,this,"el")};return this.regionManager.addRegions(a,b)},_initializeRegions:function(a){var b;this._initRegionManager(),b=f._getValue(this.regions,this,[a])||{};var d=this.getOption.call(a,"regions");d=f._getValue(d,this,[a]),c.extend(b,d),b=this.normalizeUIValues(b,["selector","el"]),this.addRegions(b)},_reInitializeRegions:function(){this.regionManager.invoke("reset")},getRegionManager:function(){return new f.RegionManager},_initRegionManager:function(){this.regionManager=this.getRegionManager(),this.regionManager._parent=this,this.listenTo(this.regionManager,"before:add:region",function(a){this.triggerMethod("before:add:region",a)}),this.listenTo(this.regionManager,"add:region",function(a,b){this[a]=b,this.triggerMethod("add:region",a,b)}),this.listenTo(this.regionManager,"before:remove:region",function(a){this.triggerMethod("before:remove:region",a)}),this.listenTo(this.regionManager,"remove:region",function(a,b){delete this[a],this.triggerMethod("remove:region",a,b)})},_getImmediateChildren:function(){return c.chain(this.regionManager.getRegions()).pluck("currentView").compact().value()}}),f.Behavior=f.Object.extend({constructor:function(a,b){this.view=b,this.defaults=c.result(this,"defaults")||{},this.options=c.extend({},this.defaults,a),this.ui=c.extend({},c.result(b,"ui"),c.result(this,"ui")),f.Object.apply(this,arguments)},$:function(){return this.view.$.apply(this.view,arguments)},destroy:function(){return this.stopListening(),this},proxyViewProperties:function(a){this.$el=a.$el,this.el=a.el}}),f.Behaviors=function(a,b){function c(a,d){return b.isObject(a.behaviors)?(d=c.parseBehaviors(a,d||b.result(a,"behaviors")),c.wrap(a,d,b.keys(g)),d):{}}function d(a,b){this._view=a,this._behaviors=b,this._triggers={}}function e(a){return a._uiBindings||a.ui}var f=/^(\S+)\s*(.*)$/,g={behaviorTriggers:function(a,b){var c=new d(this,b);return c.buildBehaviorTriggers()},behaviorEvents:function(c,d){var g={};return b.each(d,function(c,d){var h={},i=b.clone(b.result(c,"events"))||{};i=a.normalizeUIKeys(i,e(c));var j=0;b.each(i,function(a,e){var g=e.match(f),i=g[1]+"."+[this.cid,d,j++," "].join(""),k=g[2],l=i+k,m=b.isFunction(a)?a:c[a];m&&(h[l]=b.bind(m,c))},this),g=b.extend(g,h)},this),g}};return b.extend(c,{behaviorsLookup:function(){throw new a.Error({message:"You must define where your behaviors are stored.",url:"marionette.behaviors.html#behaviorslookup"})},getBehaviorClass:function(b,d){return b.behaviorClass?b.behaviorClass:a._getValue(c.behaviorsLookup,this,[b,d])[d]},parseBehaviors:function(a,d){return b.chain(d).map(function(d,e){var f=c.getBehaviorClass(d,e),g=new f(d,a),h=c.parseBehaviors(a,b.result(g,"behaviors"));return[g].concat(h)}).flatten().value()},wrap:function(a,c,d){b.each(d,function(d){a[d]=b.partial(g[d],a[d],c)})}}),b.extend(d.prototype,{buildBehaviorTriggers:function(){return b.each(this._behaviors,this._buildTriggerHandlersForBehavior,this),this._triggers},_buildTriggerHandlersForBehavior:function(c,d){var f=b.clone(b.result(c,"triggers"))||{};f=a.normalizeUIKeys(f,e(c)),b.each(f,b.bind(this._setHandlerForBehavior,this,c,d))},_setHandlerForBehavior:function(a,b,c,d){var e=d.replace(/^\S+/,function(a){return a+".behaviortriggers"+b});this._triggers[e]=this._view._buildViewTrigger(c)}}),c}(f,c),f.AppRouter=b.Router.extend({constructor:function(a){this.options=a||{},b.Router.apply(this,arguments);var c=this.getOption("appRoutes"),d=this._getController();this.processAppRoutes(d,c),this.on("route",this._processOnRoute,this)},appRoute:function(a,b){var c=this._getController();this._addAppRoute(c,a,b)},_processOnRoute:function(a,b){if(c.isFunction(this.onRoute)){var d=c.invert(this.getOption("appRoutes"))[a];this.onRoute(a,d,b)}},processAppRoutes:function(a,b){if(b){var d=c.keys(b).reverse();c.each(d,function(c){this._addAppRoute(a,c,b[c])},this)}},_getController:function(){return this.getOption("controller")},_addAppRoute:function(a,b,d){var e=a[d];if(!e)throw new f.Error('Method "'+d+'" was not found on the controller');this.route(b,d,c.bind(e,a))},mergeOptions:f.mergeOptions,getOption:f.proxyGetOption,triggerMethod:f.triggerMethod,bindEntityEvents:f.proxyBindEntityEvents,unbindEntityEvents:f.proxyUnbindEntityEvents}),f.Application=f.Object.extend({constructor:function(a){this._initializeRegions(a),this._initCallbacks=new f.Callbacks,this.submodules={},c.extend(this,a),this._initChannel(),f.Object.apply(this,arguments)},execute:function(){this.commands.execute.apply(this.commands,arguments)},request:function(){return this.reqres.request.apply(this.reqres,arguments)},addInitializer:function(a){this._initCallbacks.add(a)},start:function(a){this.triggerMethod("before:start",a),this._initCallbacks.run(a,this),this.triggerMethod("start",a)},addRegions:function(a){return this._regionManager.addRegions(a)},emptyRegions:function(){return this._regionManager.emptyRegions()},removeRegion:function(a){return this._regionManager.removeRegion(a)},getRegion:function(a){return this._regionManager.get(a)},getRegions:function(){return this._regionManager.getRegions()},module:function(a,b){var d=f.Module.getClass(b),e=c.toArray(arguments);return e.unshift(this),d.create.apply(d,e)},getRegionManager:function(){return new f.RegionManager},_initializeRegions:function(a){var b=c.isFunction(this.regions)?this.regions(a):this.regions||{};this._initRegionManager();var d=f.getOption(a,"regions");return c.isFunction(d)&&(d=d.call(this,a)),c.extend(b,d),this.addRegions(b),this},_initRegionManager:function(){this._regionManager=this.getRegionManager(),this._regionManager._parent=this,this.listenTo(this._regionManager,"before:add:region",function(){f._triggerMethod(this,"before:add:region",arguments)}),this.listenTo(this._regionManager,"add:region",function(a,b){this[a]=b,f._triggerMethod(this,"add:region",arguments)}),this.listenTo(this._regionManager,"before:remove:region",function(){f._triggerMethod(this,"before:remove:region",arguments)}),this.listenTo(this._regionManager,"remove:region",function(a){delete this[a],f._triggerMethod(this,"remove:region",arguments)})},_initChannel:function(){this.channelName=c.result(this,"channelName")||"global",this.channel=c.result(this,"channel")||b.Wreqr.radio.channel(this.channelName),this.vent=c.result(this,"vent")||this.channel.vent,this.commands=c.result(this,"commands")||this.channel.commands,this.reqres=c.result(this,"reqres")||this.channel.reqres}}),f.Module=function(a,b,d){this.moduleName=a,this.options=c.extend({},this.options,d),this.initialize=d.initialize||this.initialize,this.submodules={},this._setupInitializersAndFinalizers(),this.app=b,c.isFunction(this.initialize)&&this.initialize(a,b,this.options)},f.Module.extend=f.extend,c.extend(f.Module.prototype,b.Events,{startWithParent:!0,initialize:function(){},addInitializer:function(a){this._initializerCallbacks.add(a)},addFinalizer:function(a){this._finalizerCallbacks.add(a)},start:function(a){this._isInitialized||(c.each(this.submodules,function(b){b.startWithParent&&b.start(a)}),this.triggerMethod("before:start",a),this._initializerCallbacks.run(a,this),this._isInitialized=!0,this.triggerMethod("start",a))},stop:function(){this._isInitialized&&(this._isInitialized=!1,this.triggerMethod("before:stop"),c.invoke(this.submodules,"stop"),this._finalizerCallbacks.run(void 0,this),this._initializerCallbacks.reset(),this._finalizerCallbacks.reset(),this.triggerMethod("stop"))},addDefinition:function(a,b){this._runModuleDefinition(a,b)},_runModuleDefinition:function(a,d){if(a){var e=c.flatten([this,this.app,b,f,b.$,c,d]);a.apply(this,e)}},_setupInitializersAndFinalizers:function(){this._initializerCallbacks=new f.Callbacks,this._finalizerCallbacks=new f.Callbacks},triggerMethod:f.triggerMethod}),c.extend(f.Module,{create:function(a,b,d){var e=a,f=c.drop(arguments,3);b=b.split(".");var g=b.length,h=[];return h[g-1]=d,c.each(b,function(b,c){var g=e;e=this._getModule(g,b,a,d),this._addModuleDefinition(g,e,h[c],f)},this),e},_getModule:function(a,b,d,e,f){var g=c.extend({},e),h=this.getClass(e),i=a[b];return i||(i=new h(b,d,g),a[b]=i,a.submodules[b]=i),i},getClass:function(a){var b=f.Module;return a?a.prototype instanceof b?a:a.moduleClass||b:b},_addModuleDefinition:function(a,b,c,d){var e=this._getDefine(c),f=this._getStartWithParent(c,b);e&&b.addDefinition(e,d),this._addStartWithParent(a,b,f)},_getStartWithParent:function(a,b){var d;return c.isFunction(a)&&a.prototype instanceof f.Module?(d=b.constructor.prototype.startWithParent,c.isUndefined(d)?!0:d):c.isObject(a)?(d=a.startWithParent,c.isUndefined(d)?!0:d):!0},_getDefine:function(a){return!c.isFunction(a)||a.prototype instanceof f.Module?c.isObject(a)?a.define:null:a},_addStartWithParent:function(a,b,c){b.startWithParent=b.startWithParent&&c,b.startWithParent&&!b.startWithParentIsConfigured&&(b.startWithParentIsConfigured=!0,a.addInitializer(function(a){b.startWithParent&&b.start(a)}))}}),f});
| ver. 1.4 |
Github
|
.
| PHP 7.4.33 | Generation time: 0 |
proxy
|
phpinfo
|
Settings