store2.js 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283
  1. /*! store2 - v2.14.3 - 2024-02-14
  2. * Copyright (c) 2024 Nathan Bubna; Licensed MIT */
  3. ;(function(window, define) {
  4. var _ = {
  5. version: "2.14.3",
  6. areas: {},
  7. apis: {},
  8. nsdelim: '.',
  9. // utilities
  10. inherit: function(api, o) {
  11. for (var p in api) {
  12. if (!o.hasOwnProperty(p)) {
  13. Object.defineProperty(o, p, Object.getOwnPropertyDescriptor(api, p));
  14. }
  15. }
  16. return o;
  17. },
  18. stringify: function(d, fn) {
  19. return d === undefined || typeof d === "function" ? d+'' : JSON.stringify(d,fn||_.replace);
  20. },
  21. parse: function(s, fn) {
  22. // if it doesn't parse, return as is
  23. try{ return JSON.parse(s,fn||_.revive); }catch(e){ return s; }
  24. },
  25. // extension hooks
  26. fn: function(name, fn) {
  27. _.storeAPI[name] = fn;
  28. for (var api in _.apis) {
  29. _.apis[api][name] = fn;
  30. }
  31. },
  32. get: function(area, key){ return area.getItem(key); },
  33. set: function(area, key, string){ area.setItem(key, string); },
  34. remove: function(area, key){ area.removeItem(key); },
  35. key: function(area, i){ return area.key(i); },
  36. length: function(area){ return area.length; },
  37. clear: function(area){ area.clear(); },
  38. // core functions
  39. Store: function(id, area, namespace) {
  40. var store = _.inherit(_.storeAPI, function(key, data, overwrite) {
  41. if (arguments.length === 0){ return store.getAll(); }
  42. if (typeof data === "function"){ return store.transact(key, data, overwrite); }// fn=data, alt=overwrite
  43. if (data !== undefined){ return store.set(key, data, overwrite); }
  44. if (typeof key === "string" || typeof key === "number"){ return store.get(key); }
  45. if (typeof key === "function"){ return store.each(key); }
  46. if (!key){ return store.clear(); }
  47. return store.setAll(key, data);// overwrite=data, data=key
  48. });
  49. store._id = id;
  50. try {
  51. var testKey = '__store2_test';
  52. area.setItem(testKey, 'ok');
  53. store._area = area;
  54. area.removeItem(testKey);
  55. } catch (e) {
  56. store._area = _.storage('fake');
  57. }
  58. store._ns = namespace || '';
  59. if (!_.areas[id]) {
  60. _.areas[id] = store._area;
  61. }
  62. if (!_.apis[store._ns+store._id]) {
  63. _.apis[store._ns+store._id] = store;
  64. }
  65. return store;
  66. },
  67. storeAPI: {
  68. // admin functions
  69. area: function(id, area) {
  70. var store = this[id];
  71. if (!store || !store.area) {
  72. store = _.Store(id, area, this._ns);//new area-specific api in this namespace
  73. if (!this[id]){ this[id] = store; }
  74. }
  75. return store;
  76. },
  77. namespace: function(namespace, singleArea, delim) {
  78. delim = delim || this._delim || _.nsdelim;
  79. if (!namespace){
  80. return this._ns ? this._ns.substring(0,this._ns.length-delim.length) : '';
  81. }
  82. var ns = namespace, store = this[ns];
  83. if (!store || !store.namespace) {
  84. store = _.Store(this._id, this._area, this._ns+ns+delim);//new namespaced api
  85. store._delim = delim;
  86. if (!this[ns]){ this[ns] = store; }
  87. if (!singleArea) {
  88. for (var name in _.areas) {
  89. store.area(name, _.areas[name]);
  90. }
  91. }
  92. }
  93. return store;
  94. },
  95. isFake: function(force) {
  96. if (force) {
  97. this._real = this._area;
  98. this._area = _.storage('fake');
  99. } else if (force === false) {
  100. this._area = this._real || this._area;
  101. }
  102. return this._area.name === 'fake';
  103. },
  104. toString: function() {
  105. return 'store'+(this._ns?'.'+this.namespace():'')+'['+this._id+']';
  106. },
  107. // storage functions
  108. has: function(key) {
  109. if (this._area.has) {
  110. return this._area.has(this._in(key));//extension hook
  111. }
  112. return !!(this._in(key) in this._area);
  113. },
  114. size: function(){ return this.keys().length; },
  115. each: function(fn, fill) {// fill is used by keys(fillList) and getAll(fillList))
  116. for (var i=0, m=_.length(this._area); i<m; i++) {
  117. var key = this._out(_.key(this._area, i));
  118. if (key !== undefined) {
  119. if (fn.call(this, key, this.get(key), fill) === false) {
  120. break;
  121. }
  122. }
  123. if (m > _.length(this._area)) { m--; i--; }// in case of removeItem
  124. }
  125. return fill || this;
  126. },
  127. keys: function(fillList) {
  128. return this.each(function(k, v, list){ list.push(k); }, fillList || []);
  129. },
  130. get: function(key, alt) {
  131. var s = _.get(this._area, this._in(key)),
  132. fn;
  133. if (typeof alt === "function") {
  134. fn = alt;
  135. alt = null;
  136. }
  137. return s !== null ? _.parse(s, fn) :
  138. alt != null ? alt : s;
  139. },
  140. getAll: function(fillObj) {
  141. return this.each(function(k, v, all){ all[k] = v; }, fillObj || {});
  142. },
  143. transact: function(key, fn, alt) {
  144. var val = this.get(key, alt),
  145. ret = fn(val);
  146. this.set(key, ret === undefined ? val : ret);
  147. return this;
  148. },
  149. set: function(key, data, overwrite) {
  150. var d = this.get(key),
  151. replacer;
  152. if (d != null && overwrite === false) {
  153. return data;
  154. }
  155. if (typeof overwrite === "function") {
  156. replacer = overwrite;
  157. overwrite = undefined;
  158. }
  159. return _.set(this._area, this._in(key), _.stringify(data, replacer), overwrite) || d;
  160. },
  161. setAll: function(data, overwrite) {
  162. var changed, val;
  163. for (var key in data) {
  164. val = data[key];
  165. if (this.set(key, val, overwrite) !== val) {
  166. changed = true;
  167. }
  168. }
  169. return changed;
  170. },
  171. add: function(key, data, replacer) {
  172. var d = this.get(key);
  173. if (d instanceof Array) {
  174. data = d.concat(data);
  175. } else if (d !== null) {
  176. var type = typeof d;
  177. if (type === typeof data && type === 'object') {
  178. for (var k in data) {
  179. d[k] = data[k];
  180. }
  181. data = d;
  182. } else {
  183. data = d + data;
  184. }
  185. }
  186. _.set(this._area, this._in(key), _.stringify(data, replacer));
  187. return data;
  188. },
  189. remove: function(key, alt) {
  190. var d = this.get(key, alt);
  191. _.remove(this._area, this._in(key));
  192. return d;
  193. },
  194. clear: function() {
  195. if (!this._ns) {
  196. _.clear(this._area);
  197. } else {
  198. this.each(function(k){ _.remove(this._area, this._in(k)); }, 1);
  199. }
  200. return this;
  201. },
  202. clearAll: function() {
  203. var area = this._area;
  204. for (var id in _.areas) {
  205. if (_.areas.hasOwnProperty(id)) {
  206. this._area = _.areas[id];
  207. this.clear();
  208. }
  209. }
  210. this._area = area;
  211. return this;
  212. },
  213. // internal use functions
  214. _in: function(k) {
  215. if (typeof k !== "string"){ k = _.stringify(k); }
  216. return this._ns ? this._ns + k : k;
  217. },
  218. _out: function(k) {
  219. return this._ns ?
  220. k && k.indexOf(this._ns) === 0 ?
  221. k.substring(this._ns.length) :
  222. undefined : // so each() knows to skip it
  223. k;
  224. }
  225. },// end _.storeAPI
  226. storage: function(name) {
  227. return _.inherit(_.storageAPI, { items: {}, name: name });
  228. },
  229. storageAPI: {
  230. length: 0,
  231. has: function(k){ return this.items.hasOwnProperty(k); },
  232. key: function(i) {
  233. var c = 0;
  234. for (var k in this.items){
  235. if (this.has(k) && i === c++) {
  236. return k;
  237. }
  238. }
  239. },
  240. setItem: function(k, v) {
  241. if (!this.has(k)) {
  242. this.length++;
  243. }
  244. this.items[k] = v;
  245. },
  246. removeItem: function(k) {
  247. if (this.has(k)) {
  248. delete this.items[k];
  249. this.length--;
  250. }
  251. },
  252. getItem: function(k){ return this.has(k) ? this.items[k] : null; },
  253. clear: function(){ for (var k in this.items){ this.removeItem(k); } }
  254. }// end _.storageAPI
  255. };
  256. var store =
  257. // safely set this up (throws error in IE10/32bit mode for local files)
  258. _.Store("local", (function(){try{ return localStorage; }catch(e){}})());
  259. store.local = store;// for completeness
  260. store._ = _;// for extenders and debuggers...
  261. // safely setup store.session (throws exception in FF for file:/// urls)
  262. store.area("session", (function(){try{ return sessionStorage; }catch(e){}})());
  263. store.area("page", _.storage("page"));
  264. if (typeof define === 'function' && define.amd !== undefined) {
  265. define('store2', [], function () {
  266. return store;
  267. });
  268. } else if (typeof module !== 'undefined' && module.exports) {
  269. module.exports = store;
  270. } else {
  271. // expose the primary store fn to the global object and save conflicts
  272. if (window.store){ _.conflict = window.store; }
  273. window.store = store;
  274. }
  275. })(this, this && this.define);