996 lines · 31.2 KB · JavaScript
RawDownload
/* * ----------------------------- 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.

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t