Greasy Fork is available in English.

CodeFelony JS Library

A JavaScript library used by CodeFelony

Script này sẽ không được không được cài đặt trực tiếp. Nó là một thư viện cho các script khác để bao gồm các chỉ thị meta // @require https://update.greasyfork.org/scripts/9003/44596/CodeFelony%20JS%20Library.js

// ==UserScript==
// @name          CodeFelony JS Library
// @namespace     https://greasyfork.org/en/users/10166-moriarty
// @description   A JavaScript library used by CodeFelony
// @include       *
// @copyright     (c) 2015 CodeFelony. All Rights Reserved
// @author        Moriarty
// @version       1.3.1
// @license       LGPL version 3 or any later version; http://www.gnu.org/copyleft/lgpl.html
// @grant         GM_addStyle
// ==/UserScript==

/**
    WIKI ==> https://github.com/codefelony/cfl/wiki/
**/

(function (window, undefined) {

    'use strict'; // use strict mode in ECMAScript-5

    var version = '1.3.1'; // this will be used for CFL.prototype.version
    var intervals = []; // for the setInterval/clearInterval methods

    // regular expressions
    var rSelector = /^\*|^\.[a-z][\w\d-]*|^#[^ ]+|^[a-z]+|^\[a-z]+/i; // matches a CSS selector
    var rXpath = /^(\.*\/{1,2})+[a-zA-Z\*]+/;                         // matches an XPath query
    var rHTML = /<[^>]+>/;                                            // matches a string of HTML
    var rHyphenated = /-([a-zA-Z])/g;                                 // matches alphabetic, hyphenated strings
    var rElementObject = /^\[object HTML([a-zA-Z]+)?Element\]$/;      // matches the toString value of an element
    var rWindowObject = /^\[object Window\]$/;                        // matches the toString value of a window object
    var rValidVarname = /^[a-zA-Z$_][a-zA-Z0-9$_]*$/;                 // matches a valid variable name

    // compatibility methods for browsers that don't support ECMAScript-5 completely
    var compat = {
        'arr_indexOf' : function (searchElement, fromIndex) {
            var index = parseInt(fromIndex || 0, 10), len = this.length;
                index = index < 0 ? len + index : index; // handle negative fromIndex
                index = !(index > 0) ? 0 : index; // handle out of range and/or NaN fromIndex

            while (index < len && index >= 0) {
                if (this[index] === searchElement) {
                    return index;
                }
                index += 1;
            }

            return -1;
        },
        /*
        'filter' : function (fn, oThis) {
            var index, value, len = this.length, ret = [];

            for (index = 0; index < len; index += 1) {
                value = this[index];
                if ( fn.call(oThis, value, index, this) ) {
                    ret.push(value);
                }
            }

            return ret;
        },
        */
        'forEach' : function (fn, oThis) {
            var index, len;

            for (index = 0, len = this.length; index < len; index += 1) {
                fn.call(oThis, this[index], index, this);
            }
        },
        'map' : function (fn, oThis) {
            var index, newArr = [], len;

            for (index = 0, len = this.length; index < len; index += 1) {
                newArr[index] = fn.call(oThis, this[index], index, this);
            }

            return newArr;
        },
        'reduce' : function (fn, initialValue) {
            var index, len, value, isValueSet = false;

            if (arguments.length > 1) {
                value = initialValue;
                isValueSet = true;
            }

            for (index = 0, len = this.length; index < len; index += 1) {
                if (isValueSet) {
                    value = fn(value, this[index], index, this);
                } else {
                    value = this[index];
                    isValueSet = true;
                }
            }

            return value;
        }
    };

    // gets a method from an object's prototype. returns undefined if not found
    var getMethod = function (obj, method) {
        if (typeof XPCNativeWrapper === 'function' && typeof XPCNativeWrapper.unwrap === 'function') {
            obj = XPCNativeWrapper.unwrap(obj);
        } else if (obj.wrappedJSObject) {
            obj = obj.wrappedJSObject;
        }

        if (obj.prototype && typeof obj.prototype[method] === 'function') {
            return obj.prototype[method];
        }
    };

    // original methods for some common uses
    var core = {
        // array
        'arr_indexOf' : getMethod(Array, 'indexOf') || compat.arr_indexOf,
        'concat' : getMethod(Array, 'concat'),
        'filter' : getMethod(Array, 'filter') || compat.filter,
        'forEach' : getMethod(Array, 'forEach') || compat.forEach,
        'map' : getMethod(Array, 'map') || compat.map,
        'reduce' : getMethod(Array, 'reduce') || compat.reduce,
        'slice' : getMethod(Array, 'slice'),

        // object
        'hasOwnProperty' : getMethod(Object, 'hasOwnProperty'),
        'toString' : getMethod(Object, 'toString'),
    };

    var CFL = function CFL(selector, context) {
        return new CFL.fn.init(selector, context);
    };

    // a simple class for dealing with event listener handlers
    var handlers = {
        stack : [],

        add : function (thisElement, type, fn) {
            this.stack.push({
                element : thisElement,
                type : type,
                fn : fn
            });
        },

        get : function (thisElement, type) {
            var events = [];
                type = typeof type === 'string' ? type : '*';

            CFL.each(this.stack, function (thisEventObj) {
                if (thisElement === thisEventObj.element) {
                    if (type === '*' || thisEventObj.type === type) {
                        events.push(thisEventObj);
                    }
                }
            });

            return events;
        },

        remove : function (thisElement, type) {
            var handlerIndices = [], that = this;

            // find all the indices of what we need to remove
            CFL.each(handlers.get(thisElement, type), function (thisEventObj, index, array) {
                handlerIndices.push(
                    core.arr_indexOf.call(that.stack, thisEventObj)
                );
            });

            // remove all the indices here, using a separate array of indices
            // we can't do this as we loop over the (stack) array itself, because
            // we would be removing values as they are being iterated through
            CFL.each(handlerIndices, function (thisIndex) {
                that.stack.splice(thisIndex, 1);
            });
        }
    };

    // Node.prototype.matchesSelector compat for vendor prefixes
    function matchesSelector(element, selector) {
        if (element && typeof selector === 'string') {
            if (typeof element.mozMatchesSelector === 'function') {
                // Mozilla
                return element.mozMatchesSelector(selector);
            } else if (typeof element.webkitMatchesSelector === 'function') {
                // Webkit
                return element.webkitMatchesSelector(selector);
            } else if (typeof element.oMatchesSelector === 'function') {
                // Opera
                return element.oMatchesSelector(selector);
            } else if (typeof element.msMatchesSelector === 'function') {
                // IE
                return element.msMatchesSelector(selector);
            }
        }

        return false;
    }

    // calls 'this' with the first parameter as the first argument
    function call(a) {
        return this(a);
    }

    function toCamelCase(string) {
        return string.replace(rHyphenated, function (fullMatch, firstGroup) {
            return firstGroup.toUpperCase();
        });
    }

    // walkTheDom by Douglas Crockford
    function walkTheDom(node, func) {
        func(node);
        node = node.firstChild;

        while (node) {
            walkTheDom(node, func);
            node = node.nextSibling;
        }
    }

    // can pluck a key out of an object
    function pluck(obj) {
        var subs = this.split('.'),
            ret = obj, i;

        for (i = 0; i < subs.length; i += 1) {
            ret = ret[ subs[i] ];
            if (ret == null) {
                return '';
            }
        }

        return ret;
    }

    function sum(curValue, nextValue) {
        return curValue + nextValue;
    }

    function sumInt(curValue, nextValue) {
        return parseInt(curValue, 10) + parseInt(nextValue, 10);
    }

    // internal function for throwing errors, so the user gets
    // some sort of hint as to why their operation failed
    function error(errorString) {
        if (typeof console !== 'undefined' && typeof console.error === 'function') {
            console.error(errorString);
        }

        return null; // always return null
    }

    // will copy an element and return a new copy with the same event listeners
    function cloneElement(thisElement) {
        var newElement = thisElement.cloneNode(true);

        // clone event listeners of element
        CFL.each(handlers.get(thisElement), function (thisEventObj) {
            CFL.addEvent(newElement, thisEventObj.type, thisEventObj.fn);
        });

        return newElement;
    }

    function getEachElements(array, selector, key, type) {
        var newElementsArray = [],
            isValidSelector = typeof selector === 'string' && selector.trim() !== '';

        CFL.each(array, function (currentElement) {
            while ( currentElement = currentElement[key] ) { // note: intentional assignment
                if (type > 0 ? currentElement.nodeType === type : true) {
                    if ( isValidSelector === false || CFL(currentElement).filter(selector).exists ) {
                        newElementsArray.push(currentElement);
                        return;
                    }
                }
            }
        });

        return newElementsArray;
    }

    // this will take
    function doElementOperationOnEach(args, op) {
        var newElementsArray = [], newElement,
            passedElements = CFL.create.apply(CFL, args);

        if (this.exists) {
            if (CFL.typeOf(passedElements) === 'array') {
                this.each(function (thisElement) {
                    CFL.each(passedElements, function (passedElement) {
                        // clone the element
                        var newElement = cloneElement(passedElement);

                        // add the new elements to an array
                        newElementsArray.push(newElement);

                        // perform the passed operation on the element
                        op(thisElement, newElement);
                    });
                });
            } else {
                this.each(function (thisElement) {
                    // clone the element
                    var newElement = cloneElement(passedElements);

                    // add the new elements to an array
                    newElementsArray.push(newElement);

                    // perform the passed operation on the element
                    op(thisElement, newElement);
                });
            }
        }

        return newElementsArray;
    }

    // define CFL's prototype, aka CFL.fn
    CFL.fn = CFL.prototype = {
        isCFL : true,
        constructor : CFL,
        length : 0,
        version : version,

        // similar to jQuery. CFL is just the init constructor
        init : function (selector, context) {
            var selectorStringValue = core.toString.call(selector),
                that = this,
                elems = [];

            switch (typeof selector) {
                case 'string': {  // -- STRING --
                    if ( selector.match(rXpath) ) {
                        // handle an XPath expression
                        elems = CFL.xpath({expression : selector, type : 7, context : context});
                    } else if ( selector.match(rHTML) ) {
                        // reserved for html code creation
                        // not sure if I want to implement it
                    } else if ( selector.match(rSelector) ) {
                        if (CFL.typeOf(context) === 'array') {
                            // handle an array being passed as the context
                            return that.find.call(context, selector);
                        } else if (typeof context === 'string') {
                            // handle a selector being passsed as the context
                            context = CFL(context);
                            if (context.exists) {
                                return CFL(selector, context[0]);
                            }
                        } else if (context != null && context.isCFL === true && context.exists) {
                            // handle a CFL object being passsed as the context
                            return CFL( selector, context[0] );
                        } else {
                            // handle a regular element being passed as the context
                            context = context != null && context.querySelectorAll ? context : document;
                            elems = context.querySelectorAll(selector);
                        }
                    }
                    break;
                }
                // ---------------------------------------------------
                case 'object': {  // -- OBJECT --
                    if (selector != null) {
                        if (selector.isCFL === true) {
                            // handle a CFL object
                            return selector;
                        } else if ( core.hasOwnProperty.call(selector, 'length') ) {
                            // handle an array-like object
                            elems = selector;
                        } else if ( selectorStringValue.match(rElementObject) || selectorStringValue.match(rWindowObject) ) {
                            // handle a single element
                            elems = [selector];
                        }
                    }
                    break;
                }
                // ---------------------------------------------------
                default: {        // -- UNKNOWN --
                    if ( selectorStringValue.match(rElementObject) || selectorStringValue.match(rWindowObject) ) {
                        // handle elements that are typeof === 'function'
                        // e.g., object, applet, embed
                        elems = [selector];
                    }
                }
            }

            // define the length property of our object wrapper
            that.length = elems.length;

            // bind the elements to array-like key:value pairs in our wrapper
            // e.g., this[0] ==> element
            CFL.each(elems, function (value, index) {
                that[index] = value;
            });

            return that;
        },

        // --- STARTING LINE FOR THE CFL WRAPPER METHODS
        add : function (selector, context) {
            var newElements = CFL(selector, context).raw(),
                allElements = core.concat.call(this.raw(), newElements);
            return CFL(allElements);
        },

        addEvent : function (type, fn) {
            return this.each(function (thisElement) {
                CFL.addEvent(thisElement, type, fn);
            });
        },

        after : function () {
            var newElementsArray = doElementOperationOnEach.call(this, CFL.toArray(arguments), function (baseElement, newElement) {
                var parent = baseElement.parentNode,
                    next = baseElement.nextSibling;

                if (parent) {
                    if (next) {
                        // add the newElement after the current element
                        parent.insertBefore(newElement, next);
                    } else {
                        // nextSibling didn't exist. just append to its parent
                        parent.appendChild(newElement);
                    }
                }
            });

            return CFL(newElementsArray);
        },

        append : function () {
            var newElementsArray = doElementOperationOnEach.call(this, CFL.toArray(arguments), function (baseElement, newElement) {
                baseElement.appendChild(newElement);
            });

            return CFL(newElementsArray);
        },

        attribute : function (name, value) {
            var ret = '', valueIsValid = value != null;

            if ( typeof name === 'string' && this.exists ) {
                    this.each(function (elem) {
                        if (valueIsValid) {
                            elem.setAttribute(name, value);
                        } else {
                            ret += elem.getAttribute(name) || '';
                        }
                    });
            }

            return valueIsValid ? this : ret;
        },

        before : function () {
            var newElementsArray = doElementOperationOnEach.call(this, CFL.toArray(arguments), function (baseElement, newElement) {
                var parent = baseElement.parentNode;

                // add the newElement before the current element
                if (parent) {
                    parent.insertBefore(newElement, baseElement);
                }
            });

            return CFL(newElementsArray);
        },

        center : function () {
            return this.each(function (thisElement) {
                thisElement = CFL(thisElement);
                thisElement.css('position', 'fixed');
                thisElement.css('top', Math.floor( (window.innerHeight - thisElement.height) / 2 ) + 'px');
                thisElement.css('left', Math.floor( (window.innerWidth - thisElement.width) / 2 ) + 'px');
            });
        },

        clone : function () {
            var clonedElements = core.map.call(this, cloneElement); // variable for clarity
            return CFL(clonedElements);
        },

        css : function (name, value) {
            if (typeof name === 'string') {
                // convert the hyphenated string to camel-case
                name = toCamelCase(name);

                if (typeof value === 'string') {
                    return this.each(function (thisElement) {
                        if (name in thisElement.style) {
                            thisElement.style[name] = value;
                        }
                    });
                }

                return core.map.call(this, pluck, 'style.' + name).join('');
            } else {
                return error('.css() was not passed a string for the first argument.');
            }
        },

        each : function (fn, oThis) {
            if (this.exists) {
                CFL.each(this, fn, oThis);
            }

            return this;
        },

        get exists() {
            return this.length > 0 && this[0] != null;
        },

        filter : function (selector) {
            var newElementsArray = [];

            if (typeof selector === 'string') {
                this.each(function (thisElement) {
                    if ( matchesSelector(thisElement, selector) ) {
                        newElementsArray.push(thisElement);
                    }
                });
            }

            // returns an empty CFL object if no elements are matched
            return CFL(newElementsArray);
        },

        find : function (selector) {
            var arrayOfMatchesArrays = core.map.call(this, function (thisElement) {
                var matches = thisElement.querySelectorAll(selector);
                return CFL.toArray(matches);
            });
            var singleArrayOfMatches = arrayOfMatchesArrays.length > 0 ?
                core.reduce.call(arrayOfMatchesArrays, function (a, b) {
                    return core.concat.call(a, b);
            }) : [];

            return CFL(singleArrayOfMatches);
        },

        first : function () {
            return this.get(0);
        },

        focus : function () {
            var firstElement;

            if (this.exists) {
                firstElement = this[0];
                if (typeof firstElement.focus === 'function') {
                    firstElement.focus();
                }
            }
        },

        get : function (index) {
            index = index === 'first' ? 0 : index === 'last' ? -1 : parseInt(index, 10);

            if ( !isNaN(index) ) {
                return CFL( index < 0 ? this[this.length + index] : this[index] );
            }

            return CFL.toArray(this);
        },

        get height() {
            var arrayOfElemHeights = core.map.call(this, pluck, 'offsetHeight');
            return core.reduce.call(arrayOfElemHeights, sum);
        },

        has : function (selector) {
            var newElementsArray = [];

            if ( typeof selector === 'string' && selector.match(rSelector) ) {
                this.each(function (thisElement) {
                    if ( CFL(selector, thisElement).exists ) {
                        newElementsArray.push(thisElement);
                    }
                });
            }

            return CFL(newElementsArray);
        },

        hide : function () {
            return this.css('display', 'none');
        },

        /*
        get inView(passedContainer) {
            var isInView = false;

            this.each(function (thisElement) {
                var container = passedContainer || thisElement.parentNode;
                var visible = !!( (container.scrollTop + container.offsetHeight) >= thisElement.offsetTop &&
                           (container.scrollTop - thisElement.offsetHeight) <= thisElement.offsetTop );

                if (visible) {
                    isInView = true;
                    return 'stop';
                }
            });

            return isInView;
        },
        */

        is : function (selector) {
            for (var i = 0; i < this.length; i += 1) {
                if ( matchesSelector(this[i], selector) ) {
                    return true;
                }
            }

            return false;
        },

        isnt : function (selector) {
            return !this.is(selector);
        },

        last : function (selector) {
            return this.get(-1);
        },

        next : function (selector) {
            return CFL( getEachElements(this, selector, 'nextSibling', 1) );
        },

        not : function (selector) {
            var newElementsArray = [];

            if ( typeof selector === 'string' && selector.match(rSelector) ) {
                this.each(function (thisElement) {
                    if ( CFL(thisElement).isnt(selector) ) {
                        newElementsArray.push(thisElement);
                    }
                });
            }

            return CFL(newElementsArray);
        },

        parent : function (selector) {
            return CFL( getEachElements(this, selector, 'parentNode', 1) );
        },

        prepend : function () {
            var newElementsArray = doElementOperationOnEach.call(this, CFL.toArray(arguments), function (baseElement, newElement) {
                var firstChild = baseElement.firstChild;

                if (firstChild) {
                    baseElement.insertBefore(newElement, firstChild);
                }
            });

            return CFL(newElementsArray);
        },

        prev : function (selector) {
            return CFL( getEachElements(this, selector, 'previousSibling', 1) );
        },

        prop : function (name, value) {
            var valueIsValid = value != null, ret;

            if (typeof name === 'string' && this.exists) {
                    this.each(function (thisElement) {
                        if (valueIsValid) {
                            thisElement[name] = value;
                        } else {
                            if (typeof ret === 'undefined') {
                                ret = thisElement[name];
                            } else {
                                ret += thisElement[name];
                            }
                        }
                    });
            }

            return valueIsValid ? this : ret;
        },

        raw : function () {
            return core.slice.call(this, 0);
        },

        remove : function () {
            return this.each(function (element) {
                var parent = element.parentNode;

                if (element && parent) {
                    parent.removeChild(element);
                }
            });
        },

        removeAttribute : function (attributeName) {
            if (typeof attributeName === 'string') {
                return this.each(function (thisElement) {
                    thisElement.removeAttribute(attributeName);
                });
            } else {
                return error('.removeAttribute() was not passed a string.');
            }
        },

        removeEvent : function (type) {
            if (typeof type === 'string') {
                return this.each(function (thisElement) {
                    CFL.removeEvent(thisElement, type);
                });
            } else {
                return error('.removeEvent() was not passed a string.');
            }
        },

        replace : function () {
            var newElementsArray = doElementOperationOnEach.call(this, CFL.toArray(arguments), function (baseElement, newElement) {
                var parent = baseElement.parentNode;

                if (parent) {
                    parent.replaceChild(newElement, baseElement);
                }
            });

            return CFL(newElementsArray);
        },

        show : function (value) {
            value = typeof value === 'string' ? value : 'inline';
            return this.css('display', value);
        },

        text : function (passedText, append) {
            // convert a number to a string
            if ( typeof passedText === 'number' && !isNaN(passedText) && isFinite(passedText) ) {
                passedText += '';
            }

            // handle setting text
            if (typeof passedText === 'string') {
                if (append !== true) {
                    this.each(function (thisElement) {
                        CFL('.//text()', thisElement).each(function (textNode) {
                            textNode.data = '';
                        });
                    });
                }

                this.append('text', passedText);
                return this;
            }

            // handle getting text
            return core.reduce.call(this, function (curValue, nextElement) {
                return curValue + nextElement.textContent;
            }, '');
        },

        toggle : function () {
            return this.each(function (thisElement) {
                thisElement = CFL(thisElement);

                if (thisElement.visible) {
                    thisElement.hide();
                } else {
                    thisElement.show();
                }
            });
        },

        value : function (passedValue) {
            var elem = this[0],
                tagName = elem && elem.tagName || '',
                selectedOptions = [],
                rInputTypeBlacklist = /button|checkbox|file|image|radio|reset|submit/,
                passedValueType = CFL.typeOf(passedValue);

            if (passedValue == null) {
                // no arguments were passed, return a value
                    if (tagName === 'SELECT') {
                        if ( elem.hasAttribute('multiple') ) {
                            CFL.each(elem.options, function (thisOption) {
                                if (thisOption.selected) {
                                    selectedOptions.push(thisOption.value);
                                }
                            });

                            return selectedOptions;
                        } else {
                            return elem.options[elem.selectedIndex].value;
                        }
                    } else if ( tagName === 'INPUT' && !elem.type.match(rInputTypeBlacklist) ) {
                        return elem.value;
                    }
                    if (tagName === 'TEXTAREA') {
                        return elem.value;
                    }
            } else {
                // an argument was passed, set the value on each element
                return this.each(function (thisElement) {
                    var tagName = thisElement.tagName;

                    if (tagName === 'SELECT') {
                        if (thisElement.hasAttribute('multiple') && passedValueType === 'array') {
                                CFL.each(thisElement.options, function (thisOption) {
                                    CFL.each(passedValue, function (thisPassedValue) {
                                        if (thisOption.value == thisPassedValue) {
                                            thisOption.selected = true;
                                            return 'stop';
                                        } else {
                                            thisOption.selected = false;
                                        }
                                    });
                                });
                        } else {
                            CFL.each(thisElement.options, function (thisOption) {
                                thisOption.selected = thisOption.value == passedValue;
                            });
                        }
                    } else if (tagName === 'INPUT') {
                        if ( !thisElement.type.match(rInputTypeBlacklist) ) {
                            thisElement.value = passedValue;
                        } else if (thisElement.type === 'checkbox' || thisElement.type === 'radio') {
                            if (passedValueType === 'array') {
                                CFL.each(passedValue, function (thisPassedValue) {
                                    if (thisElement.value == thisPassedValue) {
                                        thisElement.checked = true;
                                        return 'stop';
                                    } else {
                                        thisElement.checked = false;
                                    }
                                });
                            } else if (thisElement.value == passedValue) {
                                 thisElement.checked = true;
                            }
                        }
                    } else if (tagName === 'TEXTAREA') {
                        thisElement.value = passedValue;
                    }
                });
            }

            return null;
        },

        get visible() {
            return Math.max(this.width, this.height) > 0;
        },

        get width() {
            var arrayOfElemHeights = core.map.call(this, pluck, 'offsetWidth');
            return core.reduce.call(arrayOfElemHeights, sum);
        },
    };

    // give the init function the CFL prototype for later instantiation
    CFL.fn.init.prototype = CFL.fn;

    // extend method. can extend any object it's run upon
    CFL.fn.extend = CFL.extend = function (obj) {
        var name, copy;

        for (name in obj) {
            copy = obj[name];

            if ( !core.hasOwnProperty.call(this, name) && typeof copy !== 'undefined' ) {
                this[name] = copy;
            }
        }
    };

    // --- STARTLING LINE FOR THE DIRECT CFL METHODS
    CFL.extend({
        addEvent : function addEvent(thisElement, type, fn) {
            if (thisElement != null && typeof type === 'string' && typeof fn === 'function') {
                if (typeof thisElement.addEventListener === 'function') {
                    thisElement.addEventListener(type, fn, false);
                } else if (typeof thisElement.attachEvent === 'function') {
                    type = 'on' + type;
                    thisElement.attachEvent(type, fn);
                } else {
                    return;
                }

                handlers.add(thisElement, type, fn);
            }
        },

        addScript : function addScript(contents, id, node) {
            var newElement = document.createElement('script');
            newElement.id = id || ( 'cfl-script-' + CFL.random(999) );
            newElement.innerHTML = contents;

            node = node || document.head || document.querySelector('html > head');
            node.appendChild(newElement);

            return {
                remove : function () {
                    node.removeChild(newElement);
                }
            };
        },

        addStyle : function addStyle(css, id, node) {
            id = id || ( 'cfl-style-' + CFL.random(999) );
            node = node || document.head || document.querySelector('html > head');
            if (node) {
                node.appendChild(
                    CFL.create('style', {id : id, type : 'text/css'}, [ CFL.create('text', css) ] )
                );
            }
        },

        alias : function alias(newAlias) {
            if (typeof newAlias === 'string' && newAlias.match(rValidVarname) && typeof window[newAlias] === 'undefined') {
                window[newAlias] = CFL;
            }
        },

        clearInterval : function clearInterval(index) {
            if (typeof index === 'number' && index < intervals.length) {
                window.clearTimeout( intervals[index] );
                intervals[index] = null;
            }
        },

        create : function create(elementName, descObj, kidsArray) {
            var argsLength = arguments.length,
                typeValue, prop, val, HTMLholder, ret, i;

            if (argsLength === 2 && elementName === 'text' && typeof descObj === 'string') {
                // handle text node creation
                return document.createTextNode(descObj);
            } else if ( argsLength === 1 && typeof elementName === 'string' && elementName.match(rHTML) ) {
                // handle HTML strings

                // take the HTML string and put it inside a div
                HTMLholder = document.createElement('div');
                HTMLholder.innerHTML = elementName;

                // add each childNode to an array to return
                ret = [];
                ret.push.apply(ret, HTMLholder.childNodes);
                return ret.length > 0 ? (ret.length === 1 ? ret[0] : ret) : null;
            } else if (argsLength > 1 && typeof elementName === 'string' && typeof descObj === 'object') {
                // handle the normal element name and descriptor object
                ret = document.createElement(elementName + '');

                for (prop in descObj) {
                    if ( core.hasOwnProperty.call(descObj, prop) ) {
                        val = descObj[prop];
                        if (prop.indexOf('on') === 0 && typeof val === 'function') {
                            CFL.addEvent(ret, prop.substring(2), val);
                        } else if ( prop !== 'style' && prop !== 'class' && prop in ret && typeof ret[prop] !== 'undefined' ) {
                            ret[prop] = val;
                        } else {
                            ret.setAttribute(prop, val);
                        }
                    }
                }

                if (CFL.typeOf(kidsArray) === 'array') {
                    CFL.each(kidsArray, function (kid) {
                        var val, item, i;

                        if (typeof kid === 'string') {
                            val = CFL.create(kid)

                            if (CFL.typeOf(val) === 'array') {
                                for (i = 0; i < val.length; i += 1) {
                                    ret.appendChild( val[i] );
                                }
                            } else if (CFL.typeOf(kid) === 'element') {
                                ret.appendChild(kid);
                            }
                        } else if (CFL.typeOf(kid) === 'element') {
                            ret.appendChild(kid);
                        }
                    });
                }

                return ret;
            } else if (argsLength === 1 && CFL.typeOf(elementName) === 'element') {
                // handle an element
                return elementName;
            }
        },

        each : function each(passedArray, fn, oThis) {
            var isOthisUndefined = typeof oThis !== 'undefined',
                index, len, otherThis, value;

            for (index = 0; index < passedArray.length; index += 1) {
                value = passedArray[index];
                otherThis = isOthisUndefined ? oThis : value;
                if (fn.call(otherThis, value, index, passedArray) === 'stop') {
                    break;
                }
            }
        },

        loop : function loop(maxIterations, fn) {
            var args = CFL.toArray(arguments), i;

            if (typeof maxIterations === 'number' && maxIterations > 0 && typeof fn === 'function') {
                args = args.slice(2);
                for (i = 0; i < maxIterations; i += 1) {
                    fn.apply(null, args);
                }
            }
        },

        random : function random(maxInteger, minInteger) {
            var rand = -1;

            while (rand < 0 || rand > maxInteger || rand < minInteger) {
                rand = Math.floor( Math.random() * maxInteger ) + Math.round( Math.random() );
            }

            return rand;
        },

        removeEvent : function removeEvent(thisElement, type) {
            CFL.each(handlers.get(thisElement, type), function (thisEventObj) {
                if (typeof thisElement.removeEventListener === 'function') {
                    thisEventObj.element.removeEventListener(thisEventObj.type, thisEventObj.fn, false);
                } else if (typeof thisElement.detachEvent === 'function') {
                    type = 'on' + type;
                    thisEventObj.element.detachEvent(thisEventObj.type, thisEventObj.fn);
                }

                handlers.remove(thisElement, type);
            });
        },

        runAt : function runAt(state, func, oThis) {
            var args = CFL.toArray(arguments), intv,

                // compose a list of the 4 states, to use .indexOf() upon later
                states = ['uninitialized', 'loading', 'interactive', 'complete'],

                // in-case they pass [start/end] instead of [loading/complete]
                state = state.replace('start', 'loading').replace('end', 'complete');

            // this will run their function with the specified arguments, if any,
            // and a custom 'this' value, if specified
            function runFunc() {
                func.apply( oThis, args.slice(3) );
            }

            // this will run on each state change if the specified state is
            // not achieved yet. it will run their function when it is achieved
            function checkState() {
                if (document.readyState === state) {
                    runFunc();
                    CFL.clearInterval(intv);
                }
            }

            if ( core.arr_indexOf.call(states, state) <= core.arr_indexOf.call(states, document.readyState) ) {
                // we are at, or have missed, our desired state
                // run the specified function
                runFunc();
            } else {
                intv = CFL.setInterval(checkState, 200);
            }
        },

        setInterval : function setInterval(func, delay) {
            var index = intervals.length,
                delay_orig = delay,
                count = 1, startTime;

            function doRe(func, delay) {
                return window.setTimeout(function () {
                    // drift accomodation
                    var difference = ( new Date().getTime() ) - startTime,
                        correctTime = delay_orig * count,
                        drift = difference - correctTime;

                    // execute the function before setting a new timeout
                    func.call(null);

                    // fix for when a timeout takes longer than double the original delay time to execute
                    if (drift > delay_orig) {
                        drift = delay_orig;
                    }

                    // save the reference of the new timeout in our 'intervals' stack
                    if (intervals[index] !== null) {
                        intervals[index] = doRe(func, delay_orig - drift);
                    }

                    count += 1;
                }, delay);
            }

            startTime = new Date().getTime();
            intervals[index] = doRe(func, delay_orig);

            return index;
        },

        toArray : function toArray(arr) {
            var newArr = [], // new array to store the values into
                len = arr.length || arr.snapshotLength,
                item, i;

            if (typeof len === 'number' && len > 0) {
                if (typeof arr.snapshotItem === 'function') {
                    for (i = 0; ( item = arr.snapshotItem(i) ); i += 1) {
                        newArr.push(item);
                    }
                } else {
                    // if the specified 'list' is array-like, use slice on it
                    // to convert it to an array
                    newArr = core.slice.call(arr, 0);
                }
            }

            return newArr;
        },

        toString : function toString(item) {
            var key, value, values = [];

            function stringifyValue(val) {
                var typeOfVal = CFL.typeOf(val),
                    toStringValue = core.toString.call(val);

                if (typeOfVal === 'null' || typeOfVal === 'undefined') {
                    val = typeOfVal;
                } else if (typeof val === 'string') {
                    if (val.length > 15) { // truncate strings longer than 15 characters
                        val = '"' + val.substring(0, 12) + '"...';
                    } else {
                        val = '"' + val + '"';
                    }
                } else if (typeOfVal === 'function') {
                    val = val.toString().substring(0, 20);
                } else if (typeOfVal !== 'number' && typeOfVal !== 'boolean') {
                    val = toStringValue;
                }

                return val;
            }

            switch( CFL.typeOf(item) ) {
                case 'object': {
                    for (key in item) {
                        if ( item.hasOwnProperty(key) ) {
                            value = stringifyValue( item[key] );
                            values.push( '"' + key + '" : ' + value );
                        }
                    }
                    return '{\n    ' + values.join(',\n    ') + '\n}';
                }
                // --------------------------------------
                case 'array': {
                    item = core.map.call(item, function (thisValue) {
                        return stringifyValue(thisValue);
                    });
                    return '[\n    ' + item.join(',\n    ') + '\n]';
                }
                // --------------------------------------
                case 'string': {
                    return '"' + item + '"';
                }
                // --------------------------------------
                case 'number': {
                    item = parseInt(item, 10);
                    if ( isNaN(item) ) { // no ternary operator, for clarity
                        return 'NaN';
                    } else {
                        return item.toString();
                    }
                }
                // --------------------------------------
                case 'regexp': {
                    if (item.toString().length <= 20) {
                        item.toString();
                    } else {
                        return '[object RegExp]';
                    }
                }
                // --------------------------------------
                case 'function': case 'boolean': {
                    return item.toString();
                }
                // --------------------------------------
                case 'null': {
                    return 'null';
                }
                // --------------------------------------
                case 'undefined': {
                    return 'undefined';
                }
                // --------------------------------------
                default: {
                    return core.toString.call(item);
                }
            }
        },

        // typeOf by Douglas Crockford. modified by CodeFelony
        typeOf : function typeOf(value) {
            var s = typeof value,
                ostr = core.toString.call(value);

            if (s === 'object' || s === 'function') {
                if (value) {
                    if (ostr === '[object Array]') {
                        s = 'array';
                    } else if ( ostr === '[object Text]' || ostr.match(rElementObject) ) {
                        s = 'element';
                    } else if (ostr === '[object HTMLCollection]') {
                        s = 'collection';
                    } else if (ostr === '[object NodeList]') {
                        s = 'nodelist';
                    } else if (ostr === '[object Arguments]') {
                        s = 'arguments';
                    } else if (ostr === '[object RegExp]') {
                        s = 'regexp';
                    }
                } else {
                    s = 'null';
                }
            }
            return s;
        },

        waitFor : function waitFor(info) {
            var verifier = function () { return true; },
                done = info ? info.done : null,
                i, selector, context, waitForInterval;

            if (info == null || typeof done !== 'function') { return; }

            switch ( CFL.typeOf(info.selector) ) {
                case 'string': case 'element': case 'array': {
                    selector = info.selector;
                    break;
                }
                default: {
                    return error('Invalid selector passed to CFL.waitFor()');
                }
            }

            switch ( CFL.typeOf(info.context) ) {
                case 'string': case 'element': case 'array': {
                    context = info.context;
                }
            }

            if (typeof info.verifier === 'function' && info.verifier.toString().indexOf('return ') !== -1) {
                verifier = info.verifier;
            }

            function clear() {
                CFL.clearInterval(waitForInterval);
            }

            function check() {
                var elem = CFL(selector, context);

                if (elem.exists && verifier(elem) === true) {
                    done(elem);
                    return clear();
                }

                if (i >= 150) { // check for 30 seconds max
                    return clear();
                }

                i += 1;
            }

            waitForInterval = CFL.setInterval(check, 200);
        },

        xpath : function xpath(obj) {
            var type = obj.type || 7,
                types = {
                    '1' : 'numberValue',
                    '2' : 'stringValue',
                    '3' : 'booleanValue',
                    '8' : 'singleNodeValue',
                    '9' : 'singleNodeValue'
                },
                expression = obj.expression,
                context = obj.context || document,
                doc = document, xp;

                if (typeof context.evaluate === 'function') {
                    doc = context;
                } else if (typeof context.ownerDocument.evaluate === 'function') {
                    doc = context.ownerDocument;
                }

                xp = doc.evaluate(expression, context, null, type, null);

            if (!expression) {
                error('An expression must be supplied for CFL.xpath()');
                return null;
            }

            if ( types[type] ) {
                return xp[ types[ type ] ];
            } else {
                return CFL.toArray(xp);
            }
        }
    });

    // assign CFL to the window object
    if (typeof window.CFL !== 'function') {
        // if it doesn't exist, just add it
        window.CFL = CFL;
        window._J = CFL;
    } else if (window.CFL.fn && window.CFL.fn.version) {
        // if it already exists, only replace it with a newer, yet
        // backwards compatible version (check for same major version)
        if ( window.CFL.fn.version.substring( 0, window.CFL.fn.version.indexOf('.') )
            === version.substring( 0, version.indexOf('.') ) ) {
            window.CFL = CFL;
            window._J = CFL;
        }
    }

    // just for testing purposes
    // unsafeWindow.CFL = unsafeWindow._J = CFL;

}(window));



// CFL test button
// use it to test code on user click (non-automatic)
/*
(function () {
    var mo = new MutationObserver(function (mutations) {
        mutations.forEach(function (mutation) {
            var target = mutation.target;

            if (mutation.attributeName === 'value' && target.value !== 'Run CFL test') {
                target.value = 'Run CFL test';
            }
        });
    });

    CFL(document.body).append(
        'input',
        {
            id : 'cfl_user_test',
            type : 'button',
            value : 'Run CFL test',
            style : 'display: block; position: fixed; top: 4px; right: 4px; z-index: 999999; padding: 2px 14px; font-size: 11pt; font-family: Arial, Verdana;',
            onclick : function () {

            // ---- ENTER ONCLICK CODE HERE ----
                window.setTimeout(function () {
                    CFL(document.body).append('<div id="waitForTest">I\'m a CFL.waitFor() test DIV!</div>');
                }, 1500);

                CFL.waitFor({
                    selector : '#waitForTest',
                    done : function (elem) {
                        alert('#waitForTest is loaded!');
                    }
                });
            // ---------------------------------
            }
        }
    );

    mo.observe( CFL('#cfl_user_test')[0], { attributes : true } );
}());
*/