xosDeveloper view marionette wip
[plstackapi.git] / planetstack / core / xoslib / static / js / vendor / backbone.wreqr.js
1 // Backbone.Wreqr (Backbone.Marionette)
2 // ----------------------------------
3 // v1.3.1
4 //
5 // Copyright (c)2014 Derick Bailey, Muted Solutions, LLC.
6 // Distributed under MIT license
7 //
8 // http://github.com/marionettejs/backbone.wreqr
9
10
11 (function(root, factory) {
12
13   if (typeof define === 'function' && define.amd) {
14     define(['backbone', 'underscore'], function(Backbone, _) {
15       return factory(Backbone, _);
16     });
17   } else if (typeof exports !== 'undefined') {
18     var Backbone = require('backbone');
19     var _ = require('underscore');
20     module.exports = factory(Backbone, _);
21   } else {
22     factory(root.Backbone, root._);
23   }
24
25 }(this, function(Backbone, _) {
26   "use strict";
27
28   var previousWreqr = Backbone.Wreqr;
29
30   var Wreqr = Backbone.Wreqr = {};
31
32   Backbone.Wreqr.VERSION = '1.3.1';
33
34   Backbone.Wreqr.noConflict = function () {
35     Backbone.Wreqr = previousWreqr;
36     return this;
37   };
38
39   // Handlers
40   // --------
41   // A registry of functions to call, given a name
42   
43   Wreqr.Handlers = (function(Backbone, _){
44     "use strict";
45     
46     // Constructor
47     // -----------
48   
49     var Handlers = function(options){
50       this.options = options;
51       this._wreqrHandlers = {};
52       
53       if (_.isFunction(this.initialize)){
54         this.initialize(options);
55       }
56     };
57   
58     Handlers.extend = Backbone.Model.extend;
59   
60     // Instance Members
61     // ----------------
62   
63     _.extend(Handlers.prototype, Backbone.Events, {
64   
65       // Add multiple handlers using an object literal configuration
66       setHandlers: function(handlers){
67         _.each(handlers, function(handler, name){
68           var context = null;
69   
70           if (_.isObject(handler) && !_.isFunction(handler)){
71             context = handler.context;
72             handler = handler.callback;
73           }
74   
75           this.setHandler(name, handler, context);
76         }, this);
77       },
78   
79       // Add a handler for the given name, with an
80       // optional context to run the handler within
81       setHandler: function(name, handler, context){
82         var config = {
83           callback: handler,
84           context: context
85         };
86   
87         this._wreqrHandlers[name] = config;
88   
89         this.trigger("handler:add", name, handler, context);
90       },
91   
92       // Determine whether or not a handler is registered
93       hasHandler: function(name){
94         return !! this._wreqrHandlers[name];
95       },
96   
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];
102   
103         if (!config){
104           return;
105         }
106   
107         return function(){
108           var args = Array.prototype.slice.apply(arguments);
109           return config.callback.apply(config.context, args);
110         };
111       },
112   
113       // Remove a handler for the specified name
114       removeHandler: function(name){
115         delete this._wreqrHandlers[name];
116       },
117   
118       // Remove all handlers from this registry
119       removeAllHandlers: function(){
120         this._wreqrHandlers = {};
121       }
122     });
123   
124     return Handlers;
125   })(Backbone, _);
126   
127   // Wreqr.CommandStorage
128   // --------------------
129   //
130   // Store and retrieve commands for execution.
131   Wreqr.CommandStorage = (function(){
132     "use strict";
133   
134     // Constructor function
135     var CommandStorage = function(options){
136       this.options = options;
137       this._commands = {};
138   
139       if (_.isFunction(this.initialize)){
140         this.initialize(options);
141       }
142     };
143   
144     // Instance methods
145     _.extend(CommandStorage.prototype, Backbone.Events, {
146   
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];
152   
153         // we don't have it, so add it
154         if (!commands){
155   
156           // build the configuration
157           commands = {
158             command: commandName, 
159             instances: []
160           };
161   
162           // store it
163           this._commands[commandName] = commands;
164         }
165   
166         return commands;
167       },
168   
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);
174       },
175   
176       // Clear all commands for the given `commandName`
177       clearCommands: function(commandName){
178         var command = this.getCommands(commandName);
179         command.instances = [];
180       }
181     });
182   
183     return CommandStorage;
184   })();
185   
186   // Wreqr.Commands
187   // --------------
188   //
189   // A simple command pattern implementation. Register a command
190   // handler and execute it.
191   Wreqr.Commands = (function(Wreqr){
192     "use strict";
193   
194     return Wreqr.Handlers.extend({
195       // default storage type
196       storageType: Wreqr.CommandStorage,
197   
198       constructor: function(options){
199         this.options = options || {};
200   
201         this._initializeStorage(this.options);
202         this.on("handler:add", this._executeCommands, this);
203   
204         var args = Array.prototype.slice.call(arguments);
205         Wreqr.Handlers.prototype.constructor.apply(this, args);
206       },
207   
208       // Execute a named command with the supplied args
209       execute: function(name, args){
210         name = arguments[0];
211         args = Array.prototype.slice.call(arguments, 1);
212   
213         if (this.hasHandler(name)){
214           this.getHandler(name).apply(this, args);
215         } else {
216           this.storage.addCommand(name, args);
217         }
218   
219       },
220   
221       // Internal method to handle bulk execution of stored commands
222       _executeCommands: function(name, handler, context){
223         var command = this.storage.getCommands(name);
224   
225         // loop through and execute all the stored command instances
226         _.each(command.instances, function(args){
227           handler.apply(context, args);
228         });
229   
230         this.storage.clearCommands(name);
231       },
232   
233       // Internal method to initialize storage either from the type's
234       // `storageType` or the instance `options.storageType`.
235       _initializeStorage: function(options){
236         var storage;
237   
238         var StorageType = options.storageType || this.storageType;
239         if (_.isFunction(StorageType)){
240           storage = new StorageType();
241         } else {
242           storage = StorageType;
243         }
244   
245         this.storage = storage;
246       }
247     });
248   
249   })(Wreqr);
250   
251   // Wreqr.RequestResponse
252   // ---------------------
253   //
254   // A simple request/response implementation. Register a
255   // request handler, and return a response from it
256   Wreqr.RequestResponse = (function(Wreqr){
257     "use strict";
258   
259     return Wreqr.Handlers.extend({
260       request: function(){
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);
265         }
266       }
267     });
268   
269   })(Wreqr);
270   
271   // Event Aggregator
272   // ----------------
273   // A pub-sub object that can be used to decouple various parts
274   // of an application through event-driven architecture.
275   
276   Wreqr.EventAggregator = (function(Backbone, _){
277     "use strict";
278     var EA = function(){};
279   
280     // Copy the `extend` function used by Backbone's classes
281     EA.extend = Backbone.Model.extend;
282   
283     // Copy the basic Backbone.Events on to the event aggregator
284     _.extend(EA.prototype, Backbone.Events);
285   
286     return EA;
287   })(Backbone, _);
288   
289   // Wreqr.Channel
290   // --------------
291   //
292   // An object that wraps the three messaging systems:
293   // EventAggregator, RequestResponse, Commands
294   Wreqr.Channel = (function(Wreqr){
295     "use strict";
296   
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;
302     };
303   
304     _.extend(Channel.prototype, {
305   
306       // Remove all handlers from the messaging systems of this channel
307       reset: function() {
308         this.vent.off();
309         this.vent.stopListening();
310         this.reqres.removeAllHandlers();
311         this.commands.removeAllHandlers();
312         return this;
313       },
314   
315       // Connect a hash of events; one for each messaging system
316       connectEvents: function(hash, context) {
317         this._connect('vent', hash, context);
318         return this;
319       },
320   
321       connectCommands: function(hash, context) {
322         this._connect('commands', hash, context);
323         return this;
324       },
325   
326       connectRequests: function(hash, context) {
327         this._connect('reqres', hash, context);
328         return this;
329       },
330   
331       // Attach the handlers to a given message system `type`
332       _connect: function(type, hash, context) {
333         if (!hash) {
334           return;
335         }
336   
337         context = context || this;
338         var method = (type === 'vent') ? 'on' : 'setHandler';
339   
340         _.each(hash, function(fn, eventName) {
341           this[type][method](eventName, _.bind(fn, context));
342         }, this);
343       }
344     });
345   
346   
347     return Channel;
348   })(Wreqr);
349   
350   // Wreqr.Radio
351   // --------------
352   //
353   // An object that lets you communicate with many channels.
354   Wreqr.radio = (function(Wreqr){
355     "use strict";
356   
357     var Radio = function() {
358       this._channels = {};
359       this.vent = {};
360       this.commands = {};
361       this.reqres = {};
362       this._proxyMethods();
363     };
364   
365     _.extend(Radio.prototype, {
366   
367       channel: function(channelName) {
368         if (!channelName) {
369           throw new Error('Channel must receive a name');
370         }
371   
372         return this._getChannel( channelName );
373       },
374   
375       _getChannel: function(channelName) {
376         var channel = this._channels[channelName];
377   
378         if(!channel) {
379           channel = new Wreqr.Channel(channelName);
380           this._channels[channelName] = channel;
381         }
382   
383         return channel;
384       },
385   
386       _proxyMethods: function() {
387         _.each(['vent', 'commands', 'reqres'], function(system) {
388           _.each( messageSystems[system], function(method) {
389             this[system][method] = proxyMethod(this, system, method);
390           }, this);
391         }, this);
392       }
393     });
394   
395   
396     var messageSystems = {
397       vent: [
398         'on',
399         'off',
400         'trigger',
401         'once',
402         'stopListening',
403         'listenTo',
404         'listenToOnce'
405       ],
406   
407       commands: [
408         'execute',
409         'setHandler',
410         'setHandlers',
411         'removeHandler',
412         'removeAllHandlers'
413       ],
414   
415       reqres: [
416         'request',
417         'setHandler',
418         'setHandlers',
419         'removeHandler',
420         'removeAllHandlers'
421       ]
422     };
423   
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);
428   
429         return messageSystem[method].apply(messageSystem, args);
430       };
431     };
432   
433     return new Radio();
434   
435   })(Wreqr);
436   
437
438   return Backbone.Wreqr;
439
440 }));