PageSourceSearch

http://zephyrhills.gov/Assets/Scripts/Store.js

js zephyrhills.gov collected 2026-10-04 15:23:24 UTC 31,025 bytes, 923 lines download raw bytes

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

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.