marionette-amd-rails 0.8.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2012 Godfrey Chan
2
+ Copyright (c) 2012 Andrey Subbotin
3
+
4
+ Permission is hereby granted, free of charge, to any person obtaining a copy
5
+ of this software and associated documentation files (the "Software"), to deal
6
+ in the Software without restriction, including without limitation the rights
7
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8
+ copies of the Software, and to permit persons to whom the Software is
9
+ furnished to do so, subject to the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be included in
12
+ all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
20
+ THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,42 @@
1
+ marionette-amd-rails
2
+ ====================
3
+
4
+ [![Dependency Status](https://gemnasium.com/eploko/marionette-amd-rails.png)](https://gemnasium.com/eploko/marionette-amd-rails)
5
+
6
+ This gem is a wrapper for the AMD version of Derick Bailey's [Backbone.Marionette](https://github.com/derickbailey/backbone.marionette) library. It vendors the javascript library code for use with Rails' asset pipeline (Rails 3.1+).
7
+
8
+ ## Dependencies
9
+
10
+ [Backbone.Marionette](https://github.com/derickbailey/backbone.marionette) depends on Backbone (and Backbone's dependencies). These dependencies are not currently managed by the `marionette-rails` gem directly, because there exists multiple options to use Backbone with the Rails asset pipeline, such as [`backbone-on-rails`](https://github.com/meleyal/backbone-on-rails), [`backbone-rails`](https://github.com/aflatter/backbone-rails), [`rails-backbone`](https://github.com/codebrew/backbone-rails), just to name a few.
11
+
12
+ ## Usage
13
+
14
+ Add it to your Gemfile:
15
+
16
+ group :assets do
17
+ # Your other asset gems (sass-rails, coffee-rails, etc)
18
+
19
+ gem 'marionette-amd-rails'
20
+ end
21
+
22
+ Load `backbone.marionette` module as a dependency when appropriate.
23
+
24
+ ## Versioning
25
+
26
+ The gem will mirror the [Backbone.Marionette](https://github.com/derickbailey/backbone.marionette) versioning scheme. That is, version 0.8.2.* of `marionette-amd-rails` would vendor [Backbone.Marionette](https://github.com/derickbailey/backbone.marionette) v0.8.2.
27
+
28
+ ## Contributing
29
+
30
+ For bugs in [Backbone.Marionette](https://github.com/derickbailey/backbone.marionette) itself, head over to their [issue tracker](https://github.com/derickbailey/backbone.marionette/issues). If you have a question, post it at [StackOverflow under the `backbone.marionette` tag](http://stackoverflow.com/questions/tagged/backbone.marionette).
31
+
32
+ For bugs in this gem distribution, use the [GitHub issue tracker](https://github.com/eploko/marionette-amd-rails/issues). If you could submit a pull request - that's even better!
33
+
34
+ ## Donations
35
+
36
+ If you're using Marionette and you're finding that it is saving you time and effort, then please consider donating to the upstream [Backbone.Marionette](https://github.com/derickbailey/backbone.marionette) project.
37
+
38
+ [![Donate](https://www.paypalobjects.com/en_US/i/btn/btn_donate_SM.gif)](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=7SJHYWJ487SF4)
39
+
40
+ ## License
41
+
42
+ This library is distributed under the MIT license. Please see the LICENSE file.
@@ -0,0 +1,8 @@
1
+ require 'rails'
2
+
3
+ module Marionette
4
+ module Rails
5
+ class Engine < ::Rails::Engine
6
+ end
7
+ end
8
+ end
@@ -0,0 +1,1033 @@
1
+ // Backbone.Marionette v0.8.4
2
+ //
3
+ // Copyright (C)2012 Derick Bailey, Muted Solutions, LLC
4
+ // Distributed Under MIT License
5
+ //
6
+ // Documentation and Full License Available at:
7
+ // http://github.com/derickbailey/backbone.marionette
8
+ (function (root, factory) {
9
+ if (typeof exports === 'object') {
10
+
11
+ var jquery = require('jquery');
12
+ var underscore = require('underscore');
13
+ var backbone = require('backbone');
14
+
15
+ module.exports = factory(jquery, underscore, backbone);
16
+
17
+ } else if (typeof define === 'function' && define.amd) {
18
+
19
+ define(['jquery', 'underscore', 'backbone'], factory);
20
+
21
+ }
22
+ }(this, function ($, _, Backbone) {
23
+
24
+ Backbone.Marionette = (function(Backbone, _, $){
25
+ var Marionette = {};
26
+
27
+ Marionette.version = "0.8.4";
28
+
29
+ // Marionette.View
30
+ // ---------------
31
+
32
+ // The core view type that other Marionette views extend from.
33
+ Marionette.View = Backbone.View.extend({
34
+ // Get the template or template id/selector for this view
35
+ // instance. You can set a `template` attribute in the view
36
+ // definition or pass a `template: "whatever"` parameter in
37
+ // to the constructor options. The `template` can also be
38
+ // a function that returns a selector string.
39
+ getTemplateSelector: function(){
40
+ var template;
41
+
42
+ // Get the template from `this.options.template` or
43
+ // `this.template`. The `options` takes precedence.
44
+ if (this.options && this.options.template){
45
+ template = this.options.template;
46
+ } else {
47
+ template = this.template;
48
+ }
49
+
50
+ // check if it's a function and execute it, if it is
51
+ if (_.isFunction(template)){
52
+ template = template.call(this);
53
+ }
54
+
55
+ return template;
56
+ },
57
+
58
+ // Serialize the model or collection for the view. If a model is
59
+ // found, `.toJSON()` is called. If a collection is found, `.toJSON()`
60
+ // is also called, but is used to populate an `items` array in the
61
+ // resulting data. If both are found, defaults to the model.
62
+ // You can override the `serializeData` method in your own view
63
+ // definition, to provide custom serialization for your view's data.
64
+ serializeData: function(){
65
+ var data;
66
+
67
+ if (this.model) {
68
+ data = this.model.toJSON();
69
+ }
70
+ else if (this.collection) {
71
+ data = { items: this.collection.toJSON() };
72
+ }
73
+
74
+ data = this.mixinTemplateHelpers(data);
75
+
76
+ return data;
77
+ },
78
+
79
+ // Mix in template helper methods. Looks for a
80
+ // `templateHelpers` attribute, which can either be an
81
+ // object literal, or a function that returns an object
82
+ // literal. All methods and attributes from this object
83
+ // are copies to the object passed in.
84
+ mixinTemplateHelpers: function(target){
85
+ target = target || {};
86
+ var templateHelpers = this.templateHelpers;
87
+ if (_.isFunction(templateHelpers)){
88
+ templateHelpers = templateHelpers.call(this);
89
+ }
90
+ return _.extend(target, templateHelpers);
91
+ },
92
+
93
+ // Configure `triggers` to forward DOM events to view
94
+ // events. `triggers: {"click .foo": "do:foo"}`
95
+ configureTriggers: function(){
96
+ if (!this.triggers) { return; }
97
+
98
+ var triggers = this.triggers;
99
+ var that = this;
100
+ var triggerEvents = {};
101
+
102
+ // Allow `triggers` to be configured as a function
103
+ if (_.isFunction(triggers)){ triggers = triggers.call(this); }
104
+
105
+ // Configure the triggers, prevent default
106
+ // action and stop propagation of DOM events
107
+ _.each(triggers, function(value, key){
108
+
109
+ triggerEvents[key] = function(e){
110
+ if (e && e.preventDefault){ e.preventDefault(); }
111
+ if (e && e.stopPropagation){ e.stopPropagation(); }
112
+ that.trigger(value);
113
+ }
114
+
115
+ });
116
+
117
+ return triggerEvents;
118
+ },
119
+
120
+ delegateEvents: function(events){
121
+ events = events || this.events;
122
+ if (_.isFunction(events)){ events = events.call(this)}
123
+
124
+ var combinedEvents = {};
125
+ var triggers = this.configureTriggers();
126
+ _.extend(combinedEvents, events, triggers);
127
+
128
+ Backbone.View.prototype.delegateEvents.call(this, combinedEvents);
129
+ },
130
+
131
+ // Default `close` implementation, for removing a view from the
132
+ // DOM and unbinding it. Regions will call this method
133
+ // for you. You can specify an `onClose` method in your view to
134
+ // add custom code that is called after the view is closed.
135
+ close: function(){
136
+ if (this.beforeClose) { this.beforeClose(); }
137
+
138
+ this.unbindAll();
139
+ this.remove();
140
+
141
+ if (this.onClose) { this.onClose(); }
142
+ this.trigger('close');
143
+ this.unbind();
144
+ }
145
+ });
146
+
147
+ // Item View
148
+ // ---------
149
+
150
+ // A single item view implementation that contains code for rendering
151
+ // with underscore.js templates, serializing the view's model or collection,
152
+ // and calling several methods on extended views, such as `onRender`.
153
+ Marionette.ItemView = Marionette.View.extend({
154
+ constructor: function(){
155
+ var args = slice.call(arguments);
156
+ Marionette.View.prototype.constructor.apply(this, args);
157
+
158
+ _.bindAll(this, "render");
159
+
160
+ this.initialEvents();
161
+ },
162
+
163
+ // Configured the initial events that the item view
164
+ // binds to. Override this method to prevent the initial
165
+ // events, or to add your own initial events.
166
+ initialEvents: function(){
167
+ if (this.collection){
168
+ this.bindTo(this.collection, "reset", this.render, this);
169
+ }
170
+ },
171
+
172
+ // Render the view, defaulting to underscore.js templates.
173
+ // You can override this in your view definition.
174
+ render: function(){
175
+ var that = this;
176
+
177
+ var deferredRender = $.Deferred();
178
+
179
+ var beforeRenderDone = function() {
180
+ that.trigger("before:render", that);
181
+ that.trigger("item:before:render", that);
182
+
183
+ var deferredData = that.serializeData();
184
+ $.when(deferredData).then(dataSerialized);
185
+ }
186
+
187
+ var dataSerialized = function(data){
188
+ var asyncRender = that.renderHtml(data);
189
+ $.when(asyncRender).then(templateRendered);
190
+ }
191
+
192
+ var templateRendered = function(html){
193
+ that.$el.html(html);
194
+ callDeferredMethod(that.onRender, onRenderDone, that);
195
+ }
196
+
197
+ var onRenderDone = function(){
198
+ that.trigger("render", that);
199
+ that.trigger("item:rendered", that);
200
+
201
+ deferredRender.resolve();
202
+ }
203
+
204
+ callDeferredMethod(this.beforeRender, beforeRenderDone, this);
205
+
206
+ return deferredRender.promise();
207
+ },
208
+
209
+ // Render the data for this item view in to some HTML.
210
+ // Override this method to replace the specific way in
211
+ // which an item view has it's data rendered in to html.
212
+ renderHtml: function(data) {
213
+ var template = this.getTemplateSelector();
214
+ return Marionette.Renderer.render(template, data);
215
+ },
216
+
217
+ // Override the default close event to add a few
218
+ // more events that are triggered.
219
+ close: function(){
220
+ this.trigger('item:before:close');
221
+ Marionette.View.prototype.close.apply(this, arguments);
222
+ this.trigger('item:closed');
223
+ }
224
+ });
225
+
226
+ // Collection View
227
+ // ---------------
228
+
229
+ // A view that iterates over a Backbone.Collection
230
+ // and renders an individual ItemView for each model.
231
+ Marionette.CollectionView = Marionette.View.extend({
232
+ constructor: function(){
233
+ Marionette.View.prototype.constructor.apply(this, arguments);
234
+
235
+ _.bindAll(this, "addItemView", "render");
236
+ this.initialEvents();
237
+ },
238
+
239
+ // Configured the initial events that the collection view
240
+ // binds to. Override this method to prevent the initial
241
+ // events, or to add your own initial events.
242
+ initialEvents: function(){
243
+ if (this.collection){
244
+ this.bindTo(this.collection, "add", this.addChildView, this);
245
+ this.bindTo(this.collection, "remove", this.removeItemView, this);
246
+ this.bindTo(this.collection, "reset", this.render, this);
247
+ }
248
+ },
249
+
250
+ // Handle a child item added to the collection
251
+ addChildView: function(item){
252
+ var ItemView = this.getItemView();
253
+ return this.addItemView(item, ItemView);
254
+ },
255
+
256
+ // Loop through all of the items and render
257
+ // each of them with the specified `itemView`.
258
+ render: function(){
259
+ var that = this;
260
+ var deferredRender = $.Deferred();
261
+ var promises = [];
262
+ var ItemView = this.getItemView();
263
+
264
+ if (this.beforeRender) { this.beforeRender(); }
265
+ this.trigger("collection:before:render", this);
266
+
267
+ this.closeChildren();
268
+
269
+ if (this.collection) {
270
+ this.collection.each(function(item){
271
+ var promise = that.addItemView(item, ItemView);
272
+ promises.push(promise);
273
+ });
274
+ }
275
+
276
+ deferredRender.done(function(){
277
+ if (this.onRender) { this.onRender(); }
278
+ this.trigger("collection:rendered", this);
279
+ });
280
+
281
+ $.when.apply(this, promises).then(function(){
282
+ deferredRender.resolveWith(that);
283
+ });
284
+
285
+ return deferredRender.promise();
286
+ },
287
+
288
+ // Retrieve the itemView type, either from `this.options.itemView`
289
+ // or from the `itemView` in the object definition. The "options"
290
+ // takes precedence.
291
+ getItemView: function(){
292
+ var itemView = this.options.itemView || this.itemView;
293
+
294
+ if (!itemView){
295
+ var err = new Error("An `itemView` must be specified");
296
+ err.name = "NoItemViewError";
297
+ throw err;
298
+ }
299
+
300
+ return itemView;
301
+ },
302
+
303
+ // Render the child item's view and add it to the
304
+ // HTML for the collection view.
305
+ addItemView: function(item, ItemView){
306
+ var that = this;
307
+
308
+ var view = this.buildItemView(item, ItemView);
309
+ this.bindTo(view, "all", function(){
310
+
311
+ // get the args, prepend the event name
312
+ // with "itemview:" and insert the child view
313
+ // as the first event arg (after the event name)
314
+ var args = slice.call(arguments);
315
+ args[0] = "itemview:" + args[0];
316
+ args.splice(1, 0, view);
317
+
318
+ that.trigger.apply(that, args);
319
+ });
320
+
321
+ this.storeChild(view);
322
+ this.trigger("item:added", view);
323
+
324
+ var viewRendered = view.render();
325
+ $.when(viewRendered).then(function(){
326
+ that.appendHtml(that, view);
327
+ });
328
+
329
+ return viewRendered;
330
+ },
331
+
332
+ // Build an `itemView` for every model in the collection.
333
+ buildItemView: function(item, ItemView){
334
+ var view = new ItemView({
335
+ model: item
336
+ });
337
+ return view;
338
+ },
339
+
340
+ // Remove the child view and close it
341
+ removeItemView: function(item){
342
+ var view = this.children[item.cid];
343
+ if (view){
344
+ view.close();
345
+ delete this.children[item.cid];
346
+ }
347
+ this.trigger("item:removed", view);
348
+ },
349
+
350
+ // Append the HTML to the collection's `el`.
351
+ // Override this method to do something other
352
+ // then `.append`.
353
+ appendHtml: function(collectionView, itemView){
354
+ collectionView.$el.append(itemView.el);
355
+ },
356
+
357
+ // Store references to all of the child `itemView`
358
+ // instances so they can be managed and cleaned up, later.
359
+ storeChild: function(view){
360
+ if (!this.children){
361
+ this.children = {};
362
+ }
363
+ this.children[view.model.cid] = view;
364
+ },
365
+
366
+ // Handle cleanup and other closing needs for
367
+ // the collection of views.
368
+ close: function(){
369
+ this.trigger("collection:before:close");
370
+ this.closeChildren();
371
+ Marionette.View.prototype.close.apply(this, arguments);
372
+ this.trigger("collection:closed");
373
+ },
374
+
375
+ closeChildren: function(){
376
+ if (this.children){
377
+ _.each(this.children, function(childView){
378
+ childView.close();
379
+ });
380
+ }
381
+ }
382
+ });
383
+
384
+ // Composite View
385
+ // --------------
386
+
387
+ // Used for rendering a branch-leaf, hierarchical structure.
388
+ // Extends directly from CollectionView and also renders an
389
+ // an item view as `modelView`, for the top leaf
390
+ Marionette.CompositeView = Marionette.CollectionView.extend({
391
+ constructor: function(options){
392
+ Marionette.CollectionView.apply(this, arguments);
393
+ this.itemView = this.getItemView();
394
+ },
395
+
396
+ // Retrieve the `itemView` to be used when rendering each of
397
+ // the items in the collection. The default is to return
398
+ // `this.itemView` or Marionette.CompositeView if no `itemView`
399
+ // has been defined
400
+ getItemView: function(){
401
+ return this.itemView || this.constructor;
402
+ },
403
+
404
+ // Renders the model once, and the collection once. Calling
405
+ // this again will tell the model's view to re-render itself
406
+ // but the collection will not re-render.
407
+ render: function(){
408
+ var that = this;
409
+ var compositeRendered = $.Deferred();
410
+
411
+ var modelIsRendered = this.renderModel();
412
+ $.when(modelIsRendered).then(function(html){
413
+ that.$el.html(html);
414
+ that.trigger("composite:model:rendered");
415
+ that.trigger("render");
416
+
417
+ var collectionIsRendered = that.renderCollection();
418
+ $.when(collectionIsRendered).then(function(){
419
+ compositeRendered.resolve();
420
+ });
421
+ });
422
+
423
+ compositeRendered.done(function(){
424
+ that.trigger("composite:rendered");
425
+ });
426
+
427
+ return compositeRendered.promise();
428
+ },
429
+
430
+ // Render the collection for the composite view
431
+ renderCollection: function(){
432
+ var collectionDeferred = Marionette.CollectionView.prototype.render.apply(this, arguments);
433
+ collectionDeferred.done(function(){
434
+ this.trigger("composite:collection:rendered");
435
+ });
436
+ return collectionDeferred.promise();
437
+ },
438
+
439
+ // Render an individual model, if we have one, as
440
+ // part of a composite view (branch / leaf). For example:
441
+ // a treeview.
442
+ renderModel: function(){
443
+ var data = {};
444
+ data = this.serializeData();
445
+
446
+ var template = this.getTemplateSelector();
447
+ return Marionette.Renderer.render(template, data);
448
+ }
449
+ });
450
+
451
+ // Region
452
+ // ------
453
+
454
+ // Manage the visual regions of your composite application. See
455
+ // http://lostechies.com/derickbailey/2011/12/12/composite-js-apps-regions-and-region-managers/
456
+ Marionette.Region = function(options){
457
+ this.options = options || {};
458
+
459
+ _.extend(this, options);
460
+
461
+ if (!this.el){
462
+ var err = new Error("An 'el' must be specified");
463
+ err.name = "NoElError";
464
+ throw err;
465
+ }
466
+
467
+ if (this.initialize){
468
+ this.initialize.apply(this, arguments);
469
+ }
470
+ };
471
+
472
+ _.extend(Marionette.Region.prototype, Backbone.Events, {
473
+
474
+ // Displays a backbone view instance inside of the region.
475
+ // Handles calling the `render` method for you. Reads content
476
+ // directly from the `el` attribute. Also calls an optional
477
+ // `onShow` and `close` method on your view, just after showing
478
+ // or just before closing the view, respectively.
479
+ show: function(view, appendMethod){
480
+ this.ensureEl();
481
+
482
+ this.close();
483
+ this.open(view, appendMethod);
484
+
485
+ this.currentView = view;
486
+ },
487
+
488
+ ensureEl: function(){
489
+ if (!this.$el || this.$el.length === 0){
490
+ this.$el = this.getEl(this.el);
491
+ }
492
+ },
493
+
494
+ // Override this method to change how the region finds the
495
+ // DOM element that it manages. Return a jQuery selector object.
496
+ getEl: function(selector){
497
+ return $(selector);
498
+ },
499
+
500
+ // Internal method to render and display a view. Not meant
501
+ // to be called from any external code.
502
+ open: function(view, appendMethod){
503
+ var that = this;
504
+ appendMethod = appendMethod || "html";
505
+
506
+ $.when(view.render()).then(function () {
507
+ that.$el[appendMethod](view.el);
508
+ if (view.onShow) { view.onShow(); }
509
+ if (that.onShow) { that.onShow(view); }
510
+ view.trigger("show");
511
+ that.trigger("view:show", view);
512
+ });
513
+ },
514
+
515
+ // Close the current view, if there is one. If there is no
516
+ // current view, it does nothing and returns immediately.
517
+ close: function(){
518
+ var view = this.currentView;
519
+ if (!view){ return; }
520
+
521
+ if (view.close) { view.close(); }
522
+ this.trigger("view:closed", view);
523
+
524
+ delete this.currentView;
525
+ },
526
+
527
+ // Attach an existing view to the region. This
528
+ // will not call `render` or `onShow` for the new view,
529
+ // and will not replace the current HTML for the `el`
530
+ // of the region.
531
+ attachView: function(view){
532
+ this.currentView = view;
533
+ }
534
+ });
535
+
536
+ // Layout
537
+ // ------
538
+
539
+ // Formerly known as Composite Region.
540
+ //
541
+ // Used for managing application layouts, nested layouts and
542
+ // multiple regions within an application or sub-application.
543
+ //
544
+ // A specialized view type that renders an area of HTML and then
545
+ // attaches `Region` instances to the specified `regions`.
546
+ // Used for composite view management and sub-application areas.
547
+ Marionette.Layout = Marionette.ItemView.extend({
548
+ constructor: function () {
549
+ this.vent = new Backbone.Marionette.EventAggregator();
550
+ Backbone.Marionette.ItemView.apply(this, arguments);
551
+ this.regionManagers = {};
552
+ },
553
+
554
+ render: function () {
555
+ this.initializeRegions();
556
+ return Backbone.Marionette.ItemView.prototype.render.call(this, arguments);
557
+ },
558
+
559
+ close: function () {
560
+ this.closeRegions();
561
+ Backbone.Marionette.ItemView.prototype.close.call(this, arguments);
562
+ },
563
+
564
+ // Initialize the regions that have been defined in a
565
+ // `regions` attribute on this layout. The key of the
566
+ // hash becomes an attribute on the layout object directly.
567
+ // For example: `regions: { menu: ".menu-container" }`
568
+ // will product a `layout.menu` object which is a region
569
+ // that controls the `.menu-container` DOM element.
570
+ initializeRegions: function () {
571
+ var that = this;
572
+ _.each(this.regions, function (selector, name) {
573
+ var regionManager = new Backbone.Marionette.Region({
574
+ el: selector,
575
+
576
+ getEl: function(selector){
577
+ return that.$(selector);
578
+ }
579
+ });
580
+ that.regionManagers[name] = regionManager;
581
+ that[name] = regionManager;
582
+ });
583
+ },
584
+
585
+ // Close all of the regions that have been opened by
586
+ // this layout. This method is called when the layout
587
+ // itself is closed.
588
+ closeRegions: function () {
589
+ var that = this;
590
+ _.each(this.regionManagers, function (manager, name) {
591
+ manager.close();
592
+ delete that[name];
593
+ });
594
+ this.regionManagers = {};
595
+ }
596
+ });
597
+
598
+ // AppRouter
599
+ // ---------
600
+
601
+ // Reduce the boilerplate code of handling route events
602
+ // and then calling a single method on another object.
603
+ // Have your routers configured to call the method on
604
+ // your object, directly.
605
+ //
606
+ // Configure an AppRouter with `appRoutes`.
607
+ //
608
+ // App routers can only take one `controller` object.
609
+ // It is recommended that you divide your controller
610
+ // objects in to smaller peices of related functionality
611
+ // and have multiple routers / controllers, instead of
612
+ // just one giant router and controller.
613
+ //
614
+ // You can also add standard routes to an AppRouter.
615
+
616
+ Marionette.AppRouter = Backbone.Router.extend({
617
+
618
+ constructor: function(options){
619
+ Backbone.Router.prototype.constructor.call(this, options);
620
+
621
+ if (this.appRoutes){
622
+ var controller = this.controller;
623
+ if (options && options.controller) {
624
+ controller = options.controller;
625
+ }
626
+ this.processAppRoutes(controller, this.appRoutes);
627
+ }
628
+ },
629
+
630
+ processAppRoutes: function(controller, appRoutes){
631
+ var method, methodName;
632
+ var route, routesLength, i;
633
+ var routes = [];
634
+ var router = this;
635
+
636
+ for(route in appRoutes){
637
+ if (appRoutes.hasOwnProperty(route)){
638
+ routes.unshift([route, appRoutes[route]]);
639
+ }
640
+ }
641
+
642
+ routesLength = routes.length;
643
+ for (i = 0; i < routesLength; i++){
644
+ route = routes[i][0];
645
+ methodName = routes[i][1];
646
+ method = controller[methodName];
647
+
648
+ if (!method){
649
+ var msg = "Method '" + methodName + "' was not found on the controller";
650
+ var err = new Error(msg);
651
+ err.name = "NoMethodError";
652
+ throw err;
653
+ }
654
+
655
+ method = _.bind(method, controller);
656
+ router.route(route, methodName, method);
657
+ }
658
+ }
659
+ });
660
+
661
+ // Composite Application
662
+ // ---------------------
663
+
664
+ // Contain and manage the composite application as a whole.
665
+ // Stores and starts up `Region` objects, includes an
666
+ // event aggregator as `app.vent`
667
+ Marionette.Application = function(options){
668
+ this.initCallbacks = new Marionette.Callbacks();
669
+ this.vent = new Marionette.EventAggregator();
670
+ _.extend(this, options);
671
+ };
672
+
673
+ _.extend(Marionette.Application.prototype, Backbone.Events, {
674
+ // Add an initializer that is either run at when the `start`
675
+ // method is called, or run immediately if added after `start`
676
+ // has already been called.
677
+ addInitializer: function(initializer){
678
+ this.initCallbacks.add(initializer);
679
+ },
680
+
681
+ // kick off all of the application's processes.
682
+ // initializes all of the regions that have been added
683
+ // to the app, and runs all of the initializer functions
684
+ start: function(options){
685
+ this.trigger("initialize:before", options);
686
+ this.initCallbacks.run(this, options);
687
+ this.trigger("initialize:after", options);
688
+
689
+ this.trigger("start", options);
690
+ },
691
+
692
+ // Add regions to your app.
693
+ // Accepts a hash of named strings or Region objects
694
+ // addRegions({something: "#someRegion"})
695
+ // addRegions{{something: Region.extend({el: "#someRegion"}) });
696
+ addRegions: function(regions){
697
+ var regionValue, regionObj, region;
698
+
699
+ for(region in regions){
700
+ if (regions.hasOwnProperty(region)){
701
+ regionValue = regions[region];
702
+
703
+ if (typeof regionValue === "string"){
704
+ regionObj = new Marionette.Region({
705
+ el: regionValue
706
+ });
707
+ } else {
708
+ regionObj = new regionValue();
709
+ }
710
+
711
+ this[region] = regionObj;
712
+ }
713
+ }
714
+ }
715
+ });
716
+
717
+ // BindTo: Event Binding
718
+ // ---------------------
719
+
720
+ // BindTo facilitates the binding and unbinding of events
721
+ // from objects that extend `Backbone.Events`. It makes
722
+ // unbinding events, even with anonymous callback functions,
723
+ // easy.
724
+ //
725
+ // Thanks to Johnny Oshika for this code.
726
+ // http://stackoverflow.com/questions/7567404/backbone-js-repopulate-or-recreate-the-view/7607853#7607853
727
+ Marionette.BindTo = {
728
+ // Store the event binding in array so it can be unbound
729
+ // easily, at a later point in time.
730
+ bindTo: function (obj, eventName, callback, context) {
731
+ context = context || this;
732
+ obj.on(eventName, callback, context);
733
+
734
+ if (!this.bindings) { this.bindings = []; }
735
+
736
+ var binding = {
737
+ obj: obj,
738
+ eventName: eventName,
739
+ callback: callback,
740
+ context: context
741
+ }
742
+
743
+ this.bindings.push(binding);
744
+
745
+ return binding;
746
+ },
747
+
748
+ // Unbind from a single binding object. Binding objects are
749
+ // returned from the `bindTo` method call.
750
+ unbindFrom: function(binding){
751
+ binding.obj.off(binding.eventName, binding.callback);
752
+ this.bindings = _.reject(this.bindings, function(bind){return bind === binding});
753
+ },
754
+
755
+ // Unbind all of the events that we have stored.
756
+ unbindAll: function () {
757
+ var that = this;
758
+
759
+ // The `unbindFrom` call removes elements from the array
760
+ // while it is being iterated, so clone it first.
761
+ var bindings = _.map(this.bindings, _.identity);
762
+ _.each(bindings, function (binding, index) {
763
+ that.unbindFrom(binding);
764
+ });
765
+ }
766
+ };
767
+
768
+ // Callbacks
769
+ // ---------
770
+
771
+ // A simple way of managing a collection of callbacks
772
+ // and executing them at a later point in time, using jQuery's
773
+ // `Deferred` object.
774
+ Marionette.Callbacks = function(){
775
+ this.deferred = $.Deferred();
776
+ this.promise = this.deferred.promise();
777
+ };
778
+
779
+ _.extend(Marionette.Callbacks.prototype, {
780
+
781
+ // Add a callback to be executed. Callbacks added here are
782
+ // guaranteed to execute, even if they are added after the
783
+ // `run` method is called.
784
+ add: function(callback){
785
+ this.promise.done(function(context, options){
786
+ callback.call(context, options);
787
+ });
788
+ },
789
+
790
+ // Run all registered callbacks with the context specified.
791
+ // Additional callbacks can be added after this has been run
792
+ // and they will still be executed.
793
+ run: function(context, options){
794
+ this.deferred.resolve(context, options);
795
+ }
796
+ });
797
+
798
+ // Event Aggregator
799
+ // ----------------
800
+
801
+ // A pub-sub object that can be used to decouple various parts
802
+ // of an application through event-driven architecture.
803
+ Marionette.EventAggregator = function(options){
804
+ _.extend(this, options);
805
+ };
806
+
807
+ _.extend(Marionette.EventAggregator.prototype, Backbone.Events, Marionette.BindTo, {
808
+ // Assumes the event aggregator itself is the
809
+ // object being bound to.
810
+ bindTo: function(eventName, callback, context){
811
+ return Marionette.BindTo.bindTo.call(this, this, eventName, callback, context);
812
+ }
813
+ });
814
+
815
+ // Template Cache
816
+ // --------------
817
+
818
+ // Manage templates stored in `<script>` blocks,
819
+ // caching them for faster access.
820
+ Marionette.TemplateCache = {
821
+ templates: {},
822
+ loaders: {},
823
+
824
+ // Get the specified template by id. Either
825
+ // retrieves the cached version, or loads it
826
+ // from the DOM.
827
+ get: function(templateId){
828
+ var that = this;
829
+ var templateRetrieval = $.Deferred();
830
+ var cachedTemplate = this.templates[templateId];
831
+
832
+ if (cachedTemplate){
833
+ templateRetrieval.resolve(cachedTemplate);
834
+ } else {
835
+ var loader = this.loaders[templateId];
836
+ if(loader) {
837
+ templateRetrieval = loader;
838
+ } else {
839
+ this.loaders[templateId] = templateRetrieval;
840
+
841
+ this.loadTemplate(templateId, function(template){
842
+ delete that.loaders[templateId];
843
+ that.templates[templateId] = template;
844
+ templateRetrieval.resolve(template);
845
+ });
846
+ }
847
+
848
+ }
849
+
850
+ return templateRetrieval.promise();
851
+ },
852
+
853
+ // Load a template from the DOM, by default. Override
854
+ // this method to provide your own template retrieval,
855
+ // such as asynchronous loading from a server.
856
+ loadTemplate: function(templateId, callback){
857
+ var template = $(templateId).html();
858
+
859
+ // Make sure we have a template before trying to compile it
860
+ if (!template || template.length === 0){
861
+ var msg = "Could not find template: '" + templateId + "'";
862
+ var err = new Error(msg);
863
+ err.name = "NoTemplateError";
864
+ throw err;
865
+ }
866
+
867
+ template = this.compileTemplate(template);
868
+
869
+ callback.call(this, template);
870
+ },
871
+
872
+ // Pre-compile the template before caching it. Override
873
+ // this method if you do not need to pre-compile a template
874
+ // (JST / RequireJS for example) or if you want to change
875
+ // the template engine used (Handebars, etc).
876
+ compileTemplate: function(rawTemplate){
877
+ return _.template(rawTemplate);
878
+ },
879
+
880
+ // Clear templates from the cache. If no arguments
881
+ // are specified, clears all templates:
882
+ // `clear()`
883
+ //
884
+ // If arguments are specified, clears each of the
885
+ // specified templates from the cache:
886
+ // `clear("#t1", "#t2", "...")`
887
+ clear: function(){
888
+ var i;
889
+ var length = arguments.length;
890
+
891
+ if (length > 0){
892
+ for(i=0; i<length; i++){
893
+ delete this.templates[arguments[i]];
894
+ }
895
+ } else {
896
+ this.templates = {};
897
+ }
898
+ }
899
+ };
900
+
901
+ // Renderer
902
+ // --------
903
+
904
+ // Render a template with data by passing in the template
905
+ // selector and the data to render.
906
+ Marionette.Renderer = {
907
+
908
+ // Render a template with data. The `template` parameter is
909
+ // passed to the `TemplateCache` object to retrieve the
910
+ // actual template. Override this method to provide your own
911
+ // custom rendering and template handling for all of Marionette.
912
+ render: function(template, data){
913
+ var that = this;
914
+ var asyncRender = $.Deferred();
915
+
916
+ var templateRetrieval = Marionette.TemplateCache.get(template);
917
+
918
+ $.when(templateRetrieval).then(function(template){
919
+ var html = that.renderTemplate(template, data);
920
+ asyncRender.resolve(html);
921
+ });
922
+
923
+ return asyncRender.promise();
924
+ },
925
+
926
+ // Default implementation uses underscore.js templates. Override
927
+ // this method to use your own templating engine.
928
+ renderTemplate: function(template, data){
929
+ var html = template(data);
930
+ return html;
931
+ }
932
+
933
+ };
934
+
935
+ // Modules
936
+ // -------
937
+
938
+ // The "Modules" object builds modules on an
939
+ // object that it is attached to. It should not be
940
+ // used on it's own, but should be attached to
941
+ // another object that will define modules.
942
+ Marionette.Modules = {
943
+
944
+ // Add modules to the application, providing direct
945
+ // access to your applicaiton object, Backbone,
946
+ // Marionette, jQuery and Underscore as parameters
947
+ // to a callback function.
948
+ module: function(moduleNames, moduleDefinition){
949
+ var moduleName, module, moduleOverride;
950
+ var parentModule = this;
951
+ var parentApp = this;
952
+ var moduleNames = moduleNames.split(".");
953
+
954
+ // Loop through all the parts of the module definition
955
+ var length = moduleNames.length;
956
+ for(var i = 0; i < length; i++){
957
+ var isLastModuleInChain = (i === length-1);
958
+
959
+ // Get the module name, and check if it exists on
960
+ // the current parent already
961
+ moduleName = moduleNames[i];
962
+ module = parentModule[moduleName];
963
+
964
+ // Create a new module if we don't have one already
965
+ if (!module){
966
+ module = new Marionette.Application();
967
+ }
968
+
969
+ // Check to see if we need to run the definition
970
+ // for the module. Only run the definition if one
971
+ // is supplied, and if we're at the last segment
972
+ // of the "Module.Name" chain.
973
+ if (isLastModuleInChain && moduleDefinition){
974
+ moduleOverride = moduleDefinition(module, parentApp, Backbone, Marionette, jQuery, _);
975
+ // If we have a module override, use it instead.
976
+ if (moduleOverride){
977
+ module = moduleOverride;
978
+ }
979
+ }
980
+
981
+ // If the defined module is not what we are
982
+ // currently storing as the module, replace it
983
+ if (parentModule[moduleName] !== module){
984
+ parentModule[moduleName] = module;
985
+ }
986
+
987
+ // Reset the parent module so that the next child
988
+ // in the list will be added to the correct parent
989
+ parentModule = module;
990
+ }
991
+
992
+ // Return the last module in the definition chain
993
+ return module;
994
+ }
995
+ };
996
+
997
+ // Helpers
998
+ // -------
999
+
1000
+ // For slicing `arguments` in functions
1001
+ var slice = Array.prototype.slice;
1002
+
1003
+ // Copy the `extend` function used by Backbone's classes
1004
+ var extend = Marionette.View.extend;
1005
+ Marionette.Region.extend = extend;
1006
+ Marionette.Application.extend = extend;
1007
+
1008
+ // Copy the `modules` feature on to the `Application` object
1009
+ Marionette.Application.prototype.module = Marionette.Modules.module;
1010
+
1011
+ // Copy the features of `BindTo` on to these objects
1012
+ _.extend(Marionette.View.prototype, Marionette.BindTo);
1013
+ _.extend(Marionette.Application.prototype, Marionette.BindTo);
1014
+ _.extend(Marionette.Region.prototype, Marionette.BindTo);
1015
+
1016
+ // A simple wrapper method for deferring a callback until
1017
+ // after another method has been called, passing the
1018
+ // results of the first method to the second. Uses jQuery's
1019
+ // deferred / promise objects, and $.when/then to make it
1020
+ // work.
1021
+ var callDeferredMethod = function(fn, callback, context){
1022
+ var promise;
1023
+ if (fn) { promise = fn.call(context); }
1024
+ $.when(promise).then(callback);
1025
+ }
1026
+
1027
+
1028
+ return Marionette;
1029
+ })(Backbone, _, window.jQuery || window.Zepto || window.ender);
1030
+
1031
+ return Backbone.Marionette;
1032
+
1033
+ }));
metadata ADDED
@@ -0,0 +1,62 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: marionette-amd-rails
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.8.4
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Godfrey Chan
9
+ - Andrey Subbotin
10
+ autorequire:
11
+ bindir: bin
12
+ cert_chain: []
13
+ date: 2012-06-04 00:00:00.000000000 Z
14
+ dependencies:
15
+ - !ruby/object:Gem::Dependency
16
+ name: rails
17
+ requirement: &70171054207440 !ruby/object:Gem::Requirement
18
+ none: false
19
+ requirements:
20
+ - - ! '>='
21
+ - !ruby/object:Gem::Version
22
+ version: 3.1.0
23
+ type: :runtime
24
+ prerelease: false
25
+ version_requirements: *70171054207440
26
+ description: Vendors AMD Backbone.Marionette for use with asset pipeline.
27
+ email:
28
+ - godfreykfc@gmail.com
29
+ - andrey@subbotin.me
30
+ executables: []
31
+ extensions: []
32
+ extra_rdoc_files: []
33
+ files:
34
+ - lib/marionette-rails.rb
35
+ - vendor/assets/javascripts/backbone.marionette.js
36
+ - LICENSE
37
+ - README.md
38
+ homepage: https://github.com/eploko/marionette-amd-rails
39
+ licenses: []
40
+ post_install_message:
41
+ rdoc_options: []
42
+ require_paths:
43
+ - lib
44
+ required_ruby_version: !ruby/object:Gem::Requirement
45
+ none: false
46
+ requirements:
47
+ - - ! '>='
48
+ - !ruby/object:Gem::Version
49
+ version: '0'
50
+ required_rubygems_version: !ruby/object:Gem::Requirement
51
+ none: false
52
+ requirements:
53
+ - - ! '>='
54
+ - !ruby/object:Gem::Version
55
+ version: 1.3.6
56
+ requirements: []
57
+ rubyforge_project:
58
+ rubygems_version: 1.8.10
59
+ signing_key:
60
+ specification_version: 3
61
+ summary: AMD Backbone.Marionette for Rails
62
+ test_files: []