Storage.prototype_extension

Storage.prototype extension to store all kinds of data.

This script should not be not be installed directly. It is a library for other scripts to include with the meta directive // @require https://update.greasyfork.org/scripts/7573/47933/Storageprototype_extension.js

/***************************************************************************************
****************************************************************************************
*****
*****   Storage.prototype extension to store all kinds of data
*****
*****   NOTE: Nested functions in objects are not supported (ignored).
*****
*****   This library extends the Storage object used by localStorage and sessionStorage
*****   to allow them to store all types of javascript variables with some optimizations.
*****
*****      sessionStorage maintains a separate storage area for each given origin that's
*****      available for the duration of the page session (as long as the browser is open,
*****      including page reloads and restores).
*****
*****      localStorage does the same thing,
*****      but persists even when the browser is closed and reopened.
*****
*****   Usage:
*****       localStorage.set            (key, value);
*****       sessionStorage.set          (key, value);
*****
*****       var x = localStorage.get    (key, defaultValue);
*****       var y = sessionStorage.get  (key, defaultValue);
*****
*****   Size:
*****       Storage.getSize             ();
*****
*****   Test mode:
*****       Storage.runTestCases        ();
*****
*/

(function() {

    var BOOLEAN_MARKER = 'b';
    var NUMBER_MARKER = 'n';
    var STRING_MARKER = 's';
    var JSON_MARKER = 'j';
    var FUNCTION_MARKER = 'f';

    var TEST_CASES = {
        t00: true,
        t01: false,
        t02: 0,
        t03: 1,
        t04: 2147483646,
        t05: -2147483646,
        t06: "",
        t07: !0,
        t08: !1,
        t09: null,
        t10: undefined,
        t11: "true",
        t12: "false",
        t13: {
            t131: "t131",
            t132: {
                t1321: 't621',
                t1322: true,
                t1323: this.t1321,  // ignored
                t1324: 1324
            },
            t133: undefined,        // ignored
            t134: function(){
                console.log("t134");
            }                       // ignored
        },
        t14: function(){
                console.log("t14");
        },                          // fine
        t15: {
            t151: function (){
                console.log("t151");
            }                       // ignored
        }                           // empty object
    };

    //--- Check that the environment is proper.
    if (typeof(window.Storage) != "function")
        console.error('Storage is not supported! This library requires your browser to support the Web Storage function.');
    if (typeof(window.localStorage) != "object")
        console.error('This library requires localStorage support. Your current browser does not support localStorage!');
    if (typeof(window.sessionStorage) != "object")
        console.warn('Your browser does not support sessionStorage. Store locally only!');


    /*--- set (key, value)
        The Storage object stores only strings, like a cookie,
        but in a more intuitive way.

        This function extends that to allow storing any data type.

        Parameters:
            key
                String: The unique (within this domain) name for this value.
                Should be restricted to valid Javascript identifier characters.

            value
                Any valid javascript value. Strings in JavaScript are UTF-16,
                so each character requires two bytes of memory.
                This means that while many browsers have a 5 MB limit,
                you can only store 2.5 M characters. This function logs an
                error if your browser limit is exceeded.

        Returns:
            When browser limit has been reached...
                undefined and an error is thrown wherever possible.

            In all other cases...
                undefined only.
    */
    Storage.prototype.set = function(key, value) {

        if (typeof key != "string") {
            console.error('Illegal key passed to Storage.set(). Name: ' + key + ' Value: ' + value);
            return;
        }

        if(value == void 0){
            console.error('Illegal value sent to Storage.set(). Name: ' + key + ' Value: ' + value);
            return;
        }

        if (/[^\w _-]/.test(key)) {
            console.warn('Suspect, probably illegal, key passed to Storage.set(). Name: ' + key + ' Value: ' + value);
        }

        var safeStr = false;
        switch (typeof value) {
            case 'boolean':
                safeStr = BOOLEAN_MARKER + (value ? '!0' : '!1');
                break;
            case 'string':
                safeStr = STRING_MARKER + value;
                break;
            case 'number':
                safeStr = NUMBER_MARKER + value;
                break;
            case 'object':
                safeStr = JSON_MARKER + JSON.stringify(value);
                break;
            case 'function':
                safeStr = FUNCTION_MARKER + value.toString();
                break;
            default:
                console.error('Unknown type in Storage.set()!');
                break;
        }

        try {
            if(safeStr)
                this.setItem(key, safeStr);
        } catch(err){
             console.error("Problem occurred while saving: " + err);
        }
    };  //-- End of set()


    /*--- get (key, defaultValue)
        The Storage object retrieves only strings, like a cookie,
        but in a more intuitive way.

        This function extends that to allow retrieving
        stored data from the correct data type.

        Parameters:
            key
                String: The unique (within this domain) name for this value.
                Should be restricted to valid Javascript identifier characters.

            defaultValue
                Optional. Any value to be returned, when no value has previously
                been set.

        Returns:
            When this name has been set...
                The variable or function value as previously set.

            When this name has not been set and a default is provided...
                The value passed in as a default.

            When this name has not been set and default is not provided...
                undefined
     */
    Storage.prototype.get = function(key, defaultValue) {
        var value = this.getItem(key);

        if(value == void 0)
            return defaultValue;

        switch (value[0]) {
            case 'b':
                return eval(value.substr(1));
                break;
            case 's':
                return value.substr(1);
                break;
            case 'n':
            case 'j':
                return JSON.parse(value.substr(1));
                break;
            case 'f':
                return eval('(' + value.substr(1) + ')');
                break;
            default:
                console.error('Unknown type in Storage.get()! Name: ' + key + ' DefaultValue: ' + defaultValue);
                break;
        }

        return undefined;
    }; //-- End of get()

    /*--- getSize ()
        Each separate storage object when local and session are instantiated
        can hold up to 5 MB of stringified data. This is an approximation
        of the current local or session object size in bytes.

        Parameters:
            none

        Returns:
            int showing the length of the local / session object in bytes.
     */
    Storage.prototype.getSize = function () {

        var size = 0;
        for (var i = 0; i < this.length; i++) {
            var key = this.key(i);
            size += lengthInUtf8Bytes(this.getItem(key));
        }
        return size;

        function lengthInUtf8Bytes(str) {
            // Matches only the 10.. bytes that are non-initial characters in a multi-byte sequence.
            var m = encodeURIComponent(str).match(/%[89ABab]/g);
            return str.length + (m ? m.length : 0);
        }
    };

    /*--- runTests ()
        Tests setting and retrieving values of all types on the Storage
        object with this extension.

        Parameters:
            none

        Returns:
            undefined and console output with test results.
     */
    Storage.prototype.runTests = function(){

        for(var testCase in TEST_CASES){
            if(TEST_CASES.hasOwnProperty(testCase)){

                try{

                    var testValue = TEST_CASES[testCase];
                    console.log("Setting", testCase, "with value", testValue, "(", typeof testValue, ")");

                    this.set(testCase, testValue);

                    console.log("Variable successfully set. Retrieving...");

                    var retrieved = this.get(testCase);
                    console.log("Retrieved", testCase, "with value", retrieved, "(", typeof retrieved, ")");

                    testValue === retrieved && (typeof testValue == typeof retrieved)
                        ? console.info("Test case succeeded.")
                        : console.warn("Test case failed: expected",
                        testValue, "of type", typeof testValue, "got", retrieved, "of type", typeof retrieved);

                } catch (err){
                    console.error("Test case failed:", err);
                }

            }
        }
    };
}());