jQ1.5源碼註釋以及解讀RE

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

 

  相對與1.4版本的ajax部分, 整個進行了重寫, 實在是坑爹,  如今還有不少沒弄懂,  ajax能夠很是簡單地:php

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

  也能夠一樣能夠寫幾千行,  全部的$.get(),  $.post(), $.getJSON(),  $.getScript().... 都是$.ajax的shortcut, 全部最後都是經過$.ajax 這個方法統一處理, 並且多了傳送器這東西, 一會兒以爲好酷炫, 並且查資料查到司徒大神2011就開始瞭解這些東西, 本身實在差太多了, 你們都要加油才行;css

    //匹配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裏面, 根據先進先出的方式一次次執行, 並且傳的參數也挺人性化無論你各個參數的排序怎麼樣, 最後都會自動修正, 正常的是這樣的html

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

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

 

        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;
            }
        }

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

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

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

  這些很是簡單, 真正可貴在代碼裏面:react

    //保存默認的顯示設置的變量;
    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的各個寬高值不清楚的看這個會比較麻煩:jquery

    //匹配標籤是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了;c++

  

/*!
 * 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, 祝本身週末愉快( ¯(∞)¯ )

相關文章
相關標籤/搜索