API Docs for: 3.13.0
Show:

File: app/js/model-extensions/model-sync-local.js

  1. /*
  2. An extension which provides a sync implementation through locally stored
  3. key value pairs, either through the HTML localStorage API or falling back
  4. onto an in-memory cache, that can be mixed into a Model or ModelList subclass.
  5.  
  6. @module app
  7. @submodule model-sync-local
  8. @since @VERSION@
  9. **/
  10.  
  11. /**
  12. An extension which provides a sync implementation through locally stored
  13. key value pairs, either through the HTML localStorage API or falling back
  14. onto an in-memory cache, that can be mixed into a Model or ModelList subclass.
  15.  
  16. A group of Models/ModelLists is serialized in localStorage by either its
  17. class name, or a specified 'root' that is provided.
  18.  
  19. var User = Y.Base.create('user', Y.Model, [Y.ModelSync.Local], {
  20. root: 'user'
  21. });
  22.  
  23. var Users = Y.Base.create('users', Y.ModelList, [Y.ModelSync.Local], {
  24. model: User,
  25. });
  26.  
  27. @class ModelSync.Local
  28. @extensionfor Model
  29. @extensionfor ModelList
  30. @since @VERSION@
  31. **/
  32. function LocalSync() {}
  33.  
  34. /**
  35. Properties that shouldn't be turned into ad-hoc attributes when passed to a
  36. Model or ModelList constructor.
  37.  
  38. @property _NON_ATTRS_CFG
  39. @type Array
  40. @default ['root']
  41. @static
  42. @protected
  43. @since @VERSION@
  44. **/
  45. LocalSync._NON_ATTRS_CFG = ['root'];
  46.  
  47. /**
  48. Feature testing for `localStorage` availability.
  49. Will return falsey for browsers with `localStorage`, but that don't
  50. actually work, such as iOS Safari in private browsing mode.
  51.  
  52. @property _hasLocalStorage
  53. @type Boolean
  54. @private
  55. **/
  56. LocalSync._hasLocalStorage = (function () {
  57. var LS = Y.config.win.localStorage,
  58. test = Y.guid();
  59.  
  60. try {
  61. LS.setItem(test, test);
  62. LS.removeItem(test);
  63. return true;
  64. } catch (e) {
  65. return false;
  66. }
  67. })(),
  68.  
  69. /**
  70. Object of key/value pairs to fall back on when localStorage is not available.
  71.  
  72. @property _data
  73. @type Object
  74. @private
  75. **/
  76. LocalSync._data = {};
  77.  
  78. /**
  79. Cache to quickly access a specific object with a given ID.
  80. This maps a model's ID to its reference inside of `LocalSync._data`.
  81.  
  82. @property _idMap
  83. @type Object
  84. @private
  85. **/
  86.  
  87. LocalSync._idMap = {};
  88.  
  89. LocalSync.prototype = {
  90.  
  91. // -- Public Methods -------------------------------------------------------
  92. /**
  93. Root used as the key inside of localStorage and/or the in-memory store.
  94. @property root
  95. @type String
  96. @default ""
  97. @since @VERSION@
  98. **/
  99. root: '',
  100.  
  101. /**
  102. Shortcut for access to localStorage.
  103. @property storage
  104. @type Storage
  105. @default null
  106. @since @VERSION@
  107. **/
  108. storage: null,
  109.  
  110. // -- Lifecycle Methods -----------------------------------------------------
  111. initializer: function (config) {
  112. var store, data;
  113.  
  114. config || (config = {});
  115.  
  116. if ('root' in config) {
  117. this.root = config.root || '';
  118. }
  119.  
  120. // This is checking to see if the sync layer is being applied to
  121. // a ModelList, and if so, is looking for a `root` property on its
  122. // Model's prototype instead.
  123. if (!this.root && this.model && this.model.prototype.root) {
  124. this.root = this.model.prototype.root;
  125. }
  126.  
  127. if (LocalSync._hasLocalStorage) {
  128. this.storage = Y.config.win.localStorage;
  129. store = this.storage.getItem(this.root);
  130. } else {
  131. Y.log("Could not access localStorage.", "warn");
  132. }
  133.  
  134. // Pull in existing data from localStorage, if possible.
  135. // Otherwise, see if there's existing data on the local cache.
  136. if (store) {
  137. try {
  138. LocalSync._data[this.root] = Y.JSON.parse(store);
  139. } catch (e) {
  140. LocalSync._data[this.root] = [];
  141. }
  142. } else {
  143. LocalSync._data[this.root] || (LocalSync._data[this.root] = []);
  144. }
  145.  
  146. // Map each model's ID to its reference inside of data, if there
  147. // are already existing models inside of `localStorage`.
  148. LocalSync._idMap[this.root] || (LocalSync._idMap[this.root] = {});
  149. Y.Array.each(LocalSync._data[this.root], function (item) {
  150. var id = item.id;
  151. if (id) {
  152. LocalSync._idMap[this.root][id] = item;
  153. }
  154. }, this);
  155. },
  156. // -- Public Methods -----------------------------------------------------------
  157. /**
  158. Creates a synchronization layer with the localStorage API, if available.
  159. Otherwise, falls back to a in-memory data store.
  160.  
  161. This method is called internally by load(), save(), and destroy().
  162.  
  163. @method sync
  164. @param {String} action Sync action to perform. May be one of the following:
  165.  
  166. * **create**: Store a newly-created model for the first time.
  167. * **read** : Load an existing model.
  168. * **update**: Update an existing model.
  169. * **delete**: Delete an existing model.
  170.  
  171. @param {Object} [options] Sync options
  172. @param {callback} [callback] Called when the sync operation finishes.
  173. @param {Error|null} callback.err If an error occurred, this parameter will
  174. contain the error. If the sync operation succeeded, _err_ will be
  175. falsey.
  176. @param {Any} [callback.response] The response from our sync. This value will
  177. be passed to the parse() method, which is expected to parse it and
  178. return an attribute hash.
  179. **/
  180. sync: function (action, options, callback) {
  181. options || (options = {});
  182. var response, errorInfo;
  183.  
  184. try {
  185. switch (action) {
  186. case 'read':
  187. if (this._isYUIModelList) {
  188. response = this._index(options);
  189. } else {
  190. response = this._show(options);
  191. }
  192. break;
  193. case 'create':
  194. response = this._create(options);
  195. break;
  196. case 'update':
  197. response = this._update(options);
  198. break;
  199. case 'delete':
  200. response = this._destroy(options);
  201. break;
  202. }
  203. } catch (error) {
  204. errorInfo = error.message;
  205. }
  206.  
  207. if (response) {
  208. callback(null, response);
  209. } else if (errorInfo) {
  210. callback(errorInfo);
  211. } else {
  212. callback("Data not found in LocalStorage");
  213. }
  214. },
  215.  
  216. /**
  217. Generate a random GUID for our Models. This can be overriden if you have
  218. another method of generating different IDs.
  219. @method generateID
  220. @protected
  221. @param {String} pre Optional GUID prefix
  222. **/
  223. generateID: function (pre) {
  224. return Y.guid(pre + '_');
  225. },
  226.  
  227. // -- Protected Methods ----------------------------------------------------
  228.  
  229. /**
  230. Sync method correlating to the "read" operation, for a Model List
  231. @method _index
  232. @return {Object[]} Array of objects found for that root key
  233. @protected
  234. @since @VERSION@
  235. **/
  236. _index: function () {
  237. return LocalSync._data[this.root];
  238. },
  239.  
  240. /**
  241. Sync method correlating to the "read" operation, for a Model
  242. @method _show
  243. @return {Object} Object found for that root key and model ID
  244. @protected
  245. @since @VERSION@
  246. **/
  247. _show: function () {
  248. return LocalSync._idMap[this.root][this.get('id')] || null;
  249. },
  250. /**
  251. Sync method correlating to the "create" operation
  252. @method _show
  253. @return {Object} The new object created.
  254. @protected
  255. @since @VERSION@
  256. **/
  257. _create: function () {
  258. var hash = this.toJSON(),
  259. data = LocalSync._data[this.root],
  260. idMap = LocalSync._idMap[this.root];
  261. hash.id = this.generateID(this.root);
  262. data.push(hash);
  263. idMap[hash.id] = hash;
  264.  
  265. this._save();
  266. return hash;
  267. },
  268.  
  269. /**
  270. Sync method correlating to the "update" operation
  271.  
  272. @method _update
  273. @return {Object} The updated object.
  274. @protected
  275. @since @VERSION@
  276. **/
  277. _update: function () {
  278. var hash = Y.merge(this.toJSON());
  279. LocalSync._idMap[this.get('id')] = hash;
  280. this._save();
  281. return hash;
  282. },
  283.  
  284. /**
  285. Sync method correlating to the "delete" operation. Deletes the data
  286. from the in-memory object, and saves into localStorage if available.
  287. @method _destroy
  288. @return {Object} The deleted object.
  289. @protected
  290. @since @VERSION@
  291. **/
  292. _destroy: function () {
  293. delete LocalSync._idMap[this.get('id')];
  294. this._save();
  295. return this.toJSON();
  296. },
  297. /**
  298. Saves the current in-memory store into a localStorage key/value pair
  299. if localStorage is available; otherwise, does nothing.
  300. @method _save
  301. @protected
  302. @since @VERSION@
  303. **/
  304. _save: function () {
  305. if (LocalSync._hasLocalStorage) {
  306. this.storage && this.storage.setItem(
  307. this.root,
  308. Y.JSON.stringify(LocalSync._data[this.root])
  309. );
  310. }
  311. }
  312. };
  313.  
  314. // -- Namespace ---------------------------------------------------------------
  315.  
  316. Y.namespace('ModelSync').Local = LocalSync;
  317.