PageSourceSearch

https://www.dongruncasting.com/view/templates/dongrun/js/jstorage.js

js dongruncasting.com collected 2026-10-04 01:50:02 UTC 32,933 bytes, 996 lines download raw bytes

vendor: 32,933 bytes, lines 1-996
1/*
2 * ----------------------------- JSTORAGE -------------------------------------
3 * Simple local storage wrapper to save data on the browser side, supporting
4 * all major browsers - IE6+, Firefox2+, Safari4+, Chrome4+ and Opera 10.5+
5 *
6 * Author: Andris Reinman, [email protected]
7 * Project homepage: www.jstorage.info
8 *
9 * Licensed under Unlicense:
10 *
11 * This is free and unencumbered software released into the public domain.
12 *
13 * Anyone is free to copy, modify, publish, use, compile, sell, or
14 * distribute this software, either in source code form or as a compiled
15 * binary, for any purpose, commercial or non-commercial, and by any
16 * means.
17 *
18 * In jurisdictions that recognize copyright laws, the author or authors
19 * of this software dedicate any and all copyright interest in the
20 * software to the public domain. We make this dedication for the benefit
21 * of the public at large and to the detriment of our heirs and
22 * successors. We intend this dedication to be an overt act of
23 * relinquishment in perpetuity of all present and future rights to this
24 * software under copyright law.
25 *
26 * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
27 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
28 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
29 * IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
30 * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
31 * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
32 * OTHER DEALINGS IN THE SOFTWARE.
33 *
34 * For more information, please refer to <http://unlicense.org/>
35 */
36
37/* global ActiveXObject: false */
38/* jshint browser: true */
39
40(function() {
41    'use strict';
42
43    var
44    /* jStorage version */
45        JSTORAGE_VERSION = '0.4.12',
46
47        /* detect a dollar object or create one if not found */
48        $ = window.jQuery || window.$ || (window.$ = {}),
49
50        /* check for a JSON handling support */
51        JSON = {
52            parse: window.JSON && (window.JSON.parse || window.JSON.decode) ||
53                String.prototype.evalJSON && function(str) {
54                    return String(str).evalJSON();
55            } ||
56                $.parseJSON ||
57                $.evalJSON,
58            stringify: Object.toJSON ||
59                window.JSON && (window.JSON.stringify || window.JSON.encode) ||
60                $.toJSON
61        };
62
63    // Break if no JSON support was found
64    if (typeof JSON.parse !== 'function' || typeof JSON.stringify !== 'function') {
65        throw new Error('No JSON support found, include //cdnjs.cloudflare.com/ajax/libs/json2/20110223/json2.js to page');
66    }
67
68    var
69    /* This is the object, that holds the cached values */
70        _storage = {
71            __jstorage_meta: {
72                CRC32: {}
73            }
74        },
75
76        /* Actual browser storage (localStorage or globalStorage['domain']) */
77        _storage_service = {
78            jStorage: '{}'
79        },
80
81        /* DOM element for older IE versions, holds userData behavior */
82        _storage_elm = null,
83
84        /* How much space does the storage take */
85        _storage_size = 0,
86
87        /* which backend is currently used */
88        _backend = false,
89
90        /* onchange observers */
91        _observers = {},
92
93        /* timeout to wait after onchange event */
94        _observer_timeout = false,
95
96        /* last update time */
97        _observer_update = 0,
98
99        /* pubsub observers */
100        _pubsub_observers = {},
101
102        /* skip published items older than current timestamp */
103        _pubsub_last = +new Date(),
104
105        /* Next check for TTL */
106        _ttl_timeout,
107
108        /**
109         * XML encoding and decoding as XML nodes can't be JSON'ized
110         * XML nodes are encoded and decoded if the node is the value to be saved
111         * but not if it's as a property of another object
112         * Eg. -
113         *   $.jStorage.set('key', xmlNode);        // IS OK
114         *   $.jStorage.set('key', {xml: xmlNode}); // NOT OK
115         */
116        _XMLService = {
117
118            /**
119             * Validates a XML node to be XML
120             * based on jQuery.isXML function
121             */
122            isXML: function(elm) {
123                var documentElement = (elm ? elm.ownerDocument || elm : 0).documentElement;
124                return documentElement ? documentElement.nodeName !== 'HTML' : false;
125            },
126
127            /**
128             * Encodes a XML node to string
129             * based on http://www.mercurytide.co.uk/news/article/issues-when-working-ajax/
130             */
131            encode: function(xmlNode) {
132                if (!this.isXML(xmlNode)) {
133                    return false;
134                }
135                try { // Mozilla, Webkit, Opera
136                    return new XMLSerializer().serializeToString(xmlNode);
137                } catch (E1) {
138                    try { // IE
139                        return xmlNode.xml;
140                    } catch (E2) {}
141                }
142                return false;
143            },
144
145            /**
146             * Decodes a XML node from string
147             * loosely based on http://outwestmedia.com/jquery-plugins/xmldom/
148             */
149            decode: function(xmlString) {
150                var dom_parser = ('DOMParser' in window && (new DOMParser()).parseFromString) ||
151                    (window.ActiveXObject && function(_xmlString) {
152                        var xml_doc = new ActiveXObject('Microsoft.XMLDOM');
153                        xml_doc.async = 'false';
154                        xml_doc.loadXML(_xmlString);
155                        return xml_doc;
156                    }),
157                    resultXML;
158                if (!dom_parser) {
159                    return false;
160                }
161                resultXML = dom_parser.call('DOMParser' in window && (new DOMParser()) || window, xmlString, 'text/xml');
162                return this.isXML(resultXML) ? resultXML : false;
163            }
164        };
165
166
167    ////////////////////////// PRIVATE METHODS ////////////////////////
168
169    /**
170     * Initialization function. Detects if the browser supports DOM Storage
171     * or userData behavior and behaves accordingly.
172     */
173    function _init() {
174        /* Check if browser supports localStorage */
175        var localStorageReallyWorks = false;
176        if ('localStorage' in window) {
177            try {
178                window.localStorage.setItem('_tmptest', 'tmpval');
179                localStorageReallyWorks = true;
180                window.localStorage.removeItem('_tmptest');
181            } catch (BogusQuotaExceededErrorOnIos5) {
182                // Thanks be to iOS5 Private Browsing mode which throws
183                // QUOTA_EXCEEDED_ERRROR DOM Exception 22.
184            }
185        }
186
187        if (localStorageReallyWorks) {
188            try {
189                if (window.localStorage) {
190                    _storage_service = window.localStorage;
191                    _backend = 'localStorage';
192                    _observer_update = _storage_service.jStorage_update;
193                }
194            } catch (E3) { /* Firefox fails when touching localStorage and cookies are disabled */ }
195        }
196        /* Check if browser supports globalStorage */
197        else if ('globalStorage' in window) {
198            try {
199                if (window.globalStorage) {
200                    if (window.location.hostname == 'localhost') {
201                        _storage_service = window.globalStorage['localhost.localdomain'];
202                    } else {
203                        _storage_service = window.globalStorage[window.location.hostname];
204                    }
205                    _backend = 'globalStorage';
206                    _observer_update = _storage_service.jStorage_update;
207                }
208            } catch (E4) { /* Firefox fails when touching localStorage and cookies are disabled */ }
209        }
210        /* Check if browser supports userData behavior */
211        else {
212            _storage_elm = document.createElement('link');
213            if (_storage_elm.addBehavior) {
214
215                /* Use a DOM element to act as userData storage */
216                _storage_elm.style.behavior = 'url(#default#userData)';
217
218                /* userData element needs to be inserted into the DOM! */
219                document.getElementsByTagName('head')[0].appendChild(_storage_elm);
220
221                try {
222                    _storage_elm.load('jStorage');
223                } catch (E) {
224                    // try to reset cache
225                    _storage_elm.setAttribute('jStorage', '{}');
226                    _storage_elm.save('jStorage');
227                    _storage_elm.load('jStorage');
228                }
229
230                var data = '{}';
231                try {
232                    data = _storage_elm.getAttribute('jStorage');
233                } catch (E5) {}
234
235                try {
236                    _observer_update = _storage_elm.getAttribute('jStorage_update');
237                } catch (E6) {}
238
239                _storage_service.jStorage = data;
240                _backend = 'userDataBehavior';
241            } else {
242                _storage_elm = null;
243                return;
244            }
245        }
246
247        // Load data from storage
248        _load_storage();
249
250        // remove dead keys
251        _handleTTL();
252
253        // start listening for changes
254        _setupObserver();
255
256        // initialize publish-subscribe service
257        _handlePubSub();
258
259        // handle cached navigation
260        if ('addEventListener' in window) {
261            window.addEventListener('pageshow', function(event) {
262                if (event.persisted) {
263                    _storageObserver();
264                }
265            }, false);
266        }
267    }
268
269    /**
270     * Reload data from storage when needed
271     */
272    function _reloadData() {
273        var data = '{}';
274
275        if (_backend == 'userDataBehavior') {
276            _storage_elm.load('jStorage');
277
278            try {
279                data = _storage_elm.getAttribute('jStorage');
280            } catch (E5) {}
281
282            try {
283                _observer_update = _storage_elm.getAttribute('jStorage_update');
284            } catch (E6) {}
285
286            _storage_service.jStorage = data;
287        }
288
289        _load_storage();
290
291        // remove dead keys
292        _handleTTL();
293
294        _handlePubSub();
295    }
296
297    /**
298     * Sets up a storage change observer
299     */
300    function _setupObserver() {
301        if (_backend == 'localStorage' || _backend == 'globalStorage') {
302            if ('addEventListener' in window) {
303                window.addEventListener('storage', _storageObserver, false);
304            } else {
305                document.attachEvent('onstorage', _storageObserver);
306            }
307        } else if (_backend == 'userDataBehavior') {
308            setInterval(_storageObserver, 1000);
309        }
310    }
311
312    /**
313     * Fired on any kind of data change, needs to check if anything has
314     * really been changed
315     */
316    function _storageObserver() {
317        var updateTime;
318        // cumulate change notifications with timeout
319        clearTimeout(_observer_timeout);
320        _observer_timeout = setTimeout(function() {
321
322            if (_backend == 'localStorage' || _backend == 'globalStorage') {
323                updateTime = _storage_service.jStorage_update;
324            } else if (_backend == 'userDataBehavior') {
325                _storage_elm.load('jStorage');
326                try {
327                    updateTime = _storage_elm.getAttribute('jStorage_update');
328                } catch (E5) {}
329            }
330
331            if (updateTime && updateTime != _observer_update) {
332                _observer_update = updateTime;
333                _checkUpdatedKeys();
334            }
335
336        }, 25);
337    }
338
339    /**
340     * Reloads the data and checks if any keys are changed
341     */
342    function _checkUpdatedKeys() {
343        var oldCrc32List = JSON.parse(JSON.stringify(_storage.__jstorage_meta.CRC32)),
344            newCrc32List;
345
346        _reloadData();
347        newCrc32List = JSON.parse(JSON.stringify(_storage.__jstorage_meta.CRC32));
348
349        var key,
350            updated = [],
351            removed = [];
352
353        for (key in oldCrc32List) {
354            if (oldCrc32List.hasOwnProperty(key)) {
355                if (!newCrc32List[key]) {
356                    removed.push(key);
357                    continue;
358                }
359                if (oldCrc32List[key] != newCrc32List[key] && String(oldCrc32List[key]).substr(0, 2) == '2.') {
360                    updated.push(key);
361                }
362            }
363        }
364
365        for (key in newCrc32List) {
366            if (newCrc32List.hasOwnProperty(key)) {
367                if (!oldCrc32List[key]) {
368                    updated.push(key);
369                }
370            }
371        }
372
373        _fireObservers(updated, 'updated');
374        _fireObservers(removed, 'deleted');
375    }
376
377    /**
378     * Fires observers for updated keys
379     *
380     * @param {Array|String} keys Array of key names or a key
381     * @param {String} action What happened with the value (updated, deleted, flushed)
382     */
383    function _fireObservers(keys, action) {
384        keys = [].concat(keys || []);
385
386        var i, j, len, jlen;
387
388        if (action == 'flushed') {
389            keys = [];
390            for (var key in _observers) {
391                if (_observers.hasOwnProperty(key)) {
392                    keys.push(key);
393                }
394            }
395            action = 'deleted';
396        }
397        for (i = 0, len = keys.length; i < len; i++) {
398            if (_observers[keys[i]]) {
399                for (j = 0, jlen = _observers[keys[i]].length; j < jlen; j++) {
400                    _observers[keys[i]][j](keys[i], action);
401                }
402            }
403            if (_observers['*']) {
404                for (j = 0, jlen = _observers['*'].length; j < jlen; j++) {
405                    _observers['*'][j](keys[i], action);
406                }
407            }
408        }
409    }
410
411    /**
412     * Publishes key change to listeners
413     */
414    function _publishChange() {
415        var updateTime = (+new Date()).toString();
416
417        if (_backend == 'localStorage' || _backend == 'globalStorage') {
418            try {
419                _storage_service.jStorage_update = updateTime;
420            } catch (E8) {
421                // safari private mode has been enabled after the jStorage initialization
422                _backend = false;
423            }
424        } else if (_backend == 'userDataBehavior') {
425            _storage_elm.setAttribute('jStorage_update', updateTime);
426            _storage_elm.save('jStorage');
427        }
428
429        _storageObserver();
430    }
431
432    /**
433     * Loads the data from the storage based on the supported mechanism
434     */
435    function _load_storage() {
436        /* if jStorage string is retrieved, then decode it */
437        if (_storage_service.jStorage) {
438            try {
439                _storage = JSON.parse(String(_storage_service.jStorage));
440            } catch (E6) {
441                _storage_service.jStorage = '{}';
442            }
443        } else {
444            _storage_service.jStorage = '{}';
445        }
446        _storage_size = _storage_service.jStorage ? String(_storage_service.jStorage).length : 0;
447
448        if (!_storage.__jstorage_meta) {
449            _storage.__jstorage_meta = {};
450        }
451        if (!_storage.__jstorage_meta.CRC32) {
452            _storage.__jstorage_meta.CRC32 = {};
453        }
454    }
455
456    /**
457     * This functions provides the 'save' mechanism to store the jStorage object
458     */
459    function _save() {
460        _dropOldEvents(); // remove expired events
461        try {
462            _storage_service.jStorage = JSON.stringify(_storage);
463            // If userData is used as the storage engine, additional
464            if (_storage_elm) {
465                _storage_elm.setAttribute('jStorage', _storage_service.jStorage);
466                _storage_elm.save('jStorage');
467            }
468            _storage_size = _storage_service.jStorage ? String(_storage_service.jStorage).length : 0;
469        } catch (E7) { /* probably cache is full, nothing is saved this way*/ }
470    }
471
472    /**
473     * Function checks if a key is set and is string or numberic
474     *
475     * @param {String} key Key name
476     */
477    function _checkKey(key) {
478        if (typeof key != 'string' && typeof key != 'number') {
479            throw new TypeError('Key name must be string or numeric');
480        }
481        if (key == '__jstorage_meta') {
482            throw new TypeError('Reserved key name');
483        }
484        return true;
485    }
486
487    /**
488     * Removes expired keys
489     */
490    function _handleTTL() {
491        var curtime, i, TTL, CRC32, nextExpire = Infinity,
492            changed = false,
493            deleted = [];
494
495        clearTimeout(_ttl_timeout);
496
497        if (!_storage.__jstorage_meta || typeof _storage.__jstorage_meta.TTL != 'object') {
498            // nothing to do here
499            return;
500        }
501
502        curtime = +new Date();
503        TTL = _storage.__jstorage_meta.TTL;
504
505        CRC32 = _storage.__jstorage_meta.CRC32;
506        for (i in TTL) {
507            if (TTL.hasOwnProperty(i)) {
508                if (TTL[i] <= curtime) {
509                    delete TTL[i];
510                    delete CRC32[i];
511                    delete _storage[i];
512                    changed = true;
513                    deleted.push(i);
514                } else if (TTL[i] < nextExpire) {
515                    nextExpire = TTL[i];
516                }
517            }
518        }
519
520        // set next check
521        if (nextExpire != Infinity) {
522            _ttl_timeout = setTimeout(_handleTTL, Math.min(nextExpire - curtime, 0x7FFFFFFF));
523        }
524
525        // save changes
526        if (changed) {
527            _save();
528            _publishChange();
529            _fireObservers(deleted, 'deleted');
530        }
531    }
532
533    /**
534     * Checks if there's any events on hold to be fired to listeners
535     */
536    function _handlePubSub() {
537        var i, len;
538        if (!_storage.__jstorage_meta.PubSub) {
539            return;
540        }
541        var pubelm,
542            _pubsubCurrent = _pubsub_last,
543            needFired = [];
544
545        for (i = len = _storage.__jstorage_meta.PubSub.length - 1; i >= 0; i--) {
546            pubelm = _storage.__jstorage_meta.PubSub[i];
547            if (pubelm[0] > _pubsub_last) {
548                _pubsubCurrent = pubelm[0];
549                needFired.unshift(pubelm);
550            }
551        }
552
553        for (i = needFired.length - 1; i >= 0; i--) {
554            _fireSubscribers(needFired[i][1], needFired[i][2]);
555        }
556
557        _pubsub_last = _pubsubCurrent;
558    }
559
560    /**
561     * Fires all subscriber listeners for a pubsub channel
562     *
563     * @param {String} channel Channel name
564     * @param {Mixed} payload Payload data to deliver
565     */
566    function _fireSubscribers(channel, payload) {
567        if (_pubsub_observers[channel]) {
568            for (var i = 0, len = _pubsub_observers[channel].length; i < len; i++) {
569                // send immutable data that can't be modified by listeners
570                try {
571                    _pubsub_observers[channel][i](channel, JSON.parse(JSON.stringify(payload)));
572                } catch (E) {}
573            }
574        }
575    }
576
577    /**
578     * Remove old events from the publish stream (at least 2sec old)
579     */
580    function _dropOldEvents() {
581        if (!_storage.__jstorage_meta.PubSub) {
582            return;
583        }
584
585        var retire = +new Date() - 2000;
586
587        for (var i = 0, len = _storage.__jstorage_meta.PubSub.length; i < len; i++) {
588            if (_storage.__jstorage_meta.PubSub[i][0] <= retire) {
589                // deleteCount is needed for IE6
590                _storage.__jstorage_meta.PubSub.splice(i, _storage.__jstorage_meta.PubSub.length - i);
591                break;
592            }
593        }
594
595        if (!_storage.__jstorage_meta.PubSub.length) {
596            delete _storage.__jstorage_meta.PubSub;
597        }
598
599    }
600
601    /**
602     * Publish payload to a channel
603     *
604     * @param {String} channel Channel name
605     * @param {Mixed} payload Payload to send to the subscribers
606     */
607    function _publish(channel, payload) {
608        if (!_storage.__jstorage_meta) {
609            _storage.__jstorage_meta = {};
610        }
611        if (!_storage.__jstorage_meta.PubSub) {
612            _storage.__jstorage_meta.PubSub = [];
613        }
614
615        _storage.__jstorage_meta.PubSub.unshift([+new Date(), channel, payload]);
616
617        _save();
618        _publishChange();
619    }
620
621
622    /**
623     * JS Implementation of MurmurHash2
624     *
625     *  SOURCE: https://github.com/garycourt/murmurhash-js (MIT licensed)
626     *
627     * @author <a href='mailto:[email protected]'>Gary Court</a>
628     * @see http://github.com/garycourt/murmurhash-js
629     * @author <a href='mailto:[email protected]'>Austin Appleby</a>
630     * @see http://sites.google.com/site/murmurhash/
631     *
632     * @param {string} str ASCII only
633     * @param {number} seed Positive integer only
634     * @return {number} 32-bit positive integer hash
635     */
636
637    function murmurhash2_32_gc(str, seed) {
638        var
639            l = str.length,
640            h = seed ^ l,
641            i = 0,
642            k;
643
644        while (l >= 4) {
645            k =
646                ((str.charCodeAt(i) & 0xff)) |
647                ((str.charCodeAt(++i) & 0xff) << 8) |
648                ((str.charCodeAt(++i) & 0xff) << 16) |
649                ((str.charCodeAt(++i) & 0xff) << 24);
650
651            k = (((k & 0xffff) * 0x5bd1e995) + ((((k >>> 16) * 0x5bd1e995) & 0xffff) << 16));
652            k ^= k >>> 24;
653            k = (((k & 0xffff) * 0x5bd1e995) + ((((k >>> 16) * 0x5bd1e995) & 0xffff) << 16));
654
655            h = (((h & 0xffff) * 0x5bd1e995) + ((((h >>> 16) * 0x5bd1e995) & 0xffff) << 16)) ^ k;
656
657            l -= 4;
658            ++i;
659        }
660
661        switch (l) {
662            case 3:
663                h ^= (str.charCodeAt(i + 2) & 0xff) << 16;
664                /* falls through */
665            case 2:
666                h ^= (str.charCodeAt(i + 1) & 0xff) << 8;
667                /* falls through */
668            case 1:
669                h ^= (str.charCodeAt(i) & 0xff);
670                h = (((h & 0xffff) * 0x5bd1e995) + ((((h >>> 16) * 0x5bd1e995) & 0xffff) << 16));
671        }
672
673        h ^= h >>> 13;
674        h = (((h & 0xffff) * 0x5bd1e995) + ((((h >>> 16) * 0x5bd1e995) & 0xffff) << 16));
675        h ^= h >>> 15;
676
677        return h >>> 0;
678    }
679
680    ////////////////////////// PUBLIC INTERFACE /////////////////////////
681
682    $.jStorage = {
683        /* Version number */
684        version: JSTORAGE_VERSION,
685
686        /**
687         * Sets a key's value.
688         *
689         * @param {String} key Key to set. If this value is not set or not
690         *              a string an exception is raised.
691         * @param {Mixed} value Value to set. This can be any value that is JSON
692         *              compatible (Numbers, Strings, Objects etc.).
693         * @param {Object} [options] - possible options to use
694         * @param {Number} [options.TTL] - optional TTL value, in milliseconds
695         * @return {Mixed} the used value
696         */
697        set: function(key, value, options) {
698            _checkKey(key);
699
700            options = options || {};
701
702            // undefined values are deleted automatically
703            if (typeof value == 'undefined') {
704                this.deleteKey(key);
705                return value;
706            }
707
708            if (_XMLService.isXML(value)) {
709                value = {
710                    _is_xml: true,
711                    xml: _XMLService.encode(value)
712                };
713            } else if (typeof value == 'function') {
714                return undefined; // functions can't be saved!
715            } else if (value && typeof value == 'object') {
716                // clone the object before saving to _storage tree
717                value = JSON.parse(JSON.stringify(value));
718            }
719
720            _storage[key] = value;
721
722            _storage.__jstorage_meta.CRC32[key] = '2.' + murmurhash2_32_gc(JSON.stringify(value), 0x9747b28c);
723
724            this.setTTL(key, options.TTL || 0); // also handles saving and _publishChange
725
726            _fireObservers(key, 'updated');
727            return value;
728        },
729
730        /**
731         * Looks up a key in cache
732         *
733         * @param {String} key - Key to look up.
734         * @param {mixed} def - Default value to return, if key didn't exist.
735         * @return {Mixed} the key value, default value or null
736         */
737        get: function(key, def) {
738            _checkKey(key);
739            if (key in _storage) {
740                if (_storage[key] && typeof _storage[key] == 'object' && _storage[key]._is_xml) {
741                    return _XMLService.decode(_storage[key].xml);
742                } else {
743                    return _storage[key];
744                }
745            }
746            return typeof(def) == 'undefined' ? null : def;
747        },
748
749        /**
750         * Deletes a key from cache.
751         *
752         * @param {String} key - Key to delete.
753         * @return {Boolean} true if key existed or false if it didn't
754         */
755        deleteKey: function(key) {
756            _checkKey(key);
757            if (key in _storage) {
758                delete _storage[key];
759                // remove from TTL list
760                if (typeof _storage.__jstorage_meta.TTL == 'object' &&
761                    key in _storage.__jstorage_meta.TTL) {
762                    delete _storage.__jstorage_meta.TTL[key];
763                }
764
765                delete _storage.__jstorage_meta.CRC32[key];
766
767                _save();
768                _publishChange();
769                _fireObservers(key, 'deleted');
770                return true;
771            }
772            return false;
773        },
774
775        /**
776         * Sets a TTL for a key, or remove it if ttl value is 0 or below
777         *
778         * @param {String} key - key to set the TTL for
779         * @param {Number} ttl - TTL timeout in milliseconds
780         * @return {Boolean} true if key existed or false if it didn't
781         */
782        setTTL: function(key, ttl) {
783            var curtime = +new Date();
784            _checkKey(key);
785            ttl = Number(ttl) || 0;
786            if (key in _storage) {
787
788                if (!_storage.__jstorage_meta.TTL) {
789                    _storage.__jstorage_meta.TTL = {};
790                }
791
792                // Set TTL value for the key
793                if (ttl > 0) {
794                    _storage.__jstorage_meta.TTL[key] = curtime + ttl;
795                } else {
796                    delete _storage.__jstorage_meta.TTL[key];
797                }
798
799                _save();
800
801                _handleTTL();
802
803                _publishChange();
804                return true;
805            }
806            return false;
807        },
808
809        /**
810         * Gets remaining TTL (in milliseconds) for a key or 0 when no TTL has been set
811         *
812         * @param {String} key Key to check
813         * @return {Number} Remaining TTL in milliseconds
814         */
815        getTTL: function(key) {
816            var curtime = +new Date(),
817                ttl;
818            _checkKey(key);
819            if (key in _storage && _storage.__jstorage_meta.TTL && _storage.__jstorage_meta.TTL[key]) {
820                ttl = _storage.__jstorage_meta.TTL[key] - curtime;
821                return ttl || 0;
822            }
823            return 0;
824        },
825
826        /**
827         * Deletes everything in cache.
828         *
829         * @return {Boolean} Always true
830         */
831        flush: function() {
832            _storage = {
833                __jstorage_meta: {
834                    CRC32: {}
835                }
836            };
837            _save();
838            _publishChange();
839            _fireObservers(null, 'flushed');
840            return true;
841        },
842
843        /**
844         * Returns a read-only copy of _storage
845         *
846         * @return {Object} Read-only copy of _storage
847         */
848        storageObj: function() {
849            function F() {}
850            F.prototype = _storage;
851            return new F();
852        },
853
854        /**
855         * Returns an index of all used keys as an array
856         * ['key1', 'key2',..'keyN']
857         *
858         * @return {Array} Used keys
859         */
860        index: function() {
861            var index = [],
862                i;
863            for (i in _storage) {
864                if (_storage.hasOwnProperty(i) && i != '__jstorage_meta') {
865                    index.push(i);
866                }
867            }
868            return index;
869        },
870
871        /**
872         * How much space in bytes does the storage take?
873         *
874         * @return {Number} Storage size in chars (not the same as in bytes,
875         *                  since some chars may take several bytes)
876         */
877        storageSize: function() {
878            return _storage_size;
879        },
880
881        /**
882         * Which backend is currently in use?
883         *
884         * @return {String} Backend name
885         */
886        currentBackend: function() {
887            return _backend;
888        },
889
890        /**
891         * Test if storage is available
892         *
893         * @return {Boolean} True if storage can be used
894         */
895        storageAvailable: function() {
896            return !!_backend;
897        },
898
899        /**
900         * Register change listeners
901         *
902         * @param {String} key Key name
903         * @param {Function} callback Function to run when the key changes
904         */
905        listenKeyChange: function(key, callback) {
906            _checkKey(key);
907            if (!_observers[key]) {
908                _observers[key] = [];
909            }
910            _observers[key].push(callback);
911        },
912
913        /**
914         * Remove change listeners
915         *
916         * @param {String} key Key name to unregister listeners against
917         * @param {Function} [callback] If set, unregister the callback, if not - unregister all
918         */
919        stopListening: function(key, callback) {
920            _checkKey(key);
921
922            if (!_observers[key]) {
923                return;
924            }
925
926            if (!callback) {
927                delete _observers[key];
928                return;
929            }
930
931            for (var i = _observers[key].length - 1; i >= 0; i--) {
932                if (_observers[key][i] == callback) {
933                    _observers[key].splice(i, 1);
934                }
935            }
936        },
937
938        /**
939         * Subscribe to a Publish/Subscribe event stream
940         *
941         * @param {String} channel Channel name
942         * @param {Function} callback Function to run when the something is published to the channel
943         */
944        subscribe: function(channel, callback) {
945            channel = (channel || '').toString();
946            if (!channel) {
947                throw new TypeError('Channel not defined');
948            }
949            if (!_pubsub_observers[channel]) {
950                _pubsub_observers[channel] = [];
951            }
952            _pubsub_observers[channel].push(callback);
953        },
954
955        /**
956         * Publish data to an event stream
957         *
958         * @param {String} channel Channel name
959         * @param {Mixed} payload Payload to deliver
960         */
961        publish: function(channel, payload) {
962            channel = (channel || '').toString();
963            if (!channel) {
964                throw new TypeError('Channel not defined');
965            }
966
967            _publish(channel, payload);
968        },
969
970        /**
971         * Reloads the data from browser storage
972         */
973        reInit: function() {
974            _reloadData();
975        },
976
977        /**
978         * Removes reference from global objects and saves it as jStorage
979         *
980         * @param {Boolean} option if needed to save object as simple 'jStorage' in windows context
981         */
982        noConflict: function(saveInGlobal) {
983            delete window.$.jStorage;
984
985            if (saveInGlobal) {
986                window.jStorage = this;
987            }
988
989            return this;
990        }
991    };
992
993    // Initialize jStorage
994    _init();
995
996})();

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.