1 // Backbone.Wreqr (Backbone.Marionette)
2 // ----------------------------------
5 // Copyright (c)2014 Derick Bailey, Muted Solutions, LLC.
6 // Distributed under MIT license
8 // http://github.com/marionettejs/backbone.wreqr
11 (function(root, factory) {
13 if (typeof define === 'function' && define.amd) {
14 define(['backbone', 'underscore'], function(Backbone, _) {
15 return factory(Backbone, _);
17 } else if (typeof exports !== 'undefined') {
18 var Backbone = require('backbone');
19 var _ = require('underscore');
20 module.exports = factory(Backbone, _);
22 factory(root.Backbone, root._);
25 }(this, function(Backbone, _) {
28 var previousWreqr = Backbone.Wreqr;
30 var Wreqr = Backbone.Wreqr = {};
32 Backbone.Wreqr.VERSION = '1.3.1';
34 Backbone.Wreqr.noConflict = function () {
35 Backbone.Wreqr = previousWreqr;
41 // A registry of functions to call, given a name
43 Wreqr.Handlers = (function(Backbone, _){
49 var Handlers = function(options){
50 this.options = options;
51 this._wreqrHandlers = {};
53 if (_.isFunction(this.initialize)){
54 this.initialize(options);
58 Handlers.extend = Backbone.Model.extend;
63 _.extend(Handlers.prototype, Backbone.Events, {
65 // Add multiple handlers using an object literal configuration
66 setHandlers: function(handlers){
67 _.each(handlers, function(handler, name){
70 if (_.isObject(handler) && !_.isFunction(handler)){
71 context = handler.context;
72 handler = handler.callback;
75 this.setHandler(name, handler, context);
79 // Add a handler for the given name, with an
80 // optional context to run the handler within
81 setHandler: function(name, handler, context){
87 this._wreqrHandlers[name] = config;
89 this.trigger("handler:add", name, handler, context);
92 // Determine whether or not a handler is registered
93 hasHandler: function(name){
94 return !! this._wreqrHandlers[name];
97 // Get the currently registered handler for
98 // the specified name. Throws an exception if
99 // no handler is found.
100 getHandler: function(name){
101 var config = this._wreqrHandlers[name];
108 var args = Array.prototype.slice.apply(arguments);
109 return config.callback.apply(config.context, args);
113 // Remove a handler for the specified name
114 removeHandler: function(name){
115 delete this._wreqrHandlers[name];
118 // Remove all handlers from this registry
119 removeAllHandlers: function(){
120 this._wreqrHandlers = {};
127 // Wreqr.CommandStorage
128 // --------------------
130 // Store and retrieve commands for execution.
131 Wreqr.CommandStorage = (function(){
134 // Constructor function
135 var CommandStorage = function(options){
136 this.options = options;
139 if (_.isFunction(this.initialize)){
140 this.initialize(options);
145 _.extend(CommandStorage.prototype, Backbone.Events, {
147 // Get an object literal by command name, that contains
148 // the `commandName` and the `instances` of all commands
149 // represented as an array of arguments to process
150 getCommands: function(commandName){
151 var commands = this._commands[commandName];
153 // we don't have it, so add it
156 // build the configuration
158 command: commandName,
163 this._commands[commandName] = commands;
169 // Add a command by name, to the storage and store the
170 // args for the command
171 addCommand: function(commandName, args){
172 var command = this.getCommands(commandName);
173 command.instances.push(args);
176 // Clear all commands for the given `commandName`
177 clearCommands: function(commandName){
178 var command = this.getCommands(commandName);
179 command.instances = [];
183 return CommandStorage;
189 // A simple command pattern implementation. Register a command
190 // handler and execute it.
191 Wreqr.Commands = (function(Wreqr){
194 return Wreqr.Handlers.extend({
195 // default storage type
196 storageType: Wreqr.CommandStorage,
198 constructor: function(options){
199 this.options = options || {};
201 this._initializeStorage(this.options);
202 this.on("handler:add", this._executeCommands, this);
204 var args = Array.prototype.slice.call(arguments);
205 Wreqr.Handlers.prototype.constructor.apply(this, args);
208 // Execute a named command with the supplied args
209 execute: function(name, args){
211 args = Array.prototype.slice.call(arguments, 1);
213 if (this.hasHandler(name)){
214 this.getHandler(name).apply(this, args);
216 this.storage.addCommand(name, args);
221 // Internal method to handle bulk execution of stored commands
222 _executeCommands: function(name, handler, context){
223 var command = this.storage.getCommands(name);
225 // loop through and execute all the stored command instances
226 _.each(command.instances, function(args){
227 handler.apply(context, args);
230 this.storage.clearCommands(name);
233 // Internal method to initialize storage either from the type's
234 // `storageType` or the instance `options.storageType`.
235 _initializeStorage: function(options){
238 var StorageType = options.storageType || this.storageType;
239 if (_.isFunction(StorageType)){
240 storage = new StorageType();
242 storage = StorageType;
245 this.storage = storage;
251 // Wreqr.RequestResponse
252 // ---------------------
254 // A simple request/response implementation. Register a
255 // request handler, and return a response from it
256 Wreqr.RequestResponse = (function(Wreqr){
259 return Wreqr.Handlers.extend({
261 var name = arguments[0];
262 var args = Array.prototype.slice.call(arguments, 1);
263 if (this.hasHandler(name)) {
264 return this.getHandler(name).apply(this, args);
273 // A pub-sub object that can be used to decouple various parts
274 // of an application through event-driven architecture.
276 Wreqr.EventAggregator = (function(Backbone, _){
278 var EA = function(){};
280 // Copy the `extend` function used by Backbone's classes
281 EA.extend = Backbone.Model.extend;
283 // Copy the basic Backbone.Events on to the event aggregator
284 _.extend(EA.prototype, Backbone.Events);
292 // An object that wraps the three messaging systems:
293 // EventAggregator, RequestResponse, Commands
294 Wreqr.Channel = (function(Wreqr){
297 var Channel = function(channelName) {
298 this.vent = new Backbone.Wreqr.EventAggregator();
299 this.reqres = new Backbone.Wreqr.RequestResponse();
300 this.commands = new Backbone.Wreqr.Commands();
301 this.channelName = channelName;
304 _.extend(Channel.prototype, {
306 // Remove all handlers from the messaging systems of this channel
309 this.vent.stopListening();
310 this.reqres.removeAllHandlers();
311 this.commands.removeAllHandlers();
315 // Connect a hash of events; one for each messaging system
316 connectEvents: function(hash, context) {
317 this._connect('vent', hash, context);
321 connectCommands: function(hash, context) {
322 this._connect('commands', hash, context);
326 connectRequests: function(hash, context) {
327 this._connect('reqres', hash, context);
331 // Attach the handlers to a given message system `type`
332 _connect: function(type, hash, context) {
337 context = context || this;
338 var method = (type === 'vent') ? 'on' : 'setHandler';
340 _.each(hash, function(fn, eventName) {
341 this[type][method](eventName, _.bind(fn, context));
353 // An object that lets you communicate with many channels.
354 Wreqr.radio = (function(Wreqr){
357 var Radio = function() {
362 this._proxyMethods();
365 _.extend(Radio.prototype, {
367 channel: function(channelName) {
369 throw new Error('Channel must receive a name');
372 return this._getChannel( channelName );
375 _getChannel: function(channelName) {
376 var channel = this._channels[channelName];
379 channel = new Wreqr.Channel(channelName);
380 this._channels[channelName] = channel;
386 _proxyMethods: function() {
387 _.each(['vent', 'commands', 'reqres'], function(system) {
388 _.each( messageSystems[system], function(method) {
389 this[system][method] = proxyMethod(this, system, method);
396 var messageSystems = {
424 var proxyMethod = function(radio, system, method) {
425 return function(channelName) {
426 var messageSystem = radio._getChannel(channelName)[system];
427 var args = Array.prototype.slice.call(arguments, 1);
429 return messageSystem[method].apply(messageSystem, args);
438 return Backbone.Wreqr;