文章出處

  jQ作為javascript的庫( ▼-▼ ), 盡善盡美, 代碼優美,  值得學習。  這一周平常上班沒啥事也看jQ1.5的代碼, 今天周六差不多看完了(Sizzle部分還沒看), 重新看了一下, 又有很多新東西;

 

  相對與1.4版本的ajax部分, 整個進行了重寫, 實在是坑爹,  現在還有很多沒弄懂,  ajax可以非常簡單地:

var xhr = new XMLHttpReques || new window.ActiveXObject("Microsoft.XMLHTTP");

  也可以同樣可以寫幾千行,  所有的$.get(),  $.post(), $.getJSON(),  $.getScript().... 都是$.ajax的shortcut, 所有最后都是通過$.ajax 這個方法統一處理, 而且多了傳送器這東西, 一下子覺得好酷炫, 而且查資料查到司徒大神2011就開始了解這些東西, 自己實在差太多了, 大家都要加油才行;

    //匹配URL的那種空格;
    var r20 = /%20/g,
        rbracket = /\[\]$/,
    //回車加換行,或者單單回車(for mac);
        rCRLF = /\r?\n/g,
    //是否有#
        rhash = /#.*$/,
        rheaders = /^(.*?):\s*(.*?)\r?$/mg, // IE leaves an \r character at EOL
        rinput = /^(?:color|date|datetime|email|hidden|month|number|password|range|search|tel|text|time|url|week)$/i,
        rnoContent = /^(?:GET|HEAD)$/,
        rprotocol = /^\/\//,
        rquery = /\?/,
    // "<div>11</div><script>console.log(1)</script><div>11</div>".match(/<script\b[^<]*(?:(?!<\/script>)<[^<]*)*<\/script>/gi);
    // <script>console.log(1)</script>會被匹配出來;
        rscript = /<script\b[^<]*(?:(?!<\/script>)<[^<]*)*<\/script>/gi,
        rselectTextarea = /^(?:select|textarea)/i,
        rspacesAjax = /\s+/,
        rts = /([?&])_=[^&]*/,
        rurl = /^(\w+:)\/\/([^\/?#:]+)(?::(\d+))?/,

    // Keep a copy of the old load method
        _load = jQuery.fn.load,

    /* Prefilters
     * 1) They are useful to introduce custom dataTypes (see ajax/jsonp.js for an example)
     * 2) These are called:
     *    - BEFORE asking for a transport
     *    - AFTER param serialization (s.data is a string if s.processData is true)
     * 3) key is the dataType
     * 4) the catchall symbol "*" can be used
     * 5) execution will start with transport dataType and THEN continue down to "*" if needed
     */
        prefilters = {},

    /* Transports bindings
     * 1) key is the dataType
     * 2) the catchall symbol "*" can be used
     * 3) selection will start with transport dataType and THEN go to "*" if needed
     */
        transports = {};

// Base "constructor" for jQuery.ajaxPrefilter and jQuery.ajaxTransport
    // prefilters or transports;
    // "json jsonp" "script" "script" XML請求包裝函數;
    function addToPrefiltersOrTransports( structure ) {
        // 又是一個閉包;
        // dataTypeExpression is optional and defaults to "*"
        return function( dataTypeExpression, func ) {
            //debugger;
            if ( typeof dataTypeExpression !== "string" ) {
                func = dataTypeExpression;
                dataTypeExpression = "*";
            }

            if ( jQuery.isFunction( func ) ) {
                // rspacesAjax = /\s+/;
                var dataTypes = dataTypeExpression.toLowerCase().split( rspacesAjax ),
                    i = 0,
                    length = dataTypes.length,
                    dataType,
                    list,
                    placeBefore;

                // For each dataType in the dataTypeExpression
                // json jsonp script 或者是 *;
                for(; i < length; i++ ) {
                    dataType = dataTypes[ i ];
                    // We control if we're asked to add before
                    // any existing element
                    // 可能dataTypes是這樣的 +json jsonp; 那么這個placeBefore就是ture, 這個回調會被放到了list最前排;
                    placeBefore = /^\+/.test( dataType );
                    if ( placeBefore ) {
                        dataType = dataType.substr( 1 ) || "*";
                    }
                    list = structure[ dataType ] = structure[ dataType ] || [];
                    // then we add to the structure accordingly
                    // 保存回調方法;
                    list[ placeBefore ? "unshift" : "push" ]( func );
                }
            }
        };
    }

//Base inspection function for prefilters and transports
    function inspectPrefiltersOrTransports( structure, options, originalOptions, jXHR,
                                            dataType /* internal */, inspected /* internal */ ) {

        dataType = dataType || options.dataTypes[ 0 ];
        inspected = inspected || {};

        inspected[ dataType ] = true;

        var list = structure[ dataType ],
            i = 0,
            length = list ? list.length : 0,
            executeOnly = ( structure === prefilters ),
            selection;

        for(; i < length && ( executeOnly || !selection ); i++ ) {
            selection = list[ i ]( options, originalOptions, jXHR );
            // If we got redirected to another dataType
            // we try there if not done already
            if ( typeof selection === "string" ) {
                if ( inspected[ selection ] ) {
                    selection = undefined;
                } else {
                    options.dataTypes.unshift( selection );
                    selection = inspectPrefiltersOrTransports(
                        structure, options, originalOptions, jXHR, selection, inspected );
                }
            }
        }
        // If we're only executing or nothing was selected
        // we try the catchall dataType if not done already
        if ( ( executeOnly || !selection ) && !inspected[ "*" ] ) {
            selection = inspectPrefiltersOrTransports(
                structure, options, originalOptions, jXHR, "*", inspected );
        }
        // unnecessary when only executing (prefilters)
        // but it'll be ignored by the caller in that case
        return selection;
    }

    jQuery.fn.extend({
        load: function( url, params, callback ) {
            if ( typeof url !== "string" && _load ) {
                return _load.apply( this, arguments );

                // Don't do a request if no elements are being requested
            } else if ( !this.length ) {
                return this;
            }

            var off = url.indexOf( " " );
            if ( off >= 0 ) {
                var selector = url.slice( off, url.length );
                url = url.slice( 0, off );
            }

            // Default to a GET request
            var type = "GET";

            // If the second parameter was provided
            if ( params ) {
                // If it's a function
                if ( jQuery.isFunction( params ) ) {
                    // We assume that it's the callback
                    callback = params;
                    params = null;

                    // Otherwise, build a param string
                } else if ( typeof params === "object" ) {
                    params = jQuery.param( params, jQuery.ajaxSettings.traditional );
                    type = "POST";
                }
            }

            var self = this;

            // Request the remote document
            jQuery.ajax({
                url: url,
                type: type,
                dataType: "html",
                data: params,
                // Complete callback (responseText is used internally)
                complete: function( jXHR, status, responseText ) {
                    // Store the response as specified by the jXHR object
                    responseText = jXHR.responseText;
                    // If successful, inject the HTML into all the matched elements
                    if ( jXHR.isResolved() ) {
                        // #4825: Get the actual response in case
                        // a dataFilter is present in ajaxSettings
                        jXHR.done(function( r ) {
                            responseText = r;
                        });
                        // See if a selector was specified
                        self.html( selector ?
                            // Create a dummy div to hold the results
                            jQuery("<div>")
                                // inject the contents of the document in, removing the scripts
                                // to avoid any 'Permission Denied' errors in IE
                                .append(responseText.replace(rscript, ""))

                                // Locate the specified elements
                                .find(selector) :

                            // If not, just inject the full result
                            responseText );
                    }

                    if ( callback ) {
                        self.each( callback, [ responseText, status, jXHR ] );
                    }
                }
            });

            return this;
        },
        /*
         <form id="form" action="form">
         <input type="text" name="ipt0" id="ipt0" value="111"/>
         <input type="text"  name="ipt1" id="ipt1" value="222"/>
         </form>

         ==>>  $("#form").serializeArray();
         */
        serialize: function() {
            return jQuery.param( this.serializeArray() );
        },

        serializeArray: function() {
            return this.map(function(){
                return this.elements ? jQuery.makeArray( this.elements ) : this;
            })
                .filter(function(){
                    return this.name && !this.disabled &&
                        ( this.checked || rselectTextarea.test( this.nodeName ) ||
                            rinput.test( this.type ) );
                })
                .map(function( i, elem ){
                    var val = jQuery( this ).val();

                    return val == null ?
                        null :
                        jQuery.isArray( val ) ?
                            jQuery.map( val, function( val, i ){
                                return { name: elem.name, value: val.replace( rCRLF, "\r\n" ) };
                            }) :
                        { name: elem.name, value: val.replace( rCRLF, "\r\n" ) };
                }).get();
        }
    });

    // Attach a bunch of functions for handling common AJAX events
    // 利用閉包減少代碼量;
    jQuery.each( "ajaxStart ajaxStop ajaxComplete ajaxError ajaxSuccess ajaxSend".split( " " ), function( i, o ){
        jQuery.fn[ o ] = function( f ){
            return this.bind( o, f );
        };
    } );

    jQuery.each( [ "get", "post" ], function( i, method ) {
        jQuery[ method ] = function( url, data, callback, type ) {
            // shift arguments if data argument was omitted
            if ( jQuery.isFunction( data ) ) {
                type = type || callback;
                callback = data;
                data = null;
            }

            return jQuery.ajax({
                type: method,
                url: url,
                data: data,
                success: callback,
                dataType: type
            });
        };
    } );

    jQuery.extend({

        getScript: function( url, callback ) {
            return jQuery.get( url, null, callback, "script" );
        },

        getJSON: function( url, data, callback ) {
            return jQuery.get( url, data, callback, "json" );
        },

        ajaxSetup: function( settings ) {
            /*
             setting : {
             jsonp : "callback",
             jsonpCallback : fn
             },
             setting : {
             accepts : {
             script : "text/javascript, application/javascript"
             },
             contents : {
             script : /javascript/ <<==是一個正則;
             },
             converters : function( text ) {
             jQuery.globalEval( text );
             return text;
             }
             }
             */
            //debugger;
            jQuery.extend( true, jQuery.ajaxSettings, settings );
            if ( settings.context ) {
                jQuery.ajaxSettings.context = settings.context;
            }
        },

        ajaxSettings: {
            url: location.href,
            global: true,
            type: "GET",
            contentType: "application/x-www-form-urlencoded",
            processData: true,
            async: true,
            /*
             timeout: 0,
             data: null,
             dataType: null,
             username: null,
             password: null,
             cache: null,
             traditional: false,
             headers: {},
             crossDomain: null,
             */

            accepts: {
                xml: "application/xml, text/xml",
                html: "text/html",
                text: "text/plain",
                json: "application/json, text/javascript",
                "*": "*/*"
            },

            contents: {
                xml: /xml/,
                html: /html/,
                json: /json/
            },

            responseFields: {
                xml: "responseXML",
                text: "responseText"
            },

            // List of data converters
            // 1) key format is "source_type destination_type" (a single space in-between)
            // 2) the catchall symbol "*" can be used for source_type
            converters: {

                // Convert anything to text
                "* text": window.String,

                // Text to html (true = no transformation)
                "text html": true,

                // Evaluate text as a json expression
                "text json": jQuery.parseJSON,

                // Parse text as xml
                "text xml": jQuery.parseXML
            }
        },

        //預傳送器, 后面會初始化json和jsonp(包括回調的處理等), 標準xml的預傳送器;
        ajaxPrefilter: addToPrefiltersOrTransports( prefilters ),
        //標準傳送器, 包括ajax的傳送器的執行操作, jsonp中需要添加script標簽到界面的操作;
        ajaxTransport: addToPrefiltersOrTransports( transports ),

        // file///:C/本地協議的文件;
        // Main method
        /*
         cb = function(arg){console.log(arg)};
         var url = "http://www.filltext.com/?callback=?";
         $.getJSON( url, {
         'callback' : "cb",
         'rows': 5,
         'fname': '{firstName}',
         'lname': '{lastName}',
         'tel': '{phone|format}',
         });
         */
        ajax: function( url, options ) {

            // If options is not an object,
            // we simulate pre-1.5 signature
            if ( typeof options !== "object" ) {
                options = url;
                url = undefined;
            }

            // Force options to be an object
            options = options || {};

            var // Create the final options object
                s = jQuery.extend( true, {}, jQuery.ajaxSettings, options ),
            // Callbacks contexts
            // We force the original context if it exists
            // or take it from jQuery.ajaxSettings otherwise
            // (plain objects used as context get extended)
                callbackContext =
                    ( s.context = ( "context" in options ? options : jQuery.ajaxSettings ).context ) || s,
                globalEventContext = callbackContext === s ? jQuery.event : jQuery( callbackContext ),
            // Deferreds
                deferred = jQuery.Deferred(),
                completeDeferred = jQuery._Deferred(),
            // Status-dependent callbacks
                statusCode = s.statusCode || {},
            // Headers (they are sent all at once)
                requestHeaders = {},
            // Response headers
                responseHeadersString,
                responseHeaders,
            // transport
                transport,
            // timeout handle
                timeoutTimer,
            // Cross-domain detection vars
                loc = document.location,
                protocol = loc.protocol || "http:",
                parts,
            // The jXHR state
                state = 0,
            // Loop variable
                i,

            //假的XMLHttpRequest, 因為xml的屬性 不兼容, 為xhr包裹一層, 方便統一管理;
            // Fake xhr
                jXHR = {

                    readyState: 0,

                    // Caches the header
                    setRequestHeader: function( name, value ) {
                        if ( state === 0 ) {
                            requestHeaders[ name.toLowerCase() ] = value;
                        }
                        return this;
                    },

                    // Raw string
                    getAllResponseHeaders: function() {
                        return state === 2 ? responseHeadersString : null;
                    },

                    // Builds headers hashtable if needed
                    getResponseHeader: function( key ) {
                        var match;
                        if ( state === 2 ) {
                            if ( !responseHeaders ) {
                                responseHeaders = {};
                                while( ( match = rheaders.exec( responseHeadersString ) ) ) {
                                    responseHeaders[ match[1].toLowerCase() ] = match[ 2 ];
                                }
                            }
                            match = responseHeaders[ key.toLowerCase() ];
                        }
                        return match || null;
                    },

                    // Cancel the request
                    abort: function( statusText ) {
                        statusText = statusText || "abort";
                        if ( transport ) {
                            transport.abort( statusText );
                        }
                        done( 0, statusText );
                        return this;
                    }
                };

            // Callback for when everything is done
            // It is defined here because jslint complains if it is declared
            // at the end of the function (which would be more logical and readable)
            function done( status, statusText, responses, headers) {

                // Called once
                if ( state === 2 ) {
                    return;
                }

                // State is "done" now
                state = 2;

                // Clear timeout if it exists
                if ( timeoutTimer ) {
                    clearTimeout( timeoutTimer );
                }

                // Dereference transport for early garbage collection
                // (no matter how long the jXHR object will be used)
                transport = undefined;

                // Cache response headers
                responseHeadersString = headers || "";

                // Set readyState
                jXHR.readyState = status ? 4 : 0;

                var isSuccess,
                    success,
                    error,
                    response = responses ? ajaxHandleResponses( s, jXHR, responses ) : undefined,
                    lastModified,
                    etag;

                // If successful, handle type chaining
                if ( status >= 200 && status < 300 || status === 304 ) {

                    // Set the If-Modified-Since and/or If-None-Match header, if in ifModified mode.
                    if ( s.ifModified ) {

                        if ( ( lastModified = jXHR.getResponseHeader( "Last-Modified" ) ) ) {
                            jQuery.lastModified[ s.url ] = lastModified;
                        }
                        if ( ( etag = jXHR.getResponseHeader( "Etag" ) ) ) {
                            jQuery.etag[ s.url ] = etag;
                        };
                    };

                    // If not modified
                    if ( status === 304 ) {

                        statusText = "notmodified";
                        isSuccess = true;

                        // If we have data
                    } else {

                        try {
                            success = ajaxConvert( s, response );
                            statusText = "success";
                            isSuccess = true;
                        } catch(e) {
                            // We have a parsererror
                            statusText = "parsererror";
                            error = e;
                        };
                    };
                } else {
                    // We extract error from statusText
                    // then normalize statusText and status for non-aborts
                    error = statusText;
                    if( status ) {
                        statusText = "error";
                        if ( status < 0 ) {
                            status = 0;
                        }
                    }
                };

                // Set data for the fake xhr object
                jXHR.status = status;
                jXHR.statusText = statusText;

                // Success/Error
                if ( isSuccess ) {
                    debugger;
                    deferred.resolveWith( callbackContext, [ success, statusText, jXHR ] );
                } else {
                    deferred.rejectWith( callbackContext, [ jXHR, statusText, error ] );
                }

                // Status-dependent callbacks
                jXHR.statusCode( statusCode );
                statusCode = undefined;

                if ( s.global ) {
                    globalEventContext.trigger( "ajax" + ( isSuccess ? "Success" : "Error" ),
                        [ jXHR, s, isSuccess ? success : error ] );
                }

                // Complete
                completeDeferred.resolveWith( callbackContext, [ jXHR, statusText ] );

                if ( s.global ) {
                    globalEventContext.trigger( "ajaxComplete", [ jXHR, s] );
                    // Handle the global AJAX counter
                    if ( !( --jQuery.active ) ) {
                        jQuery.event.trigger( "ajaxStop" );
                    }
                }
            };

            //var def = {}; $.Deferred().promise(def)   把這個對象送到promise會為著對象繼承(非真正意義上的繼承,只是復制了屬性而已)一個延遲對象的實例;
            // Attach deferreds
            deferred.promise( jXHR );
            jXHR.success = jXHR.done;
            jXHR.error = jXHR.fail;
            jXHR.complete = completeDeferred.done;

            // Status-dependent callbacks
            jXHR.statusCode = function( map ) {
                if ( map ) {
                    var tmp;
                    if ( state < 2 ) {
                        for( tmp in map ) {
                            statusCode[ tmp ] = [ statusCode[tmp], map[tmp] ];
                        }
                    } else {
                        tmp = map[ jXHR.status ];
                        jXHR.then( tmp, tmp );
                    }
                }
                return this;
            };
            //變量初始化完畢;

            // Remove hash character (#7531: and string promotion)
            // Add protocol if not provided (#5866: IE7 issue with protocol-less urls)
            // We also use the url parameter if available
            //去除空格;                 // rprotocol = /^\/\// 如果協議頭是//就改成 ==》 當前協議頭+//
            s.url = ( "" + ( url || s.url ) ).replace( rhash, "" ).replace( rprotocol, protocol + "//" );

            // Extract dataTypes list
            // rspacesAjax = /\s+/;
            //把用戶期望的返回類型保存起來,方便回調;
            s.dataTypes = jQuery.trim( s.dataType || "*" ).toLowerCase().split( rspacesAjax );

            //判斷是否跨域了哇;
            // Determine if a cross-domain request is in order
            if ( !s.crossDomain ) {
                parts = rurl.exec( s.url.toLowerCase() );
                s.crossDomain = !!( parts &&
                    ( parts[ 1 ] != protocol || parts[ 2 ] != loc.hostname ||
                        ( parts[ 3 ] || ( parts[ 1 ] === "http:" ? 80 : 443 ) ) !=
                            ( loc.port || ( protocol === "http:" ? 80 : 443 ) ) )
                    );
            };

            // Convert data if not already a string
            if ( s.data && s.processData && typeof s.data !== "string" ) {
                //把json的數據轉化成通過&拼接的數據;
                s.data = jQuery.param( s.data, s.traditional );
            };
            // Apply prefilters
            //prefilters = {JSON : fn,  JSONP : fn,  SCRIPT : fn};

            //根據setting的dataType類型設置預處理的參數;
            inspectPrefiltersOrTransports( prefilters, s, options, jXHR );

            // Uppercase the type
            s.type = s.type.toUpperCase();

            // Determine if request has content
            // /^(?:GET|HEAD)$/ 沒有發送數據就是沒有content;
            s.hasContent = !rnoContent.test( s.type );

            // Watch for a new set of requests
            //false
            // jQuery.active 目前等于 0;
            if ( s.global && jQuery.active++ === 0 ) {
                jQuery.event.trigger( "ajaxStart" );
            };

            // More options handling for requests with no content
            if ( !s.hasContent ) {

                // If data is available, append data to url
                if ( s.data ) {
                    // s.url  ==>>  "http://www.filltext.com/?callback=jQuery15003316175821237266_1420601952773"
                    // s.data ==>>  "callback=cb&rows=5&fname=%7BfirstName%7D&lname=%7BlastName%7D&tel=%7Bphone%7Cformat%7D"
                    s.url += ( rquery.test( s.url ) ? "&" : "?" ) + s.data;
                    //jsonp會改變callback為jQ自己定義的callback然后在執行成功的時候才執行用戶傳進來的callback;
                    //最后變成了 ==>> http://www.filltext.com/?callback=jQuery15003316175821237266_1420601952773&callback=cb&rows=5&fname=%7BfirstName%7D&lname=%7BlastName%7D&tel=%7Bphone%7Cformat%7D
                };
                // Add anti-cache in url if needed
                if ( s.cache === false ) {

                    var ts = jQuery.now(),
                    // try replacing _= if it is there;
                    // rts = /([?&])_=[^&]*/; 把 ?_=替換成?=times 或者是 #_=替換成#_=times, 而且后面不是&, 避免出現別的錯誤;
                    /*
                     "sdfsdfdsf?_=abcd".replace(rts, "$1_="+jQuery.now())  ==>>  "sdfsdfdsf?_=1420614479567"
                     "sdfsdfdsf#_=abcd".replace(rts, "$1_="+jQuery.now())  ==>>  "sdfsdfdsf#_=abcd"
                     */
                        ret = s.url.replace( rts, "$1_=" + ts );

                    // 給最后添加一個時間戳;
                    // if nothing was replaced, add timestamp to the end
                    s.url = ret + ( (ret === s.url ) ? ( rquery.test( s.url ) ? "&" : "?" ) + "_=" + ts : "" );
                };
            };

            //JSON是不走這邊的;
            // Set the correct header, if data is being sent
            if ( s.data && s.hasContent && s.contentType !== false || options.contentType ) {
                requestHeaders[ "content-type" ] = s.contentType;
            }

            //
            // Set the If-Modified-Since and/or If-None-Match header, if in ifModified mode.
            if ( s.ifModified ) {
                if ( jQuery.lastModified[ s.url ] ) {
                    requestHeaders[ "if-modified-since" ] = jQuery.lastModified[ s.url ];
                }
                if ( jQuery.etag[ s.url ] ) {
                    requestHeaders[ "if-none-match" ] = jQuery.etag[ s.url ];
                }
            }

            // 根據用戶需求的請求頭, 服務器可以返回期望的類型;
            // Set the Accepts header for the server, depending on the dataType
            requestHeaders.accept = s.dataTypes[ 0 ] && s.accepts[ s.dataTypes[0] ] ?
                s.accepts[ s.dataTypes[0] ] + ( s.dataTypes[ 0 ] !== "*" ? ", */*; q=0.01" : "" ) :
                s.accepts[ "*" ];

            // Check for headers option
            for ( i in s.headers ) {
                requestHeaders[ i.toLowerCase() ] = s.headers[ i ];
            };
            // 執行before的事件,這個是全局的;
            // Allow custom headers/mimetypes and early abort
            if ( s.beforeSend && ( s.beforeSend.call( callbackContext, jXHR, s ) === false || state === 2 ) ) {
                // Abort if not done already
                done( 0, "abort" );
                // Return false
                jXHR = false;

            } else {

                // Install callbacks on deferreds
                for ( i in { success: 1, error: 1, complete: 1 } ) {
                    //把用戶的回調安裝到延遲對象;
                    //jXHR.success( s.success );
                    //jXHR.complete( s.complete );
                    //jXHR.error( s.error );
                    jXHR[ i ]( s[ i ] );
                }

                // Get transport
                //獲取傳送器, 根據傳送器的設置發送數據, 這個里面會執行get,post或者新增script的操作;
                transport = inspectPrefiltersOrTransports( transports, s, options, jXHR );

                // If no transport, we auto-abort
                if ( !transport ) {
                    done( -1, "No Transport" );
                } else {
                    // Set state as sending
                    state = jXHR.readyState = 1;
                    // Send global event
                    // 觸發全局的ajaxSend事件;參數為JXHR, s;
                    if ( s.global ) {
                        globalEventContext.trigger( "ajaxSend", [ jXHR, s ] );
                    }

                    //開啟一定定時器,如果是異步而且超時的話就觸發超時的事件;
                    // Timeout
                    if ( s.async && s.timeout > 0 ) {
                        timeoutTimer = setTimeout( function(){
                            jXHR.abort( "timeout" );
                        }, s.timeout );
                    }

                    //JSONP的傳送器有一個是send,一個是abort;
                    try {
                        transport.send( requestHeaders, done );
                    } catch (e) {
                        //如果出了錯;
                        // Propagate exception as error if not done
                        if ( status < 2 ) {
                            done( -1, e );
                            // Simply rethrow otherwise
                        } else {
                            jQuery.error( e );
                        }
                    }
                }
            }
            return jXHR;
        },

        // Serialize an array of form elements or a set of
        // key/values into a query string
        param: function( a, traditional ) {
            var s = [],
                add = function( key, value ) {
                    // If value is a function, invoke it and return its value
                    value = jQuery.isFunction( value ) ? value() : value;
                    s[ s.length ] = encodeURIComponent( key ) + "=" + encodeURIComponent( value );
                };

            // Set traditional to true for jQuery <= 1.3.2 behavior.
            if ( traditional === undefined ) {
                traditional = jQuery.ajaxSettings.traditional;
            }

            // If an array was passed in, assume that it is an array of form elements.
            if ( jQuery.isArray( a ) || a.jquery ) {
                // Serialize the form elements
                jQuery.each( a, function() {
                    add( this.name, this.value );
                } );

            } else {
                // If traditional, encode the "old" way (the way 1.3.2 or older
                // did it), otherwise encode params recursively.
                for ( var prefix in a ) {
                    buildParams( prefix, a[ prefix ], traditional, add );
                }
            }

            // Return the resulting serialization
            return s.join( "&" ).replace( r20, "+" );
        }
    });

    function buildParams( prefix, obj, traditional, add ) {
        if ( jQuery.isArray( obj ) && obj.length ) {
            // Serialize array item.
            jQuery.each( obj, function( i, v ) {
                if ( traditional || rbracket.test( prefix ) ) {
                    // Treat each array item as a scalar.
                    add( prefix, v );

                } else {
                    // If array item is non-scalar (array or object), encode its
                    // numeric index to resolve deserialization ambiguity issues.
                    // Note that rack (as of 1.0.0) can't currently deserialize
                    // nested arrays properly, and attempting to do so may cause
                    // a server error. Possible fixes are to modify rack's
                    // deserialization algorithm or to provide an option or flag
                    // to force array serialization to be shallow.
                    buildParams( prefix + "[" + ( typeof v === "object" || jQuery.isArray(v) ? i : "" ) + "]", v, traditional, add );
                }
            });

        } else if ( !traditional && obj != null && typeof obj === "object" ) {
            // If we see an array here, it is empty and should be treated as an empty
            // object
            if ( jQuery.isArray( obj ) || jQuery.isEmptyObject( obj ) ) {
                add( prefix, "" );

                // Serialize object item.
            } else {
                jQuery.each( obj, function( k, v ) {
                    buildParams( prefix + "[" + k + "]", v, traditional, add );
                });
            }

        } else {
            // Serialize scalar item.
            add( prefix, obj );
        }
    }

// This is still on the jQuery object... for now
// Want to move this to jQuery.ajax some day
    jQuery.extend({

        // Counter for holding the number of active queries
        active: 0,

        // Last-Modified header cache for next request
        lastModified: {},
        etag: {}

    });

    /* Handles responses to an ajax request:
     * - sets all responseXXX fields accordingly
     * - finds the right dataType (mediates between content-type and expected dataType)
     * - returns the corresponding response
     */
    function ajaxHandleResponses( s, jXHR, responses ) {

        var contents = s.contents,
            dataTypes = s.dataTypes,
            responseFields = s.responseFields,
            ct,
            type,
            finalDataType,
            firstDataType;

        // Fill responseXXX fields
        for( type in responseFields ) {
            if ( type in responses ) {
                jXHR[ responseFields[type] ] = responses[ type ];
            }
        }

        // Remove auto dataType and get content-type in the process
        while( dataTypes[ 0 ] === "*" ) {
            dataTypes.shift();
            if ( ct === undefined ) {
                ct = jXHR.getResponseHeader( "content-type" );
            }
        }

        // Check if we're dealing with a known content-type
        if ( ct ) {
            for ( type in contents ) {
                if ( contents[ type ] && contents[ type ].test( ct ) ) {
                    dataTypes.unshift( type );
                    break;
                }
            }
        }

        // Check to see if we have a response for the expected dataType
        if ( dataTypes[ 0 ] in responses ) {
            finalDataType = dataTypes[ 0 ];
        } else {
            // Try convertible dataTypes
            for ( type in responses ) {
                if ( !dataTypes[ 0 ] || s.converters[ type + " " + dataTypes[0] ] ) {
                    finalDataType = type;
                    break;
                }
                if ( !firstDataType ) {
                    firstDataType = type;
                }
            }
            // Or just use first one
            finalDataType = finalDataType || firstDataType;
        }

        // If we found a dataType
        // We add the dataType to the list if needed
        // and return the corresponding response
        if ( finalDataType ) {
            if ( finalDataType !== dataTypes[ 0 ] ) {
                dataTypes.unshift( finalDataType );
            }
            return responses[ finalDataType ];
        }
    }

// Chain conversions given the request and the original response
    function ajaxConvert( s, response ) {

        // Apply the dataFilter if provided
        if ( s.dataFilter ) {
            response = s.dataFilter( response, s.dataType );
        }

        var dataTypes = s.dataTypes,
            converters = s.converters,
            i,
            length = dataTypes.length,
            tmp,
        // Current and previous dataTypes
            current = dataTypes[ 0 ],
            prev,
        // Conversion expression
            conversion,
        // Conversion function
            conv,
        // Conversion functions (transitive conversion)
            conv1,
            conv2;

        // For each dataType in the chain
        for( i = 1; i < length; i++ ) {

            // Get the dataTypes
            prev = current;
            current = dataTypes[ i ];

            // If current is auto dataType, update it to prev
            if( current === "*" ) {
                current = prev;
                // If no auto and dataTypes are actually different
            } else if ( prev !== "*" && prev !== current ) {

                // Get the converter
                conversion = prev + " " + current;
                conv = converters[ conversion ] || converters[ "* " + current ];

                // If there is no direct converter, search transitively
                if ( !conv ) {
                    conv2 = undefined;
                    for( conv1 in converters ) {
                        tmp = conv1.split( " " );
                        if ( tmp[ 0 ] === prev || tmp[ 0 ] === "*" ) {
                            conv2 = converters[ tmp[1] + " " + current ];
                            if ( conv2 ) {
                                conv1 = converters[ conv1 ];
                                if ( conv1 === true ) {
                                    conv = conv2;
                                } else if ( conv2 === true ) {
                                    conv = conv1;
                                }
                                break;
                            }
                        }
                    }
                }
                // If we found no converter, dispatch an error
                if ( !( conv || conv2 ) ) {
                    jQuery.error( "No conversion from " + conversion.replace(" "," to ") );
                }
                // If found converter is not an equivalence
                if ( conv !== true ) {
                    // Convert with 1 or 2 converters accordingly
                    response = conv ? conv( response ) : conv2( conv1(response) );
                }
            }
        }
        return response;
    }




    var jsc = jQuery.now(),
        jsre = /(\=)\?(&|$)|()\?\?()/i;

// Default jsonp settings
    jQuery.ajaxSetup({
        jsonp: "callback",
        jsonpCallback: function() {
            return jQuery.expando + "_" + ( jsc++ );
        }
    });

// Detect, normalize options and install callbacks for jsonp requests
    jQuery.ajaxPrefilter( "json jsonp", function( s, originalSettings, dataIsString /* internal */ ) {

        dataIsString = ( typeof s.data === "string" );

        if ( s.dataTypes[ 0 ] === "jsonp" ||
            originalSettings.jsonpCallback ||
            originalSettings.jsonp != null ||
            s.jsonp !== false && ( jsre.test( s.url ) ||
                dataIsString && jsre.test( s.data ) ) ) {

            var responseContainer,
                jsonpCallback = s.jsonpCallback =
                    jQuery.isFunction( s.jsonpCallback ) ? s.jsonpCallback() : s.jsonpCallback,
                previous = window[ jsonpCallback ],
                url = s.url,
                data = s.data,
                replace = "$1" + jsonpCallback + "$2";

            if ( s.jsonp !== false ) {
                url = url.replace( jsre, replace );
                if ( s.url === url ) {
                    if ( dataIsString ) {
                        data = data.replace( jsre, replace );
                    }
                    if ( s.data === data ) {
                        // Add callback manually
                        url += (/\?/.test( url ) ? "&" : "?") + s.jsonp + "=" + jsonpCallback;
                    }
                }
            }

            s.url = url;
            s.data = data;

            window[ jsonpCallback ] = function( response ) {
                responseContainer = [ response ];
            };

            s.complete = [ function() {

                // Set callback back to previous value
                window[ jsonpCallback ] = previous;

                // Call if it was a function and we have a response
                if ( previous) {
                    if ( responseContainer && jQuery.isFunction( previous ) ) {
                        window[ jsonpCallback ] ( responseContainer[ 0 ] );
                    }
                } else {
                    // else, more memory leak avoidance
                    try{
                        delete window[ jsonpCallback ];
                    } catch( e ) {}
                }

            }, s.complete ];

            // Use data converter to retrieve json after script execution
            s.converters["script json"] = function() {
                if ( ! responseContainer ) {
                    jQuery.error( jsonpCallback + " was not called" );
                }
                return responseContainer[ 0 ];
            };

            // force json dataType
            s.dataTypes[ 0 ] = "json";

            // Delegate to script
            return "script";
        }
    } );




// Install script dataType
    jQuery.ajaxSetup({
        accepts: {
            script: "text/javascript, application/javascript"
        },
        contents: {
            script: /javascript/
        },
        converters: {
            "text script": function( text ) {
                jQuery.globalEval( text );
                return text;
            }
        }
    });

// Handle cache's special case and global
    jQuery.ajaxPrefilter( "script", function( s ) {
        if ( s.cache === undefined ) {
            s.cache = false;
        }
        if ( s.crossDomain ) {
            s.type = "GET";
            s.global = false;
        }
    } );

// Bind script tag hack transport
    //這個是跨域的傳送器哇;
    jQuery.ajaxTransport( "script", function(s) {

        // This transport only deals with cross domain requests
        // 如果請求的url發生改變,就默認用jsonp方式(script標簽)進行加載;
        if ( s.crossDomain ) {

            var script,
                head = document.getElementsByTagName( "head" )[ 0 ] || document.documentElement;

            return {

                send: function( _, callback ) {

                    script = document.createElement( "script" );
                    //async是標準瀏覽器所支持的東西;
                    //defer是IE支持的異步加載方式;
                    script.async = "async";

                    //字符串編碼;
                    if ( s.scriptCharset ) {
                        script.charset = s.scriptCharset;
                    }

                    //script和link標簽只有在添加到dom才發送請求哇;
                    script.src = s.url;

                    // Attach handlers for all browsers
                    // 事件還是先加載
                    script.onload = script.onreadystatechange = function( _, isAbort ) {

                        if ( !script.readyState || /loaded|complete/.test( script.readyState ) ) {

                            // Handle memory leak in IE
                            // IE的內存泄露問題;
                            script.onload = script.onreadystatechange = null;

                            // Remove the script
                            if ( head && script.parentNode ) {
                                head.removeChild( script );
                            }

                            // Dereference the script
                            script = undefined;

                            //因為是JSONP的方式, 就直接返回200的狀態和 success的姿態;
                            // Callback if not abort
                            if ( !isAbort ) {
                                callback( 200, "success" );
                            }
                        }
                    };
                    // Use insertBefore instead of appendChild  to circumvent an IE6 bug.
                    // This arises when a base node is used (#2709 and #4378).
                    head.insertBefore( script, head.firstChild );
                },

                abort: function() {
                    if ( script ) {
                        script.onload( 0, 1 );
                    }
                }
            };
        }
    } );




    var // Next active xhr id
        xhrId = jQuery.now(),

    // active xhrs
        xhrs = {},

    // #5280: see below
        xhrUnloadAbortInstalled,
    // 用來臨時用的;
    // XHR used to determine supports properties
        testXHR;

// Create the request object
// (This is still attached to ajaxSettings for backward compatibility)
    jQuery.ajaxSettings.xhr = window.ActiveXObject ?
        /* Microsoft failed to properly
         * implement the XMLHttpRequest in IE7 (can't request local files),
         * so we use the ActiveXObject when it is available
         * Additionally XMLHttpRequest can be disabled in IE7/IE8 so
         * we need a fallback.
         */
        function() {
            if ( window.location.protocol !== "file:" ) {
                try {
                    return new window.XMLHttpRequest();
                } catch( xhrError ) {}
            }

            try {
                return new window.ActiveXObject("Microsoft.XMLHTTP");
            } catch( activeError ) {}
        } :
        // For all other browsers, use the standard XMLHttpRequest object
        function() {
            return new window.XMLHttpRequest();
        };

// Test if we can create an xhr object
    try {
        testXHR = jQuery.ajaxSettings.xhr();
    } catch( xhrCreationException ) {};

    //測試是否支持XHR這個東西;
    //Does this browser support XHR requests?
    jQuery.support.ajax = !!testXHR;

    /*
     默認情況下,跨源請求不提供憑據(cookie、HTTP認證及客戶端SSL證明等)。
     通過將withCredentials屬性設置為true,可以指定某個請求應該發送憑據。
     如果服務器接收帶憑據的請求,會用下面的HTTP頭部來響應。
     如果發送的是帶憑據的請求,但服務器的相應中沒有包含這個頭部,
     那么瀏覽器就不會把相應交給JavaScript(于是,responseText中將是空字符串,status的值為0,
     而且會調用onerror()事件處理程序)。
     另外,服務器還可以在Preflight響應中發送這個HTTP頭部,
     表示允許源發送帶憑據的請求。
     支持withCredentials屬性的瀏覽器有Firefox 3.5+、Safari 4+和Chrome。IE10及更早版本都不支持。
     */
    // 是否支持跨域直接通過withCredentials進行判斷;
    // Does this browser support crossDomain XHR requests
    jQuery.support.cors = testXHR && ( "withCredentials" in testXHR );

    // No need for the temporary xhr anymore
    testXHR = undefined;

    // Create transport if the browser can provide an xhr
    if ( jQuery.support.ajax ) {
        //傳進來的是setting;
        jQuery.ajaxTransport(function( s ) {
            // Cross domain only allowed if supported through XMLHttpRequest
            if ( !s.crossDomain || jQuery.support.cors ) {

                var callback;

                return {
                    send: function( headers, complete ) {

                        //避免錯誤的
                        // #5280: we need to abort on unload or IE will keep connections alive
                        if ( !xhrUnloadAbortInstalled ) {

                            xhrUnloadAbortInstalled = 1;

                            jQuery(window).bind( "unload", function() {

                                // Abort all pending requests
                                jQuery.each( xhrs, function( _, xhr ) {
                                    if ( xhr.onreadystatechange ) {
                                        xhr.onreadystatechange( 1 );
                                    }
                                } );

                            } );
                        }

                        // Get a new xhr
                        var xhr = s.xhr(),
                            handle;

                        // Open the socket
                        // Passing null username, generates a login popup on Opera (#2865)
                        if ( s.username ) {
                            xhr.open( s.type, s.url, s.async, s.username, s.password );
                        } else {
                            xhr.open( s.type, s.url, s.async );
                        }

                        // Requested-With header
                        // Not set for crossDomain requests with no content
                        // (see why at http://trac.dojotoolkit.org/ticket/9486)
                        // Won't change header if already provided
                        // 設置頭;
                        if ( !( s.crossDomain && !s.hasContent ) && !headers["x-requested-with"] ) {
                            headers[ "x-requested-with" ] = "XMLHttpRequest";
                        }

                        // Need an extra try/catch for cross domain requests in Firefox 3
                        try {
                            jQuery.each( headers, function( key, value ) {
                                xhr.setRequestHeader( key, value );
                            } );
                        } catch( _ ) {};

                        // Do send the request
                        // This may raise an exception which is actually
                        // handled in jQuery.ajax (so no try/catch here)
                        // POST或者是GET,所以要判斷一下;
                        xhr.send( ( s.hasContent && s.data ) || null );

                        //cancel when I use arguments (0,1 ), kind like : callback(0,1);
                        // Listener
                        callback = function( _, isAbort ) {

                            // Was never called and is aborted or complete
                            if ( callback && ( isAbort || xhr.readyState === 4 ) ) {
                                //執行成功了就不用了;
                                // Only called once
                                callback = 0;

                                // Do not keep as active anymore
                                if ( handle ) {
                                    xhr.onreadystatechange = jQuery.noop;
                                    delete xhrs[ handle ];
                                }

                                // If it's an abort
                                //cance和send放在一起, 一個接口, 多個使用;
                                if ( isAbort ) {
                                    // Abort it manually if needed
                                    if ( xhr.readyState !== 4 ) {
                                        xhr.abort();
                                    }
                                } else {
                                    // Get info
                                    var status = xhr.status,
                                        statusText,
                                        responseHeaders = xhr.getAllResponseHeaders(),
                                        responses = {},
                                        xml = xhr.responseXML;

                                    // Construct response list
                                    if ( xml && xml.documentElement /* #4958 */ ) {
                                        responses.xml = xml;
                                    }
                                    responses.text = xhr.responseText;

                                    // Firefox throws an exception(exception異常) when accessing
                                    // statusText for faulty cross-domain requests
                                    // 火狐會報異常在跨域請求的時候;
                                    try {
                                        statusText = xhr.statusText;
                                    } catch( e ) {
                                        // We normalize with Webkit giving an empty statusText
                                        statusText = "";
                                    }

                                    // 修正各種瀏覽器的狀態碼;
                                    // Filter status for non standard behaviours    
                                    status =
                                        // Opera returns 0 when it should be 304
                                        // Webkit returns 0 for failing cross-domain no matter the real status
                                        status === 0 ?
                                            (
                                                // Webkit, Firefox: filter out faulty cross-domain requests
                                                !s.crossDomain || statusText ?
                                                    (
                                                        // Opera: filter out real aborts #6060
                                                        responseHeaders ?
                                                            304 :
                                                            0
                                                        ) :
                                                    // We assume 302 but could be anything cross-domain related
                                                    302
                                                ) :
                                            (
                                                // IE sometimes returns 1223 when it should be 204 (see #1450)
                                                status == 1223 ?
                                                    204 :
                                                    status
                                                );

                                    // Call complete
                                    //有responseXML和
                                    //responseText兩種值;
                                    //返回的返回頭;
                                    complete( status, statusText, responses, responseHeaders );
                                }
                            }
                        };

                        // if we're in sync mode or it's in cache
                        // and has been retrieved directly (IE6 & IE7)
                        // we need to manually fire the callback
                        if ( !s.async || xhr.readyState === 4 ) {
                            callback();
                        } else {
                            // Add to list of active xhrs
                            handle = xhrId++;
                            xhrs[ handle ] = xhr;
                            xhr.onreadystatechange = callback;
                        }
                    },

                    abort: function() {
                        if ( callback ) {
                            callback(0,1);
                        }
                    }
                };
            }
        });
    }
View Code

 

  JS的動畫處理也看了, 他默認把所有的動畫放在一個queue里面, 根據先進先出的方式一次次執行, 而且傳的參數也挺人性化不管你各個參數的排序怎么樣, 最后都會自動修正, 正常的是這樣的

$("#div1").animate({height:40},1000,"swing",function(){console.log(1)})

  也對用戶傳進來的非px的單位, 比如em, %進行了參數轉化, 通過實例化一個動畫對象放到$.timers這個數組里面, 只要數組有東西, 就開著定時器一直跑,  你可以添加自己的動畫方法, 因為本身jQ就提供了 "swing" 和 "linear"兩種方式, $.easing;

 

        easing: {
            linear: function( p, n, firstNum, diff ) {
                return firstNum + diff * p;
            },
            swing: function( p, n, firstNum, diff ) {
                return ((-Math.cos(p*Math.PI)/2) + 0.5) * diff + firstNum;
            }
        }

  一個是線性, 就是速度不變的情況下在固定時間到達終點, 一個是先慢后快再慢這種情況; 話說好像有科學證明所有人對漸漸消失和漸漸顯示都有很好印象, 不會太唐突;

  像speed這些, 默認的動畫讓他normal就是400毫秒自己跑, 快的就是200毫秒, 慢的就是800毫秒:

        speeds: {
            slow: 600,
            fast: 200,
            // Default speed
            _default: 400
        }

  這些非常簡單, 真正難得在代碼里面:

    //保存默認的顯示設置的變量;
    var elemdisplay = {},
    //
        rfxtypes = /^(?:toggle|show|hide)$/,
    //開頭是+-這樣的符號
    //包含數字.-
    //這個應該就是符號,符號可以是百分比;
        rfxnum = /^([+\-]=)?([\d+.\-]+)([a-z%]*)$/i,
        timerId,
        fxAttrs = [
            // height animations
            [ "height", "marginTop", "marginBottom", "paddingTop", "paddingBottom" ],
            // width animations
            [ "width", "marginLeft", "marginRight", "paddingLeft", "paddingRight" ],
            // opacity animations
            [ "opacity" ]
        ];

    jQuery.fn.extend({
        /*
         $("#div1").hide();
         $("#div1").show();
         $._data($("#div1")[0])  ==>> Object {olddisplay: "block"}   所謂的olddisplay永遠只有一個值;
         */
        show: function( speed, easing, callback ) {
            var elem, display;
            //有傳深度進來就調用animate
            if ( speed || speed === 0 ) {
                return this.animate( genFx("show", 3), speed, easing, callback);
            } else {
                //直接通過display方式進行直接顯示或者隱藏;
                for ( var i = 0, j = this.length; i < j; i++ ) {
                    elem = this[i];
                    display = elem.style.display;

                    // Reset the inline display of this element to learn if it is
                    // being hidden by cascaded rules or not
                    // 不可見變成可見,!jQuery._data(elem, "olddisplay")只有第一次才走這邊;
                    // 如果沒有沒有被$(el).hide()過就沒有olddisplay的, 就直接讓元素根據樣式表的默認樣式進行顯示;
                    if ( !jQuery._data(elem, "olddisplay") && display === "none" ) {
                        display = elem.style.display = "";
                    };

                    // Set elements which have been overridden with display: none
                    // in a stylesheet to whatever the default browser style is
                    // for such an element
                    // 如果元素設置成display=""以后, 而且默認樣式還是none, 就獲取默認樣式保存到私有數據緩存系統中;
                    if ( display === "" && jQuery.css( elem, "display" ) === "none" ) {
                        jQuery._data(elem, "olddisplay", defaultDisplay(elem.nodeName));
                    };
                };

                // Set the display of most of the elements in a second loop
                // to avoid the constant reflow
                // 這個可以直接放在上面的循環, 不過為了避免常量重渲染, 才把這個放在第二個循環里面
                for ( i = 0; i < j; i++ ) {
                    elem = this[i];
                    display = elem.style.display;
                    //是空或者是none就給他展示默認的樣式;
                    if ( display === "" || display === "none" ) {
                        elem.style.display = jQuery._data(elem, "olddisplay") || "";
                    }
                }

                return this;
            }
        },

        hide: function( speed, easing, callback ) {
            if ( speed || speed === 0 ) {
                return this.animate( genFx("hide", 3), speed, easing, callback);

            } else {
                for ( var i = 0, j = this.length; i < j; i++ ) {
                    var display = jQuery.css( this[i], "display" );

                    //如果這個元素是隱藏狀態的話, 而且沒有保存原來的顯示值, 會把這個元素最初的顯示值保存起來;
                    if ( display !== "none" && !jQuery._data( this[i], "olddisplay" ) ) {
                        jQuery._data( this[i], "olddisplay", display );
                    }
                }

                // Set the display of the elements in a second loop
                // to avoid the constant reflow
                // 隱藏起來;
                for ( i = 0; i < j; i++ ) {
                    this[i].style.display = "none";
                };

                return this;
            }
        },

        // Save the old toggle function
        _toggle: jQuery.fn.toggle,

        toggle: function( fn, fn2, callback ) {
            var bool = typeof fn === "boolean";

            //臥槽, 這個是跑到click那個toggle去了;
            if ( jQuery.isFunction(fn) && jQuery.isFunction(fn2) ) {
                this._toggle.apply( this, arguments );

                //fn === null  || fn === undefined, 不傳參數的走這個else;
            } else if ( fn == null || bool ) {
                this.each(function() {
                    //有就根據不二值展示, 沒有根據當前進行顯示和隱藏;
                    var state = bool ? fn : jQuery(this).is(":hidden");
                    jQuery(this)[ state ? "show" : "hide" ]();
                });

            } else {
                //別的參數調用動畫去了;
                this.animate(genFx("toggle", 3), fn, fn2, callback);
            };

            return this;
        },

        fadeTo: function( speed, to, easing, callback ) {
            return this.filter(":hidden").css("opacity", 0).show().end()
                .animate({opacity: to}, speed, easing, callback);
        },
        /*
         $("#div1").animate({height:40},1000,"swing",function(){console.log(1)})
         $("#div1").animate({height:400},1000,"swing",function(){console.log(1)})
         */
        animate: function( prop, speed, easing, callback ) {
            /*
             參數被處理后會是這個樣子的, 因為本身就是回調是函數形式, 動畫形式是字符串形式, 經過時間是數字形式, 所以處理還是挺簡單的;
             complete: function () {
             if ( opt.queue !== false ) {
             jQuery(this).dequeue();
             }
             if ( jQuery.isFunction( opt.old ) ) {
             opt.old.call( this );
             }
             },
             duration: 1000,
             easing: "swing",
             old: function (){console.log(1)}
             */
            var optall = jQuery.speed(speed, easing, callback);

            if ( jQuery.isEmptyObject( prop ) ) {
                return this.each( optall.complete );
            };

            //默認的queue是undefined, 這個是動畫大體上形式;
            return this[ optall.queue === false ? "each" : "queue" ](function() {
                // XXX 'this' does not always have a nodeName when running the
                // test suite

                //重新復制了一份opt了;
                var opt = jQuery.extend({}, optall), p,
                    isElement = this.nodeType === 1,
                    hidden = isElement && jQuery(this).is(":hidden"),
                    self = this;

                for ( p in prop ) {
                    //要對屬性轉駝峰;
                    var name = jQuery.camelCase( p );

                    if ( p !== name ) {
                        prop[ name ] = prop[ p ];
                        delete prop[ p ];
                        p = name;
                    };

                    // 
                    if ( prop[p] === "hide" && hidden || prop[p] === "show" && !hidden ) {
                        return opt.complete.call(this);
                    }
                    /*
                     jQ1.4就只有這兩句;
                     if ( ( p === "height" || p === "width" ) && this.style ) {
                     //保存原來的display屬性和overflow屬性;
                     // Store display property
                     opt.display = jQuery.css(this, "display");

                     // Make sure that nothing sneaks out
                     opt.overflow = this.style.overflow;
                     };
                     */
                    if ( isElement && ( p === "height" || p === "width" ) ) {
                        //sneaks out 漸隱;偷偷溜走;
                        // Make sure that nothing sneaks out
                        // Record all 3 overflow attributes because IE does not
                        // change the overflow attribute when overflowX and
                        // overflowY are set to the same value
                        // 記錄這個屬性因為IE改變overflow是不改變overflowX和overflowY為相同的值;
                        opt.overflow = [ this.style.overflow, this.style.overflowX, this.style.overflowY ];

                        // Set display property to inline-block for height/width
                        // animations on inline elements that are having width/height
                        // animated
                        // 要改變width或者height的話必須要是塊元素, 所以我們讓他變成塊元素
                        //如果支持inline-block,就用inlineblock
                        //否知使用inline并設置zoom為1, 觸發元素的hasLayout;
                        if ( jQuery.css( this, "display" ) === "inline" &&
                            jQuery.css( this, "float" ) === "none" ) {
                            if ( !jQuery.support.inlineBlockNeedsLayout ) {
                                this.style.display = "inline-block";

                            } else {
                                var display = defaultDisplay(this.nodeName);

                                // inline-level elements accept inline-block;
                                // block-level elements need to be inline with layout
                                if ( display === "inline" ) {
                                    this.style.display = "inline-block";

                                } else {
                                    this.style.display = "inline";
                                    this.style.zoom = 1;
                                }
                            }
                        }
                    }


                    //$("#div1").animate({height:[1000, "swing"]},1000,function(){console.log(1)})
                    //$("#div1").animate({height:[40, "swing"]},1000,function(){console.log(1)})
                    if ( jQuery.isArray( prop[p] ) ) {
                        // Create (if needed) and add to specialEasing
                        (opt.specialEasing = opt.specialEasing || {})[p] = prop[p][1];
                        prop[p] = prop[p][0];
                    }
                };

                //觸發layout?
                if ( opt.overflow != null ) {
                    this.style.overflow = "hidden";
                }

                //需要改變的值保存到curAnim里面去;
                opt.curAnim = jQuery.extend({}, prop);

                //根據需要改變屬性的對象迭代
                jQuery.each( prop, function( name, val ) {
                    /*
                     self : 當前元素;
                     opt : {
                     complete  : fn,
                     curAnim : {
                     height : "400px"
                     },
                     duration : 1000,
                     easing : "swing",
                     old : fn,
                     overflow : ["","",""]
                     }
                     */
                    var e = new jQuery.fx( self, opt, name );

                    //如果是toggle或者說是hide或者是show的話;
                    if ( rfxtypes.test(val) ) {
                        e[ val === "toggle" ? hidden ? "show" : "hide" : val ]( prop );

                    } else {
                        var parts = rfxnum.exec(val),
                            start = e.cur() || 0;

                        //處理參數, 最后把實際的參數放到動畫實例;
                        if ( parts ) {
                            var end = parseFloat( parts[2] ),
                                unit = parts[3] || "px";

                            // We need to compute starting value
                            // 單位可能是%, em 等不常用的單位;
                            if ( unit !== "px" ) {
                                //設置為需要的單位的最終值;
                                jQuery.style( self, name, (end || 1) + unit);
                                /*計算
                                 //e.cur()而是根據用戶的unit的最終值;
                                 end         result
                                 --------  =  --------
                                 e.cur()       start
                                 result = end/e.cur()*start;

                                 */
                                start = ((end || 1) / e.cur()) * start;
                                //還原單位的初始值;
                                jQuery.style( self, name, start + unit);
                            };

                            // If a +=/-= token was provided, we're doing a relative animation
                            if ( parts[1] ) {
                                end = ((parts[1] === "-=" ? -1 : 1) * end) + start;
                            };

                            //直接放到fx的對列, 讓他跑就好了;
                            e.custom( start, end, unit );

                        } else {
                            e.custom( start, val, "" );
                        }
                    }
                });

                // For JS strict compliance
                return true;
            });
        },

        stop: function( clearQueue, gotoEnd ) {
            var timers = jQuery.timers;

            //如果有clearQueue, 把queue隊列清空
            if ( clearQueue ) {
                this.queue([]);
            };

            //把jQ下的timers時間列表給刪除
            this.each(function() {
                // go in reverse order so anything added to the queue during the loop is ignored
                for ( var i = timers.length - 1; i >= 0; i-- ) {
                    if ( timers[i].elem === this ) {
                        if (gotoEnd) {
                            // force the next step to be the last
                            timers[i](true);
                        };
                        timers.splice(i, 1);
                    };
                };
            });

            // start the next in the queue if the last step wasn't forced
            if ( !gotoEnd ) {
                this.dequeue();
            }

            return this;
        }

    });
    /*
     JSON.stringify(genFx("show", 1),null,4)
     "{
     "height": "show",
     "marginTop": "show",
     "marginBottom": "show",
     "paddingTop": "show",
     "paddingBottom": "show"
     }";

     JSON.stringify(genFx("show", 3),null,4)
     "{
     "height": "show",
     "marginTop": "show",
     "marginBottom": "show",
     "paddingTop": "show",
     "paddingBottom": "show",
     "width": "show",
     "marginLeft": "show",
     "marginRight": "show",
     "paddingLeft": "show",
     "paddingRight": "show",
     "opacity": "show"
     }"
     */
    function genFx( type, num ) {
        var obj = {};
        /*
         fxAttrs = [
         // height animations
         [ "height", "marginTop", "marginBottom", "paddingTop", "paddingBottom" ],
         // width animations
         [ "width", "marginLeft", "marginRight", "paddingLeft", "paddingRight" ],
         // opacity animations
         [ "opacity" ]
         ]
         */
        jQuery.each( fxAttrs.concat.apply([], fxAttrs.slice(0,num)), function() {
            obj[ this ] = type;
        });

        return obj;
    }

    // Generate shortcuts for custom animations
    /*  比如你要讓一個元素slideDown
     那么這個元素的height,margintop marginbottom paddingtop padding-bottom都要一個個變小, 為這個元素的這幾個屬性添加變小的定時器;
     */
    jQuery.each({
        slideDown: genFx("show", 1),
        slideUp: genFx("hide", 1),
        slideToggle: genFx("toggle", 1),
        fadeIn: { opacity: "show" },
        fadeOut: { opacity: "hide" },
        fadeToggle: { opacity: "toggle" }
    }, function( name, props ) {
        jQuery.fn[ name ] = function( speed, easing, callback ) {
            return this.animate( props, speed, easing, callback );
        };
    });

    //初始化變量, 用戶可以傳 number(動畫時間), string(動畫形式), function(動畫完成的回調)
    jQuery.extend({
        speed: function( speed, easing, fn ) {
            var opt = speed && typeof speed === "object" ? jQuery.extend({}, speed) : {
                complete: fn || !fn && easing ||
                    jQuery.isFunction( speed ) && speed,
                duration: speed,
                easing: fn && easing || easing && !jQuery.isFunction(easing) && easing
            };

            opt.duration = jQuery.fx.off ? 0 : typeof opt.duration === "number" ? opt.duration :
                opt.duration in jQuery.fx.speeds ? jQuery.fx.speeds[opt.duration] : jQuery.fx.speeds._default;

            // Queueing
            opt.old = opt.complete;
            opt.complete = function() {
                if ( opt.queue !== false ) {
                    jQuery(this).dequeue();
                }
                if ( jQuery.isFunction( opt.old ) ) {
                    opt.old.call( this );
                }
            };

            return opt;
        },

        easing: {
            linear: function( p, n, firstNum, diff ) {
                return firstNum + diff * p;
            },
            swing: function( p, n, firstNum, diff ) {
                return ((-Math.cos(p*Math.PI)/2) + 0.5) * diff + firstNum;
            }
        },

        timers: [],

        //fx動畫的函數, 很重要, 所有的動畫開始結束都是基于這個函數的;
        fx: function( elem, options, prop ) {
            this.options = options;
            this.elem = elem;
            this.prop = prop;

            if ( !options.orig ) {
                options.orig = {};
            }
        }

    });

    jQuery.fx.prototype = {
        // Simple function for setting a style value
        // 更新元素的fx.prop為fx.now;
        update: function() {
            if ( this.options.step ) {
                this.options.step.call( this.elem, this.now, this );
            }
            //opacity和默認動畫 ,  你也可以把為這個spep設置width或者高的運動step
            (jQuery.fx.step[this.prop] || jQuery.fx.step._default)( this );
        },

        // Get the current size
        // 通過$.css獲取當前樣式;
        cur: function() {
            if ( this.elem[this.prop] != null && (!this.elem.style || this.elem.style[this.prop] == null) ) {
                return this.elem[ this.prop ];
            }

            var r = parseFloat( jQuery.css( this.elem, this.prop ) );
            return r || 0;
        },

        // Start an animation from one number to another
        custom: function( from, to, unit ) {
            var self = this,
                fx = jQuery.fx;

            this.startTime = jQuery.now();
            this.start = from;
            this.end = to;
            this.unit = unit || this.unit || "px";
            this.now = this.start;
            this.pos = this.state = 0;

            function t( gotoEnd ) {
                return self.step(gotoEnd);
            }

            t.elem = this.elem;

            //只要t返回ture那么times就會push這個step, !timerId時候才添加, 只要一個線程就好了, 提高性能;
            if ( t() && jQuery.timers.push(t) && !timerId ) {
                //timerId和fx是在同一個作用域的;
                timerId = setInterval(fx.tick, fx.interval);
            }
        },

        // Simple 'show' function
        show: function() {
            // Remember where we started, so that we can go back to it later
            this.options.orig[this.prop] = jQuery.style( this.elem, this.prop );
            this.options.show = true;

            // Begin the animation
            // Make sure that we start at a small width/height to avoid any
            // flash of content
            this.custom(this.prop === "width" || this.prop === "height" ? 1 : 0, this.cur());

            // Start by showing the element
            jQuery( this.elem ).show();
        },

        // Simple 'hide' function
        hide: function() {
            // Remember where we started, so that we can go back to it later
            this.options.orig[this.prop] = jQuery.style( this.elem, this.prop );
            this.options.hide = true;

            // Begin the animation
            this.custom(this.cur(), 0);
        },

        // Each step of an animation
        //如果動畫完成了就返回ture, 否知返回false
        step: function( gotoEnd ) {
            //假設當前的這個屬性的動畫完成了
            var t = jQuery.now(), done = true;

            //jQ的動畫是根據時間進行的, 所以這邊要判斷一下時間是否超過預期的時間;
            if ( gotoEnd || t >= this.options.duration + this.startTime ) {
                this.now = this.end;
                this.pos = this.state = 1;
                this.update();

                this.options.curAnim[ this.prop ] = true;
                //this.options保存了原來的opt參數, 只要有一個屬性動畫沒完成,動畫就是未完成;
                for ( var i in this.options.curAnim ) {
                    if ( this.options.curAnim[i] !== true ) {
                        done = false;
                    };
                };

                // 如果動畫完成了就把這個元素原來的屬性賦值到元素;
                if ( done ) {
                    // Reset the overflow
                    if ( this.options.overflow != null && !jQuery.support.shrinkWrapBlocks ) {
                        var elem = this.elem,
                            options = this.options;

                        jQuery.each( [ "", "X", "Y" ], function (index, value) {
                            elem.style[ "overflow" + value ] = options.overflow[index];
                        } );
                    };

                    // Hide the element if the "hide" operation was done
                    if ( this.options.hide ) {
                        jQuery(this.elem).hide();
                    };

                    // 把結果值再賦值一遍;
                    // Reset the properties, if the item has been hidden or shown
                    if ( this.options.hide || this.options.show ) {
                        for ( var p in this.options.curAnim ) {
                            jQuery.style( this.elem, p, this.options.orig[p] );
                        };
                    };

                    // done的話就走complete;
                    // Execute the complete function
                    this.options.complete.call( this.elem );
                };
                //
                return false;

            } else {
                // 根據經過的時間表示進度;
                var n = t - this.startTime;
                this.state = n / this.options.duration;

                // 
                // Perform the easing function, defaults to swing
                var specialEasing = this.options.specialEasing && this.options.specialEasing[this.prop];
                var defaultEasing = this.options.easing || (jQuery.easing.swing ? "swing" : "linear");

                //進度
                //經過的時間
                //總的時間;
                this.pos = jQuery.easing[specialEasing || defaultEasing](this.state, n, 0, 1, this.options.duration);

                //計算結果的值;
                this.now = this.start + ((this.end - this.start) * this.pos);

                // debugger;
                // Perform the next step of the animation
                // 把值更新到元素上;
                this.update();
            }

            return true;
        }
    };

    jQuery.extend( jQuery.fx, {
        //這個tick是一個定時器, 只要有要運動元素, 這個定時器都在跑
        tick: function() {
            var timers = jQuery.timers;

            for ( var i = 0; i < timers.length; i++ ) {
                //timers保存的是每一個元素改變樣式的step閉包, 如果元素的執行完成就會返回false, 那么這個改變的線程就會被刪除;
                if ( !timers[i]() ) {
                    timers.splice(i--, 1);
                };
            };

            //如果沒有了就會清空timers, fx.stopstop就在這個方法的后面;
            if ( !timers.length ) {
                jQuery.fx.stop();
            };
        },

        interval: 13,

        stop: function() {
            clearInterval( timerId );
            timerId = null;
        },

        speeds: {
            slow: 600,
            fast: 200,
            // Default speed
            _default: 400
        },

        //fx.step 這個可以添加;
        step: {
            opacity: function( fx ) {
                jQuery.style( fx.elem, "opacity", fx.now );
            },

            _default: function( fx ) {
                if ( fx.elem.style && fx.elem.style[ fx.prop ] != null ) {
                    fx.elem.style[ fx.prop ] = (fx.prop === "width" || fx.prop === "height" ? Math.max(0, fx.now) : fx.now) + fx.unit;
                } else {
                    fx.elem[ fx.prop ] = fx.now;
                }
            }
        }
    });

    if ( jQuery.expr && jQuery.expr.filters ) {
        jQuery.expr.filters.animated = function( elem ) {
            return jQuery.grep(jQuery.timers, function( fn ) {
                return elem === fn.elem;
            }).length;
        };
    }

    function defaultDisplay( nodeName ) {
        //緩沖elemdisplay, 優化,避免下一次再跑同樣的標簽;
        if ( !elemdisplay[ nodeName ] ) {
            var elem = jQuery("<" + nodeName + ">").appendTo("body"),
                display = elem.css("display");

            elem.remove();

            if ( display === "none" || display === "" ) {
                display = "block";
            };

            elemdisplay[ nodeName ] = display;
        };

        return elemdisplay[ nodeName ];
    };
View Code

   

  獲取元素的寬高在跨瀏覽器的時候實在是悲劇, 因為早期DOM并不是規范, 瀏覽器都是按照自己的來, 導致獲取client, scoll, offset, left, top height, width有各種各樣的問題, 現在標準早已建立, 比如最簡單的盒模型等等,  根據這套標準, 獲取元素各個屬性有跡可循,如果對DOM的各個寬高值不清楚的看這個會比較麻煩:

    //匹配標簽是table或者是td或者是th這種標簽;
    var rtable = /^t(?:able|d|h)$/i,
    //匹配是body或者是html標簽;
        rroot = /^(?:body|html)$/i;

    //jQ真的很細心, 工作和生活其實都要這樣才能有成就啊;

    //getBoundingClientRect雖然是ie的東西,但是在任何瀏覽器都是全兼容的, 所以可以放心使用, 通過這個東東計算寬高很快的;
    if ( "getBoundingClientRect" in document.documentElement ) {

        //返回相對于body的top和left;
        jQuery.fn.offset = function( options ) {
            //
            var elem = this[0], box;

            //如果有傳參數, 就是設置值;
            if ( options ) {
                return this.each(function( i ) {
                    jQuery.offset.setOffset( this, options, i );
                });
            };

            //不存在elem還玩個毛;
            if ( !elem || !elem.ownerDocument ) {
                return null;
            }

            //如果是body進行特殊判斷;
            if ( elem === elem.ownerDocument.body ) {
                //是body的話直接返回body的left和top,默認的的8pxmargin
                return jQuery.offset.bodyOffset( elem );
            };

            //獲取這個元素相對于這個界面的left和top;
            try {
                box = elem.getBoundingClientRect();
            } catch(e) {};

            var doc = elem.ownerDocument,
                docElem = doc.documentElement;

            // Make sure we're not dealing with a disconnected DOM node
            // 如果box這個沒返回,  或者box的ownerDocument不包含這個box[in the fragmentDocument : document.createDocumentFragment()]
            if ( !box || !jQuery.contains( docElem, elem ) ) {
                //如果這個元素不在dom里面也有top和left嗎?
                return box ? { top: box.top, left: box.left } : { top: 0, left: 0 };
            };

            //
            var body = doc.body,
                win = getWindow(doc),
            //IE的documentElement是不顯示的, 只有body;
                clientTop  = docElem.clientTop  || body.clientTop  || 0,
                clientLeft = docElem.clientLeft || body.clientLeft || 0,
                scrollTop  = (win.pageYOffset || jQuery.support.boxModel && docElem.scrollTop  || body.scrollTop ),
                scrollLeft = (win.pageXOffset || jQuery.support.boxModel && docElem.scrollLeft || body.scrollLeft),
            //去除border的高度, 因為滾動條包含border的;
                top  = box.top  + scrollTop  - clientTop,
                left = box.left + scrollLeft - clientLeft;

            return { top: top, left: left };
        };

    } else {
        //沒有getBoundingClientRect
        jQuery.fn.offset = function( options ) {
            var elem = this[0];

            if ( options ) {
                return this.each(function( i ) {
                    jQuery.offset.setOffset( this, options, i );
                });
            }

            if ( !elem || !elem.ownerDocument ) {
                return null;
            }

            if ( elem === elem.ownerDocument.body ) {
                return jQuery.offset.bodyOffset( elem );
            }

            //獲得關于offset相關的瀏覽器特征
            jQuery.offset.initialize();

            var computedStyle,
                offsetParent = elem.offsetParent,
                prevOffsetParent = elem,
                doc = elem.ownerDocument,
                docElem = doc.documentElement,
                body = doc.body,
                defaultView = doc.defaultView,
                prevComputedStyle = defaultView ? defaultView.getComputedStyle( elem, null ) : elem.currentStyle,
                //從當前元素的offset開始(再重復一次, offset是從border開始的(不包含border,即margin-box);
                top = elem.offsetTop,
                left = elem.offsetLeft;

            //不用getBoundingClientRect獲取offset很麻煩;
            //迭代父級, 而不是迭代offsetParent哦, 因為parentNode如果有scroll的話計算出來的offsetLeft或者offsetTop不準確), 直到body或者html標簽;
            //迭代每一個父級,對于每一個父級的scroll和border進行特殊處理,
            while ( (elem = elem.parentNode) && elem !== body && elem !== docElem ) {
                //fix的標簽是根據界面定位的, 一定要例外處理, 否則計算出的值有誤;;
                if ( jQuery.offset.supportsFixedPosition && prevComputedStyle.position === "fixed" ) {
                    break;
                };

                computedStyle = defaultView ? defaultView.getComputedStyle(elem, null) : elem.currentStyle;
                //因為我們要計算的是相對界面的top和left, 所以要把對所有有滾動的父級進行處理(減去處理);
                top  -= elem.scrollTop;
                left -= elem.scrollLeft;

                //對有定位的offsetParent才處理,(要弄懂offsetParent的元素是有定位的元素,比如absolute或者是relative的元素);
                if ( elem === offsetParent ) {
                    top  += elem.offsetTop;
                    left += elem.offsetLeft;
                                       //offset應該返回的是border-box,但在一些表格元素卻沒有計算它們的border值,需要自行添加
                                       //offset是指從當前的margin-box(包含margin)到父級的border-box(包含border-box),有些瀏覽器的offset不包含border, 要注意, 所以也要特殊處理;
                                                            //又對table進行特殊處理
                    if ( jQuery.offset.doesNotAddBorder && !(jQuery.offset.doesAddBorderForTableAndCells && rtable.test(elem.nodeName)) ) {
                        top  += parseFloat( computedStyle.borderTopWidth  ) || 0;
                        left += parseFloat( computedStyle.borderLeftWidth ) || 0;
                    };

                    prevOffsetParent = offsetParent;
                    //把offsetParent給offsetParent
                    offsetParent = elem.offsetParent;
                }

                //修正safari的錯誤
                if ( jQuery.offset.subtractsBorderForOverflowNotVisible && computedStyle.overflow !== "visible" ) {
                    top  += parseFloat( computedStyle.borderTopWidth  ) || 0;
                    left += parseFloat( computedStyle.borderLeftWidth ) || 0;
                };

                prevComputedStyle = computedStyle;
            }

            //最后加上body的偏移
            if ( prevComputedStyle.position === "relative" || prevComputedStyle.position === "static" ) {
                top  += body.offsetTop;
                left += body.offsetLeft;
            }

            //如果元素使用了fix定位, 要加上最大滾動距離;
            if ( jQuery.offset.supportsFixedPosition && prevComputedStyle.position === "fixed" ) {
                top  += Math.max( docElem.scrollTop, body.scrollTop );
                left += Math.max( docElem.scrollLeft, body.scrollLeft );
            }

            return { top: top, left: left };
        };
    }

    jQuery.offset = {
        //一些兼容檢測, 坑多, 太多了;
        initialize: function() {
            var body = document.body, container = document.createElement("div"), innerDiv, checkDiv, table, td, bodyMarginTop = parseFloat( jQuery.css(body, "marginTop") ) || 0,
                html = "<div style='position:absolute;top:0;left:0;margin:0;border:5px solid #000;padding:0;width:1px;height:1px;'><div></div></div><table style='position:absolute;top:0;left:0;margin:0;border:5px solid #000;padding:0;width:1px;height:1px;' cellpadding='0' cellspacing='0'><tr><td></td></tr></table>";

            jQuery.extend( container.style, { position: "absolute", top: 0, left: 0, margin: 0, border: 0, width: "1px", height: "1px", visibility: "hidden" } );

            container.innerHTML = html;
            body.insertBefore( container, body.firstChild );
            innerDiv = container.firstChild;
            checkDiv = innerDiv.firstChild;
            td = innerDiv.nextSibling.firstChild.firstChild;

            this.doesNotAddBorder = (checkDiv.offsetTop !== 5);
            this.doesAddBorderForTableAndCells = (td.offsetTop === 5);

            checkDiv.style.position = "fixed";
            checkDiv.style.top = "20px";

            // safari subtracts parent border width here which is 5px
            this.supportsFixedPosition = (checkDiv.offsetTop === 20 || checkDiv.offsetTop === 15);
            checkDiv.style.position = checkDiv.style.top = "";

            innerDiv.style.overflow = "hidden";
            innerDiv.style.position = "relative";

            this.subtractsBorderForOverflowNotVisible = (checkDiv.offsetTop === -5);

            this.doesNotIncludeMarginInBodyOffset = (body.offsetTop !== bodyMarginTop);

            body.removeChild( container );
            body = container = innerDiv = checkDiv = table = td = null;
            jQuery.offset.initialize = jQuery.noop;
        },

        bodyOffset: function( body ) {
            var top = body.offsetTop,
                left = body.offsetLeft;

            //兼容檢測;
            jQuery.offset.initialize();

            //offsetLeft offsetTop默認就是不包含當前元素的margin,  可能向前的瀏覽器不給力;
            if ( jQuery.offset.doesNotIncludeMarginInBodyOffset ) {
                top  += parseFloat( jQuery.css(body, "marginTop") ) || 0;
                left += parseFloat( jQuery.css(body, "marginLeft") ) || 0;
            }

            return { top: top, left: left };
        },

        //這個還是jQ的工具方法;
        setOffset: function( elem, options, i ) {
            var position = jQuery.css( elem, "position" );

            //如果元素沒有指定position的值, 那么默認的返回為static;
            // set position first, in-case top/left are set even on static elem
            if ( position === "static" ) {
                elem.style.position = "relative";
            };

            //獲取默認值;
            var curElem = jQuery( elem ),
                curOffset = curElem.offset(),
                curCSSTop = jQuery.css( elem, "top" ),
                curCSSLeft = jQuery.css( elem, "left" ),
                //如果有個定位的值是auto;
                calculatePosition = (position === "absolute" && jQuery.inArray('auto', [curCSSTop, curCSSLeft]) > -1),
                props = {}, curPosition = {}, curTop, curLeft;

            // 需要計算值, 這個值是通過有定位的父級的position計算出相對的值;
            // need to be able to calculate position if either top or left is auto and position is absolute
            if ( calculatePosition ) {
                curPosition = curElem.position();
            };

            // 通過計算獲取的值優先用, 否者用計算后樣式;
            curTop  = calculatePosition ? curPosition.top  : parseInt( curCSSTop,  10 ) || 0;
            curLeft = calculatePosition ? curPosition.left : parseInt( curCSSLeft, 10 ) || 0;

            // 判斷是不是函數
            if ( jQuery.isFunction( options ) ) {
                options = options.call( elem, i, curOffset );
            };

            //
            if (options.top != null) {
               // 這里是通過$.css設置定位, 所以有個恒等式 : 設置后的相對界面的位置 - 設置前元素相對界面的位置 = 設置后元素相對父級的位置 - 設置前元素相對父級的位置
               // 我們要求的是設置后相對父級的位置, 把這個東東倒一下就好了;
               //我們要設置的offset, 相對界面的位置
                            //用戶設置的值
                                           //相對界面的定位
                                                            //相對父級的值;
                props.top = (options.top - curOffset.top) + curTop;
            }
            if (options.left != null) {
                props.left = (options.left - curOffset.left) + curLeft;
            }

            //可以傳一個using的方法, 會把元素和 最后的樣式作為參數傳進去;
            if ( "using" in options ) {
                options.using.call( elem, props );
            } else {
                curElem.css( props );
            }
        }
    };


    jQuery.fn.extend({
        // position是獲取相對有定位父級的位置;
        position: function() {
            if ( !this[0] ) {
                return null;
            }

            var elem = this[0],

            // Get *real* offsetParent
                offsetParent = this.offsetParent(),

            // Get correct offsets
                offset       = this.offset(),
            //如果是html或者body就把left和top設置為0, 不要忘記了元素的定位是包含margin的,這個很重要,而且子元素的定位是根據父級的padding-box進行設置的;
                //元素的offset是從border-box開始的
                parentOffset = rroot.test(offsetParent[0].nodeName) ? { top: 0, left: 0 } : offsetParent.offset();

            // Subtract element margins
            // note: when an element has margin: auto the offsetLeft and marginLeft
            // are the same in Safari causing offset.left to incorrectly be 0
            offset.top  -= parseFloat( jQuery.css(elem, "marginTop") ) || 0;
            offset.left -= parseFloat( jQuery.css(elem, "marginLeft") ) || 0;

            // Add offsetParent borders
            parentOffset.top  += parseFloat( jQuery.css(offsetParent[0], "borderTopWidth") ) || 0;
            parentOffset.left += parseFloat( jQuery.css(offsetParent[0], "borderLeftWidth") ) || 0;

            // Subtract the two offsets
            return {
                //元素的offset是從border-box開始的
                //position是指這個元素的margin定點到padding-box(包含padding)的距離, 所以要計算該元素和父級元素的offset, 將這兩個元素的offset相減, 當前的值是包含了 當前元素的margin的且 不包含父級元素border的,所以要重新計算
                //這里最好畫圖,要么頭會暈, 而且個個屬性的定義要弄清楚;
                top:  offset.top  - parentOffset.top,
                left: offset.left - parentOffset.left
            };
        },

        offsetParent: function() {
            return this.map(function() {
                var offsetParent = this.offsetParent || document.body;
                //如果元素的定位為static而且不是根元素, 重新定義offsetParent, 應該是某些瀏覽器會返回position為static的offstParent;
                while ( offsetParent && (!rroot.test(offsetParent.nodeName) && jQuery.css(offsetParent, "position") === "static") ) {
                    offsetParent = offsetParent.offsetParent;
                };
                return offsetParent;
            });
        }
    });


// Create scrollLeft and scrollTop methods
    jQuery.each( ["Left", "Top"], function( i, name ) {
        var method = "scroll" + name;
        //減少代碼而設置的閉包;
        //scrollLeft;
        //scrollTop
        jQuery.fn[ method ] = function(val) {
            var elem = this[0], win;

            //參數檢測;
            if ( !elem ) {
                return null;
            }

            //設置值;
            if ( val !== undefined ) {
                // Set the scroll offset
                return this.each(function() {
                    win = getWindow( this );

                    //如果是window就是設置滾動的值;
                    if ( win ) {
                        win.scrollTo(
                            !i ? val : jQuery(win).scrollLeft(),
                            i ? val : jQuery(win).scrollTop()
                        );

                    } else {
                        //this.scrollTop, this.scrollLeft;
                        this[ method ] = val;
                    }
                });
            } else {
                // 獲取
                win = getWindow( elem );

                // Return the scroll offset
                //瀏覽器的能力檢測;
                return win ? ("pageXOffset" in win) ? win[ i ? "pageYOffset" : "pageXOffset" ] :
                    jQuery.support.boxModel && win.document.documentElement[ method ] ||
                        win.document.body[ method ] :
                    //直接返回
                    elem[ method ];
            }
        };
    });

    //你不能傳一個nodeType為1的元素進來;
    function getWindow( elem ) {
        return jQuery.isWindow( elem ) ?
            elem :
            //documentNodeType ==》》 9
            elem.nodeType === 9 ?
                elem.defaultView || elem.parentWindow :
                false;
    }




// Create innerHeight, innerWidth, outerHeight and outerWidth methods
    jQuery.each([ "Height", "Width" ], function( i, name ) {
        //又是一個閉包;
        var type = name.toLowerCase();

        // innerHeight and innerWidth
        jQuery.fn["inner" + name] = function() {
            //innerWidth是包含padding的, 引用css的方法, 傳的是padding
            return this[0] ?
                parseFloat( jQuery.css( this[0], type, "padding" ) ) :
                null;
        };

        // outerHeight and outerWidth
        // 獲取的是borderBox的寬或者高度,可以傳true獲取的是包含margin的寬和高;
        jQuery.fn["outer" + name] = function( margin ) {
            return this[0] ?
                parseFloat( jQuery.css( this[0], type, margin ? "margin" : "border" ) ) :
                null;
        };

        //type為 width或者是height;
        jQuery.fn[ type ] = function( size ) {
            // Get window width or height
            var elem = this[0];
            //避免錯誤;
            if ( !elem ) {
                return size == null ? null : this;
            }

            //對函數進行處理;
            if ( jQuery.isFunction( size ) ) {
                return this.each(function( i ) {
                    var self = jQuery( this );
                                                       //引用自身, 把計算后值傳到回調方法里;
                    self[ type ]( size.call( this, i, self[ type ]() ) );
                });
            };

            // 是window的話
            if ( jQuery.isWindow( elem ) ) {
                // Everyone else use document.documentElement or document.body depending on Quirks vs Standards mode
                // 3rd condition allows Nokia support, as it supports the docElem prop but not CSS1Compat
                //標準瀏覽器的用戶寬和高是就是html標簽的寬高;
                var docElemProp = elem.document.documentElement[ "client" + name ];
                        //標準瀏覽器會走第一個
                return elem.document.compatMode === "CSS1Compat" && docElemProp ||
                    //IE的詭異模式會走這一個
                                                            //這個就不知道什么情況了;
                    elem.document.body[ "client" + name ] || docElemProp;

                // Get document width or height
            } else if ( elem.nodeType === 9 ) {
                // Either scroll[Width/Height] or offset[Width/Height], whichever is greater
                //計算documentElemnt的最大值;
                return Math.max(
                    elem.documentElement["client" + name],
                    elem.body["scroll" + name], elem.documentElement["scroll" + name],
                    elem.body["offset" + name], elem.documentElement["offset" + name]
                );

                // Get or set width or height on the element
            //這個是獲取;
            } else if ( size === undefined ) {
                var orig = jQuery.css( elem, type ),
                    ret = parseFloat( orig );
                    //parseFlaot是NaN應該是auto這種情況;
                return jQuery.isNaN( ret ) ? orig : ret;

                // Set the width or height on the element (default to pixels if value is unitless)
            //最后走設置
            } else {
                //width或者是高;
                return this.css( type, typeof size === "string" ? size : size + "px" );
            }
        };

    });

})(window);
View Code

 

   

  最后把所有代碼貼出來, 過會兒看angular去, 下次就是看jQ1.6了;

  

/*!
 * jQuery JavaScript Library v1.5
 * http://jquery.com/
 *
 * Copyright 2011, John Resig
 * Dual licensed under the MIT or GPL Version 2 licenses.
 * http://jquery.org/license
 *
 * Includes Sizzle.js
 * http://sizzlejs.com/
 * Copyright 2011, The Dojo Foundation
 * Released under the MIT, BSD, and GPL Licenses.
 *
 * Date: Mon Jan 31 08:31:29 2011 -0500
 */
//本來想折騰karma和yeoman, 折騰不起來, 浪費了一大堆時間, 不玩他們了, 復習下jQuery才是正道啊;
(function( window, undefined ) {

// Use the correct document accordingly with window argument (sandbox)
// 提高document速度;
    var document = window.document;
    var jQuery = (function() {

// Define a local copy of jQuery
        var jQuery = function( selector, context ) {
                // The jQuery object is actually just the init constructor 'enhanced'
                return new jQuery.fn.init( selector, context, rootjQuery );
            },

        //防沖突處理;
        // Map over jQuery in case of overwrite
            _jQuery = window.jQuery,

        // Map over the $ in case of overwrite
            _$ = window.$,

        //通過jQ實例化后的根節點(document), 里面用的比較多,  直接緩存起來;
        // A central reference to the root jQuery(document)
            rootjQuery,

        // A simple way to check for HTML strings or ID strings
        // (both of which we optimize for)
        //(<[\w\W]+>)匹配< ***** >

            quickExpr = /^(?:[^<]*(<[\w\W]+>)[^>]*$|#([\w\-]+)$)/,

        //匹配非空格;
        // Check if a string has a non-whitespace character in it
            rnotwhite = /\S/,

        // Used for trimming whitespace
            trimLeft = /^\s+/,
            trimRight = /\s+$/,

        // Check for digits
        //只要匹配單數字就可以了;
            rdigit = /\d/,

        // Match a standalone tag
        // 只要匹配<div sdfsd></div>
        //或者
        //<div  sdfsdf>
            rsingleTag = /^<(\w+)\s*\/?>(?:<\/\1>)?$/,

        // JSON RegExp
            rvalidchars = /^[\],:{}\s]*$/,
            rvalidescape = /\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g,
            rvalidtokens = /"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g,
            rvalidbraces = /(?:^|:|,)(?:\s*\[)+/g,

        // Useragent RegExp
        //用戶代理會匹配出用戶的瀏覽器 和 該瀏覽器的版本;
            rwebkit = /(webkit)[ \/]([\w.]+)/,
            ropera = /(opera)(?:.*version)?[ \/]([\w.]+)/,
            rmsie = /(msie) ([\w.]+)/,
            rmozilla = /(mozilla)(?:.*? rv:([\w.]+))?/,

        // Keep a UserAgent string for use with jQuery.browser
            userAgent = navigator.userAgent,

        // For matching the engine and version of the browser
            browserMatch,

        // Has the ready events already been bound?
            readyBound = false,

        //這個就是一個數組, 保存了DOM ready的列表;
        // The deferred used on DOM ready
            readyList,

        // Promise methods
            promiseMethods = "then done fail isResolved isRejected promise".split( " " ),

        //DOM準備(展示)好了就會觸發這個
        // The ready event handler
            DOMContentLoaded,

        //快捷的寫法;
        // Save a reference to some core methods
            toString = Object.prototype.toString,
            hasOwn = Object.prototype.hasOwnProperty,
            push = Array.prototype.push,
            slice = Array.prototype.slice,
            trim = String.prototype.trim,
            indexOf = Array.prototype.indexOf,

        //{ [object Object] : "object" , [object Array] : "array" }象這樣的東東;
        // [[Class]] -> type pairs
            class2type = {};

        jQuery.fn = jQuery.prototype = {
            //不改就是object的constructor了;
            constructor: jQuery,
            //處理了幾種情況
            init: function( selector, context, rootjQuery ) {
                var match, elem, ret, doc;

                // Handle $(""), $(null), or $(undefined)
                //沒有的話返回空的實例,繼承了jq的原型的空對象;
                if ( !selector ) {
                    return this;
                }

                //單獨處理傳單個元素進來的情況;
                // Handle $(DOMElement)
                if ( selector.nodeType ) {
                    this.context = this[0] = selector;
                    this.length = 1;
                    return this;
                }

                //處理了body, 優化, 1.4是沒有這個的;
                // The body element only exists once, optimize finding it
                if ( selector === "body" && !context && document.body ) {
                    this.context = document;
                    this[0] = document.body;
                    this.selector = "body";
                    this.length = 1;
                    return this;
                }


                // Handle HTML strings
                // 是字符串   或者是   一個函數
                if ( typeof selector === "string" ) {
                    // Are we dealing with HTML string or an ID?
                    match = quickExpr.exec( selector );

                    //$("<div></div>") ==>> ["<div></div>", "<div></div>", undefined];
                    //$("#div1") ==>> ["#div1", undefined, "div1"];
                    // Verify a match, and that no context was specified for #id
                    if ( match && (match[1] || !context) ) {

                        // HANDLE: $(html) -> $(array)
                        if ( match[1] ) {
                            //上下文
                            context = context instanceof jQuery ? context[0] : context;
                            //通過context獲取當前界面的文檔 或者document;
                            //有可能是iframe里面的元素;
                            doc = (context ? context.ownerDocument || context : document);

                            // If a single string is passed in and it's a single tag
                            // just do a createElement and skip the rest
                            //是否是單標簽;
                            ret = rsingleTag.exec( selector );

                            if ( ret ) {
                                //$("<div></div>",{hehe:"hehe"});
                                if ( jQuery.isPlainObject( context ) ) {
                                    selector = [ document.createElement( ret[1] ) ];
                                    jQuery.fn.attr.call( selector, context, true );

                                } else {
                                    //$("<div>")
                                    selector = [ doc.createElement( ret[1] ) ];
                                }

                            } else {
                                //通過buildFragement處理傳進來的字符串;
                                //比如 : $("<div dfdf></div><div>sdfsdf</div>");
                                //會變成: [<div dfdf></div>, <div>sdfsdf</div>];
                                ret = jQuery.buildFragment( [ match[1] ], [ doc ] );
                                selector = (ret.cacheable ? jQuery.clone(ret.fragment) : ret.fragment).childNodes;
                            }

                            return jQuery.merge( this, selector );

                            // HANDLE: $("#id")
                        } else {
                            elem = document.getElementById( match[2] );

                            // Check parentNode to catch when Blackberry 4.6 returns
                            // nodes that are no longer in the document #6963
                            if ( elem && elem.parentNode ) {
                                // Handle the case where IE and Opera return items
                                // by name instead of ID
                                // ie下name和id混肴的情況通過sizzle匹配元素
                                if ( elem.id !== match[2] ) {
                                    return rootjQuery.find( selector );
                                }

                                // Otherwise, we inject the element directly into the jQuery object
                                this.length = 1;
                                this[0] = elem;
                            }

                            this.context = document;
                            this.selector = selector;
                            return this;
                        }

                        // HANDLE: $(expr, $(...))
                    } else if ( !context || context.jquery ) {
                        return (context || rootjQuery).find( selector );

                        // HANDLE: $(expr, context)
                        // (which is just equivalent to: $(context).find(expr)
                    } else {
                        //通過sizzle
                        //this.constructor()就是 $()
                        return this.constructor( context ).find( selector );
                    }

                    // HANDLE: $(function)
                    // Shortcut for document ready
                } else if ( jQuery.isFunction( selector ) ) {
                    //$(document).ready(fn(){})
                    return rootjQuery.ready( selector );
                }

                if (selector.selector !== undefined) {
                    this.selector = selector.selector;
                    this.context = selector.context;
                }

                //繼承jQ
                return jQuery.makeArray( selector, this );
            },

            // Start with an empty selector
            selector: "",

            // The current version of jQuery being used
            jquery: "1.5",

            // The default length of a jQuery object is 0
            length: 0,

            // The number of elements contained in the matched element set
            size: function() {
                return this.length;
            },

            //這個就是 jQ.fn 
            //就是 jQ.prototype
            toArray: function() {
                return slice.call( this, 0 );
            },

            // Get the Nth element in the matched element set OR
            // Get the whole matched element set as a clean array
            //可以不用傳參數,
            //可以傳正數或者負數;
            get: function( num ) {
                return num == null ?

                    // Return a 'clean' array
                    this.toArray() :

                    // Return just the object
                    ( num < 0 ? this[ this.length + num ] : this[ num ] );
            },

            // Take an array of elements and push it onto the stack
            // (returning the new matched element set)
            // $("html").pushStack($("body")).prevObject
            pushStack: function( elems, name, selector ) {
                // Build a new jQuery matched element set
                //一個空的jQ對象;
                var ret = this.constructor();

                //如果是數組就把樹枝放到ret里去;
                if ( jQuery.isArray( elems ) ) {
                    push.apply( ret, elems );

                } else {
                    //否者就直接放到ret;
                    jQuery.merge( ret, elems );
                }

                // Add the old object onto the stack (as a reference)
                ret.prevObject = this;

                ret.context = this.context;

                //保存selector;
                if ( name === "find" ) {
                    ret.selector = this.selector + (this.selector ? " " : "") + selector;
                } else if ( name ) {
                    ret.selector = this.selector + "." + name + "(" + selector + ")";
                }

                // Return the newly-formed element set
                return ret;
            },

            // Execute a callback for every element in the matched set.
            // (You can seed the arguments with an array of args, but this is
            // only used internally.)
            each: function( callback, args ) {
                //如果有傳args的話callback的參數為value, key;
                return jQuery.each( this, callback, args );
            },

            // jQuery.ready(function() {})
            ready: function( fn ) {

                // Attach the listeners
                //沒事,這個就會綁定一次,第二次會跳出來;
                jQuery.bindReady();

                // Add the callback
                readyList.done( fn );

                //鏈式調用
                return this;
            },

            eq: function( i ) {
                return i === -1 ?
                    this.slice( i ) :
                    this.slice( i, +i + 1 );
            },

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

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

            slice: function() {
                return this.pushStack( slice.apply( this, arguments ),
                    "slice", slice.call(arguments).join(",") );
            },

            map: function( callback ) {
                return this.pushStack( jQuery.map(this, function( elem, i ) {
                    return callback.call( elem, i, elem );
                }));
            },

            end: function() {
                return this.prevObject || this.constructor(null);
            },

            // For internal use only.
            // Behaves like an Array's method, not like a jQuery method.
            push: push,
            sort: [].sort,
            splice: [].splice
        };

// 所有在fn上的
// Give the init function the jQuery prototype for later instantiation
        jQuery.fn.init.prototype = jQuery.fn;

//還是復制繼承比較靠譜
        jQuery.extend = jQuery.fn.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;
            };

            //要繼承的目標不是對象, 或者不是function
            // Handle case when target is a string or something (possible in deep copy)
            if ( typeof target !== "object" && !jQuery.isFunction(target) ) {
                target = {};
            };

            //$.extend({xx : yy}); 就是 length === 1;
            // 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
                // null undefined繼承個毛線;
                if ( (options = arguments[ i ]) != null ) {
                    // Extend the base object
                    /*
                     //這個是target === copy的情況, 避免循環引用;
                     var bar = {copy : 3 };
                     $.extend(bar, { wawa : 2 ,lele : 1  , foo : bar});
                     */
                    for ( name in options ) {
                        src = target[ name ];
                        copy = options[ name ];

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

                        //不是深度繼承,主要copy不是undefined直接覆蓋掉
                        // Recurse if we're merging plain objects or arrays
                        if ( deep && copy && ( jQuery.isPlainObject(copy) || (copyIsArray = jQuery.isArray(copy)) ) ) {
                            //如果targer下不存在這個 數組 或者 對象 屬性,就新建一個同名屬性;
                            if ( copyIsArray ) {
                                copyIsArray = false;
                                clone = src && jQuery.isArray(src) ? src : [];

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

                            //再繼承;O 了
                            // Never move original objects, clone them
                            target[ name ] = jQuery.extend( deep, clone, copy );

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

            // Return the modified object
            return target;
        };

//bind工具方法和 DOMready;
        jQuery.extend({
            noConflict: function( deep ) {

                //某些版本會這樣處理
                /*
                 if(window.$ == $) {
                 window.$ = _$;
                 }
                 */

                window.$ = _$;
                if ( deep ) {
                    window.jQuery = _jQuery;
                }

                return jQuery;
            },
            /*
             //查看jQ的6781bugs時候找到的東東,貼出來, IE ,onpropertchange屬性真是碉堡了。。;
             <!DOCTYPE html>
             <html>
             <head>
             <title>Resource Loading event/property test</title>
             <script>
             "use strict";

             //Set up the (expando) property
             if (!("resourceLoading" in document.documentElement)) {
             document.documentElement.resourceLoading = 0;
             }

             function modify(value) {
             //modify the value
             document.documentElement.resourceLoading = value;

             //Notify listeners.
             //No attachEvent branch since in IE modifying the expando triggers
             //onpropertychange
             if (document.addEventListener) {
             var evt = document.createEvent("UIEvents");
             evt.initEvent("documentElementResourceLoading", false, false);
             document.dispatchEvent(evt);
             }
             }

             function listen(callback) {
             if (document.addEventListener) {
             document.addEventListener("documentElementResourceLoading", function (evt) {
             callback(document.documentElement.resourceLoading);
             }, false);
             } else if (document.attachEvent) {
             document.documentElement.attachEvent("onpropertychange", function (evt) {
             if (evt.propertyName === "resourceLoading") {
             callback(document.documentElement.resourceLoading);
             }
             });
             }
             }

             function increment() {
             modify(document.documentElement.resourceLoading + 1);
             }
             function decrement() {
             modify(document.documentElement.resourceLoading - 1);
             }

             function msg(message) {
             document.getElementById("output").innerHTML += '<br>' + message;
             }

             increment();
             increment();

             listen(function (value) {
             msg('resourceLoading is now: ' + value);
             });

             </script>
             </head>
             <body>
             <h1>Resource Loading event/property test</h1>
             <button onclick="increment()">Increment</button>
             <button onclick="decrement()">Decrement</button>
             <button onclick="msg('resourceLoading current value: ' + document.documentElement.resourceLoading)">Current Value</button>
             <button onclick="msg('Is resourceLoading in documentElement: ' + ('resourceLoading' in document.documentElement))">resourceLoading in document.documentElement</button>
             <div id="output"></div>
             </body>
             </html>

             */
            // Is the DOM ready to be used? Set to true once it occurs.
            isReady: false,

            // A counter to track how many items to wait for before
            // the ready event fires. See #6781
            readyWait: 1,

            // Handle when the DOM is ready
            // 用戶$.ready(true)這樣執行也ok,手動觸發事件;
            ready: function( wait ) {
                // A third-party is pushing the ready event forwards
                if ( wait === true ) {
                    jQuery.readyWait--;
                }

                // Make sure that the DOM is not already loaded
                //默認的readyWait被-1了,執行的話就為真了
                //DOM的isReady是假,就會走;
                if ( !jQuery.readyWait || (wait !== true && !jQuery.isReady) ) {
                    // Make sure body exists, at least, in case IE gets a little overzealous (ticket #5443).
                    // body都不存在還玩個毛....
                    if ( !document.body ) {
                        return setTimeout( jQuery.ready, 1 );
                    }

                    // Remember that the DOM is ready
                    jQuery.isReady = true;

                    //需要支援了, wa了個去;
                    // If a normal DOM Ready event fired, decrement, and wait if need be
                    if ( wait !== true && --jQuery.readyWait > 0 ) {
                        return;
                    };

                    //reasyList是該版本jQ的延遲對象;
                    // If there are functions bound, to execute
                    readyList.resolveWith( document, [ jQuery ] );

                    // Trigger any bound ready events
                    if ( jQuery.fn.trigger ) {
                        //解綁定ready事件;
                        jQuery( document ).trigger( "ready" ).unbind( "ready" );
                    };
                }
            },

            bindReady: function() {
                //readyBound是閉包內部的全局
                if ( readyBound ) {
                    return;
                }

                readyBound = true;

                // Catch cases where $(document).ready() is called after the
                // browser event has already occurred.
                // 因為jQ有可能是在DOM已經加載完成的的狀態下加載的;
                if ( document.readyState === "complete" ) {
                    // Handle it asynchronously to allow scripts the opportunity to delay ready
                    // 雖然沒有綁定用戶事件, 但是jQ內部會添加DOMReady用來檢測DOM加載完畢的一些兼容問題;
                    return setTimeout( jQuery.ready, 1 );
                }

                //DOM3的加載完畢事件;
                // Mozilla, Opera and webkit nightlies currently support this event
                if ( document.addEventListener ) {
                    // Use the handy event callback
                    document.addEventListener( "DOMContentLoaded", DOMContentLoaded, false );
                    // 也有可能onload比DOMContentLoaded先加載完畢
                    // A fallback to window.onload, that will always work
                    window.addEventListener( "load", jQuery.ready, false );

                    // If IE event model is used
                    // 回退到IE的事件綁定;
                } else if ( document.attachEvent ) {
                    // ensure firing before onload,
                    // maybe late but safe also for iframes
                    document.attachEvent("onreadystatechange", DOMContentLoaded);

                    // A fallback to window.onload, that will always work
                    window.attachEvent( "onload", jQuery.ready );

                    // 如果當前的界面不是通過iframe套到別的界面的話;
                    // If IE and not a frame
                    // continually check to see if the document is ready
                    var toplevel = false;

                    try {
                        toplevel = window.frameElement == null;
                    } catch(e) {}

                    if ( document.documentElement.doScroll && toplevel ) {
                        doScrollCheck();
                    }
                }
            },

            // See test/unit/core.js for details concerning isFunction.
            // Since version 1.3, DOM methods and functions like alert
            // aren't supported. They return false on IE (#2968).
            // IE8及下 typeof window.alert 返回的值為 "object"
            // 擴展IE8以及前版本 的的DOM是組件,ActiveX與com組件 : 參考http://baike.baidu.com/link?url=_S3UOypMzmx855aEEmzYqlC7iaHzWSxZE4si844SqWdr1glw2VgRkNBDb949loODUc5OEyZkRGXowtnztL5wWK
            isFunction: function( obj ) {
                return jQuery.type(obj) === "function";
            },

            isArray: Array.isArray || function( obj ) {
                return jQuery.type(obj) === "array";
            },

            // A crude way of determining if an object is a window
            isWindow: function( obj ) {
                return obj && typeof obj === "object" && "setInterval" in obj;
            },

            isNaN: function( obj ) {
                return obj == null || !rdigit.test( obj ) || isNaN( obj );
            },

            type: function( obj ) {
                return obj == null ?
                    String( obj ) :
                    class2type[ toString.call(obj) ] || "object";
            },

            //是否是純凈的對象;
            isPlainObject: function( obj ) {
                // 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 || jQuery.type(obj) !== "object" || obj.nodeType || jQuery.isWindow( obj ) ) {
                    return false;
                }

                // 如果是純凈的obj,那么constructor肯定是Object;
                // Not own constructor property must be Object
                if ( obj.constructor &&
                    !hasOwn.call(obj, "constructor") &&
                    !hasOwn.call(obj.constructor.prototype, "isPrototypeOf") ) {
                    return false;
                }

                // Own properties are enumerated firstly, so to speed up,
                // if last one is own, then all properties are own.

                //繼承的key值會在最后遍歷到, 所以只要檢測最后一個就好了;
                var key;
                for ( key in obj ) {};
                return key === undefined || hasOwn.call( obj, key );
            },

            //沒啥用哇,判斷是否是空對象 ;
            isEmptyObject: function( obj ) {
                for ( var name in obj ) {
                    return false;
                }
                return true;
            },

            //直接 throw msg 不就好了 
            error: function( msg ) {
                throw msg;
            },

            parseJSON: function( data ) {
                if ( typeof data !== "string" || !data ) {
                    return null;
                }

                // Make sure leading/trailing whitespace is removed (IE can't handle it)
                data = jQuery.trim( data );

                // Make sure the incoming data is actual JSON
                // Logic borrowed from http://json.org/json2.js
                // 替換一些非法字符, 這些字符會郵箱到JSON的parse;
                //      /^[\],:{}\s]*$/
                //    /\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g
                if ( rvalidchars.test(data.replace(rvalidescape, "@")
                    .replace(rvalidtokens, "]")
                    .replace(rvalidbraces, "")) ) {

                    // Try to use the native JSON parser first
                    return window.JSON && window.JSON.parse ?
                        window.JSON.parse( data ) :
                        //匿名 自動執行 , 這里可用eval;
                        (new Function("return " + data))();

                } else {
                    jQuery.error( "Invalid JSON: " + data );
                };
                //擴展  (new Function("wa","return {hehe:wa}"))(1);
            },

            //IE通過Microsoft.XMLDOM方式parseXML;
            // Cross-browser xml parsing
            // (xml & tmp used internally)
            parseXML: function( data , xml , tmp ) {

                if ( window.DOMParser ) { // Standard
                    tmp = new DOMParser();
                    xml = tmp.parseFromString( data , "text/xml" );
                } else { // IE
                    xml = new ActiveXObject( "Microsoft.XMLDOM" );
                    xml.async = "false";
                    xml.loadXML( data );
                };

                tmp = xml.documentElement;

                if ( ! tmp || ! tmp.nodeName || tmp.nodeName === "parsererror" ) {
                    jQuery.error( "Invalid XML: " + data );
                }

                return xml;
            },

            noop: function() {},

            // Evalulates a script in a global context
            globalEval: function( data ) {
                //rnotwhite = /\S/ 非空字符就好了;
                if ( data && rnotwhite.test(data) ) {
                    // Inspired by code by Andrea Giammarchi
                    // http://webreflection.blogspot.com/2007/08/global-scope-evaluation-and-dom.html
                    var head = document.getElementsByTagName("head")[0] || document.documentElement,
                        script = document.createElement("script");

                    script.type = "text/javascript";
                    //標準的通過新建createTextNode
                    if ( jQuery.support.scriptEval() ) {
                        script.appendChild( document.createTextNode( data ) );
                    } else {
                        //IE的用text屬性;
                        script.text = data;
                    }

                    // Use insertBefore instead of appendChild to circumvent an IE6 bug.
                    // This arises when a base node is used (#2709).
                    head.insertBefore( script, head.firstChild );
                    head.removeChild( script );
                }
            },

            nodeName: function( elem, name ) {
                //實例方法的東東是引用工具方法的
                return elem.nodeName && elem.nodeName.toUpperCase() === name.toUpperCase();
            },

            // args is for internal usage only
            // $.each({1:2},function(a ,args){console.log(this);console.log(arguments)},["a","b","c","d"])
            each: function( object, callback, args ) {
                var name, i = 0,
                    length = object.length,
                    isObj = length === undefined || jQuery.isFunction(object);

                //如果有args存在的情況下, 內部用的吧,平常我們用不多;
                if ( args ) {
                    if ( isObj ) {
                        for ( name in object ) {
                            if ( callback.apply( object[ name ], args ) === false ) {
                                break;
                            }
                        }
                    } else {
                        for ( ; i < length; ) {
                            if ( callback.apply( object[ i++ ], args ) === false ) {
                                break;
                            }
                        }
                    }

                    // A special, fast, case for the most common use of each
                } else {
                    if ( isObj ) {
                        for ( name in object ) {
                            //key value
                            if ( callback.call( object[ name ], name, object[ name ] ) === false ) {
                                break;
                            }
                        }
                    } else {
                        for ( var value = object[0];
                            //index obj;
                              i < length && callback.call( value, i, value ) !== false; value = object[++i] ) {}
                    }
                }

                return object;
            },

            // Use native String.trim function wherever possible
            //第一次就直接執行, 以后就不用判斷了;
            trim: trim ?
                function( text ) {
                    return text == null ?
                        "" :
                        trim.call( text );
                } :

                // Otherwise use our own trimming functionality
                function( text ) {
                    return text == null ?
                        "" :
                        text.toString().replace( trimLeft, "" ).replace( trimRight, "" );
                },

            // results is for internal usage only
            /*
             $.makeArray("ab")
             ["ab"]
             $.makeArray(1)
             [1]
             $.makeArray( )
             []
             $.makeArray($("body"), [1,2,3,4]);
             [1, 2, 3, 4, "<body>​…​</body>​"] makeArray是把前面的往后面放;
             */
            makeArray: function( array, results ) {
                var ret = results || [];

                if ( array != null ) {
                    // The window, strings (and functions) also have 'length'
                    // The extra typeof function check is to prevent crashes
                    // in Safari 2 (See: #3039)
                    // Tweaked logic slightly to handle Blackberry 4.7 RegExp issues #6930
                    var type = jQuery.type(array);

                    if ( array.length == null || type === "string" || type === "function" || type === "regexp" || jQuery.isWindow( array ) ) {
                        push.call( ret, array );
                    } else {
                        jQuery.merge( ret, array );
                    };
                };

                return ret;
            },

            // inArray可以檢測NodeList或者是偽數組;
            inArray: function( elem, array ) {
                if ( array.indexOf ) {
                    return array.indexOf( elem );
                }

                for ( var i = 0, length = array.length; i < length; i++ ) {
                    if ( array[ i ] === elem ) {
                        return i;
                    }
                }

                return -1;
            },

            //merge可以merge偽數組;
            //$.merge($("body"),$("html"));
            merge: function( first, second ) {
                var i = first.length,
                    j = 0;

                if ( typeof second.length === "number" ) {
                    for ( var l = second.length; j < l; j++ ) {
                        first[ i++ ] = second[ j ];
                    }

                } else {
                    while ( second[j] !== undefined ) {
                        first[ i++ ] = second[ j++ ];
                    }
                }

                first.length = i;

                return first;
            },

            //grep就是new Array().filter, 當然, 前提是如果有的話;
            grep: function( elems, callback, inv ) {
                var ret = [], retVal;
                inv = !!inv;

                // Go through the array, only saving the items
                // that pass the validator function
                for ( var i = 0, length = elems.length; i < length; i++ ) {
                    retVal = !!callback( elems[ i ], i );
                    if ( inv !== retVal ) {
                        ret.push( elems[ i ] );
                    }
                }

                return ret;
            },

            // 就是new Array().map, 當然, 前提是如果有的話;
            // arg is for internal usage only
            map: function( elems, callback, arg ) {
                var ret = [], value;

                // Go through the array, translating each of the items to their
                // new value (or values).
                for ( var i = 0, length = elems.length; i < length; i++ ) {
                    value = callback( elems[ i ], i, arg );

                    if ( value != null ) {
                        ret[ ret.length ] = value;
                    }
                }

                // Flatten any nested arrays
                return ret.concat.apply( [], ret );
            },

            //小東西的大用處;
            // A global GUID counter for objects
            guid: 1,

            /*
             你可以用proxy把兩個函數設置成統一的guid,  這個存在的意義是事件里面會用到fn.guid,代理以后的proxy的guid不能變的;
             $.proxy(fn0 = function() {}, fn1 = function(){})
             fn0.guid  ==>>  1
             fn1.guid  ==>>  1
             */
            proxy: function( fn, proxy, thisObject ) {
                if ( arguments.length === 2 ) {
                    //這個....
                    if ( typeof proxy === "string" ) {
                        thisObject = fn;
                        fn = thisObject[ proxy ];
                        proxy = undefined;

                        //如果proxy不是function
                        //$.proxy(function(){console.log(this)}, document)()
                        //就是說thisObject是上下文;
                    } else if ( proxy && !jQuery.isFunction( proxy ) ) {
                        thisObject = proxy;
                        proxy = undefined;
                    }
                };


                if ( !proxy && fn ) {
                    //這個就改了上下文,沒有使用柯里化的方式;
                    proxy = function() {
                        return fn.apply( thisObject || this, arguments );
                    };
                }

                //為了統一guid
                // Set the guid of unique handler to the same of original handler, so it can be removed
                if ( fn ) {
                    //如果fn有guid就設置proxy.guid和proxy的guid相等;
                    //有proxy就把proxy的guid和fn的guid相等;
                    //都沒有就設置一個就好了;
                    proxy.guid = fn.guid = fn.guid || proxy.guid || jQuery.guid++;
                };

                // So proxy can be declared as an argument
                return proxy;
            },

            // Mutifunctional method to get and set values to a collection
            // The value/s can be optionally by executed if its a function
            //為了減少代碼量, 弄了個這個東東;
            access: function( elems, key, value, exec, fn, pass ) {
                var length = elems.length;

                // Setting many attributes
                //對對對對,這個就是設置了
                if ( typeof key === "object" ) {
                    for ( var k in key ) {
                        jQuery.access( elems, k, key[k], exec, fn, value );
                    }
                    return elems;
                }

                // Setting one attribute
                if ( value !== undefined ) {
                    // Optionally, function values get executed if exec is true
                    // 首先exec要說true, 而且value是個function;
                    // 
                    exec = !pass && exec && jQuery.isFunction(value);

                    /*
                     //當
                     $("body").attr("hehe","hehe").attr("hehe",function(index,att) { 
                     console.log(att); return att+111
                     });
                     //fn為獲取和設置的回調;
                     */
                    for ( var i = 0; i < length; i++ ) {

                        //如果是執行就執行, this為當前元素,參數為index ,
                        //fn為獲取當前值; 
                        //exec為假, value就是null或者一個要設定的值;
                        fn( elems[i], key, exec ? value.call( elems[i], i, fn( elems[i], key ) ) : value, pass );
                    }

                    return elems;
                }

                // Getting an attribute
                return length ? fn( elems[0], key ) : undefined;
            },

            now: function() {
                return (new Date()).getTime();
            },

            // Create a simple deferred (one callbacks list)
            _Deferred: function() {
                var // callbacks list
                    callbacks = [],
                // stored [ context , args ]
                    fired,
                // to avoid firing when already doing so
                    firing,
                // flag to know if the deferred has been cancelled
                    cancelled,
                // the deferred itself

                //每次都返回一個延遲對象;
                    deferred  = {

                        // done( f1, f2, ...)
                        done: function() {
                            //defer被cacel()的話就還done個毛線球;
                            if ( !cancelled ) {
                                var args = arguments,
                                    i,
                                    length,
                                    elem,
                                    type,
                                    _fired;
                                if ( fired ) {
                                    _fired = fired;
                                    fired = 0;
                                };

                                //把藥執行的方法放到callback里面去;
                                for ( i = 0, length = args.length; i < length; i++ ) {
                                    elem = args[ i ];
                                    type = jQuery.type( elem );
                                    if ( type === "array" ) {
                                        deferred.done.apply( deferred, elem );
                                    } else if ( type === "function" ) {
                                        callbacks.push( elem );
                                    }
                                };
                                //
                                if ( _fired ) {
                                    //如果resolveWith完以后就有_fired這個東東;
                                    deferred.resolveWith( _fired[ 0 ], _fired[ 1 ] );
                                }
                            }
                            return this;
                        },

                        // resolve with given context and args
                        resolveWith: function( context, args ) {
                            if ( !cancelled && !fired && !firing ) {
                                //firing變成已經執行;
                                firing = 1;
                                try {
                                    //使用context 和 args為一個數組,作為callback的參數傳進去;
                                    while( callbacks[ 0 ] ) {
                                        callbacks.shift().apply( context, args );
                                    }
                                }
                                finally {
                                    fired = [ context, args ];
                                    firing = 0;
                                }
                            }
                            return this;
                        },

                        // resolve with this as context and given arguments
                        resolve: function() {
                            //如果是通過Deferred生成的實例, 這個配合promise執行, 上下文為promise , 這個promise有這些方法;
                            //then done fail isResolved isRejected promise
                            //如果沒有promise就相當于resoveWith了, 主要是為callback提供了當前延遲對象的上下文;
                            deferred.resolveWith( jQuery.isFunction( this.promise ) ? this.promise() : this, arguments );
                            return this;
                        },

                        // Has this deferred been resolved?
                        //返回狀態
                        isResolved: function() {
                            return !!( firing || fired );
                        },

                        // Cancel
                        cancel: function() {
                            cancelled = 1;
                            callbacks = [];
                            return this;
                        }
                    };

                return deferred;
            },

            // Full fledged deferred (two callbacks list)
            Deferred: function( func ) {
                var deferred = jQuery._Deferred(),
                    failDeferred = jQuery._Deferred(),
                    promise;
                // Add errorDeferred methods, then and promise
                jQuery.extend( deferred, {
                    then: function( doneCallbacks, failCallbacks ) {
                        deferred.done( doneCallbacks ).fail( failCallbacks );
                        return this;
                    },
                    fail: failDeferred.done,
                    rejectWith: failDeferred.resolveWith,
                    reject: failDeferred.resolve,
                    isRejected: failDeferred.isResolved,
                    // Get a promise for this deferred
                    // If obj is provided, the promise aspect is added to the object
                    // promise是為了保護deferred的狀態, 防止狀態被修改;
                    promise: function( obj , i /* internal */ ) {
                        if ( obj == null ) {
                            if ( promise ) {
                                return promise;
                            }
                            promise = obj = {};
                        }
                        i = promiseMethods.length;
                        while( i-- ) {
                            obj[ promiseMethods[ i ] ] = deferred[ promiseMethods[ i ] ];
                        }
                        return obj;
                    }
                } );
                // Make sure only one callback list will be used
                //如果成功回調執行了, 失敗的回調就清空;
                //如果失敗的回調執行了, 成功的回調就清空;
                deferred.then( failDeferred.cancel, deferred.cancel );
                // Unexpose cancel
                delete deferred.cancel;
                // Call given func if any
                //如果你傳一個函數進來;

                //var f = function(arg){ f.aa = arg}; $.Deferred(f); 
                // f就有了deferred這個東東;
                if ( func ) {
                    func.call( deferred, deferred );
                }
                return deferred;
            },

            // Deferred helper
            when: function( object ) {
                var args = arguments,
                    length = args.length,
                //若傳進來只有一個deferred,那么deferred就是這個延遲對象,
                // 否則新建一deferred;
                    deferred = length <= 1 && object && jQuery.isFunction( object.promise ) ?
                        object :
                        jQuery.Deferred(),
                //保護變量;
                    promise = deferred.promise(),
                    resolveArray;

                if ( length > 1 ) {
                    resolveArray = new Array( length );
                    jQuery.each( args, function( index, element ) {
                        jQuery.when( element ).then( function( value ) {
                            //為每一個傳進來的延遲對象添加一個回調function;
                            //并把回調傳進來的value保存到resolveArray里面;
                            resolveArray[ index ] = arguments.length > 1 ? slice.call( arguments, 0 ) : value;
                            //如果count清空了
                            if( ! --length ) {
                                //執行回調;
                                deferred.resolveWith( promise, resolveArray );
                            }

                        }, deferred.reject );
                    } );
                } else if ( deferred !== object ) {
                    deferred.resolve( object );
                };
                //返回
                return promise;
            },

            // Use of jQuery.browser is frowned upon.
            // More details: http://docs.jquery.com/Utilities/jQuery.browser
            /*
             Here are some typical results:
             Internet Explorer: 6.0, 7.0, 8.0
             Mozilla/Firefox/Flock/Camino: 1.7.12, 1.8.1.3, 1.9
             Opera: 10.06, 11.01
             Safari/Webkit: 312.8, 418.9
             */
            uaMatch: function( ua ) {
                ua = ua.toLowerCase();

                var match = rwebkit.exec( ua ) ||
                    ropera.exec( ua ) ||
                    rmsie.exec( ua ) ||
                    ua.indexOf("compatible") < 0 && rmozilla.exec( ua ) ||
                    [];

                return { browser: match[1] || "", version: match[2] || "0" };
            },

            //一個jQ的子集, 毛用啊,我勒個去;
            sub: function() {
                function jQuerySubclass( selector, context ) {
                    //這個從原型鏈上找, 會找到了原來的jQ.fn.init
                    return new jQuerySubclass.fn.init( selector, context );
                }
                //繼承工具方法;
                jQuery.extend( true, jQuerySubclass, this );
                //把jQuery保存作為父類;
                jQuerySubclass.superclass = this;

                //改變sub的原型為 一個空的jQ對象;
                jQuerySubclass.fn = jQuerySubclass.prototype = this();
                //修復constructor;
                jQuerySubclass.fn.constructor = jQuerySubclass;
                //子類;
                jQuerySubclass.subclass = this.subclass;
                jQuerySubclass.fn.init = function init( selector, context ) {
                    //上下文的修復;
                    if ( context && context instanceof jQuery && !(context instanceof jQuerySubclass) ) {
                        context = jQuerySubclass(context);
                    }

                    //執行;
                    return jQuery.fn.init.call( this, selector, context, rootjQuerySubclass );
                };

                //跟jQ一樣把subClass的原型 掛到prototype.init的原型上面;
                jQuerySubclass.fn.init.prototype = jQuerySubclass.fn;
                //定義了一個閉包的 documentRoot;
                var rootjQuerySubclass = jQuerySubclass(document);
                return jQuerySubclass;
            },

            browser: {}
        });
//到了目前為止, 為$ extend了一些工具方法;

//該版本的這個延遲對象就是一個執行列表
// Create readyList deferred
        readyList = jQuery._Deferred();

//初始化type的map;
// Populate the class2type map
        jQuery.each("Boolean Number String Function Array Date RegExp Object".split(" "), function(i, name) {
            class2type[ "[object " + name + "]" ] = name.toLowerCase();
        });

//設置瀏覽器的標準;
        browserMatch = jQuery.uaMatch( userAgent );
        if ( browserMatch.browser ) {
            jQuery.browser[ browserMatch.browser ] = true;
            jQuery.browser.version = browserMatch.version;
        };

//precated : 贊成 ,deprecated不贊成;
// Deprecated, use jQuery.browser.webkit instead
        if ( jQuery.browser.webkit ) {
            jQuery.browser.safari = true;
        }

//上面有$.inArray,應該是復寫了;
        if ( indexOf ) {
            jQuery.inArray = function( elem, array ) {
                return indexOf.call( array, elem );
            };
        }

// IE doesn't match non-breaking spaces with \s
// 修復IE的正則reg的問題;
        if ( rnotwhite.test( "\xA0" ) ) {
            trimLeft = /^[\s\xA0]+/;
            trimRight = /[\s\xA0]+$/;
        }

// All jQuery objects should point back to these
        rootjQuery = jQuery(document);

// Cleanup functions for the document ready method
// 定義 標準的文檔加載完畢 執行事件 ,事件會取消加載完畢的綁定,執行jQuery.ready();
        if ( document.addEventListener ) {
            DOMContentLoaded = function() {
                document.removeEventListener( "DOMContentLoaded", DOMContentLoaded, false );
                jQuery.ready();
            };

        } else if ( document.attachEvent ) {
            DOMContentLoaded = function() {
                // Make sure body exists, at least, in case IE gets a little overzealous (ticket #5443).
                if ( document.readyState === "complete" ) {
                    document.detachEvent( "onreadystatechange", DOMContentLoaded );
                    jQuery.ready();
                }
            };
        }

// The DOM ready check for Internet Explorer
// IE下如果document可以滾動了就是dom加載完畢, 神馬鳥hack。。笑尿;;
// 事件會取消加載完畢的綁定,執行jQuery.ready()
        function doScrollCheck() {
            if ( jQuery.isReady ) {
                return;
            }

            try {
                // If IE is used, use the trick by Diego Perini
                // http://javascript.nwbox.com/IEContentLoaded/
                document.documentElement.doScroll("left");
            } catch(e) {
                setTimeout( doScrollCheck, 1 );
                return;
            }

            // and execute any waiting functions
            jQuery.ready();
        }

// Expose jQuery to the global object
        return (window.jQuery = window.$ = jQuery);

    })();


//想看JS的兼容問題嗎,來吧,來看jQuery的代碼吧,哈哈;
    (function() {

        jQuery.support = {};

        var div = document.createElement("div");

        div.style.display = "none";
        div.innerHTML = "   <link/><table></table><a href='/a' style='color:red;float:left;opacity:.55;'>a</a><input type='checkbox'/>";

        var all = div.getElementsByTagName("*"),
            a = div.getElementsByTagName("a")[0],
            select = document.createElement("select"),
            opt = select.appendChild( document.createElement("option") );

        // Can't get basic test support
        if ( !all || !all.length || !a ) {
            return;
        }

        jQuery.support = {
            // IE strips leading whitespace when .innerHTML is used
            leadingWhitespace: div.firstChild.nodeType === 3,

            // Make sure that tbody elements aren't automatically inserted
            // IE will insert them into empty tables
            tbody: !div.getElementsByTagName("tbody").length,

            // Make sure that link elements get serialized correctly by innerHTML
            // This requires a wrapper element in IE
            htmlSerialize: !!div.getElementsByTagName("link").length,

            // Get the style information from getAttribute
            // (IE uses .cssText insted)
            style: /red/.test( a.getAttribute("style") ),

            // Make sure that URLs aren't manipulated
            // (IE normalizes it by default)
            hrefNormalized: a.getAttribute("href") === "/a",

            // Make sure that element opacity exists
            // (IE uses filter instead)
            // Use a regex to work around a WebKit issue. See #5145
            opacity: /^0.55$/.test( a.style.opacity ),

            // Verify style float existence
            // (IE uses styleFloat instead of cssFloat)
            cssFloat: !!a.style.cssFloat,

            // Make sure that if no value is specified for a checkbox
            // that it defaults to "on".
            // (WebKit defaults to "" instead)
            checkOn: div.getElementsByTagName("input")[0].value === "on",

            // Make sure that a selected-by-default option has a working selected property.
            // (WebKit defaults to false instead of true, IE too, if it's in an optgroup)
            optSelected: opt.selected,

            // Will be defined later
            deleteExpando: true,
            optDisabled: false,
            checkClone: false,
            _scriptEval: null,
            noCloneEvent: true,
            boxModel: null,
            inlineBlockNeedsLayout: false,
            shrinkWrapBlocks: false,
            reliableHiddenOffsets: true
        };

        // Make sure that the options inside disabled selects aren't marked as disabled
        // (WebKit marks them as diabled)
        select.disabled = true;
        jQuery.support.optDisabled = !opt.disabled;

        jQuery.support.scriptEval = function() {
            if ( jQuery.support._scriptEval === null ) {
                var root = document.documentElement,
                    script = document.createElement("script"),
                    id = "script" + jQuery.now();

                script.type = "text/javascript";
                try {
                    script.appendChild( document.createTextNode( "window." + id + "=1;" ) );
                } catch(e) {}

                root.insertBefore( script, root.firstChild );

                // Make sure that the execution of code works by injecting a script
                // tag with appendChild/createTextNode
                // (IE doesn't support this, fails, and uses .text instead)
                if ( window[ id ] ) {
                    jQuery.support._scriptEval = true;
                    delete window[ id ];
                } else {
                    jQuery.support._scriptEval = false;
                }

                root.removeChild( script );
                // release memory in IE
                root = script = id  = null;
            }

            return jQuery.support._scriptEval;
        };

        // Test to see if it's possible to delete an expando from an element
        // Fails in Internet Explorer
        try {
            delete div.test;

        } catch(e) {
            jQuery.support.deleteExpando = false;
        }

        if ( div.attachEvent && div.fireEvent ) {
            div.attachEvent("onclick", function click() {
                // Cloning a node shouldn't copy over any
                // bound event handlers (IE does this)
                jQuery.support.noCloneEvent = false;
                div.detachEvent("onclick", click);
            });
            div.cloneNode(true).fireEvent("onclick");
        }

        div = document.createElement("div");
        div.innerHTML = "<input type='radio' name='radiotest' checked='checked'/>";

        var fragment = document.createDocumentFragment();
        fragment.appendChild( div.firstChild );

        // WebKit doesn't clone checked state correctly in fragments
        jQuery.support.checkClone = fragment.cloneNode(true).cloneNode(true).lastChild.checked;

        // Figure out if the W3C box model works as expected
        // document.body must exist before we can do this
        jQuery(function() {
            var div = document.createElement("div"),
                body = document.getElementsByTagName("body")[0];

            // Frameset documents with no body should not run this code
            if ( !body ) {
                return;
            }

            div.style.width = div.style.paddingLeft = "1px";
            body.appendChild( div );
            jQuery.boxModel = jQuery.support.boxModel = div.offsetWidth === 2;

            if ( "zoom" in div.style ) {
                // Check if natively block-level elements act like inline-block
                // elements when setting their display to 'inline' and giving
                // them layout
                // (IE < 8 does this)
                div.style.display = "inline";
                div.style.zoom = 1;
                jQuery.support.inlineBlockNeedsLayout = div.offsetWidth === 2;

                // Check if elements with layout shrink-wrap their children
                // (IE 6 does this)
                div.style.display = "";
                div.innerHTML = "<div style='width:4px;'></div>";
                jQuery.support.shrinkWrapBlocks = div.offsetWidth !== 2;
            }

            div.innerHTML = "<table><tr><td style='padding:0;border:0;display:none'></td><td>t</td></tr></table>";
            var tds = div.getElementsByTagName("td");

            // Check if table cells still have offsetWidth/Height when they are set
            // to display:none and there are still other visible table cells in a
            // table row; if so, offsetWidth/Height are not reliable for use when
            // determining if an element has been hidden directly using
            // display:none (it is still safe to use offsets if a parent element is
            // hidden; don safety goggles and see bug #4512 for more information).
            // (only IE 8 fails this test)
            jQuery.support.reliableHiddenOffsets = tds[0].offsetHeight === 0;

            tds[0].style.display = "";
            tds[1].style.display = "none";

            // Check if empty table cells still have offsetWidth/Height
            // (IE < 8 fail this test)
            jQuery.support.reliableHiddenOffsets = jQuery.support.reliableHiddenOffsets && tds[0].offsetHeight === 0;
            div.innerHTML = "";

            body.removeChild( div ).style.display = "none";
            div = tds = null;
        });

        // Technique from Juriy Zaytsev
        // http://thinkweb2.com/projects/prototype/detecting-event-support-without-browser-sniffing/
        var eventSupported = function( eventName ) {
            var el = document.createElement("div");
            eventName = "on" + eventName;

            // We only care about the case where non-standard event systems
            // are used, namely in IE. Short-circuiting here helps us to
            // avoid an eval call (in setAttribute) which can cause CSP
            // to go haywire. See: https://developer.mozilla.org/en/Security/CSP
            if ( !el.attachEvent ) {
                return true;
            }

            var isSupported = (eventName in el);
            if ( !isSupported ) {
                el.setAttribute(eventName, "return;");
                isSupported = typeof el[eventName] === "function";
            }
            el = null;

            return isSupported;
        };

        jQuery.support.submitBubbles = eventSupported("submit");
        jQuery.support.changeBubbles = eventSupported("change");

        // release memory in IE
        div = all = a = null;
    })();

//這個是jQ實例會用到的工具方法,和jQ的實例的各種息息相關的;
    var rbrace = /^(?:\{.*\}|\[.*\])$/;

    jQuery.extend({
        cache: {},

        // Please use with caution
        uuid: 0,

        // Unique for each copy of jQuery on the page
        // Non-digits removed to match rinlinejQuery
        // 該版本的expando是使用jQuery加上版本加上隨機的字符串,把非數字的拿掉;
        expando: "jQuery" + ( jQuery.fn.jquery + Math.random() ).replace( /\D/g, "" ),

        // The following elements throw uncatchable exceptions if you
        // attempt to add expando properties to them.
        noData: {
            "embed": true,
            // Ban all objects except for Flash (which handle expandos)
            "object": "clsid:D27CDB6E-AE6D-11cf-96B8-444553540000",
            "applet": true
        },

        hasData: function( elem ) {
            //如果是類型屬性就在緩存中查找對應的data, 否則象window這樣的元素直接通過expando查找;
            elem = elem.nodeType ? jQuery.cache[ elem[jQuery.expando] ] : elem[ jQuery.expando ];
            //直接返回 而且要非空;
            return !!elem && !jQuery.isEmptyObject(elem);
        },

        data: function( elem, name, data, pvt /* Internal Use Only */ ) {
            //如果是embed ,applet ,object就直接退出去,不報錯;
            if ( !jQuery.acceptData( elem ) ) {
                return;
            }

            var internalKey = jQuery.expando, getByName = typeof name === "string", thisCache,

            // ie67 不區分節點屬性和 dom屬性的, data必須統一綁定到dom屬性上面
            // We have to handle DOM nodes and JS objects differently because IE6-7
            // can't GC object references properly across the DOM-JS boundary
                isNode = elem.nodeType,

            // Only DOM nodes need the global jQuery cache; JS object data is
            // attached directly to the object so GC can occur automatically
            // 如果是dom節點就通過jQuery的cache查找,剩下的比如window或者其他的對象 直接在自己身上找;
                cache = isNode ? jQuery.cache : elem,

            // Only defining an ID for JS objects if its cache already exists allows
            // the code to shortcut on the same path as a DOM node with no cache
            //獲取唯一的id, 如果連id都沒有那還玩個毛先求;
                id = isNode ? elem[ jQuery.expando ] : elem[ jQuery.expando ] && jQuery.expando;

            // Avoid doing any more work than we need to when trying to get data on an
            // object that has no data at all
            // 如果連傳進來的data都沒有, 而且沒有唯一的id 就返回 。 
            // 有可能要是指的name是一個對象,所以也要排除name是對象, getByName確保了name是對象的話為假,就不會return了
            if ( (!id || (pvt && id && !cache[ id ][ internalKey ])) && getByName && data === undefined ) {
                return;
            }

            //沒有id就設置一個id, 當前是有保存的數據(data)的
            if ( !id ) {
                // Only DOM nodes need a new unique ID for each element since their data
                // ends up in the global cache
                // 只有dom元素才有唯一的id;其余的就直接用expando作為id, 參數的情況多,處理也多;
                if ( isNode ) {
                    elem[ jQuery.expando ] = id = ++jQuery.uuid;
                } else {
                    id = jQuery.expando;
                }
            }

            //如果不存在的話, 設置id緩存的空對象
            if ( !cache[ id ] ) {
                cache[ id ] = {};
            }

            // An object can be passed to jQuery.data instead of a key/value pair; this gets
            // shallow copied over onto the existing cache
            if ( typeof name === "object" ) {
                // pvt是內部用的,是private的意思, 把當前的name直接繼承到私有的緩存中;
                if ( pvt ) {
                    cache[ id ][ internalKey ] = jQuery.extend(cache[ id ][ internalKey ], name);
                } else {
                    //直接繼承
                    cache[ id ] = jQuery.extend(cache[ id ], name);
                }
            }

            //公用的緩存
            thisCache = cache[ id ];

            // Internal jQuery data is stored in a separate object inside the object's data
            // cache in order to avoid key collisions between internal data and user-defined
            // data
            // 有必要開辟一個私人的緩沖區,;
            if ( pvt ) {
                if ( !thisCache[ internalKey ] ) {
                    thisCache[ internalKey ] = {};
                }

                thisCache = thisCache[ internalKey ];
            }

            //data不是undefined說明 name一定是字符串了?, 好吧,說的通...也沒人那么做;
            if ( data !== undefined ) {
                thisCache[ name ] = data;
            }

            // TODO: This is a hack for 1.5 ONLY. It will be removed in 1.6. Users should
            // not attempt to inspect the internal events object using jQuery.data, as this
            // internal data object is undocumented and subject to change.
            // 你可以傳一個event, 就會獲取到該元素綁定的所有事件;
            if ( name === "events" && !thisCache[name] ) {
                return thisCache[ internalKey ] && thisCache[ internalKey ].events;
            }

            //如果是name字符串 就返回這個值, 否則返回整個元素緩存;
            return getByName ? thisCache[ name ] : thisCache;
        },

        //刪除數據;
        removeData: function( elem, name, pvt /* Internal Use Only */ ) {
            if ( !jQuery.acceptData( elem ) ) {
                return;
            }

            var internalKey = jQuery.expando, isNode = elem.nodeType,

            // See jQuery.data for more information
                cache = isNode ? jQuery.cache : elem,

            // See jQuery.data for more information
                id = isNode ? elem[ jQuery.expando ] : jQuery.expando;

            // If there is already no cache entry for this object, there is no
            // purpose in continuing
            if ( !cache[ id ] ) {
                return;
            }

            if ( name ) {
                //分內部和非內部數據兩種情況
                var thisCache = pvt ? cache[ id ][ internalKey ] : cache[ id ];

                //刪刪刪刪;
                if ( thisCache ) {
                    delete thisCache[ name ];

                    // If there is no data left in the cache, we want to continue
                    // and let the cache object itself get destroyed
                    if ( !jQuery.isEmptyObject(thisCache) ) {
                        //不給Object.delete;
                        return;
                    }
                }
            }

            // See jQuery.data for more information
            // 不管name是否有傳,只要pvt是真 , 就把所有私人緩存全刪了, 好屌是不是, 誰這么干啊,臥槽;
            if ( pvt ) {
                delete cache[ id ][ internalKey ];

                // Don't destroy the parent cache unless the internal data object
                // had been the only thing left in it
                if ( !jQuery.isEmptyObject(cache[ id ]) ) {
                    return;
                };
            };

            //引用內部數據的地址;
            var internalCache = cache[ id ][ internalKey ];

            // Browsers that fail expando deletion also refuse to delete expandos on
            // the window, but it will allow it on all other JS objects; other browsers
            // don't care
            // 兼容問題
            if ( jQuery.support.deleteExpando || cache != window ) {
                delete cache[ id ];
            } else {
                cache[ id ] = null;
            }

            // We destroyed the entire user cache at once because it's faster than
            // iterating through each key, but we need to continue to persist internal
            // data if it existed
            if ( internalCache ) {
                cache[ id ] = {};
                cache[ id ][ internalKey ] = internalCache;

                // Otherwise, we need to eliminate the expando on the node to avoid
                // false lookups in the cache for entries that no longer exist
            } else if ( isNode ) {
                // IE67 中的元素節點是com組件 ,你刪他東西要報錯的 ,他會提示你:(對象不支持此操作), 使用removeAttribute刪除屬性靠譜
                // removeAttribute 就可以刪除, 不會報錯(因為IE67不區分dom屬性和html屬性);
                // IE does not allow us to delete expando properties from nodes,
                // nor does it have a removeAttribute function on Document nodes;
                // we must handle all of these cases
                if ( jQuery.support.deleteExpando ) {
                    delete elem[ jQuery.expando ];
                } else if ( elem.removeAttribute ) {
                    elem.removeAttribute( jQuery.expando );
                } else {
                    elem[ jQuery.expando ] = null;
                }
            }
        },

        // For internal use only.
        _data: function( elem, name, data ) {
            return jQuery.data( elem, name, data, true );
        },

        // A method for determining if a DOM node can handle the data expando
        //確認dom是否可以保存數據
        acceptData: function( elem ) {
            if ( elem.nodeName ) {
                var match = jQuery.noData[ elem.nodeName.toLowerCase() ];

                if ( match ) {
                    //embed 和 applet是true,
                    //apple 的組件
                    return !(match === true || elem.getAttribute("classid") !== match);
                    /*
                     簡寫真是坑爹啊;
                     if(match === true) {
                     return false;
                     };
                     if( elem.getAttribute("classid") !== match ) {
                     return false;
                     }
                     */
                }
            }

            return true;
        }
    });

//這個為實例原型繼承了data;
    jQuery.fn.extend({
        data: function( key, value ) {
            var data = null;
            //連key都不給, 就返回所有的數據唄;
            if ( typeof key === "undefined" ) {
                if ( this.length ) {
                    //data為第一個
                    data = jQuery.data( this[0] );

                    if ( this[0].nodeType === 1 ) {
                        var attr = this[0].attributes, name;
                        for ( var i = 0, l = attr.length; i < l; i++ ) {
                            name = attr[i].name;

                            //把所有html5定義的datalist保存到緩存的列表;
                            if ( name.indexOf( "data-" ) === 0 ) {
                                name = name.substr( 5 );
                                dataAttr( this[0], name, data[ name ] );
                            }
                        }
                    }
                }

                return data;

                //這個相當于是設置了;
            } else if ( typeof key === "object" ) {
                return this.each(function() {
                    jQuery.data( this, key );
                });
            };

            //這個是key不是undefined;
            var parts = key.split(".");
            parts[1] = parts[1] ? "." + parts[1] : "";

            //這個是獲取值的情況;
            if ( value === undefined ) {
                //可以綁定元素的getData事件, 如果元素的屬性發生改變,自動觸發事件, 傳進去的參數為改變的屬性名;
                data = this.triggerHandler("getData" + parts[1] + "!", [parts[0]]);

                // Try to fetch any internally stored data first
                if ( data === undefined && this.length ) {
                    //如果這個的值不在jQ的cache緩存里面;
                    data = jQuery.data( this[0], key );
                    //從data-list里面查找;
                    data = dataAttr( this[0], key, data );
                }

                //parts[1]應該是命名空間, 為什么會重新迭代自己哇;
                return data === undefined && parts[1] ?
                    this.data( parts[0] ) :
                    data;

            } else {
                //這個是設置值
                return this.each(function() {
                    var $this = jQuery( this ),
                        args = [ parts[0], value ];

                    //觸發自定義的設置事件;
                    //你通過這種方式綁定 :
                    //$("body").bind("setData",function(){console.log(1)});
                    //$("body").data("xx",2) ==》》 就會在控制臺打出 1;
                    // ** 注意綁定的時候不要! 這個符號, 這個符號會被過濾掉;

                    $this.triggerHandler( "setData" + parts[1] + "!", args );
                    jQuery.data( this, key, value );
                    //觸發自定義的改變事件;
                    $this.triggerHandler( "changeData" + parts[1] + "!", args );
                });
            }
        },

        removeData: function( key ) {
            return this.each(function() {
                jQuery.removeData( this, key );
            });
        }
    });

//這個會把元素的datalist的值比如 data-xx="true" 更新到 $.cache.uniqueId里面去;
    function dataAttr( elem, key, data ) {
        // If nothing was found internally, try to fetch any
        // data from the HTML5 data-* attribute
        if ( data === undefined && elem.nodeType === 1 ) {
            data = elem.getAttribute( "data-" + key );

            if ( typeof data === "string" ) {
                try {
                    data = data === "true" ? true :
                        data === "false" ? false :
                            data === "null" ? null :
                                !jQuery.isNaN( data ) ? parseFloat( data ) :
                                    rbrace.test( data ) ? jQuery.parseJSON( data ) :
                                        data;
                } catch( e ) {}

                // Make sure we set the data so it isn't changed later
                jQuery.data( elem, key, data );

            } else {
                data = undefined;
            }
        }

        return data;
    }


//這個是工具方法,還是實例上面用的;
    jQuery.extend({
        queue: function( elem, type, data ) {
            if ( !elem ) {
                return;
            }

            //用戶可以使用自定義的隊列type : 默認為 fxqueue;
            type = (type || "fx") + "queue";
            //獲取在緩存系統中的隊列, 這個是公用的緩存;
            var q = jQuery._data( elem, type );

            //如果沒有data
            // Speed up dequeue by getting out quickly if this is just a lookup
            //這個是get;
            if ( !data ) {
                return q || [];
            }

            //調用工具方法_data保存隊列;
            if ( !q || jQuery.isArray(data) ) {
                q = jQuery._data( elem, type, jQuery.makeArray(data) );
            } else {
                //直接push就好了;
                q.push( data );
            };

            return q;
        },
        /*
         $("body").queue(function(bar) {
         console.log(1),bar()
         }).queue(function(bar) {
         console.log(2),bar()
         }).queue(function(bar){
         console.log(3),bar()
         }).queue(function(bar){
         console.log(4)
         });
         $("body").dequeue();
         */
        dequeue: function( elem, type ) {
            type = type || "fx";

            //獲取初始值;
            var queue = jQuery.queue( elem, type ),
                fn = queue.shift();

            //
            // If the fx queue is dequeued, always remove the progress sentinel
            if ( fn === "inprogress" ) {
                fn = queue.shift();
            }

            if ( fn ) {
                // Add a progress sentinel to prevent the fx queue from being
                // automatically dequeued
                if ( type === "fx" ) {
                    //把進度進棧;
                    queue.unshift("inprogress");
                };

                //用elem作為上下文 執行, 有一個回調時dequeue
                fn.call(elem, function() {
                    jQuery.dequeue(elem, type);
                });
            }

            if ( !queue.length ) {
                jQuery.removeData( elem, type + "queue", true );
            }
        }
    });

    /*
     $("body").queue(function(bar) {
     console.log(1),bar()
     }).queue(function(bar) {
     console.log(2),bar();
     }).queue(function(bar){
     console.log(3),bar()
     }).queue(function(bar){
     console.log(4)
     });
     //默認的fx會馬上執行哦;


     //第二次執行的時候queue[0]是 inprogress,所以不會馬上執行;
     $("body").queue(function(bar) {
     console.log(1),bar()
     }).queue(function(bar) {
     console.log(2),bar();
     }).queue(function(bar){
     console.log(3),bar()
     }).queue(function(bar){
     console.log(4)
     });

     //這樣不會馬上執行;
     $("body").queue('fx', "inprogress")
     .queue(function(bar) {
     console.log(1),bar()
     }).queue(function(bar) {
     console.log(2),bar();
     }).queue(function(bar){
     console.log(3),bar()
     }).queue(function(bar){
     console.log(4)
     });
     */
//添加方法到實例上面了
    jQuery.fn.extend({
        queue: function( type, data ) {
            if ( typeof type !== "string" ) {
                data = type;
                type = "fx";
            }

            if ( data === undefined ) {
                return jQuery.queue( this[0], type );
            }

            //進棧就馬上執行哦;
            return this.each(function( i ) {
                //如果是默認的fx
                var queue = jQuery.queue( this, type, data );

                //馬上開始隊列;
                if ( type === "fx" && queue[0] !== "inprogress" ) {
                    jQuery.dequeue( this, type );
                }
            });
        },
        dequeue: function( type ) {
            return this.each(function() {
                jQuery.dequeue( this, type );
            });
        },

        //動畫效果中有用到delay , 會延遲執行;
        // Based off of the plugin by Clint Helfers, with permission.
        // http://blindsignals.com/index.php/2009/07/jquery-delay/
        delay: function( time, type ) {
            time = jQuery.fx ? jQuery.fx.speeds[time] || time : time;
            type = type || "fx";

            return this.queue( type, function() {
                var elem = this;
                setTimeout(function() {
                    jQuery.dequeue( elem, type );
                }, time );
            });
        },

        clearQueue: function( type ) {
            return this.queue( type || "fx", [] );
        }
    });




    var rclass = /[\n\t\r]/g,
        rspaces = /\s+/,
        rreturn = /\r/g,
        rspecialurl = /^(?:href|src|style)$/,
        rtype = /^(?:button|input)$/i,
        rfocusable = /^(?:button|input|object|select|textarea)$/i,
        rclickable = /^a(?:rea)?$/i,
        rradiocheck = /^(?:radio|checkbox)$/i;

    jQuery.props = {
        "for": "htmlFor",
        "class": "className",
        readonly: "readOnly",
        maxlength: "maxLength",
        cellspacing: "cellSpacing",
        rowspan: "rowSpan",
        colspan: "colSpan",
        tabindex: "tabIndex",
        usemap: "useMap",
        frameborder: "frameBorder"
    };

    jQuery.fn.extend({
        attr: function( name, value ) {
            //通過access減少代碼量, 
            //判斷了value可以是一個function的情況;
            return jQuery.access( this, name, value, true, jQuery.attr );
        },

        //也是調用attr這個方法的, fn拿來搞毛用;
        removeAttr: function( name, fn ) {
            return this.each(function(){
                jQuery.attr( this, name, "" );
                if ( this.nodeType === 1 ) {
                    this.removeAttribute( name );
                }
            });
        },

        addClass: function( value ) {
            if ( jQuery.isFunction(value) ) {
                return this.each(function(i) {
                    var self = jQuery(this);
                    //如果value是function 就給這個function傳 這個元素的class;
                    self.addClass( value.call(this, i, self.attr("class")) );
                });
            }

            if ( value && typeof value === "string" ) {
                var classNames = (value || "").split( rspaces );

                //為什么不調用each呢,還要寫循環啊;
                for ( var i = 0, l = this.length; i < l; i++ ) {
                    var elem = this[i];
                    //是元素節點的話
                    if ( elem.nodeType === 1 ) {
                        //優化設置
                        if ( !elem.className ) {
                            elem.className = value;

                        } else {
                            //通過字符串的操作進行class操作;
                            var className = " " + elem.className + " ",
                                setClass = elem.className;

                            for ( var c = 0, cl = classNames.length; c < cl; c++ ) {
                                if ( className.indexOf( " " + classNames[c] + " " ) < 0 ) {
                                    setClass += " " + classNames[c];
                                }
                            }
                            elem.className = jQuery.trim( setClass );
                        }
                    }
                }
            }

            return this;
        },

        removeClass: function( value ) {
            //這個也可以通過access調用嗎;
            if ( jQuery.isFunction(value) ) {
                return this.each(function(i) {
                    var self = jQuery(this);
                    self.removeClass( value.call(this, i, self.attr("class")) );
                });
            }

            //字符串連接;
            if ( (value && typeof value === "string") || value === undefined ) {
                var classNames = (value || "").split( rspaces );

                for ( var i = 0, l = this.length; i < l; i++ ) {
                    var elem = this[i];

                    if ( elem.nodeType === 1 && elem.className ) {
                        if ( value ) {
                            var className = (" " + elem.className + " ").replace(rclass, " ");
                            for ( var c = 0, cl = classNames.length; c < cl; c++ ) {
                                className = className.replace(" " + classNames[c] + " ", " ");
                            }
                            elem.className = jQuery.trim( className );

                        } else {
                            elem.className = "";
                        }
                    }
                }
            }

            return this;
        },

        toggleClass: function( value, stateVal ) {
            var type = typeof value,
                isBool = typeof stateVal === "boolean";
            // if function 
            if ( jQuery.isFunction( value ) ) {
                return this.each(function(i) {
                    var self = jQuery(this);
                    self.toggleClass( value.call(this, i, self.attr("class"), stateVal), stateVal );
                });
            }

            return this.each(function() {
                if ( type === "string" ) {
                    // toggle individual class names
                    var className,
                        i = 0,
                        self = jQuery( this ),
                        state = stateVal,
                        classNames = value.split( rspaces );

                    //toggleClass 你可以傳("xx aa bb cc");一接口多用, write less, do more;
                    while ( (className = classNames[ i++ ]) ) {
                        // check each className given, space seperated list
                        state = isBool ? state : !self.hasClass( className );
                        self[ state ? "addClass" : "removeClass" ]( className );
                    }

                } else if ( type === "undefined" || type === "boolean" ) {
                    //把當前的classname保存到私有的數據里面;
                    if ( this.className ) {
                        // store className if set
                        jQuery._data( this, "__className__", this.className );
                    };

                    //簡寫是好,但是不覺得看著麻煩嗎。 john reisg  \*_*\
                    // toggle whole className
                    this.className = this.className || value === false ? "" : jQuery._data( this, "__className__" ) || "";
                }
            });
        },

        hasClass: function( selector ) {
            var className = " " + selector + " ";
            for ( var i = 0, l = this.length; i < l; i++ ) {
                //用indexOf
                if ( (" " + this[i].className + " ").replace(rclass, " ").indexOf( className ) > -1 ) {
                    return true;
                }
            }

            return false;
        },

        //val做的處理有點多哦;
        val: function( value ) {

            //獲取的情況下;
            if ( !arguments.length ) {
                var elem = this[0];

                if ( elem ) {
                    if ( jQuery.nodeName( elem, "option" ) ) {
                        // attributes.value is undefined in Blackberry 4.7 but
                        // uses .value. See #6932
                        var val = elem.attributes.value;
                        //specified 特性, 如果用戶設定了值那么specified就是true;
                        return !val || val.specified ? elem.value : elem.text;
                    };

                    // 如果是select
                    // We need to handle select boxes special
                    if ( jQuery.nodeName( elem, "select" ) ) {
                        var index = elem.selectedIndex,
                            values = [],
                            options = elem.options,
                            one = elem.type === "select-one";

                        //
                        // Nothing was selected
                        if ( index < 0 ) {
                            return null;
                        }

                        // 會返回多個值;
                        // Loop through all the selected options
                        for ( var i = one ? index : 0, max = one ? index + 1 : options.length; i < max; i++ ) {
                            var option = options[ i ];

                            // Don't return options that are disabled or in a disabled optgroup
                            if ( option.selected && (jQuery.support.optDisabled ? !option.disabled : option.getAttribute("disabled") === null) &&
                                (!option.parentNode.disabled || !jQuery.nodeName( option.parentNode, "optgroup" )) ) {

                                // Get the specific value for the option
                                value = jQuery(option).val();

                                // We don't need an array for one selects
                                if ( one ) {
                                    return value;
                                }

                                // Multi-Selects return an array
                                values.push( value );
                            }
                        }

                        return values;
                    }

                    //如果是單選radio標簽; 統一返回on;
                    // Handle the case where in Webkit "" is returned instead of "on" if a value isn't specified
                    if ( rradiocheck.test( elem.type ) && !jQuery.support.checkOn ) {
                        return elem.getAttribute("value") === null ? "on" : elem.value;
                    }

                    //最后弄這個, 如果是input的file文件類型 ,就會返回文件的全部路徑;
                    // Everything else, we just grab the value
                    return (elem.value || "").replace(rreturn, "");

                }

                return undefined;
            }

            //設置的情況下;
            var isFunction = jQuery.isFunction(value);

            return this.each(function(i) {
                var self = jQuery(this), val = value;

                //這個檢測有必要嗎....
                if ( this.nodeType !== 1 ) {
                    return;
                }

                //是function的話;
                if ( isFunction ) {
                    val = value.call(this, i, self.val());
                }

                // Treat null/undefined as ""; convert numbers to string
                if ( val == null ) {
                    val = "";
                } else if ( typeof val === "number" ) {
                    val += "";
                } else if ( jQuery.isArray(val) ) {
                    //會有設置array的元素嗎;
                    val = jQuery.map(val, function (value) {
                        return value == null ? "" : value + "";
                    });
                }

                //兼容問題的處理;
                if ( jQuery.isArray(val) && rradiocheck.test( this.type ) ) {
                    this.checked = jQuery.inArray( self.val(), val ) >= 0;

                } else if ( jQuery.nodeName( this, "select" ) ) {
                    var values = jQuery.makeArray(val);

                    jQuery( "option", this ).each(function() {
                        this.selected = jQuery.inArray( jQuery(this).val(), values ) >= 0;
                    });

                    if ( !values.length ) {
                        this.selectedIndex = -1;
                    }

                    //如果不用庫的話,自己弄也是直接使用 elem.value = settingValue;
                } else {
                    this.value = val;
                }
            });
        }
    });


    jQuery.extend({
        // shortcut, 快捷操作, 比如
        // $("inpt").attr("val","hehe").attr("css","height:100px").attr({"html":"innerHTML"});
        // 會調用實例下的指定的方法;
        attrFn: {
            val: true,
            css: true,
            html: true,
            text: true,
            data: true,
            width: true,
            height: true,
            offset: true
        },

        //這個還是工具方法上面的方法,不是實例上的方法,這個方法包攬的東西不少;
        attr: function( elem, name, value, pass ) {
            // don't get/set attributes on text, comment and attribute nodes
            //text文本;                           //comment注釋;           //屬性節點, 我就搞不懂了,為什么屬性也可以算是節點;
            if ( !elem || elem.nodeType === 3 || elem.nodeType === 8 || elem.nodeType === 2 ) {
                return undefined;
            }

            if ( pass && name in jQuery.attrFn ) {
                return jQuery(elem)[name](value);
            }

            //不是xml,話說xml我用的也少啊;
            var notxml = elem.nodeType !== 1 || !jQuery.isXMLDoc( elem ),
            // Whether we are setting (or getting)
                set = value !== undefined;

            // Try to normalize/fix the name
            // 執行的前后順序是先 (notxml&&jQuery.props
            /*
             jQuery.props = {
             "for": "htmlFor",
             "class": "className",
             readonly: "readOnly",
             maxlength: "maxLength",
             cellspacing: "cellSpacing",
             rowspan: "rowSpan",
             colspan: "colSpan",
             tabindex: "tabIndex",
             usemap: "useMap",
             frameborder: "frameBorder"
             };
             */
            name = notxml && jQuery.props[ name ] || name;

            // Only do all the following if this is a node (faster for style)
            if ( elem.nodeType === 1 ) {
                // These attributes require special treatment
                //  rspecialurl ==>> /href|src|style/;
                var special = rspecialurl.test( name );

                //safari無法獲取selectIndex的情況;
                // Safari mis-reports the default selected property of an option
                // Accessing the parent's selectedIndex property fixes it
                if ( name === "selected" && !jQuery.support.optSelected ) {
                    var parent = elem.parentNode;
                    if ( parent ) {
                        parent.selectedIndex;

                        // Make sure that it also works with optgroups, see #5701
                        if ( parent.parentNode ) {
                            parent.parentNode.selectedIndex;
                        }
                    }
                }

                // If applicable, access the attribute via the DOM 0 way
                // 'in' checks fail in Blackberry 4.7 #6931
                // href ,src 和 style不處理;
                /*如果 name in elem 就是node的屬性,不一定是attr該處理的東東了;
                 "id" in document.body  ==>>  true;
                 "className" in document.body  ==>>  true
                 */
                if ( (name in elem || elem[ name ] !== undefined) && notxml && !special ) {
                    if ( set ) {
                        //有type的只有input的節點了。。。。;
                        // We can't allow the type property to be changed (since it causes problems in IE)
                        if ( name === "type" && rtype.test( elem.nodeName ) && elem.parentNode ) {
                            jQuery.error( "type property can't be changed" );
                        };

                        //這個是node屬性,為什么要用removeAttribte刪除呢;調用attr(elem, name , null);就會把這個屬性刪了哇;
                        if ( value === null ) {
                            if ( elem.nodeType === 1 ) {
                                elem.removeAttribute( name );
                            }

                        } else {
                            elem[ name ] = value;
                        }
                    }

                    //要弄懂為什么set在前面,而get是在后面,因為set以后也要返回get;
                    //剩下的是的get;

                    //document.body.getAttributeNode("id").nodeType  ==>>  2;
                    //getAttributeNode相當于attributes.item()
                    // browsers index elements by id/name on forms, give priority to attributes.
                    /*
                     //getAttributeNode是神馬東西的DEMO;
                     <!DOCTYPE html>
                     <html>
                     <head>
                     <title></title>
                     </head>
                     <body>
                     <form name="foo">
                     <input value="hehe" type="text" name="ipt0" />
                     </form>
                     <script>
                     var eForm = document.getElementsByTagName("form")[0];
                     console.log( eForm.getAttributeNode("ipt0") ); // ==>  null;
                     console.log( eForm.getAttributeNode("name") ); // ==> nodeType==2, value=2;
                     </script>
                     </body>
                     </html>
                     */
                    if ( jQuery.nodeName( elem, "form" ) && elem.getAttributeNode(name) ) {
                        //所用的 getAttributeNode() 已不贊成使用。請使用 getAttribute() 替代。;臥槽火狐爆了這個東西;
                        return elem.getAttributeNode( name ).nodeValue;
                    };

                    // elem.tabIndex doesn't always return the correct value when it hasn't been explicitly set
                    // http://fluidproject.org/blog/2008/01/09/getting-setting-and-removing-tabindex-values-with-javascript/

                    if ( name === "tabIndex" ) {
                        var attributeNode = elem.getAttributeNode( "tabIndex" );

                        //如果用戶有定義過才返回值,沒定義郭

                        // elem.tabIndex doesn't always return the correct value when it hasn't been explicitly set
                        // 死鏈;
                        // http://fluidproject.org/blog/2008/01/09/getting-setting-and-removing-tabindex-values-with-javascript/
                        // 看看司徒正美的 :http://www.cnblogs.com/rubylouvre/archive/2009/12/07/1618182.html
                        /*
                         知識普及 : tabIndex在瀏覽器下都支持, tabIndex在W3C是屬于節點屬性又屬于固有屬性, 表單元素用的最多,
                         //DIV等這些塊節點在W3C下不能設置tabIndex, 但是所有瀏覽器廠商都實現了DIV的tabIndex;tabIndex如果有設置值得情況下,無論是通過固有屬性還是節點方式獲取,
                         值都能獲取到,如下 :
                         <div tabindex="2">第二個</div>
                         $("div[tabindex=2]")[0].tabIndex  ==>> 2
                         $("div[tabindex=2]")[0].getAttribute("tabIndex") ==>> "2"
                         //這東西記也感覺記不住;
                         但是沒有默認值得情況下, 標準瀏覽器通過節點屬性 獲取的值如果是DIV等元素 ==>> -1; 被設置了返回被設置的值; 是input這些元素 ==>> 0
                         如果是input這些元素 通過attribute獲取 ==>> null;
                         IE67無論任何方式獲取的都是返回0
                         //IE下判斷這屬性是否被設置
                         var _hasAttr = function(node, name){
                         var attr = node.getAttributeNode && node.getAttributeNode(name);
                         return attr && attr.specified; // Boolean
                         };
                         */
                        return attributeNode && attributeNode.specified ?
                            attributeNode.value :

                            //主要是處理各個瀏覽器返回值不同的的兼容問題;
                            //如果是可以聚焦的元素 或者是 擁有href的a 或者 area元素返回 null, 剩下的返回undefined;
                            //rfocusable = /(button|input|object|select|textarea)/i
                            //rclickable = /^(a|area)$/i,
                            rfocusable.test( elem.nodeName ) || rclickable.test( elem.nodeName ) && elem.href ?
                                null :
                                undefined;
                    };

                    return elem[ name ];
                };
                //node屬性完結, 輪到了html節點屬性了哇;

                //set先走,走完走get;
                //標準瀏覽器這個壓根就不走哇;
                //$.attr(document.body, "style", "height:100px;,background:#f00");
                //jQuery.support.style在chrome上是true, IE上測試是false;還是兼容測試哇;
                if ( !jQuery.support.style && notxml && name === "style" ) {
                    if ( set ) {
                        elem.style.cssText = "" + value;
                    }

                    return elem.style.cssText;
                }

                //還是set啊, 不過這個set是HTML節點的set;;
                if ( set ) {
                    // convert the value to a string (all browsers do this but IE) see #1070
                    elem.setAttribute( name, "" + value );
                }

                //沒定義按照標準的就返回undefined,bb的返回"";
                // Ensure that missing attributes return undefined
                // Blackberry 4.7 returns "" from getAttribute #6938
                if ( !elem.attributes[ name ] && (elem.hasAttribute && !elem.hasAttribute( name )) ) {
                    return undefined;
                }

                //最后對毛鏈接做一下兼容;
                //IE下會自動補全地址;, 所以要給第二個參數;
                var attr = !jQuery.support.hrefNormalized && notxml && special ?
                    // Some attributes require a special call on IE
                    elem.getAttribute( name, 2 ) :
                    elem.getAttribute( name );

                //返回get的值;
                // Non-existent attributes return null, we normalize to undefined
                return attr === null ? undefined : attr;
            };

            //如果不是dom節點的話,按照屬性設置直接設置值就好了;
            // Handle everything which isn't a DOM element node
            if ( set ) {
                elem[ name ] = value;
            }
            return elem[ name ];
        }
    });

    //千辛萬苦終于到了事件模塊, 這個模塊很重要哇 ;
    var rnamespaces = /\.(.*)$/,
        rformElems = /^(?:textarea|input|select)$/i,
        rperiod = /\./g,
        rspace = / /g,
        rescape = /[^\w\s.|`]/g,
        fcleanup = function( nm ) {
            return nm.replace(rescape, "\\$&");
        },
        eventKey = "events";
    /*
     知識點匹配需要轉義的字符;
     rescape = /[^\w\s.|`]/g,
     //為每一個需要轉義的字符添加\
     nm.replace(rescape, "\\$&");
     fcleanup("sdfsdfdsfds.s&**(((*)f\\")
     "sdfsdfdsfds.s\&\*\*\(\(\(\*\)f\\"
     */
    /*
     * A number of helper functions used for managing events.
     * Many of the ideas behind this code originated from
     * Dean Edwards' addEvent library.
     */
    jQuery.event = {
        // Bind an event to an element
        // Original by Dean Edwards
        //所有綁定事件都是通過add這個方法綁定的;
        //元素
        //click mouseover 正常情況下;

        //  如果是代理的情況下; add會綁定兩次,第一次是綁定live事件,第二個是綁定click事件; 第一個點以后的是要匹配的元素選擇器(要把 ^替換成.);
        //"live.click.div"
        // "live.click.`hehe"  ==>> click.`hehe;
        // live.click.div`hehe  ==>> "click.div`hehe";
        //data沒毛用;
        add: function( elem, types, handler, data ) {
            //debugger;
            //text節點和 comment節點全滾哇, 話說 屬性節點(nodeType === 2)的你可以綁定事件嗎? 是的,好像真的可以哇, 奇葩了;
            if ( elem.nodeType === 3 || elem.nodeType === 8 ) {
                return;
            };

            // For whatever reason, IE has trouble passing the window object
            // around, causing it to be cloned in the process
            // 跨iframe
            if ( jQuery.isWindow( elem ) && ( elem !== window && !elem.frameElement ) ) {
                elem = window;
            }

            //綁定一個空的事件
            if ( handler === false ) {
                handler = returnFalse;
            } else if ( !handler ) {
                // Fixes bug #7229. Fix recommended by jdalton
                return;
            };

            var handleObjIn, handleObj;

            //根據傳進來的handler是否有handler屬性,然后設置handlerObjIn事件描述和,事件觸發要執行的函數handlerObjIn.handler;
            if ( handler.handler ) {
                handleObjIn = handler;
                handler = handleObjIn.handler;
            };

            // Make sure that the function being executed has a unique ID
            //保證了唯一的事件, 后面可以根據這個唯一的id進行remove或者別的操作,比較方便;
            if ( !handler.guid ) {
                handler.guid = jQuery.guid++;
            };

            // Init the element's event structure
            //這個獲取的是內部私用的緩存保存的數據;
            var elemData = jQuery._data( elem );

            // If no elemData is found then we must be trying to bind to one of the
            // banned noData elements
            // 沒有就不玩啦, 為什么會沒有呢 ,因為noData的元素不能亂給事件哇, object[classId="/\d/mig"], applet, embed;
            // 對啊embed不能綁定事件,只能通過innerHTML綁定事件, 以前碰到過這種情況;
            if ( !elemData ) {
                return;
            };
            //eventKey = "events" 上面定義了這個鳥東西;
            var events = elemData[ eventKey ],
            //eventHandle是為這個元素綁定的事件
                eventHandle = elemData.handle;

            //正常的events應該是一個數組哇, 是function的情況應該特別對待;
            if ( typeof events === "function" ) {
                // On plain objects events is a fn that holds the the data
                // which prevents this data from being JSON serialized
                // the function does not need to be called, it just contains the data
                eventHandle = events.handle;
                events = events.events;

            } else if ( !events ) {
                //處理非節點元素的事件綁定, 這個應該是為了擴張綁定事件到非節點元素上面;
                if ( !elem.nodeType ) {
                    // On plain objects, create a fn that acts as the holder
                    // of the values to avoid JSON serialization of event data
                    elemData[ eventKey ] = elemData = function(){};
                };

                //新建一個事件保存列表;
                elemData.events = events = {};
            };

            //所有的事件都綁定同一個事件函數, 剩下的給event.handle處理不同的情況;
            //使用這種方式對用戶的來說, 可配置性變好了, 比如
            // 1 : 你可以讓事件按照順序執行(某些瀏覽器不按照順序來,因為事件執行時冒泡階段執行);
            // 2 : 沒想出來;
            if ( !eventHandle ) {
                elemData.handle = eventHandle = function() {
                    // Handle the second event of a trigger and when
                    // an event is called after a page has unloaded
                    //jQuery.event.triggered默認是false的;
                    return typeof jQuery !== "undefined" && !jQuery.event.triggered ?
                        jQuery.event.handle.apply( eventHandle.elem, arguments ) :
                        undefined;
                };
            };

            // Add elem as a property of the handle function
            // This is to prevent a memory leak with non-native events in IE.
            // 為事件函數添加元素的引用; 阻止ie下的內存泄漏;
            eventHandle.elem = elem;

            // Handle multiple events separated by a space
            // jQuery(...).bind("mouseover mouseout", fn);
            //開始了一大堆處理, 對綁定的事件進行c;
            types = types.split(" ");

            var type, i = 0, namespaces;

            while ( (type = types[ i++ ]) ) {
                //重新弄一個事件描述(引用);
                handleObj = handleObjIn ?
                    jQuery.extend({}, handleObjIn) :
                { handler: handler, data: data };

                // Namespaced event handlers
                // 修復時間的命名空間;
                // 目測現在事件代理被弄成 live  click^#div1^div的情況
                if ( type.indexOf(".") > -1 ) {
                    namespaces = type.split(".");
                    type = namespaces.shift();
                    handleObj.namespace = namespaces.slice(0).sort().join(".");
                } else {
                    namespaces = [];
                    handleObj.namespace = "";
                };

                //為事件描述添加事件類型
                handleObj.type = type;
                //為事件描述添加事件的guid, 這個handle是從bind那邊處理過的(處理了one,bind), 也可能從live那邊傳過來的;
                if ( !handleObj.guid ) {
                    handleObj.guid = handler.guid;
                }

                // Get the current list of functions bound to this event
                // 創建或者獲取事件的隊列;
                var handlers = events[ type ],
                    special = jQuery.event.special[ type ] || {};

                // Init the event handler queue
                if ( !handlers ) {
                    handlers = events[ type ] = [];

                    // Check for a special event handler
                    // Only use addEventListener/attachEvent if the special
                    // events handler returns false
                    // 如果綁定的是beforeunload,就特殊對待,
                    // //如果綁定focusin或者foucuseout就轉化成使用fouse和blur,
                    // live或者是
                    // 是ready就綁定到document.ready
                    // 如果是mouseenter或者是mouseleave,就使用mouseout和mousein模擬;
                    // live只有add和remove,所以這個setup肯定不走, 直接走addEVentListener的綁定;
                    if ( !special.setup || special.setup.call( elem, data, namespaces, eventHandle ) === false ) {
                        // Bind the global event handler to the element
                        //如果是live就是綁定了自定義事件, 觸發的時候要注意一下;
                        if ( elem.addEventListener ) {
                            elem.addEventListener( type, eventHandle, false );

                        } else if ( elem.attachEvent ) {
                            elem.attachEvent( "on" + type, eventHandle );
                        }
                    }
                }

                //使用事件代理的時候的卻有點繞, jQ高版本的話對事件代理進行了優化;;

                //live的時候,這里又綁定了一次哦,只有live有add和remove;
                if ( special.add ) {
                    //第一次add的是live的handlers,第二次add的才是真正的handlers;
                    //調用special的綁定方式進行綁定;
                    //這個綁定有重新迭代了一次$.event.add...所以要注意一下, 這一次的迭代才是真正綁定需要的事件
                    special.add.call( elem, handleObj );

                    if ( !handleObj.handler.guid ) {
                        handleObj.handler.guid = handler.guid;
                    };
                };
                //綁定事件的事件函數要做不同處理, 但是綁定的事件描述還是要根據事件的類型放到handlers里面去;

                //所有的處理都是為了把handleObj放到handlers這個對象里面;
                // Add the function to the element's handler list
                handlers.push( handleObj );

                //優化;
                // Keep track of which events have been used, for global triggering
                jQuery.event.global[ type ] = true;

                /*handle結構是這樣的
                 $.cache = {
                 Number ElementGuid : {
                 string jQueryExpando : {
                 events : {
                 "click" : [function(){}, function(){}, function(){}, function(){}]
                 },
                 handle : function(){....}
                 }
                 }
                 }
                 */
            };

            // Nullify elem to prevent memory leaks in IE
            elem = null;
        },

        global: {},

        // Detach an event or set of events from an element
        //刪除事件目測應該和綁定差不多道理;
        remove: function( elem, types, handler, pos ) {
            // don't do events on text and comment nodes
            // 依舊可以把事件綁定給屬性節點;
            if ( elem.nodeType === 3 || elem.nodeType === 8 ) {
                return;
            };

            if ( handler === false ) {
                handler = returnFalse;
            };

            //
            var ret, type, fn, j, i = 0, all, namespaces, namespace, special, eventType, handleObj, origType,
                elemData = jQuery.hasData( elem ) && jQuery._data( elem ),
                events = elemData && elemData[ eventKey ];

            //沒有事件列表還玩個毛;
            if ( !elemData || !events ) {
                return;
            };

            //這個和$.event.add一樣的,
            if ( typeof events === "function" ) {
                elemData = events;
                events = events.events;
            };

            //$.event.remove({type : "click",handler : clickFn});
            // types is actually an event object here
            if ( types && types.type ) {
                handler = types.handler;
                types = types.type;
            };

            // Unbind all events for the element
            // 沒有types的話, 就是移除所有的事件;
            //類型是命名空間的話
            if ( !types || typeof types === "string" && types.charAt(0) === "." ) {
                types = types || "";

                //迭代命名空間的事件,一個個刪除;
                for ( type in events ) {
                    jQuery.event.remove( elem, type + types );
                };
                //下面沒必要在走了;
                return;
            };

            // Handle multiple events separated by a space
            // jQuery(...).unbind("mouseover mouseout", fn);
            types = types.split(" ");

            while ( (type = types[ i++ ]) ) {
                origType = type;
                handleObj = null;
                all = type.indexOf(".") < 0;
                namespaces = [];

                //all 指是或否是這個事件的全部命名空間都要刪除;
                if ( !all ) {
                    // Namespaced event handlers
                    namespaces = type.split(".");
                    type = namespaces.shift();

                    namespace = new RegExp("(^|\\.)" +
                        jQuery.map( namespaces.slice(0).sort(), fcleanup ).join("\\.(?:.*\\.)?") + "(\\.|$)");
                };

                eventType = events[ type ];

                if ( !eventType ) {
                    continue;
                };

                if ( !handler ) {
                    for ( j = 0; j < eventType.length; j++ ) {
                        handleObj = eventType[ j ];
                        //對這個事件描述對象進行判斷, 如果匹配到了這個命名空間就把這個時間刪了;;
                        if ( all || namespace.test( handleObj.namespace ) ) {
                            jQuery.event.remove( elem, origType, handleObj.handler, j );
                            eventType.splice( j--, 1 );
                        }
                    }

                    continue;
                }

                special = jQuery.event.special[ type ] || {};

                for ( j = pos || 0; j < eventType.length; j++ ) {
                    handleObj = eventType[ j ];
                    //用戶也可以傳綁定的函數進來, 如果guid一樣就刪;
                    if ( handler.guid === handleObj.guid ) {
                        // remove the given handler for the given type
                        if ( all || namespace.test( handleObj.namespace ) ) {
                            if ( pos == null ) {
                                eventType.splice( j--, 1 );
                            };

                            //有remove的只有live有了;
                            if ( special.remove ) {
                                special.remove.call( elem, handleObj );
                            };
                        }

                        if ( pos != null ) {
                            break;
                        }
                    }
                }

                //如果某個事件的 事件列表刪除完了, 就把這個events【type】清空;
                // remove generic event handler if no more handlers exist
                if ( eventType.length === 0 || pos != null && eventType.length === 1 ) {
                    //mousein mouseout focusin fousout 對走這個;
                    if ( !special.teardown || special.teardown.call( elem, namespaces ) === false ) {
                        jQuery.removeEvent( elem, type, elemData.handle );
                    };

                    ret = null;
                    delete events[ type ];
                };
            };

            // Remove the expando if it's no longer used
            if ( jQuery.isEmptyObject( events ) ) {
                var handle = elemData.handle;
                if ( handle ) {
                    handle.elem = null;
                }

                delete elemData.events;
                delete elemData.handle;

                if ( typeof elemData === "function" ) {
                    jQuery.removeData( elem, eventKey, true );

                } else if ( jQuery.isEmptyObject( elemData ) ) {
                    jQuery.removeData( elem, undefined, true );
                }
            }
        },

        //trigger是給用戶用的;
        // bubbling is internal
        trigger: function( event, data, elem /*, bubbling */ ) {
            // Event object or event type
            var type = event.type || event,
                bubbling = arguments[3];

            //默認都是冒泡;
            if ( !bubbling ) {
                //自己新建一個event對象;
                event = typeof event === "object" ?
                    // jQuery.Event object
                    event[ jQuery.expando ] ? event :
                        // Object literal
                        jQuery.extend( jQuery.Event(type), event ) :
                    // Just the event type (string)
                    jQuery.Event(type);

                // 有!的代表觸發的是自定義的屬性更改事件, 對用戶來說,作用不多,有點像IE的onpropertychange;
                if ( type.indexOf("!") >= 0 ) {
                    event.type = type = type.slice(0, -1);
                    event.exclusive = true;
                };

                // Handle a global
                if ( !elem ) {
                    // 如果你要執行對應type全部事件,那么就要阻止默認事件
                    // 如果你不阻止冒泡的話會
                    // Don't bubble custom events when global (to avoid too much overhead)
                    // 這個event是假的,模擬出來的東東;
                    event.stopPropagation();

                    // Only trigger if we've ever bound an event for it
                    if ( jQuery.event.global[ type ] ) {
                        // XXX This code smells terrible. event.js should not be directly
                        // inspecting the data cache
                        jQuery.each( jQuery.cache, function() {
                            // internalKey variable is just used to make it easier to find
                            // and potentially change this stuff later; currently it just
                            // points to jQuery.expando
                            var internalKey = jQuery.expando,
                                internalCache = this[ internalKey ];
                            if ( internalCache && internalCache.events && internalCache.events[type] ) {
                                jQuery.event.trigger( event, data, internalCache.handle.elem );
                            }
                        });
                        //我不知道這里為什么不return掉;
                    }
                }

                // Handle triggering a single element

                // don't do events on text and comment nodes
                if ( !elem || elem.nodeType === 3 || elem.nodeType === 8 ) {
                    return undefined;
                }

                // Clean up in case it is reused
                event.result = undefined;
                event.target = elem;

                // Clone the incoming data, if any
                data = jQuery.makeArray( data );
                data.unshift( event );
            }

            //
            event.currentTarget = elem;

            // Trigger the event, it is assumed that "handle" is a function
            var handle = elem.nodeType ?
                jQuery._data( elem, "handle" ) :
                (jQuery._data( elem, eventKey ) || {}).handle;

            //這個就是手動觸發事件了哇, data里面是有新建的event對象的;
            if ( handle ) {
                handle.apply( elem, data );
            };

            var parent = elem.parentNode || elem.ownerDocument;

            //手動觸發行內綁定的事件;
            // Trigger an inline bound script
            try {
                if ( !(elem && elem.nodeName && jQuery.noData[elem.nodeName.toLowerCase()]) ) {
                    if ( elem[ "on" + type ] && elem[ "on" + type ].apply( elem, data ) === false ) {
                        event.result = false;
                        event.preventDefault();
                    }
                }

                // prevent IE from throwing an error for some elements with some event types, see #3533
            } catch (inlineError) {}

            //我靠這個,又手動觸發了父級的對應事件,就是事件冒泡了 ,(jQ為什么考慮這么全面);
            if ( !event.isPropagationStopped() && parent ) {
                jQuery.event.trigger( event, data, parent, true );

                //默認事件沒有被阻止的話;
            } else if ( !event.isDefaultPrevented() ) {
                var old,
                    target = event.target,
                    targetType = type.replace( rnamespaces, "" ),
                    isClick = jQuery.nodeName( target, "a" ) && targetType === "click",
                    special = jQuery.event.special[ targetType ] || {};

                if ( (!special._default || special._default.call( elem, event ) === false) &&
                    !isClick && !(target && target.nodeName && jQuery.noData[target.nodeName.toLowerCase()]) ) {

                    try {
                        if ( target[ targetType ] ) {
                            // Make sure that we don't accidentally re-trigger the onFOO events
                            old = target[ "on" + targetType ];

                            if ( old ) {
                                target[ "on" + targetType ] = null;
                            }

                            jQuery.event.triggered = true;
                            target[ targetType ]();
                        }

                        // prevent IE from throwing an error for some elements with some event types, see #3533
                    } catch (triggerError) {}

                    if ( old ) {
                        target[ "on" + targetType ] = old;
                    }

                    jQuery.event.triggered = false;
                }
            }
        },

        //所綁定的事件, 這個方法的event是最初的事件對象;
        handle: function( event ) {
            var all, handlers, namespaces, namespace_re, events,
                namespace_sort = [],
            //除了event瀏覽器的調用這個事件以外, 用戶也可以模擬一個假的event,假的eventType等等,手動觸發哦;
                args = jQuery.makeArray( arguments );
            //對事件的event進行瀏覽器兼容統一處理
            event = args[0] = jQuery.event.fix( event || window.event );
            // currentTarget指的是綁定事件的元素;,
            // 如果是代理綁定的話, 那么事件函數里面的this不是綁定的元素, 用戶如果有需要的話通過currentTarget引用即可;
            event.currentTarget = this;

            // Namespaced event handlers
            //如果沒有命名空間的話就是true;
            /*比如 你通過
             $("body").bind("click.nameSpace0",function(){console.log(1)}) 綁定了事件,
             $("body").bind("click.nameSpace1",function(){console.log(1)})
             當你左鍵點擊body元素的時候 這兩個綁定的事件都會觸發;
             但是你想手動觸發nameSpace0這個事件的話,你可以直接trigger("click.nameSpace0");
             事件的命名空間要看你怎么用了, 不用也沒大問題, 主要是解耦了各個事件函數;
             */
            all = event.type.indexOf(".") < 0 && !event.exclusive;

            //這個也只有用戶手動觸發的時候會走;
            if ( !all ) {
                namespaces = event.type.split(".");
                //事件名和事件命名空間拿出來;
                event.type = namespaces.shift();
                namespace_sort = namespaces.slice(0).sort();
                //開頭或者是一個dot;
                //結尾或者是一個dot;
                namespace_re = new RegExp("(^|\\.)" + namespace_sort.join("\\.(?:.*\\.)?") + "(\\.|$)");
            };

            //別忘記了命名空間和liveHanler是不一樣的,別犯迷糊, 一個是事件的分類,一個是在父級上面綁定事件;
            event.namespace = event.namespace || namespace_sort.join(".");

            //獲取所有事件
            // eventKey === "events";
            events = jQuery._data(this, eventKey);

            //默認的events不會是function的, 這個是什么情況,好像是jQuery.special.type[ name ]那里面的事件;
            if ( typeof events === "function" ) {
                events = events.events;
            };

            //handler不是函數哦, 是所有有關這個事件的事件描述
            //標準的事件描述對象 應該是這樣的:{data : guid : handle :function(){}, name : "xx", type : "click"}
            //獲取對應的事件 比如 click 還是 mouseoout這樣的情況;
            handlers = (events || {})[ event.type ];

            //如果沒有綁定對應的事件就不走里面, 作用1:優化, 2:避免里面報錯;
            if ( events && handlers ) {
                // Clone the handlers to prevent manipulation
                //復制一個事件描述對象;
                handlers = handlers.slice(0);

                //迭代所有的事件;
                for ( var j = 0, l = handlers.length; j < l; j++ ) {
                    var handleObj = handlers[ j ];

                    // Filter the functions by class
                    if ( all || namespace_re.test( handleObj.namespace ) ) {
                        // Pass in a reference to the handler function itself
                        // So that we can later remove it
                        //事件
                        event.handler = handleObj.handler;
                        //事件的數據;為事件添加data這個這么重要嗎,還是我不會用;
                        event.data = handleObj.data;
                        //把handleObj事件描述對象掛到event事件對象上面
                        event.handleObj = handleObj;
                        //現在事件里面的事件對象就有了handleObj事件描述對象這東西了;
                        //執行事件;
                        //默認的就一個event, 如果不是默認的就會把所有的參數重新傳進去;
                        //利用這一點,我們可以把自己定義個發布者和訂閱者,而且參數自己填(event都是要的哇)
                        var ret = handleObj.handler.apply( this, args );

                        //進行而外的處理
                        if ( ret !== undefined ) {
                            //把數據保存到event.result, 下次執行的話,可以調用event.result獲取上次事件保存的值, 有用,HOW?
                            event.result = ret;
                            //對return false進行特殊的處理;
                            if ( ret === false ) {
                                event.preventDefault();
                                event.stopPropagation();
                            };
                        };

                        //不執行了哇, 跳出這個循環了;
                        if ( event.isImmediatePropagationStopped() ) {
                            break;
                        }
                    }
                }
            }

            return event.result;
        },

        props: "altKey attrChange attrName bubbles button cancelable charCode clientX clientY ctrlKey currentTarget data detail eventPhase fromElement handler keyCode layerX layerY metaKey newValue offsetX offsetY pageX pageY prevValue relatedNode relatedTarget screenX screenY shiftKey srcElement target toElement view wheelDelta which".split(" "),

        //復制一個事件對象, 統一事件對象的兼容;
        fix: function( event ) {
            //如果已經經過jQ處理過的事件對象;
            if ( event[ jQuery.expando ] ) {
                return event;
            }

            // store a copy of the original event object
            // and "clone" to set read-only properties
            var originalEvent = event;
            event = jQuery.Event( originalEvent );

            //根據原始的對象復制一個假的事件對象, 要復制的屬性分別是:
            //altKey attrChange attrName bubbles button cancelable charCode clientX clientY ctrlKey currentTarget data detail eventPhase fromElement handler keyCode layerX layerY metaKey newValue offsetX offsetY pageX pageY prevValue relatedNode relatedTarget screenX screenY shiftKey srcElement target toElement view wheelDelta which;
            for ( var i = this.props.length, prop; i; ) {
                prop = this.props[ --i ];
                event[ prop ] = originalEvent[ prop ];
            };

            // Fix target property, if necessary
            if ( !event.target ) {
                //火狐下是srcElement ,chorme和ie都是target;
                // Fixes #1925 where srcElement might not be defined either
                event.target = event.srcElement || document;
            };

            // check if target is a textnode (safari)
            if ( event.target.nodeType === 3 ) {
                event.target = event.target.parentNode;
            }

            // Add relatedTarget, if necessary
            //修復IE下沒有relateTarget但是有fromeElement和toElement;
            if ( !event.relatedTarget && event.fromElement ) {
                event.relatedTarget = event.fromElement === event.target ? event.toElement : event.fromElement;
            };

            //這個也是IE的問題,沒有pageX和pageY;,根據clientX或者clientY加上界面滾動值在減去IE678下(Body,或者HTML標簽上)的2px問題;
            // Calculate pageX/Y if missing and clientX/Y available
            if ( event.pageX == null && event.clientX != null ) {
                var doc = document.documentElement,
                    body = document.body;

                event.pageX = event.clientX + (doc && doc.scrollLeft || body && body.scrollLeft || 0) - (doc && doc.clientLeft || body && body.clientLeft || 0);
                event.pageY = event.clientY + (doc && doc.scrollTop  || body && body.scrollTop  || 0) - (doc && doc.clientTop  || body && body.clientTop  || 0);
            };

            //DOM3規定使用whitch, 不用charCode也不用keyCode;
            // Add which for key events
            if ( event.which == null && (event.charCode != null || event.keyCode != null) ) {
                event.which = event.charCode != null ? event.charCode : event.keyCode;
            };

            //蘋果系統的META鍵就是window中的CTRL鍵;
            // Add metaKey to non-Mac browsers (use ctrl for PC's and Meta for Macs)
            if ( !event.metaKey && event.ctrlKey ) {
                event.metaKey = event.ctrlKey;
            }
            //剛剛測試過了,筆記本上面的fn鍵即使按住了,事件對象 并沒有 按住fn鍵的屬性 顯示;
            // Add which for click: 1 === left; 2 === middle; 3 === right
            // Note: button is not normalized, so don't use it
            // 保證了當前不是通過鍵盤的事件;
            //保證了是button這個鍵存在值;
            if ( !event.which && event.button !== undefined ) {
                event.which = (event.button & 1 ? 1 : ( event.button & 2 ? 3 : ( event.button & 4 ? 2 : 0 ) ));
            };

            return event;
        },

        // Deprecated, use jQuery.guid instead
        guid: 1E8,

        // Deprecated, use jQuery.proxy instead
        proxy: jQuery.proxy,

        //這幾個事件綁定的時候要特殊對待, 移除綁定也要特殊對待;
        special: {

            //當頁面加載完畢以后要初始化的幾個方法;
            ready: {
                // Make sure the ready event is setup
                setup: jQuery.bindReady,
                teardown: jQuery.noop
            },

            live: {
                //事件代理是根據事件描述handleObj對象 , 重新綁定事件, 不過handle是liveHandler,這個很重要;
                add: function( handleObj ) {
                    //這個就調用了add;
                    jQuery.event.add( this,
                        // click.^div^#div1^klass
                        liveConvert( handleObj.origType, handleObj.selector ),
                        //使用liveHandler作為事件對象;
                        jQuery.extend({}, handleObj, {handler: liveHandler, guid: handleObj.handler.guid}) );
                },

                remove: function( handleObj ) {
                    jQuery.event.remove( this, liveConvert( handleObj.origType, handleObj.selector ), handleObj );
                }
            },

            beforeunload: {
                setup: function( data, namespaces, eventHandle ) {
                    // We only want to do this special case on windows
                    if ( jQuery.isWindow( this ) ) {
                        this.onbeforeunload = eventHandle;
                    }
                },

                teardown: function( namespaces, eventHandle ) {
                    if ( this.onbeforeunload === eventHandle ) {
                        this.onbeforeunload = null;
                    }
                }
            }
        }
    };

    //第一次運行就把正確的函數賦值給對象屬性;
    jQuery.removeEvent = document.removeEventListener ?
        function( elem, type, handle ) {
            if ( elem.removeEventListener ) {
                elem.removeEventListener( type, handle, false );
            }
        } :
        function( elem, type, handle ) {
            if ( elem.detachEvent ) {
                elem.detachEvent( "on" + type, handle );
            }
        };

    //事件對象的兼容;
    jQuery.Event = function( src ) {
        // Allow instantiation without the 'new' keyword
        // if !(this instanceof jQuery.Event) 也行;
        if ( !this.preventDefault ) {
            return new jQuery.Event( src );
        }

        // Event object
        // 一般來說src是對象的話,應該是系統提供的事件對象;
        if ( src && src.type ) {
            this.originalEvent = src;
            this.type = src.type;

            // Events bubbling up the document may have been marked as prevented
            // by a handler lower down the tree; reflect the correct value.
            this.isDefaultPrevented = (src.defaultPrevented || src.returnValue === false ||
                src.getPreventDefault && src.getPreventDefault()) ? returnTrue : returnFalse;

            // Event type
        } else {
            this.type = src;
        }

        // timeStamp is buggy for some events on Firefox(#3843)
        // So we won't rely on the native value
        this.timeStamp = jQuery.now();

        // Mark it as fixed
        this[ jQuery.expando ] = true;
    };

    function returnFalse() {
        return false;
    }
    function returnTrue() {
        return true;
    }

// jQuery.Event is based on DOM3 Events as specified by the ECMAScript Language Binding
// http://www.w3.org/TR/2003/WD-DOM-Level-3-Events-20030331/ecma-script-binding.html
    jQuery.Event.prototype = {
        preventDefault: function() {
            this.isDefaultPrevented = returnTrue;

            var e = this.originalEvent;
            if ( !e ) {
                return;
            }

            // if preventDefault exists run it on the original event
            if ( e.preventDefault ) {
                e.preventDefault();

                // otherwise set the returnValue property of the original event to false (IE)
            } else {
                e.returnValue = false;
            }
        },
        stopPropagation: function() {
            this.isPropagationStopped = returnTrue;

            var e = this.originalEvent;
            if ( !e ) {
                return;
            }
            // if stopPropagation exists run it on the original event
            if ( e.stopPropagation ) {
                e.stopPropagation();
            }
            // otherwise set the cancelBubble property of the original event to true (IE)
            e.cancelBubble = true;
        },
        stopImmediatePropagation: function() {
            this.isImmediatePropagationStopped = returnTrue;
            this.stopPropagation();
        },
        isDefaultPrevented: returnFalse,
        isPropagationStopped: returnFalse,
        isImmediatePropagationStopped: returnFalse
    };

    //模擬mouseenter和mouseleave;
// Checks if an event happened on an element within another element
// Used in jQuery.event.special.mouseenter and mouseleave handlers
    var withinElement = function( event ) {
            // Check if mouse(over|out) are still within the same parent element
            var parent = event.relatedTarget;

            // FiwithinElementrefox sometimes assigns relatedTarget a XUL element
            // which we cannot access the parentNode property of
            try {
                // Traverse up the tree
                while ( parent && parent !== this ) {
                    parent = parent.parentNode;
                };

                if ( parent !== this ) {
                    // set the correct event type
                    event.type = event.data;

                    // handle event if we actually just moused on to a non sub-element
                    jQuery.event.handle.apply( this, arguments );
                }

                // assuming we've left the element since we most likely mousedover a xul element
            } catch(e) { }
        },

// In case of event delegation, we only need to rename the event.type,
// liveHandler will take care of the rest.
        delegate = function( event ) {
            event.type = event.data;
            jQuery.event.handle.apply( this, arguments );
        };

// Create mouseenter and mouseleave events
    jQuery.each({
        mouseenter: "mouseover",
        mouseleave: "mouseout"
    }, function( orig, fix ) {
        jQuery.event.special[ orig ] = {
            //setup就是綁定事件
            setup: function( data ) {
                jQuery.event.add( this, fix, data && data.selector ? delegate : withinElement, orig );
            },
            //teardown就是取消事件
            teardown: function( data ) {
                jQuery.event.remove( this, fix, data && data.selector ? delegate : withinElement );
            }
        };
    });

// submit delegation
    if ( !jQuery.support.submitBubbles ) {

        jQuery.event.special.submit = {
            //綁定事件
            setup: function( data, namespaces ) {
                if ( this.nodeName && this.nodeName.toLowerCase() !== "form" ) {
                    jQuery.event.add(this, "click.specialSubmit", function( e ) {
                        var elem = e.target,
                            type = elem.type;

                        if ( (type === "submit" || type === "image") && jQuery( elem ).closest("form").length ) {
                            e.liveFired = undefined;
                            return trigger( "submit", this, arguments );
                        }
                    });

                    jQuery.event.add(this, "keypress.specialSubmit", function( e ) {
                        var elem = e.target,
                            type = elem.type;

                        if ( (type === "text" || type === "password") && jQuery( elem ).closest("form").length && e.keyCode === 13 ) {
                            e.liveFired = undefined;
                            return trigger( "submit", this, arguments );
                        }
                    });

                } else {
                    return false;
                }
            },

            //取消事件;
            teardown: function( namespaces ) {
                jQuery.event.remove( this, ".specialSubmit" );
            }
        };

    }
// setup和teardown這些東西自己添加擴展事件, 就是閉包閉包又是閉包,一層一層一層又是一層;
// change delegation, happens here so we have bind.
    if ( !jQuery.support.changeBubbles ) {

        var changeFilters,

            getVal = function( elem ) {
                var type = elem.type, val = elem.value;

                if ( type === "radio" || type === "checkbox" ) {
                    val = elem.checked;

                } else if ( type === "select-multiple" ) {
                    val = elem.selectedIndex > -1 ?
                        jQuery.map( elem.options, function( elem ) {
                            return elem.selected;
                        }).join("-") :
                        "";

                } else if ( elem.nodeName.toLowerCase() === "select" ) {
                    val = elem.selectedIndex;
                }

                return val;
            },

            testChange = function testChange( e ) {
                var elem = e.target, data, val;

                if ( !rformElems.test( elem.nodeName ) || elem.readOnly ) {
                    return;
                }

                data = jQuery._data( elem, "_change_data" );
                val = getVal(elem);

                // the current data will be also retrieved by beforeactivate
                if ( e.type !== "focusout" || elem.type !== "radio" ) {
                    jQuery._data( elem, "_change_data", val );
                }

                if ( data === undefined || val === data ) {
                    return;
                }

                if ( data != null || val ) {
                    e.type = "change";
                    e.liveFired = undefined;
                    return jQuery.event.trigger( e, arguments[1], elem );
                }
            };

        jQuery.event.special.change = {
            filters: {
                focusout: testChange,

                beforedeactivate: testChange,

                click: function( e ) {
                    var elem = e.target, type = elem.type;

                    if ( type === "radio" || type === "checkbox" || elem.nodeName.toLowerCase() === "select" ) {
                        return testChange.call( this, e );
                    }
                },

                // Change has to be called before submit
                // Keydown will be called before keypress, which is used in submit-event delegation
                keydown: function( e ) {
                    var elem = e.target, type = elem.type;

                    if ( (e.keyCode === 13 && elem.nodeName.toLowerCase() !== "textarea") ||
                        (e.keyCode === 32 && (type === "checkbox" || type === "radio")) ||
                        type === "select-multiple" ) {
                        return testChange.call( this, e );
                    }
                },

                // Beforeactivate happens also before the previous element is blurred
                // with this event you can't trigger a change event, but you can store
                // information
                beforeactivate: function( e ) {
                    var elem = e.target;
                    jQuery._data( elem, "_change_data", getVal(elem) );
                }
            },

            //綁定事件
            setup: function( data, namespaces ) {
                if ( this.type === "file" ) {
                    return false;
                }

                for ( var type in changeFilters ) {
                    jQuery.event.add( this, type + ".specialChange", changeFilters[type] );
                }

                return rformElems.test( this.nodeName );
            },

            //取消事件
            teardown: function( namespaces ) {
                jQuery.event.remove( this, ".specialChange" );

                return rformElems.test( this.nodeName );
            }
        };

        changeFilters = jQuery.event.special.change.filters;

        // Handle when the input is .focus()'d
        changeFilters.focus = changeFilters.beforeactivate;
    }

    function trigger( type, elem, args ) {
        args[0].type = type;
        return jQuery.event.handle.apply( elem, args );
    }

    //修復瀏覽器fousein和fouseout支持;
// Create "bubbling" focus and blur events
    if ( document.addEventListener ) {
        jQuery.each({ focus: "focusin", blur: "focusout" }, function( orig, fix ) {
            jQuery.event.special[ fix ] = {
                setup: function() {
                    this.addEventListener( orig, handler, true );
                },
                teardown: function() {
                    this.removeEventListener( orig, handler, true );
                }
            };

            function handler( e ) {
                e = jQuery.event.fix( e );
                e.type = fix;
                return jQuery.event.handle.call( this, e );
            }
        });
    }

    //這個是繼承到實例原型上面的代碼;
    //利用閉包實現了bind和 one;  減少代碼量;
    jQuery.each(["bind", "one"], function( i, name ) {
        jQuery.fn[ name ] = function( type, data, fn ) {
            // Handle object literals
            //處理傳進來的是對象的情況, 調用對應的方法; 一接口的多種實用方法;
            if ( typeof type === "object" ) {
                for ( var key in type ) {
                    this[ name ](key, data, type[key], fn);
                };
                return this;
            };

            //修正參數 data , fn;
            //data為什么要===false $("div").bind("click",false,function() {});怎么辦哇;
            if ( jQuery.isFunction( data ) || data === false ) {
                fn = data;
                data = undefined;
            };

            //初始化綁定的函數
            //如果是one的話就是重新定義事件函數, 這個事件函數也是一個閉包, 引用了fn, 需要強調的是 $.proxy的作用是設置匿名事件函數的guid和fn一樣;;
            var handler = name === "one" ? jQuery.proxy( fn, function( event ) {
                jQuery( this ).unbind( event, handler );
                return fn.apply( this, arguments );
            }) : fn;

            //對unload的事件進行優化, 本身unload的事件就是不能一直掛在元素上面的;
            if ( type === "unload" && name !== "one" ) {
                this.one( type, data, fn );
            } else {
                for ( var i = 0, l = this.length; i < l; i++ ) {
                    //調用工具,  現在的參數一定是對的,  里面就不用擔心用戶亂傳參數進來了;
                    jQuery.event.add( this[i], type, handler, data );
                }
            }

            return this;
        };
    });

    //
    jQuery.fn.extend({
        unbind: function( type, fn ) {
            // Handle object literals
            // 這個和unbind做一樣的處理;
            if ( typeof type === "object" && !type.preventDefault ) {
                for ( var key in type ) {
                    this.unbind(key, type[key]);
                };

            } else {
                for ( var i = 0, l = this.length; i < l; i++ ) {
                    //unbind;
                    jQuery.event.remove( this[i], type, fn );
                }
            }

            return this;
        },

        //delegate也是調用live哇;
        delegate: function( selector, types, data, fn ) {
            return this.live( types, data, fn, selector );
        },

        //unbind是調用die,1.6以后的版本好像沒有live和die了;
        undelegate: function( selector, types, fn ) {
            if ( arguments.length === 0 ) {
                return this.unbind( "live" );
            } else {
                return this.die( types, null, fn, selector );
            }
        },

        trigger: function( type, data ) {
            return this.each(function() {
                jQuery.event.trigger( type, data, this );
            });
        },

        //trigger 和triggerHandler的區別是 后者 觸發了當前的第一個元素的對應事件, 而且阻止了默認操作和冒泡;
        triggerHandler: function( type, data ) {
            if ( this[0] ) {
                var event = jQuery.Event( type );
                event.preventDefault();
                event.stopPropagation();
                jQuery.event.trigger( event, data, this[0] );
                //事件對象有一個result, 說明 迭代執行事件的時候的返回值被保存到了event.result去;
                return event.result;
            };
        },

        //toggle和hover就是對click進行了封裝而已;
        toggle: function( fn ) {
            // Save reference to arguments for access in closure
            var args = arguments,
                i = 1;

            // link all the functions, so any of them can unbind this click handler
            // 把這幾個事件函數的guid設置成一樣的數字,保證了使用unbind的時候可以取消這個click事件;
            // i從第一個開始迭代到最后一個;
            while ( i < args.length ) {
                jQuery.proxy( fn, args[ i++ ] );
            };
            /*
             這個循環和for( var i=0; i<len ;i++); for( var i=0; i<len ;) {i++}這是一樣的;
             while ( i < args.length ) {
             jQuery.proxy( fn, args[ i ] );
             i++;
             };
             */
            //又用了一個閉包, 綁定這個了fn這個時間;
            return this.click( jQuery.proxy( fn, function( event ) {
                // Figure out which function to execute
                // i現在是總數;
                /*
                 0%4 ==>>  0
                 1%4 ==>>  1
                 2%4 ==>>  2
                 3%4 ==>>  3
                 4%4 ==>>  0

                 //內部用的_data
                 jQuery._data = function ( elem, name, data ) {
                 return jQuery.data( elem, name, data, true );
                 };
                 */
                var lastToggle = ( jQuery._data( this, "lastToggle" + fn.guid ) || 0 ) % i;
                jQuery._data( this, "lastToggle" + fn.guid, lastToggle + 1 );

                // Make sure that clicks stop
                //為什么要阻止默認事件哇;
                event.preventDefault();

                //執行
                // and execute the function
                return args[ lastToggle ].apply( this, arguments ) || false;
            }));
        },

        hover: function( fnOver, fnOut ) {
            return this.mouseenter( fnOver ).mouseleave( fnOut || fnOver );
        }
    });

    var liveMap = {
        focus: "focusin",
        blur: "focusout",
        mouseenter: "mouseover",
        mouseleave: "mouseout"
    };

    //兩個閉包, 減少代碼量;               //這里面的i沒有什么用, name才有用;
    jQuery.each(["live", "die"], function( i, name ) {
        jQuery.fn[ name ] = function( types, data, fn, origSelector /* Internal Use Only */ ) {
            var type, i = 0, match, namespaces, preType,
            //事件代理的text; 沒有的話傳進來默認為當前的元素的選擇符;
                selector = origSelector || this.selector,
            //如果有origSelector就是 當前的元素, 否則 document?, ,為context綁定事件;
                context = origSelector ? this : jQuery( this.context );

            //和bind unbind一樣,提供object的方式傳參; write less do more;
            if ( typeof types === "object" && !types.preventDefault ) {
                for ( var key in types ) {
                    context[ name ]( key, data, types[key], selector );
                };
                return this;
            };

            //處理參數, 實在不懂data有毛用哇;
            //$("body").live("click",function(){},"div");
            //$("body").live("click","",function(){},"div");
            if ( jQuery.isFunction( data ) ) {
                fn = data;
                data = undefined;
            };

            //支持多事件的情況; $("body").live("click mousein mouseout","",function(){},"div");
            types = (types || "").split(" ");

            while ( (type = types[ i++ ]) != null ) {
                // rnamespace = /\.(.*)$/;
                // 事件的命名空間, 如果你綁定了click事件,而且要區分click事件的類別分別不同情況觸發,就可以使用命名空間;
                match = rnamespaces.exec( type );
                namespaces = "";

                if ( match )  {
                    /*
                     /dfd/.exec("eadfdsdfe.sdfsdfe");
                     ["dfd"]
                     */
                    namespaces = match[0]; //命名空間
                    type = type.replace( rnamespaces, "" ); //類型
                };

                //系統沒有hover事件,把hover事件替換成mouseenter和mouseleave;
                if ( type === "hover" ) {
                    types.push( "mouseenter" + namespaces, "mouseleave" + namespaces );
                    continue;
                };

                preType = type;

                //為了讓事件冒泡吧,所以做了處理;
                /*
                 var liveMap = {
                 focus: "focusin",
                 blur: "focusout",
                 mouseenter: "mouseover",
                 mouseleave: "mouseout"
                 };
                 */
                if ( type === "focus" || type === "blur" ) {
                    types.push( liveMap[ type ] + namespaces );
                    type = type + namespaces;
                } else {
                    //這個不靠譜吧,mouseenter和mouseleave 就chrome的低版本不支持啊, 為什么要全部使用mouseover和mouseout進行模擬呢;
                    type = (liveMap[ type ] || type) + namespaces;
                };

                //現在還在閉包內部,所以要根據情況判斷是添加事件還是移除事件;
                if ( name === "live" ) {
                    // bind live handler
                    //context = origSelector ? this : jQuery( this.context );別忘記了context是this的引用或者其他對象的引用;
                    for ( var j = 0, l = context.length; j < l; j++ ) {
                        //要綁定的對象
                        //liveConvert("click",".class0 .wa")  ==>>  "click.`class0&`wa"
                        //liveConvert("click",".class0 .wa #div")  ==>>  "click.`class0&`wa&#div"
                        //內部自己約定了事件代理的描述;
                        jQuery.event.add( context[j], "live." + liveConvert( type, selector ),
                            //這個是事件的描述, 高版本的jQ把事件代理的描述和事件描述合并在一起了;
                            //preType指的是未對用戶傳進來事件名字進行處理的事件名字;
                            { data: data, selector: selector, handler: fn, origType: type, origHandler: fn, preType: preType } );
                    };
                } else {
                    // unbind live handler
                    //這個直接使用unbind 這樣好嗎 john....;
                    context.unbind( "live." + liveConvert( type, selector ), fn );
                };
            };
            return this;
        };
        //live的總結 : 事件的代理是在live處理的,使用了live綁定的元素綁定的事件默認是live開頭, 后面就是懂得自然懂了。 比如:live."click.`class0&`wa&#div"
    });

    //liveHandler也是挺簡單, 主流程是根據事件對象的 事件描述對象 匹配出符合命名空間的綁定函數, 然后讓他執行;
    function liveHandler( event ) {
        var stop, maxLevel, related, match, handleObj, elem, j, i, l, data, close, namespace, ret,
            elems = [],
            selectors = [],
            events = jQuery._data( this, eventKey );

        if ( typeof events === "function" ) {
            events = events.events;
        }

        // Make sure we avoid non-left-click bubbling in Firefox (#3861) and disabled elements in IE (#6911)
        if ( event.liveFired === this || !events || !events.live || event.target.disabled || event.button && event.type === "click" ) {
            return;
        }

        //匹配合適的命名空間哇;
        if ( event.namespace ) {
            namespace = new RegExp("(^|\\.)" + event.namespace.split(".").join("\\.(?:.*\\.)?") + "(\\.|$)");
        }

        event.liveFired = this;

        //復制一個事件描述對象的數組;
        var live = events.live.slice(0);

        for ( j = 0; j < live.length; j++ ) {
            handleObj = live[j];

            //命名空間符合event.type 就把這個函數保存起來;
            if ( handleObj.origType.replace( rnamespaces, "" ) === event.type ) {
                selectors.push( handleObj.selector );

            } else {
                live.splice( j--, 1 );
            }
        }

        //這個返回的是所有匹配的元素;
        match = jQuery( event.target ).closest( selectors, event.currentTarget );

        //這個是雙重循環,過濾合適的element
        for ( i = 0, l = match.length; i < l; i++ ) {
            close = match[i];

            for ( j = 0; j < live.length; j++ ) {
                handleObj = live[j];

                if ( close.selector === handleObj.selector && (!namespace || namespace.test( handleObj.namespace )) ) {
                    elem = close.elem;
                    related = null;

                    // Those two events require additional checking
                    if ( handleObj.preType === "mouseenter" || handleObj.preType === "mouseleave" ) {
                        event.type = handleObj.preType;
                        related = jQuery( event.relatedTarget ).closest( handleObj.selector )[0];
                    }

                    //把元素和事件對象保存起來;
                    if ( !related || related !== elem ) {
                        elems.push({ elem: elem, handleObj: handleObj, level: close.level });
                    }
                }
            }
        }

        //一個一個執行, event下的result屬性依然保存著上一個事件的返回值;
        for ( i = 0, l = elems.length; i < l; i++ ) {
            match = elems[i];

            if ( maxLevel && match.level > maxLevel ) {
                break;
            }

            event.currentTarget = match.elem;
            event.data = match.handleObj.data;
            event.handleObj = match.handleObj;

            ret = match.handleObj.origHandler.apply( match.elem, arguments );

            //這個和handle里面的代碼重復了, jQ高版本做了優化;
            if ( ret === false || event.isPropagationStopped() ) {
                maxLevel = match.level;

                if ( ret === false ) {
                    stop = false;
                }
                if ( event.isImmediatePropagationStopped() ) {
                    break;
                }
            }
        }

        return stop;
    };

    //提供給事件代理用的;
    function liveConvert( type, selector ) {
        return (type && type !== "*" ? type + "." : "") + selector.replace(rperiod, "`").replace(rspace, "&");
    };

    //shortcut, 提供實例方法上面快捷方式調用
    jQuery.each( ("blur focus focusin focusout load resize scroll unload click dblclick " +
        "mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave " +
        "change select submit keydown keypress keyup error").split(" "), function( i, name ) {

        // Handle event binding
        jQuery.fn[ name ] = function( data, fn ) {
            if ( fn == null ) {
                fn = data;
                data = null;
            }

            return arguments.length > 0 ?
                this.bind( name, data, fn ) :
                this.trigger( name );
        };

        if ( jQuery.attrFn ) {
            jQuery.attrFn[ name ] = true;
        }
    });


    
    var runtil = /Until$/,
        rparentsprev = /^(?:parents|prevUntil|prevAll)/,
    // Note: This RegExp should be improved, or likely pulled from Sizzle
        rmultiselector = /,/,
        isSimple = /^.[^:#\[\.,]*$/,
        slice = Array.prototype.slice,
    //POS這個應該是position的意思, 這個是直接引用Sizzle里面的方法;
        POS = jQuery.expr.match.POS,
    // methods guaranteed to produce a unique set when starting from a unique set
        guaranteedUnique = {
            children: true,
            contents: true,
            next: true,
            prev: true
        };

    jQuery.fn.extend({
        find: function( selector ) {
            var ret = this.pushStack( "", "find", selector ),
                length = 0;

            for ( var i = 0, l = this.length; i < l; i++ ) {
                length = ret.length;
                //可以猜出
                //selector : 用戶傳進來的選擇器字符串;
                //這個是上下文context
                //ret : 執行完畢會把結果push到ret里面去;
                jQuery.find( selector, this[i], ret );

                if ( i > 0 ) {
                    // Make sure that the results are unique
                    //雙層循環去重, 和underscore.unique一樣的;
                    for ( var n = length; n < ret.length; n++ ) {
                        for ( var r = 0; r < length; r++ ) {
                            if ( ret[r] === ret[n] ) {
                                ret.splice(n--, 1);
                                break;
                            }
                        }
                    }
                }
            }

            return ret;
        },

        //這個返回的不是布爾值哦, 不過你可以通過判斷返回值的length進行判斷;
        has: function( target ) {
            var targets = jQuery( target );
            //this.filter是對數組進行過濾
            return this.filter(function() {
                //迭代傳進來的選擇器, 如果this有包含這個選擇器就返回ture;
                for ( var i = 0, l = targets.length; i < l; i++ ) {
                    if ( jQuery.contains( this, targets[i] ) ) {
                        return true;
                    }
                }
            });
        },

        //not的返回值也是一個jQ實例,通過pushStaack方法把當前的實例保存到新實例的pre屬性;
        not: function( selector ) {
            return this.pushStack( winnow(this, selector, false), "not", selector);
        },

        //同上,這個是實例過濾, 要調用迭代要用grep方法 (知識點: jQinstance.filter 和 jQinstance.grep
        filter: function( selector ) {
            return this.pushStack( winnow(this, selector, true), "filter", selector );
        },

        // 這個返回的是布爾值;
        is: function( selector ) {
            return !!selector && jQuery.filter( selector, this ).length > 0;
        },

        closest: function( selectors, context ) {
            var ret = [], i, l, cur = this[0];

            //分了兩種情況, selector是array的情況下, seelctor不是字符串的情況下, 第一次聽所closet可以傳數組的情況,傳數組的情況用的不多;;
            if ( jQuery.isArray( selectors ) ) {
                var match, selector,
                    matches = {},
                    level = 1;

                if ( cur && selectors.length ) {
                    for ( i = 0, l = selectors.length; i < l; i++ ) {
                        selector = selectors[i];

                        if ( !matches[selector] ) {
                            matches[selector] = jQuery.expr.match.POS.test( selector ) ?
                                jQuery( selector, context || this.context ) :
                                selector;
                        }
                    }

                    while ( cur && cur.ownerDocument && cur !== context ) {
                        for ( selector in matches ) {
                            match = matches[selector];

                            if ( match.jquery ? match.index(cur) > -1 : jQuery(cur).is(match) ) {
                                ret.push({ selector: selector, elem: cur, level: level });
                            }
                        }

                        cur = cur.parentNode;
                        level++;
                    }
                }

                return ret;
            };

            //  /:(nth|eq|gt|lt|first|last|even|odd)(?:\((\d*)\))?(?=[^\-]|$)(?![^\[]*\])(?![^\(]*\))/
            var pos = POS.test( selectors ) ?
                jQuery( selectors, context || this.context ) : null;

            for ( i = 0, l = this.length; i < l; i++ ) {
                //又要遍歷一遍自己;
                cur = this[i];

                //高版本的瀏覽器都有內置的matchSelcotor方法,會更快的說;
                //在當前的元素一個一個往上跑,一個個查找, pos指的是這個選擇器的所有元素;
                while ( cur ) {
                    //一般都有pos出來的的元素;
                    if ( pos ? pos.index(cur) > -1 : jQuery.find.matchesSelector(cur, selectors) ) {
                        ret.push( cur );
                        break;
                    } else {
                        //如果沒找到就到父級,除非當前的cur到了指定的上下文或者document就跳出去,說明沒東西了嘛;
                        cur = cur.parentNode;
                        if ( !cur || !cur.ownerDocument || cur === context ) {
                            break;
                        }
                    }
                }
            };

            //通過工具方法取得唯一;
            ret = ret.length > 1 ? jQuery.unique(ret) : ret;

            return this.pushStack( ret, "closest", selectors );
        },

        // Determine the position of an element within
        // the matched set of elements
        index: function( elem ) {
            //
            if ( !elem || typeof elem === "string" ) {
                return jQuery.inArray( this[0],
                    // If it receives a string, the selector is used
                    // If it receives nothing, the siblings are used
                    //elem是string的情況
                    //沒有ele的情況,默認elem為當前元素父級的所有子元素;
                    elem ? jQuery( elem ) : this.parent().children() );
            }
            // Locate the position of the desired element
            return jQuery.inArray(
                // If it receives a jQuery object, the first element is used
                elem.jquery ? elem[0] : elem, this );
        },

        //話說die跑哪里去了哇;
        add: function( selector, context ) {
            var set = typeof selector === "string" ?
                    jQuery( selector, context ) :
                    jQuery.makeArray( selector ),
                all = jQuery.merge( this.get(), set );

            return this.pushStack( isDisconnected( set[0] ) || isDisconnected( all[0] ) ?
                all :
                jQuery.unique( all ) );
        },
        //還有addSelf這個鳥東西哇,
        andSelf: function() {
            return this.add( this.prevObject );
        }
    });

// A painfully simple check to see if an element is disconnected
// from a document (should be improved, where feasible).
    function isDisconnected( node ) {
        return !node || !node.parentNode || node.parentNode.nodeType === 11;
    };

    jQuery.each({
        parent: function( elem ) {
            var parent = elem.parentNode;
            return parent && parent.nodeType !== 11 ? parent : null;
        },
        parents: function( elem ) {
            // jQuery.dir 就是方向的意思, 這個元素的某一個方向上對應的元素;
            return jQuery.dir( elem, "parentNode" );
        },
        parentsUntil: function( elem, i, until ) {
            //utlil這個要在dir里面進行特別的對待;
            return jQuery.dir( elem, "parentNode", until );
        },
        next: function( elem ) {
            return jQuery.nth( elem, 2, "nextSibling" );
        },
        prev: function( elem ) {
            return jQuery.nth( elem, 2, "previousSibling" );
        },
        nextAll: function( elem ) {
            return jQuery.dir( elem, "nextSibling" );
        },
        prevAll: function( elem ) {
            return jQuery.dir( elem, "previousSibling" );
        },
        nextUntil: function( elem, i, until ) {
            return jQuery.dir( elem, "nextSibling", until );
        },
        prevUntil: function( elem, i, until ) {
            return jQuery.dir( elem, "previousSibling", until );
        },
        siblings: function( elem ) {
            return jQuery.sibling( elem.parentNode.firstChild, elem );
        },
        children: function( elem ) {
            return jQuery.sibling( elem.firstChild );
        },
        contents: function( elem ) {
            return jQuery.nodeName( elem, "iframe" ) ?
                //標準瀏覽器都有這個屬性
                //IE下進行特別的對待;
                elem.contentDocument || elem.contentWindow.document :
                // 1: 并不是沒有用pushStack ,pushStack在閉包里面被統一處理了,
                // 2: makeArray可以合并偽數組對象
                jQuery.makeArray( elem.childNodes );
        }
    }, function( name, fn ) {
        //這種寫法主要是為了減少代碼量;
        //每一個each都會生成一個閉包;
        jQuery.fn[ name ] = function( until, selector ) {
            var ret = jQuery.map( this, fn, until ),
            // The variable 'args' was introduced in
            // https://github.com/jquery/jquery/commit/52a0238
            // to work around a bug in Chrome 10 (Dev) and should be removed when the bug is fixed.
            // http://code.google.com/p/v8/issues/detail?id=1050
                args = slice.call(arguments);

            //有until的有三種,parentUtil, nextUntil和 preUtil;
            if ( !runtil.test( name ) ) {
                selector = until;
            };

            //又可以對傳進來的字符串進行適配;
            if ( selector && typeof selector === "string" ) {
                ret = jQuery.filter( selector, ret );
            };

            //又是取唯一;
            ret = this.length > 1 && !guaranteedUnique[ name ] ? jQuery.unique( ret ) : ret;

            //為什么要取反?
            if ( (this.length > 1 || rmultiselector.test( selector )) && rparentsprev.test( name ) ) {
                ret = ret.reverse();
            };

            //pushStack 在這里進行了統一處理;
            return this.pushStack( ret, name, args.join(",") );
        };
    });

    //這個是繼承到jQ的工具方法;
    jQuery.extend({
        filter: function( expr, elems, not ) {
            //特殊處理;
            if ( not ) {
                expr = ":not(" + expr + ")";
            }

            //優化了只有一個選中元素的情況, 全部;用了Sizzle的東東;
            return elems.length === 1 ?
                jQuery.find.matchesSelector(elems[0], expr) ? [ elems[0] ] : [] :
                jQuery.find.matches(expr, elems);
        },


        dir: function( elem, dir, until ) {
            var matched = [],
            //找到的元素;
                cur = elem[ dir ];

            //找到該方向上的所有元素,  有加上util的特殊判斷;
            //有當前元素, 可能nextSibling到了最后了, 所以沒有了cur
            //到了document節點哥也不玩了
            //沒有util就到頭
            //元素類型為節點元素
            while ( cur && cur.nodeType !== 9 && (until === undefined || cur.nodeType !== 1 || !jQuery( cur ).is( until )) ) {
                if ( cur.nodeType === 1 ) {
                    matched.push( cur );
                }
                cur = cur[dir];
            }
            return matched;
        },


        //nth相當于是index,不過nth是從第一個開始的,firstChilde是1,而且只返回一個,很簡單的接口, 不過這個循環臥槽...;
        //              開始的元素
        //第幾個開始
        //方向;
        //  $.nth( $("body").parent().get(0), null, document.documentElement);
        nth: function( cur, result, dir, elem ) {
            result = result || 1;
            var num = 0;

            for ( ; cur; cur = cur[dir] ) {
                if ( cur.nodeType === 1 && ++num === result ) {
                    break;
                }
            }

            return cur;
        },

        //這個為什么不是siblings, 但是返回的是一個數組哇;
        // $.sibling($("div").get(0))
        sibling: function( n, elem ) {
            var r = [];
            //result包含了當前的n(nodeElement);
            for ( ; n; n = n.nextSibling ) {
                if ( n.nodeType === 1 && n !== elem ) {
                    r.push( n );
                }
            }

            return r;
        }
    });

// Implement the identical functionality for filter and not
    //你搜索winnow ,引用了這個函數就只有not和filter;
    //可能是function ,可能是元素節點 ,也可能是字符串;
// not是傳進來的keep是false, 是排除的關系, filter傳進來的是true, 這個是過濾合適的關系;
// 本身not和filter可以傳多種參數,所以單獨拿出來比較靠譜;
    function winnow( elements, qualifier, keep ) {
        //如果傳進來的事函數,就把當前的
        if ( jQuery.isFunction( qualifier ) ) {
            //grep相對于是數組的filter方法
            return jQuery.grep(elements, function( elem, i ) {
                //改變上下文, 參數為index和elem ,如果這個回調返回ture就過濾掉,
                //不返回或者返回false就不過濾掉;
                var retVal = !!qualifier.call( elem, i, elem );
                return retVal === keep;
            });
            //傳element的不多吧;
        } else if ( qualifier.nodeType ) {
            return jQuery.grep(elements, function( elem, i ) {
                return (elem === qualifier) === keep;
            });

        } else if ( typeof qualifier === "string" ) {
            //把所有的節點元素過濾出來....
            var filtered = jQuery.grep(elements, function( elem ) {
                return elem.nodeType === 1;
            });

            // isSimple = ^.[^:#\[\.,]*$/;
            // 傳進來的class的情況下;
            if ( isSimple.test( qualifier ) ) {
                /*
                 //從第二個集合中獲取匹配選擇器的元素;
                 $.filter("#ipt",$("input"))

                 $.filter = function ( expr, elems, not ) {
                 if ( not ) {
                 expr = ":not(" + expr + ")";
                 }

                 return elems.length === 1 ?
                 jQuery.find.matchesSelector(elems[0], expr) ? [ elems[0] ] : [] :
                 jQuery.find.matches(expr, elems);
                 };
                 */
                return jQuery.filter(qualifier, filtered, !keep);
            } else {
                qualifier = jQuery.filter( qualifier, filtered );
            };
        };
        //$.filter 和 $.grep要分清楚....
        //$.filter第一個參數是字符串或者元素,是提供給$的實例用的,操作選中的元素并過濾出合適的子集,
        //$.grep的參數是回調;相對于Array.filter,根據回調的返回值產生集合;
        /*$.filter可以看成是$.grep的子集 可以寫成
         $.filter = function(str,elems) {
         var result = [];
         $.grep(elem,function(e, i) {
         return e.matchSelector(str);
         });
         }
         */
        return jQuery.grep(elements, function( elem, i ) {
            return (jQuery.inArray( elem, qualifier ) >= 0) === keep;
        });
    };




    var rinlinejQuery = / jQuery\d+="(?:\d+|null)"/g,
        rleadingWhitespace = /^\s+/,
        rxhtmlTag = /<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/ig,
        rtagName = /<([\w:]+)/,
        rtbody = /<tbody/i,
        rhtml = /<|&#?\w+;/,
        rnocache = /<(?:script|object|embed|option|style)/i,
    // checked="checked" or checked (html5)
        rchecked = /checked\s*(?:[^=]|=\s*.checked.)/i,
        wrapMap = {
            option: [ 1, "<select multiple='multiple'>", "</select>" ],
            legend: [ 1, "<fieldset>", "</fieldset>" ],
            thead: [ 1, "<table>", "</table>" ],
            tr: [ 2, "<table><tbody>", "</tbody></table>" ],
            td: [ 3, "<table><tbody><tr>", "</tr></tbody></table>" ],
            col: [ 2, "<table><tbody></tbody><colgroup>", "</colgroup></table>" ],
            area: [ 1, "<map>", "</map>" ],
            _default: [ 0, "", "" ]
        };

    wrapMap.optgroup = wrapMap.option;
    wrapMap.tbody = wrapMap.tfoot = wrapMap.colgroup = wrapMap.caption = wrapMap.thead;
    wrapMap.th = wrapMap.td;

// IE can't serialize <link> and <script> tags normally
    if ( !jQuery.support.htmlSerialize ) {
        wrapMap._default = [ 1, "div<div>", "</div>" ];
    }

    jQuery.fn.extend({
        text: function( text ) {
            //對于傳進來的是函數進行處理;
            if ( jQuery.isFunction(text) ) {
                return this.each(function(i) {
                    var self = jQuery( this );

                    self.text( text.call(this, i, self.text()) );
                });
            };

            //對于傳進來的不是對象,而且不是undefined進行處理;
            if ( typeof text !== "object" && text !== undefined ) {
                //通過createTextNode的方式新建文本節點;
                return this.empty().append( (this[0] && this[0].ownerDocument || document).createTextNode( text ) );
            };

            //最后是走沒有傳參數或者null的情況;
            return jQuery.text( this );
        },

        wrapAll: function( html ) {
            //又是處理是functoin的情況;
            if ( jQuery.isFunction( html ) ) {
                return this.each(function(i) {
                    jQuery(this).wrapAll( html.call(this, i) );
                });
            };

            //元素一定要存在;
            if ( this[0] ) {
                // The elements to wrap the target around
                //$(".hehe").wrapAll("<div></div><div>22222</div>") 此時的<div>22222</div>這些元素不生效;
                var wrap = jQuery( html, this[0].ownerDocument ).eq(0).clone(true);

                //可能是在DocumentFragment的情況下;
                if ( this[0].parentNode ) {
                    wrap.insertBefore( this[0] );
                };
                /*
                 $("body").map(function(){
                 return $("<div>");
                 });
                 jQueryInstance.map = function ( callback ) {
                 return this.pushStack( jQuery.map(this, function( elem, i ) {
                 return callback.call( elem, i, elem );
                 }));
                 };
                 */
                //$("body").wrapAll( $("<div><span>1111</span><span>2222</span></div>") );
                //要找到wrap內部第一個底層元素, 然后把當前的this加到該層;, 利用了wrap是返回一個新的jq數組;
                wrap.map(function() {
                    var elem = this;

                    while ( elem.firstChild && elem.firstChild.nodeType === 1 ) {
                        elem = elem.firstChild;
                    };

                    return elem;
                }).append(this);
            }

            return this;
        },
        //$("body").wrapInner("<div>")
        wrapInner: function( html ) {
            //處理了函數;
            if ( jQuery.isFunction( html ) ) {
                return this.each(function(i) {
                    jQuery(this).wrapInner( html.call(this, i) );
                });
            }

            return this.each(function() {
                //保存當前元素的內容;
                var self = jQuery( this ),
                    contents = self.contents();

                //把當前的元素包用傳進來的html進行包含; 所以說對這個元素進行wrapAll相當于選擇這個元素的子元素,然后wrapInner;
                if ( contents.length ) {
                    contents.wrapAll( html );
                    //沒子元素直接append,相當于直接innerHTML;
                } else {
                    self.append( html );
                };
            });
        },
        //wrap是wrapAll所有的this;相當于把當前的所有元素添加一個父級;
        //$("<div>1111</div><div>2222</div>").appendTo( $("body") ).wrap("<span></span>");
        wrap: function( html ) {
            return this.each(function() {
                jQuery( this ).wrapAll( html );
            });
        },

        //unwrap和wrap是相反的;
        unwrap: function() {
            return this.parent().each(function() {
                if ( !jQuery.nodeName( this, "body" ) ) {
                    //把當前替換成當前元素的子元素;
                    jQuery( this ).replaceWith( this.childNodes );
                }
            }).end();
        },

        //調用domManip減少代碼量和統一管理參數;;
        append: function() {
            return this.domManip(arguments, true, function( elem ) {
                //watch expressions;
                if ( this.nodeType === 1 ) {
                    this.appendChild( elem );
                };
            });
        },

        //內部也調用了domManip
        prepend: function() {
            return this.domManip(arguments, true, function( elem ) {
                if ( this.nodeType === 1 ) {
                    this.insertBefore( elem, this.firstChild );
                }
            });
        },

        ////domManip就是屬性系統中的access啊, 主要的作用就是統一處理各種個樣的參數;
        before: function() {
            //if(1){console.log(1)}else if(1){console.log(2)},就打了一個1哦,不要想太多;
            //$("body").before( $("<div>") );在body的前面加了個DIV元素;
            if ( this[0] && this[0].parentNode ) {
                return this.domManip(arguments, false, function( elem ) {
                    this.parentNode.insertBefore( elem, this );
                });
                //沒有parentNode情況就是在內存中,不在頁面里面
            } else if ( arguments.length ) {
                var set = jQuery(arguments[0]);
                //直接弄成jQ的偽數組對象,然后把set這個jQ對象后面添加了this這個偽數組哇;
                set.push.apply( set, this.toArray() );
                return this.pushStack( set, "before", arguments );
            };
        },

        //內部也調用了domManip
        after: function() {
            if ( this[0] && this[0].parentNode ) {
                return this.domManip(arguments, false, function( elem ) {
                    this.parentNode.insertBefore( elem, this.nextSibling );
                });
                //沒有parentNode情況就是在內存中,不在頁面里面
            } else if ( arguments.length ) {
                var set = this.pushStack( this, "after", arguments );
                set.push.apply( set, jQuery(arguments[0]).toArray() );
                return set;
            };
        },

        // keepData is for internal use only--do not document
        remove: function( selector, keepData ) {
            for ( var i = 0, elem; (elem = this[i]) != null; i++ ) {
                //沒有selector就是把當前的給刪了, 語義上應該是要匹配selector元素,把匹配到的刪了
                //都是里面沒有對selector處理, 沒啥用啊,我勒個去;
                if ( !selector || jQuery.filter( selector, [ elem ] ).length ) {
                    if ( !keepData && elem.nodeType === 1 ) {
                        //刪除緩存數據
                        jQuery.cleanData( elem.getElementsByTagName("*") );
                        jQuery.cleanData( [ elem ] );
                    };

                    //刪除元素;
                    if ( elem.parentNode ) {
                        elem.parentNode.removeChild( elem );
                    };
                }
            }

            //鏈式調用;
            return this;
        },

        empty: function() {
            //迭代當前元素;
            for ( var i = 0, elem; (elem = this[i]) != null; i++ ) {

                //刪除內部元素的數據
                // Remove element nodes and prevent memory leaks
                if ( elem.nodeType === 1 ) {
                    jQuery.cleanData( elem.getElementsByTagName("*") );
                };

                //迭代,直到把所有的元素刪除,為什么不用innerHTML = "";這種寫法;
                // Remove any remaining nodes
                while ( elem.firstChild ) {
                    elem.removeChild( elem.firstChild );
                };
            };

            return this;
        },

        //復制緩存的data和事件, 深度復制緩存的data和數據;
        /*
         只要有傳一個參數,那么第二次參數就是和第一個參數一樣;
         */
        clone: function( dataAndEvents, deepDataAndEvents ) {
            dataAndEvents = dataAndEvents == null ? true : dataAndEvents;
            deepDataAndEvents = deepDataAndEvents == null ? dataAndEvents : deepDataAndEvents;

            return this.map( function () {
                //$("body").clone(true,true); 第一個參數是指復制這個元素的事件, 第二個參數為深層次的復制事件
                return jQuery.clone( this, dataAndEvents, deepDataAndEvents );
            });
            /*
             //少寫了幾行代碼;
             this.each( function () {
             return jQuery.clone( this, dataAndEvents, deepDataAndEvents );
             });
             return this;
             */
        },

        html: function( value ) {
            debugger
            //get
            if ( value === undefined ) {
                return this[0] && this[0].nodeType === 1 ?
                    // rinlinejQuery = / jQuery\d+="(?:\d+|null)"/gIE67DOM屬性和HTML屬性不分所以要replace;
                    this[0].innerHTML.replace(rinlinejQuery, "") :
                    null;
                //get
                // See if we can take a shortcut and just use innerHTML
                //rnocache = /<(?:script|object|embed|option|style)/i
            } else if ( typeof value === "string" && !rnocache.test( value ) &&
                (jQuery.support.leadingWhitespace || !rleadingWhitespace.test( value )) &&
                // rtagName = /<([\w:]+)/;
                /*
                 wrapMap = {
                 option: [ 1, "<select multiple='multiple'>", "</select>" ],
                 legend: [ 1, "<fieldset>", "</fieldset>" ],
                 thead: [ 1, "<table>", "</table>" ],
                 tr: [ 2, "<table><tbody>", "</tbody></table>" ],
                 td: [ 3, "<table><tbody><tr>", "</tr></tbody></table>" ],
                 col: [ 2, "<table><tbody></tbody><colgroup>", "</colgroup></table>" ],
                 area: [ 1, "<map>", "</map>" ],
                 _default: [ 0, "", "" ]
                 };
                 */
                !wrapMap[ (rtagName.exec( value ) || ["", ""])[1].toLowerCase() ] ) {

                //rxhtmlTag = /<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/ig
                //非單標簽的單標簽給他替換成雙標簽了;
                value = value.replace(rxhtmlTag, "<$1></$2>");
                //如果有傳這些的標簽就不走這邊;
                //$("body").html("<tr>")  ==>> !wrapMap[ (rtagName.exec( value ) || ["", ""])[1].toLowerCase() ] )  === false
                try {
                    for ( var i = 0, l = this.length; i < l; i++ ) {
                        // Remove element nodes and prevent memory leaks
                        if ( this[i].nodeType === 1 ) {
                            //刪除事件防止內存的泄露和緩存在$.cache中的數據;
                            jQuery.cleanData( this[i].getElementsByTagName("*") );
                            this[i].innerHTML = value;
                        }
                    }

                    // If using innerHTML throws an exception, use the fallback method
                } catch(e) {
                    this.empty().append( value );
                }

            } else if ( jQuery.isFunction( value ) ) {
                //處理是函數的情況;
                this.each(function(i){
                    var self = jQuery( this );

                    self.html( value.call(this, i, self.html()) );
                });

            } else {
                //最后回退到這里;
                this.empty().append( value );
            }

            return this;
        },

        replaceWith: function( value ) {
            if ( this[0] && this[0].parentNode ) {
                // Make sure that the elements are removed from the DOM before they are inserted
                // this can help fix replacing a parent with child elements
                //處理是函數的情況;
                if ( jQuery.isFunction( value ) ) {
                    //return的是還是this哦,不是value;
                    return this.each(function(i) {
                        var self = jQuery(this), old = self.html();
                        self.replaceWith( value.call( this, i, old ) );
                    });
                };

                //把這個元素刪除, 這個元素可能在fragement中或者DOM節點上面;
                if ( typeof value !== "string" ) {
                    value = jQuery( value ).detach();
                };

                //迭代每一個;
                return this.each(function() {
                    var next = this.nextSibling,
                        parent = this.parentNode;

                    //刪除當前, 根據元素的節點所在位置 增加新元素
                    jQuery( this ).remove();

                    if ( next ) {
                        jQuery(next).before( value );
                    } else {
                        jQuery(parent).append( value );
                    };
                });
            } else {
                return this.pushStack( jQuery(jQuery.isFunction(value) ? value() : value), "replaceWith", value );
            }
        },

        detach: function( selector ) {
            return this.remove( selector, true );
        },

        //通過處理傳進來的是字符串的情況轉化成jQ對象, 或者傳進來的是函數的情況, 對傳進來的參數進行統一處理,然后放到回調里面去;
        //如果傳進來了script標簽就把script標簽執行了;
        domManip: function( args, table, callback ) {
            var results, first, fragment, parent,
                value = args[0],
                scripts = [];

            //rchecked =  /checked\s*(?:[^=]|=\s*.checked.)/i;
            //chrome還有這個毛病....
            // We can't cloneNode fragments that contain checked, in WebKit
            if ( !jQuery.support.checkClone && arguments.length === 3 && typeof value === "string" && rchecked.test( value ) ) {
                return this.each(function() {
                    jQuery(this).domManip( args, table, callback, true );
                });
            };

            //如果是函數就處理;
            if ( jQuery.isFunction(value) ) {
                return this.each(function(i) {
                    var self = jQuery(this);
                    //$("body").append(function(i,html){console.log(i+html)}),懂得自然懂哇;
                    args[0] = value.call(this, i, table ? self.html() : undefined);
                    self.domManip( args, table, callback );
                });
            };

            if ( this[0] ) {
                parent = value && value.parentNode;
                //用戶傳進來的就是fragment;
                // If we're in a fragment, just use that instead of building a new one
                if ( jQuery.support.parentNode && parent && parent.nodeType === 11 && parent.childNodes.length === this.length ) {
                    results = { fragment: parent };

                } else {
                    // jQuery.buildFragment( ["aa","bb"], this, scripts ); 你可以看看這個東東的用處;
                    results = jQuery.buildFragment( args, this, scripts );
                };


                fragment = results.fragment;

                if ( fragment.childNodes.length === 1 ) {
                    first = fragment = fragment.firstChild;
                } else {
                    first = fragment.firstChild;
                };

                //fisrt是fragment的第一個元素;
                if ( first ) {
                    table = table && jQuery.nodeName( first, "tr" );

                    //又是執行回調了;
                    for ( var i = 0, l = this.length, lastIndex = l - 1; i < l; i++ ) {
                        callback.call(
                            table ?
                                //如果傳進來的是tr元素就進行特殊處理;
                                root(this[i], first) :
                                this[i],
                            // Make sure that we do not leak memory by inadvertently discarding
                            // the original fragment (which might have attached data) instead of
                            // using it; in addition, use the original fragment object for the last
                            // item instead of first because it can end up being emptied incorrectly
                            // in certain situations (Bug #8070).
                            // Fragments from the fragment cache must always be cloned and never used
                            // in place.
                            // 是將this有多個,就復制fragment, 如果多次的話, 就直接從build的cahce緩存里面讀取;
                            results.cacheable || (l > 1 && i < lastIndex) ?
                                jQuery.clone( fragment, true, true ) :
                                fragment
                        );
                    }
                }

                if ( scripts.length ) {
                    //這個會新建一script標簽然后把js語句放進來,后來又會把scirpt標簽刪除;
                    jQuery.each( scripts, evalScript );
                }
            }

            return this;
        }
    });

    function root( elem, cur ) {
        return jQuery.nodeName(elem, "table") ?
            (elem.getElementsByTagName("tbody")[0] ||
                elem.appendChild(elem.ownerDocument.createElement("tbody"))) :
            elem;
    }

    function cloneCopyEvent( src, dest ) {

        //元素一定要是節點類型 而且元素還要有緩存的數據;
        if ( dest.nodeType !== 1 || !jQuery.hasData( src ) ) {
            return;
        };

        var internalKey = jQuery.expando,
            oldData = jQuery.data( src ),
            curData = jQuery.data( dest, oldData );

        // Switch to use the internal data object, if it exists, for the next
        // stage of data copying
        //有了jQ的緩存系統,復制事件很簡單了
        if ( (oldData = oldData[ internalKey ]) ) {
            var events = oldData.events;
            curData = curData[ internalKey ] = jQuery.extend({}, oldData);

            if ( events ) {
                //復制事件
                delete curData.handle;
                curData.events = {};

                //迭代手動添加事件;
                for ( var type in events ) {
                    for ( var i = 0, l = events[ type ].length; i < l; i++ ) {
                        jQuery.event.add( dest, type, events[ type ][ i ], events[ type ][ i ].data );
                    }
                }
            }
        }
    }

    //cloneFixAttributes是給IE用的;
    function cloneFixAttributes(src, dest) {
        // We do not need to do anything for non-Elements
        if ( dest.nodeType !== 1 ) {
            return;
        }

        var nodeName = dest.nodeName.toLowerCase();

        // clearAttributes removes the attributes, which we don't want,
        // but also removes the attachEvent events, which we *do* want
        dest.clearAttributes();

        // mergeAttributes, in contrast, only merges back on the
        // original attributes, not the events
        dest.mergeAttributes(src);

        // IE6-8 fail to clone children inside object elements that use
        // the proprietary classid attribute value (rather than the type
        // attribute) to identify the type of content to display
        if ( nodeName === "object" ) {
            dest.outerHTML = src.outerHTML;

        } else if ( nodeName === "input" && (src.type === "checkbox" || src.type === "radio") ) {
            // IE6-8 fails to persist the checked state of a cloned checkbox
            // or radio button. Worse, IE6-7 fail to give the cloned element
            // a checked appearance if the defaultChecked value isn't also set
            if ( src.checked ) {
                dest.defaultChecked = dest.checked = src.checked;
            }

            // IE6-7 get confused and end up setting the value of a cloned
            // checkbox/radio button to an empty string instead of "on"
            if ( dest.value !== src.value ) {
                dest.value = src.value;
            }

            // IE6-8 fails to return the selected option to the default selected
            // state when cloning options
        } else if ( nodeName === "option" ) {
            dest.selected = src.defaultSelected;

            // IE6-8 fails to set the defaultValue to the correct value when
            // cloning other types of input fields
        } else if ( nodeName === "input" || nodeName === "textarea" ) {
            dest.defaultValue = src.defaultValue;
        }

        // Event data gets referenced instead of copied if the expando
        // gets copied too
        dest.removeAttribute( jQuery.expando );
    }

    //$.buildFragment(["<div></div>","<div></div>","<div></div>","<div></div>"]).fragment
    jQuery.buildFragment = function( args, nodes, scripts ) {
        var fragment, cacheable, cacheresults,
            doc = (nodes && nodes[0] ? nodes[0].ownerDocument || nodes[0] : document);

        // Only cache "small" (1/2 KB) HTML strings that are associated with the main document
        // Cloning options loses the selected state, so don't cache them
        // IE 6 doesn't like it when you put <object> or <embed> elements in a fragment
        // Also, WebKit does not clone 'checked' attributes on cloneNode, so don't cache
        if ( args.length === 1 && typeof args[0] === "string" && args[0].length < 512 && doc === document &&
            args[0].charAt(0) === "<" && !rnocache.test( args[0] ) && (jQuery.support.checkClone || !rchecked.test( args[0] )) ) {
            //緩存不是很必須的,提高性能;
            cacheable = true;
            cacheresults = jQuery.fragments[ args[0] ];
            if ( cacheresults ) {
                if ( cacheresults !== 1 ) {
                    fragment = cacheresults;
                }
            }
        };

        //主要還是調用$.clean方法
        if ( !fragment ) {
            fragment = doc.createDocumentFragment();
            jQuery.clean( args, doc, fragment, scripts );
        }

        if ( cacheable ) {
            jQuery.fragments[ args[0] ] = cacheresults ? fragment : 1;
        }

        return { fragment: fragment, cacheable: cacheable };
    };

    jQuery.fragments = {};

    jQuery.each({
        appendTo: "append",
        prependTo: "prepend",
        insertBefore: "before",
        insertAfter: "after",
        replaceAll: "replaceWith"
    }, function( name, original ) {
        jQuery.fn[ name ] = function( selector ) {
            var ret = [],
            //轉化為jQ元素;
                insert = jQuery( selector ),
            //this當前元素
                parent = this.length === 1 && this[0].parentNode;

            //如果有當前元
            if ( parent && parent.nodeType === 11 && parent.childNodes.length === 1 && insert.length === 1 ) {
                insert[ original ]( this[0] );
                return this;

            } else {
                //走這邊就是fragment的情況
                for ( var i = 0, l = insert.length; i < l; i++ ) {
                    var elems = (i > 0 ? this.clone(true) : this).get();
                    jQuery( insert[i] )[ original ]( elems );
                    ret = ret.concat( elems );
                };

                return this.pushStack( ret, name, insert.selector );
            }
        };
    });

    jQuery.extend({
        clone: function( elem, dataAndEvents, deepDataAndEvents ) {
            var clone = elem.cloneNode(true),
                srcElements,
                destElements,
                i;

            //jQuery.support.noCloneEvent標準瀏覽器是不復制事件的,IE瀏覽器就會走這里面;
            //特別處理IE會復制event, 并且如果把clone的綁定的事件刪除, 原來的事件也沒有了;
            if ( !jQuery.support.noCloneEvent && (elem.nodeType === 1 || elem.nodeType === 11) && !jQuery.isXMLDoc(elem) ) {
                // IE copies events bound via attachEvent when using cloneNode.
                // Calling detachEvent on the clone will also remove the events
                // from the original. In order to get around this, we use some
                // proprietary methods to clear the events. Thanks to MooTools
                // guys for this hotness.

                // Using Sizzle here is crazy slow, so we use getElementsByTagName
                // instead;
                srcElements = elem.getElementsByTagName("*");
                destElements = clone.getElementsByTagName("*");

                // Weird iteration because IE will replace the length property
                // with an element if you are cloning the body and one of the
                // elements on the page has a name or id of "length"
                // ie的問題 , name或者id的值是"length";
                for ( i = 0; srcElements[i]; ++i ) {
                    cloneFixAttributes( srcElements[i], destElements[i] );
                };

                cloneFixAttributes( elem, clone );
            }

            // Copy the events from the original to the clone
            if ( dataAndEvents ) {

                cloneCopyEvent( elem, clone );

                if ( deepDataAndEvents && "getElementsByTagName" in elem ) {

                    srcElements = elem.getElementsByTagName("*");
                    destElements = clone.getElementsByTagName("*");

                    if ( srcElements.length ) {
                        for ( i = 0; srcElements[i]; ++i ) {
                            cloneCopyEvent( srcElements[i], destElements[i] );
                        }
                    }
                }
            }
            // Return the cloned set
            return clone;
        },
        //jQuery.clean("111")  ==》》  ["1", "1", "1"];
        //jQuery.clean(["bbb"])  ==>>  ["bbb"];
        //jQuery.clean(["<td></td>"])  ==>>  [<td></td>] DOM元素出來了;
        /*
         $.clean(["<div></div><script>console.log(1)</script>"],null, document.createDocumentFragment(),sc=[]);
         ==>>  [<div></div>,<script>console.log(1)</script>]
         ==>>  sc === [<script>console.log(1)</script>]
         */
        /*
         $.clean(["<div><script>console.log(0)</script></div><script>console.log(1)</script>"],null, document.createDocumentFragment(),sc=[]);
         ==>>   [<div></div>, <script>console.log(0)</script>, <script>console.log(1)</script>];
         */
        clean: function( elems, context, fragment, scripts ) {
            context = context || document;

            // !context.createElement fails in IE with an error but returns typeof 'object'
            if ( typeof context.createElement === "undefined" ) {
                context = context.ownerDocument || context[0] && context[0].ownerDocument || document;
            }

            //需要返回的值
            var ret = [];

            //循環傳進來的元素,你也可以傳字符串, 但是沒啥效果;
            for ( var i = 0, elem; (elem = elems[i]) != null; i++ ) {
                if ( typeof elem === "number" ) {
                    elem += "";
                }

                if ( !elem ) {
                    continue;
                }

                //這個if Else是處理忠于用戶輸入的問題;

                // rhtml = /<|&#?\w+;/;
                // Convert html string into DOM nodes
                if ( typeof elem === "string" && !rhtml.test( elem ) ) {
                    elem = context.createTextNode( elem );

                } else if ( typeof elem === "string" ) {
                    //debugger;
                    // Fix "XHTML"-style tags in all browsers
                    elem = elem.replace(rxhtmlTag, "<$1></$2>");

                    // Trim whitespace, otherwise indexOf won't work as expected
                    var tag = (rtagName.exec( elem ) || ["", ""])[1].toLowerCase(),
                        wrap = wrapMap[ tag ] || wrapMap._default,
                        depth = wrap[0],
                        div = context.createElement("div");

                    // 生成節點,如果你jQuery.clean(["<td></td>"])會變成這樣
                    // "<table><tbody><tr><td></td></tr></tbody></table>"
                    // Go to html and back, then peel off extra wrappers
                    div.innerHTML = wrap[1] + elem + wrap[2];

                    // 獲取正確的DOM深度, 獲取正確的元素;
                    // Move to the right depth
                    while ( depth-- ) {
                        div = div.lastChild;
                    };

                    // IE會自己新加table到這個元素;
                    // Remove IE's autoinserted <tbody> from table fragments
                    if ( !jQuery.support.tbody ) {

                        // String was a <table>, *may* have spurious <tbody>
                        var hasBody = rtbody.test(elem),
                            tbody = tag === "table" && !hasBody ?
                                div.firstChild && div.firstChild.childNodes :

                                // String was a bare <thead> or <tfoot>
                                wrap[1] === "<table>" && !hasBody ?
                                    div.childNodes :
                                    [];

                        for ( var j = tbody.length - 1; j >= 0 ; --j ) {
                            if ( jQuery.nodeName( tbody[ j ], "tbody" ) && !tbody[ j ].childNodes.length ) {
                                tbody[ j ].parentNode.removeChild( tbody[ j ] );
                            }
                        }

                    }

                    //IE會自己處理掉開頭的空格;
                    // IE completely kills leading whitespace when innerHTML is used
                    if ( !jQuery.support.leadingWhitespace && rleadingWhitespace.test( elem ) ) {
                        div.insertBefore( context.createTextNode( rleadingWhitespace.exec(elem)[0] ), div.firstChild );
                    }

                    //div是目前暫時用的一個容器;
                    elem = div.childNodes;
                };

                //如果是單個元素, 就直接push進去,優化
                if ( elem.nodeType ) {
                    ret.push( elem );
                } else {
                    //多個使用merge合并;
                    ret = jQuery.merge( ret, elem );
                }
            }

            //如果有傳fragment進來就把結果result放進來,有script就把script放進來;
            if ( fragment ) {
                for ( i = 0; ret[i]; i++ ) {
                    //如果放進來的元素有script標簽, 就把script提取出來到script這個數組;
                    if ( scripts && jQuery.nodeName( ret[i], "script" ) && (!ret[i].type || ret[i].type.toLowerCase() === "text/javascript") ) {
                        scripts.push( ret[i].parentNode ? ret[i].parentNode.removeChild( ret[i] ) : ret[i] );

                    } else {
                        //如果是放進來的是除了script以外的節點元素,就把這個元素下的script標簽提取出來放到ret的后面
                        if ( ret[i].nodeType === 1 ) {
                            //splice( 從第幾個開始, 要刪除的元素, 要增加的元素);
                            ret.splice.apply( ret, [i + 1, 0].concat(jQuery.makeArray(ret[i].getElementsByTagName("script"))) );
                        };
                        fragment.appendChild( ret[i] );
                    }
                }
            }

            return ret;
        },

        /*
         jQ1.4的cleanData:
         function cleanData( elems ) {
         for ( var i = 0, elem, id; (elem = elems[i]) != null; i++ ) {
         if ( !jQuery.noData[elem.nodeName.toLowerCase()] && (id = elem[expando]) ) {
         delete jQuery.cache[ id ];
         };
         };
         };
         */
        cleanData: function( elems ) {
            var data, id, cache = jQuery.cache, internalKey = jQuery.expando, special = jQuery.event.special,
                deleteExpando = jQuery.support.deleteExpando;
            /* 
             jQuery.noData  == {embed: true, object: "clsid:D27CDB6E-AE6D-11cf-96B8-444553540000", applet: true};
             */
            for ( var i = 0, elem; (elem = elems[i]) != null; i++ ) {
                if ( elem.nodeName && jQuery.noData[elem.nodeName.toLowerCase()] ) {
                    continue;
                }

                id = elem[ jQuery.expando ];

                if ( id ) {
                    data = cache[ id ] && cache[ id ][ internalKey ];

                    //不但要刪events還要刪除,事件,避免元素刪除了,事件還在, 減少不必要的內存占用;
                    if ( data && data.events ) {
                        for ( var type in data.events ) {
                            if ( special[ type ] ) {
                                jQuery.event.remove( elem, type );

                                // This is a shortcut to avoid jQuery.event.remove's overhead
                            } else {
                                jQuery.removeEvent( elem, type, data.handle );
                            }
                        }

                        //又是內存泄漏的問題;
                        // Null the DOM reference to avoid IE6/7/8 leak (#7054)
                        if ( data.handle ) {
                            data.handle.elem = null;
                        }
                    }

                    //IE的兼容;
                    if ( deleteExpando ) {
                        delete elem[ jQuery.expando ];

                    } else if ( elem.removeAttribute ) {
                        elem.removeAttribute( jQuery.expando );
                    }

                    //緩存的id刪除;
                    delete cache[ id ];
                }
            }
        }
    });

    //新建scirpt標簽, 或者直接加載遠程的js文件, 然后刪除;
    function evalScript( i, elem ) {
        if ( elem.src ) {
            jQuery.ajax({
                url: elem.src,
                async: false,
                dataType: "script"
            });
        } else {
            jQuery.globalEval( elem.text || elem.textContent || elem.innerHTML || "" );
        };

        if ( elem.parentNode ) {
            elem.parentNode.removeChild( elem );
        };
    };




    //ralpha和ropacity都是給IE用的;
    var ralpha = /alpha\([^)]*\)/i,
        ropacity = /opacity=([^)]*)/,
    //camelize;
        rdashAlpha = /-([a-z])/ig,
        rupper = /([A-Z])/g,
    //匹配時否包含了px;
        rnumpx = /^-?\d+(?:px)?$/i,
    //開頭為什么有一個-;
        rnum = /^-?\d/,

    //解決元素隱藏的時候不能獲取width或者高的情況;
        cssShow = { position: "absolute", visibility: "hidden", display: "block" },
        cssWidth = [ "Left", "Right" ],
        cssHeight = [ "Top", "Bottom" ],
        curCSS,

        getComputedStyle,
        currentStyle,

        fcamelCase = function( all, letter ) {
            return letter.toUpperCase();
        };

    jQuery.fn.css = function( name, value ) {
        // Setting 'undefined' is a no-op
        if ( arguments.length === 2 && value === undefined ) {
            return this;
        }
        //debugger;
        //通過access統一管理參數
        return jQuery.access( this, name, value, true, function( elem, name, value ) {
            //調用的不一樣, 如果value存在就走$.style,否則通過$.css獲取
            return value !== undefined ?
                jQuery.style( elem, name, value ) :
                jQuery.css( elem, name );
        });
    };

    jQuery.extend({
        // Add in style property hooks for overriding the default
        // behavior of getting and setting a style property
        cssHooks: {
            opacity: {
                get: function( elem, computed ) {
                    if ( computed ) {
                        // We should always get a number back from opacity
                        var ret = curCSS( elem, "opacity", "opacity" );
                        return ret === "" ? "1" : ret;

                    } else {
                        return elem.style.opacity;
                    }
                }
            }
        },

        // Exclude the following css properties to add px
        cssNumber: {
            "zIndex": true,
            "fontWeight": true,
            "opacity": true,
            "zoom": true,
            "lineHeight": true
        },

        // Add in properties whose names you wish to fix before
        // setting or getting the value
        cssProps: {
            // normalize float css property
            "float": jQuery.support.cssFloat ? "cssFloat" : "styleFloat"
        },

        // Get and set the style property on a DOM Node
        style: function( elem, name, value, extra ) {
            // Don't set styles on text and comment nodes
            if ( !elem || elem.nodeType === 3 || elem.nodeType === 8 || !elem.style ) {
                return;
            }

            // Make sure that we're working with the right name
            var ret, origName = jQuery.camelCase( name ),
            //直接獲取elem.style樣式屬性;
                style = elem.style, hooks = jQuery.cssHooks[ origName ];

            //修正屬性,如果$("body").style("float","xx") 那么名字會根據瀏覽器進行修正;
            name = jQuery.cssProps[ origName ] || origName;

            // Check if we're setting a value
            if ( value !== undefined ) {
                // Make sure that NaN and null values aren't set. See: #7116
                if ( typeof value === "number" && isNaN( value ) || value == null ) {
                    return;
                }

                // If a number was passed in, add 'px' to the (except for certain CSS properties)
                if ( typeof value === "number" && !jQuery.cssNumber[ origName ] ) {
                    value += "px";
                }

                /*  
                 如果是寬高和透明度的情況就走hook;
                 {
                 height : {get:fn,set:fn},
                 width : {get:fn,set:fn},
                 opacity : {get:fn,set:fn}
                 }
                 */
                // If a hook was provided, use that value, otherwise just set the specified value
                if ( !hooks || !("set" in hooks) || (value = hooks.set( elem, value )) !== undefined ) {
                    // Wrapped to prevent IE from throwing errors when 'invalid' values are provided
                    // Fixes bug #5509
                    try {
                        style[ name ] = value;
                    } catch(e) {}
                };

                /*
                 //這段話也可以寫成;
                 if ( !hooks || !("set" in hooks) ) {
                 var value = hooks.set( elem, value );
                 if( value == undefined ) {
                 try {
                 style[ name ] = value;
                 } catch(e) {};
                 };
                 }else{
                 try {
                 style[ name ] = value;
                 } catch(e) {};
                 };
                 */

            } else {
                // If a hook was provided get the non-computed value from there
                if ( hooks && "get" in hooks && (ret = hooks.get( elem, false, extra )) !== undefined ) {
                    return ret;
                }
                /*
                 //這段話也可以寫成;
                 if ( hooks && "get" in hooks ) {
                 ret = hooks.get( elem, false, extra );
                 if( ret == undefined ) {
                 return style[ name ];
                 }
                 };
                 return style[ name ];
                 */

                // Otherwise just get the value from the style object
                return style[ name ];
            }
        },

        css: function( elem, name, extra ) {
            //這個和style一樣, 如果有
            // Make sure that we're working with the right name
            var ret, origName = jQuery.camelCase( name ),
                hooks = jQuery.cssHooks[ origName ];

            name = jQuery.cssProps[ origName ] || origName;

            //如果hooks里面有get 就走hooks的get, 否則通過curCSS獲取樣式;
            // If a hook was provided get the computed value from there
            if ( hooks && "get" in hooks && (ret = hooks.get( elem, true, extra )) !== undefined ) {
                return ret;
                // Otherwise, if a way to get the computed value exists, use that
            } else if ( curCSS ) {
                //curCSS = getComputedStyle || currentStyle
                return curCSS( elem, name, origName );
            };
        },

        //隱藏的元素無法獲取widthHeight或者其他的屬性;
        // A method for quickly swapping in/out CSS properties to get correct calculations
        swap: function( elem, options, callback ) {
            var old = {};

            // Remember the old values, and insert the new ones
            for ( var name in options ) {
                old[ name ] = elem.style[ name ];
                elem.style[ name ] = options[ name ];
            }

            callback.call( elem );

            // Revert the old values
            for ( name in options ) {
                elem.style[ name ] = old[ name ];
            }
        },

        camelCase: function( string ) {
            return string.replace( rdashAlpha, fcamelCase );
        }
    });

// DEPRECATED, Use jQuery.css() instead
    jQuery.curCSS = jQuery.css;

    //減少代碼量;
    jQuery.each(["height", "width"], function( i, name ) {
        jQuery.cssHooks[ name ] = {
            get: function( elem, computed, extra ) {
                var val;
                //computed一定要是真的,否則就根本不走;
                if ( computed ) {
                    if ( elem.offsetWidth !== 0 ) {
                        val = getWH( elem, name, extra );

                    } else {
                        jQuery.swap( elem, cssShow, function() {
                            val = getWH( elem, name, extra );
                        });
                    }
                    // val < 0是什么情況;
                    if ( val <= 0 ) {
                        val = curCSS( elem, name, name );

                        if ( val === "0px" && currentStyle ) {
                            val = currentStyle( elem, name, name );
                        };

                        if ( val != null ) {
                            // Should return "auto" instead of 0, use 0 for
                            // temporary backwards-compat
                            return val === "" || val === "auto" ? "0px" : val;
                        };
                    };

                    if ( val < 0 || val == null ) {
                        val = elem.style[ name ];

                        // Should return "auto" instead of 0, use 0 for
                        // temporary backwards-compat
                        return val === "" || val === "auto" ? "0px" : val;
                    };

                    return typeof val === "string" ? val : val + "px";
                }
            },

            set: function( elem, value ) {
                if ( rnumpx.test( value ) ) {
                    // ignore negative width and height values #1599
                    value = parseFloat(value);

                    if ( value >= 0 ) {
                        return value + "px";
                    }

                } else {
                    return value;
                }
            }
        };
    });

    //IE的jQuery.support.opacity才存在;
    if ( !jQuery.support.opacity ) {
        jQuery.cssHooks.opacity = {
            get: function( elem, computed ) {
                // IE uses filters for opacity
                // IE的測試的濾鏡給他檢測出來除以一百,否則根據computed返回1或者0
                return ropacity.test((computed && elem.currentStyle ? elem.currentStyle.filter : elem.style.filter) || "") ?
                    (parseFloat(RegExp.$1) / 100) + "" :
                    computed ? "1" : "";
            },

            set: function( elem, value ) {
                var style = elem.style;

                // IE has trouble with opacity if it does not have layout
                // Force it by setting the zoom level
                style.zoom = 1;

                // Set the alpha filter to set the opacity
                var opacity = jQuery.isNaN(value) ?
                        "" :
                        "alpha(opacity=" + value * 100 + ")",
                    filter = style.filter || "";

                style.filter = ralpha.test(filter) ?
                    filter.replace(ralpha, opacity) :
                    style.filter + ' ' + opacity;
            }
        };
    }

    //標志瀏覽器的獲取計算后樣式
    if ( document.defaultView && document.defaultView.getComputedStyle ) {
        getComputedStyle = function( elem, newName, name ) {
            var ret, defaultView, computedStyle;

            name = name.replace( rupper, "-$1" ).toLowerCase();

            if ( !(defaultView = elem.ownerDocument.defaultView) ) {
                return undefined;
            }

            if ( (computedStyle = defaultView.getComputedStyle( elem, null )) ) {
                ret = computedStyle.getPropertyValue( name );
                //如果元素不含在document里面的話, 就通過style獲取樣式, 說明如果元素如果不在document里面的話, 元素的計算后樣式獲取不到
                /*
                 var div = document.createElement("div")
                 getComputedStyle(div)["display"] ==>> null;
                 div.style.height = 100
                 getComputedStyle(div)["height"] null
                 div.style.height ==》》 100px
                 */
                if ( ret === "" && !jQuery.contains( elem.ownerDocument.documentElement, elem ) ) {
                    ret = jQuery.style( elem, name );
                }
            }

            return ret;
        };
    }

    if ( document.documentElement.currentStyle ) {
        //IE獲取樣式的兼容;
        currentStyle = function( elem, name ) {
            var left,
                ret = elem.currentStyle && elem.currentStyle[ name ],
                rsLeft = elem.runtimeStyle && elem.runtimeStyle[ name ],
                style = elem.style;

            // From the awesome hack by Dean Edwards
            // http://erik.eae.net/archives/2007/07/27/18.54.15/#comment-102291

            // If we're not dealing with a regular pixel number
            // but a number that has a weird ending, we need to convert it to pixels
            if ( !rnumpx.test( ret ) && rnum.test( ret ) ) {
                // Remember the original values
                left = style.left;

                // Put in the new values to get a computed value out
                if ( rsLeft ) {
                    elem.runtimeStyle.left = elem.currentStyle.left;
                }
                style.left = name === "fontSize" ? "1em" : (ret || 0);
                ret = style.pixelLeft + "px";

                // Revert the changed values
                style.left = left;
                if ( rsLeft ) {
                    elem.runtimeStyle.left = rsLeft;
                }
            }

            return ret === "" ? "auto" : ret;
        };
    }

    curCSS = getComputedStyle || currentStyle;

    /*
     cssShow = { position: "absolute", visibility: "hidden", display: "block" },
     cssWidth = [ "Left", "Right" ],
     cssHeight = [ "Top", "Bottom" ],
     */
    function getWH( elem, name, extra ) {
        var which = name === "width" ? cssWidth : cssHeight,
            val = name === "width" ? elem.offsetWidth : elem.offsetHeight;

        //如果是border-box就返回offsetWidth或者offsetHeight;
        if ( extra === "border" ) {
            return val;
        };

        jQuery.each( which, function() {
            //不傳extra獲取是padding-box;
            if ( !extra ) {
                val -= parseFloat(jQuery.css( elem, "padding" + this )) || 0;
            };

            //傳margin是offset + margin的值;
            if ( extra === "margin" ) {
                val += parseFloat(jQuery.css( elem, "margin" + this )) || 0;

            } else {
                //最后的情況是傳進來的true, val要減去padding 在減去border;
                val -= parseFloat(jQuery.css( elem, "border" + this + "Width" )) || 0;
            }
        });

        return val;
    };

    //jQuery.expr是sizzle里面的東東;
    if ( jQuery.expr && jQuery.expr.filters ) {
        jQuery.expr.filters.hidden = function( elem ) {
            var width = elem.offsetWidth,
                height = elem.offsetHeight;

            return (width === 0 && height === 0) || (!jQuery.support.reliableHiddenOffsets && (elem.style.display || jQuery.css( elem, "display" )) === "none");
        };

        jQuery.expr.filters.visible = function( elem ) {
            return !jQuery.expr.filters.hidden( elem );
        };
    }



    //匹配URL的那種空格;
    var r20 = /%20/g,
        rbracket = /\[\]$/,
    //回車加換行,或者單單回車(for mac);
        rCRLF = /\r?\n/g,
    //是否有#
        rhash = /#.*$/,
        rheaders = /^(.*?):\s*(.*?)\r?$/mg, // IE leaves an \r character at EOL
        rinput = /^(?:color|date|datetime|email|hidden|month|number|password|range|search|tel|text|time|url|week)$/i,
        rnoContent = /^(?:GET|HEAD)$/,
        rprotocol = /^\/\//,
        rquery = /\?/,
    // "<div>11</div><script>console.log(1)</script><div>11</div>".match(/<script\b[^<]*(?:(?!<\/script>)<[^<]*)*<\/script>/gi);
    // <script>console.log(1)</script>會被匹配出來;
        rscript = /<script\b[^<]*(?:(?!<\/script>)<[^<]*)*<\/script>/gi,
        rselectTextarea = /^(?:select|textarea)/i,
        rspacesAjax = /\s+/,
        rts = /([?&])_=[^&]*/,
        rurl = /^(\w+:)\/\/([^\/?#:]+)(?::(\d+))?/,

    // Keep a copy of the old load method
        _load = jQuery.fn.load,

    /* Prefilters
     * 1) They are useful to introduce custom dataTypes (see ajax/jsonp.js for an example)
     * 2) These are called:
     *    - BEFORE asking for a transport
     *    - AFTER param serialization (s.data is a string if s.processData is true)
     * 3) key is the dataType
     * 4) the catchall symbol "*" can be used
     * 5) execution will start with transport dataType and THEN continue down to "*" if needed
     */
        prefilters = {},

    /* Transports bindings
     * 1) key is the dataType
     * 2) the catchall symbol "*" can be used
     * 3) selection will start with transport dataType and THEN go to "*" if needed
     */
        transports = {};

// Base "constructor" for jQuery.ajaxPrefilter and jQuery.ajaxTransport
    // prefilters or transports;
    // "json jsonp" "script" "script" XML請求包裝函數;
    function addToPrefiltersOrTransports( structure ) {
        // 又是一個閉包;
        // dataTypeExpression is optional and defaults to "*"
        return function( dataTypeExpression, func ) {
            //debugger;
            if ( typeof dataTypeExpression !== "string" ) {
                func = dataTypeExpression;
                dataTypeExpression = "*";
            }

            if ( jQuery.isFunction( func ) ) {
                // rspacesAjax = /\s+/;
                var dataTypes = dataTypeExpression.toLowerCase().split( rspacesAjax ),
                    i = 0,
                    length = dataTypes.length,
                    dataType,
                    list,
                    placeBefore;

                // For each dataType in the dataTypeExpression
                // json jsonp script 或者是 *;
                for(; i < length; i++ ) {
                    dataType = dataTypes[ i ];
                    // We control if we're asked to add before
                    // any existing element
                    // 可能dataTypes是這樣的 +json jsonp; 那么這個placeBefore就是ture, 這個回調會被放到了list最前排;
                    placeBefore = /^\+/.test( dataType );
                    if ( placeBefore ) {
                        dataType = dataType.substr( 1 ) || "*";
                    }
                    list = structure[ dataType ] = structure[ dataType ] || [];
                    // then we add to the structure accordingly
                    // 保存回調方法;
                    list[ placeBefore ? "unshift" : "push" ]( func );
                }
            }
        };
    }

//Base inspection function for prefilters and transports
    function inspectPrefiltersOrTransports( structure, options, originalOptions, jXHR,
                                            dataType /* internal */, inspected /* internal */ ) {

        dataType = dataType || options.dataTypes[ 0 ];
        inspected = inspected || {};

        inspected[ dataType ] = true;

        var list = structure[ dataType ],
            i = 0,
            length = list ? list.length : 0,
            executeOnly = ( structure === prefilters ),
            selection;

        for(; i < length && ( executeOnly || !selection ); i++ ) {
            selection = list[ i ]( options, originalOptions, jXHR );
            // If we got redirected to another dataType
            // we try there if not done already
            if ( typeof selection === "string" ) {
                if ( inspected[ selection ] ) {
                    selection = undefined;
                } else {
                    options.dataTypes.unshift( selection );
                    selection = inspectPrefiltersOrTransports(
                        structure, options, originalOptions, jXHR, selection, inspected );
                }
            }
        }
        // If we're only executing or nothing was selected
        // we try the catchall dataType if not done already
        if ( ( executeOnly || !selection ) && !inspected[ "*" ] ) {
            selection = inspectPrefiltersOrTransports(
                structure, options, originalOptions, jXHR, "*", inspected );
        }
        // unnecessary when only executing (prefilters)
        // but it'll be ignored by the caller in that case
        return selection;
    }

    jQuery.fn.extend({
        load: function( url, params, callback ) {
            if ( typeof url !== "string" && _load ) {
                return _load.apply( this, arguments );

                // Don't do a request if no elements are being requested
            } else if ( !this.length ) {
                return this;
            }

            var off = url.indexOf( " " );
            if ( off >= 0 ) {
                var selector = url.slice( off, url.length );
                url = url.slice( 0, off );
            }

            // Default to a GET request
            var type = "GET";

            // If the second parameter was provided
            if ( params ) {
                // If it's a function
                if ( jQuery.isFunction( params ) ) {
                    // We assume that it's the callback
                    callback = params;
                    params = null;

                    // Otherwise, build a param string
                } else if ( typeof params === "object" ) {
                    params = jQuery.param( params, jQuery.ajaxSettings.traditional );
                    type = "POST";
                }
            }

            var self = this;

            // Request the remote document
            jQuery.ajax({
                url: url,
                type: type,
                dataType: "html",
                data: params,
                // Complete callback (responseText is used internally)
                complete: function( jXHR, status, responseText ) {
                    // Store the response as specified by the jXHR object
                    responseText = jXHR.responseText;
                    // If successful, inject the HTML into all the matched elements
                    if ( jXHR.isResolved() ) {
                        // #4825: Get the actual response in case
                        // a dataFilter is present in ajaxSettings
                        jXHR.done(function( r ) {
                            responseText = r;
                        });
                        // See if a selector was specified
                        self.html( selector ?
                            // Create a dummy div to hold the results
                            jQuery("<div>")
                                // inject the contents of the document in, removing the scripts
                                // to avoid any 'Permission Denied' errors in IE
                                .append(responseText.replace(rscript, ""))

                                // Locate the specified elements
                                .find(selector) :

                            // If not, just inject the full result
                            responseText );
                    }

                    if ( callback ) {
                        self.each( callback, [ responseText, status, jXHR ] );
                    }
                }
            });

            return this;
        },
        /*
         <form id="form" action="form">
         <input type="text" name="ipt0" id="ipt0" value="111"/>
         <input type="text"  name="ipt1" id="ipt1" value="222"/>
         </form>

         ==>>  $("#form").serializeArray();
         */
        serialize: function() {
            return jQuery.param( this.serializeArray() );
        },

        serializeArray: function() {
            return this.map(function(){
                return this.elements ? jQuery.makeArray( this.elements ) : this;
            })
                .filter(function(){
                    return this.name && !this.disabled &&
                        ( this.checked || rselectTextarea.test( this.nodeName ) ||
                            rinput.test( this.type ) );
                })
                .map(function( i, elem ){
                    var val = jQuery( this ).val();

                    return val == null ?
                        null :
                        jQuery.isArray( val ) ?
                            jQuery.map( val, function( val, i ){
                                return { name: elem.name, value: val.replace( rCRLF, "\r\n" ) };
                            }) :
                        { name: elem.name, value: val.replace( rCRLF, "\r\n" ) };
                }).get();
        }
    });

    // Attach a bunch of functions for handling common AJAX events
    // 利用閉包減少代碼量;
    jQuery.each( "ajaxStart ajaxStop ajaxComplete ajaxError ajaxSuccess ajaxSend".split( " " ), function( i, o ){
        jQuery.fn[ o ] = function( f ){
            return this.bind( o, f );
        };
    } );

    jQuery.each( [ "get", "post" ], function( i, method ) {
        jQuery[ method ] = function( url, data, callback, type ) {
            // shift arguments if data argument was omitted
            if ( jQuery.isFunction( data ) ) {
                type = type || callback;
                callback = data;
                data = null;
            }

            return jQuery.ajax({
                type: method,
                url: url,
                data: data,
                success: callback,
                dataType: type
            });
        };
    } );

    jQuery.extend({

        getScript: function( url, callback ) {
            return jQuery.get( url, null, callback, "script" );
        },

        getJSON: function( url, data, callback ) {
            return jQuery.get( url, data, callback, "json" );
        },

        ajaxSetup: function( settings ) {
            /*
             setting : {
             jsonp : "callback",
             jsonpCallback : fn
             },
             setting : {
             accepts : {
             script : "text/javascript, application/javascript"
             },
             contents : {
             script : /javascript/ <<==是一個正則;
             },
             converters : function( text ) {
             jQuery.globalEval( text );
             return text;
             }
             }
             */
            //debugger;
            jQuery.extend( true, jQuery.ajaxSettings, settings );
            if ( settings.context ) {
                jQuery.ajaxSettings.context = settings.context;
            }
        },

        ajaxSettings: {
            url: location.href,
            global: true,
            type: "GET",
            contentType: "application/x-www-form-urlencoded",
            processData: true,
            async: true,
            /*
             timeout: 0,
             data: null,
             dataType: null,
             username: null,
             password: null,
             cache: null,
             traditional: false,
             headers: {},
             crossDomain: null,
             */

            accepts: {
                xml: "application/xml, text/xml",
                html: "text/html",
                text: "text/plain",
                json: "application/json, text/javascript",
                "*": "*/*"
            },

            contents: {
                xml: /xml/,
                html: /html/,
                json: /json/
            },

            responseFields: {
                xml: "responseXML",
                text: "responseText"
            },

            // List of data converters
            // 1) key format is "source_type destination_type" (a single space in-between)
            // 2) the catchall symbol "*" can be used for source_type
            converters: {

                // Convert anything to text
                "* text": window.String,

                // Text to html (true = no transformation)
                "text html": true,

                // Evaluate text as a json expression
                "text json": jQuery.parseJSON,

                // Parse text as xml
                "text xml": jQuery.parseXML
            }
        },

        //預傳送器, 后面會初始化json和jsonp(包括回調的處理等), 標準xml的預傳送器;
        ajaxPrefilter: addToPrefiltersOrTransports( prefilters ),
        //標準傳送器, 包括ajax的傳送器的執行操作, jsonp中需要添加script標簽到界面的操作;
        ajaxTransport: addToPrefiltersOrTransports( transports ),

        // file///:C/本地協議的文件;
        // Main method
        /*
         cb = function(arg){console.log(arg)};
         var url = "http://www.filltext.com/?callback=?";
         $.getJSON( url, {
         'callback' : "cb",
         'rows': 5,
         'fname': '{firstName}',
         'lname': '{lastName}',
         'tel': '{phone|format}',
         });
         */
        ajax: function( url, options ) {

            // If options is not an object,
            // we simulate pre-1.5 signature
            if ( typeof options !== "object" ) {
                options = url;
                url = undefined;
            }

            // Force options to be an object
            options = options || {};

            var // Create the final options object
                s = jQuery.extend( true, {}, jQuery.ajaxSettings, options ),
            // Callbacks contexts
            // We force the original context if it exists
            // or take it from jQuery.ajaxSettings otherwise
            // (plain objects used as context get extended)
                callbackContext =
                    ( s.context = ( "context" in options ? options : jQuery.ajaxSettings ).context ) || s,
                globalEventContext = callbackContext === s ? jQuery.event : jQuery( callbackContext ),
            // Deferreds
                deferred = jQuery.Deferred(),
                completeDeferred = jQuery._Deferred(),
            // Status-dependent callbacks
                statusCode = s.statusCode || {},
            // Headers (they are sent all at once)
                requestHeaders = {},
            // Response headers
                responseHeadersString,
                responseHeaders,
            // transport
                transport,
            // timeout handle
                timeoutTimer,
            // Cross-domain detection vars
                loc = document.location,
                protocol = loc.protocol || "http:",
                parts,
            // The jXHR state
                state = 0,
            // Loop variable
                i,

            //假的XMLHttpRequest, 因為xml的屬性 不兼容, 為xhr包裹一層, 方便統一管理;
            // Fake xhr
                jXHR = {

                    readyState: 0,

                    // Caches the header
                    setRequestHeader: function( name, value ) {
                        if ( state === 0 ) {
                            requestHeaders[ name.toLowerCase() ] = value;
                        }
                        return this;
                    },

                    // Raw string
                    getAllResponseHeaders: function() {
                        return state === 2 ? responseHeadersString : null;
                    },

                    // Builds headers hashtable if needed
                    getResponseHeader: function( key ) {
                        var match;
                        if ( state === 2 ) {
                            if ( !responseHeaders ) {
                                responseHeaders = {};
                                while( ( match = rheaders.exec( responseHeadersString ) ) ) {
                                    responseHeaders[ match[1].toLowerCase() ] = match[ 2 ];
                                }
                            }
                            match = responseHeaders[ key.toLowerCase() ];
                        }
                        return match || null;
                    },

                    // Cancel the request
                    abort: function( statusText ) {
                        statusText = statusText || "abort";
                        if ( transport ) {
                            transport.abort( statusText );
                        }
                        done( 0, statusText );
                        return this;
                    }
                };

            // Callback for when everything is done
            // It is defined here because jslint complains if it is declared
            // at the end of the function (which would be more logical and readable)
            function done( status, statusText, responses, headers) {

                // Called once
                if ( state === 2 ) {
                    return;
                }

                // State is "done" now
                state = 2;

                // Clear timeout if it exists
                if ( timeoutTimer ) {
                    clearTimeout( timeoutTimer );
                }

                // Dereference transport for early garbage collection
                // (no matter how long the jXHR object will be used)
                transport = undefined;

                // Cache response headers
                responseHeadersString = headers || "";

                // Set readyState
                jXHR.readyState = status ? 4 : 0;

                var isSuccess,
                    success,
                    error,
                    response = responses ? ajaxHandleResponses( s, jXHR, responses ) : undefined,
                    lastModified,
                    etag;

                // If successful, handle type chaining
                if ( status >= 200 && status < 300 || status === 304 ) {

                    // Set the If-Modified-Since and/or If-None-Match header, if in ifModified mode.
                    if ( s.ifModified ) {

                        if ( ( lastModified = jXHR.getResponseHeader( "Last-Modified" ) ) ) {
                            jQuery.lastModified[ s.url ] = lastModified;
                        }
                        if ( ( etag = jXHR.getResponseHeader( "Etag" ) ) ) {
                            jQuery.etag[ s.url ] = etag;
                        };
                    };

                    // If not modified
                    if ( status === 304 ) {

                        statusText = "notmodified";
                        isSuccess = true;

                        // If we have data
                    } else {

                        try {
                            success = ajaxConvert( s, response );
                            statusText = "success";
                            isSuccess = true;
                        } catch(e) {
                            // We have a parsererror
                            statusText = "parsererror";
                            error = e;
                        };
                    };
                } else {
                    // We extract error from statusText
                    // then normalize statusText and status for non-aborts
                    error = statusText;
                    if( status ) {
                        statusText = "error";
                        if ( status < 0 ) {
                            status = 0;
                        }
                    }
                };

                // Set data for the fake xhr object
                jXHR.status = status;
                jXHR.statusText = statusText;

                // Success/Error
                if ( isSuccess ) {
                    debugger;
                    deferred.resolveWith( callbackContext, [ success, statusText, jXHR ] );
                } else {
                    deferred.rejectWith( callbackContext, [ jXHR, statusText, error ] );
                }

                // Status-dependent callbacks
                jXHR.statusCode( statusCode );
                statusCode = undefined;

                if ( s.global ) {
                    globalEventContext.trigger( "ajax" + ( isSuccess ? "Success" : "Error" ),
                        [ jXHR, s, isSuccess ? success : error ] );
                }

                // Complete
                completeDeferred.resolveWith( callbackContext, [ jXHR, statusText ] );

                if ( s.global ) {
                    globalEventContext.trigger( "ajaxComplete", [ jXHR, s] );
                    // Handle the global AJAX counter
                    if ( !( --jQuery.active ) ) {
                        jQuery.event.trigger( "ajaxStop" );
                    }
                }
            };

            //var def = {}; $.Deferred().promise(def)   把這個對象送到promise會為著對象繼承(非真正意義上的繼承,只是復制了屬性而已)一個延遲對象的實例;
            // Attach deferreds
            deferred.promise( jXHR );
            jXHR.success = jXHR.done;
            jXHR.error = jXHR.fail;
            jXHR.complete = completeDeferred.done;

            // Status-dependent callbacks
            jXHR.statusCode = function( map ) {
                if ( map ) {
                    var tmp;
                    if ( state < 2 ) {
                        for( tmp in map ) {
                            statusCode[ tmp ] = [ statusCode[tmp], map[tmp] ];
                        }
                    } else {
                        tmp = map[ jXHR.status ];
                        jXHR.then( tmp, tmp );
                    }
                }
                return this;
            };
            //變量初始化完畢;

            // Remove hash character (#7531: and string promotion)
            // Add protocol if not provided (#5866: IE7 issue with protocol-less urls)
            // We also use the url parameter if available
            //去除空格;                 // rprotocol = /^\/\// 如果協議頭是//就改成 ==》 當前協議頭+//
            s.url = ( "" + ( url || s.url ) ).replace( rhash, "" ).replace( rprotocol, protocol + "//" );

            // Extract dataTypes list
            // rspacesAjax = /\s+/;
            //把用戶期望的返回類型保存起來,方便回調;
            s.dataTypes = jQuery.trim( s.dataType || "*" ).toLowerCase().split( rspacesAjax );

            //判斷是否跨域了哇;
            // Determine if a cross-domain request is in order
            if ( !s.crossDomain ) {
                parts = rurl.exec( s.url.toLowerCase() );
                s.crossDomain = !!( parts &&
                    ( parts[ 1 ] != protocol || parts[ 2 ] != loc.hostname ||
                        ( parts[ 3 ] || ( parts[ 1 ] === "http:" ? 80 : 443 ) ) !=
                            ( loc.port || ( protocol === "http:" ? 80 : 443 ) ) )
                    );
            };

            // Convert data if not already a string
            if ( s.data && s.processData && typeof s.data !== "string" ) {
                //把json的數據轉化成通過&拼接的數據;
                s.data = jQuery.param( s.data, s.traditional );
            };
            // Apply prefilters
            //prefilters = {JSON : fn,  JSONP : fn,  SCRIPT : fn};

            //根據setting的dataType類型設置預處理的參數;
            inspectPrefiltersOrTransports( prefilters, s, options, jXHR );

            // Uppercase the type
            s.type = s.type.toUpperCase();

            // Determine if request has content
            // /^(?:GET|HEAD)$/ 沒有發送數據就是沒有content;
            s.hasContent = !rnoContent.test( s.type );

            // Watch for a new set of requests
            //false
            // jQuery.active 目前等于 0;
            if ( s.global && jQuery.active++ === 0 ) {
                jQuery.event.trigger( "ajaxStart" );
            };

            // More options handling for requests with no content
            if ( !s.hasContent ) {

                // If data is available, append data to url
                if ( s.data ) {
                    // s.url  ==>>  "http://www.filltext.com/?callback=jQuery15003316175821237266_1420601952773"
                    // s.data ==>>  "callback=cb&rows=5&fname=%7BfirstName%7D&lname=%7BlastName%7D&tel=%7Bphone%7Cformat%7D"
                    s.url += ( rquery.test( s.url ) ? "&" : "?" ) + s.data;
                    //jsonp會改變callback為jQ自己定義的callback然后在執行成功的時候才執行用戶傳進來的callback;
                    //最后變成了 ==>> http://www.filltext.com/?callback=jQuery15003316175821237266_1420601952773&callback=cb&rows=5&fname=%7BfirstName%7D&lname=%7BlastName%7D&tel=%7Bphone%7Cformat%7D
                };
                // Add anti-cache in url if needed
                if ( s.cache === false ) {

                    var ts = jQuery.now(),
                    // try replacing _= if it is there;
                    // rts = /([?&])_=[^&]*/; 把 ?_=替換成?=times 或者是 #_=替換成#_=times, 而且后面不是&, 避免出現別的錯誤;
                    /*
                     "sdfsdfdsf?_=abcd".replace(rts, "$1_="+jQuery.now())  ==>>  "sdfsdfdsf?_=1420614479567"
                     "sdfsdfdsf#_=abcd".replace(rts, "$1_="+jQuery.now())  ==>>  "sdfsdfdsf#_=abcd"
                     */
                        ret = s.url.replace( rts, "$1_=" + ts );

                    // 給最后添加一個時間戳;
                    // if nothing was replaced, add timestamp to the end
                    s.url = ret + ( (ret === s.url ) ? ( rquery.test( s.url ) ? "&" : "?" ) + "_=" + ts : "" );
                };
            };

            //JSON是不走這邊的;
            // Set the correct header, if data is being sent
            if ( s.data && s.hasContent && s.contentType !== false || options.contentType ) {
                requestHeaders[ "content-type" ] = s.contentType;
            }

            //
            // Set the If-Modified-Since and/or If-None-Match header, if in ifModified mode.
            if ( s.ifModified ) {
                if ( jQuery.lastModified[ s.url ] ) {
                    requestHeaders[ "if-modified-since" ] = jQuery.lastModified[ s.url ];
                }
                if ( jQuery.etag[ s.url ] ) {
                    requestHeaders[ "if-none-match" ] = jQuery.etag[ s.url ];
                }
            }

            // 根據用戶需求的請求頭, 服務器可以返回期望的類型;
            // Set the Accepts header for the server, depending on the dataType
            requestHeaders.accept = s.dataTypes[ 0 ] && s.accepts[ s.dataTypes[0] ] ?
                s.accepts[ s.dataTypes[0] ] + ( s.dataTypes[ 0 ] !== "*" ? ", */*; q=0.01" : "" ) :
                s.accepts[ "*" ];

            // Check for headers option
            for ( i in s.headers ) {
                requestHeaders[ i.toLowerCase() ] = s.headers[ i ];
            };
            // 執行before的事件,這個是全局的;
            // Allow custom headers/mimetypes and early abort
            if ( s.beforeSend && ( s.beforeSend.call( callbackContext, jXHR, s ) === false || state === 2 ) ) {
                // Abort if not done already
                done( 0, "abort" );
                // Return false
                jXHR = false;

            } else {

                // Install callbacks on deferreds
                for ( i in { success: 1, error: 1, complete: 1 } ) {
                    //把用戶的回調安裝到延遲對象;
                    //jXHR.success( s.success );
                    //jXHR.complete( s.complete );
                    //jXHR.error( s.error );
                    jXHR[ i ]( s[ i ] );
                }

                // Get transport
                //獲取傳送器, 根據傳送器的設置發送數據, 這個里面會執行get,post或者新增script的操作;
                transport = inspectPrefiltersOrTransports( transports, s, options, jXHR );

                // If no transport, we auto-abort
                if ( !transport ) {
                    done( -1, "No Transport" );
                } else {
                    // Set state as sending
                    state = jXHR.readyState = 1;
                    // Send global event
                    // 觸發全局的ajaxSend事件;參數為JXHR, s;
                    if ( s.global ) {
                        globalEventContext.trigger( "ajaxSend", [ jXHR, s ] );
                    }

                    //開啟一定定時器,如果是異步而且超時的話就觸發超時的事件;
                    // Timeout
                    if ( s.async && s.timeout > 0 ) {
                        timeoutTimer = setTimeout( function(){
                            jXHR.abort( "timeout" );
                        }, s.timeout );
                    }

                    //JSONP的傳送器有一個是send,一個是abort;
                    try {
                        transport.send( requestHeaders, done );
                    } catch (e) {
                        //如果出了錯;
                        // Propagate exception as error if not done
                        if ( status < 2 ) {
                            done( -1, e );
                            // Simply rethrow otherwise
                        } else {
                            jQuery.error( e );
                        }
                    }
                }
            }
            return jXHR;
        },

        // Serialize an array of form elements or a set of
        // key/values into a query string
        param: function( a, traditional ) {
            var s = [],
                add = function( key, value ) {
                    // If value is a function, invoke it and return its value
                    value = jQuery.isFunction( value ) ? value() : value;
                    s[ s.length ] = encodeURIComponent( key ) + "=" + encodeURIComponent( value );
                };

            // Set traditional to true for jQuery <= 1.3.2 behavior.
            if ( traditional === undefined ) {
                traditional = jQuery.ajaxSettings.traditional;
            }

            // If an array was passed in, assume that it is an array of form elements.
            if ( jQuery.isArray( a ) || a.jquery ) {
                // Serialize the form elements
                jQuery.each( a, function() {
                    add( this.name, this.value );
                } );

            } else {
                // If traditional, encode the "old" way (the way 1.3.2 or older
                // did it), otherwise encode params recursively.
                for ( var prefix in a ) {
                    buildParams( prefix, a[ prefix ], traditional, add );
                }
            }

            // Return the resulting serialization
            return s.join( "&" ).replace( r20, "+" );
        }
    });

    function buildParams( prefix, obj, traditional, add ) {
        if ( jQuery.isArray( obj ) && obj.length ) {
            // Serialize array item.
            jQuery.each( obj, function( i, v ) {
                if ( traditional || rbracket.test( prefix ) ) {
                    // Treat each array item as a scalar.
                    add( prefix, v );

                } else {
                    // If array item is non-scalar (array or object), encode its
                    // numeric index to resolve deserialization ambiguity issues.
                    // Note that rack (as of 1.0.0) can't currently deserialize
                    // nested arrays properly, and attempting to do so may cause
                    // a server error. Possible fixes are to modify rack's
                    // deserialization algorithm or to provide an option or flag
                    // to force array serialization to be shallow.
                    buildParams( prefix + "[" + ( typeof v === "object" || jQuery.isArray(v) ? i : "" ) + "]", v, traditional, add );
                }
            });

        } else if ( !traditional && obj != null && typeof obj === "object" ) {
            // If we see an array here, it is empty and should be treated as an empty
            // object
            if ( jQuery.isArray( obj ) || jQuery.isEmptyObject( obj ) ) {
                add( prefix, "" );

                // Serialize object item.
            } else {
                jQuery.each( obj, function( k, v ) {
                    buildParams( prefix + "[" + k + "]", v, traditional, add );
                });
            }

        } else {
            // Serialize scalar item.
            add( prefix, obj );
        }
    }

// This is still on the jQuery object... for now
// Want to move this to jQuery.ajax some day
    jQuery.extend({

        // Counter for holding the number of active queries
        active: 0,

        // Last-Modified header cache for next request
        lastModified: {},
        etag: {}

    });

    /* Handles responses to an ajax request:
     * - sets all responseXXX fields accordingly
     * - finds the right dataType (mediates between content-type and expected dataType)
     * - returns the corresponding response
     */
    function ajaxHandleResponses( s, jXHR, responses ) {

        var contents = s.contents,
            dataTypes = s.dataTypes,
            responseFields = s.responseFields,
            ct,
            type,
            finalDataType,
            firstDataType;

        // Fill responseXXX fields
        for( type in responseFields ) {
            if ( type in responses ) {
                jXHR[ responseFields[type] ] = responses[ type ];
            }
        }

        // Remove auto dataType and get content-type in the process
        while( dataTypes[ 0 ] === "*" ) {
            dataTypes.shift();
            if ( ct === undefined ) {
                ct = jXHR.getResponseHeader( "content-type" );
            }
        }

        // Check if we're dealing with a known content-type
        if ( ct ) {
            for ( type in contents ) {
                if ( contents[ type ] && contents[ type ].test( ct ) ) {
                    dataTypes.unshift( type );
                    break;
                }
            }
        }

        // Check to see if we have a response for the expected dataType
        if ( dataTypes[ 0 ] in responses ) {
            finalDataType = dataTypes[ 0 ];
        } else {
            // Try convertible dataTypes
            for ( type in responses ) {
                if ( !dataTypes[ 0 ] || s.converters[ type + " " + dataTypes[0] ] ) {
                    finalDataType = type;
                    break;
                }
                if ( !firstDataType ) {
                    firstDataType = type;
                }
            }
            // Or just use first one
            finalDataType = finalDataType || firstDataType;
        }

        // If we found a dataType
        // We add the dataType to the list if needed
        // and return the corresponding response
        if ( finalDataType ) {
            if ( finalDataType !== dataTypes[ 0 ] ) {
                dataTypes.unshift( finalDataType );
            }
            return responses[ finalDataType ];
        }
    }

// Chain conversions given the request and the original response
    function ajaxConvert( s, response ) {

        // Apply the dataFilter if provided
        if ( s.dataFilter ) {
            response = s.dataFilter( response, s.dataType );
        }

        var dataTypes = s.dataTypes,
            converters = s.converters,
            i,
            length = dataTypes.length,
            tmp,
        // Current and previous dataTypes
            current = dataTypes[ 0 ],
            prev,
        // Conversion expression
            conversion,
        // Conversion function
            conv,
        // Conversion functions (transitive conversion)
            conv1,
            conv2;

        // For each dataType in the chain
        for( i = 1; i < length; i++ ) {

            // Get the dataTypes
            prev = current;
            current = dataTypes[ i ];

            // If current is auto dataType, update it to prev
            if( current === "*" ) {
                current = prev;
                // If no auto and dataTypes are actually different
            } else if ( prev !== "*" && prev !== current ) {

                // Get the converter
                conversion = prev + " " + current;
                conv = converters[ conversion ] || converters[ "* " + current ];

                // If there is no direct converter, search transitively
                if ( !conv ) {
                    conv2 = undefined;
                    for( conv1 in converters ) {
                        tmp = conv1.split( " " );
                        if ( tmp[ 0 ] === prev || tmp[ 0 ] === "*" ) {
                            conv2 = converters[ tmp[1] + " " + current ];
                            if ( conv2 ) {
                                conv1 = converters[ conv1 ];
                                if ( conv1 === true ) {
                                    conv = conv2;
                                } else if ( conv2 === true ) {
                                    conv = conv1;
                                }
                                break;
                            }
                        }
                    }
                }
                // If we found no converter, dispatch an error
                if ( !( conv || conv2 ) ) {
                    jQuery.error( "No conversion from " + conversion.replace(" "," to ") );
                }
                // If found converter is not an equivalence
                if ( conv !== true ) {
                    // Convert with 1 or 2 converters accordingly
                    response = conv ? conv( response ) : conv2( conv1(response) );
                }
            }
        }
        return response;
    }




    var jsc = jQuery.now(),
        jsre = /(\=)\?(&|$)|()\?\?()/i;

// Default jsonp settings
    jQuery.ajaxSetup({
        jsonp: "callback",
        jsonpCallback: function() {
            return jQuery.expando + "_" + ( jsc++ );
        }
    });

// Detect, normalize options and install callbacks for jsonp requests
    jQuery.ajaxPrefilter( "json jsonp", function( s, originalSettings, dataIsString /* internal */ ) {

        dataIsString = ( typeof s.data === "string" );

        if ( s.dataTypes[ 0 ] === "jsonp" ||
            originalSettings.jsonpCallback ||
            originalSettings.jsonp != null ||
            s.jsonp !== false && ( jsre.test( s.url ) ||
                dataIsString && jsre.test( s.data ) ) ) {

            var responseContainer,
                jsonpCallback = s.jsonpCallback =
                    jQuery.isFunction( s.jsonpCallback ) ? s.jsonpCallback() : s.jsonpCallback,
                previous = window[ jsonpCallback ],
                url = s.url,
                data = s.data,
                replace = "$1" + jsonpCallback + "$2";

            if ( s.jsonp !== false ) {
                url = url.replace( jsre, replace );
                if ( s.url === url ) {
                    if ( dataIsString ) {
                        data = data.replace( jsre, replace );
                    }
                    if ( s.data === data ) {
                        // Add callback manually
                        url += (/\?/.test( url ) ? "&" : "?") + s.jsonp + "=" + jsonpCallback;
                    }
                }
            }

            s.url = url;
            s.data = data;

            window[ jsonpCallback ] = function( response ) {
                responseContainer = [ response ];
            };

            s.complete = [ function() {

                // Set callback back to previous value
                window[ jsonpCallback ] = previous;

                // Call if it was a function and we have a response
                if ( previous) {
                    if ( responseContainer && jQuery.isFunction( previous ) ) {
                        window[ jsonpCallback ] ( responseContainer[ 0 ] );
                    }
                } else {
                    // else, more memory leak avoidance
                    try{
                        delete window[ jsonpCallback ];
                    } catch( e ) {}
                }

            }, s.complete ];

            // Use data converter to retrieve json after script execution
            s.converters["script json"] = function() {
                if ( ! responseContainer ) {
                    jQuery.error( jsonpCallback + " was not called" );
                }
                return responseContainer[ 0 ];
            };

            // force json dataType
            s.dataTypes[ 0 ] = "json";

            // Delegate to script
            return "script";
        }
    } );




// Install script dataType
    jQuery.ajaxSetup({
        accepts: {
            script: "text/javascript, application/javascript"
        },
        contents: {
            script: /javascript/
        },
        converters: {
            "text script": function( text ) {
                jQuery.globalEval( text );
                return text;
            }
        }
    });

// Handle cache's special case and global
    jQuery.ajaxPrefilter( "script", function( s ) {
        if ( s.cache === undefined ) {
            s.cache = false;
        }
        if ( s.crossDomain ) {
            s.type = "GET";
            s.global = false;
        }
    } );

// Bind script tag hack transport
    //這個是跨域的傳送器哇;
    jQuery.ajaxTransport( "script", function(s) {

        // This transport only deals with cross domain requests
        // 如果請求的url發生改變,就默認用jsonp方式(script標簽)進行加載;
        if ( s.crossDomain ) {

            var script,
                head = document.getElementsByTagName( "head" )[ 0 ] || document.documentElement;

            return {

                send: function( _, callback ) {

                    script = document.createElement( "script" );
                    //async是標準瀏覽器所支持的東西;
                    //defer是IE支持的異步加載方式;
                    script.async = "async";

                    //字符串編碼;
                    if ( s.scriptCharset ) {
                        script.charset = s.scriptCharset;
                    }

                    //script和link標簽只有在添加到dom才發送請求哇;
                    script.src = s.url;

                    // Attach handlers for all browsers
                    // 事件還是先加載
                    script.onload = script.onreadystatechange = function( _, isAbort ) {

                        if ( !script.readyState || /loaded|complete/.test( script.readyState ) ) {

                            // Handle memory leak in IE
                            // IE的內存泄露問題;
                            script.onload = script.onreadystatechange = null;

                            // Remove the script
                            if ( head && script.parentNode ) {
                                head.removeChild( script );
                            }

                            // Dereference the script
                            script = undefined;

                            //因為是JSONP的方式, 就直接返回200的狀態和 success的姿態;
                            // Callback if not abort
                            if ( !isAbort ) {
                                callback( 200, "success" );
                            }
                        }
                    };
                    // Use insertBefore instead of appendChild  to circumvent an IE6 bug.
                    // This arises when a base node is used (#2709 and #4378).
                    head.insertBefore( script, head.firstChild );
                },

                abort: function() {
                    if ( script ) {
                        script.onload( 0, 1 );
                    }
                }
            };
        }
    } );




    var // Next active xhr id
        xhrId = jQuery.now(),

    // active xhrs
        xhrs = {},

    // #5280: see below
        xhrUnloadAbortInstalled,
    // 用來臨時用的;
    // XHR used to determine supports properties
        testXHR;

// Create the request object
// (This is still attached to ajaxSettings for backward compatibility)
    jQuery.ajaxSettings.xhr = window.ActiveXObject ?
        /* Microsoft failed to properly
         * implement the XMLHttpRequest in IE7 (can't request local files),
         * so we use the ActiveXObject when it is available
         * Additionally XMLHttpRequest can be disabled in IE7/IE8 so
         * we need a fallback.
         */
        function() {
            if ( window.location.protocol !== "file:" ) {
                try {
                    return new window.XMLHttpRequest();
                } catch( xhrError ) {}
            }

            try {
                return new window.ActiveXObject("Microsoft.XMLHTTP");
            } catch( activeError ) {}
        } :
        // For all other browsers, use the standard XMLHttpRequest object
        function() {
            return new window.XMLHttpRequest();
        };

// Test if we can create an xhr object
    try {
        testXHR = jQuery.ajaxSettings.xhr();
    } catch( xhrCreationException ) {};

    //測試是否支持XHR這個東西;
    //Does this browser support XHR requests?
    jQuery.support.ajax = !!testXHR;

    /*
     默認情況下,跨源請求不提供憑據(cookie、HTTP認證及客戶端SSL證明等)。
     通過將withCredentials屬性設置為true,可以指定某個請求應該發送憑據。
     如果服務器接收帶憑據的請求,會用下面的HTTP頭部來響應。
     如果發送的是帶憑據的請求,但服務器的相應中沒有包含這個頭部,
     那么瀏覽器就不會把相應交給JavaScript(于是,responseText中將是空字符串,status的值為0,
     而且會調用onerror()事件處理程序)。
     另外,服務器還可以在Preflight響應中發送這個HTTP頭部,
     表示允許源發送帶憑據的請求。
     支持withCredentials屬性的瀏覽器有Firefox 3.5+、Safari 4+和Chrome。IE10及更早版本都不支持。
     */
    // 是否支持跨域直接通過withCredentials進行判斷;
    // Does this browser support crossDomain XHR requests
    jQuery.support.cors = testXHR && ( "withCredentials" in testXHR );

    // No need for the temporary xhr anymore
    testXHR = undefined;

    // Create transport if the browser can provide an xhr
    if ( jQuery.support.ajax ) {
        //傳進來的是setting;
        jQuery.ajaxTransport(function( s ) {
            // Cross domain only allowed if supported through XMLHttpRequest
            if ( !s.crossDomain || jQuery.support.cors ) {

                var callback;

                return {
                    send: function( headers, complete ) {

                        //避免錯誤的
                        // #5280: we need to abort on unload or IE will keep connections alive
                        if ( !xhrUnloadAbortInstalled ) {

                            xhrUnloadAbortInstalled = 1;

                            jQuery(window).bind( "unload", function() {

                                // Abort all pending requests
                                jQuery.each( xhrs, function( _, xhr ) {
                                    if ( xhr.onreadystatechange ) {
                                        xhr.onreadystatechange( 1 );
                                    }
                                } );

                            } );
                        }

                        // Get a new xhr
                        var xhr = s.xhr(),
                            handle;

                        // Open the socket
                        // Passing null username, generates a login popup on Opera (#2865)
                        if ( s.username ) {
                            xhr.open( s.type, s.url, s.async, s.username, s.password );
                        } else {
                            xhr.open( s.type, s.url, s.async );
                        }

                        // Requested-With header
                        // Not set for crossDomain requests with no content
                        // (see why at http://trac.dojotoolkit.org/ticket/9486)
                        // Won't change header if already provided
                        // 設置頭;
                        if ( !( s.crossDomain && !s.hasContent ) && !headers["x-requested-with"] ) {
                            headers[ "x-requested-with" ] = "XMLHttpRequest";
                        }

                        // Need an extra try/catch for cross domain requests in Firefox 3
                        try {
                            jQuery.each( headers, function( key, value ) {
                                xhr.setRequestHeader( key, value );
                            } );
                        } catch( _ ) {};

                        // Do send the request
                        // This may raise an exception which is actually
                        // handled in jQuery.ajax (so no try/catch here)
                        // POST或者是GET,所以要判斷一下;
                        xhr.send( ( s.hasContent && s.data ) || null );

                        //cancel when I use arguments (0,1 ), kind like : callback(0,1);
                        // Listener
                        callback = function( _, isAbort ) {

                            // Was never called and is aborted or complete
                            if ( callback && ( isAbort || xhr.readyState === 4 ) ) {
                                //執行成功了就不用了;
                                // Only called once
                                callback = 0;

                                // Do not keep as active anymore
                                if ( handle ) {
                                    xhr.onreadystatechange = jQuery.noop;
                                    delete xhrs[ handle ];
                                }

                                // If it's an abort
                                //cance和send放在一起, 一個接口, 多個使用;
                                if ( isAbort ) {
                                    // Abort it manually if needed
                                    if ( xhr.readyState !== 4 ) {
                                        xhr.abort();
                                    }
                                } else {
                                    // Get info
                                    var status = xhr.status,
                                        statusText,
                                        responseHeaders = xhr.getAllResponseHeaders(),
                                        responses = {},
                                        xml = xhr.responseXML;

                                    // Construct response list
                                    if ( xml && xml.documentElement /* #4958 */ ) {
                                        responses.xml = xml;
                                    }
                                    responses.text = xhr.responseText;

                                    // Firefox throws an exception(exception異常) when accessing
                                    // statusText for faulty cross-domain requests
                                    // 火狐會報異常在跨域請求的時候;
                                    try {
                                        statusText = xhr.statusText;
                                    } catch( e ) {
                                        // We normalize with Webkit giving an empty statusText
                                        statusText = "";
                                    }

                                    // 修正各種瀏覽器的狀態碼;
                                    // Filter status for non standard behaviours    
                                    status =
                                        // Opera returns 0 when it should be 304
                                        // Webkit returns 0 for failing cross-domain no matter the real status
                                        status === 0 ?
                                            (
                                                // Webkit, Firefox: filter out faulty cross-domain requests
                                                !s.crossDomain || statusText ?
                                                    (
                                                        // Opera: filter out real aborts #6060
                                                        responseHeaders ?
                                                            304 :
                                                            0
                                                        ) :
                                                    // We assume 302 but could be anything cross-domain related
                                                    302
                                                ) :
                                            (
                                                // IE sometimes returns 1223 when it should be 204 (see #1450)
                                                status == 1223 ?
                                                    204 :
                                                    status
                                                );

                                    // Call complete
                                    //有responseXML和
                                    //responseText兩種值;
                                    //返回的返回頭;
                                    complete( status, statusText, responses, responseHeaders );
                                }
                            }
                        };

                        // if we're in sync mode or it's in cache
                        // and has been retrieved directly (IE6 & IE7)
                        // we need to manually fire the callback
                        if ( !s.async || xhr.readyState === 4 ) {
                            callback();
                        } else {
                            // Add to list of active xhrs
                            handle = xhrId++;
                            xhrs[ handle ] = xhr;
                            xhr.onreadystatechange = callback;
                        }
                    },

                    abort: function() {
                        if ( callback ) {
                            callback(0,1);
                        }
                    }
                };
            }
        });
    }




    //保存默認的顯示設置的變量;
    var elemdisplay = {},
    //
        rfxtypes = /^(?:toggle|show|hide)$/,
    //開頭是+-這樣的符號
    //包含數字.-
    //這個應該就是符號,符號可以是百分比;
        rfxnum = /^([+\-]=)?([\d+.\-]+)([a-z%]*)$/i,
        timerId,
        fxAttrs = [
            // height animations
            [ "height", "marginTop", "marginBottom", "paddingTop", "paddingBottom" ],
            // width animations
            [ "width", "marginLeft", "marginRight", "paddingLeft", "paddingRight" ],
            // opacity animations
            [ "opacity" ]
        ];

    jQuery.fn.extend({
        /*
         $("#div1").hide();
         $("#div1").show();
         $._data($("#div1")[0])  ==>> Object {olddisplay: "block"}   所謂的olddisplay永遠只有一個值;
         */
        show: function( speed, easing, callback ) {
            var elem, display;
            //有傳深度進來就調用animate
            if ( speed || speed === 0 ) {
                return this.animate( genFx("show", 3), speed, easing, callback);
            } else {
                //直接通過display方式進行直接顯示或者隱藏;
                for ( var i = 0, j = this.length; i < j; i++ ) {
                    elem = this[i];
                    display = elem.style.display;

                    // Reset the inline display of this element to learn if it is
                    // being hidden by cascaded rules or not
                    // 不可見變成可見,!jQuery._data(elem, "olddisplay")只有第一次才走這邊;
                    // 如果沒有沒有被$(el).hide()過就沒有olddisplay的, 就直接讓元素根據樣式表的默認樣式進行顯示;
                    if ( !jQuery._data(elem, "olddisplay") && display === "none" ) {
                        display = elem.style.display = "";
                    };

                    // Set elements which have been overridden with display: none
                    // in a stylesheet to whatever the default browser style is
                    // for such an element
                    // 如果元素設置成display=""以后, 而且默認樣式還是none, 就獲取默認樣式保存到私有數據緩存系統中;
                    if ( display === "" && jQuery.css( elem, "display" ) === "none" ) {
                        jQuery._data(elem, "olddisplay", defaultDisplay(elem.nodeName));
                    };
                };

                // Set the display of most of the elements in a second loop
                // to avoid the constant reflow
                // 這個可以直接放在上面的循環, 不過為了避免常量重渲染, 才把這個放在第二個循環里面
                for ( i = 0; i < j; i++ ) {
                    elem = this[i];
                    display = elem.style.display;
                    //是空或者是none就給他展示默認的樣式;
                    if ( display === "" || display === "none" ) {
                        elem.style.display = jQuery._data(elem, "olddisplay") || "";
                    }
                }

                return this;
            }
        },

        hide: function( speed, easing, callback ) {
            if ( speed || speed === 0 ) {
                return this.animate( genFx("hide", 3), speed, easing, callback);

            } else {
                for ( var i = 0, j = this.length; i < j; i++ ) {
                    var display = jQuery.css( this[i], "display" );

                    //如果這個元素是隱藏狀態的話, 而且沒有保存原來的顯示值, 會把這個元素最初的顯示值保存起來;
                    if ( display !== "none" && !jQuery._data( this[i], "olddisplay" ) ) {
                        jQuery._data( this[i], "olddisplay", display );
                    }
                }

                // Set the display of the elements in a second loop
                // to avoid the constant reflow
                // 隱藏起來;
                for ( i = 0; i < j; i++ ) {
                    this[i].style.display = "none";
                };

                return this;
            }
        },

        // Save the old toggle function
        _toggle: jQuery.fn.toggle,

        toggle: function( fn, fn2, callback ) {
            var bool = typeof fn === "boolean";

            //臥槽, 這個是跑到click那個toggle去了;
            if ( jQuery.isFunction(fn) && jQuery.isFunction(fn2) ) {
                this._toggle.apply( this, arguments );

                //fn === null  || fn === undefined, 不傳參數的走這個else;
            } else if ( fn == null || bool ) {
                this.each(function() {
                    //有就根據不二值展示, 沒有根據當前進行顯示和隱藏;
                    var state = bool ? fn : jQuery(this).is(":hidden");
                    jQuery(this)[ state ? "show" : "hide" ]();
                });

            } else {
                //別的參數調用動畫去了;
                this.animate(genFx("toggle", 3), fn, fn2, callback);
            };

            return this;
        },

        fadeTo: function( speed, to, easing, callback ) {
            return this.filter(":hidden").css("opacity", 0).show().end()
                .animate({opacity: to}, speed, easing, callback);
        },
        /*
         $("#div1").animate({height:40},1000,"swing",function(){console.log(1)})
         $("#div1").animate({height:400},1000,"swing",function(){console.log(1)})
         */
        animate: function( prop, speed, easing, callback ) {
            /*
             參數被處理后會是這個樣子的, 因為本身就是回調是函數形式, 動畫形式是字符串形式, 經過時間是數字形式, 所以處理還是挺簡單的;
             complete: function () {
             if ( opt.queue !== false ) {
             jQuery(this).dequeue();
             }
             if ( jQuery.isFunction( opt.old ) ) {
             opt.old.call( this );
             }
             },
             duration: 1000,
             easing: "swing",
             old: function (){console.log(1)}
             */
            var optall = jQuery.speed(speed, easing, callback);

            if ( jQuery.isEmptyObject( prop ) ) {
                return this.each( optall.complete );
            };

            //默認的queue是undefined, 這個是動畫大體上形式;
            return this[ optall.queue === false ? "each" : "queue" ](function() {
                // XXX 'this' does not always have a nodeName when running the
                // test suite

                //重新復制了一份opt了;
                var opt = jQuery.extend({}, optall), p,
                    isElement = this.nodeType === 1,
                    hidden = isElement && jQuery(this).is(":hidden"),
                    self = this;

                for ( p in prop ) {
                    //要對屬性轉駝峰;
                    var name = jQuery.camelCase( p );

                    if ( p !== name ) {
                        prop[ name ] = prop[ p ];
                        delete prop[ p ];
                        p = name;
                    };

                    // 
                    if ( prop[p] === "hide" && hidden || prop[p] === "show" && !hidden ) {
                        return opt.complete.call(this);
                    }
                    /*
                     jQ1.4就只有這兩句;
                     if ( ( p === "height" || p === "width" ) && this.style ) {
                     //保存原來的display屬性和overflow屬性;
                     // Store display property
                     opt.display = jQuery.css(this, "display");

                     // Make sure that nothing sneaks out
                     opt.overflow = this.style.overflow;
                     };
                     */
                    if ( isElement && ( p === "height" || p === "width" ) ) {
                        //sneaks out 漸隱;偷偷溜走;
                        // Make sure that nothing sneaks out
                        // Record all 3 overflow attributes because IE does not
                        // change the overflow attribute when overflowX and
                        // overflowY are set to the same value
                        // 記錄這個屬性因為IE改變overflow是不改變overflowX和overflowY為相同的值;
                        opt.overflow = [ this.style.overflow, this.style.overflowX, this.style.overflowY ];

                        // Set display property to inline-block for height/width
                        // animations on inline elements that are having width/height
                        // animated
                        // 要改變width或者height的話必須要是塊元素, 所以我們讓他變成塊元素
                        //如果支持inline-block,就用inlineblock
                        //否知使用inline并設置zoom為1, 觸發元素的hasLayout;
                        if ( jQuery.css( this, "display" ) === "inline" &&
                            jQuery.css( this, "float" ) === "none" ) {
                            if ( !jQuery.support.inlineBlockNeedsLayout ) {
                                this.style.display = "inline-block";

                            } else {
                                var display = defaultDisplay(this.nodeName);

                                // inline-level elements accept inline-block;
                                // block-level elements need to be inline with layout
                                if ( display === "inline" ) {
                                    this.style.display = "inline-block";

                                } else {
                                    this.style.display = "inline";
                                    this.style.zoom = 1;
                                }
                            }
                        }
                    }


                    //$("#div1").animate({height:[1000, "swing"]},1000,function(){console.log(1)})
                    //$("#div1").animate({height:[40, "swing"]},1000,function(){console.log(1)})
                    if ( jQuery.isArray( prop[p] ) ) {
                        // Create (if needed) and add to specialEasing
                        (opt.specialEasing = opt.specialEasing || {})[p] = prop[p][1];
                        prop[p] = prop[p][0];
                    }
                };

                //觸發layout?
                if ( opt.overflow != null ) {
                    this.style.overflow = "hidden";
                }

                //需要改變的值保存到curAnim里面去;
                opt.curAnim = jQuery.extend({}, prop);

                //根據需要改變屬性的對象迭代
                jQuery.each( prop, function( name, val ) {
                    /*
                     self : 當前元素;
                     opt : {
                     complete  : fn,
                     curAnim : {
                     height : "400px"
                     },
                     duration : 1000,
                     easing : "swing",
                     old : fn,
                     overflow : ["","",""]
                     }
                     */
                    var e = new jQuery.fx( self, opt, name );

                    //如果是toggle或者說是hide或者是show的話;
                    if ( rfxtypes.test(val) ) {
                        e[ val === "toggle" ? hidden ? "show" : "hide" : val ]( prop );

                    } else {
                        var parts = rfxnum.exec(val),
                            start = e.cur() || 0;

                        //處理參數, 最后把實際的參數放到動畫實例;
                        if ( parts ) {
                            var end = parseFloat( parts[2] ),
                                unit = parts[3] || "px";

                            // We need to compute starting value
                            // 單位可能是%, em 等不常用的單位;
                            if ( unit !== "px" ) {
                                //設置為需要的單位的最終值;
                                jQuery.style( self, name, (end || 1) + unit);
                                /*計算
                                 //e.cur()而是根據用戶的unit的最終值;
                                 end         result
                                 --------  =  --------
                                 e.cur()       start
                                 result = end/e.cur()*start;

                                 */
                                start = ((end || 1) / e.cur()) * start;
                                //還原單位的初始值;
                                jQuery.style( self, name, start + unit);
                            };

                            // If a +=/-= token was provided, we're doing a relative animation
                            if ( parts[1] ) {
                                end = ((parts[1] === "-=" ? -1 : 1) * end) + start;
                            };

                            //直接放到fx的對列, 讓他跑就好了;
                            e.custom( start, end, unit );

                        } else {
                            e.custom( start, val, "" );
                        }
                    }
                });

                // For JS strict compliance
                return true;
            });
        },

        stop: function( clearQueue, gotoEnd ) {
            var timers = jQuery.timers;

            //如果有clearQueue, 把queue隊列清空
            if ( clearQueue ) {
                this.queue([]);
            };

            //把jQ下的timers時間列表給刪除
            this.each(function() {
                // go in reverse order so anything added to the queue during the loop is ignored
                for ( var i = timers.length - 1; i >= 0; i-- ) {
                    if ( timers[i].elem === this ) {
                        if (gotoEnd) {
                            // force the next step to be the last
                            timers[i](true);
                        };
                        timers.splice(i, 1);
                    };
                };
            });

            // start the next in the queue if the last step wasn't forced
            if ( !gotoEnd ) {
                this.dequeue();
            }

            return this;
        }

    });
    /*
     JSON.stringify(genFx("show", 1),null,4)
     "{
     "height": "show",
     "marginTop": "show",
     "marginBottom": "show",
     "paddingTop": "show",
     "paddingBottom": "show"
     }";

     JSON.stringify(genFx("show", 3),null,4)
     "{
     "height": "show",
     "marginTop": "show",
     "marginBottom": "show",
     "paddingTop": "show",
     "paddingBottom": "show",
     "width": "show",
     "marginLeft": "show",
     "marginRight": "show",
     "paddingLeft": "show",
     "paddingRight": "show",
     "opacity": "show"
     }"
     */
    function genFx( type, num ) {
        var obj = {};
        /*
         fxAttrs = [
         // height animations
         [ "height", "marginTop", "marginBottom", "paddingTop", "paddingBottom" ],
         // width animations
         [ "width", "marginLeft", "marginRight", "paddingLeft", "paddingRight" ],
         // opacity animations
         [ "opacity" ]
         ]
         */
        jQuery.each( fxAttrs.concat.apply([], fxAttrs.slice(0,num)), function() {
            obj[ this ] = type;
        });

        return obj;
    }

    // Generate shortcuts for custom animations
    /*  比如你要讓一個元素slideDown
     那么這個元素的height,margintop marginbottom paddingtop padding-bottom都要一個個變小, 為這個元素的這幾個屬性添加變小的定時器;
     */
    jQuery.each({
        slideDown: genFx("show", 1),
        slideUp: genFx("hide", 1),
        slideToggle: genFx("toggle", 1),
        fadeIn: { opacity: "show" },
        fadeOut: { opacity: "hide" },
        fadeToggle: { opacity: "toggle" }
    }, function( name, props ) {
        jQuery.fn[ name ] = function( speed, easing, callback ) {
            return this.animate( props, speed, easing, callback );
        };
    });

    //初始化變量, 用戶可以傳 number(動畫時間), string(動畫形式), function(動畫完成的回調)
    jQuery.extend({
        speed: function( speed, easing, fn ) {
            var opt = speed && typeof speed === "object" ? jQuery.extend({}, speed) : {
                complete: fn || !fn && easing ||
                    jQuery.isFunction( speed ) && speed,
                duration: speed,
                easing: fn && easing || easing && !jQuery.isFunction(easing) && easing
            };

            opt.duration = jQuery.fx.off ? 0 : typeof opt.duration === "number" ? opt.duration :
                opt.duration in jQuery.fx.speeds ? jQuery.fx.speeds[opt.duration] : jQuery.fx.speeds._default;

            // Queueing
            opt.old = opt.complete;
            opt.complete = function() {
                if ( opt.queue !== false ) {
                    jQuery(this).dequeue();
                }
                if ( jQuery.isFunction( opt.old ) ) {
                    opt.old.call( this );
                }
            };

            return opt;
        },

        easing: {
            linear: function( p, n, firstNum, diff ) {
                return firstNum + diff * p;
            },
            swing: function( p, n, firstNum, diff ) {
                return ((-Math.cos(p*Math.PI)/2) + 0.5) * diff + firstNum;
            }
        },

        timers: [],

        //fx動畫的函數, 很重要, 所有的動畫開始結束都是基于這個函數的;
        fx: function( elem, options, prop ) {
            this.options = options;
            this.elem = elem;
            this.prop = prop;

            if ( !options.orig ) {
                options.orig = {};
            }
        }

    });

    jQuery.fx.prototype = {
        // Simple function for setting a style value
        // 更新元素的fx.prop為fx.now;
        update: function() {
            if ( this.options.step ) {
                this.options.step.call( this.elem, this.now, this );
            }
            //opacity和默認動畫 ,  你也可以把為這個spep設置width或者高的運動step
            (jQuery.fx.step[this.prop] || jQuery.fx.step._default)( this );
        },

        // Get the current size
        // 通過$.css獲取當前樣式;
        cur: function() {
            if ( this.elem[this.prop] != null && (!this.elem.style || this.elem.style[this.prop] == null) ) {
                return this.elem[ this.prop ];
            }

            var r = parseFloat( jQuery.css( this.elem, this.prop ) );
            return r || 0;
        },

        // Start an animation from one number to another
        custom: function( from, to, unit ) {
            var self = this,
                fx = jQuery.fx;

            this.startTime = jQuery.now();
            this.start = from;
            this.end = to;
            this.unit = unit || this.unit || "px";
            this.now = this.start;
            this.pos = this.state = 0;

            function t( gotoEnd ) {
                return self.step(gotoEnd);
            }

            t.elem = this.elem;

            //只要t返回ture那么times就會push這個step, !timerId時候才添加, 只要一個線程就好了, 提高性能;
            if ( t() && jQuery.timers.push(t) && !timerId ) {
                //timerId和fx是在同一個作用域的;
                timerId = setInterval(fx.tick, fx.interval);
            }
        },

        // Simple 'show' function
        show: function() {
            // Remember where we started, so that we can go back to it later
            this.options.orig[this.prop] = jQuery.style( this.elem, this.prop );
            this.options.show = true;

            // Begin the animation
            // Make sure that we start at a small width/height to avoid any
            // flash of content
            this.custom(this.prop === "width" || this.prop === "height" ? 1 : 0, this.cur());

            // Start by showing the element
            jQuery( this.elem ).show();
        },

        // Simple 'hide' function
        hide: function() {
            // Remember where we started, so that we can go back to it later
            this.options.orig[this.prop] = jQuery.style( this.elem, this.prop );
            this.options.hide = true;

            // Begin the animation
            this.custom(this.cur(), 0);
        },

        // Each step of an animation
        //如果動畫完成了就返回ture, 否知返回false
        step: function( gotoEnd ) {
            //假設當前的這個屬性的動畫完成了
            var t = jQuery.now(), done = true;

            //jQ的動畫是根據時間進行的, 所以這邊要判斷一下時間是否超過預期的時間;
            if ( gotoEnd || t >= this.options.duration + this.startTime ) {
                this.now = this.end;
                this.pos = this.state = 1;
                this.update();

                this.options.curAnim[ this.prop ] = true;
                //this.options保存了原來的opt參數, 只要有一個屬性動畫沒完成,動畫就是未完成;
                for ( var i in this.options.curAnim ) {
                    if ( this.options.curAnim[i] !== true ) {
                        done = false;
                    };
                };

                // 如果動畫完成了就把這個元素原來的屬性賦值到元素;
                if ( done ) {
                    // Reset the overflow
                    if ( this.options.overflow != null && !jQuery.support.shrinkWrapBlocks ) {
                        var elem = this.elem,
                            options = this.options;

                        jQuery.each( [ "", "X", "Y" ], function (index, value) {
                            elem.style[ "overflow" + value ] = options.overflow[index];
                        } );
                    };

                    // Hide the element if the "hide" operation was done
                    if ( this.options.hide ) {
                        jQuery(this.elem).hide();
                    };

                    // 把結果值再賦值一遍;
                    // Reset the properties, if the item has been hidden or shown
                    if ( this.options.hide || this.options.show ) {
                        for ( var p in this.options.curAnim ) {
                            jQuery.style( this.elem, p, this.options.orig[p] );
                        };
                    };

                    // done的話就走complete;
                    // Execute the complete function
                    this.options.complete.call( this.elem );
                };
                //
                return false;

            } else {
                // 根據經過的時間表示進度;
                var n = t - this.startTime;
                this.state = n / this.options.duration;

                // 
                // Perform the easing function, defaults to swing
                var specialEasing = this.options.specialEasing && this.options.specialEasing[this.prop];
                var defaultEasing = this.options.easing || (jQuery.easing.swing ? "swing" : "linear");

                //進度
                //經過的時間
                //總的時間;
                this.pos = jQuery.easing[specialEasing || defaultEasing](this.state, n, 0, 1, this.options.duration);

                //計算結果的值;
                this.now = this.start + ((this.end - this.start) * this.pos);

                // debugger;
                // Perform the next step of the animation
                // 把值更新到元素上;
                this.update();
            }

            return true;
        }
    };

    jQuery.extend( jQuery.fx, {
        //這個tick是一個定時器, 只要有要運動元素, 這個定時器都在跑
        tick: function() {
            var timers = jQuery.timers;

            for ( var i = 0; i < timers.length; i++ ) {
                //timers保存的是每一個元素改變樣式的step閉包, 如果元素的執行完成就會返回false, 那么這個改變的線程就會被刪除;
                if ( !timers[i]() ) {
                    timers.splice(i--, 1);
                };
            };

            //如果沒有了就會清空timers, fx.stopstop就在這個方法的后面;
            if ( !timers.length ) {
                jQuery.fx.stop();
            };
        },

        interval: 13,

        stop: function() {
            clearInterval( timerId );
            timerId = null;
        },

        speeds: {
            slow: 600,
            fast: 200,
            // Default speed
            _default: 400
        },

        //fx.step 這個可以添加;
        step: {
            opacity: function( fx ) {
                jQuery.style( fx.elem, "opacity", fx.now );
            },

            _default: function( fx ) {
                if ( fx.elem.style && fx.elem.style[ fx.prop ] != null ) {
                    fx.elem.style[ fx.prop ] = (fx.prop === "width" || fx.prop === "height" ? Math.max(0, fx.now) : fx.now) + fx.unit;
                } else {
                    fx.elem[ fx.prop ] = fx.now;
                }
            }
        }
    });

    if ( jQuery.expr && jQuery.expr.filters ) {
        jQuery.expr.filters.animated = function( elem ) {
            return jQuery.grep(jQuery.timers, function( fn ) {
                return elem === fn.elem;
            }).length;
        };
    }

    function defaultDisplay( nodeName ) {
        //緩沖elemdisplay, 優化,避免下一次再跑同樣的標簽;
        if ( !elemdisplay[ nodeName ] ) {
            var elem = jQuery("<" + nodeName + ">").appendTo("body"),
                display = elem.css("display");

            elem.remove();

            if ( display === "none" || display === "" ) {
                display = "block";
            };

            elemdisplay[ nodeName ] = display;
        };

        return elemdisplay[ nodeName ];
    };



    //匹配標簽是table或者是td或者是th這種標簽;
    var rtable = /^t(?:able|d|h)$/i,
    //匹配是body或者是html標簽;
        rroot = /^(?:body|html)$/i;

    //jQ真的很細心, 工作和生活其實都要這樣才能有成就啊;

    //getBoundingClientRect雖然是ie的東西,但是在任何瀏覽器都是全兼容的, 所以可以放心使用, 通過這個東東計算寬高很快的;
    if ( "getBoundingClientRect" in document.documentElement ) {

        //返回相對于body的top和left;
        jQuery.fn.offset = function( options ) {
            //
            var elem = this[0], box;

            //如果有傳參數, 就是設置值;
            if ( options ) {
                return this.each(function( i ) {
                    jQuery.offset.setOffset( this, options, i );
                });
            };

            //不存在elem還玩個毛;
            if ( !elem || !elem.ownerDocument ) {
                return null;
            }

            //如果是body進行特殊判斷;
            if ( elem === elem.ownerDocument.body ) {
                //是body的話直接返回body的left和top,默認的的8pxmargin
                return jQuery.offset.bodyOffset( elem );
            };

            //獲取這個元素相對于這個界面的left和top;
            try {
                box = elem.getBoundingClientRect();
            } catch(e) {};

            var doc = elem.ownerDocument,
                docElem = doc.documentElement;

            // Make sure we're not dealing with a disconnected DOM node
            // 如果box這個沒返回,  或者box的ownerDocument不包含這個box[in the fragmentDocument : document.createDocumentFragment()]
            if ( !box || !jQuery.contains( docElem, elem ) ) {
                //如果這個元素不在dom里面也有top和left嗎?
                return box ? { top: box.top, left: box.left } : { top: 0, left: 0 };
            };

            //
            var body = doc.body,
                win = getWindow(doc),
            //IE的documentElement是不顯示的, 只有body;
                clientTop  = docElem.clientTop  || body.clientTop  || 0,
                clientLeft = docElem.clientLeft || body.clientLeft || 0,
                scrollTop  = (win.pageYOffset || jQuery.support.boxModel && docElem.scrollTop  || body.scrollTop ),
                scrollLeft = (win.pageXOffset || jQuery.support.boxModel && docElem.scrollLeft || body.scrollLeft),
            //去除border的高度, 因為滾動條包含border的;
                top  = box.top  + scrollTop  - clientTop,
                left = box.left + scrollLeft - clientLeft;

            return { top: top, left: left };
        };

    } else {
        //沒有getBoundingClientRect
        jQuery.fn.offset = function( options ) {
            var elem = this[0];

            if ( options ) {
                return this.each(function( i ) {
                    jQuery.offset.setOffset( this, options, i );
                });
            }

            if ( !elem || !elem.ownerDocument ) {
                return null;
            }

            if ( elem === elem.ownerDocument.body ) {
                return jQuery.offset.bodyOffset( elem );
            }

            //獲得關于offset相關的瀏覽器特征
            jQuery.offset.initialize();

            var computedStyle,
                offsetParent = elem.offsetParent,
                prevOffsetParent = elem,
                doc = elem.ownerDocument,
                docElem = doc.documentElement,
                body = doc.body,
                defaultView = doc.defaultView,
                prevComputedStyle = defaultView ? defaultView.getComputedStyle( elem, null ) : elem.currentStyle,
                //從當前元素的offset開始(再重復一次, offset是從border開始的(不包含border,即margin-box);
                top = elem.offsetTop,
                left = elem.offsetLeft;

            //不用getBoundingClientRect獲取offset很麻煩;
            //迭代父級, 而不是迭代offsetParent哦, 因為parentNode如果有scroll的話計算出來的offsetLeft或者offsetTop不準確), 直到body或者html標簽;
            //迭代每一個父級,對于每一個父級的scroll和border進行特殊處理,
            while ( (elem = elem.parentNode) && elem !== body && elem !== docElem ) {
                //fix的標簽是根據界面定位的, 一定要例外處理, 否則計算出的值有誤;;
                if ( jQuery.offset.supportsFixedPosition && prevComputedStyle.position === "fixed" ) {
                    break;
                };

                computedStyle = defaultView ? defaultView.getComputedStyle(elem, null) : elem.currentStyle;
                //因為我們要計算的是相對界面的top和left, 所以要把對所有有滾動的父級進行處理(減去處理);
                top  -= elem.scrollTop;
                left -= elem.scrollLeft;

                //對有定位的offsetParent才處理,(要弄懂offsetParent的元素是有定位的元素,比如absolute或者是relative的元素);
                if ( elem === offsetParent ) {
                    top  += elem.offsetTop;
                    left += elem.offsetLeft;
                                       //offset應該返回的是border-box,但在一些表格元素卻沒有計算它們的border值,需要自行添加
                                       //offset是指從當前的margin-box(包含margin)到父級的border-box(包含border-box),有些瀏覽器的offset不包含border, 要注意, 所以也要特殊處理;
                                                            //又對table進行特殊處理
                    if ( jQuery.offset.doesNotAddBorder && !(jQuery.offset.doesAddBorderForTableAndCells && rtable.test(elem.nodeName)) ) {
                        top  += parseFloat( computedStyle.borderTopWidth  ) || 0;
                        left += parseFloat( computedStyle.borderLeftWidth ) || 0;
                    };

                    prevOffsetParent = offsetParent;
                    //把offsetParent給offsetParent
                    offsetParent = elem.offsetParent;
                }

                //修正safari的錯誤
                if ( jQuery.offset.subtractsBorderForOverflowNotVisible && computedStyle.overflow !== "visible" ) {
                    top  += parseFloat( computedStyle.borderTopWidth  ) || 0;
                    left += parseFloat( computedStyle.borderLeftWidth ) || 0;
                };

                prevComputedStyle = computedStyle;
            }

            //最后加上body的偏移
            if ( prevComputedStyle.position === "relative" || prevComputedStyle.position === "static" ) {
                top  += body.offsetTop;
                left += body.offsetLeft;
            }

            //如果元素使用了fix定位, 要加上最大滾動距離;
            if ( jQuery.offset.supportsFixedPosition && prevComputedStyle.position === "fixed" ) {
                top  += Math.max( docElem.scrollTop, body.scrollTop );
                left += Math.max( docElem.scrollLeft, body.scrollLeft );
            }

            return { top: top, left: left };
        };
    }

    jQuery.offset = {
        //一些兼容檢測, 坑多, 太多了;
        initialize: function() {
            var body = document.body, container = document.createElement("div"), innerDiv, checkDiv, table, td, bodyMarginTop = parseFloat( jQuery.css(body, "marginTop") ) || 0,
                html = "<div style='position:absolute;top:0;left:0;margin:0;border:5px solid #000;padding:0;width:1px;height:1px;'><div></div></div><table style='position:absolute;top:0;left:0;margin:0;border:5px solid #000;padding:0;width:1px;height:1px;' cellpadding='0' cellspacing='0'><tr><td></td></tr></table>";

            jQuery.extend( container.style, { position: "absolute", top: 0, left: 0, margin: 0, border: 0, width: "1px", height: "1px", visibility: "hidden" } );

            container.innerHTML = html;
            body.insertBefore( container, body.firstChild );
            innerDiv = container.firstChild;
            checkDiv = innerDiv.firstChild;
            td = innerDiv.nextSibling.firstChild.firstChild;

            this.doesNotAddBorder = (checkDiv.offsetTop !== 5);
            this.doesAddBorderForTableAndCells = (td.offsetTop === 5);

            checkDiv.style.position = "fixed";
            checkDiv.style.top = "20px";

            // safari subtracts parent border width here which is 5px
            this.supportsFixedPosition = (checkDiv.offsetTop === 20 || checkDiv.offsetTop === 15);
            checkDiv.style.position = checkDiv.style.top = "";

            innerDiv.style.overflow = "hidden";
            innerDiv.style.position = "relative";

            this.subtractsBorderForOverflowNotVisible = (checkDiv.offsetTop === -5);

            this.doesNotIncludeMarginInBodyOffset = (body.offsetTop !== bodyMarginTop);

            body.removeChild( container );
            body = container = innerDiv = checkDiv = table = td = null;
            jQuery.offset.initialize = jQuery.noop;
        },

        bodyOffset: function( body ) {
            var top = body.offsetTop,
                left = body.offsetLeft;

            //兼容檢測;
            jQuery.offset.initialize();

            //offsetLeft offsetTop默認就是不包含當前元素的margin,  可能向前的瀏覽器不給力;
            if ( jQuery.offset.doesNotIncludeMarginInBodyOffset ) {
                top  += parseFloat( jQuery.css(body, "marginTop") ) || 0;
                left += parseFloat( jQuery.css(body, "marginLeft") ) || 0;
            }

            return { top: top, left: left };
        },

        //這個還是jQ的工具方法;
        setOffset: function( elem, options, i ) {
            var position = jQuery.css( elem, "position" );

            //如果元素沒有指定position的值, 那么默認的返回為static;
            // set position first, in-case top/left are set even on static elem
            if ( position === "static" ) {
                elem.style.position = "relative";
            };

            //獲取默認值;
            var curElem = jQuery( elem ),
                curOffset = curElem.offset(),
                curCSSTop = jQuery.css( elem, "top" ),
                curCSSLeft = jQuery.css( elem, "left" ),
                //如果有個定位的值是auto;
                calculatePosition = (position === "absolute" && jQuery.inArray('auto', [curCSSTop, curCSSLeft]) > -1),
                props = {}, curPosition = {}, curTop, curLeft;

            // 需要計算值, 這個值是通過有定位的父級的position計算出相對的值;
            // need to be able to calculate position if either top or left is auto and position is absolute
            if ( calculatePosition ) {
                curPosition = curElem.position();
            };

            // 通過計算獲取的值優先用, 否者用計算后樣式;
            curTop  = calculatePosition ? curPosition.top  : parseInt( curCSSTop,  10 ) || 0;
            curLeft = calculatePosition ? curPosition.left : parseInt( curCSSLeft, 10 ) || 0;

            // 判斷是不是函數
            if ( jQuery.isFunction( options ) ) {
                options = options.call( elem, i, curOffset );
            };

            //
            if (options.top != null) {
               // 這里是通過$.css設置定位, 所以有個恒等式 : 設置后的相對界面的位置 - 設置前元素相對界面的位置 = 設置后元素相對父級的位置 - 設置前元素相對父級的位置
               // 我們要求的是設置后相對父級的位置, 把這個東東倒一下就好了;
               //我們要設置的offset, 相對界面的位置
                            //用戶設置的值
                                           //相對界面的定位
                                                            //相對父級的值;
                props.top = (options.top - curOffset.top) + curTop;
            }
            if (options.left != null) {
                props.left = (options.left - curOffset.left) + curLeft;
            }

            //可以傳一個using的方法, 會把元素和 最后的樣式作為參數傳進去;
            if ( "using" in options ) {
                options.using.call( elem, props );
            } else {
                curElem.css( props );
            }
        }
    };


    jQuery.fn.extend({
        // position是獲取相對有定位父級的位置;
        position: function() {
            if ( !this[0] ) {
                return null;
            }

            var elem = this[0],

            // Get *real* offsetParent
                offsetParent = this.offsetParent(),

            // Get correct offsets
                offset       = this.offset(),
            //如果是html或者body就把left和top設置為0, 不要忘記了元素的定位是包含margin的,這個很重要,而且子元素的定位是根據父級的padding-box進行設置的;
                //元素的offset是從border-box開始的
                parentOffset = rroot.test(offsetParent[0].nodeName) ? { top: 0, left: 0 } : offsetParent.offset();

            // Subtract element margins
            // note: when an element has margin: auto the offsetLeft and marginLeft
            // are the same in Safari causing offset.left to incorrectly be 0
            offset.top  -= parseFloat( jQuery.css(elem, "marginTop") ) || 0;
            offset.left -= parseFloat( jQuery.css(elem, "marginLeft") ) || 0;

            // Add offsetParent borders
            parentOffset.top  += parseFloat( jQuery.css(offsetParent[0], "borderTopWidth") ) || 0;
            parentOffset.left += parseFloat( jQuery.css(offsetParent[0], "borderLeftWidth") ) || 0;

            // Subtract the two offsets
            return {
                //元素的offset是從border-box開始的
                //position是指這個元素的margin定點到padding-box(包含padding)的距離, 所以要計算該元素和父級元素的offset, 將這兩個元素的offset相減, 當前的值是包含了 當前元素的margin的且 不包含父級元素border的,所以要重新計算
                //這里最好畫圖,要么頭會暈, 而且個個屬性的定義要弄清楚;
                top:  offset.top  - parentOffset.top,
                left: offset.left - parentOffset.left
            };
        },

        offsetParent: function() {
            return this.map(function() {
                var offsetParent = this.offsetParent || document.body;
                //如果元素的定位為static而且不是根元素, 重新定義offsetParent, 應該是某些瀏覽器會返回position為static的offstParent;
                while ( offsetParent && (!rroot.test(offsetParent.nodeName) && jQuery.css(offsetParent, "position") === "static") ) {
                    offsetParent = offsetParent.offsetParent;
                };
                return offsetParent;
            });
        }
    });


// Create scrollLeft and scrollTop methods
    jQuery.each( ["Left", "Top"], function( i, name ) {
        var method = "scroll" + name;
        //減少代碼而設置的閉包;
        //scrollLeft;
        //scrollTop
        jQuery.fn[ method ] = function(val) {
            var elem = this[0], win;

            //參數檢測;
            if ( !elem ) {
                return null;
            }

            //設置值;
            if ( val !== undefined ) {
                // Set the scroll offset
                return this.each(function() {
                    win = getWindow( this );

                    //如果是window就是設置滾動的值;
                    if ( win ) {
                        win.scrollTo(
                            !i ? val : jQuery(win).scrollLeft(),
                            i ? val : jQuery(win).scrollTop()
                        );

                    } else {
                        //this.scrollTop, this.scrollLeft;
                        this[ method ] = val;
                    }
                });
            } else {
                // 獲取
                win = getWindow( elem );

                // Return the scroll offset
                //瀏覽器的能力檢測;
                return win ? ("pageXOffset" in win) ? win[ i ? "pageYOffset" : "pageXOffset" ] :
                    jQuery.support.boxModel && win.document.documentElement[ method ] ||
                        win.document.body[ method ] :
                    //直接返回
                    elem[ method ];
            }
        };
    });

    //你不能傳一個nodeType為1的元素進來;
    function getWindow( elem ) {
        return jQuery.isWindow( elem ) ?
            elem :
            //documentNodeType ==》》 9
            elem.nodeType === 9 ?
                elem.defaultView || elem.parentWindow :
                false;
    }




// Create innerHeight, innerWidth, outerHeight and outerWidth methods
    jQuery.each([ "Height", "Width" ], function( i, name ) {
        //又是一個閉包;
        var type = name.toLowerCase();

        // innerHeight and innerWidth
        jQuery.fn["inner" + name] = function() {
            //innerWidth是包含padding的, 引用css的方法, 傳的是padding
            return this[0] ?
                parseFloat( jQuery.css( this[0], type, "padding" ) ) :
                null;
        };

        // outerHeight and outerWidth
        // 獲取的是borderBox的寬或者高度,可以傳true獲取的是包含margin的寬和高;
        jQuery.fn["outer" + name] = function( margin ) {
            return this[0] ?
                parseFloat( jQuery.css( this[0], type, margin ? "margin" : "border" ) ) :
                null;
        };

        //type為 width或者是height;
        jQuery.fn[ type ] = function( size ) {
            // Get window width or height
            var elem = this[0];
            //避免錯誤;
            if ( !elem ) {
                return size == null ? null : this;
            }

            //對函數進行處理;
            if ( jQuery.isFunction( size ) ) {
                return this.each(function( i ) {
                    var self = jQuery( this );
                                                       //引用自身, 把計算后值傳到回調方法里;
                    self[ type ]( size.call( this, i, self[ type ]() ) );
                });
            };

            // 是window的話
            if ( jQuery.isWindow( elem ) ) {
                // Everyone else use document.documentElement or document.body depending on Quirks vs Standards mode
                // 3rd condition allows Nokia support, as it supports the docElem prop but not CSS1Compat
                //標準瀏覽器的用戶寬和高是就是html標簽的寬高;
                var docElemProp = elem.document.documentElement[ "client" + name ];
                        //標準瀏覽器會走第一個
                return elem.document.compatMode === "CSS1Compat" && docElemProp ||
                    //IE的詭異模式會走這一個
                                                            //這個就不知道什么情況了;
                    elem.document.body[ "client" + name ] || docElemProp;

                // Get document width or height
            } else if ( elem.nodeType === 9 ) {
                // Either scroll[Width/Height] or offset[Width/Height], whichever is greater
                //計算documentElemnt的最大值;
                return Math.max(
                    elem.documentElement["client" + name],
                    elem.body["scroll" + name], elem.documentElement["scroll" + name],
                    elem.body["offset" + name], elem.documentElement["offset" + name]
                );

                // Get or set width or height on the element
            //這個是獲取;
            } else if ( size === undefined ) {
                var orig = jQuery.css( elem, type ),
                    ret = parseFloat( orig );
                    //parseFlaot是NaN應該是auto這種情況;
                return jQuery.isNaN( ret ) ? orig : ret;

                // Set the width or height on the element (default to pixels if value is unitless)
            //最后走設置
            } else {
                //width或者是高;
                return this.css( type, typeof size === "string" ? size : size + "px" );
            }
        };

    });

})(window);

  Sizzle被我拿掉了;

   END, 祝自己周末愉快( ¯(∞)¯ )


文章列表

arrow
arrow
    全站熱搜
    創作者介紹
    創作者 大師兄 的頭像
    大師兄

    IT工程師數位筆記本

    大師兄 發表在 痞客邦 留言(0) 人氣()