PunCha

导航

JavaScript学习之: MooTools 1.4.5 源码阅读

No 废话. Start!


/*
---
MooTools: the javascript framework

web build:
 - http://mootools.net/core/dce97d7a88c57a1b0474a9a90f0687e1

packager build:
 - packager build Core/Core Core/Array Core/String Core/Number Core/Function Core/Object Core/Class Core/Class.Extras Core/JSON

...
*/

/*
---

name: Core

description: The heart of MooTools.

license: MIT-style license.

copyright: Copyright (c) 2006-2012 [Valerio Proietti](http://mad4milk.net/).

authors: The MooTools production team (http://mootools.net/developers/)

inspiration:
  - Class implementation inspired by [Base.js](http://dean.edwards.name/weblog/2006/03/base/) Copyright (c) 2006 Dean Edwards, [GNU Lesser General Public License](http://opensource.org/licenses/lgpl-license.php)
  - Some functionality inspired by [Prototype.js](http://prototypejs.org) Copyright (c) 2005-2007 Sam Stephenson, [MIT License](http://opensource.org/licenses/mit-license.php)

provides: [Core, MooTools, Type, typeOf, instanceOf, Native]

...
*/

// MT1.4.5 部分代码解读  -- by PunCha 2012/12/23

(function () {
    // 这里所有的代码都被包在一个匿名函数的内部,所以this指针很肯定指向的是JS世界的全局对象!

    // Track版本信息
    this.MooTools = {
        version: '1.4.5',
        build: 'ab8ea8824dc3b24b6666867a2c4ed58ebb762cf0'
    };

    //
    // MT库自己提供了typeOf, instanceOf函数。这两个函数是为了MT库设计的,来替代
    // JS原生的typeof, instanceof 运算符(注意大小写)。
    //

    // 这个是MT自定义的typeOf全局函数,是在JS的小写的typeof运算符的基础上做了定制和扩展。
    // 规则:
    //  1)null --> "null"
    //  2) 有$family属性的,直接返回$family属性值(MT特殊的对象)。MT把所有内置对象的原型都添加了这个属性,所以array,Number这些会在这里返回。
    //  3)有nodeName属性的,根据nodeType返回: element, textnode, whitespace
    //  4) 有length属性的,且length是数字类型,则返回arguments(如果有callee属性)或collection(如果有item属性)。不会返回'array',或'string'
    //  5)一般不会不满足。假如不满足,则返回js typeof运算符的结果:"number"、"string"、"boolean"、"object"、"function" 和 "undefined"。 
    var typeOf = this.typeOf = function (item) {
        if (item == null) return 'null';
        if (item.$family != null) return item.$family();

        if (item.nodeName) {
            if (item.nodeType == 1) return 'element';
            if (item.nodeType == 3) return (/\S/).test(item.nodeValue) ? 'textnode' : 'whitespace';
        } else if (typeof item.length == 'number') {
            if (item.callee) return 'arguments';
            if ('item' in item) return 'collection';
        }

        return typeof item;
    };

    // 这个是MT自定义的instanceOf全局函数,是在JS的小写的instanceof运算符的基础上做了定制和扩展。
    // 规则:
    //  1) null --> false.
    //  2) 根据对象的constructor属性(或者$constructor属性,这个是自定义Class时使用的),匹配对象类型。
    //  3) 使用JS的instanceof运算符。
    var instanceOf = this.instanceOf = function (item, object) {
        if (item == null) return false;
        var constructor = item.$constructor || item.constructor;
        while (constructor) {
            if (constructor === object) return true;
            constructor = constructor.parent;
        }
        /*<ltIE8>*/
        if (!item.hasOwnProperty) return false;
        /*</ltIE8>*/
        return item instanceof object;
    };

    // MT扩充了JS原生的Function对象/构造函数,这个是所有JS函数的始祖。
    //

    // this.Function是全局的Function对象(就是JS内置的Function对象)
    var Function = this.Function;

    // [Refer to 棍子上的萝卜]
    // 这里先对IE不能遍历对象中类似toString方法的bug做一个修正
    // 有些浏览器不会枚举Object中定义部分属性即使重新在对象中定义了这些属性,比如toString
    // PunCha: NodeJs的世界没有这个问题,所以enumerables永远是null。
    var enumerables = true;
    for (var i in { toString: 1 }) enumerables = null;
    if (enumerables) enumerables = ['hasOwnProperty', 'valueOf', 'isPrototypeOf', 'propertyIsEnumerable', 'toLocaleString', 'toString', 'constructor'];

    // MT的灵魂函数。总的来说,是包装一下原函数,并返回强化过的新函数。
    // 这个对原函数有限制,原函数必须有2个参数key/value。强化后的版本,可以传入key/value,也可以传入有多个key/value的一个对象。
    // 这个函数很难理解,可以结合Function.prototype.extend一起来看。
    // 这个是蛮精妙的函数,MT为在Function的原型上添加了这个方法,所以JS的所有函数都自动继承了这个方法,但是文档上面没有这个方法,说明作者是不想expose的。
    // 所有的操作都是应用在this对象上的。
    Function.prototype.overloadSetter = function (usePlural) {

        // self指代的是原函数对象(是一个函数哦)
        var self = this;

        // 包装之后的函数
        return function (a, b) {
            //  Tricky:假如不带参数的调用包装后的函数,返回非强化的版本(原函数),注意不会执行原函数的逻辑。
            if (a == null) return this;
            if (usePlural || typeof a != 'string')
            {
                //  强化!假如第一个参数不是string,那么就假设传入的是对象(并忽略参数b),枚举传入对象的每个属性,并对this对象执行原函数的逻辑(将属性,属性值作为参数传入)。
                for (var k in a) self.call(this, k, a[k]);
                if (enumerables) for (var i = enumerables.length; i--;) {
                    k = enumerables[i];
                    if (a.hasOwnProperty(k)) self.call(this, k, a[k]);
                }
            }
            else
            {
                // 这个就是执行原函数:传入a,b两个参数。维持this指针不变。在后面的应用中可以看到这个是为了在this对象上附加属性。
                self.call(this, a, b);
            }
            return this;
        };
    };

    // [Refer to 棍子上的萝卜]
    // 使得一个类似getStyle(name)的方法(本来返回value)接受一个数组参数如[name1,name2,name3],然后返回一个返回值的字面量对象如{name1:value1,name2:value,name3:value3}.
    Function.prototype.overloadGetter = function (usePlural) {
        var self = this;
        return function (a) {
            var args, result;
            if (typeof a != 'string') args = a;
            else if (arguments.length > 1) args = arguments;
            else if (usePlural) args = [a];
            if (args) {
                result = {};
                for (var i = 0; i < args.length; i++) result[args[i]] = self.call(this, args[i]);
            } else {
                result = self.call(this, a);
            }
            return result;
        };
    };

    // 这个是作者想暴露的API,逻辑简单,在this对象上附加key,value。最后wrap了一下,让这个逻辑可以应用于多次。
    // 但是要注意,wrap后的参数可以是key(字符串类型)和value,也可以是包含多个属性/值对的一个对象。
    Function.prototype.extend = function (key, value) {
        this[key] = value;
    }.overloadSetter();

    // 这个是作者想暴露的API,逻辑简单,在this的*原型*对象上附加key,value。最后wrap了一下,让这个逻辑可以应用于多次。
    // 注意点同上。
    Function.prototype.implement = function (key, value) {
        this.prototype[key] = value;
    }.overloadSetter();

    // slice函数快捷方式
    var slice = Array.prototype.slice;

    // 一切皆函数!
    Function.from = function (item) {
        return (typeOf(item) == 'function') ? item : function () {
            return item;
        };
    };

    // 一切皆数组!将类数组转化成数组;将对象转换成一个成员的数组。
    Array.from = function (item) {
        if (item == null) return [];
        return (Type.isEnumerable(item) && typeof item != 'string') ? (typeOf(item) == 'array') ? item : slice.call(item) : [item];
    };

    // 转化成数字或者null
    Number.from = function (item) {
        var number = parseFloat(item);
        return isFinite(number) ? number : null;
    };

    // 这个实现倒是简单(自动调用toString())
    String.from = function (item) {
        return item + '';
    };

    // 为函数添加$hidden, $protected “访问修饰符”。~OO之封装~
    // 但是严格的说,OO思想里面所有的变量、函数都有访问修饰符,但是作者仅仅为
    // 函数做了处理。
    //

    // 这2个函数设计的可以级联(返回this)。
    Function.implement({

        // 隐藏起来的意思是,别人“拿不走”我的这个函数。把我传给别的对象implement, extend的话,
        // 别人是得不到这个函数的。
        hide: function () {
            this.$hidden = true;
            return this;
        },

        // 把方法保护起来,这个是保护自己的方法不受别人篡改。一旦受保护,那么
        // 他就不会被同名函数覆盖;同时类的实例也不能访问该方法。
        protect: function () {
            this.$protected = true;
            return this;
        }

    });

    // Type是MT的灵魂类型之一

    // 注意,虽然这个是构造函数,但是构造出来的Type的实例是完全没有实际用处的,这个从他的返回值也可以验证。
    // 这个函数本身只用来注册类型的。object参数取名有点问题,其实他要求传入是类型的构造函数,是函数!
    // 作用是添加$family属性、添加Type.isXXXX()快捷方法,并附加上Type类的静态方法。(同时也重载掉了
    // Function.implement、extend函数,替换成Type.implement() 和 Type.extend()。) 
    var Type = this.Type = function (name, object) {
        if (name) {
            var lower = name.toLowerCase();
            var typeCheck = function (item) {
                return (typeOf(item) == lower);
            };

            // 在Type类附加了一个类静态方法,用来检查类型。
            // 所以你最好也遵从这些规范,自己写的类型也注册下,不过假如你使用MT的Class类打造你
            // 自己的类型的话,就不用了,因为Class类会帮你注册。
            Type['is' + name] = typeCheck;

            // 附加$family属性,这样typeOf就能更好的工作了。看到没,还被hide起来了。
            if (object != null) {
                object.prototype.$family = (function () {
                    return lower;
                }).hide();

            }
        }

        // 假如没有传入object参数,会使这个注册函数“失色”不少。功能变得就仅仅是提供个isXXX的快捷检查方法。
        if (object == null) return null;

        // 注意,这个this指针是正在构建的Type实例。就是说,对传入的对象构造函数,会在本身附加Type原型的成员。
        // 注意:
        //  1)是原型的成员,而不是Type本身的,比如上面的Type[is+name]是不会有的。
        //  2)是对传入的对象的构造函数本身,而不是其原型,所以构造出来的对象是不会有Type的成员的。
        //  3)不同类型的object,其extend实现实不同的。一般是调用Fuction.extend。
        object.extend(this);

        // 注册了之后,会附加$constructor属性,这样instanceOf就能工作了。
        object.$constructor = Type;
        // 呵呵,做事做全套。
        object.prototype.$constructor = object;

        return object;
    };

    // 导出Object始祖对象的toString()实现。
    var toString = Object.prototype.toString;

    // 对象有length属性,且不是函数。
    Type.isEnumerable = function (item) {
        return (item != null && typeof item.length == 'number' && toString.call(item) != '[object Function]');
    };

    // 这是一个Map,key是不同的类别。唯一有可能往这个数组里面添加项目的是Type.mirror()函数。
    var hooks = {};

    // 根据对象的类别取得该类别所有相关类型的数组。
    var hooksOf = function (object) {
        var type = typeOf(object.prototype);
        return hooks[type] || (hooks[type] = []);
    };

    // 这个方法是附属于对象的,因为他里面用到了this.prototype,所以this指针是对象的构造函数!
    // 这个函数被Type.implement使用,一大作用就是会重载掉在Type里面注册的类型的Function.implement函数!
    var implement = function (name, method) {

        // 跳过私有函数,私有函数不能赋予其他对象。
        if (method && method.$hidden) return;

        // 获取对象类别
        var hooks = hooksOf(this);

        // 对于该类别的每个相关类型
        for (var i = 0; i < hooks.length; i++) {
            var hook = hooks[i];
            if (typeOf(hook) == 'type') {
                // 如果是Type类型,则为Type类型的原型定义(implement)此方法或属性!
                implement.call(hook, name, method);
            }
            else {
                // 否则运行这个钩子函数(hook[i]是外部通过调用mirror时提供的,只要是函数就行)
                // 感觉就是给了某些特殊的应用一次处理的时间。
                hook.call(this, name, method);
            }
        }

        // 在this的原型上(重)定义该方法(跳过受保护的方法),所以该类型的实例会继承这个方法。
        var previous = this.prototype[name];
        if (previous == null || !previous.$protected) this.prototype[name] = method;

        // 如果本身没有这个方法,那么定义一个同名的可以调用它实例的方法(类级别的静态方法!)
        // MT的原则是函数多多益善啊!
        if (this[name] == null && typeOf(method) == 'function') extend.call(this, name, function (item) {
            return method.apply(item, slice.call(arguments, 1));
        });
    };

    // Type.extend
    var extend = function (name, method) {

        // 私有方法不能给别人。
        if (method && method.$hidden) return;

        // 没有当然给一个。已经有了,而且被保护了,那么不能改写。
        var previous = this[name];
        if (previous == null || !previous.$protected) this[name] = method;
    };

    // Type本身是一个函数,所以这里implement是调用Function.implement,相当于把这些方法附属到Type原型上
    // 所以任何type实例,或继承自Type都有了这些方法。
    Type.implement({

        // Type实例将使用特有的implement(非Function.implement)
        implement: implement.overloadSetter(),

        // Type实例将使用特有的extend(非Function.extend)
        extend: extend.overloadSetter(),

        // 为这个函数取一个别名。注意,别名定义在implement和类本身上
        alias: function (name, existing) {
            implement.call(this, name, this.prototype[existing]);
        }.overloadSetter(),

        // 增加一个同类别的类型,注意这是唯一一个地方push一个对象到hooks数组。
        // [Refer to 棍子上的萝卜]
        // 将hook(一般为一个函数)暂时存放在hooks[type]的数组中,不作处理,直到这个Type类型的构造函数implement的时候
        // 其中type是这个Type类型的构造函数指定的name属性,
        // 如果传入hook为另外一个Type类型则在implement时另外一个Type类型也会被连带处理。
        // mirror方法非常重要,相当于1.2中的afterImplement,比如Element类implement了一个方法 setStyle ,如果在连带的hook中处理了Elements
        // 那它implement的同时也会影响到Elements了!!!所以$$(".className").setStyle(xx,xx)是正确的语法!!!太高端了mootools
        // PunCha: 其实我还是完全不懂,但是我写了个example:
        //   function A() {}
        //   function B() {}
        //   new Type("banana", A).mirror(function (name) {console.log("A-" + name);});
        //   new Type("banana", B).mirror(function (name) {console.log("B-" + name);});
        //   A.implement("Wow", "value");
        // 输出: A-Wow  B-Wow
        // 啥用处???!
        mirror: function (hook) {
            hooksOf(this).push(hook);
            return this;
        }

    });

    // 对Type本身进行注册,所以Type本身也有了Type原型的方法。
    new Type('Type', Type);

    // 简而言之,new Type(name,func)即仅仅只对输入的func做了一点加工,
    // 比如使其对typeOf以及instanceOf有效。使其可以使用implement以及
    // extend, mirror等等方法,还有Type.isname的快捷方法。
    //------------------------------------------------------------


    // 开始构建MT的Type大军,将内置类型一个一个变成Type
    //


    // 为传入的类型注册。对传入的类型的原型附加多个成员函数;
    // 注意!Object类型,没有注册,只不过做了下保护。
    var force = function (name, object, methods) {

        // 只有在object是Object类型的时候,isType才会是false。也就是始祖对象。
        var isType = (object != Object),
            prototype = object.prototype;

        // 注册。注意object还是传入的object,其实没变(Type内部返回object)
        if (isType) object = new Type(name, object);

        for (var i = 0, l = methods.length; i < l; i++) {
            var key = methods[i],
                generic = object[key],
                proto = prototype[key];

            // 如果该类型本身有这个方法,保护起来免得被改写!比如Array.push()
            if (generic) generic.protect();
            // 如果原型上有这个方法,粗鲁的将他改成受保护的。
            // TODO:搞不懂,为什么不直接调用下proto.protect()?
            if (isType && proto) object.implement(key, proto.protect());
        }

        // 对于非Object类型
        if (isType) {

            // 赠送一个方法?
            var methodsEnumerable = prototype.propertyIsEnumerable(methods[0]);
            // 反正就是挨个调用成员函数。你传入的fn必须支持2个参数,第一个是值,第二个是属性
            object.forEachMethod = function (fn) {
                if (!methodsEnumerable) for (var i = 0, l = methods.length; i < l; i++) {
                    fn.call(prototype, prototype[methods[i]], methods[i]);
                }
                for (var key in prototype) fn.call(prototype, prototype[key], key)
            };
        }

        return force;
    };

    // 构建Type大军,注册了所有内置类型!且这些方法都被保护了起来。
    force('String', String, [
        'charAt', 'charCodeAt', 'concat', 'indexOf', 'lastIndexOf', 'match', 'quote', 'replace', 'search',
        'slice', 'split', 'substr', 'substring', 'trim', 'toLowerCase', 'toUpperCase'
    ])('Array', Array, [
        'pop', 'push', 'reverse', 'shift', 'sort', 'splice', 'unshift', 'concat', 'join', 'slice',
        'indexOf', 'lastIndexOf', 'filter', 'forEach', 'every', 'map', 'some', 'reduce', 'reduceRight'
    ])('Number', Number, [
        'toExponential', 'toFixed', 'toLocaleString', 'toPrecision'
    ])('Function', Function, [
        'apply', 'call', 'bind'
    ])('RegExp', RegExp, [
        'exec', 'test'
    ])('Object', Object, [
        'create', 'defineProperty', 'defineProperties', 'keys',
        'getPrototypeOf', 'getOwnPropertyDescriptor', 'getOwnPropertyNames',
        'preventExtensions', 'isExtensible', 'seal', 'isSealed', 'freeze', 'isFrozen'
    ])('Date', Date, ['now']);

    // 对Object进行特殊的扩展,Object没有$family、以及implement, extend属性。在这里,为其附加extend方法,也就说
    // 所有的{}都可以用这个函数附加方法了。而且这个是强化版的。
    Object.extend = extend.overloadSetter();

    // 替换了Date的默认now实现。
    Date.extend('now', function () {
        return +(new Date);
    });

    // 为boolean做了注册,为什么要单独注册?可能是因为他没有成员函数
    new Type('Boolean', Boolean);

    // 修正NaN会返回number类型的问题
    Number.prototype.$family = function () {
        return isFinite(this) ? 'number' : 'null';
    }.hide();

    // 这个比Math.Random好用多了。
    Number.extend('random', function (min, max) {
        return Math.floor(Math.random() * (max - min + 1) + min);
    });

    var hasOwnProperty = Object.prototype.hasOwnProperty;

    // forEach方法,仅对真正的property才调用。
    Object.extend('forEach', function (object, fn, bind) {
        for (var key in object) {
            if (hasOwnProperty.call(object, key)) fn.call(bind, object[key], key, object);
        }
    });

    Object.each = Object.forEach;

    Array.implement({

        forEach: function (fn, bind) {
            for (var i = 0, l = this.length; i < l; i++) {
                if (i in this) fn.call(bind, this[i], i, this);
            }
        },

        each: function (fn, bind) {
            Array.forEach(this, fn, bind);
            return this;
        }

    });

    // Array & Object cloning, Object merging and appending

    // 深度克隆啊,只对array和object处理就够了吗?
    var cloneOf = function (item) {
        switch (typeOf(item)) {
            case 'array': return item.clone();
            case 'object': return Object.clone(item);
            default: return item;
        }
    };

    Array.implement('clone', function () {
        var i = this.length, clone = new Array(i);
        while (i--) clone[i] = cloneOf(this[i]);
        return clone;
    });

    var mergeOne = function (source, key, current) {
        switch (typeOf(current)) {
            case 'object':
                if (typeOf(source[key]) == 'object') Object.merge(source[key], current);
                else source[key] = Object.clone(current);
                break;
            case 'array': source[key] = current.clone(); break;
            default: source[key] = current;
        }
        return source;
    };

    // Object又多了3个类的静态方法。
    Object.extend({

        merge: function (source, k, v) {
            if (typeOf(k) == 'string') return mergeOne(source, k, v);
            for (var i = 1, l = arguments.length; i < l; i++) {
                var object = arguments[i];
                for (var key in object) mergeOne(source, key, object[key]);
            }
            return source;
        },

        clone: function (object) {
            var clone = {};
            for (var key in object) clone[key] = cloneOf(object[key]);
            return clone;
        },

        append: function (original) {
            for (var i = 1, l = arguments.length; i < l; i++) {
                var extended = arguments[i] || {};
                for (var key in extended) original[key] = extended[key];
            }
            return original;
        }

    });

    // Object-less types

    // 这里其实不是严格意义上的注册,因为第二个参数没传进去。所以,这里仅仅是为Type添加isXXX的快捷方法。
    ['Object', 'WhiteSpace', 'TextNode', 'Collection', 'Arguments'].each(function (name) {
        new Type(name);
    });

    // Unique ID

    var UID = Date.now();

    String.extend('uniqueID', function () {
        return (UID++).toString(36);
    });



})();


/*
---

name: Array

description: Contains Array Prototypes like each, contains, and erase.

license: MIT-style license.

requires: Type

provides: Array

...
*/

// 这里的implement方法是Type.implement。这些函数没什么多说的,都很简单。
Array.implement({

    /*<!ES5>*/
    every: function (fn, bind) {
        for (var i = 0, l = this.length >>> 0; i < l; i++) {
            if ((i in this) && !fn.call(bind, this[i], i, this)) return false;
        }
        return true;
    },

    filter: function (fn, bind) {
        var results = [];
        for (var value, i = 0, l = this.length >>> 0; i < l; i++) if (i in this) {
            value = this[i];
            if (fn.call(bind, value, i, this)) results.push(value);
        }
        return results;
    },

    indexOf: function (item, from) {
        var length = this.length >>> 0;
        for (var i = (from < 0) ? Math.max(0, length + from) : from || 0; i < length; i++) {
            if (this[i] === item) return i;
        }
        return -1;
    },

    map: function (fn, bind) {
        var length = this.length >>> 0, results = Array(length);
        for (var i = 0; i < length; i++) {
            if (i in this) results[i] = fn.call(bind, this[i], i, this);
        }
        return results;
    },

    some: function (fn, bind) {
        for (var i = 0, l = this.length >>> 0; i < l; i++) {
            if ((i in this) && fn.call(bind, this[i], i, this)) return true;
        }
        return false;
    },
    /*</!ES5>*/

    clean: function () {
        return this.filter(function (item) {
            return item != null;
        });
    },

    invoke: function (methodName) {
        var args = Array.slice(arguments, 1);
        return this.map(function (item) {
            return item[methodName].apply(item, args);
        });
    },

    associate: function (keys) {
        var obj = {}, length = Math.min(this.length, keys.length);
        for (var i = 0; i < length; i++) obj[keys[i]] = this[i];
        return obj;
    },

    link: function (object) {
        var result = {};
        for (var i = 0, l = this.length; i < l; i++) {
            for (var key in object) {
                if (object[key](this[i])) {
                    result[key] = this[i];
                    delete object[key];
                    break;
                }
            }
        }
        return result;
    },

    contains: function (item, from) {
        return this.indexOf(item, from) != -1;
    },

    append: function (array) {
        this.push.apply(this, array);
        return this;
    },

    getLast: function () {
        return (this.length) ? this[this.length - 1] : null;
    },

    getRandom: function () {
        return (this.length) ? this[Number.random(0, this.length - 1)] : null;
    },

    include: function (item) {
        if (!this.contains(item)) this.push(item);
        return this;
    },

    combine: function (array) {
        for (var i = 0, l = array.length; i < l; i++) this.include(array[i]);
        return this;
    },

    erase: function (item) {
        for (var i = this.length; i--;) {
            if (this[i] === item) this.splice(i, 1);
        }
        return this;
    },

    empty: function () {
        this.length = 0;
        return this;
    },

    flatten: function () {
        var array = [];
        for (var i = 0, l = this.length; i < l; i++) {
            var type = typeOf(this[i]);
            if (type == 'null') continue;
            array = array.concat((type == 'array' || type == 'collection' || type == 'arguments' || instanceOf(this[i], Array)) ? Array.flatten(this[i]) : this[i]);
        }
        return array;
    },

    pick: function () {
        for (var i = 0, l = this.length; i < l; i++) {
            if (this[i] != null) return this[i];
        }
        return null;
    },

    // 这个array参数是boolean类型的。。这设计的超奇怪,呵呵。
    hexToRgb: function (array) {
        if (this.length != 3) return null;
        var rgb = this.map(function (value) {
            if (value.length == 1) value += value;
            return value.toInt(16);
        });
        return (array) ? rgb : 'rgb(' + rgb + ')';
    },

    rgbToHex: function (array) {
        if (this.length < 3) return null;
        if (this.length == 4 && this[3] == 0 && !array) return 'transparent';
        var hex = [];
        for (var i = 0; i < 3; i++) {
            var bit = (this[i] - 0).toString(16);
            hex.push((bit.length == 1) ? '0' + bit : bit);
        }
        return (array) ? hex : '#' + hex.join('');
    }

});




/*
---

name: String

description: Contains String Prototypes like camelCase, capitalize, test, and toInt.

license: MIT-style license.

requires: Type

provides: String

...
*/

String.implement({

    test: function (regex, params) {
        return ((typeOf(regex) == 'regexp') ? regex : new RegExp('' + regex, params)).test(this);
    },

    contains: function (string, separator) {
        return (separator) ? (separator + this + separator).indexOf(separator + string + separator) > -1 : String(this).indexOf(string) > -1;
    },

    trim: function () {
        return String(this).replace(/^\s+|\s+$/g, '');
    },

    clean: function () {
        return String(this).replace(/\s+/g, ' ').trim();
    },

    camelCase: function () {
        return String(this).replace(/-\D/g, function (match) {
            return match.charAt(1).toUpperCase();
        });
    },

    hyphenate: function () {
        return String(this).replace(/[A-Z]/g, function (match) {
            return ('-' + match.charAt(0).toLowerCase());
        });
    },

    capitalize: function () {
        return String(this).replace(/\b[a-z]/g, function (match) {
            return match.toUpperCase();
        });
    },

    escapeRegExp: function () {
        return String(this).replace(/([-.*+?^${}()|[\]\/\\])/g, '\\$1');
    },

    toInt: function (base) {
        return parseInt(this, base || 10);
    },

    toFloat: function () {
        return parseFloat(this);
    },

    hexToRgb: function (array) {
        var hex = String(this).match(/^#?(\w{1,2})(\w{1,2})(\w{1,2})$/);
        return (hex) ? hex.slice(1).hexToRgb(array) : null;
    },

    rgbToHex: function (array) {
        var rgb = String(this).match(/\d{1,3}/g);
        return (rgb) ? rgb.rgbToHex(array) : null;
    },

    substitute: function (object, regexp) {
        return String(this).replace(regexp || (/\\?\{([^{}]+)\}/g), function (match, name) {
            if (match.charAt(0) == '\\') return match.slice(1);
            return (object[name] != null) ? object[name] : '';
        });
    }

});


/*
---

name: Number

description: Contains Number Prototypes like limit, round, times, and ceil.

license: MIT-style license.

requires: Type

provides: Number

...
*/

Number.implement({

    limit: function (min, max) {
        return Math.min(max, Math.max(min, this));
    },

    round: function (precision) {
        precision = Math.pow(10, precision || 0).toFixed(precision < 0 ? -precision : 0);
        return Math.round(this * precision) / precision;
    },

    times: function (fn, bind) {
        for (var i = 0; i < this; i++) fn.call(bind, i, this);
    },

    toFloat: function () {
        return parseFloat(this);
    },

    toInt: function (base) {
        return parseInt(this, base || 10);
    }

});

// 这个比较有意思!取了个别名。Number.each == Number.times
Number.alias('each', 'times');

(function (math) {
    var methods = {};
    math.each(function (name) {
        if (!Number[name]) methods[name] = function () {
            return Math[name].apply(null, [this].concat(Array.from(arguments)));
        };
    });
    Number.implement(methods);
})(['abs', 'acos', 'asin', 'atan', 'atan2', 'ceil', 'cos', 'exp', 'floor', 'log', 'max', 'min', 'pow', 'sin', 'sqrt', 'tan']);


/*
---

name: Function

description: Contains Function Prototypes like create, bind, pass, and delay.

license: MIT-style license.

requires: Type

provides: Function

...
*/

Function.extend({

    // 安全的执行多个无参函数,这个是类静态方法。
    attempt: function () {
        for (var i = 0, l = arguments.length; i < l; i++) {
            try {
                return arguments[i]();
            } catch (e) { }
        }
        return null;
    }

});

Function.implement({

    // 这个是实例方法。
    attempt: function (args, bind) {
        try {
            return this.apply(bind, Array.from(args));
        } catch (e) { }

        return null;
    },

    // 假如没有bind函数,那么我们写一个!
    /*<!ES5-bind>*/
    bind: function (that) {
        var self = this,
			args = arguments.length > 1 ? Array.slice(arguments, 1) : null,
			F = function () { };

        var bound = function () {
            var context = that, length = arguments.length;
            if (this instanceof bound) {
                F.prototype = self.prototype;
                context = new F;
            }
            var result = (!args && !length)
				? self.call(context)    // 无参
				: self.apply(context, args && length ? args.concat(Array.slice(arguments)) : args || arguments);
            return context == that ? result : context;
        };
        return bound;
    },
    /*</!ES5-bind>*/

    pass: function (args, bind) {
        var self = this;
        if (args != null) args = Array.from(args);
        return function () {
            return self.apply(bind, args || arguments);
        };
    },

    delay: function (delay, bind, args) {
        return setTimeout(this.pass((args == null ? [] : args), bind), delay);
    },

    periodical: function (periodical, bind, args) {
        return setInterval(this.pass((args == null ? [] : args), bind), periodical);
    }

});




/*
---

name: Object

description: Object generic methods

license: MIT-style license.

requires: Type

provides: [Object, Hash]

...
*/

(function () {

    var hasOwnProperty = Object.prototype.hasOwnProperty;

    Object.extend({

        subset: function (object, keys) {
            var results = {};
            for (var i = 0, l = keys.length; i < l; i++) {
                var k = keys[i];
                if (k in object) results[k] = object[k];
            }
            return results;
        },

        map: function (object, fn, bind) {
            var results = {};
            for (var key in object) {
                if (hasOwnProperty.call(object, key)) results[key] = fn.call(bind, object[key], key, object);
            }
            return results;
        },

        filter: function (object, fn, bind) {
            var results = {};
            for (var key in object) {
                var value = object[key];
                if (hasOwnProperty.call(object, key) && fn.call(bind, value, key, object)) results[key] = value;
            }
            return results;
        },

        every: function (object, fn, bind) {
            for (var key in object) {
                if (hasOwnProperty.call(object, key) && !fn.call(bind, object[key], key)) return false;
            }
            return true;
        },

        some: function (object, fn, bind) {
            for (var key in object) {
                if (hasOwnProperty.call(object, key) && fn.call(bind, object[key], key)) return true;
            }
            return false;
        },

        keys: function (object) {
            var keys = [];
            for (var key in object) {
                if (hasOwnProperty.call(object, key)) keys.push(key);
            }
            return keys;
        },

        values: function (object) {
            var values = [];
            for (var key in object) {
                if (hasOwnProperty.call(object, key)) values.push(object[key]);
            }
            return values;
        },

        getLength: function (object) {
            return Object.keys(object).length;
        },

        keyOf: function (object, value) {
            for (var key in object) {
                if (hasOwnProperty.call(object, key) && object[key] === value) return key;
            }
            return null;
        },

        contains: function (object, value) {
            return Object.keyOf(object, value) != null;
        },

        toQueryString: function (object, base) {
            var queryString = [];

            Object.each(object, function (value, key) {
                if (base) key = base + '[' + key + ']';
                var result;
                switch (typeOf(value)) {
                    case 'object': result = Object.toQueryString(value, key); break;
                    case 'array':
                        var qs = {};
                        value.each(function (val, i) {
                            qs[i] = val;
                        });
                        result = Object.toQueryString(qs, key);
                        break;
                    default: result = key + '=' + encodeURIComponent(value);
                }
                if (value != null) queryString.push(result);
            });

            return queryString.join('&');
        }

    });

})();




/*
---

name: Class

description: Contains the Class Function for easily creating, extending, and implementing reusable Classes.

license: MIT-style license.

requires: [Array, String, Function, Number]

provides: Class

...
*/

(function () {

    // 这里其实分2步写会比较清楚:
    // function Class (params) { .... }
    // new Type('Class', Class);
    // Type注册函数会返回定义的function(params)。
    var Class = this.Class = new Type('Class', function (params) {
        // 如果参数是一个函数,那么就当成是initialize(初始化)函数
        if (instanceOf(params, Function)) params = { initialize: params };

        // 这个函数很关键,调用new Class()就会返回这个函数。当然这个函数的constructor是Function。
        // 所以不要去管返回的类型的constructor了。new Class(xxx).constructor == 这个函数。
        // 注意,外部函数的this是正在构建的Class的实例,而其内部的this是指向将来被构造的该类型的实例,
        // 这个和Class对象无关。 
        // 细节:新建这个函数作为新建的类的原型, 然后调用extend函数把Class所有的成员复制给newClass,
        // 然后params对象implement到newClass类中,这里调用的implement是Class的implement方法。
        var newClass = function () {

            // 剥离这个this对象的外部一切关联。因为是原型继承,所以怕引用的类型会被所有实例共享。
            reset(this);
            // 在构造子类的过程中,MT会自动创建一个父类的实例,作为子类的原型,那么在构造父类的过程中
            // 是不应该调用其initialize函数的。应该由子类的initialize函数显式调用。
            if (newClass.$prototyping) return this;
            this.$caller = null;
            // 调用构造函数,这个initialize函数是通过implement(params)生成的。
            var value = (this.initialize) ? this.initialize.apply(this, arguments) : this;
            this.$caller = this.caller = null;
            return value;
        }.extend(this).implement(params);  // 这里的this是Class对象了!

        // 其实构造出来的实例的constructor都不太对,所以真实的一些信息都在这里保存。
        // 我们在使用过程中,应该使用$constructor,就像instanceOf那样。
        newClass.$constructor = Class;
        newClass.prototype.$constructor = newClass;

        // 这里的Parent是下面定义的Parent函数。这个是原型上的parent。
        newClass.prototype.parent = parent;

        // 注意哦,这里返回的是函数,而不是对象!所以 var Foo = new Class(XXX);Foo不是对象是构造函数。
        return newClass;
    });

    // 这个是让子类调用父类被override的函数用的, 在子类的成员函数里面调用下this.parent()
    // 就可以了,连函数名都不用指定,因为这些必要的调用信息在wrap函数时提供。
    var parent = function () {

        // parent方法只能在子类里面调用,也就是说这个方法肯定是在wrap的wrapper函数内部被
        // 触发的,那么$caller肯定也就会被正确的设置了。$caller就是wrapper。
        if (!this.$caller) throw new Error('The method "parent" cannot be called.');

        var name = this.$caller.$name,  // 函数名
            parent = this.$caller.$owner.parent,    // 其实就是该对象的父类,可以用this.$constrcutor替代。
            previous = (parent) ? parent.prototype[name] : null;    // 父对象的函数
        if (!previous) throw new Error('The method "' + name + '" has no parent.');// 父对象一定要有子类的这个函数,不然怎么调用!
        return previous.apply(this, arguments); // 用this指针进行调用。
    };

 
    // [Refer to 棍子上的萝卜]
    // 对象的剥离(也就是clone),这里要详细说明一下reset函数的工作原理:
    // 首先创建了一个新的空函数F,然后将F的prototype属性设置为作为参数object传入的原型对象,prototype属性就是用来指向原型对象的,通过原型链机制,
    // 它提供了到所有继承而来的成员的链接,最后通过new运算符作用于F创建出一个新对象返回。这个新的对象就是一个以给定对象为原型对象的空对象,
    // 以下面的例子来解说,先执行reset(b)语句,然后读取b.ref.x的值,这时你得到的是其原型对象的同名属性值,其实是一个返指最初的a.x的链接,
    // 而在这之后你写入b.ref.x一个新值,也就是直接为b.ref对象定义了一个新的属性x,这时你再读取b.ref.x就不是指向a.x了
    // 如果想详细了解原型式继承可翻阅JavaScript设计模式一书,非常棒的一本书,真的很棒!!!哈哈......
    // var a = { x: 1 };
    // var b = { y: 2, ref: a };
    // log.info('b.ref == a : ' + (b.ref == a)); //输出true
    // log.info(b.y); // 输出2
    // log.info(b.ref.x); // 输出1
    // reset(b); //解除引用
    // log.info('b.ref == a : ' + (b.ref == a)); //输出false
    // log.info(b.y); // 输出2
    // log.info(b.ref.x); // 输出1
    // b.ref.x = 10;
    // log.info(b.ref.x); // 输出10
    // log.info(a.x); // 输出1
    var reset = function (object) {
        for (var key in object) {
            var value = object[key];
            switch (typeOf(value)) {
                case 'object':
                    var F = function () { };
                    F.prototype = value;
                    object[key] = reset(new F);
                    break;
                case 'array': object[key] = value.clone(); break;
            }
        }
        return object;
    };

    // 把成员函数包一下,并添加$owner, $origin, $name3个属性。最终真正执行的是被包过的wrapper。
    // 注意,这个函数配合parent函数是实现回溯调用的关键。回溯的意思是,假如类库有多级(C继承B,
    // B继承A,而且ABC上都实现了foo()函数,每一级的foo()都会调用其父类的foo。那么:
    // var c = new C(); c.foo(); 会一级一级正确的调用上去!
    var wrap = function (self, key, method) {

        if (method.$origin) method = method.$origin;

        // 真正执行的是这个函数,注意这里用了extend,保存了必要的信息。这些信息很重要!举个例子,
        // D是B的子类,B和D都有foo(), bar(),且B的foo()会调用bar(),那么D.foo()执行的时候,首先会调用B.foo(),
        // 而这时候B.foo()内部调用的bar()不是B的,而是D的!这个就是多态!
        var wrapper = function () {
            // 这里的设计真的很奇怪!假如这个成员函数是受保护的,那么只有在子类的同名函数里
            // 通过parent()来调用外,其他情况下都不能调用!包括这个类的实例或者其他成员函数!
            // 这个实在不知道为什么这么设计。。。。
            if (method.$protected && this.$caller == null) throw new Error('The method "' + key + '" cannot be called.');
            // 保留caller信息
            var caller = this.caller;
            // 保留$caller信息(最后一级的调用,this.$caller是空,但是回溯到上一级,
            // 那么$caller就指向前一次的wrapper函数。
            var current = this.$caller;
            // caller设置到$caller
            this.caller = current;
            // $caller指向wrapper本身,所以parent才能正确调用。因为parent每次都会从$caller取信息
            // 同时要注意,每次的回溯,$caller值会不一样。
            this.$caller = wrapper;
            // 触发成员函数的调用(注意,parent()会在这里面被调用,而一旦parent被调用,又会触发wrapper被
            // 再次调用,但是这个时候$caller已经不同了。
            var result = method.apply(this, arguments);
            // 还原,把$caller设置为null是重要的。
            this.$caller = current; this.caller = caller;
            return result;
        }.extend({ $owner: self, $origin: method, $name: key });    // this指针,函数本身,函数名。
        return wrapper;
    };


    // 核心函数:实现Extends/Implements,成员函数的包装,普通属性的附加。
    var implement = function (key, value, retain) {

        // 对于特定的key,即:Extends, Implements,把逻辑交给Clas.Mutators类处理。
        // 该类负责把对value进行一些处理。这个很灵活,但是你的成员函数就不能取这2个
        // 名字了。
        if (Class.Mutators.hasOwnProperty(key)) {
            value = Class.Mutators[key].call(this, value);
            // 假如处理后的value变成了null,那么就停止后续的处理了。Extends和
            // Implements的返回值都是null。
            if (value == null) return this;
        }
        // 所有的成员函数,都会被包在wrap函数内部,wrap负责保存一些场景信息,以便
        // 子类调用父类用。要注意,hidden函数会被跳过。(Extends和Implements都回
        // 返回null)
        if (typeOf(value) == 'function') {
            // 拿不走hidden的
            if (value.$hidden) return this;
            // 在Impements内部调用本函数时retain参数设为ture,表明只是合并方法到原型中,
            // 不用包一下。而在其他时候,用wrap函数包
            this.prototype[key] = (retain) ? value : wrap(this, key, value);
        } else {
            // 普通的属性是会被覆盖的。
            Object.merge(this.prototype, key, value);
        }

        return this;
    };

    // 构建一个实例。注意klass是一个构造函数!
    var getInstance = function (klass) {
        // 标记一下,防止klass的initialize函数被调用。
        klass.$prototyping = true;
        var proto = new klass;
        delete klass.$prototyping;
        return proto;
    };

    // 设置Class.implement方法
    Class.implement('implement', implement.overloadSetter());

    // [Refer to 棍子上的萝卜]
    // Mutator是一个可以改变你的类的结构的一个很特殊的函数,它们是产生特别功能和优雅化继承和掺元的的有力工具。
    // 建立一个Mutatorr有二个部分:mutator的关键字 和mutator的实际函数,关键字既是mutator的名字,
    // 也是在构建类时候的keyword。Mootools把mutators 储存在Class.Mutators对象中。
    // 当你传一个对象给Class构造函数的时候,Mootools检查这个对象的的每一个键在Class.Mutators对象的是不是有
    // mutator函数的对应的名字在里面。如果找到了,它就调用这个函数并且把键的值传给它做处理。
    // Class.Mutators对象包含了两个内建的Mutator: Extends 和 Implements,分别实现原型式继承和多亲继承。
    // MooTools在Class.Extras模块中提供了三个掺元类Chain、Events、Options,至于作用就不用多说了吧,呵呵。
    Class.Mutators = {

        // 这里会重写原型,所以Extends属性一定要放在最前面传入!
        Extends: function (parent) {

            // 添加了parent属性。注意和原型上的parent这个parent是新的类型上的,而不是原型上的。
            // 原型上还有另外一个parent函数。。。这2个Parent好乱。
            this.parent = parent;

            // 改写了原型链,而且注意,是新建了一个实例,所以不存在信息会共享的情况。但是MT在
            // 这里没有修正constrcutor信息。还有,那些基类的成员函数,自动的被继承过来了,这个
            // 对于实现回溯调用也起到了一定的作用。
            this.prototype = getInstance(parent);
        },

        // items必须是一个或多个构造函数,而不能是简单的对象。
        Implements: function (items) {
            Array.from(items).each(function (item) {
                var instance = new item;
                for (var key in instance) implement.call(this, key, instance[key], true);
            }, this);
        }
    };

})();


/*
---

name: Class.Extras

description: Contains Utility Classes that can be implemented into your own Classes to ease the execution of many common tasks.

license: MIT-style license.

requires: Class

provides: [Class.Extras, Chain, Events, Options]

...
*/

(function () {

    // 这个就是一个很普通的队列的实现
    this.Chain = new Class({

        $chain: [],

        // 入队
        chain: function () {
            this.$chain.append(Array.flatten(arguments));
            return this;
        },

        // 出队并执行,使用这个语句让他出队执行完毕:
        // while (false !== XXX.callChain()) {}
        callChain: function () {
            return (this.$chain.length) ? this.$chain.shift().apply(this, arguments) : false;
        },

        // 清楚队列
        clearChain: function () {
            this.$chain.empty();
            return this;
        }

    });

    // 把onXyyy变成xyyy(把前缀on去掉,把去调之后的第一个字符变成小写)
    // 其实隐含的限制就是:事件类型必须是on开头的。
    var removeOn = function (string) {
        return string.replace(/^on([A-Z])/, function (full, first) {
            return first.toLowerCase();
        });
    };

    this.Events = new Class({

        $events: {},

        // 往事件队列里面塞一个事件处理程序。根据文档,internal是为了避免被remove。。。
        addEvent: function (type, fn, internal) {
            type = removeOn(type);


            // 建立一个改事件类型的数组,把事件处理程序塞进去(不会重复)。
            this.$events[type] = (this.$events[type] || []).include(fn);

            // 标记为internal,这个标记在原函数对象上。。。is it OK?
            if (internal) fn.internal = true;
            return this;
        },

        // 往事件队列里面塞多个事件处理程序,传入的时对象字面量
        addEvents: function (events) {
            for (var type in events) this.addEvent(type, events[type]);
            return this;
        },

        // 触发事件,传入“事件类型、参数、延迟”。还支持延迟,HOHO
        fireEvent: function (type, args, delay) {

            type = removeOn(type);
            var events = this.$events[type];

            // 该事件类型没注册,直接走人
            if (!events) return this;
            args = Array.from(args);

            // 使用this指针绑定调用事件处理程序
            events.each(function (fn) {
                if (delay) fn.delay(delay, this, args);
                else fn.apply(this, args);
            }, this);
            return this;
        },

        // 移除掉某一事件处理程序(不能移除被标记为internal的)
        removeEvent: function (type, fn) {
            type = removeOn(type);
            var events = this.$events[type];
            if (events && !fn.internal) {
                var index = events.indexOf(fn);
                if (index != -1) delete events[index];
            }
            return this;
        },

        // 移除掉某一事件处理程序(不能移除被标记为internal的)。哎,这个函数提供的功能有点不一致啊!!真头痛!
        removeEvents: function (events) {
            var type;
            if (typeOf(events) == 'object') {
                // 这种情况下,移除多个事件处理程序。
                for (type in events) this.removeEvent(type, events[type]);
                return this;
            }

            // 移除该事件类型的所有事件处理程序(internal的不会被移除,因为其内部调用removeEvent)
            if (events) events = removeOn(events);
            for (type in this.$events) {
                if (events && events != type) continue;
                var fns = this.$events[type];
                for (var i = fns.length; i--;) if (i in fns) {
                    this.removeEvent(type, fns[i]);
                }
            }
            return this;
        }

    });

    this.Options = new Class({

        // 一种功能是:统一添加事件处理程序。传入类似于{onClick: fuction(){}, onDblClick: function(){}}这种的。
        // 另外种功能就是把key/value插入到options成员中(但是会做深拷贝),比如: {"size": 3, "color" : red },
        // 具体可以看文档,要避免深拷贝的话,可以把值用函数封装:{ "size": function(){ return mSize; }
        setOptions: function () {

            // 把属性/值合并到options数组(我觉得可能这个Extras是为了客户端代码服务的吧!因为太specific了。
            var options = this.options = Object.merge.apply(null, [{}, this.options].append(arguments));

            // 和Events Extras协同工作。
            if (this.addEvent) for (var option in options) {
                // 假如key/value匹配: onXXX : function(){}的话,就注册一个事件。
                if (typeOf(options[option]) != 'function' || !(/^on[A-Z]/).test(option)) continue;
                this.addEvent(option, options[option]);
                delete options[option];
            }
            return this;
        }

    });

})();


/*
---

name: JSON

description: JSON encoder and decoder.

license: MIT-style license.

SeeAlso: <http://www.json.org/>

requires: [Array, String, Number, Function]

provides: JSON

...
*/

if (typeof JSON == 'undefined') this.JSON = {};



(function () {

    var special = { '\b': '\\b', '\t': '\\t', '\n': '\\n', '\f': '\\f', '\r': '\\r', '"': '\\"', '\\': '\\\\' };

    var escape = function (chr) {
        return special[chr] || '\\u' + ('0000' + chr.charCodeAt(0).toString(16)).slice(-4);
    };

    JSON.validate = function (string) {
        string = string.replace(/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g, '@').
                        replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g, ']').
                        replace(/(?:^|:|,)(?:\s*\[)+/g, '');

        return (/^[\],:{}\s]*$/).test(string);
    };

    JSON.encode = JSON.stringify ? function (obj) {
        return JSON.stringify(obj);
    } : function (obj) {
        if (obj && obj.toJSON) obj = obj.toJSON();

        switch (typeOf(obj)) {
            case 'string':
                return '"' + obj.replace(/[\x00-\x1f\\"]/g, escape) + '"';
            case 'array':
                return '[' + obj.map(JSON.encode).clean() + ']';
            case 'object': case 'hash':
                var string = [];
                Object.each(obj, function (value, key) {
                    var json = JSON.encode(value);
                    if (json) string.push(JSON.encode(key) + ':' + json);
                });
                return '{' + string + '}';
            case 'number': case 'boolean': return '' + obj;
            case 'null': return 'null';
        }

        return null;
    };

    JSON.decode = function (string, secure) {
        if (!string || typeOf(string) != 'string') return null;

        if (secure || JSON.secure) {
            if (JSON.parse) return JSON.parse(string);
            if (!JSON.validate(string)) throw new Error('JSON could not decode the input; security is enabled and the value is not secure.');
        }

        return eval('(' + string + ')');
    };

})();


posted on 2012-12-24 00:02  PunCha  阅读(167)  评论(0编辑  收藏  举报