PageRenderTime 47ms CodeModel.GetById 23ms RepoModel.GetById 0ms app.codeStats 0ms

/src/models/livecollection.js

https://bitbucket.org/gordonbrander/rjs-compile-test
JavaScript | 147 lines | 71 code | 21 blank | 55 comment | 10 complexity | 2138d65700832f9a329eb0bc7f4ae84b MD5 | raw file
  1. define([
  2. 'underscore',
  3. 'backbone'
  4. ],
  5. function (util, Backbone) {
  6. var Collection = Backbone.Collection;
  7. // Define a Backbone Collection handling the details of running a "live"
  8. // collection. Live collections are expected to handle fetching their own
  9. // data, rather than being composed from separate models.
  10. // They typically add new models instead of resetting the collection.
  11. // A custom add comparison makes sure that duplicate models are not
  12. // added. End result: only new elements will be added, instead
  13. // of redrawing the whole collection.
  14. //
  15. // myCollection.fetch({ diff: true }); // adds and avoids duplicates
  16. // myCollection.fetch(); // resets
  17. //
  18. // Thanks to this Bocoup article:
  19. // <http://weblog.bocoup.com/backbone-live-collections>
  20. var LiveCollection = Collection.extend({
  21. // An specialized fetch that can add new models and remove
  22. // outdated ones. Models in response that are already in the
  23. // collection are left alone. Usage:
  24. //
  25. // myCollection.fetch({ diff: true });
  26. fetch: function (options) {
  27. options = util.extend({
  28. silent: false
  29. }, options);
  30. // Trigger a fetch event -- useful for adding, then removing
  31. // loaders.
  32. if (!options.silent) this.trigger('fetch');
  33. if (options.diff) {
  34. var success = options.success,
  35. prune = util.bind(this.prune, this);
  36. // Add new models, rather than resetting.
  37. options.add = true;
  38. // Wrap the success callback, adding a pruning
  39. // step after fetching.
  40. options.success = function (collection, resp) {
  41. // Fires regardless of whether collection is reset or added.
  42. if (!options.silent) collection.trigger('success');
  43. prune(collection, resp);
  44. if (success) success(collection, resp);
  45. };
  46. }
  47. // Delegate to original fetch method.
  48. Collection.prototype.fetch.call(this, options);
  49. },
  50. // A custom add function that can prevent models with duplicate IDs
  51. // from being added to the collection. Usage:
  52. //
  53. // myCollection.add({ unique: true });
  54. add: function(models, options) {
  55. var modelsToAdd = models;
  56. options = util.extend({
  57. unique: true
  58. }, options);
  59. // If unique option is set, don't add duplicate IDs.
  60. if (options.unique) {
  61. modelsToAdd = [];
  62. util.each(models, function(model) {
  63. if ( !model.id || util.isUndefined( this.get(model.id) ) ) {
  64. modelsToAdd.push(model);
  65. }
  66. }, this);
  67. }
  68. return Collection.prototype.add.call(this, modelsToAdd, options);
  69. },
  70. // Weed out old models in collection, that are no longer being returned
  71. // by the endpoint. Typically used as a callback for this.fetch's
  72. // success option.
  73. prune: function (collection, resp) {
  74. // Process response -- we get the raw
  75. // results directly from Backbone.sync.
  76. var parsedResp = this.parse(resp),
  77. modelToID = function (model) { return model.id; },
  78. respIDs, collectionIDs, oldModels;
  79. // Convert array of JSON model objects to array of IDs.
  80. respIDs = util.map(parsedResp, modelToID);
  81. collectionIDs = util.map(collection.toJSON(), modelToID);
  82. // Find the difference between the two...
  83. oldModels = util.difference(collectionIDs, respIDs);
  84. // ...and remove it from the collection
  85. // (remove can take IDs or objects).
  86. collection.remove(oldModels);
  87. },
  88. // Poll this collection's endpoint.
  89. // Options:
  90. //
  91. // * `interval`: time between polls, in milliseconds.
  92. // * `tries`: the maximum number of polls for this stream.
  93. stream: function(options) {
  94. var polledCount = 0;
  95. // Cancel any potential previous stream.
  96. this.unstream();
  97. var update = util.bind(function() {
  98. // Make a shallow copy of the options object.
  99. // `Backbone.collection.fetch` wraps the success function
  100. // in an outer function (line `527`), replacing options.success.
  101. // That means if we don't copy the object every poll, we'll end
  102. // up modifying the reference object and creating callback inception.
  103. //
  104. // Furthermore, since the sync success wrapper
  105. // that wraps and replaces options.success has a different arguments
  106. // order, you'll end up getting the wrong arguments.
  107. var opts = util.clone(options);
  108. if (!opts.tries || polledCount < opts.tries) {
  109. polledCount = polledCount + 1;
  110. this.fetch(opts);
  111. this.pollTimeout = setTimeout(update, opts.interval || 1000);
  112. }
  113. }, this);
  114. update();
  115. },
  116. // Stop polling.
  117. unstream: function() {
  118. clearTimeout(this.pollTimeout);
  119. delete this.pollTimeout;
  120. },
  121. isStreaming : function() {
  122. return util.isUndefined(this.pollTimeout);
  123. }
  124. });
  125. return LiveCollection;
  126. });