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

/js/lib/backbone.localStorage.js

https://bitbucket.org/sulab/data-chart-plugin
JavaScript | 213 lines | 141 code | 34 blank | 38 comment | 35 complexity | 5fb8136e355f609eba20942d061a6e0d MD5 | raw file
  1. /**
  2. * Backbone localStorage Adapter
  3. * Version 1.1.0
  4. *
  5. * https://github.com/jeromegn/Backbone.localStorage
  6. */
  7. (function (root, factory) {
  8. if (typeof define === "function" && define.amd) {
  9. // AMD. Register as an anonymous module.
  10. define(["underscore","backbone"], function(_, Backbone) {
  11. // Use global variables if the locals are undefined.
  12. return factory(_ || root._, Backbone || root.Backbone);
  13. });
  14. } else {
  15. // RequireJS isn't being used. Assume underscore and backbone are loaded in <script> tags
  16. factory(_, Backbone);
  17. }
  18. }(this, function(_, Backbone) {
  19. // A simple module to replace `Backbone.sync` with *localStorage*-based
  20. // persistence. Models are given GUIDS, and saved into a JSON object. Simple
  21. // as that.
  22. // Hold reference to Underscore.js and Backbone.js in the closure in order
  23. // to make things work even if they are removed from the global namespace
  24. // Generate four random hex digits.
  25. function S4() {
  26. return (((1+Math.random())*0x10000)|0).toString(16).substring(1);
  27. };
  28. // Generate a pseudo-GUID by concatenating random hexadecimal.
  29. function guid() {
  30. return (S4()+S4()+"-"+S4()+"-"+S4()+"-"+S4()+"-"+S4()+S4()+S4());
  31. };
  32. // Our Store is represented by a single JS object in *localStorage*. Create it
  33. // with a meaningful name, like the name you'd give a table.
  34. // window.Store is deprectated, use Backbone.LocalStorage instead
  35. Backbone.LocalStorage = window.Store = function(name) {
  36. this.name = name;
  37. var store = this.localStorage().getItem(this.name);
  38. this.records = (store && store.split(",")) || [];
  39. };
  40. _.extend(Backbone.LocalStorage.prototype, {
  41. // Save the current state of the **Store** to *localStorage*.
  42. save: function() {
  43. this.localStorage().setItem(this.name, this.records.join(","));
  44. },
  45. // Add a model, giving it a (hopefully)-unique GUID, if it doesn't already
  46. // have an id of it's own.
  47. create: function(model) {
  48. if (!model.id) {
  49. model.id = guid();
  50. model.set(model.idAttribute, model.id);
  51. }
  52. this.localStorage().setItem(this.name+"-"+model.id, JSON.stringify(model));
  53. this.records.push(model.id.toString());
  54. this.save();
  55. return this.find(model);
  56. },
  57. // Update a model by replacing its copy in `this.data`.
  58. update: function(model) {
  59. this.localStorage().setItem(this.name+"-"+model.id, JSON.stringify(model));
  60. if (!_.include(this.records, model.id.toString()))
  61. this.records.push(model.id.toString()); this.save();
  62. return this.find(model);
  63. },
  64. // Retrieve a model from `this.data` by id.
  65. find: function(model) {
  66. return this.jsonData(this.localStorage().getItem(this.name+"-"+model.id));
  67. },
  68. // Return the array of all models currently in storage.
  69. findAll: function() {
  70. return _(this.records).chain()
  71. .map(function(id){
  72. return this.jsonData(this.localStorage().getItem(this.name+"-"+id));
  73. }, this)
  74. .compact()
  75. .value();
  76. },
  77. // Delete a model from `this.data`, returning it.
  78. destroy: function(model) {
  79. if (model.isNew())
  80. return false
  81. this.localStorage().removeItem(this.name+"-"+model.id);
  82. this.records = _.reject(this.records, function(id){
  83. return id === model.id.toString();
  84. });
  85. this.save();
  86. return model;
  87. },
  88. localStorage: function() {
  89. return localStorage;
  90. },
  91. // fix for "illegal access" error on Android when JSON.parse is passed null
  92. jsonData: function (data) {
  93. return data && JSON.parse(data);
  94. },
  95. // Clear localStorage for specific collection.
  96. _clear: function() {
  97. var local = this.localStorage(),
  98. itemRe = new RegExp("^" + this.name + "-");
  99. // Remove id-tracking item (e.g., "foo").
  100. local.removeItem(this.name);
  101. // Match all data items (e.g., "foo-ID") and remove.
  102. _.chain(local).keys()
  103. .filter(function (k) { return itemRe.test(k); })
  104. .each(function (k) { local.removeItem(k); });
  105. },
  106. // Size of localStorage.
  107. _storageSize: function() {
  108. return this.localStorage().length;
  109. }
  110. });
  111. // localSync delegate to the model or collection's
  112. // *localStorage* property, which should be an instance of `Store`.
  113. // window.Store.sync and Backbone.localSync is deprecated, use Backbone.LocalStorage.sync instead
  114. Backbone.LocalStorage.sync = window.Store.sync = Backbone.localSync = function(method, model, options) {
  115. var store = model.localStorage || model.collection.localStorage;
  116. var resp, errorMessage, syncDfd = $.Deferred && $.Deferred(); //If $ is having Deferred - use it.
  117. try {
  118. switch (method) {
  119. case "read":
  120. resp = model.id != undefined ? store.find(model) : store.findAll();
  121. break;
  122. case "create":
  123. resp = store.create(model);
  124. break;
  125. case "update":
  126. resp = store.update(model);
  127. break;
  128. case "delete":
  129. resp = store.destroy(model);
  130. break;
  131. }
  132. } catch(error) {
  133. if (error.code === DOMException.QUOTA_EXCEEDED_ERR && store._storageSize() === 0)
  134. errorMessage = "Private browsing is unsupported";
  135. else
  136. errorMessage = error.message;
  137. }
  138. if (resp) {
  139. model.trigger("sync", model, resp, options);
  140. if (options && options.success)
  141. if (Backbone.VERSION === "0.9.10") {
  142. options.success(model, resp, options);
  143. } else {
  144. options.success(resp);
  145. }
  146. if (syncDfd)
  147. syncDfd.resolve(resp);
  148. } else {
  149. errorMessage = errorMessage ? errorMessage
  150. : "Record Not Found";
  151. model.trigger("error", model, errorMessage, options);
  152. if (options && options.error)
  153. if (Backbone.VERSION === "0.9.10") {
  154. options.error(model, errorMessage, options);
  155. } else {
  156. options.error(errorMessage);
  157. }
  158. if (syncDfd)
  159. syncDfd.reject(errorMessage);
  160. }
  161. // add compatibility with $.ajax
  162. // always execute callback for success and error
  163. if (options && options.complete) options.complete(resp);
  164. return syncDfd && syncDfd.promise();
  165. };
  166. Backbone.ajaxSync = Backbone.sync;
  167. Backbone.getSyncMethod = function(model) {
  168. if(model.localStorage || (model.collection && model.collection.localStorage)) {
  169. return Backbone.localSync;
  170. }
  171. return Backbone.ajaxSync;
  172. };
  173. // Override 'Backbone.sync' to default to localSync,
  174. // the original 'Backbone.sync' is still available in 'Backbone.ajaxSync'
  175. Backbone.sync = function(method, model, options) {
  176. return Backbone.getSyncMethod(model).apply(this, [method, model, options]);
  177. };
  178. return Backbone.LocalStorage;
  179. }));