Tw100

Memoria_Quickbar

Nov 23rd, 2017
99
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. /*
  2.  * ----------------------------- Memoria -------------------------------------
  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, andris.reinman@gmail.com
  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:gary.court@gmail.com'>Gary Court</a>
  628.      * @see http://github.com/garycourt/murmurhash-js
  629.      * @author <a href='mailto:aappleby@gmail.com'>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. })();
Add Comment
Please, Sign In to add comment