/* * ----------------------------- JSTORAGE ------------------------------------- * Simple local storage wrapper to save data on the browser side, supporting * all major browsers - IE6+, Firefox2+, Safari4+, Chrome4+ and Opera 10.5+ * * Author: Andris Reinman, andris.reinman@gmail.com * Project homepage: www.jstorage.info * * Licensed under Unlicense: * * This is free and unencumbered software released into the public domain. * * Anyone is free to copy, modify, publish, use, compile, sell, or * distribute this software, either in source code form or as a compiled * binary, for any purpose, commercial or non-commercial, and by any * means. * * In jurisdictions that recognize copyright laws, the author or authors * of this software dedicate any and all copyright interest in the * software to the public domain. We make this dedication for the benefit * of the public at large and to the detriment of our heirs and * successors. We intend this dedication to be an overt act of * relinquishment in perpetuity of all present and future rights to this * software under copyright law. * * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. * IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. * * For more information, please refer to <http://unlicense.org/> */ /* global ActiveXObject: false *//* jshint browser: true */ (function () { 'use strict'; var /* jStorage version */ JSTORAGE_VERSION = '0.4.12', /* detect a dollar object or create one if not found */ $ = window.jQuery || window.$ || (window.$ = {}), /* check for a JSON handling support */ JSON = { parse: window.JSON && (window.JSON.parse || window.JSON.decode) || String.prototype.evalJSON && function (str) { return String(str).evalJSON(); } || $.parseJSON || $.evalJSON, stringify: Object.toJSON || window.JSON && (window.JSON.stringify || window.JSON.encode) || $.toJSON }; // Break if no JSON support was found if (typeof JSON.parse !== 'function' || typeof JSON.stringify !== 'function') { throw new Error('No JSON support found, include //cdnjs.cloudflare.com/ajax/libs/json2/20110223/json2.js to page'); } var /* This is the object, that holds the cached values */ _storage = { __jstorage_meta: { CRC32: {} } }, /* Actual browser storage (localStorage or globalStorage['domain']) */ _storage_service = { jStorage: '{}' }, /* DOM element for older IE versions, holds userData behavior */ _storage_elm = null, /* How much space does the storage take */ _storage_size = 0, /* which backend is currently used */ _backend = false, /* onchange observers */ _observers = {}, /* timeout to wait after onchange event */ _observer_timeout = false, /* last update time */ _observer_update = 0, /* pubsub observers */ _pubsub_observers = {}, /* skip published items older than current timestamp */ _pubsub_last = +new Date(), /* Next check for TTL */ _ttl_timeout, /** * XML encoding and decoding as XML nodes can't be JSON'ized * XML nodes are encoded and decoded if the node is the value to be saved * but not if it's as a property of another object * Eg. - * $.jStorage.set('key', xmlNode); // IS OK * $.jStorage.set('key', {xml: xmlNode}); // NOT OK */ _XMLService = { /** * Validates a XML node to be XML * based on jQuery.isXML function */ isXML: function (elm) { var documentElement = (elm ? elm.ownerDocument || elm : 0).documentElement; return documentElement ? documentElement.nodeName !== 'HTML' : false; }, /** * Encodes a XML node to string * based on http://www.mercurytide.co.uk/news/article/issues-when-working-ajax/ */ encode: function (xmlNode) { if (!this.isXML(xmlNode)) { return false; } try { // Mozilla, Webkit, Opera return new XMLSerializer().serializeToString(xmlNode); } catch (E1) { try { // IE return xmlNode.xml; } catch (E2) { } } return false; }, /** * Decodes a XML node from string * loosely based on http://outwestmedia.com/jquery-plugins/xmldom/ */ decode: function (xmlString) { var dom_parser = ('DOMParser' in window && (new DOMParser()).parseFromString) || (window.ActiveXObject && function (_xmlString) { var xml_doc = new ActiveXObject('Microsoft.XMLDOM'); xml_doc.async = 'false'; xml_doc.loadXML(_xmlString); return xml_doc; }), resultXML; if (!dom_parser) { return false; } resultXML = dom_parser.call('DOMParser' in window && (new DOMParser()) || window, xmlString, 'text/xml'); return this.isXML(resultXML) ? resultXML : false; } }; ////////////////////////// PRIVATE METHODS //////////////////////// /** * Initialization function. Detects if the browser supports DOM Storage * or userData behavior and behaves accordingly. */ function _init() { /* Check if browser supports localStorage */ var localStorageReallyWorks = false; if ('localStorage' in window) { try { window.localStorage.setItem('_tmptest', 'tmpval'); localStorageReallyWorks = true; window.localStorage.removeItem('_tmptest'); } catch (BogusQuotaExceededErrorOnIos5) { // Thanks be to iOS5 Private Browsing mode which throws // QUOTA_EXCEEDED_ERRROR DOM Exception 22. } } if (localStorageReallyWorks) { try { if (window.localStorage) { _storage_service = window.localStorage; _backend = 'localStorage'; _observer_update = _storage_service.jStorage_update; } } catch (E3) { /* Firefox fails when touching localStorage and cookies are disabled */ } } /* Check if browser supports globalStorage */ else if ('globalStorage' in window) { try { if (window.globalStorage) { if (window.location.hostname == 'localhost') { _storage_service = window.globalStorage['localhost.localdomain']; } else { _storage_service = window.globalStorage[window.location.hostname]; } _backend = 'globalStorage'; _observer_update = _storage_service.jStorage_update; } } catch (E4) { /* Firefox fails when touching localStorage and cookies are disabled */ } } /* Check if browser supports userData behavior */ else { _storage_elm = document.createElement('link'); if (_storage_elm.addBehavior) { /* Use a DOM element to act as userData storage */ _storage_elm.style.behavior = 'url(#default#userData)'; /* userData element needs to be inserted into the DOM! */ document.getElementsByTagName('head')[0].appendChild(_storage_elm); try { _storage_elm.load('jStorage'); } catch (E) { // try to reset cache _storage_elm.setAttribute('jStorage', '{}'); _storage_elm.save('jStorage'); _storage_elm.load('jStorage'); } var data = '{}'; try { data = _storage_elm.getAttribute('jStorage'); } catch (E5) { } try { _observer_update = _storage_elm.getAttribute('jStorage_update'); } catch (E6) { } _storage_service.jStorage = data; _backend = 'userDataBehavior'; } else { _storage_elm = null; return; } } // Load data from storage _load_storage(); // remove dead keys _handleTTL(); // start listening for changes _setupObserver(); // initialize publish-subscribe service _handlePubSub(); // handle cached navigation if ('addEventListener' in window) { window.addEventListener('pageshow', function (event) { if (event.persisted) { _storageObserver(); } }, false); } } /** * Reload data from storage when needed */ function _reloadData() { var data = '{}'; if (_backend == 'userDataBehavior') { _storage_elm.load('jStorage'); try { data = _storage_elm.getAttribute('jStorage'); } catch (E5) { } try { _observer_update = _storage_elm.getAttribute('jStorage_update'); } catch (E6) { } _storage_service.jStorage = data; } _load_storage(); // remove dead keys _handleTTL(); _handlePubSub(); } /** * Sets up a storage change observer */ function _setupObserver() { if (_backend == 'localStorage' || _backend == 'globalStorage') { if ('addEventListener' in window) { window.addEventListener('storage', _storageObserver, false); } else { document.attachEvent('onstorage', _storageObserver); } } else if (_backend == 'userDataBehavior') { setInterval(_storageObserver, 1000); } } /** * Fired on any kind of data change, needs to check if anything has * really been changed */ function _storageObserver() { var updateTime; // cumulate change notifications with timeout clearTimeout(_observer_timeout); _observer_timeout = setTimeout(function () { if (_backend == 'localStorage' || _backend == 'globalStorage') { updateTime = _storage_service.jStorage_update; } else if (_backend == 'userDataBehavior') { _storage_elm.load('jStorage'); try { updateTime = _storage_elm.getAttribute('jStorage_update'); } catch (E5) { } } if (updateTime && updateTime != _observer_update) { _observer_update = updateTime; _checkUpdatedKeys(); } }, 25); } /** * Reloads the data and checks if any keys are changed */ function _checkUpdatedKeys() { var oldCrc32List = JSON.parse(JSON.stringify(_storage.__jstorage_meta.CRC32)), newCrc32List; _reloadData(); newCrc32List = JSON.parse(JSON.stringify(_storage.__jstorage_meta.CRC32)); var key, updated = [], removed = []; for (key in oldCrc32List) { if (oldCrc32List.hasOwnProperty(key)) { if (!newCrc32List[key]) { removed.push(key); continue; } if (oldCrc32List[key] != newCrc32List[key] && String(oldCrc32List[key]).substr(0, 2) == '2.') { updated.push(key); } } } for (key in newCrc32List) { if (newCrc32List.hasOwnProperty(key)) { if (!oldCrc32List[key]) { updated.push(key); } } } _fireObservers(updated, 'updated'); _fireObservers(removed, 'deleted'); } /** * Fires observers for updated keys * * @param {Array|String} keys Array of key names or a key * @param {String} action What happened with the value (updated, deleted, flushed) */ function _fireObservers(keys, action) { keys = [].concat(keys || []); var i, j, len, jlen; if (action == 'flushed') { keys = []; for (var key in _observers) { if (_observers.hasOwnProperty(key)) { keys.push(key); } } action = 'deleted'; } for (i = 0, len = keys.length; i < len; i++) { if (_observers[keys[i]]) { for (j = 0, jlen = _observers[keys[i]].length; j < jlen; j++) { _observers[keys[i]][j](keys[i], action); } } if (_observers['*']) { for (j = 0, jlen = _observers['*'].length; j < jlen; j++) { _observers['*'][j](keys[i], action); } } } } /** * Publishes key change to listeners */ function _publishChange() { var updateTime = (+new Date()).toString(); if (_backend == 'localStorage' || _backend == 'globalStorage') { try { _storage_service.jStorage_update = updateTime; } catch (E8) { // safari private mode has been enabled after the jStorage initialization _backend = false; } } else if (_backend == 'userDataBehavior') { _storage_elm.setAttribute('jStorage_update', updateTime); _storage_elm.save('jStorage'); } _storageObserver(); } /** * Loads the data from the storage based on the supported mechanism */ function _load_storage() { /* if jStorage string is retrieved, then decode it */ if (_storage_service.jStorage) { try { _storage = JSON.parse(String(_storage_service.jStorage)); } catch (E6) { _storage_service.jStorage = '{}'; } } else { _storage_service.jStorage = '{}'; } _storage_size = _storage_service.jStorage ? String(_storage_service.jStorage).length : 0; if (!_storage.__jstorage_meta) { _storage.__jstorage_meta = {}; } if (!_storage.__jstorage_meta.CRC32) { _storage.__jstorage_meta.CRC32 = {}; } } /** * This functions provides the 'save' mechanism to store the jStorage object */ function _save() { _dropOldEvents(); // remove expired events try { _storage_service.jStorage = JSON.stringify(_storage); // If userData is used as the storage engine, additional if (_storage_elm) { _storage_elm.setAttribute('jStorage', _storage_service.jStorage); _storage_elm.save('jStorage'); } _storage_size = _storage_service.jStorage ? String(_storage_service.jStorage).length : 0; } catch (E7) { /* probably cache is full, nothing is saved this way*/ } } /** * Function checks if a key is set and is string or numberic * * @param {String} key Key name */ function _checkKey(key) { if (typeof key != 'string' && typeof key != 'number') { throw new TypeError('Key name must be string or numeric'); } if (key == '__jstorage_meta') { throw new TypeError('Reserved key name'); } return true; } /** * Removes expired keys */ function _handleTTL() { var curtime, i, TTL, CRC32, nextExpire = Infinity, changed = false, deleted = []; clearTimeout(_ttl_timeout); if (!_storage.__jstorage_meta || typeof _storage.__jstorage_meta.TTL != 'object') { // nothing to do here return; }Lines 1–500 of 996. Load more
/* * ----------------------------- JSTORAGE ------------------------------------- * Simple local storage wrapper to save data on the browser side, supporting * all major browsers - IE6+, Firefox2+, Safari4+, Chrome4+ and Opera 10.5+ * * Author: Andris Reinman, andris.reinman@gmail.com * Project homepage: www.jstorage.info * * Licensed under Unlicense: * * This is free and unencumbered software released into the public domain. * * Anyone is free to copy, modify, publish, use, compile, sell, or * distribute this software, either in source code form or as a compiled * binary, for any purpose, commercial or non-commercial, and by any * means. * * In jurisdictions that recognize copyright laws, the author or authors * of this software dedicate any and all copyright interest in the * software to the public domain. We make this dedication for the benefit * of the public at large and to the detriment of our heirs and * successors. We intend this dedication to be an overt act of * relinquishment in perpetuity of all present and future rights to this * software under copyright law. * * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. * IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. * * For more information, please refer to <http://unlicense.org/> */ /* global ActiveXObject: false */ /* jshint browser: true */ (function () { 'use strict'; var /* jStorage version */ JSTORAGE_VERSION = '0.4.12', /* detect a dollar object or create one if not found */ $ = window.jQuery || window.$ || (window.$ = {}), /* check for a JSON handling support */ JSON = { parse: window.JSON && (window.JSON.parse || window.JSON.decode) || String.prototype.evalJSON && function (str) { return String(str).evalJSON(); } || $.parseJSON || $.evalJSON, stringify: Object.toJSON || window.JSON && (window.JSON.stringify || window.JSON.encode) || $.toJSON }; // Break if no JSON support was found if (typeof JSON.parse !== 'function' || typeof JSON.stringify !== 'function') { throw new Error('No JSON support found, include //cdnjs.cloudflare.com/ajax/libs/json2/20110223/json2.js to page'); } var /* This is the object, that holds the cached values */ _storage = { __jstorage_meta: { CRC32: {} } }, /* Actual browser storage (localStorage or globalStorage['domain']) */ _storage_service = { jStorage: '{}' }, /* DOM element for older IE versions, holds userData behavior */ _storage_elm = null, /* How much space does the storage take */ _storage_size = 0, /* which backend is currently used */ _backend = false, /* onchange observers */ _observers = {}, /* timeout to wait after onchange event */ _observer_timeout = false, /* last update time */ _observer_update = 0, /* pubsub observers */ _pubsub_observers = {}, /* skip published items older than current timestamp */ _pubsub_last = +new Date(), /* Next check for TTL */ _ttl_timeout, /** * XML encoding and decoding as XML nodes can't be JSON'ized * XML nodes are encoded and decoded if the node is the value to be saved * but not if it's as a property of another object * Eg. - * $.jStorage.set('key', xmlNode); // IS OK * $.jStorage.set('key', {xml: xmlNode}); // NOT OK */ _XMLService = { /** * Validates a XML node to be XML * based on jQuery.isXML function */ isXML: function (elm) { var documentElement = (elm ? elm.ownerDocument || elm : 0).documentElement; return documentElement ? documentElement.nodeName !== 'HTML' : false; }, /** * Encodes a XML node to string * based on http://www.mercurytide.co.uk/news/article/issues-when-working-ajax/ */ encode: function (xmlNode) { if (!this.isXML(xmlNode)) { return false; } try { // Mozilla, Webkit, Opera return new XMLSerializer().serializeToString(xmlNode); } catch (E1) { try { // IE return xmlNode.xml; } catch (E2) { } } return false; }, /** * Decodes a XML node from string * loosely based on http://outwestmedia.com/jquery-plugins/xmldom/ */ decode: function (xmlString) { var dom_parser = ('DOMParser' in window && (new DOMParser()).parseFromString) || (window.ActiveXObject && function (_xmlString) { var xml_doc = new ActiveXObject('Microsoft.XMLDOM'); xml_doc.async = 'false'; xml_doc.loadXML(_xmlString); return xml_doc; }), resultXML; if (!dom_parser) { return false; } resultXML = dom_parser.call('DOMParser' in window && (new DOMParser()) || window, xmlString, 'text/xml'); return this.isXML(resultXML) ? resultXML : false; } }; ////////////////////////// PRIVATE METHODS //////////////////////// /** * Initialization function. Detects if the browser supports DOM Storage * or userData behavior and behaves accordingly. */ function _init() { /* Check if browser supports localStorage */ var localStorageReallyWorks = false; if ('localStorage' in window) { try { window.localStorage.setItem('_tmptest', 'tmpval'); localStorageReallyWorks = true; window.localStorage.removeItem('_tmptest'); } catch (BogusQuotaExceededErrorOnIos5) { // Thanks be to iOS5 Private Browsing mode which throws // QUOTA_EXCEEDED_ERRROR DOM Exception 22. } } if (localStorageReallyWorks) { try { if (window.localStorage) { _storage_service = window.localStorage; _backend = 'localStorage'; _observer_update = _storage_service.jStorage_update; } } catch (E3) { /* Firefox fails when touching localStorage and cookies are disabled */ } } /* Check if browser supports globalStorage */ else if ('globalStorage' in window) { try { if (window.globalStorage) { if (window.location.hostname == 'localhost') { _storage_service = window.globalStorage['localhost.localdomain']; } else { _storage_service = window.globalStorage[window.location.hostname]; } _backend = 'globalStorage'; _observer_update = _storage_service.jStorage_update; } } catch (E4) { /* Firefox fails when touching localStorage and cookies are disabled */ } } /* Check if browser supports userData behavior */ else { _storage_elm = document.createElement('link'); if (_storage_elm.addBehavior) { /* Use a DOM element to act as userData storage */ _storage_elm.style.behavior = 'url(#default#userData)'; /* userData element needs to be inserted into the DOM! */ document.getElementsByTagName('head')[0].appendChild(_storage_elm); try { _storage_elm.load('jStorage'); } catch (E) { // try to reset cache _storage_elm.setAttribute('jStorage', '{}'); _storage_elm.save('jStorage'); _storage_elm.load('jStorage'); } var data = '{}'; try { data = _storage_elm.getAttribute('jStorage'); } catch (E5) { } try { _observer_update = _storage_elm.getAttribute('jStorage_update'); } catch (E6) { } _storage_service.jStorage = data; _backend = 'userDataBehavior'; } else { _storage_elm = null; return; } } // Load data from storage _load_storage(); // remove dead keys _handleTTL(); // start listening for changes _setupObserver(); // initialize publish-subscribe service _handlePubSub(); // handle cached navigation if ('addEventListener' in window) { window.addEventListener('pageshow', function (event) { if (event.persisted) { _storageObserver(); } }, false); } } /** * Reload data from storage when needed */ function _reloadData() { var data = '{}'; if (_backend == 'userDataBehavior') { _storage_elm.load('jStorage'); try { data = _storage_elm.getAttribute('jStorage'); } catch (E5) { } try { _observer_update = _storage_elm.getAttribute('jStorage_update'); } catch (E6) { } _storage_service.jStorage = data; } _load_storage(); // remove dead keys _handleTTL(); _handlePubSub(); } /** * Sets up a storage change observer */ function _setupObserver() { if (_backend == 'localStorage' || _backend == 'globalStorage') { if ('addEventListener' in window) { window.addEventListener('storage', _storageObserver, false); } else { document.attachEvent('onstorage', _storageObserver); } } else if (_backend == 'userDataBehavior') { setInterval(_storageObserver, 1000); } } /** * Fired on any kind of data change, needs to check if anything has * really been changed */ function _storageObserver() { var updateTime; // cumulate change notifications with timeout clearTimeout(_observer_timeout); _observer_timeout = setTimeout(function () { if (_backend == 'localStorage' || _backend == 'globalStorage') { updateTime = _storage_service.jStorage_update; } else if (_backend == 'userDataBehavior') { _storage_elm.load('jStorage'); try { updateTime = _storage_elm.getAttribute('jStorage_update'); } catch (E5) { } } if (updateTime && updateTime != _observer_update) { _observer_update = updateTime; _checkUpdatedKeys(); } }, 25); } /** * Reloads the data and checks if any keys are changed */ function _checkUpdatedKeys() { var oldCrc32List = JSON.parse(JSON.stringify(_storage.__jstorage_meta.CRC32)), newCrc32List; _reloadData(); newCrc32List = JSON.parse(JSON.stringify(_storage.__jstorage_meta.CRC32)); var key, updated = [], removed = []; for (key in oldCrc32List) { if (oldCrc32List.hasOwnProperty(key)) { if (!newCrc32List[key]) { removed.push(key); continue; } if (oldCrc32List[key] != newCrc32List[key] && String(oldCrc32List[key]).substr(0, 2) == '2.') { updated.push(key); } } } for (key in newCrc32List) { if (newCrc32List.hasOwnProperty(key)) { if (!oldCrc32List[key]) { updated.push(key); } } } _fireObservers(updated, 'updated'); _fireObservers(removed, 'deleted'); } /** * Fires observers for updated keys * * @param {Array|String} keys Array of key names or a key * @param {String} action What happened with the value (updated, deleted, flushed) */ function _fireObservers(keys, action) { keys = [].concat(keys || []); var i, j, len, jlen; if (action == 'flushed') { keys = []; for (var key in _observers) { if (_observers.hasOwnProperty(key)) { keys.push(key); } } action = 'deleted'; } for (i = 0, len = keys.length; i < len; i++) { if (_observers[keys[i]]) { for (j = 0, jlen = _observers[keys[i]].length; j < jlen; j++) { _observers[keys[i]][j](keys[i], action); } } if (_observers['*']) { for (j = 0, jlen = _observers['*'].length; j < jlen; j++) { _observers['*'][j](keys[i], action); } } } } /** * Publishes key change to listeners */ function _publishChange() { var updateTime = (+new Date()).toString(); if (_backend == 'localStorage' || _backend == 'globalStorage') { try { _storage_service.jStorage_update = updateTime; } catch (E8) { // safari private mode has been enabled after the jStorage initialization _backend = false; } } else if (_backend == 'userDataBehavior') { _storage_elm.setAttribute('jStorage_update', updateTime); _storage_elm.save('jStorage'); } _storageObserver(); } /** * Loads the data from the storage based on the supported mechanism */ function _load_storage() { /* if jStorage string is retrieved, then decode it */ if (_storage_service.jStorage) { try { _storage = JSON.parse(String(_storage_service.jStorage)); } catch (E6) { _storage_service.jStorage = '{}'; } } else { _storage_service.jStorage = '{}'; } _storage_size = _storage_service.jStorage ? String(_storage_service.jStorage).length : 0; if (!_storage.__jstorage_meta) { _storage.__jstorage_meta = {}; } if (!_storage.__jstorage_meta.CRC32) { _storage.__jstorage_meta.CRC32 = {}; } } /** * This functions provides the 'save' mechanism to store the jStorage object */ function _save() { _dropOldEvents(); // remove expired events try { _storage_service.jStorage = JSON.stringify(_storage); // If userData is used as the storage engine, additional if (_storage_elm) { _storage_elm.setAttribute('jStorage', _storage_service.jStorage); _storage_elm.save('jStorage'); } _storage_size = _storage_service.jStorage ? String(_storage_service.jStorage).length : 0; } catch (E7) { /* probably cache is full, nothing is saved this way*/ } } /** * Function checks if a key is set and is string or numberic * * @param {String} key Key name */ function _checkKey(key) { if (typeof key != 'string' && typeof key != 'number') { throw new TypeError('Key name must be string or numeric'); } if (key == '__jstorage_meta') { throw new TypeError('Reserved key name'); } return true; } /** * Removes expired keys */ function _handleTTL() { var curtime, i, TTL, CRC32, nextExpire = Infinity, changed = false, deleted = []; clearTimeout(_ttl_timeout); if (!_storage.__jstorage_meta || typeof _storage.__jstorage_meta.TTL != 'object') { // nothing to do here return; }