<commit_before>/*
 * ----------------------------- JSTORAGE -------------------------------------
 * Simple local storage wrapper to save data on the browser side, supporting
 * all major browsers - IE6+, Firefox2+, Safari4+, Chrome4+ and Opera 10.5+
 *
 * Copyright (c) 2010 Andris Reinman, andris.reinman@gmail.com
 * Project homepage: www.jstorage.info
 *
 * Licensed under MIT-style license:
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */

/**
 * $.jStorage
 *
 * USAGE:
 *
 * jStorage requires Prototype, MooTools or jQuery! If jQuery is used, then
 * jQuery-JSON (http://code.google.com/p/jquery-json/) is also needed.
 * (jQuery-JSON needs to be loaded BEFORE jStorage!)
 *
 * Methods:
 *
 * -set(key, value)
 * $.jStorage.set(key, value) -> saves a value
 *
 * -get(key[, default])
 * value = $.jStorage.get(key [, default]) ->
 *    retrieves value if key exists, or default if it doesn't
 *
 * -deleteKey(key)
 * $.jStorage.deleteKey(key) -> removes a key from the storage
 *
 * -flush()
 * $.jStorage.flush() -> clears the cache
 *
 * -storageObj()
 * $.jStorage.storageObj() -> returns a read-ony copy of the actual storage
 *
 * -storageSize()
 * $.jStorage.storageSize() -> returns the size of the storage in bytes
 *
 * -index()
 * $.jStorage.index() -> returns the used keys as an array
 *
 * -storageAvailable()
 * $.jStorage.storageAvailable() -> returns true if storage is available
 *
 * -reInit()
 * $.jStorage.reInit() -> reloads the data from browser storage
 *
 * <value> can be any JSON-able value, including objects and arrays.
 *
 **/

(function($){
    if(!$ || !($.toJSON || Object.toJSON || window.JSON)){
        throw new Error("jQuery, MooTools or Prototype needs to be loaded before jStorage!");
    }

    var
        /* This is the object, that holds the cached values */
        _storage = {},

        /* Actual browser storage (localStorage or globalStorage['domain']) */
        _storage_service = {jStorage:"{}"},

        /* DOM element for older IE versions, holds userData behavior */
        _storage_elm = null,

        /* How much space does the storage take */
        _storage_size = 0,

        /* function to encode objects to JSON strings */
        json_encode = $.toJSON || Object.toJSON || (window.JSON && (JSON.encode || JSON.stringify)),

        /* function to decode objects from JSON strings */
        json_decode = $.evalJSON || (window.JSON && (JSON.decode || JSON.parse)) || function(str){
            return String(str).evalJSON();
        },

        /* which backend is currently used */
        _backend = false,

        /* Next check for TTL */
        _ttl_timeout,

        /**
         * XML encoding and decoding as XML nodes can't be JSON'ized
         * XML nodes are encoded and decoded if the node is the value to be saved
         * but not if it's as a property of another object
         * Eg. -
         *   $.jStorage.set("key", xmlNode);        // IS OK
         *   $.jStorage.set("key", {xml: xmlNode}); // NOT OK
         */
        _XMLService = {

            /**
             * Validates a XML node to be XML
             * based on jQuery.isXML function
             */
            isXML: function(elm){
                var documentElement = (elm ? elm.ownerDocument || elm : 0).documentElement;
                return documentElement ? documentElement.nodeName !== "HTML" : false;
            },

            /**
             * Encodes a XML node to string
             * based on http://www.mercurytide.co.uk/news/article/issues-when-working-ajax/
             */
            encode: function(xmlNode) {
                if(!this.isXML(xmlNode)){
                    return false;
                }
                try{ // Mozilla, Webkit, Opera
                    return new XMLSerializer().serializeToString(xmlNode);
                }catch(E1) {
                    try {  // IE
                        return xmlNode.xml;
                    }catch(E2){}
                }
                return false;
            },

            /**
             * Decodes a XML node from string
             * loosely based on http://outwestmedia.com/jquery-plugins/xmldom/
             */
            decode: function(xmlString){
                var dom_parser = ("DOMParser" in window && (new DOMParser()).parseFromString) ||
                        (window.ActiveXObject && function(_xmlString) {
                    var xml_doc = new ActiveXObject('Microsoft.XMLDOM');
                    xml_doc.async = 'false';
                    xml_doc.loadXML(_xmlString);
                    return xml_doc;
                }),
                resultXML;
                if(!dom_parser){
                    return false;
                }
                resultXML = dom_parser.call("DOMParser" in window && (new DOMParser()) || window, xmlString, 'text/xml');
                return this.isXML(resultXML)?resultXML:false;
            }
        };

    ////////////////////////// PRIVATE METHODS ////////////////////////

    /**
     * Initialization function. Detects if the browser supports DOM Storage
     * or userData behavior and behaves accordingly.
     * @returns undefined
     */
    function _init(){
        /* Check if browser supports localStorage */
        var localStorageReallyWorks = false;
        if("localStorage" in window){
            try {
                window.localStorage.setItem('_tmptest', 'tmpval');
                localStorageReallyWorks = true;
                window.localStorage.removeItem('_tmptest');
            } catch(BogusQuotaExceededErrorOnIos5) {
                // Thanks be to iOS5 Private Browsing mode which throws
                // QUOTA_EXCEEDED_ERRROR DOM Exception 22.
            }
        }
        if(localStorageReallyWorks){
            try {
                if(window.localStorage) {
                    _storage_service = window.localStorage;
                    _backend = "localStorage";
                }
            } catch(E3) {/* Firefox fails when touching localStorage and cookies are disabled */}
        }
        /* Check if browser supports globalStorage */
        else if("globalStorage" in window){
            try {
                if(window.globalStorage) {
                    _storage_service = window.globalStorage[window.location.hostname];
                    _backend = "globalStorage";
                }
            } catch(E4) {/* Firefox fails when touching localStorage and cookies are disabled */}
        }
        /* Check if browser supports userData behavior */
        else {
            _storage_elm = document.createElement('link');
            if(_storage_elm.addBehavior){

                /* Use a DOM element to act as userData storage */
                _storage_elm.style.behavior = 'url(#default#userData)';

                /* userData element needs to be inserted into the DOM! */
                document.getElementsByTagName('head')[0].appendChild(_storage_elm);

                _storage_elm.load("jStorage");
                var data = "{}";
                try{
                    data = _storage_elm.getAttribute("jStorage");
                }catch(E5){}
                _storage_service.jStorage = data;
                _backend = "userDataBehavior";
            }else{
                _storage_elm = null;
                return;
            }
        }

        _load_storage();

        // remove dead keys
        _handleTTL();
    }

    /**
     * Loads the data from the storage based on the supported mechanism
     * @returns undefined
     */
    function _load_storage(){
        /* if jStorage string is retrieved, then decode it */
        if(_storage_service.jStorage){
            try{
                _storage = json_decode(String(_storage_service.jStorage));
            }catch(E6){_storage_service.jStorage = "{}";}
        }else{
            _storage_service.jStorage = "{}";
        }
        _storage_size = _storage_service.jStorage?String(_storage_service.jStorage).length:0;
    }

    /**
     * This functions provides the "save" mechanism to store the jStorage object
     * @returns undefined
     */
    function _save(){
        try{
            _storage_service.jStorage = json_encode(_storage);
            // If userData is used as the storage engine, additional
            if(_storage_elm) {
                _storage_elm.setAttribute("jStorage",_storage_service.jStorage);
                _storage_elm.save("jStorage");
            }
            _storage_size = _storage_service.jStorage?String(_storage_service.jStorage).length:0;
        }catch(E7){/* probably cache is full, nothing is saved this way*/}
    }

    /**
     * Function checks if a key is set and is string or numberic
     */
    function _checkKey(key){
        if(!key || (typeof key != "string" && typeof key != "number")){
            throw new TypeError('Key name must be string or numeric');
        }
        if(key == "__jstorage_meta"){
            throw new TypeError('Reserved key name');
        }
        return true;
    }

    /**
     * Removes expired keys
     */
    function _handleTTL(){
        var curtime, i, TTL, nextExpire = Infinity, changed = false;

        clearTimeout(_ttl_timeout);

        if(!_storage.__jstorage_meta || typeof _storage.__jstorage_meta.TTL != "object"){
            // nothing to do here
            return;
        }

        curtime = +new Date();
        TTL = _storage.__jstorage_meta.TTL;
        for(i in TTL){
            if(TTL.hasOwnProperty(i)){
                if(TTL[i] <= curtime){
                    delete TTL[i];
                    delete _storage[i];
                    changed = true;
                }else if(TTL[i] < nextExpire){
                    nextExpire = TTL[i];
                }
            }
        }

        // set next check
        if(nextExpire != Infinity){
            _ttl_timeout = setTimeout(_handleTTL, nextExpire - curtime);
        }

        // save changes
        if(changed){
            _save();
        }
    }

    ////////////////////////// PUBLIC INTERFACE /////////////////////////

    $.jStorage = {
        /* Version number */
        version: "0.1.6.0",

        /**
         * Sets a key's value.
         *
         * @param {String} key - Key to set. If this value is not set or not
         *              a string an exception is raised.
         * @param value - Value to set. This can be any value that is JSON
         *              compatible (Numbers, Strings, Objects etc.).
         * @returns the used value
         */
        set: function(key, value){
            _checkKey(key);
            if(_XMLService.isXML(value)){
                value = {_is_xml:true,xml:_XMLService.encode(value)};
            }
            _storage[key] = value;
            _save();
            return value;
        },

        /**
         * Looks up a key in cache
         *
         * @param {String} key - Key to look up.
         * @param {mixed} def - Default value to return, if key didn't exist.
         * @returns the key value, default value or <null>
         */
        get: function(key, def){
            _checkKey(key);
            if(key in _storage){
                if(_storage[key] && typeof _storage[key] == "object" &&
                        _storage[key]._is_xml &&
                            _storage[key]._is_xml){
                    return _XMLService.decode(_storage[key].xml);
                }else{
                    return _storage[key];
                }
            }
            return typeof(def) == 'undefined' ? null : def;
        },

        /**
         * Deletes a key from cache.
         *
         * @param {String} key - Key to delete.
         * @returns true if key existed or false if it didn't
         */
        deleteKey: function(key){
            _checkKey(key);
            if(key in _storage){
                delete _storage[key];
                // remove from TTL list
                if(_storage.__jstorage_meta &&
                  typeof _storage.__jstorage_meta.TTL == "object" &&
                  key in _storage.__jstorage_meta.TTL){
                    delete _storage.__jstorage_meta.TTL[key];
                }
                _save();
                return true;
            }
            return false;
        },

        /**
         * Sets a TTL for a key, or remove it if ttl value is 0 or below
         *
         * @param {String} key - key to set the TTL for
         * @param {Number} ttl - TTL timeout in milliseconds
         * @returns true if key existed or false if it didn't
         */
        setTTL: function(key, ttl){
            var curtime = +new Date();
            _checkKey(key);
            ttl = Number(ttl) || 0;
            if(key in _storage){

                if(!_storage.__jstorage_meta){
                    _storage.__jstorage_meta = {};
                }
                if(!_storage.__jstorage_meta.TTL){
                    _storage.__jstorage_meta.TTL = {};
                }

                // Set TTL value for the key
                if(ttl>0){
                    _storage.__jstorage_meta.TTL[key] = curtime + ttl;
                }else{
                    delete _storage.__jstorage_meta.TTL[key];
                }

                _save();

                _handleTTL();
                return true;
            }
            return false;
        },

        /**
         * Deletes everything in cache.
         *
         * @return true
         */
        flush: function(){
            _storage = {};
            _save();
            return true;
        },

        /**
         * Returns a read-only copy of _storage
         *
         * @returns Object
        */
        storageObj: function(){
            function F() {}
            F.prototype = _storage;
            return new F();
        },

        /**
         * Returns an index of all used keys as an array
         * ['key1', 'key2',..'keyN']
         *
         * @returns Array
        */
        index: function(){
            var index = [], i;
            for(i in _storage){
                if(_storage.hasOwnProperty(i) && i != "__jstorage_meta"){
                    index.push(i);
                }
            }
            return index;
        },

        /**
         * How much space in bytes does the storage take?
         *
         * @returns Number
         */
        storageSize: function(){
            return _storage_size;
        },

        /**
         * Which backend is currently in use?
         *
         * @returns String
         */
        currentBackend: function(){
            return _backend;
        },

        /**
         * Test if storage is available
         *
         * @returns Boolean
         */
        storageAvailable: function(){
            return !!_backend;
        },

        /**
         * Reloads the data from browser storage
         *
         * @returns undefined
         */
        reInit: function(){
            var new_storage_elm, data;
            if(_storage_elm && _storage_elm.addBehavior){
                new_storage_elm = document.createElement('link');

                _storage_elm.parentNode.replaceChild(new_storage_elm, _storage_elm);
                _storage_elm = new_storage_elm;

                /* Use a DOM element to act as userData storage */
                _storage_elm.style.behavior = 'url(#default#userData)';

                /* userData element needs to be inserted into the DOM! */
                document.getElementsByTagName('head')[0].appendChild(_storage_elm);

                _storage_elm.load("jStorage");
                data = "{}";
                try{
                    data = _storage_elm.getAttribute("jStorage");
                }catch(E5){}
                _storage_service.jStorage = data;
                _backend = "userDataBehavior";
            }

            _load_storage();
        }
    };

    // Initialize jStorage
    _init();

})(window.jQuery || window.$);<commit_msg>added TTL support<commit_after>/*
 * ----------------------------- JSTORAGE -------------------------------------
 * Simple local storage wrapper to save data on the browser side, supporting
 * all major browsers - IE6+, Firefox2+, Safari4+, Chrome4+ and Opera 10.5+
 *
 * Copyright (c) 2010 Andris Reinman, andris.reinman@gmail.com
 * Project homepage: www.jstorage.info
 *
 * Licensed under MIT-style license:
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */

/**
 * $.jStorage
 *
 * USAGE:
 *
 * jStorage requires Prototype, MooTools or jQuery! If jQuery is used, then
 * jQuery-JSON (http://code.google.com/p/jquery-json/) is also needed.
 * (jQuery-JSON needs to be loaded BEFORE jStorage!)
 *
 * Methods:
 *
 * -set(key, value)
 * $.jStorage.set(key, value) -> saves a value
 *
 * -get(key[, default])
 * value = $.jStorage.get(key [, default]) ->
 *    retrieves value if key exists, or default if it doesn't
 *
 * -deleteKey(key)
 * $.jStorage.deleteKey(key) -> removes a key from the storage
 *
 * -flush()
 * $.jStorage.flush() -> clears the cache
 *
 * -storageObj()
 * $.jStorage.storageObj() -> returns a read-ony copy of the actual storage
 *
 * -storageSize()
 * $.jStorage.storageSize() -> returns the size of the storage in bytes
 *
 * -index()
 * $.jStorage.index() -> returns the used keys as an array
 *
 * -storageAvailable()
 * $.jStorage.storageAvailable() -> returns true if storage is available
 *
 * -reInit()
 * $.jStorage.reInit() -> reloads the data from browser storage
 *
 * <value> can be any JSON-able value, including objects and arrays.
 *
 **/

(function($){
    if(!$ || !($.toJSON || Object.toJSON || window.JSON)){
        throw new Error("jQuery, MooTools or Prototype needs to be loaded before jStorage!");
    }

    var
        /* This is the object, that holds the cached values */
        _storage = {},

        /* Actual browser storage (localStorage or globalStorage['domain']) */
        _storage_service = {jStorage:"{}"},

        /* DOM element for older IE versions, holds userData behavior */
        _storage_elm = null,

        /* How much space does the storage take */
        _storage_size = 0,

        /* function to encode objects to JSON strings */
        json_encode = $.toJSON || Object.toJSON || (window.JSON && (JSON.encode || JSON.stringify)),

        /* function to decode objects from JSON strings */
        json_decode = $.evalJSON || (window.JSON && (JSON.decode || JSON.parse)) || function(str){
            return String(str).evalJSON();
        },

        /* which backend is currently used */
        _backend = false,

        /* Next check for TTL */
        _ttl_timeout,

        /**
         * XML encoding and decoding as XML nodes can't be JSON'ized
         * XML nodes are encoded and decoded if the node is the value to be saved
         * but not if it's as a property of another object
         * Eg. -
         *   $.jStorage.set("key", xmlNode);        // IS OK
         *   $.jStorage.set("key", {xml: xmlNode}); // NOT OK
         */
        _XMLService = {

            /**
             * Validates a XML node to be XML
             * based on jQuery.isXML function
             */
            isXML: function(elm){
                var documentElement = (elm ? elm.ownerDocument || elm : 0).documentElement;
                return documentElement ? documentElement.nodeName !== "HTML" : false;
            },

            /**
             * Encodes a XML node to string
             * based on http://www.mercurytide.co.uk/news/article/issues-when-working-ajax/
             */
            encode: function(xmlNode) {
                if(!this.isXML(xmlNode)){
                    return false;
                }
                try{ // Mozilla, Webkit, Opera
                    return new XMLSerializer().serializeToString(xmlNode);
                }catch(E1) {
                    try {  // IE
                        return xmlNode.xml;
                    }catch(E2){}
                }
                return false;
            },

            /**
             * Decodes a XML node from string
             * loosely based on http://outwestmedia.com/jquery-plugins/xmldom/
             */
            decode: function(xmlString){
                var dom_parser = ("DOMParser" in window && (new DOMParser()).parseFromString) ||
                        (window.ActiveXObject && function(_xmlString) {
                    var xml_doc = new ActiveXObject('Microsoft.XMLDOM');
                    xml_doc.async = 'false';
                    xml_doc.loadXML(_xmlString);
                    return xml_doc;
                }),
                resultXML;
                if(!dom_parser){
                    return false;
                }
                resultXML = dom_parser.call("DOMParser" in window && (new DOMParser()) || window, xmlString, 'text/xml');
                return this.isXML(resultXML)?resultXML:false;
            }
        };

    ////////////////////////// PRIVATE METHODS ////////////////////////

    /**
     * Initialization function. Detects if the browser supports DOM Storage
     * or userData behavior and behaves accordingly.
     * @returns undefined
     */
    function _init(){
        /* Check if browser supports localStorage */
        var localStorageReallyWorks = false;
        if("localStorage" in window){
            try {
                window.localStorage.setItem('_tmptest', 'tmpval');
                localStorageReallyWorks = true;
                window.localStorage.removeItem('_tmptest');
            } catch(BogusQuotaExceededErrorOnIos5) {
                // Thanks be to iOS5 Private Browsing mode which throws
                // QUOTA_EXCEEDED_ERRROR DOM Exception 22.
            }
        }
        if(localStorageReallyWorks){
            try {
                if(window.localStorage) {
                    _storage_service = window.localStorage;
                    _backend = "localStorage";
                }
            } catch(E3) {/* Firefox fails when touching localStorage and cookies are disabled */}
        }
        /* Check if browser supports globalStorage */
        else if("globalStorage" in window){
            try {
                if(window.globalStorage) {
                    _storage_service = window.globalStorage[window.location.hostname];
                    _backend = "globalStorage";
                }
            } catch(E4) {/* Firefox fails when touching localStorage and cookies are disabled */}
        }
        /* Check if browser supports userData behavior */
        else {
            _storage_elm = document.createElement('link');
            if(_storage_elm.addBehavior){

                /* Use a DOM element to act as userData storage */
                _storage_elm.style.behavior = 'url(#default#userData)';

                /* userData element needs to be inserted into the DOM! */
                document.getElementsByTagName('head')[0].appendChild(_storage_elm);

                _storage_elm.load("jStorage");
                var data = "{}";
                try{
                    data = _storage_elm.getAttribute("jStorage");
                }catch(E5){}
                _storage_service.jStorage = data;
                _backend = "userDataBehavior";
            }else{
                _storage_elm = null;
                return;
            }
        }

        _load_storage();

        // remove dead keys
        _handleTTL();
    }

    /**
     * Loads the data from the storage based on the supported mechanism
     * @returns undefined
     */
    function _load_storage(){
        /* if jStorage string is retrieved, then decode it */
        if(_storage_service.jStorage){
            try{
                _storage = json_decode(String(_storage_service.jStorage));
            }catch(E6){_storage_service.jStorage = "{}";}
        }else{
            _storage_service.jStorage = "{}";
        }
        _storage_size = _storage_service.jStorage?String(_storage_service.jStorage).length:0;
    }

    /**
     * This functions provides the "save" mechanism to store the jStorage object
     * @returns undefined
     */
    function _save(){
        try{
            _storage_service.jStorage = json_encode(_storage);
            // If userData is used as the storage engine, additional
            if(_storage_elm) {
                _storage_elm.setAttribute("jStorage",_storage_service.jStorage);
                _storage_elm.save("jStorage");
            }
            _storage_size = _storage_service.jStorage?String(_storage_service.jStorage).length:0;
        }catch(E7){/* probably cache is full, nothing is saved this way*/}
    }

    /**
     * Function checks if a key is set and is string or numberic
     */
    function _checkKey(key){
        if(!key || (typeof key != "string" && typeof key != "number")){
            throw new TypeError('Key name must be string or numeric');
        }
        if(key == "__jstorage_meta"){
            throw new TypeError('Reserved key name');
        }
        return true;
    }

    /**
     * Removes expired keys
     */
    function _handleTTL(){
        var curtime, i, TTL, nextExpire = Infinity, changed = false;

        clearTimeout(_ttl_timeout);

        if(!_storage.__jstorage_meta || typeof _storage.__jstorage_meta.TTL != "object"){
            // nothing to do here
            return;
        }

        curtime = +new Date();
        TTL = _storage.__jstorage_meta.TTL;
        for(i in TTL){
            if(TTL.hasOwnProperty(i)){
                if(TTL[i] <= curtime){
                    delete TTL[i];
                    delete _storage[i];
                    changed = true;
                }else if(TTL[i] < nextExpire){
                    nextExpire = TTL[i];
                }
            }
        }

        // set next check
        if(nextExpire != Infinity){
            _ttl_timeout = setTimeout(_handleTTL, nextExpire - curtime);
        }

        // save changes
        if(changed){
            _save();
        }
    }

    ////////////////////////// PUBLIC INTERFACE /////////////////////////

    $.jStorage = {
        /* Version number */
        version: "0.1.6.0",

        /**
         * Sets a key's value.
         *
         * @param {String} key - Key to set. If this value is not set or not
         *              a string an exception is raised.
         * @param value - Value to set. This can be any value that is JSON
         *              compatible (Numbers, Strings, Objects etc.).
         * @returns the used value
         */
        set: function(key, value){
            _checkKey(key);
            if(_XMLService.isXML(value)){
                value = {_is_xml:true,xml:_XMLService.encode(value)};
            }
            _storage[key] = value;
            _save();
            return value;
        },

        /**
         * Looks up a key in cache
         *
         * @param {String} key - Key to look up.
         * @param {mixed} def - Default value to return, if key didn't exist.
         * @returns the key value, default value or <null>
         */
        get: function(key, def){
            _checkKey(key);
            if(key in _storage){
                if(_storage[key] && typeof _storage[key] == "object" &&
                        _storage[key]._is_xml &&
                            _storage[key]._is_xml){
                    return _XMLService.decode(_storage[key].xml);
                }else{
                    return _storage[key];
                }
            }
            return typeof(def) == 'undefined' ? null : def;
        },

        /**
         * Deletes a key from cache.
         *
         * @param {String} key - Key to delete.
         * @returns true if key existed or false if it didn't
         */
        deleteKey: function(key){
            _checkKey(key);
            if(key in _storage){
                delete _storage[key];
                // remove from TTL list
                if(_storage.__jstorage_meta &&
                  typeof _storage.__jstorage_meta.TTL == "object" &&
                  key in _storage.__jstorage_meta.TTL){
                    delete _storage.__jstorage_meta.TTL[key];
                }
                _save();
                return true;
            }
            return false;
        },

        /**
         * Sets a TTL for a key, or remove it if ttl value is 0 or below
         *
         * @param {String} key - key to set the TTL for
         * @param {Number} ttl - TTL timeout in milliseconds
         * @returns true if key existed or false if it didn't
         */
        setTTL: function(key, ttl){
            var curtime = +new Date();
            _checkKey(key);
            ttl = Number(ttl) || 0;
            if(key in _storage){

                if(!_storage.__jstorage_meta){
                    _storage.__jstorage_meta = {};
                }
                if(!_storage.__jstorage_meta.TTL){
                    _storage.__jstorage_meta.TTL = {};
                }

                // Set TTL value for the key
                if(ttl>0){
                    _storage.__jstorage_meta.TTL[key] = curtime + ttl;
                }else{
                    delete _storage.__jstorage_meta.TTL[key];
                }

                _save();

                _handleTTL();
                return true;
            }
            return false;
        },

        /**
         * Deletes everything in cache.
         *
         * @return true
         */
        flush: function(){
            _storage = {};
            _save();
            return true;
        },

        /**
         * Returns a read-only copy of _storage
         *
         * @returns Object
        */
        storageObj: function(){
            function F() {}
            F.prototype = _storage;
            return new F();
        },

        /**
         * Returns an index of all used keys as an array
         * ['key1', 'key2',..'keyN']
         *
         * @returns Array
        */
        index: function(){
            var index = [], i;
            for(i in _storage){
                if(_storage.hasOwnProperty(i) && i != "__jstorage_meta"){
                    index.push(i);
                }
            }
            return index;
        },

        /**
         * How much space in bytes does the storage take?
         *
         * @returns Number
         */
        storageSize: function(){
            return _storage_size;
        },

        /**
         * Which backend is currently in use?
         *
         * @returns String
         */
        currentBackend: function(){
            return _backend;
        },

        /**
         * Test if storage is available
         *
         * @returns Boolean
         */
        storageAvailable: function(){
            return !!_backend;
        },

        /**
         * Reloads the data from browser storage
         *
         * @returns undefined
         */
        reInit: function(){
            var new_storage_elm, data;
            if(_storage_elm && _storage_elm.addBehavior){
                new_storage_elm = document.createElement('link');

                _storage_elm.parentNode.replaceChild(new_storage_elm, _storage_elm);
                _storage_elm = new_storage_elm;

                /* Use a DOM element to act as userData storage */
                _storage_elm.style.behavior = 'url(#default#userData)';

                /* userData element needs to be inserted into the DOM! */
                document.getElementsByTagName('head')[0].appendChild(_storage_elm);

                _storage_elm.load("jStorage");
                data = "{}";
                try{
                    data = _storage_elm.getAttribute("jStorage");
                }catch(E5){}
                _storage_service.jStorage = data;
                _backend = "userDataBehavior";
            }

            _load_storage();
        }
    };

    // Initialize jStorage
    _init();

})(window.jQuery || window.$);<|endoftext|>


<commit_before>var testHarnessTests = [
  'blink/2-keyframes-with-offsets.html',
  'blink/3-keyframes-with-offsets.html',
  'blink/add-keyframes.html',
  'blink/insufficient-keyframes.html',
  'blink/simple-keyframes.html',
  'blink/keyframes-with-null-offsets.html',
  'blink/keyframe-properties.html',
  'blink/out-of-order-keyframes.html',
  'blink/same-offset-keyframes.html',
  'blink/eased-keyframes.html',
  'blink/get-animation-players.html',
];

var testHarnessFailures = [
  'blink/get-css-players.html',
];

var interpolationTests = [
  'blink/interpolation/background-color-interpolation.html',
  'blink/interpolation/background-position-interpolation.html',
  'blink/interpolation/background-position-origin-interpolation.html',
  'blink/interpolation/background-size-interpolation.html',
  'blink/interpolation/border-color-interpolation.html',
  'blink/interpolation/border-radius-interpolation.html',
  'blink/interpolation/border-width-interpolation.html',
  'blink/interpolation/bottom-interpolation.html',
  'blink/interpolation/box-shadow-interpolation.html',
  'blink/interpolation/calc-interpolation.html',
  'blink/interpolation/color-interpolation.html',
  'blink/interpolation/flex-interpolation.html',
  'blink/interpolation/font-size-interpolation.html',
  'blink/interpolation/font-weight-interpolation.html',
  'blink/interpolation/height-interpolation.html',
  'blink/interpolation/left-interpolation.html',
  'blink/interpolation/letter-spacing-interpolation.html',
  'blink/interpolation/margin-interpolation.html',
  'blink/interpolation/max-height-interpolation.html',
  'blink/interpolation/max-width-interpolation.html',
  'blink/interpolation/min-height-interpolation.html',
  'blink/interpolation/min-width-interpolation.html',
  'blink/interpolation/object-position-interpolation.html',
  'blink/interpolation/opacity-interpolation.html',
  'blink/interpolation/orphans-interpolation.html',
  'blink/interpolation/outline-color-interpolation.html',
  'blink/interpolation/outline-offset-interpolation.html',
  'blink/interpolation/outline-width-interpolation.html',
  'blink/interpolation/padding-interpolation.html',
  'blink/interpolation/perspective-interpolation.html',
  'blink/interpolation/perspective-origin-interpolation.html',
  'blink/interpolation/right-interpolation.html',
  'blink/interpolation/shape-image-threshold.html',
  'blink/interpolation/shape-margin.html',
  'blink/interpolation/shape-outside.html',
  'blink/interpolation/text-decoration-color-interpolation.html',
  'blink/interpolation/text-indent-interpolation.html',
  'blink/interpolation/text-shadow-interpolation.html',
  'blink/interpolation/top-interpolation.html',
  'blink/interpolation/transform-none-interpolation.html',
  'blink/interpolation/transform-origin-interpolation.html',
  'blink/interpolation/transform-perspective-interpolation.html',
  'blink/interpolation/transform-rotate-interpolation.html',
  'blink/interpolation/transform-scale-interpolation.html',
  'blink/interpolation/transform-skew-interpolation.html',
  'blink/interpolation/transform-translate-interpolation.html',
  'blink/interpolation/vertical-align-interpolation.html',
  'blink/interpolation/viewport-unit-interpolation.html',
  'blink/interpolation/visibility-interpolation.html',
  'blink/interpolation/widows-interpolation.html',
  'blink/interpolation/width-interpolation.html',
  'blink/interpolation/word-spacing-interpolation.html',
  'blink/interpolation/z-index-interpolation.html',
  'blink/interpolation/zoom-iterpolation.html',
];

var interpolationFailures = [
  'blink/interpolation/background-image-interpolation.html',
  'blink/interpolation/border-image-outset-interpolation.html',
  'blink/interpolation/border-image-slice-interpolation.html',
  'blink/interpolation/border-image-source-interpolation.html',
  'blink/interpolation/border-image-width-interpolation.html',
  'blink/interpolation/border-spacing-interpolation.html',
  'blink/interpolation/clip-interpolation.html',
  'blink/interpolation/filter-interpolation.html',
  'blink/interpolation/line-height-interpolation.html',
  'blink/interpolation/list-style-image-interpolation.html',
  'blink/interpolation/transform-matrix-interpolation.html',
];
<commit_msg>Mark clip and line-height interpolation tests as passing<commit_after>var testHarnessTests = [
  'blink/2-keyframes-with-offsets.html',
  'blink/3-keyframes-with-offsets.html',
  'blink/add-keyframes.html',
  'blink/insufficient-keyframes.html',
  'blink/simple-keyframes.html',
  'blink/keyframes-with-null-offsets.html',
  'blink/keyframe-properties.html',
  'blink/out-of-order-keyframes.html',
  'blink/same-offset-keyframes.html',
  'blink/eased-keyframes.html',
  'blink/get-animation-players.html',
];

var testHarnessFailures = [
  'blink/get-css-players.html',
];

var interpolationTests = [
  'blink/interpolation/background-color-interpolation.html',
  'blink/interpolation/background-position-interpolation.html',
  'blink/interpolation/background-position-origin-interpolation.html',
  'blink/interpolation/background-size-interpolation.html',
  'blink/interpolation/border-color-interpolation.html',
  'blink/interpolation/border-radius-interpolation.html',
  'blink/interpolation/border-width-interpolation.html',
  'blink/interpolation/bottom-interpolation.html',
  'blink/interpolation/box-shadow-interpolation.html',
  'blink/interpolation/calc-interpolation.html',
  'blink/interpolation/clip-interpolation.html',
  'blink/interpolation/color-interpolation.html',
  'blink/interpolation/flex-interpolation.html',
  'blink/interpolation/font-size-interpolation.html',
  'blink/interpolation/font-weight-interpolation.html',
  'blink/interpolation/height-interpolation.html',
  'blink/interpolation/left-interpolation.html',
  'blink/interpolation/letter-spacing-interpolation.html',
  'blink/interpolation/line-height-interpolation.html',
  'blink/interpolation/margin-interpolation.html',
  'blink/interpolation/max-height-interpolation.html',
  'blink/interpolation/max-width-interpolation.html',
  'blink/interpolation/min-height-interpolation.html',
  'blink/interpolation/min-width-interpolation.html',
  'blink/interpolation/object-position-interpolation.html',
  'blink/interpolation/opacity-interpolation.html',
  'blink/interpolation/orphans-interpolation.html',
  'blink/interpolation/outline-color-interpolation.html',
  'blink/interpolation/outline-offset-interpolation.html',
  'blink/interpolation/outline-width-interpolation.html',
  'blink/interpolation/padding-interpolation.html',
  'blink/interpolation/perspective-interpolation.html',
  'blink/interpolation/perspective-origin-interpolation.html',
  'blink/interpolation/right-interpolation.html',
  'blink/interpolation/shape-image-threshold.html',
  'blink/interpolation/shape-margin.html',
  'blink/interpolation/shape-outside.html',
  'blink/interpolation/text-decoration-color-interpolation.html',
  'blink/interpolation/text-indent-interpolation.html',
  'blink/interpolation/text-shadow-interpolation.html',
  'blink/interpolation/top-interpolation.html',
  'blink/interpolation/transform-none-interpolation.html',
  'blink/interpolation/transform-origin-interpolation.html',
  'blink/interpolation/transform-perspective-interpolation.html',
  'blink/interpolation/transform-rotate-interpolation.html',
  'blink/interpolation/transform-scale-interpolation.html',
  'blink/interpolation/transform-skew-interpolation.html',
  'blink/interpolation/transform-translate-interpolation.html',
  'blink/interpolation/vertical-align-interpolation.html',
  'blink/interpolation/viewport-unit-interpolation.html',
  'blink/interpolation/visibility-interpolation.html',
  'blink/interpolation/widows-interpolation.html',
  'blink/interpolation/width-interpolation.html',
  'blink/interpolation/word-spacing-interpolation.html',
  'blink/interpolation/z-index-interpolation.html',
  'blink/interpolation/zoom-iterpolation.html',
];

var interpolationFailures = [
  'blink/interpolation/background-image-interpolation.html',
  'blink/interpolation/border-image-outset-interpolation.html',
  'blink/interpolation/border-image-slice-interpolation.html',
  'blink/interpolation/border-image-source-interpolation.html',
  'blink/interpolation/border-image-width-interpolation.html',
  'blink/interpolation/border-spacing-interpolation.html',
  'blink/interpolation/filter-interpolation.html',
  'blink/interpolation/list-style-image-interpolation.html',
  'blink/interpolation/transform-matrix-interpolation.html',
];
<|endoftext|>


<commit_before>function appendSVG(document) {

    /*
     'Normal':[1,0,0,0,0, 0,1,0,0,0, 0,0,1,0,0, 0,0,0,1,0, 0,0,0,0,1],
     'Protanopia':[0.567,0.433,0,0,0, 0.558,0.442,0,0,0, 0,0.242,0.758,0,0, 0,0,0,1,0, 0,0,0,0,1],
     'Protanomaly':[0.817,0.183,0,0,0, 0.333,0.667,0,0,0, 0,0.125,0.875,0,0, 0,0,0,1,0, 0,0,0,0,1],
     'Deuteranopia':[0.625,0.375,0,0,0, 0.7,0.3,0,0,0, 0,0.3,0.7,0,0, 0,0,0,1,0, 0,0,0,0,1],
     'Deuteranomaly':[0.8,0.2,0,0,0, 0.258,0.742,0,0,0, 0,0.142,0.858,0,0, 0,0,0,1,0, 0,0,0,0,1],
     'Tritanopia':[0.95,0.05,0,0,0, 0,0.433,0.567,0,0, 0,0.475,0.525,0,0, 0,0,0,1,0, 0,0,0,0,1],
     'Tritanomaly':[0.967,0.033,0,0,0, 0,0.733,0.267,0,0, 0,0.183,0.817,0,0, 0,0,0,1,0, 0,0,0,0,1],
     'Achromatopsia':[0.299,0.587,0.114,0,0, 0.299,0.587,0.114,0,0, 0.299,0.587,0.114,0,0, 0,0,0,1,0, 0,0,0,0,1],
     'Achromatomaly':[0.618,0.320,0.062,0,0, 0.163,0.775,0.062,0,0, 0.163,0.320,0.516,0,0,0,0,0,1,0,0,0,0,0]
     */

    var svg = '<svg id="colorBlindSVG" version="1.1" xmlns="http://www.w3.org/2000/svg" baseProfile="full"> <filter id="protanopia"> <feColorMatrix type="matrix" values="0.567, 0.433, 0, 0, 0, 0.558, 0.442, 0, 0, 0, 0, 0.242, 0.758, 0, 0, 0, 0, 0, 1, 0" in="SourceGraphic" /> </filter> <filter id="protanomaly"> <feColorMatrix type="matrix" values="0.817,0.183,0,0,0 0.333,0.667,0,0,0 0,0.125,0.875,0,0 0,0,0,1,0" in="SourceGraphic" /> </filter> <filter id="deuteranopia"> <feColorMatrix type="matrix" values="0.625,0.375,0,0,0 0.7,0.3,0,0,0 0,0.3,0.7,0,0 0,0,0,1,0" in="SourceGraphic" /> </filter> <filter id="deuteranomaly"> <feColorMatrix type="matrix" values="0.8,0.2,0,0,0 0.258,0.742,0,0,0 0,0.142,0.858,0,0 0,0,0,1,0" in="SourceGraphic" /> </filter> <filter id="tritanopia"> <feColorMatrix type="matrix" values="0.95,0.05,0,0,0 0,0.433,0.567,0,0 0,0.475,0.525,0,0 0,0,0,1,0" in="SourceGraphic" /> </filter> <filter id="tritanomaly"> <feColorMatrix type="matrix" values="0.967,0.033,0,0,0 0,0.733,0.267,0,0 0,0.183,0.817,0,0 0,0,0,1,0" in="SourceGraphic" /> </filter> <filter id="achromatopsia"> <feColorMatrix type="matrix" values="0.299,0.587,0.114,0,0 0.299,0.587,0.114,0,0 0.299,0.587,0.114,0,0 0,0,0,1,0" in="SourceGraphic" /> </filter> <filter id="achromatomaly"> <feColorMatrix type="matrix" values="0.618,0.320,0.062,0,0 0.163,0.775,0.062,0,0 0.163,0.320,0.516,0,0 0,0,0,1,0" in="SourceGraphic" /> </filter> </svg>';

    var blockColorblindContent = document.getElementById("blockColorblindContent");
    if (blockColorblindContent !== undefined && blockColorblindContent !== null) {
        blockColorblindContent.parentNode.removeChild(blockColorblindContent);
    }

    var iDiv = document.createElement('div');
    iDiv.id = 'blockColorblindContent';
    iDiv.innerHTML = svg;
    iDiv.style.display = 'none'; //Fix issue "blockColorblindContent" is showing and taking up space on some websites #5
    document.getElementsByTagName('body')[0].appendChild(iDiv);
}

function changeColors(type) {
    appendSVG(document);
    revertColors(document);

    var css = 'html {' +
        'filter: url(#' + type + '); -webkit-filter: url(#' + type + '); -moz-filter: url(#' + type + '); -o-filter: url(#' + type + '); -ms-filter: url(#' + type + '); ' +
        '}';

    applyingStyle(document, css);
}

function revertColors(document) {
    var css = 'html { -webkit-filter: none; -moz-filter: none; -o-filter: none; -ms-filter: none; }';
    applyingStyle(document, css);
}

function applyingStyle(document, css) {

    var head = document.getElementsByTagName('head')[0],
        style = document.createElement('style');

    style.type = 'text/css';
    if (style.styleSheet) {
        style.styleSheet.cssText = css;
    } else {
        style.appendChild(document.createTextNode(css));
    }
    head.appendChild(style);
}

function execute() {
    chrome.storage.sync.get('colorblindingValue', function (obj) {
        if (obj.colorblindingValue === null || obj.colorblindingValue === undefined) {
            obj.colorblindingValue = "normal";
            chrome.storage.sync.set({'colorblindingValue': obj.colorblindingValue});
        }
        changeColors(obj.colorblindingValue);
    });
}
<commit_msg>Update colorblinding.js - fix <commit_after>function appendSVG(document) {

    /*
     'Normal':[1,0,0,0,0, 0,1,0,0,0, 0,0,1,0,0, 0,0,0,1,0, 0,0,0,0,1],
     'Protanopia':[0.567,0.433,0,0,0, 0.558,0.442,0,0,0, 0,0.242,0.758,0,0, 0,0,0,1,0, 0,0,0,0,1],
     'Protanomaly':[0.817,0.183,0,0,0, 0.333,0.667,0,0,0, 0,0.125,0.875,0,0, 0,0,0,1,0, 0,0,0,0,1],
     'Deuteranopia':[0.625,0.375,0,0,0, 0.7,0.3,0,0,0, 0,0.3,0.7,0,0, 0,0,0,1,0, 0,0,0,0,1],
     'Deuteranomaly':[0.8,0.2,0,0,0, 0.258,0.742,0,0,0, 0,0.142,0.858,0,0, 0,0,0,1,0, 0,0,0,0,1],
     'Tritanopia':[0.95,0.05,0,0,0, 0,0.433,0.567,0,0, 0,0.475,0.525,0,0, 0,0,0,1,0, 0,0,0,0,1],
     'Tritanomaly':[0.967,0.033,0,0,0, 0,0.733,0.267,0,0, 0,0.183,0.817,0,0, 0,0,0,1,0, 0,0,0,0,1],
     'Achromatopsia':[0.299,0.587,0.114,0,0, 0.299,0.587,0.114,0,0, 0.299,0.587,0.114,0,0, 0,0,0,1,0, 0,0,0,0,1],
     'Achromatomaly':[0.618,0.320,0.062,0,0, 0.163,0.775,0.062,0,0, 0.163,0.320,0.516,0,0,0,0,0,1,0,0,0,0,0]
     */

    var svg = '<svg id="colorBlindSVG" version="1.1" xmlns="http://www.w3.org/2000/svg" baseProfile="full"> <filter id="protanopia"> <feColorMatrix type="matrix" values="0.567, 0.433, 0, 0, 0, 0.558, 0.442, 0, 0, 0, 0, 0.242, 0.758, 0, 0, 0, 0, 0, 1, 0" in="SourceGraphic" /> </filter> <filter id="protanomaly"> <feColorMatrix type="matrix" values="0.817,0.183,0,0,0 0.333,0.667,0,0,0 0,0.125,0.875,0,0 0,0,0,1,0" in="SourceGraphic" /> </filter> <filter id="deuteranopia"> <feColorMatrix type="matrix" values="0.625,0.375,0,0,0 0.7,0.3,0,0,0 0,0.3,0.7,0,0 0,0,0,1,0" in="SourceGraphic" /> </filter> <filter id="deuteranomaly"> <feColorMatrix type="matrix" values="0.8,0.2,0,0,0 0.258,0.742,0,0,0 0,0.142,0.858,0,0 0,0,0,1,0" in="SourceGraphic" /> </filter> <filter id="tritanopia"> <feColorMatrix type="matrix" values="0.95,0.05,0,0,0 0,0.433,0.567,0,0 0,0.475,0.525,0,0 0,0,0,1,0" in="SourceGraphic" /> </filter> <filter id="tritanomaly"> <feColorMatrix type="matrix" values="0.967,0.033,0,0,0 0,0.733,0.267,0,0 0,0.183,0.817,0,0 0,0,0,1,0" in="SourceGraphic" /> </filter> <filter id="achromatopsia"> <feColorMatrix type="matrix" values="0.299,0.587,0.114,0,0 0.299,0.587,0.114,0,0 0.299,0.587,0.114,0,0 0,0,0,1,0" in="SourceGraphic" /> </filter> <filter id="achromatomaly"> <feColorMatrix type="matrix" values="0.618,0.320,0.062,0,0 0.163,0.775,0.062,0,0 0.163,0.320,0.516,0,0 0,0,0,1,0" in="SourceGraphic" /> </filter> </svg>';

    var blockColorblindContent = document.getElementById("blockColorblindContent");
    if (blockColorblindContent !== undefined && blockColorblindContent !== null) {
        blockColorblindContent.parentNode.removeChild(blockColorblindContent);
    }

    var iDiv = document.createElement('div');
    iDiv.id = 'blockColorblindContent';
    iDiv.innerHTML = svg;
    iDiv.style.display = 'none'; //Fix issue "blockColorblindContent" is showing and taking up space on some websites #5
    document.getElementsByTagName('body')[0].appendChild(iDiv);
}

function changeColors(type) {
    appendSVG(document);
    revertColors(document);

    var css = 'html {' +
        'filter: url(#' + type + '); -webkit-filter: url(#' + type + '); -moz-filter: url(#' + type + '); -o-filter: url(#' + type + '); -ms-filter: url(#' + type + '); ' +
        '}';

    applyingStyle(document, css);
}

function revertColors(document) {
    var css = 'html { -webkit-filter: none; -moz-filter: none; -o-filter: none; -ms-filter: none; } #blockColorblindContent { display: none; }';
    applyingStyle(document, css);
}

function applyingStyle(document, css) {

    var head = document.getElementsByTagName('head')[0],
        style = document.createElement('style');

    style.type = 'text/css';
    if (style.styleSheet) {
        style.styleSheet.cssText = css;
    } else {
        style.appendChild(document.createTextNode(css));
    }
    head.appendChild(style);
}

function execute() {
    chrome.storage.sync.get('colorblindingValue', function (obj) {
        if (obj.colorblindingValue === null || obj.colorblindingValue === undefined) {
            obj.colorblindingValue = "normal";
            chrome.storage.sync.set({'colorblindingValue': obj.colorblindingValue});
        }
        changeColors(obj.colorblindingValue);
    });
}
<|endoftext|>


<commit_before>/*
 * AjaxStream JQuery JavaScript Library v1.0.0
 *
 * Requires JQuery 1.11.2 or higher
 *
 * The MIT License (MIT)
 *
 * Copyright (c) 2015 Shane Tindle
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 *
 */

$.ajaxStream =
(function($) {
	// js GUID
	function _s() {
            return Math.floor((1 + Math.random()) * 0x10000)
                .toString(16)
                .substring(1);
        }

	function _g(n) {
		var _r = "";

		if (typeof n != "number")
			n = 5;

		for (var i = 0; i < n.length; i++)
			_r += _s() + "_";

        	return _r;
    	}

	// unique ids
	var _i = {
		o: _g(), // <form/>
		r: _g(), // <iframe/>
		i: _g() // <input type="file"/>
	};

	// form builder
	function makeForm(p) {
		// id unique to the page
		var i = _g(1) + new Date().getTime();
		var f = {
			o: _i.o + i
		};

		// return object
		return $("<form/>")
			.attr("method", "POST")
			.attr("name", f.o)
			.attr("id", f.o)
			.attr("enctype", "multipart/form-data")
			.css({
				position: "absolute",
				top: "-1200px",
				left: "-1200px"
			})
			.appendTo("body");
	}

	// frame builder
	function makeFrame() {
		// id unique to the page
		var i = _g(1) + new Date().getTime();
		var f = {
			r: _i.r + i
		};

		// return object
		return $("<iframe/>")
			.attr("id", f.r)
			.attr("name", f.r)
			.css({
				position: "absolute",
				top: -9999,
				left: -9999
			})
			.attr("src", "about:blank")
			.appendTo("body");
	}

	// input builder
	function makeInput(n, v) {
		return $("<input/>")
			.attr("name", n)
			.val(v);
	}

	// response handler
	function response(timeout, done, xhtml, frame, form, p) {
		var status;

		try {
			if (frame[0].contentWindow) {
	                	xhtml.responseText =
	                    	frame.contents().find("body").length > 0 ?
	                    	(
	                        	frame.contents().find("body").find("pre").length > 0 ?
	                        	frame.contents().find("body").find("pre").html() :
	                        	frame.contents().find("body").html()
	                    	) :
	                    	(
	                        	frame[0].contentWindow.document.body ?
	                        	(
	                            		frame[0].contentWindow.document.body.getElementByName("pre") ?
	                            		frame[0].contentWindow.document.body.getElementByName("pre").innerHTML :
	                            		frame[0].contentWindow.document.body.innerHTML
	                        	) :
	                        	frame[0].contentWindow.document.body.innerHTML
	                    	);
	                	xhtml.responseXML = frame[0].contentWindow.document.XMLDocument ? frame[0].contentWindow.document.XMLDocument : frame[0].contentWindow.document;
            		}
		} catch (e) {
			// handle the error...
			if (p.debug)
				console.log("contentWindow not appended: " + e);
		}

		try {
			if (frame[0].contentDocument) {
                		xhtml.responseText = frame[0].contentDocument.document.body ? frame[0].contentDocument.document.body.innerHTML : null;
                		xhtml.responseXML = frame[0].contentDocument.document.XMLDocument ? frame[0].contentDocument.document.XMLDocument : frame[0].contentDocument.document;
            		}
		} catch (e) {
			// handle the error...
			if (p.debug)
				console.log("contentWindow not appended: " + e);
		}

		if (xhtml || timeout === "timeout") {
            		done = true;

            		try {
                		status = timeout !== "timeout" ? "success" : "error";

                		if (status !== "error") {
					p.dataType = p.dataType.toLowerCase();
					if (p.dataType !== "file") {
						var data = p.dataType === "xml" ? xhtml.responseXML : xhtml.responseText;

						if (p.dataType === "script")
							$.globalEval(data);

						if (p.dataType === "json")
							data = $.parseJSON(data);
					} else {
						if (typeof p.success === typeof Function)
							p.success(null, status);
					}

					if (p.global)
						$.event.trigger("ajaxSuccess", [xhtml]);
                		} else {
                    			// handle the error...
                    			if (p.debug)
						console.log("Executing ajaxError");

                    			if (typeof p.error === typeof Function)
						p.error(xhtml, status, "ajax failure on ie");

                    			if (p.global)
						$.event.trigger("ajaxError", [xhtml]);
                		}
                		//if (param.global) $.event.trigger("ajaxComplete", [xhtml]);
            		} catch (e) {
                		// handle the error...
                		if (p.debug) console.log("unexpected error: " + e);
                		try {
                			// retry code
                    			if (typeof p.error === typeof Function)
						p.error(xhtml, status, "ajax failure on ie");
                		} catch (ee) {
					/* nothing left to do */
					throw ee;
				}
            		}

            		// request complete
            		if (p.global)
				$.event.trigger("ajaxComplete", [xhtml]);

            		// handle the global ajax counter
            		if (p.global && ! --$.active)
				$.event.trigger("ajaxStop");

            		// process it
            		if (typeof p.complete === typeof Function)
				p.complete(xhtml, status);

            		frame.unbind();

            		try {
                		frame.remove();
            		} catch (e) {
                		// handle the error...
                		if (p.debug)
					console.log("failed to remove frame");
            		}

            		try {
                		form.remove();
            		} catch (e) {
                		// handle the error...
                		if (p.debug)
					console.log("failed to remove form");
            		}
			delete xhtml;
            		//xhtml = null;
        	}
	}

	function real(p) {
		var d = new FormData();
		var url = typeof p.url === "string" ? p.url : "";

		// extract files from tag
		if (p.files instanceof Array && p.files.length > 0) {
			try {
				var name;
				var obj;
				for (var i = 0; i < p.files.length; i++) {
					obj = $(p.files[i]);
					name = obj.attr("name");
					if (name === "")
						name = "file";
					$.each(obj[0].files, function(ii, file) {
						d.append(name, file);
					});
				}
			} catch (e) {
				// error in extracting files
				if (p.debug)
					console.log("failed to add extracted files");
			}
		}

		// server is responsible for decoding json string name
		// only allow POST

		$.each(p.data, function(name, obj) {
			d.append(name, JSON.stringify(p.data));
		});

		// send data
		$.ajax({
			url: url,
			data: d,
			dataType: p.dataType,
			cache: false,
			contentType: false,
			processData: false,
			async: true,
			type: "POST",
			success: function(data, textStatus) {
				if (typeof p.success === typeof Function)
					p.success(data, textStatus);
			},
			error: function(jqXHR, textStatus, errorThrown) {
				if (typeof p.error === typeof Function)
					p.error(jqXHR, textStatus, errorThrown);
			},
			complete: function(jqXHR, textStatus) {
				if (typeof p.complete === typeof Function)
					p.complete(jqXHR, textStatus);
			}
		});
	}

	function fake(p) {
		var p = $.extend({},
			$.ajaxSettings,
			p);

		var processForm = makeForm(p),
			processIFrame = makeFrame(),
			processId = "streamfile_" + _g(2) + new Date().getTime();

		if (p.files instanceof Array && p.files.length > 0) {
			try {
				var c = 0;
				var origobj;
				var clone;
				for (var i = 0; i < p.files.length; i++) {
					origobj = $(p.files[i]);
					clone = origobj.clone();
					origobj.attr("id", _i.i + processId + c++);
					origobj.before(clone);
					origobj.appendTo(processForm);
				}
			} catch (e) {
				// error in extracting files
				if (p.debug)
					console.log("failed to add extracted files");
			}
		}

		// server is responsible for decoding json string name
		// only allow POST

		$.each(p.data, function(name, obj) {
			processForm.append(makeInput(name, JSON.stringify(obj));
		});

		// look for new requests
		if (p.global && !$.active++)
			$.event.trigger("ajaxStart");

		var done = false;

		// build request
		var xhtml = {};
		if (p.global)
			$.event.trigger("ajaxSend", [xhtml]);

		if (p.timeout > 0)
			setTimeout(function () {
				if (!done)
					response("timeout", done, xhtml, processIFrame, processForm, p);
			}, p.timeout);

		try {
			processForm.attr("action", p.url);
			processForm.attr("method", "POST");
			processForm.attr("target", processIFrame.attr("id"));

			if (p.debug)
				console.log(processIFrame.attr("id"));

			processForm.attr("encoding", "multipart/form-data");
			processForm.attr("enctype", "multipart/form-data");
			processForm.submit();

		} catch (e) {
			// handle the error...
			if (p.debug)
				console.log("error in submitting");
		}

		processIFrame.load(function () {
			done = true;
			response("success", done, xhtml, processIFrame, processForm, p);
		});
	}

	function main(p) {
		var p = $.extend({}, {
			url: "", // URL to submit data to
			data: {}, // object to encode - first layer keys will be part of multipart form
			dataType: "json", // server response expected
			files: [], // files to send to the server (must be array of valid <input/>)
			timeout: -1, // max request time
			success: function(data, textStatus) {}, // success promise
			error: function (jqXHR, textStatus, errorThrown) {}, // error promise
			complete: function (jqXHR, textStatus) {} // completion promise
		}, p);

		var isReal = false;

		try {
			// is new FormData supported?
			var t = new FormData();
			isReal = true;
		} catch (e) {
			isReal = false;
		}

		if (p.dataType === "file") {
			// this is a download request
			fake(p);
			return;
		}

		if (isReal) {
			real(p);
		} else {
			fake(p);
		}

		return;
	}

	return main;
})($);
<commit_msg>Update ajaxstream.jquery-1.0.0.js<commit_after>/*
 * AjaxStream JQuery JavaScript Library v1.0.0
 *
 * Requires JQuery 1.11.2 or higher
 *
 * The MIT License (MIT)
 *
 * Copyright (c) 2015 Shane Tindle
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 *
 */

$.ajaxStream =
(function($) {
	// js GUID
	function _s() {
            return Math.floor((1 + Math.random()) * 0x10000)
                .toString(16)
                .substring(1);
        }

	function _g(n) {
		var _r = "";

		if (typeof n != "number")
			n = 5;

		for (var i = 0; i < n.length; i++)
			_r += _s() + "_";

        	return _r;
    	}

	// unique ids
	var _i = {
		o: _g(), // <form/>
		r: _g(), // <iframe/>
		i: _g() // <input type="file"/>
	};

	// form builder
	function makeForm(p) {
		// id unique to the page
		var i = _g(1) + new Date().getTime();
		var f = {
			o: _i.o + i
		};

		// return object
		return $("<form/>")
			.attr("method", "POST")
			.attr("name", f.o)
			.attr("id", f.o)
			.attr("enctype", "multipart/form-data")
			.css({
				position: "absolute",
				top: "-1200px",
				left: "-1200px"
			})
			.appendTo("body");
	}

	// frame builder
	function makeFrame() {
		// id unique to the page
		var i = _g(1) + new Date().getTime();
		var f = {
			r: _i.r + i
		};

		// return object
		return $("<iframe/>")
			.attr("id", f.r)
			.attr("name", f.r)
			.css({
				position: "absolute",
				top: -9999,
				left: -9999
			})
			.attr("src", "about:blank")
			.appendTo("body");
	}

	// input builder
	function makeInput(n, v) {
		return $("<input/>")
			.attr("name", n)
			.val(v);
	}

	// response handler
	function response(timeout, done, xhtml, frame, form, p) {
		var status;

		try {
			if (frame[0].contentWindow) {
	                	xhtml.responseText =
	                    	frame.contents().find("body").length > 0 ?
	                    	(
	                        	frame.contents().find("body").find("pre").length > 0 ?
	                        	frame.contents().find("body").find("pre").html() :
	                        	frame.contents().find("body").html()
	                    	) :
	                    	(
	                        	frame[0].contentWindow.document.body ?
	                        	(
	                            		frame[0].contentWindow.document.body.getElementByName("pre") ?
	                            		frame[0].contentWindow.document.body.getElementByName("pre").innerHTML :
	                            		frame[0].contentWindow.document.body.innerHTML
	                        	) :
	                        	frame[0].contentWindow.document.body.innerHTML
	                    	);
	                	xhtml.responseXML = frame[0].contentWindow.document.XMLDocument ? frame[0].contentWindow.document.XMLDocument : frame[0].contentWindow.document;
            		}
		} catch (e) {
			// handle the error...
			if (p.debug)
				console.log("contentWindow not appended: " + e);
		}

		try {
			if (frame[0].contentDocument) {
                		xhtml.responseText = frame[0].contentDocument.document.body ? frame[0].contentDocument.document.body.innerHTML : null;
                		xhtml.responseXML = frame[0].contentDocument.document.XMLDocument ? frame[0].contentDocument.document.XMLDocument : frame[0].contentDocument.document;
            		}
		} catch (e) {
			// handle the error...
			if (p.debug)
				console.log("contentWindow not appended: " + e);
		}

		if (xhtml || timeout === "timeout") {
            		done = true;

            		try {
                		status = timeout !== "timeout" ? "success" : "error";

                		if (status !== "error") {
					p.dataType = p.dataType.toLowerCase();
					if (p.dataType !== "file") {
						var data = p.dataType === "xml" ? xhtml.responseXML : xhtml.responseText;

						if (p.dataType === "script")
							$.globalEval(data);

						if (p.dataType === "json")
							data = $.parseJSON(data);
					} else {
						if (typeof p.success === typeof Function)
							p.success(null, status);
					}

					if (p.global)
						$.event.trigger("ajaxSuccess", [xhtml]);
                		} else {
                    			// handle the error...
                    			if (p.debug)
						console.log("Executing ajaxError");

                    			if (typeof p.error === typeof Function)
						p.error(xhtml, status, "ajax failure on ie");

                    			if (p.global)
						$.event.trigger("ajaxError", [xhtml]);
                		}
                		//if (param.global) $.event.trigger("ajaxComplete", [xhtml]);
            		} catch (e) {
                		// handle the error...
                		if (p.debug) console.log("unexpected error: " + e);
                		try {
                			// retry code
                    			if (typeof p.error === typeof Function)
						p.error(xhtml, status, "ajax failure on ie");
                		} catch (ee) {
					/* nothing left to do */
					throw ee;
				}
            		}

            		// request complete
            		if (p.global)
				$.event.trigger("ajaxComplete", [xhtml]);

            		// handle the global ajax counter
            		if (p.global && ! --$.active)
				$.event.trigger("ajaxStop");

            		// process it
            		if (typeof p.complete === typeof Function)
				p.complete(xhtml, status);

            		frame.unbind();

            		try {
                		frame.remove();
            		} catch (e) {
                		// handle the error...
                		if (p.debug)
					console.log("failed to remove frame");
            		}

            		try {
                		form.remove();
            		} catch (e) {
                		// handle the error...
                		if (p.debug)
					console.log("failed to remove form");
            		}
			delete xhtml;
            		//xhtml = null;
        	}
	}

	function real(p) {
		var d = new FormData();
		var url = typeof p.url === "string" ? p.url : "";

		// extract files from tag
		if (p.files instanceof Array && p.files.length > 0) {
			try {
				var name;
				var obj;
				for (var i = 0; i < p.files.length; i++) {
					obj = $(p.files[i]);
					name = obj.attr("name");
					if (name === "")
						name = "file";
					$.each(obj[0].files, function(ii, file) {
						d.append(name, file);
					});
				}
			} catch (e) {
				// error in extracting files
				if (p.debug)
					console.log("failed to add extracted files");
			}
		}

		// server is responsible for decoding json string name
		// only allow POST

		$.each(p.data, function(name, obj) {
			d.append(name, JSON.stringify(p.data));
		});

		// send data
		$.ajax({
			url: url,
			data: d,
			dataType: p.dataType,
			cache: false,
			contentType: false,
			processData: false,
			async: true,
			type: "POST",
			success: function(data, textStatus) {
				if (typeof p.success === typeof Function)
					p.success(data, textStatus);
			},
			error: function(jqXHR, textStatus, errorThrown) {
				if (typeof p.error === typeof Function)
					p.error(jqXHR, textStatus, errorThrown);
			},
			complete: function(jqXHR, textStatus) {
				if (typeof p.complete === typeof Function)
					p.complete(jqXHR, textStatus);
			}
		});
	}

	function fake(p) {
		var p = $.extend({},
			$.ajaxSettings,
			p);

		var processForm = makeForm(p),
			processIFrame = makeFrame(),
			processId = "streamfile_" + _g(2) + new Date().getTime();

		if (p.files instanceof Array && p.files.length > 0) {
			try {
				var c = 0;
				var origobj;
				var clone;
				for (var i = 0; i < p.files.length; i++) {
					origobj = $(p.files[i]);
					clone = origobj.clone();
					origobj.attr("id", _i.i + processId + c++);
					origobj.before(clone);
					origobj.appendTo(processForm);
				}
			} catch (e) {
				// error in extracting files
				if (p.debug)
					console.log("failed to add extracted files");
			}
		}

		// server is responsible for decoding json string name
		// only allow POST

		$.each(p.data, function(name, obj) {
			processForm.append(makeInput(name, JSON.stringify(obj)));
		});

		// look for new requests
		if (p.global && !$.active++)
			$.event.trigger("ajaxStart");

		var done = false;

		// build request
		var xhtml = {};
		if (p.global)
			$.event.trigger("ajaxSend", [xhtml]);

		if (p.timeout > 0)
			setTimeout(function () {
				if (!done)
					response("timeout", done, xhtml, processIFrame, processForm, p);
			}, p.timeout);

		try {
			processForm.attr("action", p.url);
			processForm.attr("method", "POST");
			processForm.attr("target", processIFrame.attr("id"));

			if (p.debug)
				console.log(processIFrame.attr("id"));

			processForm.attr("encoding", "multipart/form-data");
			processForm.attr("enctype", "multipart/form-data");
			processForm.submit();

		} catch (e) {
			// handle the error...
			if (p.debug)
				console.log("error in submitting");
		}

		processIFrame.load(function () {
			done = true;
			response("success", done, xhtml, processIFrame, processForm, p);
		});
	}

	function main(p) {
		var p = $.extend({}, {
			url: "", // URL to submit data to
			data: {}, // object to encode - first layer keys will be part of multipart form
			dataType: "json", // server response expected
			files: [], // files to send to the server (must be array of valid <input/>)
			timeout: -1, // max request time
			success: function(data, textStatus) {}, // success promise
			error: function (jqXHR, textStatus, errorThrown) {}, // error promise
			complete: function (jqXHR, textStatus) {} // completion promise
		}, p);

		var isReal = false;

		try {
			// is new FormData supported?
			var t = new FormData();
			isReal = true;
		} catch (e) {
			isReal = false;
		}

		if (p.dataType === "file") {
			// this is a download request
			fake(p);
			return;
		}

		if (isReal) {
			real(p);
		} else {
			fake(p);
		}

		return;
	}

	return main;
})($);
<|endoftext|>


<commit_before>(function() {
	superselector = function()
	{
		if (!document.getElementById("superselect"))
		{
			this.message = "Ctrl + click an element on the page to generate a selector";
			this.pConfigOptions = ["Ignore all config"];
			this.config = this.pConfigOptions;

			this.ignoreClasses = "hover, mouseOver";
			this.ignoreIdPrefixes = "generic_";

			this.prevElement = null;
			this.prevElementBorder = null;

			this.initialize(this);
		}
	};

	superselector.prototype.handleClick = function(oEvent)
	{
		if(oEvent.ctrlKey)
		{
			/* Update current values */
			this.message = document.getElementById("superselect_generated").innerHTML;
			this.pConfigOptions = "";
			this.ignoreClasses = document.getElementById("ignoreClasses").value;
			this.ignoreIdPrefixes = document.getElementById("ignoreIdPrefixes").value;

			var genConfig = new superselector.Config(this.ignoreClasses, this.ignoreIdPrefixes, this.configOptionIsSelected("ignoreAllConfig"));
			var elementTarget = new superselector.Target(oEvent.target, genConfig);
			document.getElementById("superselect_generated").innerHTML = (elementTarget.calculateSelector());
			this.highlight(oEvent.target);
			this.setActive('superselect_generatortab', 'superselect_generator')
		}
	};

	superselector.prototype.highlight = function(target)
	{
		if (this.prevElement != null)
		{
			this.prevElement.style.outline = this.prevElementBorder;
		}
		this.prevElement = target;
		this.prevElementBorder = target.style.outline;
		target.style.outline = "2px black dashed";
	};

	superselector.prototype.configOptionIsSelected = function(sConfigId)
	{
		if(document.getElementById(sConfigId).checked == true)
		{
			return true;
		}
		return false;
	};

	superselector.prototype.setActive = function(tabIdToSetActive, tabContentIdToDisplay)
	{
		var tabContainer = document.getElementById("superselect_tabs");
		var pTabs = tabContainer.getElementsByTagName("li");
		this.setArrayElementClasses(pTabs, "");
		document.getElementById(tabIdToSetActive).setAttribute("class", "superselect_activetab");

		var pageContainer = document.getElementById("superselect_tabcontent");
		var pPages = pageContainer.children;
		this.setArrayElementClasses(pPages, "superselect_hidden");
		document.getElementById(tabContentIdToDisplay).setAttribute("class", "");
	};

	superselector.prototype.setArrayElementClasses = function(pArray, sClass)
	{
		for (var i = 0; i < pArray.length; i++)
		{
			pArray[i].setAttribute("class", sClass);
		}
	};

	superselector.prototype.slide = function(hide)
	{
		var display = document.getElementById("superselect");
		if(hide)
		{
			display.setAttribute("class", "superselect superselect_hidden");
		}
		else
		{
			display.setAttribute("class", "superselect superselect_dockright");
		}

	};
	/* END OF MODEL */

	/* CONFIG */
	superselector.Config = function(ignoreClasses, ignoreIdPrefixes, disableConfig)
	{
		this.disableConfig = disableConfig;
		this.ignoreClasses = ignoreClasses.replace(/\s+/g, '').split(/\s*,\s*/);
		this.ignoreIdPrefixes = ignoreIdPrefixes.replace(/\s+/g, '').split(/\s*,\s*/);
	};
	/* END OF CONFIG */

	/* TARGET */
	superselector.Target = function(eElement, oConfig)
	{
		this.targetElement = eElement;
		this.currentElement = eElement;
		this.completed = false;
		this.selectors = [];
		this.disableConfig = oConfig.disableConfig;
		this.ignoreIdPrefixes = oConfig.ignoreIdPrefixes;
		this.ignoreClasses = oConfig.ignoreClasses;
	};

	superselector.Target.prototype.calculateSelector = function()
	{
		var prefix = '$$("';
		var suffix = '");';

		this.traverseAndCalc(this.targetElement);

		return prefix + this.generateSelectorString() + suffix;
	};

	superselector.Target.prototype.generateSelectorString = function()
	{
		var selectorString = "";
		for (var i = this.selectors.length-1; i >= 0 ; i--)
		{
			selectorString += this.selectors[i];
			if (i != 0) { selectorString += " "; }
		}

		return selectorString;
	};

	superselector.Target.prototype.traverseAndCalc = function(eElement)
	{
		var sId = eElement.getAttribute("id");
		var sClass = eElement.getAttribute("class");

		while (this.completed == false && this.currentElement != null)
		{
			sId = this.currentElement.getAttribute("id");
			sClass = this.currentElement.getAttribute("class");

			if(sId != null && this.isAllowedId(sId))			/* ID CODE PATH */
			{
				this.calculateIdSelector(sId);
			}
			else if (sClass != null && sClass != "")			/* CLASS CODE PATH */
			{
				this.calculateClassSelector(sClass);
			}
			else
			{
				this.calculateTagSelector(); 					/* TAGNAME CODE PATH */
			}

			this.currentElement = this.currentElement.parentElement;
		}

		this._appendEqSuffixIfNeeded();

		return this.selectors;
	};

	superselector.Target.prototype.calculateIdSelector = function (sElementId)
	{
		if(document.getElementById(sElementId) != null)
		{
			this.selectors.push("#" + sElementId);
			this.completed = true;
		}
	};

	superselector.Target.prototype.calculateClassSelector = function(sClass)
	{
		var selectorForClass = this._generateSelectorClassesString(sClass);	/* remove dupe */
		if(selectorForClass != "")
		{
			this.selectors.push("." + selectorForClass);

			var foundElementsInDom = document.getElementsByClassName(selectorForClass);
			if (foundElementsInDom.length == 1)
			{
				this.completed = true;
			}
		}
		else
		{
			this.calculateTagSelector();
		}
	};

	superselector.Target.prototype.calculateTagSelector = function()
	{
		var sTagName = this.currentElement.tagName;
		var eParent = this.currentElement.parentElement;
		var pChildElementsToConsider = (eParent == null ? [] : eParent.children);
		var pTagElementsToConsider = (eParent == null ? [] : eParent.getElementsByTagName(sTagName));

		if(sTagName.toLowerCase() == "body")
		{
			this.selectors.push(sTagName.toLowerCase());
		}

		else if(pTagElementsToConsider.length == 1)
		{
			this.selectors.push(sTagName.toLowerCase());
		}

		else
		{
			for (var i = 0; i < pChildElementsToConsider.length; i++)
			{
				if (pChildElementsToConsider[i] == this.currentElement)
				{
					this.selectors.push(sTagName.toLowerCase() + ":nth-child(" + (i+1) + ")");
				}
			}
		}
	};

	superselector.Target.prototype.isAllowedId = function(sIdName)
	{
		if (this.disableConfig)
		{
			return true;
		}
		for (var i = 0; i < this.ignoreIdPrefixes.length; i++)
		{
			if (sIdName.toLowerCase().indexOf(this.ignoreIdPrefixes[i].toLowerCase()) == 0 &&
				this.ignoreIdPrefixes[i] != "")
			{
				return false;
			}
		}
		return true;
	};

	superselector.Target.prototype.isAllowedClass = function(sName)
	{
		if (this.disableConfig)
		{
			return true;
		}
		for (var i = 0; i < this.ignoreClasses.length; i++)
		{
			if (this.ignoreClasses[i].toLowerCase() == sName.toLowerCase())
			{
				return false;
			}
		}
		return true;
	};

	/*
	 * Private
	 */

	superselector.Target.prototype._appendEqSuffixIfNeeded = function()
	{

	};

	superselector.Target.prototype._generateSelectorClassesString = function(sClassName)
	{
		var pValidClasses = this._returnValidClasses(sClassName);
		var eParent =  this.currentElement.parentElement;

		/* See if we can get a class selector within the current element's parent which is unique */
		for (var i = 0; i < pValidClasses.length; i++)
		{
			if (eParent.getElementsByClassName(pValidClasses[i]).length == 1 &&
				eParent.getElementsByClassName(pValidClasses[i])[0] == this.currentElement)
			{
				return pValidClasses[i];
			}
		}

		/* See if we can get a class + eq(X) selector inside the current element's parent */
		var className = pValidClasses[0];
		var foundElements = eParent.getElementsByClassName(className);
		for (var i = 0; i < foundElements.length; i ++)
		{
			if (foundElements[i] == this.currentElement)
			{
				return className + ":nth-child(" + (i+1) + ")";
			}
		}

		return pValidClasses.join(", ");
	};

	/* Filter classes against the ignore classes list */
	superselector.Target.prototype._returnValidClasses = function(className)
	{
		var classes = className.split(" ");
		var pClassesToReturn = [];

		for (var i = 0; i < classes.length; i++)
		{
			if(this.isAllowedClass(classes[i]))
			{
				if(this._classDoesNotAlreadyExistInArray(classes[i], pClassesToReturn))
				{
					pClassesToReturn.push(classes[i]);
				}
			}
		}
		return pClassesToReturn;
	};

	/* Assert that sClass does not exist in pClassArray */
	superselector.Target.prototype._classDoesNotAlreadyExistInArray = function(sClass, pClassArray)
	{
		for (var x = 0; x < pClassArray.length; x++)
		{
			if(sClass.toLowerCase() == pClassArray[x].toLowerCase())
			{
				return false;
			}
		}
		return true;
	};
	/* END OF TARGET */

	/* INITIALIZE */
	superselector.prototype.initialize = function(oSS)
	{
		var css = document.createElement('link');
		css.href = "http://caplin.github.com/SuperSelector/superselector/style.css";
		css.type = 'text/css';
		css.rel  = 'stylesheet';
		var head = document.getElementsByTagName('head')[0];
		head.appendChild(css);

		var superselectorHtml =
			"
" +
			"
" +
			"
" +
				"
Generator
" +
				"
Config Classes
" +
				"
Config Ids
" +
			"
" +
			"
" +
			"
" +
				"
" +
					"

" +
					"
" +
						"<input id='ignoreAllConfig' type='checkbox' value='Ignore all config' name=''>" +
						"Ignore all config" +
					"
" +
				"
" +
				"
" +
					"
Comma-delimiter list of classes to ignore  (E.g 'hover, mouseOver')
" +
					"<textarea id='ignoreClasses' class='config_area' name='ignoreClasses'></textarea>" +
				"
" +
				"
" +
					"
Comma-delimiter list of ID prefixes to ignore (E.g 'generated, generic_')
" +
					"<textarea id='ignoreIdPrefixes' class='config_area' name='ignoreIdPrefixes'></textarea>" +
				"
" +
			"
";

		var eElement = document.createElement("div");
		eElement.innerHTML = superselectorHtml;
		document.body.appendChild(eElement);

		document.getElementById("superselect_generated").innerHTML = oSS.message;
		document.getElementById("ignoreClasses").value = oSS.ignoreClasses;
		document.getElementById("ignoreIdPrefixes").value = oSS.ignoreIdPrefixes;

		document.body.addEventListener("click", oSS.handleClick.bind(oSS), false);
	};

	new superselector();

	return undefined;
})();

<commit_msg>updating styling for selected element<commit_after>(function() {
	superselector = function()
	{
		if (!document.getElementById("superselect"))
		{
			this.message = "Ctrl + click an element on the page to generate a selector";
			this.pConfigOptions = ["Ignore all config"];
			this.config = this.pConfigOptions;

			this.ignoreClasses = "hover, mouseOver";
			this.ignoreIdPrefixes = "generic_";

			this.prevElement = null;
			this.prevElementBorder = null;

			this.initialize(this);
		}
	};

	superselector.prototype.handleClick = function(oEvent)
	{
		if(oEvent.ctrlKey)
		{
			/* Update current values */
			this.message = document.getElementById("superselect_generated").innerHTML;
			this.pConfigOptions = "";
			this.ignoreClasses = document.getElementById("ignoreClasses").value;
			this.ignoreIdPrefixes = document.getElementById("ignoreIdPrefixes").value;

			var genConfig = new superselector.Config(this.ignoreClasses, this.ignoreIdPrefixes, this.configOptionIsSelected("ignoreAllConfig"));
			var elementTarget = new superselector.Target(oEvent.target, genConfig);
			document.getElementById("superselect_generated").innerHTML = (elementTarget.calculateSelector());
			this.highlight(oEvent.target);
			this.setActive('superselect_generatortab', 'superselect_generator')
		}
	};

	superselector.prototype.highlight = function(target)
	{
		if (this.prevElement != null)
		{
			this.prevElement.style.outline = this.prevElementBorder;
		}
		this.prevElement = target;
		this.prevElementBorder = target.style.outline;
		target.style.outline = "1px black dashed";
	};

	superselector.prototype.configOptionIsSelected = function(sConfigId)
	{
		if(document.getElementById(sConfigId).checked == true)
		{
			return true;
		}
		return false;
	};

	superselector.prototype.setActive = function(tabIdToSetActive, tabContentIdToDisplay)
	{
		var tabContainer = document.getElementById("superselect_tabs");
		var pTabs = tabContainer.getElementsByTagName("li");
		this.setArrayElementClasses(pTabs, "");
		document.getElementById(tabIdToSetActive).setAttribute("class", "superselect_activetab");

		var pageContainer = document.getElementById("superselect_tabcontent");
		var pPages = pageContainer.children;
		this.setArrayElementClasses(pPages, "superselect_hidden");
		document.getElementById(tabContentIdToDisplay).setAttribute("class", "");
	};

	superselector.prototype.setArrayElementClasses = function(pArray, sClass)
	{
		for (var i = 0; i < pArray.length; i++)
		{
			pArray[i].setAttribute("class", sClass);
		}
	};

	superselector.prototype.slide = function(hide)
	{
		var display = document.getElementById("superselect");
		if(hide)
		{
			display.setAttribute("class", "superselect superselect_hidden");
		}
		else
		{
			display.setAttribute("class", "superselect superselect_dockright");
		}

	};
	/* END OF MODEL */

	/* CONFIG */
	superselector.Config = function(ignoreClasses, ignoreIdPrefixes, disableConfig)
	{
		this.disableConfig = disableConfig;
		this.ignoreClasses = ignoreClasses.replace(/\s+/g, '').split(/\s*,\s*/);
		this.ignoreIdPrefixes = ignoreIdPrefixes.replace(/\s+/g, '').split(/\s*,\s*/);
	};
	/* END OF CONFIG */

	/* TARGET */
	superselector.Target = function(eElement, oConfig)
	{
		this.targetElement = eElement;
		this.currentElement = eElement;
		this.completed = false;
		this.selectors = [];
		this.disableConfig = oConfig.disableConfig;
		this.ignoreIdPrefixes = oConfig.ignoreIdPrefixes;
		this.ignoreClasses = oConfig.ignoreClasses;
	};

	superselector.Target.prototype.calculateSelector = function()
	{
		var prefix = '$$("';
		var suffix = '");';

		this.traverseAndCalc(this.targetElement);

		return prefix + this.generateSelectorString() + suffix;
	};

	superselector.Target.prototype.generateSelectorString = function()
	{
		var selectorString = "";
		for (var i = this.selectors.length-1; i >= 0 ; i--)
		{
			selectorString += this.selectors[i];
			if (i != 0) { selectorString += " "; }
		}

		return selectorString;
	};

	superselector.Target.prototype.traverseAndCalc = function(eElement)
	{
		var sId = eElement.getAttribute("id");
		var sClass = eElement.getAttribute("class");

		while (this.completed == false && this.currentElement != null)
		{
			sId = this.currentElement.getAttribute("id");
			sClass = this.currentElement.getAttribute("class");

			if(sId != null && this.isAllowedId(sId))			/* ID CODE PATH */
			{
				this.calculateIdSelector(sId);
			}
			else if (sClass != null && sClass != "")			/* CLASS CODE PATH */
			{
				this.calculateClassSelector(sClass);
			}
			else
			{
				this.calculateTagSelector(); 					/* TAGNAME CODE PATH */
			}

			this.currentElement = this.currentElement.parentElement;
		}

		this._appendEqSuffixIfNeeded();

		return this.selectors;
	};

	superselector.Target.prototype.calculateIdSelector = function (sElementId)
	{
		if(document.getElementById(sElementId) != null)
		{
			this.selectors.push("#" + sElementId);
			this.completed = true;
		}
	};

	superselector.Target.prototype.calculateClassSelector = function(sClass)
	{
		var selectorForClass = this._generateSelectorClassesString(sClass);	/* remove dupe */
		if(selectorForClass != "")
		{
			this.selectors.push("." + selectorForClass);

			var foundElementsInDom = document.getElementsByClassName(selectorForClass);
			if (foundElementsInDom.length == 1)
			{
				this.completed = true;
			}
		}
		else
		{
			this.calculateTagSelector();
		}
	};

	superselector.Target.prototype.calculateTagSelector = function()
	{
		var sTagName = this.currentElement.tagName;
		var eParent = this.currentElement.parentElement;
		var pChildElementsToConsider = (eParent == null ? [] : eParent.children);
		var pTagElementsToConsider = (eParent == null ? [] : eParent.getElementsByTagName(sTagName));

		if(sTagName.toLowerCase() == "body")
		{
			this.selectors.push(sTagName.toLowerCase());
		}

		else if(pTagElementsToConsider.length == 1)
		{
			this.selectors.push(sTagName.toLowerCase());
		}

		else
		{
			for (var i = 0; i < pChildElementsToConsider.length; i++)
			{
				if (pChildElementsToConsider[i] == this.currentElement)
				{
					this.selectors.push(sTagName.toLowerCase() + ":nth-child(" + (i+1) + ")");
				}
			}
		}
	};

	superselector.Target.prototype.isAllowedId = function(sIdName)
	{
		if (this.disableConfig)
		{
			return true;
		}
		for (var i = 0; i < this.ignoreIdPrefixes.length; i++)
		{
			if (sIdName.toLowerCase().indexOf(this.ignoreIdPrefixes[i].toLowerCase()) == 0 &&
				this.ignoreIdPrefixes[i] != "")
			{
				return false;
			}
		}
		return true;
	};

	superselector.Target.prototype.isAllowedClass = function(sName)
	{
		if (this.disableConfig)
		{
			return true;
		}
		for (var i = 0; i < this.ignoreClasses.length; i++)
		{
			if (this.ignoreClasses[i].toLowerCase() == sName.toLowerCase())
			{
				return false;
			}
		}
		return true;
	};

	/*
	 * Private
	 */

	superselector.Target.prototype._appendEqSuffixIfNeeded = function()
	{

	};

	superselector.Target.prototype._generateSelectorClassesString = function(sClassName)
	{
		var pValidClasses = this._returnValidClasses(sClassName);
		var eParent =  this.currentElement.parentElement;

		/* See if we can get a class selector within the current element's parent which is unique */
		for (var i = 0; i < pValidClasses.length; i++)
		{
			if (eParent.getElementsByClassName(pValidClasses[i]).length == 1 &&
				eParent.getElementsByClassName(pValidClasses[i])[0] == this.currentElement)
			{
				return pValidClasses[i];
			}
		}

		/* See if we can get a class + eq(X) selector inside the current element's parent */
		var className = pValidClasses[0];
		var foundElements = eParent.getElementsByClassName(className);
		for (var i = 0; i < foundElements.length; i ++)
		{
			if (foundElements[i] == this.currentElement)
			{
				return className + ":nth-child(" + (i+1) + ")";
			}
		}

		return pValidClasses.join(", ");
	};

	/* Filter classes against the ignore classes list */
	superselector.Target.prototype._returnValidClasses = function(className)
	{
		var classes = className.split(" ");
		var pClassesToReturn = [];

		for (var i = 0; i < classes.length; i++)
		{
			if(this.isAllowedClass(classes[i]))
			{
				if(this._classDoesNotAlreadyExistInArray(classes[i], pClassesToReturn))
				{
					pClassesToReturn.push(classes[i]);
				}
			}
		}
		return pClassesToReturn;
	};

	/* Assert that sClass does not exist in pClassArray */
	superselector.Target.prototype._classDoesNotAlreadyExistInArray = function(sClass, pClassArray)
	{
		for (var x = 0; x < pClassArray.length; x++)
		{
			if(sClass.toLowerCase() == pClassArray[x].toLowerCase())
			{
				return false;
			}
		}
		return true;
	};
	/* END OF TARGET */

	/* INITIALIZE */
	superselector.prototype.initialize = function(oSS)
	{
		var css = document.createElement('link');
		css.href = "http://caplin.github.com/SuperSelector/superselector/style.css";
		css.type = 'text/css';
		css.rel  = 'stylesheet';
		var head = document.getElementsByTagName('head')[0];
		head.appendChild(css);

		var superselectorHtml =
			"
" +
			"
" +
			"
" +
				"
Generator
" +
				"
Config Classes
" +
				"
Config Ids
" +
			"
" +
			"
" +
			"
" +
				"
" +
					"

" +
					"
" +
						"<input id='ignoreAllConfig' type='checkbox' value='Ignore all config' name=''>" +
						"Ignore all config" +
					"
" +
				"
" +
				"
" +
					"
Comma-delimiter list of classes to ignore  (E.g 'hover, mouseOver')
" +
					"<textarea id='ignoreClasses' class='config_area' name='ignoreClasses'></textarea>" +
				"
" +
				"
" +
					"
Comma-delimiter list of ID prefixes to ignore (E.g 'generated, generic_')
" +
					"<textarea id='ignoreIdPrefixes' class='config_area' name='ignoreIdPrefixes'></textarea>" +
				"
" +
			"
";

		var eElement = document.createElement("div");
		eElement.innerHTML = superselectorHtml;
		document.body.appendChild(eElement);

		document.getElementById("superselect_generated").innerHTML = oSS.message;
		document.getElementById("ignoreClasses").value = oSS.ignoreClasses;
		document.getElementById("ignoreIdPrefixes").value = oSS.ignoreIdPrefixes;

		document.body.addEventListener("click", oSS.handleClick.bind(oSS), false);
	};

	new superselector();

	return undefined;
})();

<|endoftext|>