yano-backbone-rails 2.2.0 → 2.2.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 4b5b4e2f77a8eef9485ebf79905b9d762350c0c7
4
- data.tar.gz: 7db2da55a2cc7d57eaaae4329d1565be86de5db5
3
+ metadata.gz: 676c52eac1bba3bc8489438d50db26145df21209
4
+ data.tar.gz: 30af094566337d1d44165e42814826689c4aee95
5
5
  SHA512:
6
- metadata.gz: cacd87ed66e1b401bdb4dba136fb48fb1ed95d1036248e1c9a099d9084d7402924dccd567c932c24e7089ff679a750e564df38389bed0763b4c80cab5b43abe4
7
- data.tar.gz: fe776a18567ec8805968d14329f5703ee4e0968a1930fe79ff09a020a9ad9232a5ea8a9a6c823a8ec199cec5a94d642728e1b4bb213e85cdd0ca1399f05fd5b1
6
+ metadata.gz: aaeeef9c73aa07c6b5708528cf782344511de7869b891a62f4a8c478d75a33e9f23ee85b6877b2d2b3a59eb0a1d406b28ac553229ba316f62d73850c7533d628
7
+ data.tar.gz: 41766aa65260e2636e29c0ac8cbf4ea4f48369540ff9b43984bdd907086e3ae90949179dc86a9af4c4227233ec40c701870b26b39dfa57bdade6178e573ecc5a
data/Rakefile CHANGED
@@ -99,12 +99,12 @@ namespace :update do
99
99
  version = Yano::Backbone::Rails::MARIONETTE_VERSION
100
100
 
101
101
  puts "Downloading marionette.js"
102
- puts "curl -o ./javascripts/marionette/backbone.marionette.js https://raw.githubusercontent.com/marionettejs/backbone.marionette/v#{version}/lib/core/backbone.marionette.js"
103
- puts `curl -o ./javascripts/marionette/backbone.marionette.js https://raw.githubusercontent.com/marionettejs/backbone.marionette/v#{version}/lib/core/backbone.marionette.js`
102
+ puts "curl -o ./javascripts/marionette/backbone.marionette.js https://raw.githubusercontent.com/marionettejs/backbone.marionette/v#{version}/lib/backbone.marionette.js"
103
+ puts `curl -o ./javascripts/marionette/backbone.marionette.js https://raw.githubusercontent.com/marionettejs/backbone.marionette/v#{version}/lib/backbone.marionette.js`
104
104
 
105
105
  puts "Downloading marionette.min.js"
106
- puts "curl -o ./javascripts/marionette/backbone.marionette.min.js https://raw.githubusercontent.com/marionettejs/backbone.marionette/v#{version}/lib/core/backbone.marionette.min.js"
107
- puts `curl -o ./javascripts/marionette/backbone.marionette.min.js https://raw.githubusercontent.com/marionettejs/backbone.marionette/v#{version}/lib/core/backbone.marionette.min.js`
106
+ puts "curl -o ./javascripts/marionette/backbone.marionette.min.js https://raw.githubusercontent.com/marionettejs/backbone.marionette/v#{version}/lib/backbone.marionette.min.js"
107
+ puts `curl -o ./javascripts/marionette/backbone.marionette.min.js https://raw.githubusercontent.com/marionettejs/backbone.marionette/v#{version}/lib/backbone.marionette.min.js`
108
108
  File.open('./javascripts/marionette/backbone.marionette.min.js', 'r') do |file|
109
109
  File.open("./javascripts/marionette/backbone.marionette.min.js.erb", 'w') do |new_file|
110
110
  while (line = file.gets)
@@ -122,8 +122,8 @@ namespace :update do
122
122
  puts "Downloading backbone.min.map"
123
123
  puts "mkdir -p ./source_maps"
124
124
  puts `mkdir -p ./source_maps`
125
- puts "curl -o ./source_maps/backbone.marionette.min.js.map https://raw.githubusercontent.com/marionettejs/backbone.marionette/v#{version}/lib/core/backbone.marionette.min.js.map"
126
- puts `curl -o ./source_maps/backbone.marionette.min.js.map https://raw.githubusercontent.com/marionettejs/backbone.marionette/v#{version}/lib/core/backbone.marionette.min.js.map`
125
+ puts "curl -o ./source_maps/backbone.marionette.min.js.map https://raw.githubusercontent.com/marionettejs/backbone.marionette/v#{version}/lib/backbone.marionette.min.js.map"
126
+ puts `curl -o ./source_maps/backbone.marionette.min.js.map https://raw.githubusercontent.com/marionettejs/backbone.marionette/v#{version}/lib/backbone.marionette.min.js.map`
127
127
  end
128
128
 
129
129
  puts "\e[32mDone!\e[0m"
@@ -1,7 +1,7 @@
1
1
  module Yano
2
2
  module Backbone
3
3
  module Rails
4
- VERSION = "2.2.0"
4
+ VERSION = "2.2.1"
5
5
  UNDERSCORE_VERSION = "1.8.3"
6
6
  BACKBONE_VERSION = "1.3.3"
7
7
  MARIONETTE_VERSION = "3.0.0"
@@ -1 +1,3240 @@
1
- 404: Not Found
1
+ // MarionetteJS (Backbone.Marionette)
2
+ // ----------------------------------
3
+ // v3.0.0
4
+ //
5
+ // Copyright (c)2016 Derick Bailey, Muted Solutions, LLC.
6
+ // Distributed under MIT license
7
+ //
8
+ // http://marionettejs.com
9
+
10
+
11
+ (function (global, factory) {
12
+ typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory(require('backbone'), require('underscore'), require('backbone.radio')) :
13
+ typeof define === 'function' && define.amd ? define(['backbone', 'underscore', 'backbone.radio'], factory) :
14
+ (global.Marionette = global['Mn'] = factory(global.Backbone,global._,global.Backbone.Radio));
15
+ }(this, function (Backbone,_,Radio) { 'use strict';
16
+
17
+ Backbone = 'default' in Backbone ? Backbone['default'] : Backbone;
18
+ _ = 'default' in _ ? _['default'] : _;
19
+ Radio = 'default' in Radio ? Radio['default'] : Radio;
20
+
21
+ var version = "3.0.0";
22
+
23
+ //Internal utility for creating context style global utils
24
+ var proxy = function proxy(method) {
25
+ return function (context) {
26
+ for (var _len = arguments.length, args = Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) {
27
+ args[_key - 1] = arguments[_key];
28
+ }
29
+
30
+ return method.apply(context, args);
31
+ };
32
+ };
33
+
34
+ // Borrow the Backbone `extend` method so we can use it as needed
35
+ var extend = Backbone.Model.extend;
36
+
37
+ var deprecate = function deprecate(message, test) {
38
+ if (_.isObject(message)) {
39
+ message = message.prev + ' is going to be removed in the future. ' + 'Please use ' + message.next + ' instead.' + (message.url ? ' See: ' + message.url : '');
40
+ }
41
+
42
+ if (!Marionette.DEV_MODE) {
43
+ return;
44
+ }
45
+
46
+ if ((test === undefined || !test) && !deprecate._cache[message]) {
47
+ deprecate._warn('Deprecation warning: ' + message);
48
+ deprecate._cache[message] = true;
49
+ }
50
+ };
51
+
52
+ deprecate._console = typeof console !== 'undefined' ? console : {};
53
+ deprecate._warn = function () {
54
+ var warn = deprecate._console.warn || deprecate._console.log || _.noop;
55
+ return warn.apply(deprecate._console, arguments);
56
+ };
57
+ deprecate._cache = {};
58
+
59
+ // Determine if `el` is a child of the document
60
+ var isNodeAttached = function isNodeAttached(el) {
61
+ return Backbone.$.contains(document.documentElement, el);
62
+ };
63
+
64
+ // Merge `keys` from `options` onto `this`
65
+ var mergeOptions = function mergeOptions(options, keys) {
66
+ if (!options) {
67
+ return;
68
+ }
69
+ _.extend(this, _.pick(options, keys));
70
+ };
71
+
72
+ // Marionette.getOption
73
+ // --------------------
74
+
75
+ // Retrieve an object, function or other value from the
76
+ // object or its `options`, with `options` taking precedence.
77
+ var getOption = function getOption(optionName) {
78
+ if (!optionName) {
79
+ return;
80
+ }
81
+ if (this.options && this.options[optionName] !== undefined) {
82
+ return this.options[optionName];
83
+ } else {
84
+ return this[optionName];
85
+ }
86
+ };
87
+
88
+ // Marionette.normalizeMethods
89
+ // ----------------------
90
+
91
+ // Pass in a mapping of events => functions or function names
92
+ // and return a mapping of events => functions
93
+ var normalizeMethods = function normalizeMethods(hash) {
94
+ var _this = this;
95
+
96
+ return _.reduce(hash, function (normalizedHash, method, name) {
97
+ if (!_.isFunction(method)) {
98
+ method = _this[method];
99
+ }
100
+ if (method) {
101
+ normalizedHash[name] = method;
102
+ }
103
+ return normalizedHash;
104
+ }, {});
105
+ };
106
+
107
+ // split the event name on the ":"
108
+ var splitter = /(^|:)(\w)/gi;
109
+
110
+ // take the event section ("section1:section2:section3")
111
+ // and turn it in to uppercase name onSection1Section2Section3
112
+ function getEventName(match, prefix, eventName) {
113
+ return eventName.toUpperCase();
114
+ }
115
+
116
+ // Trigger an event and/or a corresponding method name. Examples:
117
+ //
118
+ // `this.triggerMethod("foo")` will trigger the "foo" event and
119
+ // call the "onFoo" method.
120
+ //
121
+ // `this.triggerMethod("foo:bar")` will trigger the "foo:bar" event and
122
+ // call the "onFooBar" method.
123
+ function triggerMethod(event) {
124
+ // get the method name from the event name
125
+ var methodName = 'on' + event.replace(splitter, getEventName);
126
+ var method = getOption.call(this, methodName);
127
+ var result = void 0;
128
+
129
+ // call the onMethodName if it exists
130
+
131
+ for (var _len = arguments.length, args = Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) {
132
+ args[_key - 1] = arguments[_key];
133
+ }
134
+
135
+ if (_.isFunction(method)) {
136
+ // pass all args, except the event name
137
+ result = method.apply(this, args);
138
+ }
139
+
140
+ // trigger the event
141
+ this.trigger.apply(this, [event].concat(args));
142
+
143
+ return result;
144
+ }
145
+
146
+ // triggerMethodOn invokes triggerMethod on a specific context
147
+ //
148
+ // e.g. `Marionette.triggerMethodOn(view, 'show')`
149
+ // will trigger a "show" event or invoke onShow the view.
150
+ function triggerMethodOn(context) {
151
+ var fnc = _.isFunction(context.triggerMethod) ? context.triggerMethod : triggerMethod;
152
+
153
+ for (var _len2 = arguments.length, args = Array(_len2 > 1 ? _len2 - 1 : 0), _key2 = 1; _key2 < _len2; _key2++) {
154
+ args[_key2 - 1] = arguments[_key2];
155
+ }
156
+
157
+ return fnc.apply(context, args);
158
+ }
159
+
160
+ // Trigger method on children unless a pure Backbone.View
161
+ function triggerMethodChildren(view, event, shouldTrigger) {
162
+ if (!view._getImmediateChildren) {
163
+ return;
164
+ }
165
+ _.each(view._getImmediateChildren(), function (child) {
166
+ if (!shouldTrigger(child)) {
167
+ return;
168
+ }
169
+ triggerMethodOn(child, event, child);
170
+ });
171
+ }
172
+
173
+ function shouldTriggerAttach(view) {
174
+ return !view._isAttached;
175
+ }
176
+
177
+ function shouldAttach(view) {
178
+ if (!shouldTriggerAttach(view)) {
179
+ return false;
180
+ }
181
+ view._isAttached = true;
182
+ return true;
183
+ }
184
+
185
+ function shouldTriggerDetach(view) {
186
+ return view._isAttached;
187
+ }
188
+
189
+ function shouldDetach(view) {
190
+ if (!shouldTriggerDetach(view)) {
191
+ return false;
192
+ }
193
+ view._isAttached = false;
194
+ return true;
195
+ }
196
+
197
+ // Monitor a view's state, propagating attach/detach events to children and firing dom:refresh
198
+ // whenever a rendered view is attached or an attached view is rendered.
199
+ function monitorViewEvents(view) {
200
+ if (view._areViewEventsMonitored) {
201
+ return;
202
+ }
203
+
204
+ view._areViewEventsMonitored = true;
205
+
206
+ function handleBeforeAttach() {
207
+ triggerMethodChildren(view, 'before:attach', shouldTriggerAttach);
208
+ }
209
+
210
+ function handleAttach() {
211
+ triggerMethodChildren(view, 'attach', shouldAttach);
212
+ triggerDOMRefresh();
213
+ }
214
+
215
+ function handleBeforeDetach() {
216
+ triggerMethodChildren(view, 'before:detach', shouldTriggerDetach);
217
+ }
218
+
219
+ function handleDetach() {
220
+ triggerMethodChildren(view, 'detach', shouldDetach);
221
+ }
222
+
223
+ function handleRender() {
224
+ triggerDOMRefresh();
225
+ }
226
+
227
+ function triggerDOMRefresh() {
228
+ if (view._isAttached && view._isRendered) {
229
+ triggerMethodOn(view, 'dom:refresh', view);
230
+ }
231
+ }
232
+
233
+ view.on({
234
+ 'before:attach': handleBeforeAttach,
235
+ 'attach': handleAttach,
236
+ 'before:detach': handleBeforeDetach,
237
+ 'detach': handleDetach,
238
+ 'render': handleRender
239
+ });
240
+ }
241
+
242
+ var errorProps = ['description', 'fileName', 'lineNumber', 'name', 'message', 'number'];
243
+
244
+ var MarionetteError = extend.call(Error, {
245
+ urlRoot: 'http://marionettejs.com/docs/v' + version + '/',
246
+
247
+ constructor: function constructor(message, options) {
248
+ if (_.isObject(message)) {
249
+ options = message;
250
+ message = options.message;
251
+ } else if (!options) {
252
+ options = {};
253
+ }
254
+
255
+ var error = Error.call(this, message);
256
+ _.extend(this, _.pick(error, errorProps), _.pick(options, errorProps));
257
+
258
+ this.captureStackTrace();
259
+
260
+ if (options.url) {
261
+ this.url = this.urlRoot + options.url;
262
+ }
263
+ },
264
+ captureStackTrace: function captureStackTrace() {
265
+ if (Error.captureStackTrace) {
266
+ Error.captureStackTrace(this, MarionetteError);
267
+ }
268
+ },
269
+ toString: function toString() {
270
+ return this.name + ': ' + this.message + (this.url ? ' See: ' + this.url : '');
271
+ }
272
+ });
273
+
274
+ MarionetteError.extend = extend;
275
+
276
+ // Bind/unbind the event to handlers specified as a string of
277
+ // handler names on the target object
278
+ function bindFromStrings(target, entity, evt, methods, actionName) {
279
+ var methodNames = methods.split(/\s+/);
280
+
281
+ _.each(methodNames, function (methodName) {
282
+ var method = target[methodName];
283
+ if (!method) {
284
+ throw new MarionetteError('Method "' + methodName + '" was configured as an event handler, but does not exist.');
285
+ }
286
+
287
+ target[actionName](entity, evt, method);
288
+ });
289
+ }
290
+
291
+ // generic looping function
292
+ function iterateEvents(target, entity, bindings, actionName) {
293
+ if (!entity || !bindings) {
294
+ return;
295
+ }
296
+
297
+ // type-check bindings
298
+ if (!_.isObject(bindings)) {
299
+ throw new MarionetteError({
300
+ message: 'Bindings must be an object.',
301
+ url: 'marionette.functions.html#marionettebindevents'
302
+ });
303
+ }
304
+
305
+ // iterate the bindings and bind/unbind them
306
+ _.each(bindings, function (method, evt) {
307
+
308
+ // allow for a list of method names as a string
309
+ if (_.isString(method)) {
310
+ bindFromStrings(target, entity, evt, method, actionName);
311
+ return;
312
+ }
313
+
314
+ target[actionName](entity, evt, method);
315
+ });
316
+ }
317
+
318
+ function bindEvents(entity, bindings) {
319
+ iterateEvents(this, entity, bindings, 'listenTo');
320
+ return this;
321
+ }
322
+
323
+ function unbindEvents(entity, bindings) {
324
+ iterateEvents(this, entity, bindings, 'stopListening');
325
+ return this;
326
+ }
327
+
328
+ function iterateReplies(target, channel, bindings, actionName) {
329
+ if (!channel || !bindings) {
330
+ return;
331
+ }
332
+
333
+ // type-check bindings
334
+ if (!_.isObject(bindings)) {
335
+ throw new MarionetteError({
336
+ message: 'Bindings must be an object.',
337
+ url: 'marionette.functions.html#marionettebindrequests'
338
+ });
339
+ }
340
+
341
+ var normalizedRadioRequests = normalizeMethods.call(target, bindings);
342
+
343
+ channel[actionName](normalizedRadioRequests, target);
344
+ }
345
+
346
+ function bindRequests(channel, bindings) {
347
+ iterateReplies(this, channel, bindings, 'reply');
348
+ return this;
349
+ }
350
+
351
+ function unbindRequests(channel, bindings) {
352
+ iterateReplies(this, channel, bindings, 'stopReplying');
353
+ return this;
354
+ }
355
+
356
+ // Internal utility for setting options consistently across Mn
357
+ var setOptions = function setOptions() {
358
+ for (var _len = arguments.length, args = Array(_len), _key = 0; _key < _len; _key++) {
359
+ args[_key] = arguments[_key];
360
+ }
361
+
362
+ this.options = _.extend.apply(_, [{}, _.result(this, 'options')].concat(args));
363
+ };
364
+
365
+ var CommonMixin = {
366
+
367
+ // Imports the "normalizeMethods" to transform hashes of
368
+ // events=>function references/names to a hash of events=>function references
369
+ normalizeMethods: normalizeMethods,
370
+
371
+ _setOptions: setOptions,
372
+
373
+ // A handy way to merge passed-in options onto the instance
374
+ mergeOptions: mergeOptions,
375
+
376
+ // Enable getting options from this or this.options by name.
377
+ getOption: getOption,
378
+
379
+ // Enable binding view's events from another entity.
380
+ bindEvents: bindEvents,
381
+
382
+ // Enable unbinding view's events from another entity.
383
+ unbindEvents: unbindEvents
384
+ };
385
+
386
+ // MixinOptions
387
+ // - channelName
388
+ // - radioEvents
389
+ // - radioRequests
390
+
391
+ var RadioMixin = {
392
+ _initRadio: function _initRadio() {
393
+ var channelName = _.result(this, 'channelName');
394
+
395
+ if (!channelName) {
396
+ return;
397
+ }
398
+
399
+ /* istanbul ignore next */
400
+ if (!Radio) {
401
+ throw new MarionetteError({
402
+ name: 'BackboneRadioMissing',
403
+ message: 'The dependency "backbone.radio" is missing.'
404
+ });
405
+ }
406
+
407
+ var channel = this._channel = Radio.channel(channelName);
408
+
409
+ var radioEvents = _.result(this, 'radioEvents');
410
+ this.bindEvents(channel, radioEvents);
411
+
412
+ var radioRequests = _.result(this, 'radioRequests');
413
+ this.bindRequests(channel, radioRequests);
414
+
415
+ this.on('destroy', this._destroyRadio);
416
+ },
417
+ _destroyRadio: function _destroyRadio() {
418
+ this._channel.stopReplying(null, null, this);
419
+ },
420
+ getChannel: function getChannel() {
421
+ return this._channel;
422
+ },
423
+
424
+
425
+ // Proxy `bindEvents`
426
+ bindEvents: bindEvents,
427
+
428
+ // Proxy `unbindEvents`
429
+ unbindEvents: unbindEvents,
430
+
431
+ // Proxy `bindRequests`
432
+ bindRequests: bindRequests,
433
+
434
+ // Proxy `unbindRequests`
435
+ unbindRequests: unbindRequests
436
+
437
+ };
438
+
439
+ var ClassOptions = ['channelName', 'radioEvents', 'radioRequests'];
440
+
441
+ // A Base Class that other Classes should descend from.
442
+ // Object borrows many conventions and utilities from Backbone.
443
+ var MarionetteObject = function MarionetteObject(options) {
444
+ this._setOptions(options);
445
+ this.mergeOptions(options, ClassOptions);
446
+ this.cid = _.uniqueId(this.cidPrefix);
447
+ this._initRadio();
448
+ this.initialize.apply(this, arguments);
449
+ };
450
+
451
+ MarionetteObject.extend = extend;
452
+
453
+ // Object Methods
454
+ // --------------
455
+
456
+ // Ensure it can trigger events with Backbone.Events
457
+ _.extend(MarionetteObject.prototype, Backbone.Events, CommonMixin, RadioMixin, {
458
+ cidPrefix: 'mno',
459
+
460
+ // for parity with Marionette.AbstractView lifecyle
461
+ _isDestroyed: false,
462
+
463
+ isDestroyed: function isDestroyed() {
464
+ return this._isDestroyed;
465
+ },
466
+
467
+
468
+ //this is a noop method intended to be overridden by classes that extend from this base
469
+ initialize: function initialize() {},
470
+ destroy: function destroy() {
471
+ if (this._isDestroyed) {
472
+ return this;
473
+ }
474
+
475
+ for (var _len = arguments.length, args = Array(_len), _key = 0; _key < _len; _key++) {
476
+ args[_key] = arguments[_key];
477
+ }
478
+
479
+ this.triggerMethod.apply(this, ['before:destroy', this].concat(args));
480
+
481
+ this._isDestroyed = true;
482
+ this.triggerMethod.apply(this, ['destroy', this].concat(args));
483
+ this.stopListening();
484
+
485
+ return this;
486
+ },
487
+
488
+
489
+ triggerMethod: triggerMethod
490
+ });
491
+
492
+ // Manage templates stored in `<script>` blocks,
493
+ // caching them for faster access.
494
+ var TemplateCache = function TemplateCache(templateId) {
495
+ this.templateId = templateId;
496
+ };
497
+
498
+ // TemplateCache object-level methods. Manage the template
499
+ // caches from these method calls instead of creating
500
+ // your own TemplateCache instances
501
+ _.extend(TemplateCache, {
502
+ templateCaches: {},
503
+
504
+ // Get the specified template by id. Either
505
+ // retrieves the cached version, or loads it
506
+ // from the DOM.
507
+ get: function get(templateId, options) {
508
+ var cachedTemplate = this.templateCaches[templateId];
509
+
510
+ if (!cachedTemplate) {
511
+ cachedTemplate = new TemplateCache(templateId);
512
+ this.templateCaches[templateId] = cachedTemplate;
513
+ }
514
+
515
+ return cachedTemplate.load(options);
516
+ },
517
+
518
+
519
+ // Clear templates from the cache. If no arguments
520
+ // are specified, clears all templates:
521
+ // `clear()`
522
+ //
523
+ // If arguments are specified, clears each of the
524
+ // specified templates from the cache:
525
+ // `clear("#t1", "#t2", "...")`
526
+ clear: function clear() {
527
+ var i = void 0;
528
+
529
+ for (var _len = arguments.length, args = Array(_len), _key = 0; _key < _len; _key++) {
530
+ args[_key] = arguments[_key];
531
+ }
532
+
533
+ var length = args.length;
534
+
535
+ if (length > 0) {
536
+ for (i = 0; i < length; i++) {
537
+ delete this.templateCaches[args[i]];
538
+ }
539
+ } else {
540
+ this.templateCaches = {};
541
+ }
542
+ }
543
+ });
544
+
545
+ // TemplateCache instance methods, allowing each
546
+ // template cache object to manage its own state
547
+ // and know whether or not it has been loaded
548
+ _.extend(TemplateCache.prototype, {
549
+
550
+ // Internal method to load the template
551
+ load: function load(options) {
552
+ // Guard clause to prevent loading this template more than once
553
+ if (this.compiledTemplate) {
554
+ return this.compiledTemplate;
555
+ }
556
+
557
+ // Load the template and compile it
558
+ var template = this.loadTemplate(this.templateId, options);
559
+ this.compiledTemplate = this.compileTemplate(template, options);
560
+
561
+ return this.compiledTemplate;
562
+ },
563
+
564
+
565
+ // Load a template from the DOM, by default. Override
566
+ // this method to provide your own template retrieval
567
+ // For asynchronous loading with AMD/RequireJS, consider
568
+ // using a template-loader plugin as described here:
569
+ // https://github.com/marionettejs/backbone.marionette/wiki/Using-marionette-with-requirejs
570
+ loadTemplate: function loadTemplate(templateId, options) {
571
+ var $template = Backbone.$(templateId);
572
+
573
+ if (!$template.length) {
574
+ throw new MarionetteError({
575
+ name: 'NoTemplateError',
576
+ message: 'Could not find template: "' + templateId + '"'
577
+ });
578
+ }
579
+ return $template.html();
580
+ },
581
+
582
+
583
+ // Pre-compile the template before caching it. Override
584
+ // this method if you do not need to pre-compile a template
585
+ // (JST / RequireJS for example) or if you want to change
586
+ // the template engine used (Handebars, etc).
587
+ compileTemplate: function compileTemplate(rawTemplate, options) {
588
+ return _.template(rawTemplate, options);
589
+ }
590
+ });
591
+
592
+ var _invoke = _.invokeMap || _.invoke;
593
+
594
+ var toConsumableArray = function (arr) {
595
+ if (Array.isArray(arr)) {
596
+ for (var i = 0, arr2 = Array(arr.length); i < arr.length; i++) arr2[i] = arr[i];
597
+
598
+ return arr2;
599
+ } else {
600
+ return Array.from(arr);
601
+ }
602
+ };
603
+
604
+ // MixinOptions
605
+ // - behaviors
606
+
607
+ // Takes care of getting the behavior class
608
+ // given options and a key.
609
+ // If a user passes in options.behaviorClass
610
+ // default to using that.
611
+ // If a user passes in a Behavior Class directly, use that
612
+ // Otherwise delegate the lookup to the users `behaviorsLookup` implementation.
613
+ function getBehaviorClass(options, key) {
614
+ if (options.behaviorClass) {
615
+ return options.behaviorClass;
616
+ //treat functions as a Behavior constructor
617
+ } else if (_.isFunction(options)) {
618
+ return options;
619
+ }
620
+
621
+ // behaviorsLookup can be either a flat object or a method
622
+ if (_.isFunction(Marionette.Behaviors.behaviorsLookup)) {
623
+ return Marionette.Behaviors.behaviorsLookup(options, key)[key];
624
+ }
625
+
626
+ return Marionette.Behaviors.behaviorsLookup[key];
627
+ }
628
+
629
+ // Iterate over the behaviors object, for each behavior
630
+ // instantiate it and get its grouped behaviors.
631
+ // This accepts a list of behaviors in either an object or array form
632
+ function parseBehaviors(view, behaviors) {
633
+ return _.chain(behaviors).map(function (options, key) {
634
+ var BehaviorClass = getBehaviorClass(options, key);
635
+ //if we're passed a class directly instead of an object
636
+ var _options = options === BehaviorClass ? {} : options;
637
+ var behavior = new BehaviorClass(_options, view);
638
+ var nestedBehaviors = parseBehaviors(view, _.result(behavior, 'behaviors'));
639
+
640
+ return [behavior].concat(nestedBehaviors);
641
+ }).flatten().value();
642
+ }
643
+
644
+ var BehaviorsMixin = {
645
+ _initBehaviors: function _initBehaviors() {
646
+ var behaviors = _.result(this, 'behaviors');
647
+
648
+ // Behaviors defined on a view can be a flat object literal
649
+ // or it can be a function that returns an object.
650
+ this._behaviors = _.isObject(behaviors) ? parseBehaviors(this, behaviors) : {};
651
+ },
652
+ _getBehaviorTriggers: function _getBehaviorTriggers() {
653
+ var triggers = _invoke(this._behaviors, 'getTriggers');
654
+ return _.extend.apply(_, [{}].concat(toConsumableArray(triggers)));
655
+ },
656
+ _getBehaviorEvents: function _getBehaviorEvents() {
657
+ var events = _invoke(this._behaviors, 'getEvents');
658
+ return _.extend.apply(_, [{}].concat(toConsumableArray(events)));
659
+ },
660
+
661
+
662
+ // proxy behavior $el to the view's $el.
663
+ _proxyBehaviorViewProperties: function _proxyBehaviorViewProperties() {
664
+ _invoke(this._behaviors, 'proxyViewProperties');
665
+ },
666
+
667
+
668
+ // delegate modelEvents and collectionEvents
669
+ _delegateBehaviorEntityEvents: function _delegateBehaviorEntityEvents() {
670
+ _invoke(this._behaviors, 'delegateEntityEvents');
671
+ },
672
+
673
+
674
+ // undelegate modelEvents and collectionEvents
675
+ _undelegateBehaviorEntityEvents: function _undelegateBehaviorEntityEvents() {
676
+ _invoke(this._behaviors, 'undelegateEntityEvents');
677
+ },
678
+ _destroyBehaviors: function _destroyBehaviors(args) {
679
+ // Call destroy on each behavior after
680
+ // destroying the view.
681
+ // This unbinds event listeners
682
+ // that behaviors have registered for.
683
+ _invoke.apply(undefined, [this._behaviors, 'destroy'].concat(toConsumableArray(args)));
684
+ },
685
+ _bindBehaviorUIElements: function _bindBehaviorUIElements() {
686
+ _invoke(this._behaviors, 'bindUIElements');
687
+ },
688
+ _unbindBehaviorUIElements: function _unbindBehaviorUIElements() {
689
+ _invoke(this._behaviors, 'unbindUIElements');
690
+ },
691
+ _triggerEventOnBehaviors: function _triggerEventOnBehaviors() {
692
+ var behaviors = this._behaviors;
693
+ // Use good ol' for as this is a very hot function
694
+
695
+ for (var _len = arguments.length, args = Array(_len), _key = 0; _key < _len; _key++) {
696
+ args[_key] = arguments[_key];
697
+ }
698
+
699
+ for (var i = 0, length = behaviors && behaviors.length; i < length; i++) {
700
+ triggerMethod.apply(behaviors[i], args);
701
+ }
702
+ }
703
+ };
704
+
705
+ // MixinOptions
706
+ // - collectionEvents
707
+ // - modelEvents
708
+
709
+ var DelegateEntityEventsMixin = {
710
+ // Handle `modelEvents`, and `collectionEvents` configuration
711
+ _delegateEntityEvents: function _delegateEntityEvents(model, collection) {
712
+ this._undelegateEntityEvents(model, collection);
713
+
714
+ var modelEvents = _.result(this, 'modelEvents');
715
+ bindEvents.call(this, model, modelEvents);
716
+
717
+ var collectionEvents = _.result(this, 'collectionEvents');
718
+ bindEvents.call(this, collection, collectionEvents);
719
+ },
720
+ _undelegateEntityEvents: function _undelegateEntityEvents(model, collection) {
721
+ var modelEvents = _.result(this, 'modelEvents');
722
+ unbindEvents.call(this, model, modelEvents);
723
+
724
+ var collectionEvents = _.result(this, 'collectionEvents');
725
+ unbindEvents.call(this, collection, collectionEvents);
726
+ }
727
+ };
728
+
729
+ // Borrow event splitter from Backbone
730
+ var delegateEventSplitter = /^(\S+)\s*(.*)$/;
731
+
732
+ function uniqueName(eventName, selector) {
733
+ return [eventName + _.uniqueId('.evt'), selector].join(' ');
734
+ }
735
+
736
+ // Set event name to be namespaced using a unique index
737
+ // to generate a non colliding event namespace
738
+ // http://api.jquery.com/event.namespace/
739
+ var getUniqueEventName = function getUniqueEventName(eventName) {
740
+ var match = eventName.match(delegateEventSplitter);
741
+ return uniqueName(match[1], match[2]);
742
+ };
743
+
744
+ // Internal method to create an event handler for a given `triggerDef` like
745
+ // 'click:foo'
746
+ function buildViewTrigger(view, triggerDef) {
747
+ if (_.isString(triggerDef)) {
748
+ triggerDef = { event: triggerDef };
749
+ }
750
+
751
+ var eventName = triggerDef.event;
752
+ var shouldPreventDefault = triggerDef.preventDefault !== false;
753
+ var shouldStopPropagation = triggerDef.stopPropagation !== false;
754
+
755
+ return function (e) {
756
+ if (shouldPreventDefault) {
757
+ e.preventDefault();
758
+ }
759
+
760
+ if (shouldStopPropagation) {
761
+ e.stopPropagation();
762
+ }
763
+
764
+ view.triggerMethod(eventName, view);
765
+ };
766
+ }
767
+
768
+ var TriggersMixin = {
769
+
770
+ // Configure `triggers` to forward DOM events to view
771
+ // events. `triggers: {"click .foo": "do:foo"}`
772
+ _getViewTriggers: function _getViewTriggers(view, triggers) {
773
+ // Configure the triggers, prevent default
774
+ // action and stop propagation of DOM events
775
+ return _.reduce(triggers, function (events, value, key) {
776
+ key = getUniqueEventName(key);
777
+ events[key] = buildViewTrigger(view, value);
778
+ return events;
779
+ }, {});
780
+ }
781
+ };
782
+
783
+ // allows for the use of the @ui. syntax within
784
+ // a given key for triggers and events
785
+ // swaps the @ui with the associated selector.
786
+ // Returns a new, non-mutated, parsed events hash.
787
+ var _normalizeUIKeys = function _normalizeUIKeys(hash, ui) {
788
+ return _.reduce(hash, function (memo, val, key) {
789
+ var normalizedKey = normalizeUIString(key, ui);
790
+ memo[normalizedKey] = val;
791
+ return memo;
792
+ }, {});
793
+ };
794
+
795
+ // utility method for parsing @ui. syntax strings
796
+ // into associated selector
797
+ var normalizeUIString = function normalizeUIString(uiString, ui) {
798
+ return uiString.replace(/@ui\.[a-zA-Z-_$0-9]*/g, function (r) {
799
+ return ui[r.slice(4)];
800
+ });
801
+ };
802
+
803
+ // allows for the use of the @ui. syntax within
804
+ // a given value for regions
805
+ // swaps the @ui with the associated selector
806
+ var _normalizeUIValues = function _normalizeUIValues(hash, ui, properties) {
807
+ _.each(hash, function (val, key) {
808
+ if (_.isString(val)) {
809
+ hash[key] = normalizeUIString(val, ui);
810
+ } else if (_.isObject(val) && _.isArray(properties)) {
811
+ _.extend(val, _normalizeUIValues(_.pick(val, properties), ui));
812
+ /* Value is an object, and we got an array of embedded property names to normalize. */
813
+ _.each(properties, function (property) {
814
+ var propertyVal = val[property];
815
+ if (_.isString(propertyVal)) {
816
+ val[property] = normalizeUIString(propertyVal, ui);
817
+ }
818
+ });
819
+ }
820
+ });
821
+ return hash;
822
+ };
823
+
824
+ var UIMixin = {
825
+
826
+ // normalize the keys of passed hash with the views `ui` selectors.
827
+ // `{"@ui.foo": "bar"}`
828
+ normalizeUIKeys: function normalizeUIKeys(hash) {
829
+ var uiBindings = this._getUIBindings();
830
+ return _normalizeUIKeys(hash, uiBindings);
831
+ },
832
+
833
+
834
+ // normalize the values of passed hash with the views `ui` selectors.
835
+ // `{foo: "@ui.bar"}`
836
+ normalizeUIValues: function normalizeUIValues(hash, properties) {
837
+ var uiBindings = this._getUIBindings();
838
+ return _normalizeUIValues(hash, uiBindings, properties);
839
+ },
840
+ _getUIBindings: function _getUIBindings() {
841
+ var uiBindings = _.result(this, '_uiBindings');
842
+ var ui = _.result(this, 'ui');
843
+ return uiBindings || ui;
844
+ },
845
+
846
+
847
+ // This method binds the elements specified in the "ui" hash inside the view's code with
848
+ // the associated jQuery selectors.
849
+ _bindUIElements: function _bindUIElements() {
850
+ var _this = this;
851
+
852
+ if (!this.ui) {
853
+ return;
854
+ }
855
+
856
+ // store the ui hash in _uiBindings so they can be reset later
857
+ // and so re-rendering the view will be able to find the bindings
858
+ if (!this._uiBindings) {
859
+ this._uiBindings = this.ui;
860
+ }
861
+
862
+ // get the bindings result, as a function or otherwise
863
+ var bindings = _.result(this, '_uiBindings');
864
+
865
+ // empty the ui so we don't have anything to start with
866
+ this._ui = {};
867
+
868
+ // bind each of the selectors
869
+ _.each(bindings, function (selector, key) {
870
+ _this._ui[key] = _this.$(selector);
871
+ });
872
+
873
+ this.ui = this._ui;
874
+ },
875
+ _unbindUIElements: function _unbindUIElements() {
876
+ var _this2 = this;
877
+
878
+ if (!this.ui || !this._uiBindings) {
879
+ return;
880
+ }
881
+
882
+ // delete all of the existing ui bindings
883
+ _.each(this.ui, function ($el, name) {
884
+ delete _this2.ui[name];
885
+ });
886
+
887
+ // reset the ui element to the original bindings configuration
888
+ this.ui = this._uiBindings;
889
+ delete this._uiBindings;
890
+ delete this._ui;
891
+ },
892
+ _getUI: function _getUI(name) {
893
+ return this._ui[name];
894
+ }
895
+ };
896
+
897
+ // MixinOptions
898
+ // - behaviors
899
+ // - childViewEventPrefix
900
+ // - childViewEvents
901
+ // - childViewTriggers
902
+ // - collectionEvents
903
+ // - modelEvents
904
+ // - triggers
905
+ // - ui
906
+
907
+
908
+ var ViewMixin = {
909
+ supportsRenderLifecycle: true,
910
+ supportsDestroyLifecycle: true,
911
+
912
+ _isDestroyed: false,
913
+
914
+ isDestroyed: function isDestroyed() {
915
+ return !!this._isDestroyed;
916
+ },
917
+
918
+
919
+ _isRendered: false,
920
+
921
+ isRendered: function isRendered() {
922
+ return !!this._isRendered;
923
+ },
924
+
925
+
926
+ _isAttached: false,
927
+
928
+ isAttached: function isAttached() {
929
+ return !!this._isAttached;
930
+ },
931
+
932
+
933
+ // Overriding Backbone.View's `setElement` to handle
934
+ // if an el was previously defined. If so, the view might be
935
+ // rendered or attached on setElement.
936
+ setElement: function setElement() {
937
+ var hasEl = !!this.el;
938
+
939
+ Backbone.View.prototype.setElement.apply(this, arguments);
940
+
941
+ if (hasEl) {
942
+ this._isRendered = !!this.$el.length;
943
+ this._isAttached = isNodeAttached(this.el);
944
+ }
945
+
946
+ return this;
947
+ },
948
+
949
+
950
+ // Overriding Backbone.View's `delegateEvents` to handle
951
+ // `events` and `triggers`
952
+ delegateEvents: function delegateEvents(eventsArg) {
953
+
954
+ this._proxyBehaviorViewProperties();
955
+ this._buildEventProxies();
956
+
957
+ var viewEvents = this._getEvents(eventsArg);
958
+
959
+ if (typeof eventsArg === 'undefined') {
960
+ this.events = viewEvents;
961
+ }
962
+
963
+ var combinedEvents = _.extend({}, this._getBehaviorEvents(), viewEvents, this._getBehaviorTriggers(), this.getTriggers());
964
+
965
+ Backbone.View.prototype.delegateEvents.call(this, combinedEvents);
966
+
967
+ return this;
968
+ },
969
+ _getEvents: function _getEvents(eventsArg) {
970
+ var events = eventsArg || this.events;
971
+
972
+ if (_.isFunction(events)) {
973
+ return this.normalizeUIKeys(events.call(this));
974
+ }
975
+
976
+ return this.normalizeUIKeys(events);
977
+ },
978
+
979
+
980
+ // Configure `triggers` to forward DOM events to view
981
+ // events. `triggers: {"click .foo": "do:foo"}`
982
+ getTriggers: function getTriggers() {
983
+ if (!this.triggers) {
984
+ return;
985
+ }
986
+
987
+ // Allow `triggers` to be configured as a function
988
+ var triggers = this.normalizeUIKeys(_.result(this, 'triggers'));
989
+
990
+ // Configure the triggers, prevent default
991
+ // action and stop propagation of DOM events
992
+ return this._getViewTriggers(this, triggers);
993
+ },
994
+
995
+
996
+ // Handle `modelEvents`, and `collectionEvents` configuration
997
+ delegateEntityEvents: function delegateEntityEvents() {
998
+ this._delegateEntityEvents(this.model, this.collection);
999
+
1000
+ // bind each behaviors model and collection events
1001
+ this._delegateBehaviorEntityEvents();
1002
+
1003
+ return this;
1004
+ },
1005
+
1006
+
1007
+ // Handle unbinding `modelEvents`, and `collectionEvents` configuration
1008
+ undelegateEntityEvents: function undelegateEntityEvents() {
1009
+ this._undelegateEntityEvents(this.model, this.collection);
1010
+
1011
+ // unbind each behaviors model and collection events
1012
+ this._undelegateBehaviorEntityEvents();
1013
+
1014
+ return this;
1015
+ },
1016
+
1017
+
1018
+ // Internal helper method to verify whether the view hasn't been destroyed
1019
+ _ensureViewIsIntact: function _ensureViewIsIntact() {
1020
+ if (this._isDestroyed) {
1021
+ throw new MarionetteError({
1022
+ name: 'ViewDestroyedError',
1023
+ message: 'View (cid: "' + this.cid + '") has already been destroyed and cannot be used.'
1024
+ });
1025
+ }
1026
+ },
1027
+
1028
+
1029
+ // Handle destroying the view and its children.
1030
+ destroy: function destroy() {
1031
+ if (this._isDestroyed) {
1032
+ return this;
1033
+ }
1034
+ var shouldTriggerDetach = !!this._isAttached;
1035
+
1036
+ for (var _len = arguments.length, args = Array(_len), _key = 0; _key < _len; _key++) {
1037
+ args[_key] = arguments[_key];
1038
+ }
1039
+
1040
+ this.triggerMethod.apply(this, ['before:destroy', this].concat(args));
1041
+ if (shouldTriggerDetach) {
1042
+ this.triggerMethod('before:detach', this);
1043
+ }
1044
+
1045
+ // unbind UI elements
1046
+ this.unbindUIElements();
1047
+
1048
+ // remove the view from the DOM
1049
+ // https://github.com/jashkenas/backbone/blob/1.2.3/backbone.js#L1235
1050
+ this._removeElement();
1051
+
1052
+ if (shouldTriggerDetach) {
1053
+ this._isAttached = false;
1054
+ this.triggerMethod('detach', this);
1055
+ }
1056
+
1057
+ // remove children after the remove to prevent extra paints
1058
+ this._removeChildren();
1059
+
1060
+ this._destroyBehaviors(args);
1061
+
1062
+ this._isDestroyed = true;
1063
+ this._isRendered = false;
1064
+ this.triggerMethod.apply(this, ['destroy', this].concat(args));
1065
+
1066
+ this.stopListening();
1067
+
1068
+ return this;
1069
+ },
1070
+ bindUIElements: function bindUIElements() {
1071
+ this._bindUIElements();
1072
+ this._bindBehaviorUIElements();
1073
+
1074
+ return this;
1075
+ },
1076
+
1077
+
1078
+ // This method unbinds the elements specified in the "ui" hash
1079
+ unbindUIElements: function unbindUIElements() {
1080
+ this._unbindUIElements();
1081
+ this._unbindBehaviorUIElements();
1082
+
1083
+ return this;
1084
+ },
1085
+ getUI: function getUI(name) {
1086
+ this._ensureViewIsIntact();
1087
+ return this._getUI(name);
1088
+ },
1089
+
1090
+
1091
+ // used as the prefix for child view events
1092
+ // that are forwarded through the layoutview
1093
+ childViewEventPrefix: 'childview',
1094
+
1095
+ // import the `triggerMethod` to trigger events with corresponding
1096
+ // methods if the method exists
1097
+ triggerMethod: function triggerMethod$$() {
1098
+ for (var _len2 = arguments.length, args = Array(_len2), _key2 = 0; _key2 < _len2; _key2++) {
1099
+ args[_key2] = arguments[_key2];
1100
+ }
1101
+
1102
+ var ret = triggerMethod.apply(this, args);
1103
+
1104
+ this._triggerEventOnBehaviors.apply(this, args);
1105
+ this._triggerEventOnParentLayout.apply(this, args);
1106
+
1107
+ return ret;
1108
+ },
1109
+
1110
+
1111
+ // Cache `childViewEvents` and `childViewTriggers`
1112
+ _buildEventProxies: function _buildEventProxies() {
1113
+ this._childViewEvents = _.result(this, 'childViewEvents');
1114
+ this._childViewTriggers = _.result(this, 'childViewTriggers');
1115
+ },
1116
+ _triggerEventOnParentLayout: function _triggerEventOnParentLayout(eventName) {
1117
+ var layoutView = this._parentView();
1118
+ if (!layoutView) {
1119
+ return;
1120
+ }
1121
+
1122
+ // invoke triggerMethod on parent view
1123
+ var eventPrefix = _.result(layoutView, 'childViewEventPrefix');
1124
+ var prefixedEventName = eventPrefix + ':' + eventName;
1125
+
1126
+ for (var _len3 = arguments.length, args = Array(_len3 > 1 ? _len3 - 1 : 0), _key3 = 1; _key3 < _len3; _key3++) {
1127
+ args[_key3 - 1] = arguments[_key3];
1128
+ }
1129
+
1130
+ layoutView.triggerMethod.apply(layoutView, [prefixedEventName].concat(args));
1131
+
1132
+ // use the parent view's childViewEvents handler
1133
+ var childViewEvents = layoutView.normalizeMethods(layoutView._childViewEvents);
1134
+
1135
+ if (!!childViewEvents && _.isFunction(childViewEvents[eventName])) {
1136
+ childViewEvents[eventName].apply(layoutView, args);
1137
+ }
1138
+
1139
+ // use the parent view's proxyEvent handlers
1140
+ var childViewTriggers = layoutView._childViewTriggers;
1141
+
1142
+ // Call the event with the proxy name on the parent layout
1143
+ if (childViewTriggers && _.isString(childViewTriggers[eventName])) {
1144
+ layoutView.triggerMethod.apply(layoutView, [childViewTriggers[eventName]].concat(args));
1145
+ }
1146
+ },
1147
+
1148
+
1149
+ // Walk the _parent tree until we find a view (if one exists).
1150
+ // Returns the parent view hierarchically closest to this view.
1151
+ _parentView: function _parentView() {
1152
+ var parent = this._parent;
1153
+
1154
+ while (parent) {
1155
+ if (parent instanceof View) {
1156
+ return parent;
1157
+ }
1158
+ parent = parent._parent;
1159
+ }
1160
+ }
1161
+ };
1162
+
1163
+ _.extend(ViewMixin, BehaviorsMixin, CommonMixin, DelegateEntityEventsMixin, TriggersMixin, UIMixin);
1164
+
1165
+ function destroyBackboneView(view) {
1166
+ if (!view.supportsDestroyLifecycle) {
1167
+ triggerMethodOn(view, 'before:destroy', view);
1168
+ }
1169
+
1170
+ var shouldTriggerDetach = !!view._isAttached;
1171
+
1172
+ if (shouldTriggerDetach) {
1173
+ triggerMethodOn(view, 'before:detach', view);
1174
+ }
1175
+
1176
+ view.remove();
1177
+
1178
+ if (shouldTriggerDetach) {
1179
+ view._isAttached = false;
1180
+ triggerMethodOn(view, 'detach', view);
1181
+ }
1182
+
1183
+ view._isDestroyed = true;
1184
+
1185
+ if (!view.supportsDestroyLifecycle) {
1186
+ triggerMethodOn(view, 'destroy', view);
1187
+ }
1188
+ }
1189
+
1190
+ var ClassOptions$2 = ['allowMissingEl', 'parentEl', 'replaceElement'];
1191
+
1192
+ var Region = MarionetteObject.extend({
1193
+ cidPrefix: 'mnr',
1194
+ replaceElement: false,
1195
+ _isReplaced: false,
1196
+
1197
+ constructor: function constructor(options) {
1198
+ this._setOptions(options);
1199
+
1200
+ this.mergeOptions(options, ClassOptions$2);
1201
+
1202
+ // getOption necessary because options.el may be passed as undefined
1203
+ this._initEl = this.el = this.getOption('el');
1204
+
1205
+ // Handle when this.el is passed in as a $ wrapped element.
1206
+ this.el = this.el instanceof Backbone.$ ? this.el[0] : this.el;
1207
+
1208
+ if (!this.el) {
1209
+ throw new MarionetteError({
1210
+ name: 'NoElError',
1211
+ message: 'An "el" must be specified for a region.'
1212
+ });
1213
+ }
1214
+
1215
+ this.$el = this.getEl(this.el);
1216
+ MarionetteObject.call(this, options);
1217
+ },
1218
+
1219
+
1220
+ // Displays a backbone view instance inside of the region. Handles calling the `render`
1221
+ // method for you. Reads content directly from the `el` attribute. The `preventDestroy`
1222
+ // option can be used to prevent a view from the old view being destroyed on show.
1223
+ show: function show(view, options) {
1224
+ if (!this._ensureElement(options)) {
1225
+ return;
1226
+ }
1227
+ this._ensureView(view);
1228
+ if (view === this.currentView) {
1229
+ return this;
1230
+ }
1231
+
1232
+ this.triggerMethod('before:show', this, view, options);
1233
+
1234
+ monitorViewEvents(view);
1235
+
1236
+ this.empty(options);
1237
+
1238
+ // We need to listen for if a view is destroyed in a way other than through the region.
1239
+ // If this happens we need to remove the reference to the currentView since once a view
1240
+ // has been destroyed we can not reuse it.
1241
+ view.on('destroy', this.empty, this);
1242
+
1243
+ // Make this region the view's parent.
1244
+ // It's important that this parent binding happens before rendering so that any events
1245
+ // the child may trigger during render can also be triggered on the child's ancestor views.
1246
+ view._parent = this;
1247
+
1248
+ this._renderView(view);
1249
+
1250
+ this._attachView(view, options);
1251
+
1252
+ this.triggerMethod('show', this, view, options);
1253
+ return this;
1254
+ },
1255
+ _renderView: function _renderView(view) {
1256
+ if (view._isRendered) {
1257
+ return;
1258
+ }
1259
+
1260
+ if (!view.supportsRenderLifecycle) {
1261
+ triggerMethodOn(view, 'before:render', view);
1262
+ }
1263
+
1264
+ view.render();
1265
+
1266
+ if (!view.supportsRenderLifecycle) {
1267
+ view._isRendered = true;
1268
+ triggerMethodOn(view, 'render', view);
1269
+ }
1270
+ },
1271
+ _attachView: function _attachView(view) {
1272
+ var options = arguments.length <= 1 || arguments[1] === undefined ? {} : arguments[1];
1273
+
1274
+ var shouldTriggerAttach = !view._isAttached && isNodeAttached(this.el);
1275
+ var shouldReplaceEl = typeof options.replaceElement === 'undefined' ? !!_.result(this, 'replaceElement') : !!options.replaceElement;
1276
+
1277
+ if (shouldTriggerAttach) {
1278
+ triggerMethodOn(view, 'before:attach', view);
1279
+ }
1280
+
1281
+ this.attachHtml(view, shouldReplaceEl);
1282
+
1283
+ if (shouldTriggerAttach) {
1284
+ view._isAttached = true;
1285
+ triggerMethodOn(view, 'attach', view);
1286
+ }
1287
+
1288
+ this.currentView = view;
1289
+ },
1290
+ _ensureElement: function _ensureElement() {
1291
+ var options = arguments.length <= 0 || arguments[0] === undefined ? {} : arguments[0];
1292
+
1293
+ if (!_.isObject(this.el)) {
1294
+ this.$el = this.getEl(this.el);
1295
+ this.el = this.$el[0];
1296
+ }
1297
+
1298
+ if (!this.$el || this.$el.length === 0) {
1299
+ var allowMissingEl = typeof options.allowMissingEl === 'undefined' ? !!_.result(this, 'allowMissingEl') : !!options.allowMissingEl;
1300
+
1301
+ if (allowMissingEl) {
1302
+ return false;
1303
+ } else {
1304
+ throw new MarionetteError('An "el" must exist in DOM for this region ' + this.cid);
1305
+ }
1306
+ }
1307
+ return true;
1308
+ },
1309
+ _ensureView: function _ensureView(view) {
1310
+ if (!view) {
1311
+ throw new MarionetteError({
1312
+ name: 'ViewNotValid',
1313
+ message: 'The view passed is undefined and therefore invalid. You must pass a view instance to show.'
1314
+ });
1315
+ }
1316
+
1317
+ if (view._isDestroyed) {
1318
+ throw new MarionetteError({
1319
+ name: 'ViewDestroyedError',
1320
+ message: 'View (cid: "' + view.cid + '") has already been destroyed and cannot be used.'
1321
+ });
1322
+ }
1323
+ },
1324
+
1325
+
1326
+ // Override this method to change how the region finds the DOM element that it manages. Return
1327
+ // a jQuery selector object scoped to a provided parent el or the document if none exists.
1328
+ getEl: function getEl(el) {
1329
+ return Backbone.$(el, _.result(this, 'parentEl'));
1330
+ },
1331
+ _replaceEl: function _replaceEl(view) {
1332
+ // always restore the el to ensure the regions el is present before replacing
1333
+ this._restoreEl();
1334
+
1335
+ var parent = this.el.parentNode;
1336
+
1337
+ parent.replaceChild(view.el, this.el);
1338
+ this._isReplaced = true;
1339
+ },
1340
+
1341
+
1342
+ // Restore the region's element in the DOM.
1343
+ _restoreEl: function _restoreEl() {
1344
+ // There is nothing to replace
1345
+ if (!this._isReplaced) {
1346
+ return;
1347
+ }
1348
+
1349
+ var view = this.currentView;
1350
+
1351
+ if (!view) {
1352
+ return;
1353
+ }
1354
+
1355
+ var parent = view.el.parentNode;
1356
+
1357
+ if (!parent) {
1358
+ return;
1359
+ }
1360
+
1361
+ parent.replaceChild(this.el, view.el);
1362
+ this._isReplaced = false;
1363
+ },
1364
+
1365
+
1366
+ // Check to see if the region's el was replaced.
1367
+ isReplaced: function isReplaced() {
1368
+ return !!this._isReplaced;
1369
+ },
1370
+
1371
+
1372
+ // Override this method to change how the new view is appended to the `$el` that the
1373
+ // region is managing
1374
+ attachHtml: function attachHtml(view, shouldReplace) {
1375
+ if (shouldReplace) {
1376
+ // replace the region's node with the view's node
1377
+ this._replaceEl(view);
1378
+ } else {
1379
+ this.el.appendChild(view.el);
1380
+ }
1381
+ },
1382
+
1383
+
1384
+ // Destroy the current view, if there is one. If there is no current view, it does
1385
+ // nothing and returns immediately.
1386
+ empty: function empty() {
1387
+ var options = arguments.length <= 0 || arguments[0] === undefined ? { allowMissingEl: true } : arguments[0];
1388
+
1389
+ var view = this.currentView;
1390
+
1391
+ // If there is no view in the region we should only detach current html
1392
+ if (!view) {
1393
+ if (this._ensureElement(options)) {
1394
+ this.detachHtml();
1395
+ }
1396
+ return this;
1397
+ }
1398
+
1399
+ view.off('destroy', this.empty, this);
1400
+ this.triggerMethod('before:empty', this, view);
1401
+
1402
+ this._restoreEl();
1403
+
1404
+ delete this.currentView;
1405
+
1406
+ if (!view._isDestroyed) {
1407
+ this._removeView(view, options);
1408
+ delete view._parent;
1409
+ }
1410
+
1411
+ this.triggerMethod('empty', this, view);
1412
+ return this;
1413
+ },
1414
+ _removeView: function _removeView(view) {
1415
+ var _ref = arguments.length <= 1 || arguments[1] === undefined ? {} : arguments[1];
1416
+
1417
+ var preventDestroy = _ref.preventDestroy;
1418
+
1419
+ var shouldPreventDestroy = !!preventDestroy;
1420
+
1421
+ if (shouldPreventDestroy) {
1422
+ this._detachView(view);
1423
+ return;
1424
+ }
1425
+
1426
+ if (view.destroy) {
1427
+ view.destroy();
1428
+ } else {
1429
+ destroyBackboneView(view);
1430
+ }
1431
+ },
1432
+ _detachView: function _detachView(view) {
1433
+ var shouldTriggerDetach = !!view._isAttached;
1434
+ if (shouldTriggerDetach) {
1435
+ triggerMethodOn(view, 'before:detach', view);
1436
+ }
1437
+
1438
+ this.detachHtml();
1439
+
1440
+ if (shouldTriggerDetach) {
1441
+ view._isAttached = false;
1442
+ triggerMethodOn(view, 'detach', view);
1443
+ }
1444
+ },
1445
+
1446
+
1447
+ // Override this method to change how the region detaches current content
1448
+ detachHtml: function detachHtml() {
1449
+ this.$el.contents().detach();
1450
+ },
1451
+
1452
+
1453
+ // Checks whether a view is currently present within the region. Returns `true` if there is
1454
+ // and `false` if no view is present.
1455
+ hasView: function hasView() {
1456
+ return !!this.currentView;
1457
+ },
1458
+
1459
+
1460
+ // Reset the region by destroying any existing view and clearing out the cached `$el`.
1461
+ // The next time a view is shown via this region, the region will re-query the DOM for
1462
+ // the region's `el`.
1463
+ reset: function reset(options) {
1464
+ this.empty(options);
1465
+
1466
+ if (this.$el) {
1467
+ this.el = this._initEl;
1468
+ }
1469
+
1470
+ delete this.$el;
1471
+ return this;
1472
+ },
1473
+ destroy: function destroy(options) {
1474
+ this.reset(options);
1475
+ return MarionetteObject.prototype.destroy.apply(this, arguments);
1476
+ }
1477
+ });
1478
+
1479
+ // MixinOptions
1480
+ // - regions
1481
+ // - regionClass
1482
+
1483
+ var RegionsMixin = {
1484
+ regionClass: Region,
1485
+
1486
+ // Internal method to initialize the regions that have been defined in a
1487
+ // `regions` attribute on this View.
1488
+ _initRegions: function _initRegions() {
1489
+
1490
+ // init regions hash
1491
+ this.regions = this.regions || {};
1492
+ this._regions = {};
1493
+
1494
+ this.addRegions(_.result(this, 'regions'));
1495
+ },
1496
+
1497
+
1498
+ // Internal method to re-initialize all of the regions by updating
1499
+ // the `el` that they point to
1500
+ _reInitRegions: function _reInitRegions() {
1501
+ _invoke(this._regions, 'reset');
1502
+ },
1503
+
1504
+
1505
+ // Add a single region, by name, to the View
1506
+ addRegion: function addRegion(name, definition) {
1507
+ var regions = {};
1508
+ regions[name] = definition;
1509
+ return this.addRegions(regions)[name];
1510
+ },
1511
+
1512
+
1513
+ // Add multiple regions as a {name: definition, name2: def2} object literal
1514
+ addRegions: function addRegions(regions) {
1515
+ // If there's nothing to add, stop here.
1516
+ if (_.isEmpty(regions)) {
1517
+ return;
1518
+ }
1519
+
1520
+ // Normalize region selectors hash to allow
1521
+ // a user to use the @ui. syntax.
1522
+ regions = this.normalizeUIValues(regions, ['selector', 'el']);
1523
+
1524
+ // Add the regions definitions to the regions property
1525
+ this.regions = _.extend({}, this.regions, regions);
1526
+
1527
+ return this._addRegions(regions);
1528
+ },
1529
+
1530
+
1531
+ // internal method to build and add regions
1532
+ _addRegions: function _addRegions(regionDefinitions) {
1533
+ var _this = this;
1534
+
1535
+ return _.reduce(regionDefinitions, function (regions, definition, name) {
1536
+ regions[name] = _this._buildRegion(definition);
1537
+ _this._addRegion(regions[name], name);
1538
+ return regions;
1539
+ }, {});
1540
+ },
1541
+
1542
+
1543
+ // return the region instance from the definition
1544
+ _buildRegion: function _buildRegion(definition) {
1545
+ if (definition instanceof Region) {
1546
+ return definition;
1547
+ }
1548
+
1549
+ return this._buildRegionFromDefinition(definition);
1550
+ },
1551
+ _buildRegionFromDefinition: function _buildRegionFromDefinition(definition) {
1552
+ if (_.isString(definition)) {
1553
+ return this._buildRegionFromObject({ el: definition });
1554
+ }
1555
+
1556
+ if (_.isFunction(definition)) {
1557
+ return this._buildRegionFromRegionClass(definition);
1558
+ }
1559
+
1560
+ if (_.isObject(definition)) {
1561
+ return this._buildRegionFromObject(definition);
1562
+ }
1563
+
1564
+ throw new MarionetteError({
1565
+ message: 'Improper region configuration type.',
1566
+ url: 'marionette.region.html#region-configuration-types'
1567
+ });
1568
+ },
1569
+ _buildRegionFromObject: function _buildRegionFromObject(definition) {
1570
+ var RegionClass = definition.regionClass || this.regionClass;
1571
+
1572
+ var options = _.omit(definition, 'regionClass');
1573
+
1574
+ _.defaults(options, {
1575
+ el: definition.selector,
1576
+ parentEl: _.partial(_.result, this, 'el')
1577
+ });
1578
+
1579
+ return new RegionClass(options);
1580
+ },
1581
+
1582
+
1583
+ // Build the region directly from a given `RegionClass`
1584
+ _buildRegionFromRegionClass: function _buildRegionFromRegionClass(RegionClass) {
1585
+ return new RegionClass({
1586
+ parentEl: _.partial(_.result, this, 'el')
1587
+ });
1588
+ },
1589
+ _addRegion: function _addRegion(region, name) {
1590
+ this.triggerMethod('before:add:region', this, name, region);
1591
+
1592
+ region._parent = this;
1593
+
1594
+ this._regions[name] = region;
1595
+
1596
+ this.triggerMethod('add:region', this, name, region);
1597
+ },
1598
+
1599
+
1600
+ // Remove a single region from the View, by name
1601
+ removeRegion: function removeRegion(name) {
1602
+ var region = this._regions[name];
1603
+
1604
+ this._removeRegion(region, name);
1605
+
1606
+ return region;
1607
+ },
1608
+
1609
+
1610
+ // Remove all regions from the View
1611
+ removeRegions: function removeRegions() {
1612
+ var regions = this.getRegions();
1613
+
1614
+ _.each(this._regions, _.bind(this._removeRegion, this));
1615
+
1616
+ return regions;
1617
+ },
1618
+ _removeRegion: function _removeRegion(region, name) {
1619
+ this.triggerMethod('before:remove:region', this, name, region);
1620
+
1621
+ region.empty();
1622
+ region.stopListening();
1623
+
1624
+ delete this.regions[name];
1625
+ delete this._regions[name];
1626
+
1627
+ this.triggerMethod('remove:region', this, name, region);
1628
+ },
1629
+
1630
+
1631
+ // Empty all regions in the region manager, but
1632
+ // leave them attached
1633
+ emptyRegions: function emptyRegions() {
1634
+ var regions = this.getRegions();
1635
+ _invoke(regions, 'empty');
1636
+ return regions;
1637
+ },
1638
+
1639
+
1640
+ // Checks to see if view contains region
1641
+ // Accepts the region name
1642
+ // hasRegion('main')
1643
+ hasRegion: function hasRegion(name) {
1644
+ return !!this.getRegion(name);
1645
+ },
1646
+
1647
+
1648
+ // Provides access to regions
1649
+ // Accepts the region name
1650
+ // getRegion('main')
1651
+ getRegion: function getRegion(name) {
1652
+ return this._regions[name];
1653
+ },
1654
+
1655
+
1656
+ // Get all regions
1657
+ getRegions: function getRegions() {
1658
+ return _.clone(this._regions);
1659
+ },
1660
+ showChildView: function showChildView(name, view) {
1661
+ var region = this.getRegion(name);
1662
+
1663
+ for (var _len = arguments.length, args = Array(_len > 2 ? _len - 2 : 0), _key = 2; _key < _len; _key++) {
1664
+ args[_key - 2] = arguments[_key];
1665
+ }
1666
+
1667
+ return region.show.apply(region, [view].concat(args));
1668
+ },
1669
+ getChildView: function getChildView(name) {
1670
+ return this.getRegion(name).currentView;
1671
+ }
1672
+ };
1673
+
1674
+ // Render a template with data by passing in the template
1675
+ // selector and the data to render.
1676
+ var Renderer = {
1677
+
1678
+ // Render a template with data. The `template` parameter is
1679
+ // passed to the `TemplateCache` object to retrieve the
1680
+ // template function. Override this method to provide your own
1681
+ // custom rendering and template handling for all of Marionette.
1682
+ render: function render(template, data) {
1683
+ if (!template) {
1684
+ throw new MarionetteError({
1685
+ name: 'TemplateNotFoundError',
1686
+ message: 'Cannot render the template since its false, null or undefined.'
1687
+ });
1688
+ }
1689
+
1690
+ var templateFunc = _.isFunction(template) ? template : TemplateCache.get(template);
1691
+
1692
+ return templateFunc(data);
1693
+ }
1694
+ };
1695
+
1696
+ var ClassOptions$1 = ['behaviors', 'childViewEventPrefix', 'childViewEvents', 'childViewTriggers', 'collectionEvents', 'events', 'modelEvents', 'regionClass', 'regions', 'template', 'templateContext', 'triggers', 'ui'];
1697
+
1698
+ // The standard view. Includes view events, automatic rendering
1699
+ // of Underscore templates, nested views, and more.
1700
+ var View = Backbone.View.extend({
1701
+ constructor: function constructor(options) {
1702
+ this.render = _.bind(this.render, this);
1703
+
1704
+ this._setOptions(options);
1705
+
1706
+ this.mergeOptions(options, ClassOptions$1);
1707
+
1708
+ monitorViewEvents(this);
1709
+
1710
+ this._initBehaviors();
1711
+ this._initRegions();
1712
+
1713
+ var args = Array.prototype.slice.call(arguments);
1714
+ args[0] = this.options;
1715
+ Backbone.View.prototype.constructor.apply(this, args);
1716
+
1717
+ this.delegateEntityEvents();
1718
+ },
1719
+
1720
+
1721
+ // Serialize the view's model *or* collection, if
1722
+ // it exists, for the template
1723
+ serializeData: function serializeData() {
1724
+ if (!this.model && !this.collection) {
1725
+ return {};
1726
+ }
1727
+
1728
+ // If we have a model, we serialize that
1729
+ if (this.model) {
1730
+ return this.serializeModel();
1731
+ }
1732
+
1733
+ // Otherwise, we serialize the collection,
1734
+ // making it available under the `items` property
1735
+ return {
1736
+ items: this.serializeCollection()
1737
+ };
1738
+ },
1739
+
1740
+
1741
+ // Prepares the special `model` property of a view
1742
+ // for being displayed in the template. By default
1743
+ // we simply clone the attributes. Override this if
1744
+ // you need a custom transformation for your view's model
1745
+ serializeModel: function serializeModel() {
1746
+ if (!this.model) {
1747
+ return {};
1748
+ }
1749
+ return _.clone(this.model.attributes);
1750
+ },
1751
+
1752
+
1753
+ // Serialize a collection by cloning each of
1754
+ // its model's attributes
1755
+ serializeCollection: function serializeCollection() {
1756
+ if (!this.collection) {
1757
+ return {};
1758
+ }
1759
+ return this.collection.map(function (model) {
1760
+ return _.clone(model.attributes);
1761
+ });
1762
+ },
1763
+
1764
+
1765
+ // Render the view, defaulting to underscore.js templates.
1766
+ // You can override this in your view definition to provide
1767
+ // a very specific rendering for your view. In general, though,
1768
+ // you should override the `Marionette.Renderer` object to
1769
+ // change how Marionette renders views.
1770
+ // Subsequent renders after the first will re-render all nested
1771
+ // views.
1772
+ render: function render() {
1773
+ this._ensureViewIsIntact();
1774
+
1775
+ this.triggerMethod('before:render', this);
1776
+
1777
+ // If this is not the first render call, then we need to
1778
+ // re-initialize the `el` for each region
1779
+ if (this._isRendered) {
1780
+ this._reInitRegions();
1781
+ }
1782
+
1783
+ this._renderTemplate();
1784
+ this.bindUIElements();
1785
+
1786
+ this._isRendered = true;
1787
+ this.triggerMethod('render', this);
1788
+
1789
+ return this;
1790
+ },
1791
+
1792
+
1793
+ // Internal method to render the template with the serialized data
1794
+ // and template context via the `Marionette.Renderer` object.
1795
+ _renderTemplate: function _renderTemplate() {
1796
+ var template = this.getTemplate();
1797
+
1798
+ // Allow template-less views
1799
+ if (template === false) {
1800
+ return;
1801
+ }
1802
+
1803
+ // Add in entity data and template context
1804
+ var data = this.mixinTemplateContext(this.serializeData());
1805
+
1806
+ // Render and add to el
1807
+ var html = Renderer.render(template, data, this);
1808
+ this.attachElContent(html);
1809
+ },
1810
+
1811
+
1812
+ // Get the template for this view
1813
+ // instance. You can set a `template` attribute in the view
1814
+ // definition or pass a `template: "whatever"` parameter in
1815
+ // to the constructor options.
1816
+ getTemplate: function getTemplate() {
1817
+ return this.template;
1818
+ },
1819
+
1820
+
1821
+ // Mix in template context methods. Looks for a
1822
+ // `templateContext` attribute, which can either be an
1823
+ // object literal, or a function that returns an object
1824
+ // literal. All methods and attributes from this object
1825
+ // are copies to the object passed in.
1826
+ mixinTemplateContext: function mixinTemplateContext() {
1827
+ var target = arguments.length <= 0 || arguments[0] === undefined ? {} : arguments[0];
1828
+
1829
+ var templateContext = _.result(this, 'templateContext');
1830
+ return _.extend(target, templateContext);
1831
+ },
1832
+
1833
+
1834
+ // Attaches the content of a given view.
1835
+ // This method can be overridden to optimize rendering,
1836
+ // or to render in a non standard way.
1837
+ //
1838
+ // For example, using `innerHTML` instead of `$el.html`
1839
+ //
1840
+ // ```js
1841
+ // attachElContent(html) {
1842
+ // this.el.innerHTML = html;
1843
+ // return this;
1844
+ // }
1845
+ // ```
1846
+ attachElContent: function attachElContent(html) {
1847
+ this.$el.html(html);
1848
+
1849
+ return this;
1850
+ },
1851
+
1852
+
1853
+ // called by ViewMixin destroy
1854
+ _removeChildren: function _removeChildren() {
1855
+ this.removeRegions();
1856
+ },
1857
+ _getImmediateChildren: function _getImmediateChildren() {
1858
+ return _.chain(this.getRegions()).map('currentView').compact().value();
1859
+ }
1860
+ });
1861
+
1862
+ _.extend(View.prototype, ViewMixin, RegionsMixin);
1863
+
1864
+ var methods = ['forEach', 'each', 'map', 'find', 'detect', 'filter', 'select', 'reject', 'every', 'all', 'some', 'any', 'include', 'contains', 'invoke', 'toArray', 'first', 'initial', 'rest', 'last', 'without', 'isEmpty', 'pluck', 'reduce'];
1865
+
1866
+ var emulateCollection = function emulateCollection(object, listProperty) {
1867
+ _.each(methods, function (method) {
1868
+ object[method] = function () {
1869
+ var list = _.values(_.result(this, listProperty));
1870
+ var args = [list].concat(_.toArray(arguments));
1871
+ return _[method].apply(_, args);
1872
+ };
1873
+ });
1874
+ };
1875
+
1876
+ // Provide a container to store, retrieve and
1877
+ // shut down child views.
1878
+ var Container = function Container(views) {
1879
+ this._views = {};
1880
+ this._indexByModel = {};
1881
+ this._indexByCustom = {};
1882
+ this._updateLength();
1883
+
1884
+ _.each(views, _.bind(this.add, this));
1885
+ };
1886
+
1887
+ emulateCollection(Container.prototype, '_views');
1888
+
1889
+ // Container Methods
1890
+ // -----------------
1891
+
1892
+ _.extend(Container.prototype, {
1893
+
1894
+ // Add a view to this container. Stores the view
1895
+ // by `cid` and makes it searchable by the model
1896
+ // cid (and model itself). Optionally specify
1897
+ // a custom key to store an retrieve the view.
1898
+ add: function add(view, customIndex) {
1899
+ return this._add(view, customIndex)._updateLength();
1900
+ },
1901
+
1902
+
1903
+ // To be used when avoiding call _updateLength
1904
+ // When you are done adding all your new views
1905
+ // call _updateLength
1906
+ _add: function _add(view, customIndex) {
1907
+ var viewCid = view.cid;
1908
+
1909
+ // store the view
1910
+ this._views[viewCid] = view;
1911
+
1912
+ // index it by model
1913
+ if (view.model) {
1914
+ this._indexByModel[view.model.cid] = viewCid;
1915
+ }
1916
+
1917
+ // index by custom
1918
+ if (customIndex) {
1919
+ this._indexByCustom[customIndex] = viewCid;
1920
+ }
1921
+
1922
+ return this;
1923
+ },
1924
+
1925
+
1926
+ // Find a view by the model that was attached to
1927
+ // it. Uses the model's `cid` to find it.
1928
+ findByModel: function findByModel(model) {
1929
+ return this.findByModelCid(model.cid);
1930
+ },
1931
+
1932
+
1933
+ // Find a view by the `cid` of the model that was attached to
1934
+ // it. Uses the model's `cid` to find the view `cid` and
1935
+ // retrieve the view using it.
1936
+ findByModelCid: function findByModelCid(modelCid) {
1937
+ var viewCid = this._indexByModel[modelCid];
1938
+ return this.findByCid(viewCid);
1939
+ },
1940
+
1941
+
1942
+ // Find a view by a custom indexer.
1943
+ findByCustom: function findByCustom(index) {
1944
+ var viewCid = this._indexByCustom[index];
1945
+ return this.findByCid(viewCid);
1946
+ },
1947
+
1948
+
1949
+ // Find by index. This is not guaranteed to be a
1950
+ // stable index.
1951
+ findByIndex: function findByIndex(index) {
1952
+ return _.values(this._views)[index];
1953
+ },
1954
+
1955
+
1956
+ // retrieve a view by its `cid` directly
1957
+ findByCid: function findByCid(cid) {
1958
+ return this._views[cid];
1959
+ },
1960
+
1961
+
1962
+ // Remove a view
1963
+ remove: function remove(view) {
1964
+ return this._remove(view)._updateLength();
1965
+ },
1966
+
1967
+
1968
+ // To be used when avoiding call _updateLength
1969
+ // When you are done adding all your new views
1970
+ // call _updateLength
1971
+ _remove: function _remove(view) {
1972
+ var viewCid = view.cid;
1973
+
1974
+ // delete model index
1975
+ if (view.model) {
1976
+ delete this._indexByModel[view.model.cid];
1977
+ }
1978
+
1979
+ // delete custom index
1980
+ _.some(this._indexByCustom, _.bind(function (cid, key) {
1981
+ if (cid === viewCid) {
1982
+ delete this._indexByCustom[key];
1983
+ return true;
1984
+ }
1985
+ }, this));
1986
+
1987
+ // remove the view from the container
1988
+ delete this._views[viewCid];
1989
+
1990
+ return this;
1991
+ },
1992
+
1993
+
1994
+ // Update the `.length` attribute on this container
1995
+ _updateLength: function _updateLength() {
1996
+ this.length = _.size(this._views);
1997
+
1998
+ return this;
1999
+ }
2000
+ });
2001
+
2002
+ var ClassOptions$3 = ['behaviors', 'childView', 'childViewEventPrefix', 'childViewEvents', 'childViewOptions', 'childViewTriggers', 'collectionEvents', 'events', 'filter', 'emptyView', 'emptyViewOptions', 'modelEvents', 'reorderOnSort', 'sort', 'triggers', 'ui', 'viewComparator'];
2003
+
2004
+ // A view that iterates over a Backbone.Collection
2005
+ // and renders an individual child view for each model.
2006
+ var CollectionView = Backbone.View.extend({
2007
+
2008
+ // flag for maintaining the sorted order of the collection
2009
+ sort: true,
2010
+
2011
+ // constructor
2012
+ // option to pass `{sort: false}` to prevent the `CollectionView` from
2013
+ // maintaining the sorted order of the collection.
2014
+ // This will fallback onto appending childView's to the end.
2015
+ //
2016
+ // option to pass `{viewComparator: compFunction()}` to allow the `CollectionView`
2017
+ // to use a custom sort order for the collection.
2018
+ constructor: function constructor(options) {
2019
+ this.render = _.bind(this.render, this);
2020
+
2021
+ this._setOptions(options);
2022
+
2023
+ this.mergeOptions(options, ClassOptions$3);
2024
+
2025
+ monitorViewEvents(this);
2026
+
2027
+ this._initBehaviors();
2028
+ this.once('render', this._initialEvents);
2029
+ this._initChildViewStorage();
2030
+ this._bufferedChildren = [];
2031
+
2032
+ var args = Array.prototype.slice.call(arguments);
2033
+ args[0] = this.options;
2034
+ Backbone.View.prototype.constructor.apply(this, args);
2035
+
2036
+ this.delegateEntityEvents();
2037
+ },
2038
+
2039
+
2040
+ // Instead of inserting elements one by one into the page, it's much more performant to insert
2041
+ // elements into a document fragment and then insert that document fragment into the page
2042
+ _startBuffering: function _startBuffering() {
2043
+ this._isBuffering = true;
2044
+ },
2045
+ _endBuffering: function _endBuffering() {
2046
+ var shouldTriggerAttach = !!this._isAttached;
2047
+ var triggerOnChildren = shouldTriggerAttach ? this._getImmediateChildren() : [];
2048
+
2049
+ this._isBuffering = false;
2050
+
2051
+ _.each(triggerOnChildren, function (child) {
2052
+ triggerMethodOn(child, 'before:attach', child);
2053
+ });
2054
+
2055
+ this.attachBuffer(this, this._createBuffer());
2056
+
2057
+ _.each(triggerOnChildren, function (child) {
2058
+ child._isAttached = true;
2059
+ triggerMethodOn(child, 'attach', child);
2060
+ });
2061
+
2062
+ this._bufferedChildren = [];
2063
+ },
2064
+ _getImmediateChildren: function _getImmediateChildren() {
2065
+ return _.values(this.children._views);
2066
+ },
2067
+
2068
+
2069
+ // Configured the initial events that the collection view binds to.
2070
+ _initialEvents: function _initialEvents() {
2071
+ if (this.collection) {
2072
+ this.listenTo(this.collection, 'add', this._onCollectionAdd);
2073
+ this.listenTo(this.collection, 'remove', this._onCollectionRemove);
2074
+ this.listenTo(this.collection, 'reset', this.render);
2075
+
2076
+ if (this.sort) {
2077
+ this.listenTo(this.collection, 'sort', this._sortViews);
2078
+ }
2079
+ }
2080
+ },
2081
+
2082
+
2083
+ // Handle a child added to the collection
2084
+ _onCollectionAdd: function _onCollectionAdd(child, collection, opts) {
2085
+ // `index` is present when adding with `at` since BB 1.2; indexOf fallback for < 1.2
2086
+ var index = opts.at !== undefined && (opts.index || collection.indexOf(child));
2087
+
2088
+ // When filtered or when there is no initial index, calculate index.
2089
+ if (this.filter || index === false) {
2090
+ index = _.indexOf(this._filteredSortedModels(index), child);
2091
+ }
2092
+
2093
+ if (this._shouldAddChild(child, index)) {
2094
+ this._destroyEmptyView();
2095
+ var ChildView = this._getChildView(child);
2096
+ this._addChild(child, ChildView, index);
2097
+ }
2098
+ },
2099
+
2100
+
2101
+ // get the child view by model it holds, and remove it
2102
+ _onCollectionRemove: function _onCollectionRemove(model) {
2103
+ var view = this.children.findByModel(model);
2104
+ this.removeChildView(view);
2105
+ this._checkEmpty();
2106
+ },
2107
+
2108
+
2109
+ // Render children views. Override this method to provide your own implementation of a
2110
+ // render function for the collection view.
2111
+ render: function render() {
2112
+ this._ensureViewIsIntact();
2113
+ this.triggerMethod('before:render', this);
2114
+ this._renderChildren();
2115
+ this._isRendered = true;
2116
+ this.triggerMethod('render', this);
2117
+ return this;
2118
+ },
2119
+
2120
+
2121
+ // An efficient rendering used for filtering. Instead of modifying the whole DOM for the
2122
+ // collection view, we are only adding or removing the related childrenViews.
2123
+ setFilter: function setFilter(filter) {
2124
+ var _ref = arguments.length <= 1 || arguments[1] === undefined ? {} : arguments[1];
2125
+
2126
+ var preventRender = _ref.preventRender;
2127
+
2128
+ var canBeRendered = this._isRendered && !this._isDestroyed;
2129
+ var filterChanged = this.filter !== filter;
2130
+ var shouldRender = canBeRendered && filterChanged && !preventRender;
2131
+
2132
+ if (shouldRender) {
2133
+ var previousModels = this._filteredSortedModels();
2134
+ this.filter = filter;
2135
+ var models = this._filteredSortedModels();
2136
+ this._applyModelDeltas(models, previousModels);
2137
+ } else {
2138
+ this.filter = filter;
2139
+ }
2140
+
2141
+ return this;
2142
+ },
2143
+
2144
+
2145
+ // `removeFilter` is actually an alias for removing filters.
2146
+ removeFilter: function removeFilter(options) {
2147
+ return this.setFilter(null, options);
2148
+ },
2149
+
2150
+
2151
+ // Calculate and apply difference by cid between `models` and `previousModels`.
2152
+ _applyModelDeltas: function _applyModelDeltas(models, previousModels) {
2153
+ var _this = this;
2154
+
2155
+ var currentIds = {};
2156
+ _.each(models, function (model, index) {
2157
+ var addedChildNotExists = !_this.children.findByModel(model);
2158
+ if (addedChildNotExists) {
2159
+ _this._onCollectionAdd(model, _this.collection, { at: index });
2160
+ }
2161
+ currentIds[model.cid] = true;
2162
+ });
2163
+ _.each(previousModels, function (prevModel) {
2164
+ var removedChildExists = !currentIds[prevModel.cid] && _this.children.findByModel(prevModel);
2165
+ if (removedChildExists) {
2166
+ _this._onCollectionRemove(prevModel);
2167
+ }
2168
+ });
2169
+ },
2170
+
2171
+
2172
+ // Reorder DOM after sorting. When your element's rendering do not use their index,
2173
+ // you can pass reorderOnSort: true to only reorder the DOM after a sort instead of
2174
+ // rendering all the collectionView.
2175
+ reorder: function reorder() {
2176
+ var _this2 = this;
2177
+
2178
+ var children = this.children;
2179
+ var models = this._filteredSortedModels();
2180
+
2181
+ if (!models.length && this._showingEmptyView) {
2182
+ return this;
2183
+ }
2184
+
2185
+ var anyModelsAdded = _.some(models, function (model) {
2186
+ return !children.findByModel(model);
2187
+ });
2188
+
2189
+ // If there are any new models added due to filtering we need to add child views,
2190
+ // so render as normal.
2191
+ if (anyModelsAdded) {
2192
+ this.render();
2193
+ } else {
2194
+ (function () {
2195
+ // Get the DOM nodes in the same order as the models.
2196
+ var elsToReorder = _.map(models, function (model, index) {
2197
+ var view = children.findByModel(model);
2198
+ view._index = index;
2199
+ return view.el;
2200
+ });
2201
+
2202
+ // Find the views that were children before but aren't in this new ordering.
2203
+ var filteredOutViews = children.filter(function (view) {
2204
+ return !_.contains(elsToReorder, view.el);
2205
+ });
2206
+
2207
+ _this2.triggerMethod('before:reorder', _this2);
2208
+
2209
+ // Since append moves elements that are already in the DOM, appending the elements
2210
+ // will effectively reorder them.
2211
+ _this2._appendReorderedChildren(elsToReorder);
2212
+
2213
+ // remove any views that have been filtered out
2214
+ _.each(filteredOutViews, _.bind(_this2.removeChildView, _this2));
2215
+ _this2._checkEmpty();
2216
+
2217
+ _this2.triggerMethod('reorder', _this2);
2218
+ })();
2219
+ }
2220
+ return this;
2221
+ },
2222
+
2223
+
2224
+ // Render view after sorting. Override this method to change how the view renders
2225
+ // after a `sort` on the collection.
2226
+ resortView: function resortView() {
2227
+ if (this.reorderOnSort) {
2228
+ this.reorder();
2229
+ } else {
2230
+ this._renderChildren();
2231
+ }
2232
+ return this;
2233
+ },
2234
+
2235
+
2236
+ // Internal method. This checks for any changes in the order of the collection.
2237
+ // If the index of any view doesn't match, it will render.
2238
+ _sortViews: function _sortViews() {
2239
+ var _this3 = this;
2240
+
2241
+ var models = this._filteredSortedModels();
2242
+
2243
+ // check for any changes in sort order of views
2244
+ var orderChanged = _.find(models, function (item, index) {
2245
+ var view = _this3.children.findByModel(item);
2246
+ return !view || view._index !== index;
2247
+ });
2248
+
2249
+ if (orderChanged) {
2250
+ this.resortView();
2251
+ }
2252
+ },
2253
+
2254
+
2255
+ // Internal reference to what index a `emptyView` is.
2256
+ _emptyViewIndex: -1,
2257
+
2258
+ // Internal method. Separated so that CompositeView can append to the childViewContainer
2259
+ // if necessary
2260
+ _appendReorderedChildren: function _appendReorderedChildren(children) {
2261
+ this.$el.append(children);
2262
+ },
2263
+
2264
+
2265
+ // Internal method. Separated so that CompositeView can have more control over events
2266
+ // being triggered, around the rendering process
2267
+ _renderChildren: function _renderChildren() {
2268
+ if (this._isRendered) {
2269
+ this._destroyEmptyView();
2270
+ this._destroyChildren({ checkEmpty: false });
2271
+ }
2272
+
2273
+ var models = this._filteredSortedModels();
2274
+ if (this.isEmpty({ processedModels: models })) {
2275
+ this._showEmptyView();
2276
+ } else {
2277
+ this.triggerMethod('before:render:children', this);
2278
+ this._startBuffering();
2279
+ this._showCollection(models);
2280
+ this._endBuffering();
2281
+ this.triggerMethod('render:children', this);
2282
+ }
2283
+ },
2284
+
2285
+
2286
+ // Internal method to loop through collection and show each child view.
2287
+ _showCollection: function _showCollection(models) {
2288
+ var _this4 = this;
2289
+
2290
+ _.each(models, function (child, index) {
2291
+ var ChildView = _this4._getChildView(child);
2292
+ _this4._addChild(child, ChildView, index);
2293
+ });
2294
+ },
2295
+
2296
+
2297
+ // Allow the collection to be sorted by a custom view comparator
2298
+ _filteredSortedModels: function _filteredSortedModels(addedAt) {
2299
+ if (!this.collection || !this.collection.length) {
2300
+ return [];
2301
+ }
2302
+
2303
+ var viewComparator = this.getViewComparator();
2304
+ var models = this.collection.models;
2305
+ addedAt = Math.min(Math.max(addedAt, 0), models.length - 1);
2306
+
2307
+ if (viewComparator) {
2308
+ var addedModel = void 0;
2309
+ // Preserve `at` location, even for a sorted view
2310
+ if (addedAt) {
2311
+ addedModel = models[addedAt];
2312
+ models = models.slice(0, addedAt).concat(models.slice(addedAt + 1));
2313
+ }
2314
+ models = this._sortModelsBy(models, viewComparator);
2315
+ if (addedModel) {
2316
+ models.splice(addedAt, 0, addedModel);
2317
+ }
2318
+ }
2319
+
2320
+ // Filter after sorting in case the filter uses the index
2321
+ models = this._filterModels(models);
2322
+
2323
+ return models;
2324
+ },
2325
+ getViewComparator: function getViewComparator() {
2326
+ return this.viewComparator;
2327
+ },
2328
+
2329
+
2330
+ // Filter an array of models, if a filter exists
2331
+ _filterModels: function _filterModels(models) {
2332
+ var _this5 = this;
2333
+
2334
+ if (this.filter) {
2335
+ models = _.filter(models, function (model, index) {
2336
+ return _this5._shouldAddChild(model, index);
2337
+ });
2338
+ }
2339
+ return models;
2340
+ },
2341
+ _sortModelsBy: function _sortModelsBy(models, comparator) {
2342
+ if (typeof comparator === 'string') {
2343
+ return _.sortBy(models, function (model) {
2344
+ return model.get(comparator);
2345
+ });
2346
+ } else if (comparator.length === 1) {
2347
+ return _.sortBy(models, _.bind(comparator, this));
2348
+ } else {
2349
+ return models.sort(_.bind(comparator, this));
2350
+ }
2351
+ },
2352
+
2353
+
2354
+ // Internal method to show an empty view in place of a collection of child views,
2355
+ // when the collection is empty
2356
+ _showEmptyView: function _showEmptyView() {
2357
+ var EmptyView = this._getEmptyView();
2358
+
2359
+ if (EmptyView && !this._showingEmptyView) {
2360
+ this._showingEmptyView = true;
2361
+
2362
+ var model = new Backbone.Model();
2363
+ var emptyViewOptions = this.emptyViewOptions || this.childViewOptions;
2364
+ if (_.isFunction(emptyViewOptions)) {
2365
+ emptyViewOptions = emptyViewOptions.call(this, model, this._emptyViewIndex);
2366
+ }
2367
+
2368
+ var view = this.buildChildView(model, EmptyView, emptyViewOptions);
2369
+
2370
+ this.triggerMethod('before:render:empty', this, view);
2371
+ this._addChildView(view, 0);
2372
+ this.triggerMethod('render:empty', this, view);
2373
+
2374
+ view._parent = this;
2375
+ }
2376
+ },
2377
+
2378
+
2379
+ // Internal method to destroy an existing emptyView instance if one exists. Called when
2380
+ // a collection view has been rendered empty, and then a child is added to the collection.
2381
+ _destroyEmptyView: function _destroyEmptyView() {
2382
+ if (this._showingEmptyView) {
2383
+ this.triggerMethod('before:remove:empty', this);
2384
+
2385
+ this._destroyChildren();
2386
+ delete this._showingEmptyView;
2387
+
2388
+ this.triggerMethod('remove:empty', this);
2389
+ }
2390
+ },
2391
+
2392
+
2393
+ // Retrieve the empty view class
2394
+ _getEmptyView: function _getEmptyView() {
2395
+ var emptyView = this.emptyView;
2396
+
2397
+ if (!emptyView) {
2398
+ return;
2399
+ }
2400
+
2401
+ return this._getView(emptyView);
2402
+ },
2403
+
2404
+
2405
+ // Retrieve the `childView` class
2406
+ // The `childView` property can be either a view class or a function that
2407
+ // returns a view class. If it is a function, it will receive the model that
2408
+ // will be passed to the view instance (created from the returned view class)
2409
+ _getChildView: function _getChildView(child) {
2410
+ var childView = this.childView;
2411
+
2412
+ if (!childView) {
2413
+ throw new MarionetteError({
2414
+ name: 'NoChildViewError',
2415
+ message: 'A "childView" must be specified'
2416
+ });
2417
+ }
2418
+
2419
+ childView = this._getView(childView, child);
2420
+
2421
+ if (!childView) {
2422
+ throw new MarionetteError({
2423
+ name: 'InvalidChildViewError',
2424
+ message: '"childView" must be a view class or a function that returns a view class'
2425
+ });
2426
+ }
2427
+
2428
+ return childView;
2429
+ },
2430
+
2431
+
2432
+ // First check if the `view` is a view class (the common case)
2433
+ // Then check if it's a function (which we assume that returns a view class)
2434
+ _getView: function _getView(view, child) {
2435
+ if (view.prototype instanceof Backbone.View || view === Backbone.View) {
2436
+ return view;
2437
+ } else if (_.isFunction(view)) {
2438
+ return view.call(this, child);
2439
+ }
2440
+ },
2441
+
2442
+
2443
+ // Internal method for building and adding a child view
2444
+ _addChild: function _addChild(child, ChildView, index) {
2445
+ var childViewOptions = this._getChildViewOptions(child, index);
2446
+
2447
+ var view = this.buildChildView(child, ChildView, childViewOptions);
2448
+
2449
+ this.addChildView(view, index);
2450
+
2451
+ return view;
2452
+ },
2453
+ _getChildViewOptions: function _getChildViewOptions(child, index) {
2454
+ if (_.isFunction(this.childViewOptions)) {
2455
+ return this.childViewOptions(child, index);
2456
+ }
2457
+
2458
+ return this.childViewOptions;
2459
+ },
2460
+
2461
+
2462
+ // Render the child's view and add it to the HTML for the collection view at a given index.
2463
+ // This will also update the indices of later views in the collection in order to keep the
2464
+ // children in sync with the collection.
2465
+ addChildView: function addChildView(view, index) {
2466
+ this.triggerMethod('before:add:child', this, view);
2467
+
2468
+ // increment indices of views after this one
2469
+ this._updateIndices(view, true, index);
2470
+
2471
+ view._parent = this;
2472
+
2473
+ this._addChildView(view, index);
2474
+
2475
+ this.triggerMethod('add:child', this, view);
2476
+
2477
+ return view;
2478
+ },
2479
+
2480
+
2481
+ // Internal method. This decrements or increments the indices of views after the added/removed
2482
+ // view to keep in sync with the collection.
2483
+ _updateIndices: function _updateIndices(view, increment, index) {
2484
+ if (!this.sort) {
2485
+ return;
2486
+ }
2487
+
2488
+ if (increment) {
2489
+ // assign the index to the view
2490
+ view._index = index;
2491
+ }
2492
+
2493
+ // update the indexes of views after this one
2494
+ this.children.each(function (laterView) {
2495
+ if (laterView._index >= view._index) {
2496
+ laterView._index += increment ? 1 : -1;
2497
+ }
2498
+ });
2499
+ },
2500
+
2501
+
2502
+ // Internal Method. Add the view to children and render it at the given index.
2503
+ _addChildView: function _addChildView(view, index) {
2504
+ // Only trigger attach if already attached and not buffering,
2505
+ // otherwise _endBuffering() or Region#show() handles this.
2506
+ var shouldTriggerAttach = !this._isBuffering && this._isAttached;
2507
+
2508
+ monitorViewEvents(view);
2509
+
2510
+ // set up the child view event forwarding
2511
+ this._proxyChildEvents(view);
2512
+
2513
+ // Store the child view itself so we can properly remove and/or destroy it later
2514
+ this.children.add(view);
2515
+
2516
+ if (!view.supportsRenderLifecycle) {
2517
+ triggerMethodOn(view, 'before:render', view);
2518
+ }
2519
+
2520
+ // Render view
2521
+ view.render();
2522
+
2523
+ if (!view.supportsRenderLifecycle) {
2524
+ view._isRendered = true;
2525
+ triggerMethodOn(view, 'render', view);
2526
+ }
2527
+
2528
+ if (shouldTriggerAttach) {
2529
+ triggerMethodOn(view, 'before:attach', view);
2530
+ }
2531
+
2532
+ // Attach view
2533
+ this.attachHtml(this, view, index);
2534
+
2535
+ if (shouldTriggerAttach) {
2536
+ view._isAttached = true;
2537
+ triggerMethodOn(view, 'attach', view);
2538
+ }
2539
+ },
2540
+
2541
+
2542
+ // Build a `childView` for a model in the collection.
2543
+ buildChildView: function buildChildView(child, ChildViewClass, childViewOptions) {
2544
+ var options = _.extend({ model: child }, childViewOptions);
2545
+ return new ChildViewClass(options);
2546
+ },
2547
+
2548
+
2549
+ // Remove the child view and destroy it. This function also updates the indices of later views
2550
+ // in the collection in order to keep the children in sync with the collection.
2551
+ removeChildView: function removeChildView(view) {
2552
+ if (!view || view._isDestroyed) {
2553
+ return view;
2554
+ }
2555
+
2556
+ this.triggerMethod('before:remove:child', this, view);
2557
+
2558
+ if (view.destroy) {
2559
+ view.destroy();
2560
+ } else {
2561
+ destroyBackboneView(view);
2562
+ }
2563
+
2564
+ delete view._parent;
2565
+ this.stopListening(view);
2566
+ this.children.remove(view);
2567
+ this.triggerMethod('remove:child', this, view);
2568
+
2569
+ // decrement the index of views after this one
2570
+ this._updateIndices(view, false);
2571
+
2572
+ return view;
2573
+ },
2574
+
2575
+
2576
+ // check if the collection is empty or optionally whether an array of pre-processed models is empty
2577
+ isEmpty: function isEmpty(options) {
2578
+ var models = void 0;
2579
+ if (_.result(options, 'processedModels')) {
2580
+ models = options.processedModels;
2581
+ } else {
2582
+ models = this.collection ? this.collection.models : [];
2583
+ models = this._filterModels(models);
2584
+ }
2585
+ return models.length === 0;
2586
+ },
2587
+
2588
+
2589
+ // If empty, show the empty view
2590
+ _checkEmpty: function _checkEmpty() {
2591
+ if (this.isEmpty()) {
2592
+ this._showEmptyView();
2593
+ }
2594
+ },
2595
+
2596
+
2597
+ // You might need to override this if you've overridden attachHtml
2598
+ attachBuffer: function attachBuffer(collectionView, buffer) {
2599
+ collectionView.$el.append(buffer);
2600
+ },
2601
+
2602
+
2603
+ // Create a fragment buffer from the currently buffered children
2604
+ _createBuffer: function _createBuffer() {
2605
+ var elBuffer = document.createDocumentFragment();
2606
+ _.each(this._bufferedChildren, function (b) {
2607
+ elBuffer.appendChild(b.el);
2608
+ });
2609
+ return elBuffer;
2610
+ },
2611
+
2612
+
2613
+ // Append the HTML to the collection's `el`. Override this method to do something other
2614
+ // than `.append`.
2615
+ attachHtml: function attachHtml(collectionView, childView, index) {
2616
+ if (collectionView._isBuffering) {
2617
+ // buffering happens on reset events and initial renders
2618
+ // in order to reduce the number of inserts into the
2619
+ // document, which are expensive.
2620
+ collectionView._bufferedChildren.splice(index, 0, childView);
2621
+ } else {
2622
+ // If we've already rendered the main collection, append
2623
+ // the new child into the correct order if we need to. Otherwise
2624
+ // append to the end.
2625
+ if (!collectionView._insertBefore(childView, index)) {
2626
+ collectionView._insertAfter(childView);
2627
+ }
2628
+ }
2629
+ },
2630
+
2631
+
2632
+ // Internal method. Check whether we need to insert the view into the correct position.
2633
+ _insertBefore: function _insertBefore(childView, index) {
2634
+ var currentView = void 0;
2635
+ var findPosition = this.sort && index < this.children.length - 1;
2636
+ if (findPosition) {
2637
+ // Find the view after this one
2638
+ currentView = this.children.find(function (view) {
2639
+ return view._index === index + 1;
2640
+ });
2641
+ }
2642
+
2643
+ if (currentView) {
2644
+ currentView.$el.before(childView.el);
2645
+ return true;
2646
+ }
2647
+
2648
+ return false;
2649
+ },
2650
+
2651
+
2652
+ // Internal method. Append a view to the end of the $el
2653
+ _insertAfter: function _insertAfter(childView) {
2654
+ this.$el.append(childView.el);
2655
+ },
2656
+
2657
+
2658
+ // Internal method to set up the `children` object for storing all of the child views
2659
+ _initChildViewStorage: function _initChildViewStorage() {
2660
+ this.children = new Container();
2661
+ },
2662
+
2663
+
2664
+ // called by ViewMixin destroy
2665
+ _removeChildren: function _removeChildren() {
2666
+ this._destroyChildren({ checkEmpty: false });
2667
+ },
2668
+
2669
+
2670
+ // Destroy the child views that this collection view is holding on to, if any
2671
+ _destroyChildren: function _destroyChildren() {
2672
+ var _ref2 = arguments.length <= 0 || arguments[0] === undefined ? {} : arguments[0];
2673
+
2674
+ var checkEmpty = _ref2.checkEmpty;
2675
+
2676
+ this.triggerMethod('before:destroy:children', this);
2677
+ var shouldCheckEmpty = checkEmpty !== false;
2678
+ var childViews = this.children.map(_.identity);
2679
+
2680
+ this.children.each(_.bind(this.removeChildView, this));
2681
+
2682
+ if (shouldCheckEmpty) {
2683
+ this._checkEmpty();
2684
+ }
2685
+
2686
+ this.triggerMethod('destroy:children', this);
2687
+ return childViews;
2688
+ },
2689
+
2690
+
2691
+ // Return true if the given child should be shown. Return false otherwise.
2692
+ // The filter will be passed (child, index, collection), where
2693
+ // 'child' is the given model
2694
+ // 'index' is the index of that model in the collection
2695
+ // 'collection' is the collection referenced by this CollectionView
2696
+ _shouldAddChild: function _shouldAddChild(child, index) {
2697
+ var filter = this.filter;
2698
+ return !_.isFunction(filter) || filter.call(this, child, index, this.collection);
2699
+ },
2700
+
2701
+
2702
+ // Set up the child view event forwarding. Uses a "childview:" prefix in front of all forwarded events.
2703
+ _proxyChildEvents: function _proxyChildEvents(view) {
2704
+ var _this6 = this;
2705
+
2706
+ var prefix = _.result(this, 'childViewEventPrefix');
2707
+
2708
+ // Forward all child view events through the parent,
2709
+ // prepending "childview:" to the event name
2710
+ this.listenTo(view, 'all', function (eventName) {
2711
+ for (var _len = arguments.length, args = Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) {
2712
+ args[_key - 1] = arguments[_key];
2713
+ }
2714
+
2715
+ var childEventName = prefix + ':' + eventName;
2716
+
2717
+ var childViewEvents = _this6.normalizeMethods(_this6._childViewEvents);
2718
+
2719
+ // call collectionView childViewEvent if defined
2720
+ if (typeof childViewEvents !== 'undefined' && _.isFunction(childViewEvents[eventName])) {
2721
+ childViewEvents[eventName].apply(_this6, args);
2722
+ }
2723
+
2724
+ // use the parent view's proxyEvent handlers
2725
+ var childViewTriggers = _this6._childViewTriggers;
2726
+
2727
+ // Call the event with the proxy name on the parent layout
2728
+ if (childViewTriggers && _.isString(childViewTriggers[eventName])) {
2729
+ _this6.triggerMethod.apply(_this6, [childViewTriggers[eventName]].concat(args));
2730
+ }
2731
+
2732
+ _this6.triggerMethod.apply(_this6, [childEventName].concat(args));
2733
+ });
2734
+ }
2735
+ });
2736
+
2737
+ _.extend(CollectionView.prototype, ViewMixin);
2738
+
2739
+ var ClassOptions$4 = ['childViewContainer', 'template', 'templateContext'];
2740
+
2741
+ // Used for rendering a branch-leaf, hierarchical structure.
2742
+ // Extends directly from CollectionView
2743
+ // @deprecated
2744
+ var CompositeView = CollectionView.extend({
2745
+
2746
+ // Setting up the inheritance chain which allows changes to
2747
+ // Marionette.CollectionView.prototype.constructor which allows overriding
2748
+ // option to pass '{sort: false}' to prevent the CompositeView from
2749
+ // maintaining the sorted order of the collection.
2750
+ // This will fallback onto appending childView's to the end.
2751
+ constructor: function constructor(options) {
2752
+ deprecate('CompositeView is deprecated. Convert to View at your earliest convenience');
2753
+
2754
+ this.mergeOptions(options, ClassOptions$4);
2755
+
2756
+ CollectionView.prototype.constructor.apply(this, arguments);
2757
+ },
2758
+
2759
+
2760
+ // Configured the initial events that the composite view
2761
+ // binds to. Override this method to prevent the initial
2762
+ // events, or to add your own initial events.
2763
+ _initialEvents: function _initialEvents() {
2764
+
2765
+ // Bind only after composite view is rendered to avoid adding child views
2766
+ // to nonexistent childViewContainer
2767
+
2768
+ if (this.collection) {
2769
+ this.listenTo(this.collection, 'add', this._onCollectionAdd);
2770
+ this.listenTo(this.collection, 'remove', this._onCollectionRemove);
2771
+ this.listenTo(this.collection, 'reset', this.renderChildren);
2772
+
2773
+ if (this.sort) {
2774
+ this.listenTo(this.collection, 'sort', this._sortViews);
2775
+ }
2776
+ }
2777
+ },
2778
+
2779
+
2780
+ // Retrieve the `childView` to be used when rendering each of
2781
+ // the items in the collection. The default is to return
2782
+ // `this.childView` or Marionette.CompositeView if no `childView`
2783
+ // has been defined. As happens in CollectionView, `childView` can
2784
+ // be a function (which should return a view class).
2785
+ _getChildView: function _getChildView(child) {
2786
+ var childView = this.childView;
2787
+
2788
+ // for CompositeView, if `childView` is not specified, we'll get the same
2789
+ // composite view class rendered for each child in the collection
2790
+ // then check if the `childView` is a view class (the common case)
2791
+ // finally check if it's a function (which we assume that returns a view class)
2792
+ if (!childView) {
2793
+ return this.constructor;
2794
+ }
2795
+
2796
+ childView = this._getView(childView, child);
2797
+
2798
+ if (!childView) {
2799
+ throw new MarionetteError({
2800
+ name: 'InvalidChildViewError',
2801
+ message: '"childView" must be a view class or a function that returns a view class'
2802
+ });
2803
+ }
2804
+
2805
+ return childView;
2806
+ },
2807
+
2808
+
2809
+ // Return the serialized model
2810
+ serializeData: function serializeData() {
2811
+ return this.serializeModel();
2812
+ },
2813
+
2814
+
2815
+ // Renders the model and the collection.
2816
+ render: function render() {
2817
+ this._ensureViewIsIntact();
2818
+ this._isRendering = true;
2819
+ this.resetChildViewContainer();
2820
+
2821
+ this.triggerMethod('before:render', this);
2822
+
2823
+ this._renderTemplate();
2824
+ this.bindUIElements();
2825
+ this.renderChildren();
2826
+
2827
+ this._isRendering = false;
2828
+ this._isRendered = true;
2829
+ this.triggerMethod('render', this);
2830
+ return this;
2831
+ },
2832
+ renderChildren: function renderChildren() {
2833
+ if (this._isRendered || this._isRendering) {
2834
+ CollectionView.prototype._renderChildren.call(this);
2835
+ }
2836
+ },
2837
+
2838
+
2839
+ // You might need to override this if you've overridden attachHtml
2840
+ attachBuffer: function attachBuffer(compositeView, buffer) {
2841
+ var $container = this.getChildViewContainer(compositeView);
2842
+ $container.append(buffer);
2843
+ },
2844
+
2845
+
2846
+ // Internal method. Append a view to the end of the $el.
2847
+ // Overidden from CollectionView to ensure view is appended to
2848
+ // childViewContainer
2849
+ _insertAfter: function _insertAfter(childView) {
2850
+ var $container = this.getChildViewContainer(this, childView);
2851
+ $container.append(childView.el);
2852
+ },
2853
+
2854
+
2855
+ // Internal method. Append reordered childView'.
2856
+ // Overidden from CollectionView to ensure reordered views
2857
+ // are appended to childViewContainer
2858
+ _appendReorderedChildren: function _appendReorderedChildren(children) {
2859
+ var $container = this.getChildViewContainer(this);
2860
+ $container.append(children);
2861
+ },
2862
+
2863
+
2864
+ // Internal method to ensure an `$childViewContainer` exists, for the
2865
+ // `attachHtml` method to use.
2866
+ getChildViewContainer: function getChildViewContainer(containerView, childView) {
2867
+ if (!!containerView.$childViewContainer) {
2868
+ return containerView.$childViewContainer;
2869
+ }
2870
+
2871
+ var container = void 0;
2872
+ var childViewContainer = containerView.childViewContainer;
2873
+ if (childViewContainer) {
2874
+
2875
+ var selector = _.result(containerView, 'childViewContainer');
2876
+
2877
+ if (selector.charAt(0) === '@' && containerView.ui) {
2878
+ container = containerView.ui[selector.substr(4)];
2879
+ } else {
2880
+ container = containerView.$(selector);
2881
+ }
2882
+
2883
+ if (container.length <= 0) {
2884
+ throw new MarionetteError({
2885
+ name: 'ChildViewContainerMissingError',
2886
+ message: 'The specified "childViewContainer" was not found: ' + containerView.childViewContainer
2887
+ });
2888
+ }
2889
+ } else {
2890
+ container = containerView.$el;
2891
+ }
2892
+
2893
+ containerView.$childViewContainer = container;
2894
+ return container;
2895
+ },
2896
+
2897
+
2898
+ // Internal method to reset the `$childViewContainer` on render
2899
+ resetChildViewContainer: function resetChildViewContainer() {
2900
+ if (this.$childViewContainer) {
2901
+ this.$childViewContainer = undefined;
2902
+ }
2903
+ }
2904
+ });
2905
+
2906
+ // To prevent duplication but allow the best View organization
2907
+ // Certain View methods are mixed directly into the deprecated CompositeView
2908
+ var MixinFromView = _.pick(View.prototype, 'serializeModel', 'getTemplate', '_renderTemplate', 'mixinTemplateContext', 'attachElContent');
2909
+ _.extend(CompositeView.prototype, MixinFromView);
2910
+
2911
+ var ClassOptions$5 = ['collectionEvents', 'events', 'modelEvents', 'triggers', 'ui'];
2912
+
2913
+ var Behavior = MarionetteObject.extend({
2914
+ cidPrefix: 'mnb',
2915
+
2916
+ constructor: function constructor(options, view) {
2917
+ // Setup reference to the view.
2918
+ // this comes in handle when a behavior
2919
+ // wants to directly talk up the chain
2920
+ // to the view.
2921
+ this.view = view;
2922
+ this.defaults = _.clone(_.result(this, 'defaults', {}));
2923
+ this._setOptions(this.defaults, options);
2924
+ this.mergeOptions(this.options, ClassOptions$5);
2925
+
2926
+ // Construct an internal UI hash using
2927
+ // the behaviors UI hash and then the view UI hash.
2928
+ // This allows the user to use UI hash elements
2929
+ // defined in the parent view as well as those
2930
+ // defined in the given behavior.
2931
+ // This order will help the reuse and share of a behavior
2932
+ // between multiple views, while letting a view override a
2933
+ // selector under an UI key.
2934
+ this.ui = _.extend({}, _.result(this, 'ui'), _.result(view, 'ui'));
2935
+
2936
+ MarionetteObject.apply(this, arguments);
2937
+ },
2938
+
2939
+
2940
+ // proxy behavior $ method to the view
2941
+ // this is useful for doing jquery DOM lookups
2942
+ // scoped to behaviors view.
2943
+ $: function $() {
2944
+ return this.view.$.apply(this.view, arguments);
2945
+ },
2946
+
2947
+
2948
+ // Stops the behavior from listening to events.
2949
+ // Overrides Object#destroy to prevent additional events from being triggered.
2950
+ destroy: function destroy() {
2951
+ this.stopListening();
2952
+
2953
+ return this;
2954
+ },
2955
+ proxyViewProperties: function proxyViewProperties() {
2956
+ this.$el = this.view.$el;
2957
+ this.el = this.view.el;
2958
+
2959
+ return this;
2960
+ },
2961
+ bindUIElements: function bindUIElements() {
2962
+ this._bindUIElements();
2963
+
2964
+ return this;
2965
+ },
2966
+ unbindUIElements: function unbindUIElements() {
2967
+ this._unbindUIElements();
2968
+
2969
+ return this;
2970
+ },
2971
+ getUI: function getUI(name) {
2972
+ this.view._ensureViewIsIntact();
2973
+ return this._getUI(name);
2974
+ },
2975
+
2976
+
2977
+ // Handle `modelEvents`, and `collectionEvents` configuration
2978
+ delegateEntityEvents: function delegateEntityEvents() {
2979
+ this._delegateEntityEvents(this.view.model, this.view.collection);
2980
+
2981
+ return this;
2982
+ },
2983
+ undelegateEntityEvents: function undelegateEntityEvents() {
2984
+ this._undelegateEntityEvents(this.view.model, this.view.collection);
2985
+
2986
+ return this;
2987
+ },
2988
+ getEvents: function getEvents() {
2989
+ // Normalize behavior events hash to allow
2990
+ // a user to use the @ui. syntax.
2991
+ var behaviorEvents = this.normalizeUIKeys(_.result(this, 'events'));
2992
+
2993
+ // binds the handler to the behavior and builds a unique eventName
2994
+ return _.reduce(behaviorEvents, function (events, behaviorHandler, key) {
2995
+ if (!_.isFunction(behaviorHandler)) {
2996
+ behaviorHandler = this[behaviorHandler];
2997
+ }
2998
+ if (!behaviorHandler) {
2999
+ return;
3000
+ }
3001
+ key = getUniqueEventName(key);
3002
+ events[key] = _.bind(behaviorHandler, this);
3003
+ return events;
3004
+ }, {}, this);
3005
+ },
3006
+
3007
+
3008
+ // Internal method to build all trigger handlers for a given behavior
3009
+ getTriggers: function getTriggers() {
3010
+ if (!this.triggers) {
3011
+ return;
3012
+ }
3013
+
3014
+ // Normalize behavior triggers hash to allow
3015
+ // a user to use the @ui. syntax.
3016
+ var behaviorTriggers = this.normalizeUIKeys(_.result(this, 'triggers'));
3017
+
3018
+ return this._getViewTriggers(this.view, behaviorTriggers);
3019
+ }
3020
+ });
3021
+
3022
+ _.extend(Behavior.prototype, DelegateEntityEventsMixin, TriggersMixin, UIMixin);
3023
+
3024
+ var ClassOptions$6 = ['region', 'regionClass'];
3025
+
3026
+ // A container for a Marionette application.
3027
+ var Application = MarionetteObject.extend({
3028
+ cidPrefix: 'mna',
3029
+
3030
+ constructor: function constructor(options) {
3031
+ this._setOptions(options);
3032
+
3033
+ this.mergeOptions(options, ClassOptions$6);
3034
+
3035
+ this._initRegion();
3036
+
3037
+ MarionetteObject.prototype.constructor.apply(this, arguments);
3038
+ },
3039
+
3040
+
3041
+ regionClass: Region,
3042
+
3043
+ _initRegion: function _initRegion(options) {
3044
+ var region = this.region;
3045
+ var RegionClass = this.regionClass;
3046
+
3047
+ // if the region is a string expect an el or selector
3048
+ // and instantiate a region
3049
+ if (_.isString(region)) {
3050
+ this._region = new RegionClass({
3051
+ el: region
3052
+ });
3053
+ return;
3054
+ }
3055
+
3056
+ this._region = region;
3057
+ },
3058
+ getRegion: function getRegion() {
3059
+ return this._region;
3060
+ },
3061
+ showView: function showView(view) {
3062
+ var region = this.getRegion();
3063
+
3064
+ for (var _len = arguments.length, args = Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) {
3065
+ args[_key - 1] = arguments[_key];
3066
+ }
3067
+
3068
+ return region.show.apply(region, [view].concat(args));
3069
+ },
3070
+ getView: function getView() {
3071
+ return this.getRegion().currentView;
3072
+ },
3073
+
3074
+
3075
+ // kick off all of the application's processes.
3076
+ start: function start(options) {
3077
+ this.triggerMethod('before:start', this, options);
3078
+ this.triggerMethod('start', this, options);
3079
+ return this;
3080
+ }
3081
+ });
3082
+
3083
+ var ClassOptions$7 = ['appRoutes', 'controller'];
3084
+
3085
+ var AppRouter = Backbone.Router.extend({
3086
+ constructor: function constructor(options) {
3087
+ this._setOptions(options);
3088
+
3089
+ this.mergeOptions(options, ClassOptions$7);
3090
+
3091
+ Backbone.Router.apply(this, arguments);
3092
+
3093
+ var appRoutes = this.appRoutes;
3094
+ var controller = this._getController();
3095
+ this.processAppRoutes(controller, appRoutes);
3096
+ this.on('route', this._processOnRoute, this);
3097
+ },
3098
+
3099
+
3100
+ // Similar to route method on a Backbone Router but
3101
+ // method is called on the controller
3102
+ appRoute: function appRoute(route, methodName) {
3103
+ var controller = this._getController();
3104
+ this._addAppRoute(controller, route, methodName);
3105
+ return this;
3106
+ },
3107
+
3108
+
3109
+ // process the route event and trigger the onRoute
3110
+ // method call, if it exists
3111
+ _processOnRoute: function _processOnRoute(routeName, routeArgs) {
3112
+ // make sure an onRoute before trying to call it
3113
+ if (_.isFunction(this.onRoute)) {
3114
+ // find the path that matches the current route
3115
+ var routePath = _.invert(this.appRoutes)[routeName];
3116
+ this.onRoute(routeName, routePath, routeArgs);
3117
+ }
3118
+ },
3119
+
3120
+
3121
+ // Internal method to process the `appRoutes` for the
3122
+ // router, and turn them in to routes that trigger the
3123
+ // specified method on the specified `controller`.
3124
+ processAppRoutes: function processAppRoutes(controller, appRoutes) {
3125
+ var _this = this;
3126
+
3127
+ if (!appRoutes) {
3128
+ return this;
3129
+ }
3130
+
3131
+ var routeNames = _.keys(appRoutes).reverse(); // Backbone requires reverted order of routes
3132
+
3133
+ _.each(routeNames, function (route) {
3134
+ _this._addAppRoute(controller, route, appRoutes[route]);
3135
+ });
3136
+
3137
+ return this;
3138
+ },
3139
+ _getController: function _getController() {
3140
+ return this.controller;
3141
+ },
3142
+ _addAppRoute: function _addAppRoute(controller, route, methodName) {
3143
+ var method = controller[methodName];
3144
+
3145
+ if (!method) {
3146
+ throw new MarionetteError('Method "' + methodName + '" was not found on the controller');
3147
+ }
3148
+
3149
+ this.route(route, methodName, _.bind(method, controller));
3150
+ },
3151
+
3152
+
3153
+ triggerMethod: triggerMethod
3154
+ });
3155
+
3156
+ _.extend(AppRouter.prototype, CommonMixin);
3157
+
3158
+ // Placeholder method to be extended by the user.
3159
+ // The method should define the object that stores the behaviors.
3160
+ // i.e.
3161
+ //
3162
+ // ```js
3163
+ // Marionette.Behaviors.behaviorsLookup: function() {
3164
+ // return App.Behaviors
3165
+ // }
3166
+ // ```
3167
+ function behaviorsLookup() {
3168
+ throw new MarionetteError({
3169
+ message: 'You must define where your behaviors are stored.',
3170
+ url: 'marionette.behaviors.md#behaviorslookup'
3171
+ });
3172
+ }
3173
+
3174
+ // Add Feature flags here
3175
+ // e.g. 'class' => false
3176
+ var FEATURES = {};
3177
+
3178
+ function isEnabled(name) {
3179
+ return !!FEATURES[name];
3180
+ }
3181
+
3182
+ function setEnabled(name, state) {
3183
+ return FEATURES[name] = state;
3184
+ }
3185
+
3186
+ var previousMarionette = Backbone.Marionette;
3187
+ var Marionette = Backbone.Marionette = {};
3188
+
3189
+ // This allows you to run multiple instances of Marionette on the same
3190
+ // webapp. After loading the new version, call `noConflict()` to
3191
+ // get a reference to it. At the same time the old version will be
3192
+ // returned to Backbone.Marionette.
3193
+ Marionette.noConflict = function () {
3194
+ Backbone.Marionette = previousMarionette;
3195
+ return this;
3196
+ };
3197
+
3198
+ // Utilities
3199
+ Marionette.bindEvents = proxy(bindEvents);
3200
+ Marionette.unbindEvents = proxy(unbindEvents);
3201
+ Marionette.bindRequests = proxy(bindRequests);
3202
+ Marionette.unbindRequests = proxy(unbindRequests);
3203
+ Marionette.mergeOptions = proxy(mergeOptions);
3204
+ Marionette.getOption = proxy(getOption);
3205
+ Marionette.normalizeMethods = proxy(normalizeMethods);
3206
+ Marionette.extend = extend;
3207
+ Marionette.isNodeAttached = isNodeAttached;
3208
+ Marionette.deprecate = deprecate;
3209
+ Marionette.triggerMethod = proxy(triggerMethod);
3210
+ Marionette.triggerMethodOn = triggerMethodOn;
3211
+ Marionette.isEnabled = isEnabled;
3212
+ Marionette.setEnabled = setEnabled;
3213
+ Marionette.monitorViewEvents = monitorViewEvents;
3214
+
3215
+ Marionette.Behaviors = {};
3216
+ Marionette.Behaviors.behaviorsLookup = behaviorsLookup;
3217
+
3218
+ // Classes
3219
+ Marionette.Application = Application;
3220
+ Marionette.AppRouter = AppRouter;
3221
+ Marionette.Renderer = Renderer;
3222
+ Marionette.TemplateCache = TemplateCache;
3223
+ Marionette.View = View;
3224
+ Marionette.CollectionView = CollectionView;
3225
+ Marionette.CompositeView = CompositeView;
3226
+ Marionette.Behavior = Behavior;
3227
+ Marionette.Region = Region;
3228
+ Marionette.Error = MarionetteError;
3229
+ Marionette.Object = MarionetteObject;
3230
+
3231
+ // Configuration
3232
+ Marionette.DEV_MODE = false;
3233
+ Marionette.FEATURES = FEATURES;
3234
+ Marionette.VERSION = version;
3235
+
3236
+ return Marionette;
3237
+
3238
+ }));
3239
+
3240
+ //# sourceMappingURL=backbone.marionette.js.map