PageRenderTime 50ms CodeModel.GetById 19ms RepoModel.GetById 0ms app.codeStats 0ms

/ajax/libs/backbone.marionette/0.7.2/backbone.marionette.js

https://gitlab.com/Blueprint-Marketing/cdnjs
JavaScript | 855 lines | 491 code | 152 blank | 212 comment | 58 complexity | bb718bffac3dfec4306e1ae63367fe60 MD5 | raw file
  1. // Backbone.Marionette v0.7.2
  2. //
  3. // Copyright (C)2011 Derick Bailey, Muted Solutions, LLC
  4. // Distributed Under MIT License
  5. //
  6. // Documentation and Full License Available at:
  7. // http://github.com/derickbailey/backbone.marionette
  8. Backbone.Marionette = (function(Backbone, _, $){
  9. var Marionette = {};
  10. Marionette.version = "0.7.2";
  11. // Marionette.View
  12. // ---------------
  13. // The core view type that other Marionette views extend from.
  14. Marionette.View = Backbone.View.extend({
  15. // Get the template or template id/selector for this view
  16. // instance. You can set a `template` attribute in the view
  17. // definition or pass a `template: "whatever"` parameter in
  18. // to the constructor options. The `template` can also be
  19. // a function that returns a selector string.
  20. getTemplateSelector: function(){
  21. var template;
  22. // Get the template from `this.options.template` or
  23. // `this.template`. The `options` takes precedence.
  24. if (this.options && this.options.template){
  25. template = this.options.template;
  26. } else {
  27. template = this.template;
  28. }
  29. // check if it's a function and execute it, if it is
  30. if (_.isFunction(template)){
  31. template = template.call(this);
  32. }
  33. return template;
  34. },
  35. // Serialize the model or collection for the view. If a model is
  36. // found, `.toJSON()` is called. If a collection is found, `.toJSON()`
  37. // is also called, but is used to populate an `items` array in the
  38. // resulting data. If both are found, defaults to the model.
  39. // You can override the `serializeData` method in your own view
  40. // definition, to provide custom serialization for your view's data.
  41. serializeData: function(){
  42. var data;
  43. if (this.model) { data = this.model.toJSON(); }
  44. else if (this.collection) {
  45. data = { items: this.collection.toJSON() };
  46. }
  47. return data;
  48. },
  49. // Default `close` implementation, for removing a view from the
  50. // DOM and unbinding it. Regions will call this method
  51. // for you. You can specify an `onClose` method in your view to
  52. // add custom code that is called after the view is closed.
  53. close: function(){
  54. if (this.beforeClose) { this.beforeClose(); }
  55. this.unbindAll();
  56. this.remove();
  57. if (this.onClose) { this.onClose(); }
  58. this.trigger('close');
  59. this.unbind();
  60. }
  61. });
  62. // Item View
  63. // ---------
  64. // A single item view implementation that contains code for rendering
  65. // with underscore.js templates, serializing the view's model or collection,
  66. // and calling several methods on extended views, such as `onRender`.
  67. Marionette.ItemView = Marionette.View.extend({
  68. constructor: function(){
  69. var args = slice.call(arguments);
  70. Marionette.View.prototype.constructor.apply(this, args);
  71. _.bindAll(this, "render");
  72. this.initialEvents();
  73. },
  74. // Configured the initial events that the item view
  75. // binds to. Override this method to prevent the initial
  76. // events, or to add your own initial events.
  77. initialEvents: function(){
  78. if (this.collection){
  79. this.bindTo(this.collection, "reset", this.render, this);
  80. }
  81. },
  82. // Render the view, defaulting to underscore.js templates.
  83. // You can override this in your view definition.
  84. render: function(){
  85. var that = this;
  86. var deferredRender = $.Deferred();
  87. var deferredData = this.serializeData();
  88. if (this.beforeRender) { this.beforeRender(); }
  89. this.trigger("item:before:render", that);
  90. $.when(deferredData).then(function(data) {
  91. var asyncRender = that.renderHtml(data);
  92. $.when(asyncRender).then(function(html){
  93. that.$el.html(html);
  94. var onRenderPromise = {};
  95. if (that.onRender) { onRenderPromise = that.onRender(); }
  96. $.when(onRenderPromise).then(function() {
  97. that.trigger("item:rendered", that);
  98. that.trigger("render", that);
  99. deferredRender.resolve();
  100. });
  101. });
  102. });
  103. return deferredRender.promise();
  104. },
  105. // Render the data for this item view in to some HTML.
  106. // Override this method to replace the specific way in
  107. // which an item view has it's data rendered in to html.
  108. renderHtml: function(data) {
  109. var template = this.getTemplateSelector();
  110. return Marionette.Renderer.render(template, data);
  111. },
  112. // Override the default close event to add a few
  113. // more events that are triggered.
  114. close: function(){
  115. this.trigger('item:before:close');
  116. Marionette.View.prototype.close.apply(this, arguments);
  117. this.trigger('item:closed');
  118. }
  119. });
  120. // Collection View
  121. // ---------------
  122. // A view that iterates over a Backbone.Collection
  123. // and renders an individual ItemView for each model.
  124. Marionette.CollectionView = Marionette.View.extend({
  125. constructor: function(){
  126. Marionette.View.prototype.constructor.apply(this, arguments);
  127. _.bindAll(this, "addItemView", "render");
  128. this.initialEvents();
  129. },
  130. // Configured the initial events that the collection view
  131. // binds to. Override this method to prevent the initial
  132. // events, or to add your own initial events.
  133. initialEvents: function(){
  134. if (this.collection){
  135. this.bindTo(this.collection, "add", this.addChildView, this);
  136. this.bindTo(this.collection, "remove", this.removeItemView, this);
  137. this.bindTo(this.collection, "reset", this.render, this);
  138. }
  139. },
  140. // Handle a child item added to the collection
  141. addChildView: function(item){
  142. var ItemView = this.getItemView();
  143. return this.addItemView(item, ItemView);
  144. },
  145. // Loop through all of the items and render
  146. // each of them with the specified `itemView`.
  147. render: function(){
  148. var that = this;
  149. var deferredRender = $.Deferred();
  150. var promises = [];
  151. var ItemView = this.getItemView();
  152. if (this.beforeRender) { this.beforeRender(); }
  153. this.trigger("collection:before:render", this);
  154. this.closeChildren();
  155. if (this.collection) {
  156. this.collection.each(function(item){
  157. var promise = that.addItemView(item, ItemView);
  158. promises.push(promise);
  159. });
  160. }
  161. deferredRender.done(function(){
  162. if (this.onRender) { this.onRender(); }
  163. this.trigger("collection:rendered", this);
  164. });
  165. $.when.apply(this, promises).then(function(){
  166. deferredRender.resolveWith(that);
  167. });
  168. return deferredRender.promise();
  169. },
  170. // Retrieve the itemView type, either from `this.options.itemView`
  171. // or from the `itemView` in the object definition. The "options"
  172. // takes precedence.
  173. getItemView: function(){
  174. var itemView = this.options.itemView || this.itemView;
  175. if (!itemView){
  176. var err = new Error("An `itemView` must be specified");
  177. err.name = "NoItemViewError";
  178. throw err;
  179. }
  180. return itemView;
  181. },
  182. // Render the child item's view and add it to the
  183. // HTML for the collection view.
  184. addItemView: function(item, ItemView){
  185. var that = this;
  186. var view = this.buildItemView(item, ItemView);
  187. this.storeChild(view);
  188. this.trigger("item:added", view);
  189. var viewRendered = view.render();
  190. $.when(viewRendered).then(function(){
  191. that.appendHtml(that, view);
  192. });
  193. return viewRendered;
  194. },
  195. // Build an `itemView` for every model in the collection.
  196. buildItemView: function(item, ItemView){
  197. var view = new ItemView({
  198. model: item
  199. });
  200. return view;
  201. },
  202. // Remove the child view and close it
  203. removeItemView: function(item){
  204. var view = this.children[item.cid];
  205. if (view){
  206. view.close();
  207. delete this.children[item.cid];
  208. }
  209. this.trigger("item:removed", view);
  210. },
  211. // Append the HTML to the collection's `el`.
  212. // Override this method to do something other
  213. // then `.append`.
  214. appendHtml: function(collectionView, itemView){
  215. collectionView.$el.append(itemView.el);
  216. },
  217. // Store references to all of the child `itemView`
  218. // instances so they can be managed and cleaned up, later.
  219. storeChild: function(view){
  220. if (!this.children){
  221. this.children = {};
  222. }
  223. this.children[view.model.cid] = view;
  224. },
  225. // Handle cleanup and other closing needs for
  226. // the collection of views.
  227. close: function(){
  228. this.trigger("collection:before:close");
  229. this.closeChildren();
  230. Marionette.View.prototype.close.apply(this, arguments);
  231. this.trigger("collection:closed");
  232. },
  233. closeChildren: function(){
  234. if (this.children){
  235. _.each(this.children, function(childView){
  236. childView.close();
  237. });
  238. }
  239. }
  240. });
  241. // Composite View
  242. // --------------
  243. // Used for rendering a branch-leaf, hierarchical structure.
  244. // Extends directly from CollectionView and also renders an
  245. // an item view as `modelView`, for the top leaf
  246. Marionette.CompositeView = Marionette.CollectionView.extend({
  247. constructor: function(options){
  248. Marionette.CollectionView.apply(this, arguments);
  249. this.itemView = this.getItemView();
  250. },
  251. // Retrieve the `itemView` to be used when rendering each of
  252. // the items in the collection. The default is to return
  253. // `this.itemView` or Marionette.CompositeView if no `itemView`
  254. // has been defined
  255. getItemView: function(){
  256. return this.itemView || this.constructor;
  257. },
  258. // Renders the model once, and the collection once. Calling
  259. // this again will tell the model's view to re-render itself
  260. // but the collection will not re-render.
  261. render: function(){
  262. var that = this;
  263. var compositeRendered = $.Deferred();
  264. var modelIsRendered = this.renderModel();
  265. $.when(modelIsRendered).then(function(html){
  266. that.$el.html(html);
  267. that.trigger("composite:model:rendered");
  268. that.trigger("render");
  269. var collectionIsRendered = that.renderCollection();
  270. $.when(collectionIsRendered).then(function(){
  271. compositeRendered.resolve();
  272. });
  273. });
  274. compositeRendered.done(function(){
  275. that.trigger("composite:rendered");
  276. });
  277. return compositeRendered.promise();
  278. },
  279. // Render the collection for the composite view
  280. renderCollection: function(){
  281. var collectionDeferred = Marionette.CollectionView.prototype.render.apply(this, arguments);
  282. collectionDeferred.done(function(){
  283. this.trigger("composite:collection:rendered");
  284. });
  285. return collectionDeferred.promise();
  286. },
  287. // Render an individual model, if we have one, as
  288. // part of a composite view (branch / leaf). For example:
  289. // a treeview.
  290. renderModel: function(){
  291. var data = {};
  292. data = this.serializeData();
  293. var template = this.getTemplateSelector();
  294. return Marionette.Renderer.render(template, data);
  295. }
  296. });
  297. // Region
  298. // ------
  299. // Manage the visual regions of your composite application. See
  300. // http://lostechies.com/derickbailey/2011/12/12/composite-js-apps-regions-and-region-managers/
  301. Marionette.Region = function(options){
  302. this.options = options || {};
  303. _.extend(this, options);
  304. if (!this.el){
  305. var err = new Error("An 'el' must be specified");
  306. err.name = "NoElError";
  307. throw err;
  308. }
  309. if (this.initialize){
  310. this.initialize.apply(this, arguments);
  311. }
  312. };
  313. _.extend(Marionette.Region.prototype, Backbone.Events, {
  314. // Displays a backbone view instance inside of the region.
  315. // Handles calling the `render` method for you. Reads content
  316. // directly from the `el` attribute. Also calls an optional
  317. // `onShow` and `close` method on your view, just after showing
  318. // or just before closing the view, respectively.
  319. show: function(view, appendMethod){
  320. this.ensureEl();
  321. this.close();
  322. this.open(view, appendMethod);
  323. this.currentView = view;
  324. },
  325. ensureEl: function(){
  326. if (!this.$el || this.$el.length === 0){
  327. this.$el = this.getEl(this.el);
  328. }
  329. },
  330. // Override this method to change how the region finds the
  331. // DOM element that it manages. Return a jQuery selector object.
  332. getEl: function(selector){
  333. return $(selector);
  334. },
  335. // Internal method to render and display a view. Not meant
  336. // to be called from any external code.
  337. open: function(view, appendMethod){
  338. var that = this;
  339. appendMethod = appendMethod || "html";
  340. $.when(view.render()).then(function () {
  341. that.$el[appendMethod](view.el);
  342. if (view.onShow) { view.onShow(); }
  343. if (that.onShow) { that.onShow(view); }
  344. view.trigger("show");
  345. that.trigger("view:show", view);
  346. });
  347. },
  348. // Close the current view, if there is one. If there is no
  349. // current view, it does nothing and returns immediately.
  350. close: function(){
  351. var view = this.currentView;
  352. if (!view){ return; }
  353. if (view.close) { view.close(); }
  354. this.trigger("view:closed", view);
  355. delete this.currentView;
  356. },
  357. // Attach an existing view to the region. This
  358. // will not call `render` or `onShow` for the new view,
  359. // and will not replace the current HTML for the `el`
  360. // of the region.
  361. attachView: function(view){
  362. this.currentView = view;
  363. }
  364. });
  365. // Layout
  366. // ------
  367. // Formerly known as Composite Region.
  368. //
  369. // Used for managing application layouts, nested layouts and
  370. // multiple regions within an application or sub-application.
  371. //
  372. // A specialized view type that renders an area of HTML and then
  373. // attaches `Region` instances to the specified `regions`.
  374. // Used for composite view management and sub-application areas.
  375. Marionette.Layout = Marionette.ItemView.extend({
  376. constructor: function () {
  377. this.vent = new Backbone.Marionette.EventAggregator();
  378. Backbone.Marionette.ItemView.apply(this, arguments);
  379. this.regionManagers = {};
  380. },
  381. render: function () {
  382. this.initializeRegions();
  383. return Backbone.Marionette.ItemView.prototype.render.call(this, arguments);
  384. },
  385. close: function () {
  386. this.closeRegions();
  387. Backbone.Marionette.ItemView.prototype.close.call(this, arguments);
  388. },
  389. initializeRegions: function () {
  390. var that = this;
  391. _.each(this.regions, function (selector, name) {
  392. var regionManager = new Backbone.Marionette.Region({
  393. el: selector,
  394. getEl: function(selector){
  395. return that.$(selector);
  396. }
  397. });
  398. that.regionManagers[name] = regionManager;
  399. that[name] = regionManager;
  400. });
  401. },
  402. closeRegions: function () {
  403. var that = this;
  404. _.each(this.regionManagers, function (manager, name) {
  405. manager.close();
  406. delete that[name];
  407. });
  408. this.regionManagers = {};
  409. }
  410. });
  411. // AppRouter
  412. // ---------
  413. // Reduce the boilerplate code of handling route events
  414. // and then calling a single method on another object.
  415. // Have your routers configured to call the method on
  416. // your object, directly.
  417. //
  418. // Configure an AppRouter with `appRoutes`.
  419. //
  420. // App routers can only take one `controller` object.
  421. // It is reocmmended that you divide your controller
  422. // objects in to smaller peices of related functionality
  423. // and have multiple routers / controllers, instead of
  424. // just one giant router and controller.
  425. //
  426. // You can also add standard routes to an AppRouter.
  427. Marionette.AppRouter = Backbone.Router.extend({
  428. constructor: function(options){
  429. Backbone.Router.prototype.constructor.call(this, options);
  430. if (this.appRoutes){
  431. var controller = this.controller;
  432. if (options && options.controller) {
  433. controller = options.controller;
  434. }
  435. this.processAppRoutes(controller, this.appRoutes);
  436. }
  437. },
  438. processAppRoutes: function(controller, appRoutes){
  439. var method, methodName;
  440. var route, routesLength, i;
  441. var routes = [];
  442. var router = this;
  443. for(route in appRoutes){
  444. if (appRoutes.hasOwnProperty(route)){
  445. routes.unshift([route, appRoutes[route]]);
  446. }
  447. }
  448. routesLength = routes.length;
  449. for (i = 0; i < routesLength; i++){
  450. route = routes[i][0];
  451. methodName = routes[i][1];
  452. method = _.bind(controller[methodName], controller);
  453. router.route(route, methodName, method);
  454. }
  455. }
  456. });
  457. // Composite Application
  458. // ---------------------
  459. // Contain and manage the composite application as a whole.
  460. // Stores and starts up `Region` objects, includes an
  461. // event aggregator as `app.vent`
  462. Marionette.Application = function(options){
  463. this.initCallbacks = new Marionette.Callbacks();
  464. this.vent = new Marionette.EventAggregator();
  465. _.extend(this, options);
  466. };
  467. _.extend(Marionette.Application.prototype, Backbone.Events, {
  468. // Add an initializer that is either run at when the `start`
  469. // method is called, or run immediately if added after `start`
  470. // has already been called.
  471. addInitializer: function(initializer){
  472. this.initCallbacks.add(initializer);
  473. },
  474. // kick off all of the application's processes.
  475. // initializes all of the regions that have been added
  476. // to the app, and runs all of the initializer functions
  477. start: function(options){
  478. this.trigger("initialize:before", options);
  479. this.initCallbacks.run(this, options);
  480. this.trigger("initialize:after", options);
  481. this.trigger("start", options);
  482. },
  483. // Add regions to your app.
  484. // Accepts a hash of named strings or Region objects
  485. // addRegions({something: "#someRegion"})
  486. // addRegions{{something: Region.extend({el: "#someRegion"}) });
  487. addRegions: function(regions){
  488. var regionValue, regionObj, region;
  489. for(region in regions){
  490. if (regions.hasOwnProperty(region)){
  491. regionValue = regions[region];
  492. if (typeof regionValue === "string"){
  493. regionObj = new Marionette.Region({
  494. el: regionValue
  495. });
  496. } else {
  497. regionObj = new regionValue();
  498. }
  499. this[region] = regionObj;
  500. }
  501. }
  502. }
  503. });
  504. // BindTo: Event Binding
  505. // ---------------------
  506. // BindTo facilitates the binding and unbinding of events
  507. // from objects that extend `Backbone.Events`. It makes
  508. // unbinding events, even with anonymous callback functions,
  509. // easy.
  510. //
  511. // Thanks to Johnny Oshika for this code.
  512. // http://stackoverflow.com/questions/7567404/backbone-js-repopulate-or-recreate-the-view/7607853#7607853
  513. Marionette.BindTo = {
  514. // Store the event binding in array so it can be unbound
  515. // easily, at a later point in time.
  516. bindTo: function (obj, eventName, callback, context) {
  517. context = context || this;
  518. obj.on(eventName, callback, context);
  519. if (!this.bindings) { this.bindings = []; }
  520. var binding = {
  521. obj: obj,
  522. eventName: eventName,
  523. callback: callback,
  524. context: context
  525. }
  526. this.bindings.push(binding);
  527. return binding;
  528. },
  529. // Unbind from a single binding object. Binding objects are
  530. // returned from the `bindTo` method call.
  531. unbindFrom: function(binding){
  532. binding.obj.off(binding.eventName, binding.callback);
  533. var index = _.indexOf(this.bindings, binding);
  534. Array.remove(this.bindings, index);
  535. },
  536. // Unbind all of the events that we have stored.
  537. unbindAll: function () {
  538. var that = this;
  539. _.each(this.bindings, function (binding, index) {
  540. that.unbindFrom(binding);
  541. });
  542. this.bindings = [];
  543. }
  544. };
  545. // Callbacks
  546. // ---------
  547. // A simple way of managing a collection of callbacks
  548. // and executing them at a later point in time, using jQuery's
  549. // `Deferred` object.
  550. Marionette.Callbacks = function(){
  551. this.deferred = $.Deferred();
  552. this.promise = this.deferred.promise();
  553. };
  554. _.extend(Marionette.Callbacks.prototype, {
  555. // Add a callback to be executed. Callbacks added here are
  556. // guaranteed to execute, even if they are added after the
  557. // `run` method is called.
  558. add: function(callback){
  559. this.promise.done(function(context, options){
  560. callback.call(context, options);
  561. });
  562. },
  563. // Run all registered callbacks with the context specified.
  564. // Additional callbacks can be added after this has been run
  565. // and they will still be executed.
  566. run: function(context, options){
  567. this.deferred.resolve(context, options);
  568. }
  569. });
  570. // Event Aggregator
  571. // ----------------
  572. // A pub-sub object that can be used to decouple various parts
  573. // of an application through event-driven architecture.
  574. Marionette.EventAggregator = function(options){
  575. _.extend(this, options);
  576. };
  577. _.extend(Marionette.EventAggregator.prototype, Backbone.Events, Marionette.BindTo, {
  578. // Assumes the event aggregator itself is the
  579. // object being bound to.
  580. bindTo: function(eventName, callback, context){
  581. Marionette.BindTo.bindTo.call(this, this, eventName, callback, context);
  582. }
  583. });
  584. // Template Cache
  585. // --------------
  586. // Manage templates stored in `<script>` blocks,
  587. // caching them for faster access.
  588. Marionette.TemplateCache = {
  589. templates: {},
  590. loaders: {},
  591. // Get the specified template by id. Either
  592. // retrieves the cached version, or loads it
  593. // from the DOM.
  594. get: function(templateId){
  595. var that = this;
  596. var templateRetrieval = $.Deferred();
  597. var cachedTemplate = this.templates[templateId];
  598. if (cachedTemplate){
  599. templateRetrieval.resolve(cachedTemplate);
  600. } else {
  601. var loader = this.loaders[templateId];
  602. if(loader) {
  603. templateRetrieval = loader;
  604. } else {
  605. this.loaders[templateId] = templateRetrieval;
  606. this.loadTemplate(templateId, function(template){
  607. delete that.loaders[templateId];
  608. that.templates[templateId] = template;
  609. templateRetrieval.resolve(template);
  610. });
  611. }
  612. }
  613. return templateRetrieval.promise();
  614. },
  615. // Load a template from the DOM, by default. Override
  616. // this method to provide your own template retrieval,
  617. // such as asynchronous loading from a server.
  618. loadTemplate: function(templateId, callback){
  619. var template = $(templateId).html();
  620. // Make sure we have a template before trying to compile it
  621. if (!template || template.length === 0){
  622. var msg = "Could not find template: '" + templateId + "'";
  623. var err = new Error(msg);
  624. err.name = "NoTemplateError";
  625. throw err;
  626. }
  627. template = this.compileTemplate(template);
  628. callback.call(this, template);
  629. },
  630. // Pre-compile the template before caching it. Override
  631. // this method if you do not need to pre-compile a template
  632. // (JST / RequireJS for example) or if you want to change
  633. // the template engine used (Handebars, etc).
  634. compileTemplate: function(rawTemplate){
  635. return _.template(rawTemplate);
  636. },
  637. // Clear templates from the cache. If no arguments
  638. // are specified, clears all templates:
  639. // `clear()`
  640. //
  641. // If arguments are specified, clears each of the
  642. // specified templates from the cache:
  643. // `clear("#t1", "#t2", "...")`
  644. clear: function(){
  645. var i;
  646. var length = arguments.length;
  647. if (length > 0){
  648. for(i=0; i<length; i++){
  649. delete this.templates[arguments[i]];
  650. }
  651. } else {
  652. this.templates = {};
  653. }
  654. }
  655. };
  656. // Renderer
  657. // --------
  658. // Render a template with data by passing in the template
  659. // selector and the data to render.
  660. Marionette.Renderer = {
  661. // Render a template with data. The `template` parameter is
  662. // passed to the `TemplateCache` object to retrieve the
  663. // actual template. Override this method to provide your own
  664. // custom rendering and template handling for all of Marionette.
  665. render: function(template, data){
  666. var that = this;
  667. var asyncRender = $.Deferred();
  668. var templateRetrieval = Marionette.TemplateCache.get(template);
  669. $.when(templateRetrieval).then(function(template){
  670. var html = that.renderTemplate(template, data);
  671. asyncRender.resolve(html);
  672. });
  673. return asyncRender.promise();
  674. },
  675. // Default implementation uses underscore.js templates. Override
  676. // this method to use your own templating engine.
  677. renderTemplate: function(template, data){
  678. var html = template(data);
  679. return html;
  680. }
  681. };
  682. // Helpers
  683. // -------
  684. // For slicing `arguments` in functions
  685. var slice = Array.prototype.slice;
  686. // Copy the `extend` function used by Backbone's classes
  687. var extend = Marionette.View.extend;
  688. Marionette.Region.extend = extend;
  689. Marionette.Application.extend = extend;
  690. // Copy the features of `BindTo` on to these objects
  691. _.extend(Marionette.View.prototype, Marionette.BindTo);
  692. _.extend(Marionette.Application.prototype, Marionette.BindTo);
  693. _.extend(Marionette.Region.prototype, Marionette.BindTo);
  694. // Array Remove - By John Resig (MIT Licensed)
  695. if (!Array.remove){
  696. Array.remove = function(array, from, to) {
  697. var rest = array.slice((to || from) + 1 || array.length);
  698. array.length = from < 0 ? array.length + from : from;
  699. return array.push.apply(array, rest);
  700. };
  701. }
  702. return Marionette;
  703. })(Backbone, _, window.jQuery || window.Zepto || window.ender);