// Backbone.Wreqr (Backbone.Marionette) // ---------------------------------- // v1.4.0 // // Copyright (c)2016 Derick Bailey, Muted Solutions, LLC. // Distributed under MIT license // // http://github.com/marionettejs/backbone.wreqr (function(root, factory) { if (typeof define === 'function' && define.amd) { define(['backbone', 'underscore'], function(Backbone, _) { return factory(Backbone, _); }); } else if (typeof exports !== 'undefined') { var Backbone = require('backbone'); var _ = require('underscore'); module.exports = factory(Backbone, _); } else { factory(root.Backbone, root._); } }(this, function(Backbone, _) { "use strict"; var previousWreqr = Backbone.Wreqr; var Wreqr = Backbone.Wreqr = {}; Backbone.Wreqr.VERSION = '1.4.0'; 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, _.bind(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, _.bind(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'], _.bind(function(system) { _.each( messageSystems[system], _.bind(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; }));