25'ten fazla konu seçemezsiniz Konular bir harf veya rakamla başlamalı, kısa çizgiler ('-') içerebilir ve en fazla 35 karakter uzunluğunda olabilir.
 
 
 
 
 
 

679 satır
24 KiB

  1. /*
  2. * JS Storage Plugin
  3. *
  4. * Copyright (c) 2019 Julien Maurel
  5. *
  6. * Licensed under the MIT license:
  7. * http://www.opensource.org/licenses/mit-license.php
  8. *
  9. * Project home:
  10. * https://github.com/julien-maurel/js-storage
  11. *
  12. * Version: 1.1.0
  13. */
  14. (function (factory) {
  15. var registeredInModuleLoader = false;
  16. if (typeof define === 'function' && define.amd) {
  17. define(['jquery', 'js-cookie/cookie-wrapper'], factory);
  18. registeredInModuleLoader = true;
  19. }
  20. if (typeof exports === 'object') {
  21. module.exports = factory();
  22. registeredInModuleLoader = true;
  23. }
  24. if (!registeredInModuleLoader) {
  25. var OldStorages = window.Storages;
  26. var api = window.Storages = factory();
  27. api.noConflict = function () {
  28. window.Storages = OldStorages;
  29. return api;
  30. };
  31. }
  32. }(function () {
  33. // Variables used by utilities functions (like isPlainObject...)
  34. var class2type = {};
  35. var toString = class2type.toString;
  36. var hasOwn = class2type.hasOwnProperty;
  37. var fnToString = hasOwn.toString;
  38. var ObjectFunctionString = fnToString.call(Object);
  39. var getProto = Object.getPrototypeOf;
  40. var apis = {};
  41. // Prefix to use with cookie fallback
  42. var cookie_local_prefix = "ls_";
  43. var cookie_session_prefix = "ss_";
  44. // Get items from a storage
  45. function _get() {
  46. var storage = this._type, l = arguments.length, s = window[storage], a = arguments, a0 = a[0], vi, ret, tmp, i, j;
  47. if (l < 1) {
  48. throw new Error('Minimum 1 argument must be given');
  49. } else if (Array.isArray(a0)) {
  50. // If second argument is an array, return an object with value of storage for each item in this array
  51. ret = {};
  52. for (i in a0) {
  53. if (a0.hasOwnProperty(i)) {
  54. vi = a0[i];
  55. try {
  56. ret[vi] = JSON.parse(s.getItem(vi));
  57. } catch (e) {
  58. ret[vi] = s.getItem(vi);
  59. }
  60. }
  61. }
  62. return ret;
  63. } else if (l == 1) {
  64. // If only 1 argument, return value directly
  65. try {
  66. return JSON.parse(s.getItem(a0));
  67. } catch (e) {
  68. return s.getItem(a0);
  69. }
  70. } else {
  71. // If more than 1 argument, parse storage to retrieve final value to return it
  72. // Get first level
  73. try {
  74. ret = JSON.parse(s.getItem(a0));
  75. if (!ret) {
  76. throw new ReferenceError(a0 + ' is not defined in this storage');
  77. }
  78. } catch (e) {
  79. throw new ReferenceError(a0 + ' is not defined in this storage');
  80. }
  81. // Parse next levels
  82. for (i = 1; i < l - 1; i++) {
  83. ret = ret[a[i]];
  84. if (ret === undefined) {
  85. throw new ReferenceError([].slice.call(a, 0, i + 1).join('.') + ' is not defined in this storage');
  86. }
  87. }
  88. // If last argument is an array, return an object with value for each item in this array
  89. // Else return value normally
  90. if (Array.isArray(a[i])) {
  91. tmp = ret;
  92. ret = {};
  93. for (j in a[i]) {
  94. if (a[i].hasOwnProperty(j)) {
  95. ret[a[i][j]] = tmp[a[i][j]];
  96. }
  97. }
  98. return ret;
  99. } else {
  100. return ret[a[i]];
  101. }
  102. }
  103. }
  104. // Set items of a storage
  105. function _set() {
  106. var storage = this._type, l = arguments.length, s = window[storage], a = arguments, a0 = a[0], a1 = a[1], vi, to_store = isNaN(a1) ? {} : [], type, tmp, i;
  107. if (l < 1 || !_isPlainObject(a0) && l < 2) {
  108. throw new Error('Minimum 2 arguments must be given or first parameter must be an object');
  109. } else if (_isPlainObject(a0)) {
  110. // If first argument is an object, set values of storage for each property of this object
  111. for (i in a0) {
  112. if (a0.hasOwnProperty(i)) {
  113. vi = a0[i];
  114. if (!_isPlainObject(vi) && !this.alwaysUseJson) {
  115. s.setItem(i, vi);
  116. } else {
  117. s.setItem(i, JSON.stringify(vi));
  118. }
  119. }
  120. }
  121. return a0;
  122. } else if (l == 2) {
  123. // If only 2 arguments, set value of storage directly
  124. if (typeof a1 === 'object' || this.alwaysUseJson) {
  125. s.setItem(a0, JSON.stringify(a1));
  126. } else {
  127. s.setItem(a0, a1);
  128. }
  129. return a1;
  130. } else {
  131. // If more than 3 arguments, parse storage to retrieve final node and set value
  132. // Get first level
  133. try {
  134. tmp = s.getItem(a0);
  135. if (tmp != null) {
  136. to_store = JSON.parse(tmp);
  137. }
  138. } catch (e) {
  139. }
  140. tmp = to_store;
  141. // Parse next levels and set value
  142. for (i = 1; i < l - 2; i++) {
  143. vi = a[i];
  144. type = isNaN(a[i + 1]) ? "object" : "array";
  145. if (!tmp[vi] || type == "object" && !_isPlainObject(tmp[vi]) || type == "array" && !Array.isArray(tmp[vi])) {
  146. if (type == "array") tmp[vi] = [];
  147. else tmp[vi] = {};
  148. }
  149. tmp = tmp[vi];
  150. }
  151. tmp[a[i]] = a[i + 1];
  152. s.setItem(a0, JSON.stringify(to_store));
  153. return to_store;
  154. }
  155. }
  156. // Remove items from a storage
  157. function _remove() {
  158. var storage = this._type, l = arguments.length, s = window[storage], a = arguments, a0 = a[0], to_store, tmp, i, j;
  159. if (l < 1) {
  160. throw new Error('Minimum 1 argument must be given');
  161. } else if (Array.isArray(a0)) {
  162. // If first argument is an array, remove values from storage for each item of this array
  163. for (i in a0) {
  164. if (a0.hasOwnProperty(i)) {
  165. s.removeItem(a0[i]);
  166. }
  167. }
  168. return true;
  169. } else if (l == 1) {
  170. // If only 2 arguments, remove value from storage directly
  171. s.removeItem(a0);
  172. return true;
  173. } else {
  174. // If more than 2 arguments, parse storage to retrieve final node and remove value
  175. // Get first level
  176. try {
  177. to_store = tmp = JSON.parse(s.getItem(a0));
  178. } catch (e) {
  179. throw new ReferenceError(a0 + ' is not defined in this storage');
  180. }
  181. // Parse next levels and remove value
  182. for (i = 1; i < l - 1; i++) {
  183. tmp = tmp[a[i]];
  184. if (tmp === undefined) {
  185. throw new ReferenceError([].slice.call(a, 1, i).join('.') + ' is not defined in this storage');
  186. }
  187. }
  188. // If last argument is an array,remove value for each item in this array
  189. // Else remove value normally
  190. if (Array.isArray(a[i])) {
  191. for (j in a[i]) {
  192. if (a[i].hasOwnProperty(j)) {
  193. delete tmp[a[i][j]];
  194. }
  195. }
  196. } else {
  197. delete tmp[a[i]];
  198. }
  199. s.setItem(a0, JSON.stringify(to_store));
  200. return true;
  201. }
  202. }
  203. // Remove all items from a storage
  204. function _removeAll(reinit_ns) {
  205. var keys = _keys.call(this), i;
  206. for (i in keys) {
  207. if (keys.hasOwnProperty(i)) {
  208. _remove.call(this, keys[i]);
  209. }
  210. }
  211. // Reinitialize all namespace storages
  212. if (reinit_ns) {
  213. for (i in apis.namespaceStorages) {
  214. if (apis.namespaceStorages.hasOwnProperty(i)) {
  215. _createNamespace(i);
  216. }
  217. }
  218. }
  219. }
  220. // Check if items of a storage are empty
  221. function _isEmpty() {
  222. var l = arguments.length, a = arguments, a0 = a[0], i;
  223. if (l == 0) {
  224. // If no argument, test if storage is empty
  225. return (_keys.call(this).length == 0);
  226. } else if (Array.isArray(a0)) {
  227. // If first argument is an array, test each item of this array and return true only if all items are empty
  228. for (i = 0; i < a0.length; i++) {
  229. if (!_isEmpty.call(this, a0[i])) {
  230. return false;
  231. }
  232. }
  233. return true;
  234. } else {
  235. // If at least 1 argument, try to get value and test it
  236. try {
  237. var v = _get.apply(this, arguments);
  238. // Convert result to an object (if last argument is an array, _get return already an object) and test each item
  239. if (!Array.isArray(a[l - 1])) {
  240. v = {'totest': v};
  241. }
  242. for (i in v) {
  243. if (v.hasOwnProperty(i) && !(
  244. (_isPlainObject(v[i]) && _isEmptyObject(v[i])) ||
  245. (Array.isArray(v[i]) && !v[i].length) ||
  246. (typeof v[i] !== 'boolean' && !v[i])
  247. )) {
  248. return false;
  249. }
  250. }
  251. return true;
  252. } catch (e) {
  253. return true;
  254. }
  255. }
  256. }
  257. // Check if items of a storage exist
  258. function _isSet() {
  259. var l = arguments.length, a = arguments, a0 = a[0], i;
  260. if (l < 1) {
  261. throw new Error('Minimum 1 argument must be given');
  262. }
  263. if (Array.isArray(a0)) {
  264. // If first argument is an array, test each item of this array and return true only if all items exist
  265. for (i = 0; i < a0.length; i++) {
  266. if (!_isSet.call(this, a0[i])) {
  267. return false;
  268. }
  269. }
  270. return true;
  271. } else {
  272. // For other case, try to get value and test it
  273. try {
  274. var v = _get.apply(this, arguments);
  275. // Convert result to an object (if last argument is an array, _get return already an object) and test each item
  276. if (!Array.isArray(a[l - 1])) {
  277. v = {'totest': v};
  278. }
  279. for (i in v) {
  280. if (v.hasOwnProperty(i) && !(v[i] !== undefined && v[i] !== null)) {
  281. return false;
  282. }
  283. }
  284. return true;
  285. } catch (e) {
  286. return false;
  287. }
  288. }
  289. }
  290. // Get keys of a storage or of an item of the storage
  291. function _keys() {
  292. var storage = this._type, l = arguments.length, s = window[storage], keys = [], o = {};
  293. // If at least 1 argument, get value from storage to retrieve keys
  294. // Else, use storage to retrieve keys
  295. if (l > 0) {
  296. o = _get.apply(this, arguments);
  297. } else {
  298. o = s;
  299. }
  300. if (o && o._cookie) {
  301. // If storage is a cookie, use js-cookie to retrieve keys
  302. var cookies = Cookies.get();
  303. for (var key in cookies) {
  304. if (cookies.hasOwnProperty(key) && key != '') {
  305. keys.push(key.replace(o._prefix, ''));
  306. }
  307. }
  308. } else {
  309. for (var i in o) {
  310. if (o.hasOwnProperty(i)) {
  311. keys.push(i);
  312. }
  313. }
  314. }
  315. return keys;
  316. }
  317. // Create new namespace storage
  318. function _createNamespace(name) {
  319. if (!name || typeof name != "string") {
  320. throw new Error('First parameter must be a string');
  321. }
  322. if (storage_available) {
  323. if (!window.localStorage.getItem(name)) {
  324. window.localStorage.setItem(name, '{}');
  325. }
  326. if (!window.sessionStorage.getItem(name)) {
  327. window.sessionStorage.setItem(name, '{}');
  328. }
  329. } else {
  330. if (!window.localCookieStorage.getItem(name)) {
  331. window.localCookieStorage.setItem(name, '{}');
  332. }
  333. if (!window.sessionCookieStorage.getItem(name)) {
  334. window.sessionCookieStorage.setItem(name, '{}');
  335. }
  336. }
  337. var ns = {
  338. localStorage: _extend({}, apis.localStorage, {_ns: name}),
  339. sessionStorage: _extend({}, apis.sessionStorage, {_ns: name})
  340. };
  341. if (cookies_available) {
  342. if (!window.cookieStorage.getItem(name)) {
  343. window.cookieStorage.setItem(name, '{}');
  344. }
  345. ns.cookieStorage = _extend({}, apis.cookieStorage, {_ns: name});
  346. }
  347. apis.namespaceStorages[name] = ns;
  348. return ns;
  349. }
  350. // Test if storage is natively available on browser
  351. function _testStorage(name) {
  352. var foo = 'jsapi';
  353. try {
  354. if (!window[name]) {
  355. return false;
  356. }
  357. window[name].setItem(foo, foo);
  358. window[name].removeItem(foo);
  359. return true;
  360. } catch (e) {
  361. return false;
  362. }
  363. }
  364. // Test if a variable is a plain object (from jQuery)
  365. function _isPlainObject(obj) {
  366. var proto, Ctor;
  367. // Detect obvious negatives
  368. // Use toString instead of jQuery.type to catch host objects
  369. if (!obj || toString.call(obj) !== "[object Object]") {
  370. return false;
  371. }
  372. proto = getProto(obj);
  373. // Objects with no prototype (e.g., `Object.create( null )`) are plain
  374. if (!proto) {
  375. return true;
  376. }
  377. // Objects with prototype are plain iff they were constructed by a global Object function
  378. Ctor = hasOwn.call(proto, "constructor") && proto.constructor;
  379. return typeof Ctor === "function" && fnToString.call(Ctor) === ObjectFunctionString;
  380. }
  381. // Test if a variable is an empty object (from jQuery)
  382. function _isEmptyObject(obj) {
  383. var name;
  384. for (name in obj) {
  385. return false;
  386. }
  387. return true;
  388. }
  389. // Merge objects
  390. function _extend() {
  391. var i = 1;
  392. var result = arguments[0];
  393. for (; i < arguments.length; i++) {
  394. var attributes = arguments[i];
  395. for (var key in attributes) {
  396. if (attributes.hasOwnProperty(key)) {
  397. result[key] = attributes[key];
  398. }
  399. }
  400. }
  401. return result;
  402. }
  403. // Check if storages are natively available on browser and check is js-cookie is present
  404. var storage_available = _testStorage('localStorage');
  405. var cookies_available = typeof Cookies !== 'undefined';
  406. // Namespace object
  407. var storage = {
  408. _type: '',
  409. _ns: '',
  410. _callMethod: function (f, a) {
  411. a = Array.prototype.slice.call(a);
  412. var p = [], a0 = a[0];
  413. if (this._ns) {
  414. p.push(this._ns);
  415. }
  416. if (typeof a0 === 'string' && a0.indexOf('.') !== -1) {
  417. a.shift();
  418. [].unshift.apply(a, a0.split('.'));
  419. }
  420. [].push.apply(p, a);
  421. return f.apply(this, p);
  422. },
  423. // Define if plugin always use JSON to store values (even to store simple values like string, int...) or not
  424. alwaysUseJson: false,
  425. // Get items. If no parameters and storage have a namespace, return all namespace
  426. get: function () {
  427. if (!storage_available && !cookies_available){
  428. return null;
  429. }
  430. return this._callMethod(_get, arguments);
  431. },
  432. // Set items
  433. set: function () {
  434. var l = arguments.length, a = arguments, a0 = a[0];
  435. if (l < 1 || !_isPlainObject(a0) && l < 2) {
  436. throw new Error('Minimum 2 arguments must be given or first parameter must be an object');
  437. }
  438. if (!storage_available && !cookies_available){
  439. return null;
  440. }
  441. // If first argument is an object and storage is a namespace storage, set values individually
  442. if (_isPlainObject(a0) && this._ns) {
  443. for (var i in a0) {
  444. if (a0.hasOwnProperty(i)) {
  445. this._callMethod(_set, [i, a0[i]]);
  446. }
  447. }
  448. return a0;
  449. } else {
  450. var r = this._callMethod(_set, a);
  451. if (this._ns) {
  452. return r[a0.split('.')[0]];
  453. } else {
  454. return r;
  455. }
  456. }
  457. },
  458. // Delete items
  459. remove: function () {
  460. if (arguments.length < 1) {
  461. throw new Error('Minimum 1 argument must be given');
  462. }
  463. if (!storage_available && !cookies_available){
  464. return null;
  465. }
  466. return this._callMethod(_remove, arguments);
  467. },
  468. // Delete all items
  469. removeAll: function (reinit_ns) {
  470. if (!storage_available && !cookies_available){
  471. return null;
  472. }
  473. if (this._ns) {
  474. this._callMethod(_set, [{}]);
  475. return true;
  476. } else {
  477. return this._callMethod(_removeAll, [reinit_ns]);
  478. }
  479. },
  480. // Items empty
  481. isEmpty: function () {
  482. if (!storage_available && !cookies_available){
  483. return null;
  484. }
  485. return this._callMethod(_isEmpty, arguments);
  486. },
  487. // Items exists
  488. isSet: function () {
  489. if (arguments.length < 1) {
  490. throw new Error('Minimum 1 argument must be given');
  491. }
  492. if (!storage_available && !cookies_available){
  493. return null;
  494. }
  495. return this._callMethod(_isSet, arguments);
  496. },
  497. // Get keys of items
  498. keys: function () {
  499. if (!storage_available && !cookies_available){
  500. return null;
  501. }
  502. return this._callMethod(_keys, arguments);
  503. }
  504. };
  505. // Use js-cookie for compatibility with old browsers and give access to cookieStorage
  506. if (cookies_available) {
  507. // sessionStorage is valid for one window/tab. To simulate that with cookie, we set a name for the window and use it for the name of the cookie
  508. if (!window.name) {
  509. window.name = Math.floor(Math.random() * 100000000);
  510. }
  511. var cookie_storage = {
  512. _cookie: true,
  513. _prefix: '',
  514. _expires: null,
  515. _path: null,
  516. _domain: null,
  517. _secure: false,
  518. setItem: function (n, v) {
  519. Cookies.set(this._prefix + n, v, {expires: this._expires, path: this._path, domain: this._domain, secure: this._secure});
  520. },
  521. getItem: function (n) {
  522. return Cookies.get(this._prefix + n);
  523. },
  524. removeItem: function (n) {
  525. return Cookies.remove(this._prefix + n, {path: this._path});
  526. },
  527. clear: function () {
  528. var cookies = Cookies.get();
  529. for (var key in cookies) {
  530. if (cookies.hasOwnProperty(key) && key != '') {
  531. if (!this._prefix && key.indexOf(cookie_local_prefix) === -1 && key.indexOf(cookie_session_prefix) === -1 || this._prefix && key.indexOf(this._prefix) === 0) {
  532. Cookies.remove(key);
  533. }
  534. }
  535. }
  536. },
  537. setExpires: function (e) {
  538. this._expires = e;
  539. return this;
  540. },
  541. setPath: function (p) {
  542. this._path = p;
  543. return this;
  544. },
  545. setDomain: function (d) {
  546. this._domain = d;
  547. return this;
  548. },
  549. setSecure: function (s) {
  550. this._secure = s;
  551. return this;
  552. },
  553. setConf: function (c) {
  554. if (c.path) {
  555. this._path = c.path;
  556. }
  557. if (c.domain) {
  558. this._domain = c.domain;
  559. }
  560. if (c.secure) {
  561. this._secure = c.secure;
  562. }
  563. if (c.expires) {
  564. this._expires = c.expires;
  565. }
  566. return this;
  567. },
  568. setDefaultConf: function () {
  569. this._path = this._domain = this._expires = null;
  570. this._secure = false;
  571. }
  572. };
  573. if (!storage_available) {
  574. window.localCookieStorage = _extend({}, cookie_storage, {
  575. _prefix: cookie_local_prefix,
  576. _expires: 365 * 10,
  577. _secure: true
  578. });
  579. window.sessionCookieStorage = _extend({}, cookie_storage, {
  580. _prefix: cookie_session_prefix + window.name + '_',
  581. _secure: true
  582. });
  583. }
  584. window.cookieStorage = _extend({}, cookie_storage);
  585. // cookieStorage API
  586. apis.cookieStorage = _extend({}, storage, {
  587. _type: 'cookieStorage',
  588. setExpires: function (e) {
  589. window.cookieStorage.setExpires(e);
  590. return this;
  591. },
  592. setPath: function (p) {
  593. window.cookieStorage.setPath(p);
  594. return this;
  595. },
  596. setDomain: function (d) {
  597. window.cookieStorage.setDomain(d);
  598. return this;
  599. },
  600. setSecure: function (s) {
  601. window.cookieStorage.setSecure(s);
  602. return this;
  603. },
  604. setConf: function (c) {
  605. window.cookieStorage.setConf(c);
  606. return this;
  607. },
  608. setDefaultConf: function () {
  609. window.cookieStorage.setDefaultConf();
  610. return this;
  611. }
  612. });
  613. }
  614. // Get a new API on a namespace
  615. apis.initNamespaceStorage = function (ns) {
  616. return _createNamespace(ns);
  617. };
  618. if (storage_available) {
  619. // localStorage API
  620. apis.localStorage = _extend({}, storage, {_type: 'localStorage'});
  621. // sessionStorage API
  622. apis.sessionStorage = _extend({}, storage, {_type: 'sessionStorage'});
  623. } else {
  624. // localStorage API
  625. apis.localStorage = _extend({}, storage, {_type: 'localCookieStorage'});
  626. // sessionStorage API
  627. apis.sessionStorage = _extend({}, storage, {_type: 'sessionCookieStorage'});
  628. }
  629. // List of all namespace storage
  630. apis.namespaceStorages = {};
  631. // Remove all items in all storages
  632. apis.removeAllStorages = function (reinit_ns) {
  633. apis.localStorage.removeAll(reinit_ns);
  634. apis.sessionStorage.removeAll(reinit_ns);
  635. if (apis.cookieStorage) {
  636. apis.cookieStorage.removeAll(reinit_ns);
  637. }
  638. if (!reinit_ns) {
  639. apis.namespaceStorages = {};
  640. }
  641. };
  642. // About alwaysUseJson
  643. // By default, all values are string on html storages and the plugin don't use json to store simple values (strings, int, float...)
  644. // So by default, if you do storage.setItem('test',2), value in storage will be "2", not 2
  645. // If you set this property to true, all values set with the plugin will be stored as json to have typed values in any cases
  646. apis.alwaysUseJsonInStorage = function (value) {
  647. storage.alwaysUseJson = value;
  648. apis.localStorage.alwaysUseJson = value;
  649. apis.sessionStorage.alwaysUseJson = value;
  650. if (apis.cookieStorage) {
  651. apis.cookieStorage.alwaysUseJson = value;
  652. }
  653. };
  654. return apis;
  655. }));