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

/ajax/libs/backbone.layoutmanager/0.8.1/backbone.layoutmanager.js

https://bitbucket.org/kolbyjAFK/cdnjs
JavaScript | 853 lines | 427 code | 154 blank | 272 comment | 90 complexity | a253da13b25d8679fd6cb8754e731b1a MD5 | raw file
Possible License(s): MPL-2.0-no-copyleft-exception
  1. /*!
  2. * backbone.layoutmanager.js v0.8.1
  3. * Copyright 2012, Tim Branyen (@tbranyen)
  4. * backbone.layoutmanager.js may be freely distributed under the MIT license.
  5. */
  6. (function(window) {
  7. "use strict";
  8. // Hoisted, referenced at the bottom of the source. This caches a list of all
  9. // LayoutManager options at definition time.
  10. var keys;
  11. // Localize global dependency references.
  12. var Backbone = window.Backbone;
  13. var _ = window._;
  14. var $ = window.$;
  15. // Maintain references to the two `Backbone.View` functions that are
  16. // overwritten so that they can be proxied.
  17. var _configure = Backbone.View.prototype._configure;
  18. var render = Backbone.View.prototype.render;
  19. // Cache these methods for performance.
  20. var aPush = Array.prototype.push;
  21. var aConcat = Array.prototype.concat;
  22. var aSplice = Array.prototype.splice;
  23. // LayoutManager is a wrapper around a `Backbone.View`.
  24. var LayoutManager = Backbone.View.extend({
  25. // This named function allows for significantly easier debugging.
  26. constructor: function Layout(options) {
  27. // Options may not always be passed to the constructor, this ensures it is
  28. // always an object.
  29. options = options || {};
  30. // Grant this View superpowers.
  31. LayoutManager.setupView(this, options);
  32. // Have Backbone set up the rest of this View.
  33. Backbone.View.call(this, options);
  34. },
  35. // Shorthand to `setView` function with the `insert` flag set.
  36. insertView: function(selector, view) {
  37. // If the `view` argument exists, then a selector was passed in. This code
  38. // path will forward the selector on to `setView`.
  39. if (view) {
  40. return this.setView(selector, view, true);
  41. }
  42. // If no `view` argument is defined, then assume the first argument is the
  43. // View, somewhat now confusingly named `selector`.
  44. return this.setView(selector, true);
  45. },
  46. // Iterate over an object and ensure every value is wrapped in an array to
  47. // ensure they will be inserted, then pass that object to `setViews`.
  48. insertViews: function(views) {
  49. // If an array of views was passed it should be inserted into the
  50. // root view. Much like calling insertView without a selector.
  51. if (_.isArray(views)) {
  52. return this.setViews({ "": views });
  53. }
  54. _.each(views, function(view, selector) {
  55. views[selector] = _.isArray(view) ? view : [view];
  56. });
  57. return this.setViews(views);
  58. },
  59. // Returns the View that matches the `getViews` filter function.
  60. getView: function(fn) {
  61. // If `getView` is invoked with undefined as the first argument, then the
  62. // second argument will be used instead. This is to allow
  63. // `getViews(undefined, fn)` to work as `getViews(fn)`. Useful for when
  64. // you are allowing an optional selector.
  65. if (typeof fn !== "function" && typeof fn !== "string") {
  66. fn = arguments[1];
  67. }
  68. return this.getViews(fn).first().value();
  69. },
  70. // Provide a filter function to get a flattened array of all the subviews.
  71. // If the filter function is omitted it will return all subviews. If a
  72. // String is passed instead, it will return the Views for that selector.
  73. getViews: function(fn) {
  74. // Generate an array of all top level (no deeply nested) Views flattened.
  75. var views = _.chain(this.views).map(function(view) {
  76. return _.isArray(view) ? view : [view];
  77. }, this).flatten().value();
  78. // If the filter argument is a String, then return a chained Version of the
  79. // elements.
  80. if (typeof fn === "string") {
  81. return _.chain([this.views[fn]]).flatten();
  82. }
  83. // If the argument passed is an Object, then pass it to `_.where`.
  84. if (typeof fn === "object") {
  85. return _.chain([_.where(views, fn)]).flatten();
  86. }
  87. // If a filter function is provided, run it on all Views and return a
  88. // wrapped chain. Otherwise, simply return a wrapped chain of all Views.
  89. return _.chain(typeof fn === "function" ? _.filter(views, fn) : views);
  90. },
  91. // Use this to remove Views, internally uses `getViews` so you can pass the
  92. // same argument here as you would to that method.
  93. removeView: function(fn) {
  94. // Allow an optional selector or function to find the right model and
  95. // remove nested Views based off the results of the selector or filter.
  96. return this.getViews(fn).each(function(nestedView) {
  97. nestedView.remove();
  98. });
  99. },
  100. // This takes in a partial name and view instance and assigns them to
  101. // the internal collection of views. If a view is not a LayoutManager
  102. // instance, then mix in the LayoutManager prototype. This ensures
  103. // all Views can be used successfully.
  104. //
  105. // Must definitely wrap any render method passed in or defaults to a
  106. // typical render function `return layout(this).render()`.
  107. setView: function(name, view, insert) {
  108. var manager, existing, options;
  109. // Parent view, the one you are setting a View on.
  110. var root = this;
  111. // If no name was passed, use an empty string and shift all arguments.
  112. if (typeof name !== "string") {
  113. insert = view;
  114. view = name;
  115. name = "";
  116. }
  117. // If the parent views object doesn't exist... create it.
  118. this.views = this.views || {};
  119. // Shorthand the `__manager__` property.
  120. manager = view.__manager__;
  121. // Shorthand the View that potentially already exists.
  122. existing = this.views[name];
  123. // If the View has not been properly set up, throw an Error message
  124. // indicating that the View needs `manage: true` set.
  125. if (!manager) {
  126. throw new Error("Please set `View#manage` property with selector '" +
  127. name + "' to `true`.");
  128. }
  129. // Assign options.
  130. options = view.getAllOptions();
  131. // Add reference to the parentView.
  132. manager.parent = root;
  133. // Add reference to the placement selector used.
  134. manager.selector = name;
  135. // Set up event bubbling, inspired by Backbone.ViewMaster. Do not bubble
  136. // internal events that are triggered.
  137. view.on("all", function(name) {
  138. if (name !== "beforeRender" && name !== "afterRender") {
  139. root.trigger.apply(root, arguments);
  140. }
  141. }, view);
  142. // Code path is less complex for Views that are not being inserted. Simply
  143. // remove existing Views and bail out with the assignment.
  144. if (!insert) {
  145. // If the View we are adding has already been rendered, simply inject it
  146. // into the parent.
  147. if (manager.hasRendered) {
  148. // If this View is not using a dynamically created container element,
  149. // use the children instead.
  150. if (manager.noel) {
  151. view.setElement(view.$el.children(), false);
  152. }
  153. // Apply the partial.
  154. options.partial(root.el, manager.selector, view.$el, manager.insert);
  155. }
  156. // Ensure remove is called when swapping View's.
  157. if (existing) {
  158. // If the views are an array, iterate and remove each individually.
  159. _.each(aConcat.call([], existing), function(nestedView) {
  160. nestedView.remove();
  161. });
  162. }
  163. // Assign to main views object and return for chainability.
  164. return this.views[name] = view;
  165. }
  166. // Ensure this.views[name] is an array and push this View to the end.
  167. this.views[name] = aConcat.call([], existing || [], view);
  168. // Put the view into `insert` mode.
  169. manager.insert = true;
  170. return view;
  171. },
  172. // Allows the setting of multiple views instead of a single view.
  173. setViews: function(views) {
  174. // Iterate over all the views and use the View's view method to assign.
  175. _.each(views, function(view, name) {
  176. // If the view is an array put all views into insert mode.
  177. if (_.isArray(view)) {
  178. return _.each(view, function(view) {
  179. this.insertView(name, view);
  180. }, this);
  181. }
  182. // Assign each view using the view function.
  183. this.setView(name, view);
  184. }, this);
  185. // Allow for chaining
  186. return this;
  187. },
  188. // By default this should find all nested views and render them into
  189. // the this.el and call done once all of them have successfully been
  190. // resolved.
  191. //
  192. // This function returns a promise that can be chained to determine
  193. // once all subviews and main view have been rendered into the view.el.
  194. render: function() {
  195. var root = this;
  196. var options = root.getAllOptions();
  197. var manager = root.__manager__;
  198. var parent = manager.parent;
  199. var rentManager = parent && parent.__manager__;
  200. var def = options.deferred();
  201. // Triggered once the render has succeeded.
  202. function resolve() {
  203. var next, afterRender;
  204. // If there is a parent, attach.
  205. if (parent) {
  206. if (!options.contains(parent.el, root.el)) {
  207. // If this View is not using a dynamically created container element,
  208. // use the children instead.
  209. if (manager.noel) {
  210. root.setElement(root.$el.children(), false);
  211. }
  212. // Apply the partial.
  213. options.partial(parent.el, manager.selector, root.$el,
  214. manager.insert);
  215. }
  216. }
  217. // Ensure events are always correctly bound after rendering.
  218. root.delegateEvents();
  219. // If no parent, ensure the elements are still set correctly.
  220. if (!parent && manager.noel) {
  221. root.setElement(root.$el.children(), false);
  222. }
  223. // Set this View as successfully rendered.
  224. manager.hasRendered = true;
  225. // Resolve the deferred.
  226. def.resolveWith(root, [root]);
  227. // Only process the queue if it exists.
  228. if (next = manager.queue.shift()) {
  229. // Ensure that the next render is only called after all other
  230. // `done` handlers have completed. This will prevent `render`
  231. // callbacks from firing out of order.
  232. next();
  233. } else {
  234. // Once the queue is depleted, remove it, the render process has
  235. // completed.
  236. delete manager.queue;
  237. }
  238. // Reusable function for triggering the afterRender callback and event
  239. // and setting the hasRendered flag.
  240. function completeRender() {
  241. var afterRender = options.afterRender;
  242. if (afterRender) {
  243. afterRender.call(root, root);
  244. }
  245. // Always emit an afterRender event.
  246. root.trigger("afterRender", root);
  247. }
  248. // If the parent is currently rendering, wait until it has completed
  249. // until calling the nested View's `afterRender`.
  250. if (rentManager && rentManager.queue) {
  251. // Wait until the parent View has finished rendering, which could be
  252. // asynchronous, and trigger afterRender on this View once it has
  253. // compeleted.
  254. return parent.once("afterRender", function() {
  255. // Trigger the afterRender and set hasRendered.
  256. completeRender();
  257. });
  258. }
  259. // This View and its parent have both rendered.
  260. completeRender();
  261. }
  262. // Actually facilitate a render.
  263. function actuallyRender() {
  264. var options = root.getAllOptions();
  265. var manager = root.__manager__;
  266. var parent = manager.parent;
  267. var rentManager = parent && parent.__manager__;
  268. // The `_viewRender` method is broken out to abstract away from having
  269. // too much code in `processRender`.
  270. root._render(LayoutManager._viewRender, options).done(function() {
  271. // If there are no children to worry about, complete the render
  272. // instantly.
  273. if (!_.keys(root.views).length) {
  274. return resolve();
  275. }
  276. // Create a list of promises to wait on until rendering is done.
  277. // Since this method will run on all children as well, its sufficient
  278. // for a full hierarchical.
  279. var promises = _.map(root.views, function(view) {
  280. var insert = _.isArray(view);
  281. // If items are being inserted, they will be in a non-zero length
  282. // Array.
  283. if (insert && view.length) {
  284. // Schedule each view to be rendered in order and return a promise
  285. // representing the result of the final rendering.
  286. return _.reduce(view.slice(1), function(prevRender, view) {
  287. return prevRender.then(function() {
  288. return view.render();
  289. });
  290. // The first view should be rendered immediately, and the resulting
  291. // promise used to initialize the reduction.
  292. }, view[0].render());
  293. }
  294. // Only return the fetch deferred, resolve the main deferred after
  295. // the element has been attached to it's parent.
  296. return !insert ? view.render() : view;
  297. });
  298. // Once all nested Views have been rendered, resolve this View's
  299. // deferred.
  300. options.when(promises).done(function() {
  301. resolve();
  302. });
  303. });
  304. }
  305. // Another render is currently happening if there is an existing queue, so
  306. // push a closure to render later into the queue.
  307. if (manager.queue) {
  308. aPush.call(manager.queue, function() {
  309. actuallyRender();
  310. });
  311. } else {
  312. manager.queue = [];
  313. // This the first `render`, preceeding the `queue` so render
  314. // immediately.
  315. actuallyRender(root, def);
  316. }
  317. // Add the View to the deferred so that `view.render().view.el` is
  318. // possible.
  319. def.view = root;
  320. // This is the promise that determines if the `render` function has
  321. // completed or not.
  322. return def;
  323. },
  324. // Ensure the cleanup function is called whenever remove is called.
  325. remove: function() {
  326. // Force remove itself from its parent.
  327. LayoutManager._removeView(this, true);
  328. // Call the original remove function.
  329. return this._remove.apply(this, arguments);
  330. },
  331. // Merge instance and global options.
  332. getAllOptions: function() {
  333. // Instance overrides take precedence, fallback to prototype options.
  334. return _.extend({}, this, LayoutManager.prototype.options, this.options);
  335. }
  336. },
  337. {
  338. // Clearable cache.
  339. _cache: {},
  340. // Creates a deferred and returns a function to call when finished.
  341. _makeAsync: function(options, done) {
  342. var handler = options.deferred();
  343. // Used to handle asynchronous renders.
  344. handler.async = function() {
  345. handler._isAsync = true;
  346. return done;
  347. };
  348. return handler;
  349. },
  350. // This gets passed to all _render methods. The `root` value here is passed
  351. // from the `manage(this).render()` line in the `_render` function
  352. _viewRender: function(root, options) {
  353. var url, contents, fetchAsync;
  354. var manager = root.__manager__;
  355. // This function is responsible for pairing the rendered template into
  356. // the DOM element.
  357. function applyTemplate(rendered) {
  358. // Actually put the rendered contents into the element.
  359. if (rendered) {
  360. options.html(root.$el, rendered);
  361. }
  362. // Resolve only after fetch and render have succeeded.
  363. fetchAsync.resolveWith(root, [root]);
  364. }
  365. // Once the template is successfully fetched, use its contents to proceed.
  366. // Context argument is first, since it is bound for partial application
  367. // reasons.
  368. function done(context, contents) {
  369. // Store the rendered template someplace so it can be re-assignable.
  370. var rendered;
  371. // This allows the `render` method to be asynchronous as well as `fetch`.
  372. var renderAsync = LayoutManager._makeAsync(options, function(rendered) {
  373. applyTemplate(rendered);
  374. });
  375. // Ensure the cache is up-to-date.
  376. LayoutManager.cache(url, contents);
  377. // Render the View into the el property.
  378. if (contents) {
  379. rendered = options.render.call(renderAsync, contents, context);
  380. }
  381. // If the function was synchronous, continue execution.
  382. if (!renderAsync._isAsync) {
  383. applyTemplate(rendered);
  384. }
  385. }
  386. return {
  387. // This `render` function is what gets called inside of the View render,
  388. // when `manage(this).render` is called. Returns a promise that can be
  389. // used to know when the element has been rendered into its parent.
  390. render: function() {
  391. var context = root.serialize || options.serialize;
  392. var template = root.template || options.template;
  393. // If data is a function, immediately call it.
  394. if (_.isFunction(context)) {
  395. context = context.call(root);
  396. }
  397. // This allows for `var done = this.async()` and then `done(contents)`.
  398. fetchAsync = LayoutManager._makeAsync(options, function(contents) {
  399. done(context, contents);
  400. });
  401. // Set the url to the prefix + the view's template property.
  402. if (typeof template === "string") {
  403. url = options.prefix + template;
  404. }
  405. // Check if contents are already cached and if they are, simply process
  406. // the template with the correct data.
  407. if (contents = LayoutManager.cache(url)) {
  408. done(context, contents, url);
  409. return fetchAsync;
  410. }
  411. // Fetch layout and template contents.
  412. if (typeof template === "string") {
  413. contents = options.fetch.call(fetchAsync, options.prefix + template);
  414. // If the template is already a function, simply call it.
  415. } else if (typeof template === "function") {
  416. contents = template;
  417. // If its not a string and not undefined, pass the value to `fetch`.
  418. } else if (template != null) {
  419. contents = options.fetch.call(fetchAsync, template);
  420. }
  421. // If the function was synchronous, continue execution.
  422. if (!fetchAsync._isAsync) {
  423. done(context, contents);
  424. }
  425. return fetchAsync;
  426. }
  427. };
  428. },
  429. // Remove all nested Views.
  430. _removeViews: function(root, force) {
  431. var views;
  432. // Shift arguments around.
  433. if (typeof root === "boolean") {
  434. force = root;
  435. root = this;
  436. }
  437. // Allow removeView to be called on instances.
  438. root = root || this;
  439. // Iterate over all of the nested View's and remove.
  440. root.getViews().each(function(view) {
  441. // Force doesn't care about if a View has rendered or not.
  442. if (view.__manager__.hasRendered || force) {
  443. LayoutManager._removeView(view, force);
  444. }
  445. });
  446. },
  447. // Remove a single nested View.
  448. _removeView: function(view, force) {
  449. var parentViews;
  450. // Shorthand the manager for easier access.
  451. var manager = view.__manager__;
  452. // Test for keep.
  453. var keep = typeof view.keep === "boolean" ? view.keep : view.options.keep;
  454. // Only remove views that do not have `keep` attribute set, unless the
  455. // View is in `insert` mode and the force flag is set.
  456. if (!keep && (manager.insert === true || force)) {
  457. // Clean out the events.
  458. LayoutManager.cleanViews(view);
  459. // Since we are removing this view, force subviews to remove
  460. view._removeViews(true);
  461. // Remove the View completely.
  462. view.$el.remove();
  463. // Bail out early if no parent exists.
  464. if (!manager.parent) { return; }
  465. // Assign (if they exist) the sibling Views to a property.
  466. parentViews = manager.parent.views[manager.selector];
  467. // If this is an array of items remove items that are not marked to
  468. // keep.
  469. if (_.isArray(parentViews)) {
  470. // Remove duplicate Views.
  471. return _.each(_.clone(parentViews), function(view, i) {
  472. // If the managers match, splice off this View.
  473. if (view && view.__manager__ === manager) {
  474. aSplice.call(parentViews, i, 1);
  475. }
  476. });
  477. }
  478. // Otherwise delete the parent selector.
  479. delete manager.parent.views[manager.selector];
  480. }
  481. },
  482. // Cache templates into LayoutManager._cache.
  483. cache: function(path, contents) {
  484. // If template path is found in the cache, return the contents.
  485. if (path in this._cache && contents == null) {
  486. return this._cache[path];
  487. // Ensure path and contents aren't undefined.
  488. } else if (path != null && contents != null) {
  489. return this._cache[path] = contents;
  490. }
  491. // If the template is not in the cache, return undefined.
  492. },
  493. // Accept either a single view or an array of views to clean of all DOM
  494. // events internal model and collection references and all Backbone.Events.
  495. cleanViews: function(views) {
  496. // Clear out all existing views.
  497. _.each(aConcat.call([], views), function(view) {
  498. // Remove all custom events attached to this View.
  499. view.unbind();
  500. // Automatically unbind `model`.
  501. if (view.model instanceof Backbone.Model) {
  502. view.model.off(null, null, view);
  503. }
  504. // Automatically unbind `collection`.
  505. if (view.collection instanceof Backbone.Collection) {
  506. view.collection.off(null, null, view);
  507. }
  508. // Automatically unbind events bound to this View.
  509. view.stopListening();
  510. // If a custom cleanup method was provided on the view, call it after
  511. // the initial cleanup is done
  512. _.result(view, "cleanup");
  513. });
  514. },
  515. // This static method allows for global configuration of LayoutManager.
  516. configure: function(options) {
  517. _.extend(LayoutManager.prototype.options, options);
  518. // Allow LayoutManager to manage Backbone.View.prototype.
  519. if (options.manage) {
  520. Backbone.View.prototype.manage = true;
  521. }
  522. // Disable the element globally.
  523. if (options.el === false) {
  524. Backbone.View.prototype.el = false;
  525. }
  526. },
  527. // Configure a View to work with the LayoutManager plugin.
  528. setupView: function(views, options) {
  529. // Set up all Views passed.
  530. _.each(aConcat.call([], views), function(view) {
  531. // If the View has already been setup, no need to do it again.
  532. if (view.__manager__) {
  533. return;
  534. }
  535. var views, declaredViews, viewOptions;
  536. var proto = LayoutManager.prototype;
  537. var viewOverrides = _.pick(view, keys);
  538. // Ensure necessary properties are set.
  539. _.defaults(view, {
  540. // Ensure a view always has a views object.
  541. views: {},
  542. // Internal state object used to store whether or not a View has been
  543. // taken over by layout manager and if it has been rendered into the DOM.
  544. __manager__: {},
  545. // Add the ability to remove all Views.
  546. _removeViews: LayoutManager._removeViews,
  547. // Add the ability to remove itself.
  548. _removeView: LayoutManager._removeView
  549. // Mix in all LayoutManager prototype properties as well.
  550. }, LayoutManager.prototype);
  551. // Extend the options with the prototype and passed options.
  552. options = view.options = _.defaults(options || {}, view.options,
  553. proto.options);
  554. // Ensure view events are properly copied over.
  555. viewOptions = _.pick(options, aConcat.call(["events"],
  556. _.values(options.events)));
  557. // Merge the View options into the View.
  558. _.extend(view, viewOptions);
  559. // If the View still has the Backbone.View#render method, remove it. Don't
  560. // want it accidentally overriding the LM render.
  561. if (viewOverrides.render === LayoutManager.prototype.render ||
  562. viewOverrides.render === Backbone.View.prototype.render) {
  563. delete viewOverrides.render;
  564. }
  565. // Pick out the specific properties that can be dynamically added at
  566. // runtime and ensure they are available on the view object.
  567. _.extend(options, viewOverrides);
  568. // By default the original Remove function is the Backbone.View one.
  569. view._remove = Backbone.View.prototype.remove;
  570. // Always use this render function when using LayoutManager.
  571. view._render = function(manage, options) {
  572. // Keep the view consistent between callbacks and deferreds.
  573. var view = this;
  574. // Shorthand the manager.
  575. var manager = view.__manager__;
  576. // Cache these properties.
  577. var beforeRender = options.beforeRender;
  578. // Ensure all nested Views are properly scrubbed if re-rendering.
  579. if (manager.hasRendered) {
  580. this._removeViews();
  581. }
  582. // If a beforeRender function is defined, call it.
  583. if (beforeRender) {
  584. beforeRender.call(this, this);
  585. }
  586. // Always emit a beforeRender event.
  587. this.trigger("beforeRender", this);
  588. // Render!
  589. return manage(this, options).render();
  590. };
  591. // Ensure the render is always set correctly.
  592. view.render = LayoutManager.prototype.render;
  593. // If the user provided their own remove override, use that instead of the
  594. // default.
  595. if (view.remove !== proto.remove) {
  596. view._remove = view.remove;
  597. view.remove = proto.remove;
  598. }
  599. // Normalize views to exist on either instance or options, default to
  600. // options.
  601. views = options.views || view.views;
  602. // Set the internal views, only if selectors have been provided.
  603. if (_.keys(views).length) {
  604. // Keep original object declared containing Views.
  605. declaredViews = views;
  606. // Reset the property to avoid duplication or overwritting.
  607. view.views = {};
  608. // Set the declared Views.
  609. view.setViews(declaredViews);
  610. }
  611. // If a template is passed use that instead.
  612. if (view.options.template) {
  613. view.options.template = options.template;
  614. // Ensure the template is mapped over.
  615. } else if (view.template) {
  616. options.template = view.template;
  617. // Remove it from the instance.
  618. delete view.template;
  619. }
  620. });
  621. }
  622. });
  623. // Convenience assignment to make creating Layout's slightly shorter.
  624. Backbone.Layout = LayoutManager;
  625. // Tack on the version.
  626. LayoutManager.VERSION = "0.8.1";
  627. // Override _configure to provide extra functionality that is necessary in
  628. // order for the render function reference to be bound during initialize.
  629. Backbone.View.prototype._configure = function(options) {
  630. var noel, retVal;
  631. // Remove the container element provided by Backbone.
  632. if ("el" in options ? options.el === false : this.el === false) {
  633. noel = true;
  634. }
  635. // Run the original _configure.
  636. retVal = _configure.apply(this, arguments);
  637. // If manage is set, do it!
  638. if (options.manage || this.manage) {
  639. // Set up this View.
  640. LayoutManager.setupView(this);
  641. }
  642. // Assign the `noel` property once we're sure the View we're working with is
  643. // mangaed by LayoutManager.
  644. if (this.__manager__) {
  645. this.__manager__.noel = noel;
  646. }
  647. // Act like nothing happened.
  648. return retVal;
  649. };
  650. // Default configuration options; designed to be overriden.
  651. LayoutManager.prototype.options = {
  652. // Prefix template/layout paths.
  653. prefix: "",
  654. // Can be used to supply a different deferred implementation.
  655. deferred: function() {
  656. return $.Deferred();
  657. },
  658. // Fetch is passed a path and is expected to return template contents as a
  659. // function or string.
  660. fetch: function(path) {
  661. return _.template($(path).html());
  662. },
  663. // This is the most common way you will want to partially apply a view into
  664. // a layout.
  665. partial: function(root, name, $el, insert) {
  666. // If no selector is specified, assume the parent should be added to.
  667. var $root = name ? $(root).find(name) : $(root);
  668. // Use the insert method if insert argument is true.
  669. if (insert) {
  670. this.insert($root, $el);
  671. } else {
  672. this.html($root, $el);
  673. }
  674. },
  675. // Override this with a custom HTML method, passed a root element and content
  676. // (a jQuery collection or a string) to replace the innerHTML with.
  677. html: function($root, content) {
  678. $root.html(content);
  679. },
  680. // Very similar to HTML except this one will appendChild by default.
  681. insert: function($root, $el) {
  682. $root.append($el);
  683. },
  684. // Return a deferred for when all promises resolve/reject.
  685. when: function(promises) {
  686. return $.when.apply(null, promises);
  687. },
  688. // By default, render using underscore's templating.
  689. render: function(template, context) {
  690. return template(context);
  691. },
  692. // A method to determine if a View contains another.
  693. contains: function(parent, child) {
  694. return $.contains(parent, child);
  695. }
  696. };
  697. // Maintain a list of the keys at define time.
  698. keys = _.keys(LayoutManager.prototype.options);
  699. })(typeof global === "object" ? global : this);