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