store.js 7.06 KB
/**
 * @fileOverview store.js封装
 * @author:Hbomb(zhouqq@yoho.cn)
 * @date:2013-04-09
 */
define("lib/util/store", [], function(require, exports, module)
{
    var json = require("lib/util/json");
    var store = {}, win = window, doc = win.document, localStorageName = 'localStorage', namespace = '__storejs__', storage;

    store.disabled = false;
    store.set = function(key, value)
    {
    };
    store.get = function(key)
    {
    };
    store.remove = function(key)
    {
    };
    store.clear = function()
    {
    };
    store.transact = function(key, defaultVal, transactionFn)
    {
        var val = store.get(key);
        if (transactionFn == null)
        {
            transactionFn = defaultVal;
            defaultVal = null;
        }
        if (typeof val == 'undefined')
        {
            val = defaultVal || {};
        }
        transactionFn(val);
        store.set(key, val);
    };
    store.getAll = function()
    {
    };

    store.serialize = function(value)
    {
        return json.stringify(value);
    };
    store.deserialize = function(value)
    {
        if (typeof value != 'string')
        {
            return undefined;
        }
        try
        {
            return json.parse(value);
        }
        catch (e)
        {
            return value || undefined;
        }
    };

    // Functions to encapsulate questionable FireFox 3.6.13 behavior
    // when about.config::dom.storage.enabled === false
    // See https://github.com/marcuswestin/store.js/issues#issue/13
    function isLocalStorageNameSupported()
    {
        try
        {
            return (localStorageName in win && win[localStorageName]);
        }
        catch (err)
        {
            return false;
        }
    }
    
    if (isLocalStorageNameSupported())
    {
        storage = win[localStorageName];
        store.set = function(key, val)
        {
            if (val === undefined)
            {
                return store.remove(key);
            }
            storage.setItem(key, store.serialize(val));
            return val;
        };
        store.get = function(key)
        {
            return store.deserialize(storage.getItem(key));
        };
        store.remove = function(key)
        {
            storage.removeItem(key);
        };
        store.clear = function()
        {
            storage.clear();
        };
        store.getAll = function()
        {
            var ret = {};
            for ( var i = 0; i < storage.length; ++i)
            {
                var key = storage.key(i);
                ret[key] = store.get(key);
            }
            return ret;
        };
    }
    else if (doc.documentElement.addBehavior)
    {
        var storageOwner, storageContainer;
        // Since #userData storage applies only to specific paths, we need to
        // somehow link our data to a specific path. We choose /favicon.ico
        // as a pretty safe option, since all browsers already make a request to
        // this URL anyway and being a 404 will not hurt us here. We wrap an
        // iframe pointing to the favicon in an ActiveXObject(htmlfile) object
        // (see: http://msdn.microsoft.com/en-us/library/aa752574(v=VS.85).aspx)
        // since the iframe access rules appear to allow direct access and
        // manipulation of the document element, even for a 404 page. This
        // document can be used instead of the current document (which would
        // have been limited to the current path) to perform #userData storage.
        try
        {
            storageContainer = new ActiveXObject('htmlfile');
            storageContainer.open();
            storageContainer.write('<s' + 'cript>document.w=window</s'
                    + 'cript><iframe src="/favicon.ico"></frame>');
            storageContainer.close();
            storageOwner = storageContainer.w.frames[0].document;
            storage = storageOwner.createElement('div');
        }
        catch (e)
        {
            // somehow ActiveXObject instantiation failed (perhaps some special
            // security settings or otherwse), fall back to per-path storage
            storage = doc.createElement('div');
            storageOwner = doc.body;
        }
        function withIEStorage(storeFunction)
        {
            return function()
            {
                var args = Array.prototype.slice.call(arguments, 0);
                args.unshift(storage);
                // See
                // http://msdn.microsoft.com/en-us/library/ms531081(v=VS.85).aspx
                // and
                // http://msdn.microsoft.com/en-us/library/ms531424(v=VS.85).aspx
                storageOwner.appendChild(storage);
                storage.addBehavior('#default#userData');
                storage.load(localStorageName);
                var result = storeFunction.apply(store, args);
                storageOwner.removeChild(storage);
                return result;
            };
        };
        
        // In IE7, keys may not contain special chars. See all of
        // https://github.com/marcuswestin/store.js/issues/40
        var forbiddenCharsRegex = new RegExp(
                "[!\"#$%&'()*+,/\\\\:;<=>?@[\\]^`{|}~]", "g");
        function ieKeyFix(key)
        {
            return key.replace(forbiddenCharsRegex, '___');
        }
        store.set = withIEStorage(function(storage, key, val)
        {
            key = ieKeyFix(key);
            if (val === undefined)
            {
                return store.remove(key);
            }
            storage.setAttribute(key, store.serialize(val));
            storage.save(localStorageName);
            return val;
        });
        store.get = withIEStorage(function(storage, key)
        {
            key = ieKeyFix(key);
            return store.deserialize(storage.getAttribute(key));
        });
        store.remove = withIEStorage(function(storage, key)
        {
            key = ieKeyFix(key);
            storage.removeAttribute(key);
            storage.save(localStorageName);
        });
        store.clear = withIEStorage(function(storage)
        {
            var attributes = storage.XMLDocument.documentElement.attributes;
            storage.load(localStorageName);
            for ( var i = 0, attr; attr = attributes[i]; i++)
            {
                storage.removeAttribute(attr.name);
            }
            storage.save(localStorageName);
        });
        store.getAll = withIEStorage(function(storage)
        {
            var attributes = storage.XMLDocument.documentElement.attributes;
            var ret = {};
            for ( var i = 0, attr; attr = attributes[i]; ++i)
            {
                var key = ieKeyFix(attr.name);
                ret[attr.name] = store.deserialize(storage.getAttribute(key));
            }
            return ret;
        });
    };
    
    try
    {
        store.set(namespace, namespace);
        if (store.get(namespace) != namespace)
        {
            store.disabled = true;
        }
        store.remove(namespace);
    }
    catch (e)
    {
        store.disabled = true;
    }
    store.enabled = !store.disabled;

    module.exports = store;

});