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.