2016年12月13日 星期二

(js)Area88_可獨立亦可橋接jQuery

/**
 * 林伯: 看!姓王的有錢人去吃屎
 */
(function() {
    // debugger;

    var Area88_ = window.Area88_ = function(domList) {
        // debugger;
        // (dom)包裹器
        var obj = new Area88_.prototype.init(domList);
        return obj;
    };
    /* -------------------------------------------- */
    if (window.jQuery) {
        // 若有(jquery)把什麼都交給他
        Area88_ = window.Area88_ = window.jQuery;
        return;
    } else {
        // 沒載入(jquery)好啦!就是我了
        window.$ = window.Area88_;
    }
    ////////////////////////////////////////////////////////////////////////////
    // 進入(Area88_戰區)
    var toString = Object.prototype.toString;
    var hasOwn = Object.prototype.hasOwnProperty;

    var class2type = {};
    var classArray = ['Boolean', 'Number', 'String', 'Function', 'Array', 'Date,', 'RegExp', 'Object'];

    classArray.forEach(function(name) {
        class2type["[object " + name + "]"] = name.toLowerCase();
    });
    ////////////////////////////////////////////////////////////////////////////
    // 核心

    // 指定Area88_函式的原形
    Area88_.fn = Area88_.prototype = {};

    // 包裹器
    Area88_.fn.init = init;

    // 指定包裹器的原形
    Area88_.fn.init.prototype = Area88_.prototype;

    /* ====================================================================== */
    /**
     * (dom)包裹器
     */
    function init(domList) {
        // debugger;

        this.length = 0;

        if (typeof domList == 'string') {
            domList = document.querySelectorAll(domList);
        }
        /* ---------------------------------- */
        if (typeof domList.length == 'number') {
            for (var i = 0; i < domList.length; i++) {
                this[i] = domList[i];
            }
            this.length = domList.length;
        } else if (typeof domList.nodeName != 'undefined') {
            this[0] = domList;
            this.length = 1;
        }
        /* ---------------------------------- */
        return this;
    };
    ////////////////////////////////////////////////////////////////////////////
    /**
     * 基本方法擴充(each)
     */

    (function($) {
        /**
         * 是否像(array)
         */
        $.isArraylike = $.isArraylike || function(obj) {
            // debugger;

            var length = obj.length,
                type = $.type(obj);

            if ($.isWindow(obj)) {
                return false;
            }

            if (obj.nodeType === 1 && length) {
                return true;
            }

            if (type === "array") {
                return true;
            } else if ((type !== "function") && (typeof length == "number")) {
                return true;
            }
            return false;
        };
        /* ====================================================================== */
        /**
         * 是否是{}
         */
        $.isPlainObject = $.isPlainObject || function(obj) {
            // debugger;

            // Must be an Object.
            // Because of IE, we also have to check the presence of the constructor property.
            // Make sure that DOM nodes and window objects don't pass through, as well
            if (!obj || $.type(obj) !== "object" || obj.nodeType || $.isWindow(obj)) {
                return false;
            }
            var key;
            for (key in obj) {};

            return key === undefined || hasOwn.call(obj, key);
        };
        /* ====================================================================== */
        /**
         *
         */
        $.isWindow = $.isWindow || function(obj) {
            return (obj) && (typeof obj === "object") && ("setInterval" in obj);
        };

        $.extend = $.extend || function() {
            var options, name, src, copy, copyIsArray, clone,
                target = arguments[0] || {},
                i = 1,
                length = arguments.length,
                deep = false;

            // Handle a deep copy situation
            if (typeof target === "boolean") {
                deep = target;
                target = arguments[1] || {};
                // skip the boolean and the target
                i = 2;
            }

            // Handle case when target is a string or something (possible in deep copy)
            if (typeof target !== "object" && typeof target != 'function') {
                target = {};
            }

            // extend jQuery itself if only one argument is passed
            if (length === i) {
                target = this;
                --i;
            }

            for (; i < length; i++) {
                // Only deal with non-null/undefined values
                if ((options = arguments[i]) != null) {
                    // Extend the base object
                    for (name in options) {
                        src = target[name];
                        copy = options[name];

                        // Prevent never-ending loop
                        if (target === copy) {
                            continue;
                        }

                        // Recurse if we're merging plain objects or arrays
                        if (deep && copy && ($.isPlainObject(copy) || (copyIsArray = Array.isArray(copy)))) {
                            if (copyIsArray) {
                                copyIsArray = false;
                                clone = src && Array.isArray(src) ? src : [];

                            } else {
                                clone = src && $.isPlainObject(src) ? src : {};
                            }

                            // Never move original objects, clone them
                            target[name] = $.extend(deep, clone, copy);

                            // Don't bring in undefined values
                        } else if (copy !== undefined) {
                            target[name] = copy;
                        }
                    }
                }
            }

            // Return the modified object
            return target;
        };
    })(Area88_);

    ////////////////////////////////////////////////////////////////////////////
    (function($) {
        $.fn.each = $.fn.each || function(callback) {
            // debugger;
            $.each(this, callback);
        };

        $.each = $.each || function(obj, callback) {
            // debugger;
            var value,
                i = 0,
                length = obj.length,
                isArray = $.isArraylike(obj);

            if (isArray) {
                for (; i < length; i++) {
                    value = callback.call(obj[i], i, obj[i]);

                    if (value === false) {
                        break;
                    }
                }
            } else {
                for (i in obj) {
                    value = callback.call(obj[i], i, obj[i]);

                    if (value === false) {
                        break;
                    }
                }
            }
            return obj;
        };
    })(Area88_);

    ////////////////////////////////////////////////////////////////////////////

    (function($) {
        $.type = $.type || function(obj) {
            var type;

            if (obj == null) {
                type = String(obj);
            } else {
                type = class2type[toString.call(obj)] || "object";
            }
            return type;
        };
        /* ================================================================== */

    })(Area88_);
})();
////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
(function($) {
    $.getType_ = $.getType_ || function(obj) {
        var type = toString.call(obj) || '';
        type = type.replace(/(^\s?\[object\s?)|(\]\s?$)/gi, '').toLowerCase();
        return type;
    };
})(Area88_);
////////////////////////////////////////////////////////////////////////////////
/**
 * $.deepCopy_(data)
 */
(function($) {
    var toString = Object.prototype.toString;

    $.deepCopy_ = function(data) {
        // debugger;

        var clone;
        var type = getType(data);

        // 若(data)是(不含子孫物件)
        if (data == null || (type != 'array' && type != 'map' && !isPlainObject(data))) {
            clone = data;
            return clone;
        }
        /* ---------------------------------- */
        // data含有子孫物件
        if (type == 'map') {
            clone = new Map();
        } else if (type == 'array') {
            clone = [];
        } else {
            clone = {};
        }

        copyChild(clone, data);
        //////////////////////////////////////////////////
        /**
         * 會進來的(data)一定是有子孫元素
         */
        function copyChild(target, data) {
            // debugger;
            var clone;
            var dataType = getType(data);
            var targetType = getType(target);
            /* ---------------------------------- */
            if (targetType != dataType) {
                throw new Error('要拷貝的數據型態不同');
            } else if (dataType == 'map') {
                // 若是(Map)
                data.forEach(function(child, key) {
                    var childType = getType(child);

                    if (childType != 'array' && childType != 'map' && !isPlainObject(child)) {
                        // 若子物件是單純數據
                        target.set(key, child);
                    } else {
                        // 若(child)物件還攜帶有子孫

                        // (clone)必須與(child)同型態
                        clone = getInitClone(childType);
                        /* ------------------------ */
                        // 遞回,把child拷貝到 clone
                        copyChild(clone, child);
                        target.set(key, clone);
                    }
                });

            } else if (dataType == 'array') {
                // 若是(array)

                data.forEach(function(child, key) {
                    var childType = getType(child);

                    if (childType != 'array' && childType != 'map' && !isPlainObject(child)) {
                        // 若子物件是單純數據

                        target[key] = child;

                    } else {
                        // 若子物件還攜帶有子孫

                        // (clone)必須與(child)同型態
                        clone = getInitClone(childType);
                        /* ------------------------ */
                        // 遞回,把child拷貝到 clone
                        copyChild(clone, child);
                        target[key] = clone;
                    }
                });

            } else if (isPlainObject(data)) {
                // 若(data)是(PlainObject)
                for (var key in data) {
                    if (data.hasOwnProperty(key)) {
                        var child = data[key];
                        var childType = getType(child);

                        if (childType != 'array' && childType != 'map' && !isPlainObject(child)) {
                            // 若子物件是單純數據

                            target[key] = child;

                        } else {
                            // 若子物件還攜帶有子孫

                            // (clone)必須與(child)同型態
                            clone = getInitClone(childType);
                            /* ------------------------ */
                            // 遞回,把child拷貝到 clone
                            copyChild(clone, child);
                            target[key] = clone;
                        }
                    }
                }
            } else {
                throw new Error('data have no child');
            }
        };
        /* ---------------------------------- */
        return clone;
    };

    function getInitClone(type) {
        var clone;
        switch (type) {
            case 'array':
                clone = [];
                break;
            case 'map':
                clone = new Map();
                break;
            default:
                clone = {};
                break;
        }
        return clone;
    };

    function getType(obj) {
        var type = toString.call(obj) || '';
        type = type.replace(/(^\s?\[object\s?)|(\]\s?$)/gi, '').toLowerCase();
        return type;
    };
    /* ================================================================== */
    /**
     * 只能是{}
     */
    function isPlainObject(obj) {
        if (!obj || getType(obj) !== "object") {
            return false;
        }

        var constructorName;

        try {
            constructorName = obj.constructor.toString();
        } catch (error) {}

        // 'Object() {[native code]}'
        if (!/Object\(\s*\)\s*\{\s*\[native\s*code\]\s*\}\s*$/gi.test(constructorName)) {
            return false;
        }
        return true;
    };

})(Area88_);
////////////////////////////////////////////////////////////////////////////////