PageRenderTime 31ms CodeModel.GetById 14ms app.highlight 13ms RepoModel.GetById 2ms app.codeStats 0ms

/static/scripts/jstorage.js

https://bitbucket.org/cistrome/cistrome-harvard/
JavaScript | 420 lines | 195 code | 41 blank | 184 comment | 44 complexity | 1ed3525ccbd6baad44ed74b13096a156 MD5 | raw file
  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 Andris Reinman, andris.reinman@gmail.com
  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/**
 28 * $.jStorage
 29 * 
 30 * USAGE:
 31 *
 32 * jStorage requires Prototype, MooTools or jQuery! If jQuery is used, then
 33 * jQuery-JSON (http://code.google.com/p/jquery-json/) is also needed.
 34 * (jQuery-JSON needs to be loaded BEFORE jStorage!)
 35 *
 36 * Methods:
 37 *
 38 * -set(key, value)
 39 * $.jStorage.set(key, value) -> saves a value
 40 *
 41 * -get(key[, default])
 42 * value = $.jStorage.get(key [, default]) ->
 43 *    retrieves value if key exists, or default if it doesn't
 44 *
 45 * -deleteKey(key)
 46 * $.jStorage.deleteKey(key) -> removes a key from the storage
 47 *
 48 * -flush()
 49 * $.jStorage.flush() -> clears the cache
 50 * 
 51 * -storageObj()
 52 * $.jStorage.storageObj() -> returns a read-ony copy of the actual storage
 53 * 
 54 * -storageSize()
 55 * $.jStorage.storageSize() -> returns the size of the storage in bytes
 56 *
 57 * -index()
 58 * $.jStorage.index() -> returns the used keys as an array
 59 * 
 60 * -storageAvailable()
 61 * $.jStorage.storageAvailable() -> returns true if storage is available
 62 * 
 63 * -reInit()
 64 * $.jStorage.reInit() -> reloads the data from browser storage
 65 * 
 66 * <value> can be any JSON-able value, including objects and arrays.
 67 *
 68 **/
 69
 70(function($){
 71    if(!$ || !($.toJSON || Object.toJSON || window.JSON)){
 72        throw new Error("jQuery, MooTools or Prototype needs to be loaded before jStorage!");
 73    }
 74    
 75    var
 76        /* This is the object, that holds the cached values */ 
 77        _storage = {},
 78
 79        /* Actual browser storage (localStorage or globalStorage['domain']) */
 80        _storage_service = {jStorage:"{}"},
 81
 82        /* DOM element for older IE versions, holds userData behavior */
 83        _storage_elm = null,
 84        
 85        /* How much space does the storage take */
 86        _storage_size = 0,
 87
 88        /* function to encode objects to JSON strings */
 89        json_encode = $.toJSON || Object.toJSON || (window.JSON && (JSON.encode || JSON.stringify)),
 90
 91        /* function to decode objects from JSON strings */
 92        json_decode = $.evalJSON || (window.JSON && (JSON.decode || JSON.parse)) || function(str){
 93            return String(str).evalJSON();
 94        },
 95        
 96        /* which backend is currently used */
 97        _backend = false;
 98        
 99        /**
100         * XML encoding and decoding as XML nodes can't be JSON'ized
101         * XML nodes are encoded and decoded if the node is the value to be saved
102         * but not if it's as a property of another object
103         * Eg. -
104         *   $.jStorage.set("key", xmlNode);        // IS OK
105         *   $.jStorage.set("key", {xml: xmlNode}); // NOT OK
106         */
107        _XMLService = {
108            
109            /**
110             * Validates a XML node to be XML
111             * based on jQuery.isXML function
112             */
113            isXML: function(elm){
114                var documentElement = (elm ? elm.ownerDocument || elm : 0).documentElement;
115                return documentElement ? documentElement.nodeName !== "HTML" : false;
116            },
117            
118            /**
119             * Encodes a XML node to string
120             * based on http://www.mercurytide.co.uk/news/article/issues-when-working-ajax/
121             */
122            encode: function(xmlNode) {
123                if(!this.isXML(xmlNode)){
124                    return false;
125                }
126                try{ // Mozilla, Webkit, Opera
127                    return new XMLSerializer().serializeToString(xmlNode);
128                }catch(E1) {
129                    try {  // IE
130                        return xmlNode.xml;
131                    }catch(E2){}
132                }
133                return false;
134            },
135            
136            /**
137             * Decodes a XML node from string
138             * loosely based on http://outwestmedia.com/jquery-plugins/xmldom/
139             */
140            decode: function(xmlString){
141                var dom_parser = ("DOMParser" in window && (new DOMParser()).parseFromString) ||
142                        (window.ActiveXObject && function(_xmlString) {
143                    var xml_doc = new ActiveXObject('Microsoft.XMLDOM');
144                    xml_doc.async = 'false';
145                    xml_doc.loadXML(_xmlString);
146                    return xml_doc;
147                }),
148                resultXML;
149                if(!dom_parser){
150                    return false;
151                }
152                resultXML = dom_parser.call("DOMParser" in window && (new DOMParser()) || window, xmlString, 'text/xml');
153                return this.isXML(resultXML)?resultXML:false;
154            }
155        };
156
157    ////////////////////////// PRIVATE METHODS ////////////////////////
158
159    /**
160     * Initialization function. Detects if the browser supports DOM Storage
161     * or userData behavior and behaves accordingly.
162     * @returns undefined
163     */
164    function _init(){
165        /* Check if browser supports localStorage */
166        if("localStorage" in window){
167            try {
168                if(window.localStorage) {
169                    _storage_service = window.localStorage;
170                    _backend = "localStorage";
171                }
172            } catch(E3) {/* Firefox fails when touching localStorage and cookies are disabled */}
173        }
174        /* Check if browser supports globalStorage */
175        else if("globalStorage" in window){
176            try {
177                if(window.globalStorage) {
178                    _storage_service = window.globalStorage[window.location.hostname];
179                    _backend = "globalStorage";
180                }
181            } catch(E4) {/* Firefox fails when touching localStorage and cookies are disabled */}
182        }
183        /* Check if browser supports userData behavior */
184        else {
185            _storage_elm = document.createElement('link');
186            if(_storage_elm.addBehavior){
187
188                /* Use a DOM element to act as userData storage */
189                _storage_elm.style.behavior = 'url(#default#userData)';
190
191                /* userData element needs to be inserted into the DOM! */
192                document.getElementsByTagName('head')[0].appendChild(_storage_elm);
193
194                _storage_elm.load("jStorage");
195                var data = "{}";
196                try{
197                    data = _storage_elm.getAttribute("jStorage");
198                }catch(E5){}
199                _storage_service.jStorage = data;
200                _backend = "userDataBehavior";
201            }else{
202                _storage_elm = null;
203                return;
204            }
205        }
206
207        _load_storage();
208    }
209    
210    /**
211     * Loads the data from the storage based on the supported mechanism
212     * @returns undefined
213     */
214    function _load_storage(){
215        /* if jStorage string is retrieved, then decode it */
216        if(_storage_service.jStorage){
217            try{
218                _storage = json_decode(String(_storage_service.jStorage));
219            }catch(E6){_storage_service.jStorage = "{}";}
220        }else{
221            _storage_service.jStorage = "{}";
222        }
223        _storage_size = _storage_service.jStorage?String(_storage_service.jStorage).length:0;    
224    }
225
226    /**
227     * This functions provides the "save" mechanism to store the jStorage object
228     * @returns undefined
229     */
230    function _save(){
231        try{
232            _storage_service.jStorage = json_encode(_storage);
233            // If userData is used as the storage engine, additional
234            if(_storage_elm) {
235                _storage_elm.setAttribute("jStorage",_storage_service.jStorage);
236                _storage_elm.save("jStorage");
237            }
238            _storage_size = _storage_service.jStorage?String(_storage_service.jStorage).length:0;
239        }catch(E7){/* probably cache is full, nothing is saved this way*/}
240    }
241
242    /**
243     * Function checks if a key is set and is string or numberic
244     */
245    function _checkKey(key){
246        if(!key || (typeof key != "string" && typeof key != "number")){
247            throw new TypeError('Key name must be string or numeric');
248        }
249        return true;
250    }
251
252    ////////////////////////// PUBLIC INTERFACE /////////////////////////
253
254    $.jStorage = {
255        /* Version number */
256        version: "0.1.5.0",
257
258        /**
259         * Sets a key's value.
260         * 
261         * @param {String} key - Key to set. If this value is not set or not
262         *              a string an exception is raised.
263         * @param value - Value to set. This can be any value that is JSON
264         *              compatible (Numbers, Strings, Objects etc.).
265         * @returns the used value
266         */
267        set: function(key, value){
268            _checkKey(key);
269            if(_XMLService.isXML(value)){
270                value = {_is_xml:true,xml:_XMLService.encode(value)};
271            }
272            _storage[key] = value;
273            _save();
274            return value;
275        },
276        
277        /**
278         * Looks up a key in cache
279         * 
280         * @param {String} key - Key to look up.
281         * @param {mixed} def - Default value to return, if key didn't exist.
282         * @returns the key value, default value or <null>
283         */
284        get: function(key, def){
285            _checkKey(key);
286            if(key in _storage){
287                if(typeof _storage[key] == "object" &&
288                        _storage[key]._is_xml &&
289                            _storage[key]._is_xml){
290                    return _XMLService.decode(_storage[key].xml);
291                }else{
292                    return _storage[key];
293                }
294            }
295            return typeof(def) == 'undefined' ? null : def;
296        },
297        
298        /**
299         * Deletes a key from cache.
300         * 
301         * @param {String} key - Key to delete.
302         * @returns true if key existed or false if it didn't
303         */
304        deleteKey: function(key){
305            _checkKey(key);
306            if(key in _storage){
307                delete _storage[key];
308                _save();
309                return true;
310            }
311            return false;
312        },
313
314        /**
315         * Deletes everything in cache.
316         * 
317         * @returns true
318         */
319        flush: function(){
320            _storage = {};
321            _save();
322            /*
323             * Just to be sure - andris9/jStorage#3
324             */
325            try{
326                window.localStorage.clear();
327            }catch(E8){}
328            return true;
329        },
330        
331        /**
332         * Returns a read-only copy of _storage
333         * 
334         * @returns Object
335        */
336        storageObj: function(){
337            function F() {}
338            F.prototype = _storage;
339            return new F();
340        },
341        
342        /**
343         * Returns an index of all used keys as an array
344         * ['key1', 'key2',..'keyN']
345         * 
346         * @returns Array
347        */
348        index: function(){
349            var index = [], i;
350            for(i in _storage){
351                if(_storage.hasOwnProperty(i)){
352                    index.push(i);
353                }
354            }
355            return index;
356        },
357        
358        /**
359         * How much space in bytes does the storage take?
360         * 
361         * @returns Number
362         */
363        storageSize: function(){
364            return _storage_size;
365        },
366        
367        /**
368         * Which backend is currently in use?
369         * 
370         * @returns String
371         */
372        currentBackend: function(){
373            return _backend;
374        },
375        
376        /**
377         * Test if storage is available
378         * 
379         * @returns Boolean
380         */
381        storageAvailable: function(){
382            return !!_backend;
383        },
384        
385        /**
386         * Reloads the data from browser storage
387         * 
388         * @returns undefined
389         */
390        reInit: function(){
391            var new_storage_elm, data;
392            if(_storage_elm && _storage_elm.addBehavior){
393                new_storage_elm = document.createElement('link');
394                
395                _storage_elm.parentNode.replaceChild(new_storage_elm, _storage_elm);
396                _storage_elm = new_storage_elm;
397                
398                /* Use a DOM element to act as userData storage */
399                _storage_elm.style.behavior = 'url(#default#userData)';
400
401                /* userData element needs to be inserted into the DOM! */
402                document.getElementsByTagName('head')[0].appendChild(_storage_elm);
403
404                _storage_elm.load("jStorage");
405                data = "{}";
406                try{
407                    data = _storage_elm.getAttribute("jStorage");
408                }catch(E5){}
409                _storage_service.jStorage = data;
410                _backend = "userDataBehavior";
411            }
412            
413            _load_storage();
414        }
415    };
416
417    // Initialize jStorage
418    _init();
419
420})(window.jQuery || window.$);