dom Framework核心模块发布!(更新)

想起几年 Dean Edwards 大神发布Base2时那句注释:

You know, writing a javascript library is awfully time consuming.

虽然javascript看似容易入门,但想深入是极其困难,因为它与java这些古典式的语言非常不同,最可恨的是那狗屎一般的DOM实现——API千差万别,bugs层出不同。因此光凭javascript是无能为力,DOM API的地位是相当重要。我们不需要hash,堆这样数据结构,加之IE6也承载不了这样复杂的东西,只需要能对DOM进行强大的处理就行了,随之很多东西都建立于其上。也基于这个原因,我的框架就直接叫dom了(终于被我想到一个与closure一样酷的名字了,泪目)。虽然有人不同意对原生对象进行扩展,但既然javascript提供了如此强大的入侵式语法,放弃它太可惜了,因此本框架还是走Prototypejs与mootools的走路。原型的充分利用能让我们真正实现那句所谓的:"Write Less, Do More!"

由于我的东西是定位于"框架",非"类库",因此涉及的东西也非常多。由于战线过长,漫长的时间中发生各种事,导致有些模块被反复修改许多次,内部版本已达到3.0以上,有时模块则还没有完工。不过不管了,先放出来吧,长期的闭门造车也是个问题。

为了开发这东西,我很久没有上来冒泡了,憋环了,唠叨了这么多。好了,先介绍一下我的核心模块。由于加载系统还没有选型好,现在但求轻松选用最简单的AJAX同步阻塞,也就是我原来的第一版实现。其次是浏览器的特性侦测,浏览器检测,对集合的操作等一些常用函数。最后还有domReady,本来想让它独立为一个模块的,不过如果颗粒过细就意味着请求很多,对服务器的压力太大了。当然,我最后放出来还是一个JS文件。我没有服务器,不能像jQuery,mootools那样提供合并模块的服务。不过,为了方便大家合并,它的设计也是非常良好,直接剪切到核心模块文件就行了。每一个模块都是一个自执行函数,不会让那些变量什么的跑出去。

下面是源码,我会逐一讲解它们的用法,如果你们有什么好的实现也请不吝赐救!

 
/*dom Framework version 1.0
/*dom Framework version 1.0
Copyright 2010
Dual licensed under the MIT or GPL Version 2 licenses.
author: <ruby> <rb>司徒正美<rp>(zhongqincheng)</rp></rb><rt>しとぅなさみ</rt></ruby>
http://www.cnblogs.com/rubylouvre/
*/
(function(){
    var window = this,
    dom = function (selector,context) {
        dom.require("node");
        return (this instanceof dom) ? this.init.apply(this,arguments) : new dom(selector,context)
    },
    //几个简写
    fn = "prototype",
    co = "constructor",
    has = "hasOwnProperty",
    tags = "getElementsByTagName",
    to_s = Object[fn].toString,
    //使用eval大法防止IE的条件编译在压缩时被删掉
    ie = eval("''+/*@cc_on"+" @_jscript_version@*/-0")*1,
    //把别人的库保存到一个临时变量中
    _dom = window.dom,
    //永久性命名空间,如果这个名字被其他库占用就没救了
    namespace = escape(document.URL.split("#")[0]),
    //判定原生对象与基本类型,第二个参数为字符串,大小写敏感
    is = function (obj,type) {
        return   (type === "Object" && obj === Object(obj)) ||
        (type === "Number" &&  obj === +obj ) ||
        (type === "Null" && obj === null) ||
        (type === "Undefined" && obj === void 0 ) ||
        to_s.call(obj).slice(8,-1) === type;
    },
    //=========================================
    // 判定是否为纯净的对象,
    // 指以{},{aa:1,bb:1}或new Object(不带参数,见ecma262v5 15.2.2.1)形式得到的对象实例,用于深拷贝
    //=========================================
    isPureObject = function(obj){
        return !!(obj && is(obj,"Object") && obj[co] === Object)
    },
    dontEnum = true;
    for (var i in {
        toString: 1
    }) dontEnum = null;//只处理这三个关键的不遍历属性
    if (dontEnum) dontEnum = ["constructor", "toString", "valueOf"];
    //=========================================
    // 特征侦探
    //==========================================
    dom.env = new function(){
        var div = dom.parser = document.createElement("div"),
        root = document.documentElement,sliceNodes = true;
        div.innerHTML = ' <link/><a href="/nasami" name="'+namespace+'" style="float:left;opacity:.25;"></a>'+
        '<input type="radio" name="n" checked="checked"/><object><param/></object><table></table>';
        var a = div[tags]("a")[0],s = a.style; a.expando = true;
        try{
            Array[fn].slice.call(div.childNodes)
        }catch(e){
            sliceNodes = false;
        }
        var box = div.cloneNode(true);
        box.style.width = box.style.paddingLeft = "1px";
        root.insertBefore(box, root.firstChild);
        var w3cBox = box.offsetWidth === 2,
        mixupsName = document.getElementById(namespace) === box[tags]("a")[0],
        method = a.matchesSelector  || a.webkitMatchesSelector || a.mozMatchesSelector
        root.removeChild(box);
        return {
            //某些浏览器的innerHTML会自动去掉标签外的空白
            removeBlank: div.innerHTML.charAt(0) === "<",
            //某些浏览器会自动为table添加tbody
            insertTbody: !!div[tags]("tbody").length,
            sliceNodes:sliceNodes,
            //IE67会混淆id与name
            mixupsName:mixupsName,
            //某些浏览器会自动补全路径
            convertUrl: a.getAttribute("href") !== "/nasami",
            //某些浏览器使用document.getElementByTagName("*")遍历Object元素下的param元素
            traverseAllElements: !!div[tags]("param").length,
            //http://www.cnblogs.com/rubylouvre/archive/2010/01/09/1642978.html
            traverseAllProperties: !dontEnum,
            //https://prototype.lighthouseapp.com/projects/8886/tickets/264-ie-can-t-create-link-elements-from-html-literals
            //某些浏览器不能通过innerHTML序列化link,style,script等元素
            serializeAll: !!div[tags]("link").length,
            //IE的cloneNode才是真正意义的复制,能复制动态添加的自定义属性与事件
            cloneAll: !!a.cloneNode(true).expando,
            //http://www.cnblogs.com/rubylouvre/archive/2010/05/16/1736711.html
            //在safari下,指定了name属性的radio是无法复制checked属性的
            cloneChecked: div[tags]("input")[0].cloneNode(true).checked,
            //IE67是没有style特性(特性的值的类型为文本),只有el.style(CSSStyleDeclaration)
            hasStyleAttribute:a.getAttribute("style") !== s,
            //http://www.cnblogs.com/rubylouvre/archive/2010/05/16/1736535.html
            //IE8返回".25" ,IE9pp2返回0.25,chrome等返回"0.25"
            w3cOpacity: s.opacity == "0.25",
            //某些浏览器不支持w3c的cssFloat属性来获取浮动样式,而是使用独家的styleFloat属性
            w3cFloat: !!s.cssFloat,
            //某些浏览器存在怪异模式,此时盒子模型的宽高所围成的矩形等于border围成的矩形
            //就像开发商计算建筑面积时用的是IE6模型 , 业主计算套内面积时用的是W3C模型
            w3cBox: w3cBox,
            //IE8等支持W3C的selector APIs
            querySelector:!!(document.querySelectorAll && div.querySelectorAll ),
            matchesSelector: method && method.name,//返回对应的方法名,没有为undefined
            //除safari与IE外默认新添加的option为选中状态
            optSelected: document.createElement("select").appendChild( document.createElement("option") ).selected,
            //http://oreilly.com/catalog/jscript4/chapter/ch17.html
            w3cRange:!!(document.implementation && document.implementation.hasFeature("Range","2.0"))
        }
    };
    //=========================================
    // 数组化
    //==========================================
    dom.slice =  function(){
        var item = arguments[0]||[], method = Array[fn].slice;
        //IE中不能slice节点集合,它们是基于COM的,并非Object的实例,需要转换为原生数组
        if(!dom.env.sliceNodes && !(item instanceof Object)){
            var i = item.length,ret = [];
            while(i--){
                ret[i] = item[i]
            }
            item = ret;
        }
        return  method.apply(item,  method.call(arguments, 1));
    }
    //==========================================
    // 混入方法
    //==========================================
    dom.mixin = function() {
        var queue = dom.slice(arguments),deep = false,tk,sk;
        if(typeof queue[0] === "boolean"){
            deep = queue.shift();
        }
        var target = queue[0], source = queue[1]
        if(queue.length===1){
            target = this; source = queue[0]
        }
        if (target && source ){
            for(var key in source){
                if(source[has](key)){
                    tk = target[key]; sk = source[key];
                    if(target === tk) continue;//如window.window === window,会陷入死循环,
                    //如果是深拷贝,则检测source的当前属性是否为纯对象或数组,是则特殊处理它,
                    //对于其他类型的属性,它们的处理同浅拷贝
                    if ( deep && sk && ( dom.isPureObject(sk) || dom.isArray(sk) ) ) {
                        var clone = tk && ( dom.isPureObject(tk) || dom.isArray(tk) ) ? tk
                        : dom.isArray(tk) ? [] : {};
                        target[ key ] = dom.mixin(deep, clone, sk );
                    //属性值绝对不能为undefined
                    } else if ( sk !== undefined ) {
                        target[ key ] = sk;
                    }
                }
            }
            if(!dom.env.traverseAllProperties && source[has]){
                var d = 3;
                while ((key = dontEnum[--d])) {
                    source[has](key) && (target[key] = source[key]);
                }
            }
        }
        if(queue.length > 2 ){
            var others = queue.slice(2);//dom.slice(arguments,2);
            for(var i=0,n=others.length;i<n;i++){
                target = arguments.callee(deep,target,others[i]);
            }
        }
        return target;
    };
    dom.include = function(obj){
        dom.require("node")
        dom.mixin(dom.prototype,obj)
    };
    dom.mixin({
        //=========================================
        // 浏览器嗅探
        //==========================================
        //除了另无它法,肯定不使用navigator.userAgent来判定浏览器。因为在第一次浏览器大战初期,
        //Netscape占绝对统计地位,大部分人们不愿意兼容其他浏览器,并通过检测其UA让他们的网站
        //只允许Netscape访问,这就逼使其他浏览器(包括IE)修改自己的UA伪装成Netscape来通过那
        //些自以为是的脚本,于是出现每个人都声称自己是别人的局面,即使最新的IE9的UA也是这样
        //Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 6.0; Trident/5.0)
        ie: !!ie,//内核为trident
        ie5: ie === 5.5,
        ie6: ie === 5.6,
        ie7: ie === 5.7,
        //指IE8以下的版本或IE8运行于兼容模式下
        ie67 :!-[1,] && dom.env.querySelector === false,
        ie8: ie === 5.8,
        ie9: ie === 5.9,
        firefox: !!top.crypto,//内核Gecko
        opera:  is(top.opera,"Opera"),//内核 Presto 9.5为Kestrel 10为Carakan
        chrome: !!(top.google && top.chrome) ,//内核V8
        safari: /apple/i.test(navigator.vendor),// 内核 WebCore
        quirk: !dom.env.w3cBox,
        //=========================================
        // 获取特殊节点
        //==========================================
        //参数都是可选,除了script方法要求可选参数为普通对象,其他为DOM对象
        document : function (obj) {
            if(obj){ //IE5.5中,HTML元素不存在ownerDocument属性
                return obj.documentElement ? obj : (obj.ownerDocument || obj.document);
            }else{
                return window.document;
            }
        },
        root:function(obj){
            return dom.document(obj).documentElement;
        },
        head:function(obj){
            return dom.document(obj)[tags]("HEAD")[0] ;
        },
        body: function(obj){
            return dom.document(obj).body
        },
        script:function(obj){
            //注意,charset属性当且仅当同时指定了src属性才有效
            //注意,IE下用innerHTML序列化script标签存在bug,script标签前面要有其他节点
            dom.parser.innerHTML = '<br><script type="text/javascript" charset="utf-8" ><\/script>'
            return dom.mixin(dom.parser.childNodes[1],obj);
        },
        //=========================================
        // 各种判定
        //==========================================
        is:is,
        isPureObject:isPureObject,
        //判断是否为XML文档
        //http://www.cnblogs.com/rubylouvre/archive/2010/03/14/1685360.html
        isXML: function(el){
            var doc = dom.document(el);
            return (!!doc.xmlVersion) || (!!doc.xml) || is(doc,"XMLDocument") || (!doc.body);
        },
        //是否这元素节点,如果传入两个参数,则判定元素的标签类型
        isElement : function (obj,tag) {
            if(arguments.length === 2){
                tag = dom.isXML(obj) ? tag : tag.toUpperCase()
                return  obj.nodeName === tag ;
            }
            return !! (obj && obj.nodeType === 1) ;
        },
        isFormElement : function(obj){
            return !!(obj.tagName && "name" in obj && "form" in obj);
        },
        isNative : function(obj){//判定是否为原生方法
            return !! obj && (/\{\s*\[native code\]\s*\}/.test(String(obj)) ||
                /\{\s*\/\* source code not available \*\/\s*\}/.test(String(obj)));
        },
        //包括Array,Arguments,NodeList,HTMLCollection,IXMLDOMNodeList与自定义类数组对象
        //select.options集合(它们两个都有item与length属性)
        isArrayLike :  function (obj) {
            if(!obj || obj.document || obj.nodeType || is(obj,"Function")) return false;
            return isFinite(obj.length) ;
        },
        //检测是否为空对象,只检测本地属性
        isEmptyObject: function(obj ) {
            for ( var key in obj )
                if(obj[has] && obj[has](key))
                    return false;
            return true;
        },
        isInDomTree : function(node,context){
            var root = (context || document).documentElement;
            return node === root || dom.contains(node,root);
        },
        contains :function(el, root) {//分别为子节点与父节点
            if (el.compareDocumentPosition)
                return (el.compareDocumentPosition(root) & 8) === 8;
            if (root.contains && el.nodeType === 1){
                return root.contains(el) && root !== el;
            }
            while ((el = el.parentNode))
                if (el === root) return true;
            return false;
        },
        //=========================================
        // 各种处理集合的方法
        //==========================================
        keys : function(obj){
            var result = [],ri = 0;
            if(!dom.env.traverseAllProperties && obj[has]){
                obj[has]("constructor") && (result[ri++] = "constructor");
                obj[has]("toString") &&  (result[ri++] = "toString");
                obj[has]("valueOf") &&  (result[ri++]= "valueOf");
            }
            for(var key in obj)
                if(obj[has] && obj[has](key))
                    result[ri++] = key;
            return result;
        },
        each: function (obj, fn, bind ) {
            if (dom.isArrayLike(obj)) {
                for (var i = 0, n = obj.length ; i < n; i++) {
                    if ( fn.call(bind || obj[i], obj[i], i, obj) === false ) {//绑定作用域
                        break;
                    }
                }
            }else if(obj[has]){
                for(var prop in obj){
                    if(obj[has](prop)){//value,key,obj,绑定对象默认为value
                        if ( fn.call(bind || obj[prop], obj[prop], prop, obj) === false ) {
                            break;
                        }
                    }
                }
                if(!dom.env.traverseAllProperties){
                    var d = 3;
                    while ((prop = dontEnum[--d])) {
                        if(obj[has](prop)){
                            if ( fn.call(bind || obj[prop], obj[prop], prop, obj) === false ) {
                                break;
                            }
                        }
                    }
                }
            }
            return obj
        },

        map:function(array, fn, bind){
            var result = [],ri = 0, value
            for (var i = 0,n = array.length; i < n; i++){
                value = fn.call(bind || array[i],array[i],i,array)
                if(value != null){
                    result[ri++] = value;
                }
            }
            return result;
        },

        filter:function(array, fn, bind){
            var result = [],ri = 0;
            for (var i = 0,n = array.length; i < n; i++){
                if(fn.call(bind || array[i],array[i],i,array)){
                    result[ ri++] = array[i];
                }
            }
            return result;
        },
        //类似python中的range()函数
        range : function() {
            var a     = dom.slice(arguments);
            var solo  = a.length <= 1;
            var start = solo ? 0 : a[0],
            stop = solo ? a[0] : a[1],
            step = a[2] || 1;
            var len   = Math.ceil((stop - start) / step);
            if (len <= 0) return [];
            var range = [];
            for (var i = start, ri = 0; true; i += step) {
                if ((step > 0 ? i - stop : stop - i) >= 0) return range;
                range[ri++] = i;
            }
        },
        merge : function(array,args) {//合并集合
            array = dom.slice(array);
            var arrayLength = array.length, length = args.length;
            while (length--) array[arrayLength + length] = args[length];
            return array;
        },
        inArray : function(el,arr){
            if(arr.indexOf)
                return arr.indexOf(el) !== -1;
            for (var i = 0, n = arr.length; i < n; i++)
                if (arr[i] === el) return true;
            return false;
        },
        now: Date.now || function(){
            return new Date().valueOf();
        },
        random : function(min, max, exact){
            var range = min + (Math.random()*(max - min));
            return exact === void(0) ? Math.round(range) : range.toFixed(exact);
        },
        //var a = {length:4,0:1,1:2,2:3,3:4};
        //dom.console.log(dom.toArray(a)
        toArray : function (obj) {
            return  obj != null ? dom.isArrayLike(obj)  ? dom.slice(obj): [obj] :[]
        },
        alias : function(newName) {
            //如果不指定新名,则随机生成一个,换言之,则进入忍者模式,需要用一个变量来接受它
            newName = newName || "__dom__"+dom.now();
            window.dom = _dom;
            return window[namespace] = window[newName]  = dom;
        },
        //生成一个值都相同的对象,用于if语句进行过滤
        oneObject : function(array,val){
            var result = {},value = val !== undefined ? val :1;
            for(var i=0,n=array.length;i<n;i++)
                result[array[i]] = value;
            return result;
        },
        globalEval: function( code ) {
            //IE中,window.eval()和eval()一样只在当前作用域生效。
            //Firefox,Safari,Opera中,直接调用eval()为当前作用域,window.eval()调用为全局作用域。
            if ( code && /\S/.test(code) ) {
                var method = window.execScript ? "execScript" : "eval"
                try{
                    window[method](code);
                }catch(e){}
            }
        },
        uuid:1,
        expando : "dom" + (new Date-0),//设在元素上的自定义属性
        noop:function(){},
        cacher : function(fn, bind, post) {
            return function (){
                var self = arguments.callee,
                array = dom.slice(arguments),
                args = array.join("\u25ba"),//►,一个黑色的三角形
                cache = self.cache = self.cache || {},
                count = self.count = self.count || [];
                if (cache.hasOwnProperty(args)) {
                    return post ? post(cache[args]) : cache[args];
                }
                count.length >= 1e3 && delete cache[count.shift()];
                count.push(args);
                cache[args] = fn.apply(bind, array);
                return post ? post(cache[args]) : cache[args];
            }
        },

        //此方法只对FF浏览器有效,调试用
        //https://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Global_Objects/Object/noSuchMethod
        __noSuchMethod__: function(name, args) {
            dom.require("console")
            dom.console.error("尝试用以下参数("+args+")执行当前对象的'" + name + "'方法遭遇失败!");
        },
        ready: function( fn ) {//domReady
            var self = arguments.callee;
            self.init();
            if ( self.status ) {
                fn();
            } else if ( self.list) {
                self.list.push( fn );
            }
        }      
    });
    dom.mixin(dom.ready,{
        list:[],
        status:false,
        init: function(){
            if (arguments.callee.used ) {
                return;
            }
            arguments.callee.used = true;
            var fire = dom.ready.fire
            //用于window.onload的内部
            if ( document.readyState === "complete" ) {
                return fire();
            }
            if (-[1,]) {//Safari3.1+,Chrome,Firefox2+ ,Opera9+,听说IE9也支持DOMContentLoaded
                //https://developer.mozilla.org/en/Gecko-Specific_DOM_Events
                document.addEventListener( "DOMContentLoaded", function() {
                    document.removeEventListener( "DOMContentLoaded",  arguments.callee , false );
                    fire();
                }, false );
            } else {//IE5 IE6 IE7 IE8
                //http://dev.jquery.com/ticket/2614
                //当页面包含图片时,onreadystatechange事件会触发在window.onload之后,
                //换言之,它只能正确地执行于页面不包含二进制资源或非常少或者被缓存时
                document.attachEvent("onreadystatechange", function(e) {
                    if ( document.readyState == "complete" ) {
                        document.detachEvent("onreadystatechange", arguments.callee );
                        fire();
                    }
                });
                //doScroll方法通常只会正确执行一个全新的页面
                (function(){
                    if ( dom.ready.status ) {
                        return;
                    }
                    //doScroll存在于所有标签而不管其是否支持滚动条
                    //当DOM树时我们就可以调用其doSroll方法
                    //若用document.documentElement.doScroll(),我们需要判定其是否位于顶层document
                    //http://msdn.microsoft.com/en-us/library/ms536414(VS.85).aspx
                    var node = new Image
                    try {
                        node.doScroll();
                        node = null//防止IE内存泄漏
                    } catch( e ) {
                        //javascrpt最短时钟间隔为16ms,这里取其倍数
                        //http://blog.csdn.net/aimingoo/archive/2006/12/21/1451556.aspx
                        setTimeout( arguments.callee, 64 );
                        return;
                    }
                    fire();
                });
            }
        },
        fire: function() {
            if ( !dom.ready.status ) {
                if ( !document.body ) {
                    return setTimeout(arguments.callee, 16 );
                }
                dom.ready.status = true;
                if ( dom.ready.list ) {
                    for(var i=0, fn;fn = dom.ready.list[i++];)
                        fn();
                    delete dom.ready.list;
                }
            }
        }
    });
    //添加更多见词明义的判定
    dom.each(["Array","Function","Number","String","Undefined","Null"],function(name){
        dom["is"+name] = function(obj){
            return is(obj,name);
        }
    });

    // ECMA-5 15.4.3.2
    if(dom.isNative(Array.isArray)){
        dom.isArray = Array.isArray;
    }

    // ECMA-5 15.2.3.14
    if(dom.isNative(Object.keys)){
        dom.keys = Object.keys;
    }

    (function(){
        //游览器环境不能使用Msxml2.XMLHTTP.5.0与Msxml2.XMLHTTP.4.0
        //http://blogs.msdn.com/xmlteam/archive/2006/10/23/using-the-right-version-of-msxml-in-internet-explorer.aspx
        var s = ["XMLHttpRequest",
        "ActiveXObject('Msxml2.XMLHTTP.6.0')",
        "ActiveXObject('Msxml2.XMLHTTP.3.0')",
        "ActiveXObject('Msxml2.XMLHTTP')",
        "ActiveXObject('Microsoft.XMLHTTP')"];
       
        if(dom.ie7 && location.protocol === "file:"){
            s.shift();
        }
        for(var i = 0 ,el;el=s[i++];){
            try{
                if(eval("new "+el)){
                    dom.xhr = new Function( "return new "+el)
                    break;
                }
            }catch(e){}
        }
    })();

    //=========================================
    // 核心模块 模块加载系统
    //==========================================
    //dom有三个重要的信息储存区,
    //一个是env用于储存与浏览器有关的东西
    //一个是lib用于储存与框架有关的东西
    //一个是cache用于储存运行收集的东西
    dom.lib = {
        loaded:{},
        //获取核心模块所在的JS文件所在的文件夹路径
        baseUrl :(function(){
            var result;
            try{
                throw ""
            }catch(e){
                result = e.fileName || e.sourceURL;
            }
            if(!result){
                var scripts = document[tags]('script'),
                script = scripts[scripts.length - 1];
                result = script.src;
            }
            return result.substr( 0, result.lastIndexOf('/'));
        })()
    }
    //name为模块名,create不存在此属性是否创建一个空对象
    dom.mixin({
        //创建一个命名空间
        namespace:function(name,create,context){
            var parts=name.split("."),obj = context || window;
            for(var i=0, p; obj && (p=parts[i]); i++){
                if(i == 0 && this[p]){
                    p = this[p];
                }
                obj = (p in obj ? obj[p] : (create ? obj[p]={} : undefined));
            }
            return obj;
        },
        //同步加载模块
        require : function(name,timeout){
            name = name.indexOf("dom.") === 0 ? name.slice(4):name
            timeout = timeout || 2000
            var module = "dom."+name,url;
            if(dom.lib.loaded[name]) return
            //处理dom.node(http://www.cnblogs.com/rubylouvre/dom/node.js)的情形
            var _url = module.match(/\(([^)]+)\)/);
            url = _url && _url[1] ? _url[1] : dom.lib.baseUrl+"/"+ module.replace(/\./g, "/") + ".js";
            var xhr = dom.xhr();
            xhr.open("GET",url,false);
            xhr.setRequestHeader("If-Modified-Since","0");
            xhr.send(null);
            dom.globalEval( xhr.responseText|| "")
            setTimeout(function(){
                try{
                    xhr.abort()
                }catch(e){}
            },timeout);
        },
        //提供命名空间,标识此模块已经加载过
        provide : function(name){
            name = name.indexOf("dom.") === 0 ? name.slice(4):name
            dom.lib.loaded[name] = true;
            dom.namespace("dom."+name,true)
        }
    });
    //====================添加其他模块======================
    window[namespace] = window.dom = dom;
})();
所有模块

许多代码我以前都放过出来了,应该没有什么难度,我也将会在下一篇讲解它们的。如果到时能把chm文档搞出来就最好不过了,现在姑且先放出让大家瞧瞧。最后还是那句老话,如果某某方法有什么好的实现,请不吝赐救!

文档下载地址

下载回来后对着文件点右键-->属性-->解除锁定。

原文地址:https://www.cnblogs.com/rubylouvre/p/1761107.html