PageRenderTime 43ms CodeModel.GetById 17ms RepoModel.GetById 1ms app.codeStats 0ms

/static/js/models/basemodel.js

https://bitbucket.org/gordonbrander/accordion-drawer-prototypes
JavaScript | 158 lines | 86 code | 26 blank | 46 comment | 13 complexity | 7ea0470c563f1758ea7cb9dfe369d322 MD5 | raw file
Possible License(s): Apache-2.0
  1. // This is a base class for models -- a thick and delicious layer of frosting
  2. // spread all over Backbone's Model cake.
  3. define([
  4. 'underscore',
  5. 'backbone',
  6. 'lib/promise',
  7. 'lib/lazily',
  8. 'models/statemixin'
  9. ],
  10. function (
  11. util,
  12. Backbone,
  13. Promise,
  14. lazily,
  15. stateMixin
  16. ) {
  17. var __Array = Array.prototype;
  18. var __BaseModel = util.extend(stateMixin, {
  19. parent: function () {
  20. return this.collection;
  21. },
  22. // A specialized fetch that decouples processing response objects from
  23. // fetching.
  24. //
  25. // Returns a `Promise` for the response value.
  26. //
  27. // This implementation of fetch completely bypasses Backbone's fetch
  28. // because we want to be able to provide the response, without parsing,
  29. // to the success event. It does use `Backbone.sync`, though.
  30. fetch: function (options) {
  31. options = options || {};
  32. var model = this;
  33. var cachedFetch = this._promisedFetch;
  34. // Check for an unresolved (in-flight) cached fetch promise on this
  35. // instance. If we find one, return it instead of issuing an
  36. // additional fetch.
  37. //
  38. // TODO: this is naive and assumes there is no real difference between
  39. // kinds of fetch determined by `options`. If we need to improve this
  40. // we could key the cache by doing a JSON.stringify on `options` and
  41. // using that as a promise cache key. For now, I want to avoid the
  42. // perf penalty until we know we need this. -GB
  43. if (
  44. cachedFetch &&
  45. !cachedFetch.resolved &&
  46. !cachedFetch.rejected
  47. ) {
  48. return cachedFetch;
  49. }
  50. this._promisedFetch = new Promise();
  51. var promisedFetch = this._promisedFetch;
  52. // If success and error callbacks were provided, add them to the
  53. // `then` queue.
  54. promisedFetch.then(options.success, options.error);
  55. // Trigger a fetch event -- useful for adding, then removing
  56. // loaders.
  57. if (!options.silent) this.trigger('fetch');
  58. // Redefine success/error handlers to manage promise.
  59. options.success = function (resp) {
  60. model = model.process(resp, options);
  61. promisedFetch.resolve(model);
  62. };
  63. options.error = function (resp) {
  64. if (!options.silent) model.trigger('error', resp, resp, options);
  65. promisedFetch.reject(resp);
  66. };
  67. (this.sync || Backbone.sync).call(this, 'read', this, options);
  68. return promisedFetch;
  69. },
  70. // Process a response object -- the object is usually the result of a
  71. // call to `fetch`, but may be passed to `process` directly.
  72. // Either way, the logical path for processing a response object should
  73. // be the same.
  74. process: function (resp, options) {
  75. options = options || {};
  76. var model = this;
  77. // Use any passed `parse` option, or fall back to `this.parse`.
  78. model.set((options.parse || this.parse).call(this, resp), options);
  79. // Notify other interested parties that we've had a value come
  80. // back from the server. Provide the value unparsed.
  81. if (!options.silent) model.trigger('success', resp, options);
  82. return model;
  83. }
  84. });
  85. // Extend Backbone Model with __BaseModel object.
  86. var BaseModel = Backbone.Model.extend(__BaseModel);
  87. // This method should be used to provision subcollections on model
  88. // prototypes.
  89. // Pass the model constructor function, the key you want the method to be
  90. // provisioned on and the collection constructor to use.
  91. BaseModel.subcollection = function (key, Collection) {
  92. Collection = Collection || Backbone.Collection;
  93. // This is the list of events from a collection that seem worth republishing
  94. // to me.
  95. var republish = [
  96. 'add',
  97. 'remove',
  98. 'move',
  99. 'reset',
  100. 'state'
  101. ];
  102. return lazily(function () {
  103. var self = this;
  104. var subcollection = new Collection();
  105. subcollection.parent = function () { return self; };
  106. // Republish subCollection events on the model.
  107. subcollection.bind('all', function (ev, model, collection) {
  108. var args = arguments,
  109. trigger = this.trigger;
  110. // If we get one of the events above from our collection, let's
  111. // add the key prefix and republish it.
  112. if (collection === subcollection && util.indexOf(republish, ev) !== -1) {
  113. args = __Array.slice.call(arguments);
  114. args.shift();
  115. args.unshift(key + ' ' + ev);
  116. return trigger.apply(self, args);
  117. }
  118. // Otherwise, if we got the event from elsewhere, let's check if
  119. // the event contains any of the words above IN ADDITION to. If it does, we can
  120. // republish it.
  121. // This will only publish events that look like `foo add`,
  122. // `foobar remove`, `foobar baz remove`.
  123. var iOf;
  124. for (var i = 0; i < republish.length; i++) {
  125. if (ev.indexOf(' ' + republish[i]) > 0) return trigger.apply(self, args);
  126. }
  127. }, this);
  128. return subcollection;
  129. });
  130. };
  131. return BaseModel;
  132. });