| // Backbone.Wreqr (Backbone.Marionette) |
| // ---------------------------------- |
| // v1.3.1 |
| // |
| // Copyright (c)2014 Derick Bailey, Muted Solutions, LLC. |
| // Distributed under MIT license |
| // |
| // http://github.com/marionettejs/backbone.wreqr |
| |
| |
| (function(root, factory) { |
| |
| if (typeof define === 'function' && define.amd) { |
| define(['backbone', 'underscore'], function(Backbone, _) { |
| return factory(Backbone, _); |
| }); |
| } else if (typeof exports !== 'undefined') { |
| var Backbone = require('backbone'); |
| var _ = require('underscore'); |
| module.exports = factory(Backbone, _); |
| } else { |
| factory(root.Backbone, root._); |
| } |
| |
| }(this, function(Backbone, _) { |
| "use strict"; |
| |
| var previousWreqr = Backbone.Wreqr; |
| |
| var Wreqr = Backbone.Wreqr = {}; |
| |
| Backbone.Wreqr.VERSION = '1.3.1'; |
| |
| Backbone.Wreqr.noConflict = function () { |
| Backbone.Wreqr = previousWreqr; |
| return this; |
| }; |
| |
| // Handlers |
| // -------- |
| // A registry of functions to call, given a name |
| |
| Wreqr.Handlers = (function(Backbone, _){ |
| "use strict"; |
| |
| // Constructor |
| // ----------- |
| |
| var Handlers = function(options){ |
| this.options = options; |
| this._wreqrHandlers = {}; |
| |
| if (_.isFunction(this.initialize)){ |
| this.initialize(options); |
| } |
| }; |
| |
| Handlers.extend = Backbone.Model.extend; |
| |
| // Instance Members |
| // ---------------- |
| |
| _.extend(Handlers.prototype, Backbone.Events, { |
| |
| // Add multiple handlers using an object literal configuration |
| setHandlers: function(handlers){ |
| _.each(handlers, function(handler, name){ |
| var context = null; |
| |
| if (_.isObject(handler) && !_.isFunction(handler)){ |
| context = handler.context; |
| handler = handler.callback; |
| } |
| |
| this.setHandler(name, handler, context); |
| }, this); |
| }, |
| |
| // Add a handler for the given name, with an |
| // optional context to run the handler within |
| setHandler: function(name, handler, context){ |
| var config = { |
| callback: handler, |
| context: context |
| }; |
| |
| this._wreqrHandlers[name] = config; |
| |
| this.trigger("handler:add", name, handler, context); |
| }, |
| |
| // Determine whether or not a handler is registered |
| hasHandler: function(name){ |
| return !! this._wreqrHandlers[name]; |
| }, |
| |
| // Get the currently registered handler for |
| // the specified name. Throws an exception if |
| // no handler is found. |
| getHandler: function(name){ |
| var config = this._wreqrHandlers[name]; |
| |
| if (!config){ |
| return; |
| } |
| |
| return function(){ |
| var args = Array.prototype.slice.apply(arguments); |
| return config.callback.apply(config.context, args); |
| }; |
| }, |
| |
| // Remove a handler for the specified name |
| removeHandler: function(name){ |
| delete this._wreqrHandlers[name]; |
| }, |
| |
| // Remove all handlers from this registry |
| removeAllHandlers: function(){ |
| this._wreqrHandlers = {}; |
| } |
| }); |
| |
| return Handlers; |
| })(Backbone, _); |
| |
| // Wreqr.CommandStorage |
| // -------------------- |
| // |
| // Store and retrieve commands for execution. |
| Wreqr.CommandStorage = (function(){ |
| "use strict"; |
| |
| // Constructor function |
| var CommandStorage = function(options){ |
| this.options = options; |
| this._commands = {}; |
| |
| if (_.isFunction(this.initialize)){ |
| this.initialize(options); |
| } |
| }; |
| |
| // Instance methods |
| _.extend(CommandStorage.prototype, Backbone.Events, { |
| |
| // Get an object literal by command name, that contains |
| // the `commandName` and the `instances` of all commands |
| // represented as an array of arguments to process |
| getCommands: function(commandName){ |
| var commands = this._commands[commandName]; |
| |
| // we don't have it, so add it |
| if (!commands){ |
| |
| // build the configuration |
| commands = { |
| command: commandName, |
| instances: [] |
| }; |
| |
| // store it |
| this._commands[commandName] = commands; |
| } |
| |
| return commands; |
| }, |
| |
| // Add a command by name, to the storage and store the |
| // args for the command |
| addCommand: function(commandName, args){ |
| var command = this.getCommands(commandName); |
| command.instances.push(args); |
| }, |
| |
| // Clear all commands for the given `commandName` |
| clearCommands: function(commandName){ |
| var command = this.getCommands(commandName); |
| command.instances = []; |
| } |
| }); |
| |
| return CommandStorage; |
| })(); |
| |
| // Wreqr.Commands |
| // -------------- |
| // |
| // A simple command pattern implementation. Register a command |
| // handler and execute it. |
| Wreqr.Commands = (function(Wreqr){ |
| "use strict"; |
| |
| return Wreqr.Handlers.extend({ |
| // default storage type |
| storageType: Wreqr.CommandStorage, |
| |
| constructor: function(options){ |
| this.options = options || {}; |
| |
| this._initializeStorage(this.options); |
| this.on("handler:add", this._executeCommands, this); |
| |
| var args = Array.prototype.slice.call(arguments); |
| Wreqr.Handlers.prototype.constructor.apply(this, args); |
| }, |
| |
| // Execute a named command with the supplied args |
| execute: function(name, args){ |
| name = arguments[0]; |
| args = Array.prototype.slice.call(arguments, 1); |
| |
| if (this.hasHandler(name)){ |
| this.getHandler(name).apply(this, args); |
| } else { |
| this.storage.addCommand(name, args); |
| } |
| |
| }, |
| |
| // Internal method to handle bulk execution of stored commands |
| _executeCommands: function(name, handler, context){ |
| var command = this.storage.getCommands(name); |
| |
| // loop through and execute all the stored command instances |
| _.each(command.instances, function(args){ |
| handler.apply(context, args); |
| }); |
| |
| this.storage.clearCommands(name); |
| }, |
| |
| // Internal method to initialize storage either from the type's |
| // `storageType` or the instance `options.storageType`. |
| _initializeStorage: function(options){ |
| var storage; |
| |
| var StorageType = options.storageType || this.storageType; |
| if (_.isFunction(StorageType)){ |
| storage = new StorageType(); |
| } else { |
| storage = StorageType; |
| } |
| |
| this.storage = storage; |
| } |
| }); |
| |
| })(Wreqr); |
| |
| // Wreqr.RequestResponse |
| // --------------------- |
| // |
| // A simple request/response implementation. Register a |
| // request handler, and return a response from it |
| Wreqr.RequestResponse = (function(Wreqr){ |
| "use strict"; |
| |
| return Wreqr.Handlers.extend({ |
| request: function(){ |
| var name = arguments[0]; |
| var args = Array.prototype.slice.call(arguments, 1); |
| if (this.hasHandler(name)) { |
| return this.getHandler(name).apply(this, args); |
| } |
| } |
| }); |
| |
| })(Wreqr); |
| |
| // Event Aggregator |
| // ---------------- |
| // A pub-sub object that can be used to decouple various parts |
| // of an application through event-driven architecture. |
| |
| Wreqr.EventAggregator = (function(Backbone, _){ |
| "use strict"; |
| var EA = function(){}; |
| |
| // Copy the `extend` function used by Backbone's classes |
| EA.extend = Backbone.Model.extend; |
| |
| // Copy the basic Backbone.Events on to the event aggregator |
| _.extend(EA.prototype, Backbone.Events); |
| |
| return EA; |
| })(Backbone, _); |
| |
| // Wreqr.Channel |
| // -------------- |
| // |
| // An object that wraps the three messaging systems: |
| // EventAggregator, RequestResponse, Commands |
| Wreqr.Channel = (function(Wreqr){ |
| "use strict"; |
| |
| var Channel = function(channelName) { |
| this.vent = new Backbone.Wreqr.EventAggregator(); |
| this.reqres = new Backbone.Wreqr.RequestResponse(); |
| this.commands = new Backbone.Wreqr.Commands(); |
| this.channelName = channelName; |
| }; |
| |
| _.extend(Channel.prototype, { |
| |
| // Remove all handlers from the messaging systems of this channel |
| reset: function() { |
| this.vent.off(); |
| this.vent.stopListening(); |
| this.reqres.removeAllHandlers(); |
| this.commands.removeAllHandlers(); |
| return this; |
| }, |
| |
| // Connect a hash of events; one for each messaging system |
| connectEvents: function(hash, context) { |
| this._connect('vent', hash, context); |
| return this; |
| }, |
| |
| connectCommands: function(hash, context) { |
| this._connect('commands', hash, context); |
| return this; |
| }, |
| |
| connectRequests: function(hash, context) { |
| this._connect('reqres', hash, context); |
| return this; |
| }, |
| |
| // Attach the handlers to a given message system `type` |
| _connect: function(type, hash, context) { |
| if (!hash) { |
| return; |
| } |
| |
| context = context || this; |
| var method = (type === 'vent') ? 'on' : 'setHandler'; |
| |
| _.each(hash, function(fn, eventName) { |
| this[type][method](eventName, _.bind(fn, context)); |
| }, this); |
| } |
| }); |
| |
| |
| return Channel; |
| })(Wreqr); |
| |
| // Wreqr.Radio |
| // -------------- |
| // |
| // An object that lets you communicate with many channels. |
| Wreqr.radio = (function(Wreqr){ |
| "use strict"; |
| |
| var Radio = function() { |
| this._channels = {}; |
| this.vent = {}; |
| this.commands = {}; |
| this.reqres = {}; |
| this._proxyMethods(); |
| }; |
| |
| _.extend(Radio.prototype, { |
| |
| channel: function(channelName) { |
| if (!channelName) { |
| throw new Error('Channel must receive a name'); |
| } |
| |
| return this._getChannel( channelName ); |
| }, |
| |
| _getChannel: function(channelName) { |
| var channel = this._channels[channelName]; |
| |
| if(!channel) { |
| channel = new Wreqr.Channel(channelName); |
| this._channels[channelName] = channel; |
| } |
| |
| return channel; |
| }, |
| |
| _proxyMethods: function() { |
| _.each(['vent', 'commands', 'reqres'], function(system) { |
| _.each( messageSystems[system], function(method) { |
| this[system][method] = proxyMethod(this, system, method); |
| }, this); |
| }, this); |
| } |
| }); |
| |
| |
| var messageSystems = { |
| vent: [ |
| 'on', |
| 'off', |
| 'trigger', |
| 'once', |
| 'stopListening', |
| 'listenTo', |
| 'listenToOnce' |
| ], |
| |
| commands: [ |
| 'execute', |
| 'setHandler', |
| 'setHandlers', |
| 'removeHandler', |
| 'removeAllHandlers' |
| ], |
| |
| reqres: [ |
| 'request', |
| 'setHandler', |
| 'setHandlers', |
| 'removeHandler', |
| 'removeAllHandlers' |
| ] |
| }; |
| |
| var proxyMethod = function(radio, system, method) { |
| return function(channelName) { |
| var messageSystem = radio._getChannel(channelName)[system]; |
| var args = Array.prototype.slice.call(arguments, 1); |
| |
| return messageSystem[method].apply(messageSystem, args); |
| }; |
| }; |
| |
| return new Radio(); |
| |
| })(Wreqr); |
| |
| |
| return Backbone.Wreqr; |
| |
| })); |