第五节:JQuery框架源码简析

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了第五节:JQuery框架源码简析相关的知识,希望对你有一定的参考价值。

(续1)


5、选择器Sizzle


我们把选择器Sizzle实现的代码放在文件jquery.sizzle.js中。关于选择器Sizzle的说明,请看《jQuery技术内幕:深入解析jQuery架构设计与实现原理》,这里不再赘述了。


/*!

 * Sizzle CSS Selector Engine

 *  Copyright 2011, The Dojo Foundation

 *  Released under the MIT, BSD, and GPL Licenses.

 *  More information: http://sizzlejs.com/

 */

function jQuery_Sizzle(jQuery){

var chunker = /((?:\((?:\([^()]+\)|[^()]+)+\)|\[(?:\[[^\[\]]*\]|[‘"][^‘"]*[‘"]|[^\[\]‘"]+)+\]|\\.|[^ >+~,(\[\\]+)+|[>+~])(\s*,\s*)?((?:.|\r|\n)*)/g,

    expando = "sizcache" + (Math.random() + ‘‘).replace(‘.‘, ‘‘),

    done = 0,

    toString = Object.prototype.toString,

    hasDuplicate = false,

    baseHasDuplicate = true,

    rBackslash = /\\/g,

    rReturn = /\r\n/g,

    rNonWord = /\W/;

// Here we check if the javascript engine is using some sort of

// optimization where it does not always call our comparision

// function. If that is the case, discard the hasDuplicate value.

//   Thus far that includes Google Chrome.

[0, 0].sort(function() {

    baseHasDuplicate = false;

    return 0;

});

var Sizzle = function( selector, context, results, seed ) {

    results = results || [];

    context = context || document;

    var origContext = context;

    if ( context.nodeType !== 1 && context.nodeType !== 9 ) {

        return [];

    }

    if ( !selector || typeof selector !== "string" ) {

        return results;

    }

    var m, set, checkSet, extra, ret, cur, pop, i,

        prune = true,

        contextXML = Sizzle.isXML( context ),

        parts = [],

        soFar = selector;

    // Reset the position of the chunker regexp (start from head)

    do {

        chunker.exec( "" );

        m = chunker.exec( soFar );

        if ( m ) {

            soFar = m[3];

            parts.push( m[1] );

            if ( m[2] ) {

                extra = m[3];

                break;

            }

        }

    } while ( m );

    if ( parts.length > 1 && origPOS.exec( selector ) ) {

        if ( parts.length === 2 && Expr.relative[ parts[0] ] ) {

            set = posProcess( parts[0] + parts[1], context, seed );

        } else {

            set = Expr.relative[ parts[0] ] ?

                [ context ] :

                Sizzle( parts.shift(), context );

            while ( parts.length ) {

                selector = parts.shift();

                if ( Expr.relative[ selector ] ) {

                    selector += parts.shift();

                }

                set = posProcess( selector, set, seed );

            }

        }

    } else {

        // Take a shortcut and set the context if the root selector is an ID

        // (but not if it‘ll be faster if the inner selector is an ID)

        if ( !seed && parts.length > 1 && context.nodeType === 9 && !contextXML &&

                Expr.match.ID.test(parts[0]) && !Expr.match.ID.test(parts[parts.length - 1]) ) {

            ret = Sizzle.find( parts.shift(), context, contextXML );

            context = ret.expr ?

                Sizzle.filter( ret.expr, ret.set )[0] :

                ret.set[0];

        }

        if ( context ) {

            ret = seed ?

                { expr: parts.pop(), set: makeArray(seed) } :

                Sizzle.find( parts.pop(), parts.length === 1 && (parts[0] === "~" || parts[0] === "+") && context.parentNode ? context.parentNode : context, contextXML );

            set = ret.expr ?

                Sizzle.filter( ret.expr, ret.set ) :

                ret.set;

            if ( parts.length > 0 ) {

                checkSet = makeArray( set );

            } else {

                prune = false;

            }

            while ( parts.length ) {

                cur = parts.pop();

                pop = cur;

                if ( !Expr.relative[ cur ] ) {

                    cur = "";

                } else {

                    pop = parts.pop();

                }

                if ( pop == null ) {

                    pop = context;

                }

                Expr.relative[ cur ]( checkSet, pop, contextXML );

            }

        } else {

            checkSet = parts = [];

        }

    }

    if ( !checkSet ) {

        checkSet = set;

    }

    if ( !checkSet ) {

        Sizzle.error( cur || selector );

    }

    if ( toString.call(checkSet) === "[object Array]" ) {

        if ( !prune ) {

            results.push.apply( results, checkSet );

        } else if ( context && context.nodeType === 1 ) {

            for ( i = 0; checkSet[i] != null; i++ ) {

                if ( checkSet[i] && (checkSet[i] === true || checkSet[i].nodeType === 1 && Sizzle.contains(context, checkSet[i])) ) {

                    results.push( set[i] );

                }

            }

        } else {

            for ( i = 0; checkSet[i] != null; i++ ) {

                if ( checkSet[i] && checkSet[i].nodeType === 1 ) {

                    results.push( set[i] );

                }

            }

        }

    } else {

        makeArray( checkSet, results );

    }

    if ( extra ) {

        Sizzle( extra, origContext, results, seed );

        Sizzle.uniqueSort( results );

    }

    return results;

};

Sizzle.uniqueSort = function( results ) {

    if ( sortOrder ) {

        hasDuplicate = baseHasDuplicate;

        results.sort( sortOrder );

        if ( hasDuplicate ) {

            for ( var i = 1; i < results.length; i++ ) {

                if ( results[i] === results[ i - 1 ] ) {

                    results.splice( i--, 1 );

                }

            }

        }

    }

    return results;

};

Sizzle.matches = function( expr, set ) {

    return Sizzle( expr, null, null, set );

};

Sizzle.matchesSelector = function( node, expr ) {

    return Sizzle( expr, null, null, [node] ).length > 0;

};

Sizzle.find = function( expr, context, isXML ) {

    var set, i, len, match, type, left;

    if ( !expr ) {

        return [];

    }

    for ( i = 0, len = Expr.order.length; i < len; i++ ) {

        type = Expr.order[i];

        if ( (match = Expr.leftMatch[ type ].exec( expr )) ) {

            left = match[1];

            match.splice( 1, 1 );

            if ( left.substr( left.length - 1 ) !== "\\" ) {

                match[1] = (match[1] || "").replace( rBackslash, "" );

                set = Expr.find[ type ]( match, context, isXML );

                if ( set != null ) {

                    expr = expr.replace( Expr.match[ type ], "" );

                    break;

                }

            }

        }

    }

    if ( !set ) {

        set = typeof context.getElementsByTagName !== "undefined" ?

            context.getElementsByTagName( "*" ) :

            [];

    }

    return { set: set, expr: expr };

};

Sizzle.filter = function( expr, set, inplace, not ) {

    var match, anyFound,

        type, found, item, filter, left,

        i, pass,

        old = expr,

        result = [],

        curLoop = set,

        isXMLFilter = set && set[0] && Sizzle.isXML( set[0] );

    while ( expr && set.length ) {

        for ( type in Expr.filter ) {

            if ( (match = Expr.leftMatch[ type ].exec( expr )) != null && match[2] ) {

                filter = Expr.filter[ type ];

                left = match[1];

                anyFound = false;

                match.splice(1,1);

                if ( left.substr( left.length - 1 ) === "\\" ) {

                    continue;

                }

                if ( curLoop === result ) {

                    result = [];

                }

                if ( Expr.preFilter[ type ] ) {

                    match = Expr.preFilter[ type ]( match, curLoop, inplace, result, not, isXMLFilter );

                    if ( !match ) {

                        anyFound = found = true;

                    } else if ( match === true ) {

                        continue;

                    }

                }

                if ( match ) {

                    for ( i = 0; (item = curLoop[i]) != null; i++ ) {

                        if ( item ) {

                            found = filter( item, match, i, curLoop );

                            pass = not ^ found;

                            if ( inplace && found != null ) {

                                if ( pass ) {

                                    anyFound = true;

                                } else {

                                    curLoop[i] = false;

                                }

                            } else if ( pass ) {

                                result.push( item );

                                anyFound = true;

                            }

                        }

                    }

                }

                if ( found !== undefined ) {

                    if ( !inplace ) {

                        curLoop = result;

                    }

                    expr = expr.replace( Expr.match[ type ], "" );

                    if ( !anyFound ) {

                        return [];

                    }

                    break;

                }

            }

        }

        // Improper expression

        if ( expr === old ) {

            if ( anyFound == null ) {

                Sizzle.error( expr );

            } else {

                break;

            }

        }

        old = expr;

    }

    return curLoop;

};

Sizzle.error = function( msg ) {

    throw new Error( "Syntax error, unrecognized expression: " + msg );

};

/**

 * Utility function for retreiving the text value of an array of DOM nodes

 * @param {Array|Element} elem

 */

var getText = Sizzle.getText = function( elem ) {

    var i, node,

        nodeType = elem.nodeType,

        ret = "";

    if ( nodeType ) {

        if ( nodeType === 1 || nodeType === 9 ) {

            // Use textContent || innerText for elements

            if ( typeof elem.textContent === ‘string‘ ) {

                return elem.textContent;

            } else if ( typeof elem.innerText === ‘string‘ ) {

                // Replace IE‘s carriage returns

                return elem.innerText.replace( rReturn, ‘‘ );

            } else {

                // Traverse it‘s children

                for ( elem = elem.firstChild; elem; elem = elem.nextSibling) {

                    ret += getText( elem );

                }

            }

        } else if ( nodeType === 3 || nodeType === 4 ) {

            return elem.nodeValue;

        }

    } else {

        // If no nodeType, this is expected to be an array

        for ( i = 0; (node = elem[i]); i++ ) {

            // Do not traverse comment nodes

            if ( node.nodeType !== 8 ) {

                ret += getText( node );

            }

        }

    }

    return ret;

};

var Expr = Sizzle.selectors = {

    order: [ "ID", "NAME", "TAG" ],

    match: {

        ID: /#((?:[\w\u00c0-\uFFFF\-]|\\.)+)/,

        CLASS: /\.((?:[\w\u00c0-\uFFFF\-]|\\.)+)/,

        NAME: /\[name=[‘"]*((?:[\w\u00c0-\uFFFF\-]|\\.)+)[‘"]*\]/,

        ATTR: /\[\s*((?:[\w\u00c0-\uFFFF\-]|\\.)+)\s*(?:(\S?=)\s*(?:([‘"])(.*?)\3|(#?(?:[\w\u00c0-\uFFFF\-]|\\.)*)|)|)\s*\]/,

        TAG: /^((?:[\w\u00c0-\uFFFF\*\-]|\\.)+)/,

        CHILD: /:(only|nth|last|first)-child(?:\(\s*(even|odd|(?:[+\-]?\d+|(?:[+\-]?\d*)?n\s*(?:[+\-]\s*\d+)?))\s*\))?/,

        POS: /:(nth|eq|gt|lt|first|last|even|odd)(?:\((\d*)\))?(?=[^\-]|$)/,

        PSEUDO: /:((?:[\w\u00c0-\uFFFF\-]|\\.)+)(?:\(([‘"]?)((?:\([^\)]+\)|[^\(\)]*)+)\2\))?/

    },

    leftMatch: {},

    attrMap: {

        "class": "className",

        "for": "htmlFor"

    },

    attrHandle: {

        href: function( elem ) {

            return elem.getAttribute( "href" );

        },

        type: function( elem ) {

            return elem.getAttribute( "type" );

        }

    },

    relative: {

        "+": function(checkSet, part){

            var isPartStr = typeof part === "string",

                isTag = isPartStr && !rNonWord.test( part ),

                isPartStrNotTag = isPartStr && !isTag;

            if ( isTag ) {

                part = part.toLowerCase();

            }

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

                if ( (elem = checkSet[i]) ) {

                    while ( (elem = elem.previousSibling) && elem.nodeType !== 1 ) {}

                    checkSet[i] = isPartStrNotTag || elem && elem.nodeName.toLowerCase() === part ?

                        elem || false :

                        elem === part;

                }

            }

            if ( isPartStrNotTag ) {

                Sizzle.filter( part, checkSet, true );

            }

        },

        ">": function( checkSet, part ) {

            var elem,

                isPartStr = typeof part === "string",

                i = 0,

                l = checkSet.length;

            if ( isPartStr && !rNonWord.test( part ) ) {

                part = part.toLowerCase();

                for ( ; i < l; i++ ) {

                    elem = checkSet[i];

                    if ( elem ) {

                        var parent = elem.parentNode;

                        checkSet[i] = parent.nodeName.toLowerCase() === part ? parent : false;

                    }

                }

            } else {

                for ( ; i < l; i++ ) {

                    elem = checkSet[i];

                    if ( elem ) {

                        checkSet[i] = isPartStr ?

                            elem.parentNode :

                            elem.parentNode === part;

                    }

                }

                if ( isPartStr ) {

                    Sizzle.filter( part, checkSet, true );

                }

            }

        },

        "": function(checkSet, part, isXML){

            var nodeCheck,

                doneName = done++,

                checkFn = dirCheck;

            if ( typeof part === "string" && !rNonWord.test( part ) ) {

                part = part.toLowerCase();

                nodeCheck = part;

                checkFn = dirNodeCheck;

            }

            checkFn( "parentNode", part, doneName, checkSet, nodeCheck, isXML );

        },

        "~": function( checkSet, part, isXML ) {

            var nodeCheck,

                doneName = done++,

                checkFn = dirCheck;

            if ( typeof part === "string" && !rNonWord.test( part ) ) {

                part = part.toLowerCase();

                nodeCheck = part;

                checkFn = dirNodeCheck;

            }

            checkFn( "previousSibling", part, doneName, checkSet, nodeCheck, isXML );

        }

    },

    find: {

        ID: function( match, context, isXML ) {

            if ( typeof context.getElementById !== "undefined" && !isXML ) {

                var m = context.getElementById(match[1]);

                // Check parentNode to catch when Blackberry 4.6 returns

                // nodes that are no longer in the document #6963

                return m && m.parentNode ? [m] : [];

            }

        },

        NAME: function( match, context ) {

            if ( typeof context.getElementsByName !== "undefined" ) {

                var ret = [],

                    results = context.getElementsByName( match[1] );

                for ( var i = 0, l = results.length; i < l; i++ ) {

                    if ( results[i].getAttribute("name") === match[1] ) {

                        ret.push( results[i] );

                    }

                }

                return ret.length === 0 ? null : ret;

            }

        },

        TAG: function( match, context ) {

            if ( typeof context.getElementsByTagName !== "undefined" ) {

                return context.getElementsByTagName( match[1] );

            }

        }

    },

    preFilter: {

        CLASS: function( match, curLoop, inplace, result, not, isXML ) {

            match = " " + match[1].replace( rBackslash, "" ) + " ";

            if ( isXML ) {

                return match;

            }

            for ( var i = 0, elem; (elem = curLoop[i]) != null; i++ ) {

                if ( elem ) {

                    if ( not ^ (elem.className && (" " + elem.className + " ").replace(/[\t\n\r]/g, " ").indexOf(match) >= 0) ) {

                        if ( !inplace ) {

                            result.push( elem );

                        }

                    } else if ( inplace ) {

                        curLoop[i] = false;

                    }

                }

            }

            return false;

        },

        ID: function( match ) {

            return match[1].replace( rBackslash, "" );

        },

        TAG: function( match, curLoop ) {

            return match[1].replace( rBackslash, "" ).toLowerCase();

        },

        CHILD: function( match ) {

            if ( match[1] === "nth" ) {

                if ( !match[2] ) {

                    Sizzle.error( match[0] );

                }

                match[2] = match[2].replace(/^\+|\s*/g, ‘‘);

                // parse equations like ‘even‘, ‘odd‘, ‘5‘, ‘2n‘, ‘3n+2‘, ‘4n-1‘, ‘-n+6‘

                var test = /(-?)(\d*)(?:n([+\-]?\d*))?/.exec(

                    match[2] === "even" && "2n" || match[2] === "odd" && "2n+1" ||

                    !/\D/.test( match[2] ) && "0n+" + match[2] || match[2]);

                // calculate the numbers (first)n+(last) including if they are negative

                match[2] = (test[1] + (test[2] || 1)) - 0;

                match[3] = test[3] - 0;

            }

            else if ( match[2] ) {

                Sizzle.error( match[0] );

            }

            // TODO: Move to normal caching system

            match[0] = done++;

            return match;

        },

        ATTR: function( match, curLoop, inplace, result, not, isXML ) {

            var name = match[1] = match[1].replace( rBackslash, "" );

            if ( !isXML && Expr.attrMap[name] ) {

                match[1] = Expr.attrMap[name];

            }

            // Handle if an un-quoted value was used

            match[4] = ( match[4] || match[5] || "" ).replace( rBackslash, "" );

            if ( match[2] === "~=" ) {

                match[4] = " " + match[4] + " ";

            }

            return match;

        },

        PSEUDO: function( match, curLoop, inplace, result, not ) {

            if ( match[1] === "not" ) {

                // If we‘re dealing with a complex expression, or a simple one

                if ( ( chunker.exec(match[3]) || "" ).length > 1 || /^\w/.test(match[3]) ) {

                    match[3] = Sizzle(match[3], null, null, curLoop);

                } else {

                    var ret = Sizzle.filter(match[3], curLoop, inplace, true ^ not);

                    if ( !inplace ) {

                        result.push.apply( result, ret );

                    }

                    return false;

                }

            } else if ( Expr.match.POS.test( match[0] ) || Expr.match.CHILD.test( match[0] ) ) {

                return true;

            }

            return match;

        },

        POS: function( match ) {

            match.unshift( true );

            return match;

        }

    },

    filters: {

        enabled: function( elem ) {

            return elem.disabled === false && elem.type !== "hidden";

        },

        disabled: function( elem ) {

            return elem.disabled === true;

        },

        checked: function( elem ) {

            return elem.checked === true;

        },

        selected: function( elem ) {

            // Accessing this property makes selected-by-default

            // options in Safari work properly

            if ( elem.parentNode ) {

                elem.parentNode.selectedIndex;

            }

            return elem.selected === true;

        },

        parent: function( elem ) {

            return !!elem.firstChild;

        },

        empty: function( elem ) {

            return !elem.firstChild;

        },

        has: function( elem, i, match ) {

            return !!Sizzle( match[3], elem ).length;

        },

        header: function( elem ) {

            return (/h\d/i).test( elem.nodeName );

        },

        text: function( elem ) {

            var attr = elem.getAttribute( "type" ), type = elem.type;

            // IE6 and 7 will map elem.type to ‘text‘ for new HTML5 types (search, etc)

            // use getAttribute instead to test this case

            return elem.nodeName.toLowerCase() === "input" && "text" === type && ( attr === type || attr === null );

        },

        radio: function( elem ) {

            return elem.nodeName.toLowerCase() === "input" && "radio" === elem.type;

        },

        checkbox: function( elem ) {

            return elem.nodeName.toLowerCase() === "input" && "checkbox" === elem.type;

        },

        file: function( elem ) {

            return elem.nodeName.toLowerCase() === "input" && "file" === elem.type;

        },

        password: function( elem ) {

            return elem.nodeName.toLowerCase() === "input" && "password" === elem.type;

        },

        submit: function( elem ) {

            var name = elem.nodeName.toLowerCase();

            return (name === "input" || name === "button") && "submit" === elem.type;

        },

        image: function( elem ) {

            return elem.nodeName.toLowerCase() === "input" && "image" === elem.type;

        },

        reset: function( elem ) {

            var name = elem.nodeName.toLowerCase();

            return (name === "input" || name === "button") && "reset" === elem.type;

        },

        button: function( elem ) {

            var name = elem.nodeName.toLowerCase();

            return name === "input" && "button" === elem.type || name === "button";

        },

        input: function( elem ) {

            return (/input|select|textarea|button/i).test( elem.nodeName );

        },

        focus: function( elem ) {

            return elem === elem.ownerDocument.activeElement;

        }

    },

    setFilters: {

        first: function( elem, i ) {

            return i === 0;

        },

        last: function( elem, i, match, array ) {

            return i === array.length - 1;

        },

        even: function( elem, i ) {

            return i % 2 === 0;

        },

        odd: function( elem, i ) {

            return i % 2 === 1;

        },

        lt: function( elem, i, match ) {

            return i < match[3] - 0;

        },

        gt: function( elem, i, match ) {

            return i > match[3] - 0;

        },

        nth: function( elem, i, match ) {

            return match[3] - 0 === i;

        },

        eq: function( elem, i, match ) {

            return match[3] - 0 === i;

        }

    },

    filter: {

        PSEUDO: function( elem, match, i, array ) {

            var name = match[1],

                filter = Expr.filters[ name ];

            if ( filter ) {

                return filter( elem, i, match, array );

            } else if ( name === "contains" ) {

                return (elem.textContent || elem.innerText || getText([ elem ]) || "").indexOf(match[3]) >= 0;

            } else if ( name === "not" ) {

                var not = match[3];

                for ( var j = 0, l = not.length; j < l; j++ ) {

                    if ( not[j] === elem ) {

                        return false;

                    }

                }

                return true;

            } else {

                Sizzle.error( name );

            }

        },

        CHILD: function( elem, match ) {

            var first, last,

                doneName, parent, cache,

                count, diff,

                type = match[1],

                node = elem;

            switch ( type ) {

                case "only":

                case "first":

                    while ( (node = node.previousSibling) )     {

                        if ( node.nodeType === 1 ) {

                            return false;

                        }

                    }

                    if ( type === "first" ) {

                        return true;

                    }

                    node = elem;

                case "last":

                    while ( (node = node.nextSibling) )     {

                        if ( node.nodeType === 1 ) {

                            return false;

                        }

                    }

                    return true;

                case "nth":

                    first = match[2];

                    last = match[3];

                    if ( first === 1 && last === 0 ) {

                        return true;

                    }

                    doneName = match[0];

                    parent = elem.parentNode;

                    if ( parent && (parent[ expando ] !== doneName || !elem.nodeIndex) ) {

                        count = 0;

                        for ( node = parent.firstChild; node; node = node.nextSibling ) {

                            if ( node.nodeType === 1 ) {

                                node.nodeIndex = ++count;

                            }

                        }

                        parent[ expando ] = doneName;

                    }

                    diff = elem.nodeIndex - last;

                    if ( first === 0 ) {

                        return diff === 0;

                    } else {

                        return ( diff % first === 0 && diff / first >= 0 );

                    }

            }

        },

        ID: function( elem, match ) {

            return elem.nodeType === 1 && elem.getAttribute("id") === match;

        },

        TAG: function( elem, match ) {

            return (match === "*" && elem.nodeType === 1) || !!elem.nodeName && elem.nodeName.toLowerCase() === match;

        },

        CLASS: function( elem, match ) {

            return (" " + (elem.className || elem.getAttribute("class")) + " ")

                .indexOf( match ) > -1;

        },

        ATTR: function( elem, match ) {

            var name = match[1],

                result = Sizzle.attr ?

                    Sizzle.attr( elem, name ) :

                    Expr.attrHandle[ name ] ?

                    Expr.attrHandle[ name ]( elem ) :

                    elem[ name ] != null ?

                        elem[ name ] :

                        elem.getAttribute( name ),

                value = result + "",

                type = match[2],

                check = match[4];

            return result == null ?

                type === "!=" :

                !type && Sizzle.attr ?

                result != null :

                type === "=" ?

                value === check :

                type === "*=" ?

                value.indexOf(check) >= 0 :

                type === "~=" ?

                (" " + value + " ").indexOf(check) >= 0 :

                !check ?

                value && result !== false :

                type === "!=" ?

                value !== check :

                type === "^=" ?

                value.indexOf(check) === 0 :

                type === "$=" ?

                value.substr(value.length - check.length) === check :

                type === "|=" ?

                value === check || value.substr(0, check.length + 1) === check + "-" :

                false;

        },

        POS: function( elem, match, i, array ) {

            var name = match[2],

                filter = Expr.setFilters[ name ];

            if ( filter ) {

                return filter( elem, i, match, array );

            }

        }

    }

};

var origPOS = Expr.match.POS,

    fescape = function(all, num){

        return "\\" + (num - 0 + 1);

    };

for ( var type in Expr.match ) {

    Expr.match[ type ] = new RegExp( Expr.match[ type ].source + (/(?![^\[]*\])(?![^\(]*\))/.source) );

    Expr.leftMatch[ type ] = new RegExp( /(^(?:.|\r|\n)*?)/.source + Expr.match[ type ].source.replace(/\\(\d+)/g, fescape) );

}

var makeArray = function( array, results ) {

    array = Array.prototype.slice.call( array, 0 );

    if ( results ) {

        results.push.apply( results, array );

        return results;

    }

    return array;

};

// Perform a simple check to determine if the browser is capable of

// converting a NodeList to an array using builtin methods.

// Also verifies that the returned array holds DOM nodes

// (which is not the case in the Blackberry browser)

try {

    Array.prototype.slice.call( document.documentElement.childNodes, 0 )[0].nodeType;

// Provide a fallback method if it does not work

} catch( e ) {

    makeArray = function( array, results ) {

        var i = 0,

            ret = results || [];

        if ( toString.call(array) === "[object Array]" ) {

            Array.prototype.push.apply( ret, array );

        } else {

            if ( typeof array.length === "number" ) {

                for ( var l = array.length; i < l; i++ ) {

                    ret.push( array[i] );

                }

            } else {

                for ( ; array[i]; i++ ) {

                    ret.push( array[i] );

                }

            }

        }

        return ret;

    };

}

var sortOrder, siblingCheck;

if ( document.documentElement.compareDocumentPosition ) {

    sortOrder = function( a, b ) {

        if ( a === b ) {

            hasDuplicate = true;

            return 0;

        }

        if ( !a.compareDocumentPosition || !b.compareDocumentPosition ) {

            return a.compareDocumentPosition ? -1 : 1;

        }

        return a.compareDocumentPosition(b) & 4 ? -1 : 1;

    };

} else {

    sortOrder = function( a, b ) {

        // The nodes are identical, we can exit early

        if ( a === b ) {

            hasDuplicate = true;

            return 0;

        // Fallback to using sourceIndex (in IE) if it‘s available on both nodes

        } else if ( a.sourceIndex && b.sourceIndex ) {

            return a.sourceIndex - b.sourceIndex;

        }

        var al, bl,

            ap = [],

            bp = [],

            aup = a.parentNode,

            bup = b.parentNode,

            cur = aup;

        // If the nodes are siblings (or identical) we can do a quick check

        if ( aup === bup ) {

            return siblingCheck( a, b );

        // If no parents were found then the nodes are disconnected

        } else if ( !aup ) {

            return -1;

        } else if ( !bup ) {

            return 1;

        }

        // Otherwise they‘re somewhere else in the tree so we need

        // to build up a full list of the parentNodes for comparison

        while ( cur ) {

            ap.unshift( cur );

            cur = cur.parentNode;

        }

        cur = bup;

        while ( cur ) {

            bp.unshift( cur );

            cur = cur.parentNode;

        }

        al = ap.length;

        bl = bp.length;

        // Start walking down the tree looking for a discrepancy

        for ( var i = 0; i < al && i < bl; i++ ) {

            if ( ap[i] !== bp[i] ) {

                return siblingCheck( ap[i], bp[i] );

            }

        }

        // We ended someplace up the tree so do a sibling check

        return i === al ?

            siblingCheck( a, bp[i], -1 ) :

            siblingCheck( ap[i], b, 1 );

    };

    siblingCheck = function( a, b, ret ) {

        if ( a === b ) {

            return ret;

        }

        var cur = a.nextSibling;

        while ( cur ) {

            if ( cur === b ) {

                return -1;

            }

            cur = cur.nextSibling;

        }

        return 1;

    };

}

// Check to see if the browser returns elements by name when

// querying by getElementById (and provide a workaround)

(function(){

    // We‘re going to inject a fake input element with a specified name

    var form = document.createElement("div"),

        id = "script" + (new Date()).getTime(),

        root = document.documentElement;

    form.innerHTML = "<a name=‘" + id + "‘/>";

    // Inject it into the root element, check its status, and remove it quickly

    root.insertBefore( form, root.firstChild );

    // The workaround has to do additional checks after a getElementById

    // Which slows things down for other browsers (hence the branching)

    if ( document.getElementById( id ) ) {

        Expr.find.ID = function( match, context, isXML ) {

            if ( typeof context.getElementById !== "undefined" && !isXML ) {

                var m = context.getElementById(match[1]);

                return m ?

                    m.id === match[1] || typeof m.getAttributeNode !== "undefined" && m.getAttributeNode("id").nodeValue === match[1] ?

                        [m] :

                        undefined :

                    [];

            }

        };

        Expr.filter.ID = function( elem, match ) {

            var node = typeof elem.getAttributeNode !== "undefined" && elem.getAttributeNode("id");

            return elem.nodeType === 1 && node && node.nodeValue === match;

        };

    }

    root.removeChild( form );

    // release memory in IE

    root = form = null;

})();

(function(){

    // Check to see if the browser returns only elements

    // when doing getElementsByTagName("*")

    // Create a fake element

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

    div.appendChild( document.createComment("") );

    // Make sure no comments are found

    if ( div.getElementsByTagName("*").length > 0 ) {

        Expr.find.TAG = function( match, context ) {

            var results = context.getElementsByTagName( match[1] );

            // Filter out possible comments

            if ( match[1] === "*" ) {

                var tmp = [];

                for ( var i = 0; results[i]; i++ ) {

                    if ( results[i].nodeType === 1 ) {

                        tmp.push( results[i] );

                    }

                }

                results = tmp;

            }

            return results;

        };

    }

    // Check to see if an attribute returns normalized href attributes

    div.innerHTML = "<a href=‘#‘></a>";

    if ( div.firstChild && typeof div.firstChild.getAttribute !== "undefined" &&

            div.firstChild.getAttribute("href") !== "#" ) {

        Expr.attrHandle.href = function( elem ) {

            return elem.getAttribute( "href", 2 );

        };

    }

    // release memory in IE

    div = null;

})();

if ( document.querySelectorAll ) {

    (function(){

        var oldSizzle = Sizzle,

            div = document.createElement("div"),

            id = "__sizzle__";

        div.innerHTML = "<p class=‘TEST‘></p>";

        // Safari can‘t handle uppercase or unicode characters when

        // in quirks mode.

        if ( div.querySelectorAll && div.querySelectorAll(".TEST").length === 0 ) {

            return;

        }

        Sizzle = function( query, context, extra, seed ) {

            context = context || document;

            // Only use querySelectorAll on non-XML documents

            // (ID selectors don‘t work in non-HTML documents)

            if ( !seed && !Sizzle.isXML(context) ) {

                // See if we find a selector to speed up

                var match = /^(\w+$)|^\.([\w\-]+$)|^#([\w\-]+$)/.exec( query );

                if ( match && (context.nodeType === 1 || context.nodeType === 9) ) {

                    // Speed-up: Sizzle("TAG")

                    if ( match[1] ) {

                        return makeArray( context.getElementsByTagName( query ), extra );

                    // Speed-up: Sizzle(".CLASS")

                    } else if ( match[2] && Expr.find.CLASS && context.getElementsByClassName ) {

                        return makeArray( context.getElementsByClassName( match[2] ), extra );

                    }

                }

                if ( context.nodeType === 9 ) {

                    // Speed-up: Sizzle("body")

                    // The body element only exists once, optimize finding it

                    if ( query === "body" && context.body ) {

                        return makeArray( [ context.body ], extra );

                    // Speed-up: Sizzle("#ID")

                    } else if ( match && match[3] ) {

                        var elem = context.getElementById( match[3] );

                        // 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

                            if ( elem.id === match[3] ) {

                                return makeArray( [ elem ], extra );

                            }

                        } else {

                            return makeArray( [], extra );

                        }

                    }

                    try {

                        return makeArray( context.querySelectorAll(query), extra );

                    } catch(qsaError) {}

                // qSA works strangely on Element-rooted queries

                // We can work around this by specifying an extra ID on the root

                // and working up from there (Thanks to Andrew Dupont for the technique)

                // IE 8 doesn‘t work on object elements

                } else if ( context.nodeType === 1 && context.nodeName.toLowerCase() !== "object" ) {

                    var oldContext = context,

                        old = context.getAttribute( "id" ),

                        nid = old || id,

                        hasParent = context.parentNode,

                        relativeHierarchySelector = /^\s*[+~]/.test( query );

                    if ( !old ) {

                        context.setAttribute( "id", nid );

                    } else {

                        nid = nid.replace( /‘/g, "\\$&" );

                    }

                    if ( relativeHierarchySelector && hasParent ) {

                        context = context.parentNode;

                    }

                    try {

                        if ( !relativeHierarchySelector || hasParent ) {

                            return makeArray( context.querySelectorAll( "[id=‘" + nid + "‘] " + query ), extra );

                        }

                    } catch(pseudoError) {

                    } finally {

                        if ( !old ) {

                            oldContext.removeAttribute( "id" );

                        }

                    }

                }

            }

            return oldSizzle(query, context, extra, seed);

        };

        for ( var prop in oldSizzle ) {

            Sizzle[ prop ] = oldSizzle[ prop ];

        }

        // release memory in IE

        div = null;

    })();

}

(function(){

    var html = document.documentElement,

        matches = html.matchesSelector || html.mozMatchesSelector || html.webkitMatchesSelector || html.msMatchesSelector;

    if ( matches ) {

        // Check to see if it‘s possible to do matchesSelector

        // on a disconnected node (IE 9 fails this)

        var disconnectedMatch = !matches.call( document.createElement( "div" ), "div" ),

            pseudoWorks = false;

        try {

            // This should fail with an exception

            // Gecko does not error, returns false instead

            matches.call( document.documentElement, "[test!=‘‘]:sizzle" );

        } catch( pseudoError ) {

            pseudoWorks = true;

        }

        Sizzle.matchesSelector = function( node, expr ) {

            // Make sure that attribute selectors are quoted

            expr = expr.replace(/\=\s*([^‘"\]]*)\s*\]/g, "=‘$1‘]");

            if ( !Sizzle.isXML( node ) ) {

                try {

                    if ( pseudoWorks || !Expr.match.PSEUDO.test( expr ) && !/!=/.test( expr ) ) {

                        var ret = matches.call( node, expr );

                        // IE 9‘s matchesSelector returns false on disconnected nodes

                        if ( ret || !disconnectedMatch ||

                                // As well, disconnected nodes are said to be in a document

                                // fragment in IE 9, so check for that

                                node.document && node.document.nodeType !== 11 ) {

                            return ret;

                        }

                    }

                } catch(e) {}

            }

            return Sizzle(expr, null, null, [node]).length > 0;

        };

    }

})();

(function(){

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

    div.innerHTML = "<div class=‘test e‘></div><div class=‘test‘></div>";

    // Opera can‘t find a second classname (in 9.6)

    // Also, make sure that getElementsByClassName actually exists

    if ( !div.getElementsByClassName || div.getElementsByClassName("e").length === 0 ) {

        return;

    }

    // Safari caches class attributes, doesn‘t catch changes (in 3.2)

    div.lastChild.className = "e";

    if ( div.getElementsByClassName("e").length === 1 ) {

        return;

    }

    Expr.order.splice(1, 0, "CLASS");

    Expr.find.CLASS = function( match, context, isXML ) {

        if ( typeof context.getElementsByClassName !== "undefined" && !isXML ) {

            return context.getElementsByClassName(match[1]);

        }

    };

    // release memory in IE

    div = null;

})();

function dirNodeCheck( dir, cur, doneName, checkSet, nodeCheck, isXML ) {

    for ( var i = 0, l = checkSet.length; i < l; i++ ) {

        var elem = checkSet[i];

        if ( elem ) {

            var match = false;

            elem = elem[dir];

            while ( elem ) {

                if ( elem[ expando ] === doneName ) {

                    match = checkSet[elem.sizset];

                    break;

                }

                if ( elem.nodeType === 1 && !isXML ){

                    elem[ expando ] = doneName;

                    elem.sizset = i;

                }

                if ( elem.nodeName.toLowerCase() === cur ) {

                    match = elem;

                    break;

                }

                elem = elem[dir];

            }

            checkSet[i] = match;

        }

    }

}

function dirCheck( dir, cur, doneName, checkSet, nodeCheck, isXML ) {

    for ( var i = 0, l = checkSet.length; i < l; i++ ) {

        var elem = checkSet[i];

        if ( elem ) {

            var match = false;

            elem = elem[dir];

            while ( elem ) {

                if ( elem[ expando ] === doneName ) {

                    match = checkSet[elem.sizset];

                    break;

                }

                if ( elem.nodeType === 1 ) {

                    if ( !isXML ) {

                        elem[ expando ] = doneName;

                        elem.sizset = i;

                    }

                    if ( typeof cur !== "string" ) {

                        if ( elem === cur ) {

                            match = true;

                            break;

                        }

                    } else if ( Sizzle.filter( cur, [elem] ).length > 0 ) {

                        match = elem;

                        break;

                    }

                }

                elem = elem[dir];

            }

            checkSet[i] = match;

        }

    }

}

if ( document.documentElement.contains ) {

    Sizzle.contains = function( a, b ) {

        return a !== b && (a.contains ? a.contains(b) : true);

    };

} else if ( document.documentElement.compareDocumentPosition ) {

    Sizzle.contains = function( a, b ) {

        return !!(a.compareDocumentPosition(b) & 16);

    };

} else {

    Sizzle.contains = function() {

        return false;

    };

}

Sizzle.isXML = function( elem ) {

    // documentElement is verified for cases where it doesn‘t yet exist

    // (such as loading iframes in IE - #4833)

    var documentElement = (elem ? elem.ownerDocument || elem : 0).documentElement;

    return documentElement ? documentElement.nodeName !== "HTML" : false;

};

var posProcess = function( selector, context, seed ) {

    var match,

        tmpSet = [],

        later = "",

        root = context.nodeType ? [context] : context;

    // Position selectors must be done after the filter

    // And so must :not(positional) so we move all PSEUDOs to the end

    while ( (match = Expr.match.PSEUDO.exec( selector )) ) {

        later += match[0];

        selector = selector.replace( Expr.match.PSEUDO, "" );

    }

    selector = Expr.relative[selector] ? selector + "*" : selector;

    for ( var i = 0, l = root.length; i < l; i++ ) {

        Sizzle( selector, root[i], tmpSet, seed );

    }

    return Sizzle.filter( later, tmpSet );

};

// EXPOSE

// Override sizzle attribute retrieval

Sizzle.attr = jQuery.attr;

Sizzle.selectors.attrMap = {};

jQuery.find = Sizzle;

jQuery.expr = Sizzle.selectors;

jQuery.expr[":"] = jQuery.expr.filters;

jQuery.unique = Sizzle.uniqueSort;

jQuery.text = Sizzle.getText;

jQuery.isXMLDoc = Sizzle.isXML;

jQuery.contains = Sizzle.contains;

}


6、选择器扩展方法

我们把有关选择器的相关方法代码放在文件jquery.extend.sizzle.js中。


function jQuery_extend_sizzle(jQuery){

    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 = 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 self = this,

                i, l;

            if ( typeof selector !== "string" ) {

                return jQuery( selector ).filter(function() {

                    for ( i = 0, l = self.length; i < l; i++ ) {

                        if ( jQuery.contains( self[ i ], this ) ) {

                            return true;

                        }

                    }

                });

            }

            var ret = this.pushStack( "", "find", selector ),

                length, n, r;

            for ( i = 0, l = this.length; i < l; i++ ) {

                length = ret.length;

                jQuery.find( selector, this[i], ret );

                if ( i > 0 ) {

                    // Make sure that the results are unique

                    for ( n = length; n < ret.length; n++ ) {

                        for ( r = 0; r < length; r++ ) {

                            if ( ret[r] === ret[n] ) {

                                ret.splice(n--, 1);

                                break;

                            }

                        }

                    }

                }

            }

            return ret;

        },

        has: function( target ) {

            var targets = jQuery( target );

            return this.filter(function() {

                for ( var i = 0, l = targets.length; i < l; i++ ) {

                    if ( jQuery.contains( this, targets[i] ) ) {

                        return true;

                    }

                }

            });

        },

        not: function( selector ) {

            return this.pushStack( winnow(this, selector, false), "not", selector);

        },

        filter: function( selector ) {

            return this.pushStack( winnow(this, selector, true), "filter", selector );

        },

        is: function( selector ) {

            return !!selector && (

                typeof selector === "string" ?

                    // If this is a positional selector, check membership in the returned set

                    // so $("p:first").is("p:last") won‘t return true for a doc with two "p".

                    POS.test( selector ) ?

                        jQuery( selector, this.context ).index( this[0] ) >= 0 :

                        jQuery.filter( selector, this ).length > 0 :

                    this.filter( selector ).length > 0 );

        },

        closest: function( selectors, context ) {

            var ret = [], i, l, cur = this[0];

            // Array (deprecated as of jQuery 1.7)

            if ( jQuery.isArray( selectors ) ) {

                var level = 1;

                while ( cur && cur.ownerDocument && cur !== context ) {

                    for ( i = 0; i < selectors.length; i++ ) {

                        if ( jQuery( cur ).is( selectors[ i ] ) ) {

                            ret.push({ selector: selectors[ i ], elem: cur, level: level });

                        }

                    }

                    cur = cur.parentNode;

                    level++;

                }

                return ret;

            }

            // String

            var pos = POS.test( selectors ) || typeof selectors !== "string" ?

                    jQuery( selectors, context || this.context ) :

                    0;

            for ( i = 0, l = this.length; i < l; i++ ) {

                cur = this[i];

                while ( cur ) {

                    if ( pos ? pos.index(cur) > -1 : jQuery.find.matchesSelector(cur, selectors) ) {

                        ret.push( cur );

                        break;

                    } else {

                        cur = cur.parentNode;

                        if ( !cur || !cur.ownerDocument || cur === context || cur.nodeType === 11 ) {

                            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 ) {

            // No argument, return index in parent

            if ( !elem ) {

                return ( this[0] && this[0].parentNode ) ? this.prevAll().length : -1;

            }

            // index in selector

            if ( typeof elem === "string" ) {

                return jQuery.inArray( this[0], jQuery( elem ) );

            }

            // 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 );

        },

        add: function( selector, context ) {

            var set = typeof selector === "string" ?

                    jQuery( selector, context ) :

                    jQuery.makeArray( selector && selector.nodeType ? [ selector ] : selector ),

                all = jQuery.merge( this.get(), set );

            return this.pushStack( isDisconnected( set[0] ) || isDisconnected( all[0] ) ?

                all :

                jQuery.unique( all ) );

        },

        andSelf: function() {

            return this.add( this.prevObject );

        }

    });

}


7、浏览器功能测试


我们将浏览器功能测试support的相关方法代码放在文件jquery.extend.support.js中。jQuery各模块通过浏览器功能测试模块的测试结果来解决浏览器不兼容问题。


function jQuery_support(jQuery){

    jQuery.support = (function() {

        var support,

            all,

            a,

            select,

            opt,

            input,

            marginDiv,

            fragment,

            tds,

            events,

            eventName,

            i,

            isSupported,

            div = document.createElement( "div" ),

            documentElement = document.documentElement;

        // Preliminary tests

        div.setAttribute("className", "t");

        div.innerHTML = "   <link/><table></table><a href=‘/a‘ style=‘top:1px;float:left;opacity:.55;‘>a</a><input type=‘checkbox‘/>";

        all = div.getElementsByTagName( "*" );

        a = div.getElementsByTagName( "a" )[ 0 ];

        // Can‘t get basic test support

        if ( !all || !all.length || !a ) {

            return {};

        }

        // First batch of supports tests

        select = document.createElement( "select" );

        opt = select.appendChild( document.createElement("option") );

        input = div.getElementsByTagName( "input" )[ 0 ];

        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 instead)

            style: /top/.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: ( input.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,

            // Test setAttribute on camelCase class. If it works, we need attrFixes when doing get/setAttribute (ie6/7)

            getSetAttribute: div.className !== "t",

            // Tests for enctype support on a form(#6743)

            enctype: !!document.createElement("form").enctype,

            // Makes sure cloning an html5 element does not cause problems

            // Where outerHTML is undefined, this still works

            html5Clone: document.createElement("nav").cloneNode( true ).outerHTML !== "<:nav></:nav>",

            // Will be defined later

            submitBubbles: true,

            changeBubbles: true,

            focusinBubbles: false,

            deleteExpando: true,

            noCloneEvent: true,

            inlineBlockNeedsLayout: false,

            shrinkWrapBlocks: false,

            reliableMarginRight: true

        };

        // Make sure checked status is properly cloned

        input.checked = true;

        support.noCloneChecked = input.cloneNode( true ).checked;

        // Make sure that the options inside disabled selects aren‘t marked as disabled

        // (WebKit marks them as disabled)

        select.disabled = true;

        support.optDisabled = !opt.disabled;

        // Test to see if it‘s possible to delete an expando from an element

        // Fails in Internet Explorer

        try {

            delete div.test;

        } catch( e ) {

            support.deleteExpando = false;

        }

        if ( !div.addEventListener && div.attachEvent && div.fireEvent ) {

            div.attachEvent( " function() {

                // Cloning a node shouldn‘t copy over any

                // bound event handlers (IE does this)

                support.noCloneEvent = false;

            });

            div.cloneNode( true ).fireEvent( " );

        }

        // Check if a radio maintains its value

        // after being appended to the DOM

        input = document.createElement("input");

        input.value = "t";

        input.setAttribute("type", "radio");

        support.radioValue = input.value === "t";

        input.setAttribute("checked", "checked");

        div.appendChild( input );

        fragment = document.createDocumentFragment();

        fragment.appendChild( div.lastChild );

        // WebKit doesn‘t clone checked state correctly in fragments

        support.checkClone = fragment.cloneNode( true ).cloneNode( true ).lastChild.checked;

        // Check if a disconnected checkbox will retain its checked

        // value of true after appended to the DOM (IE6/7)

        support.appendChecked = input.checked;

        fragment.removeChild( input );

        fragment.appendChild( div );

        div.innerHTML = "";

        // Check if div with explicit width and no margin-right incorrectly

        // gets computed margin-right based on width of container. For more

        // info see bug #3333

        // Fails in WebKit before Feb 2011 nightlies

        // WebKit Bug 13343 - getComputedStyle returns wrong value for margin-right

        if ( window.getComputedStyle ) {

            marginDiv = document.createElement( "div" );

            marginDiv.style.width = "0";

            marginDiv.style.marginRight = "0";

            div.style.width = "2px";

            div.appendChild( marginDiv );

            support.reliableMarginRight =

                ( parseInt( ( window.getComputedStyle( marginDiv, null ) || { marginRight: 0 } ).marginRight, 10 ) || 0 ) === 0;

        }

        // Technique from Juriy Zaytsev

        // http://perfectionkills.com/detecting-event-support-without-browser-sniffing/

        // 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 ( div.attachEvent ) {

            for( i in {

                submit: 1,

                change: 1,

                focusin: 1

            }) {

                eventName = "on" + i;

                isSupported = ( eventName in div );

                if ( !isSupported ) {

                    div.setAttribute( eventName, "return;" );

                    isSupported = ( typeof div[ eventName ] === "function" );

                }

                support[ i + "Bubbles" ] = isSupported;

            }

        }

        fragment.removeChild( div );

        // Null elements to avoid leaks in IE

        fragment = select = opt = marginDiv = div = input = null;

        // Run tests that need a body at doc ready

        jQuery(function() {

            var container, outer, inner, table, td, offsetSupport,

                conMarginTop, ptlm, vb, style, html,

                body = document.getElementsByTagName("body")[0];

            if ( !body ) {

                // Return for frameset docs that don‘t have a body

                return;

            }

            conMarginTop = 1;

            ptlm = "position:absolute;top:0;left:0;width:1px;height:1px;margin:0;";

            vb = "visibility:hidden;border:0;";

            style = "style=‘" + ptlm + "border:5px solid #000;padding:0;‘";

            html = "<div " + style + "><div></div></div>" +

                "<table " + style + " cellpadding=‘0‘ cellspacing=‘0‘>" +

                "<tr><td></td></tr></table>";

            container = document.createElement("div");

            container.style.cssText = vb + "width:0;height:0;position:static;top:0;margin-top:" + conMarginTop + "px";

            body.insertBefore( container, body.firstChild );

            // Construct the test element

            div = document.createElement("div");

            container.appendChild( div );

            // 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)

            div.innerHTML = "<table><tr><td style=‘padding:0;border:0;display:none‘></td><td>t</td></tr></table>";

            tds = div.getElementsByTagName( "td" );

            isSupported = ( 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)

            support.reliableHiddenOffsets = isSupported && ( tds[ 0 ].offsetHeight === 0 );

            // Figure out if the W3C box model works as expected

            div.innerHTML = "";

            div.style.width = div.style.paddingLeft = "1px";

            jQuery.boxModel = support.boxModel = div.offsetWidth === 2;

            if ( typeof div.style.zoom !== "undefined" ) {

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

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

                support.shrinkWrapBlocks = ( div.offsetWidth !== 2 );

            }

            div.style.cssText = ptlm + vb;

            div.innerHTML = html;

            outer = div.firstChild;

            inner = outer.firstChild;

            td = outer.nextSibling.firstChild.firstChild;

            offsetSupport = {

                doesNotAddBorder: ( inner.offsetTop !== 5 ),

                doesAddBorderForTableAndCells: ( td.offsetTop === 5 )

            };

            inner.style.position = "fixed";

            inner.style.top = "20px";

            // safari subtracts parent border width here which is 5px

            offsetSupport.fixedPosition = ( inner.offsetTop === 20 || inner.offsetTop === 15 );

            inner.style.position = inner.style.top = "";

            outer.style.overflow = "hidden";

            outer.style.position = "relative";

            offsetSupport.subtractsBorderForOverflowNotVisible = ( inner.offsetTop === -5 );

            offsetSupport.doesNotIncludeMarginInBodyOffset = ( body.offsetTop !== conMarginTop );

            body.removeChild( container );

            div  = container = null;

            jQuery.extend( support, offsetSupport );

        });

        return support;

    })();

}


8、属性操作


我们将jQuery有关属性操作的相关方法代码放在文件jquery.extend.attrs.js中。


function jQuery_extend_attributes(jQuery){

    jQuery.fn.extend({

        attr: function( name, value ) {

            return jQuery.access( this, name, value, true, jQuery.attr );

        },

        removeAttr: function( name ) {

            return this.each(function() {

                jQuery.removeAttr( this, name );

            });

        },

        prop: function( name, value ) {

            return jQuery.access( this, name, value, true, jQuery.prop );

        },

        removeProp: function( name ) {

            name = jQuery.propFix[ name ] || name;

            return this.each(function() {

                // try/catch handles cases where IE balks (such as removing a property on window)

                try {

                    this[ name ] = undefined;

                    delete this[ name ];

                } catch( e ) {}

            });

        },

        addClass: function( value ) {

            var classNames, i, l, elem,

                setClass, c, cl;

            if ( jQuery.isFunction( value ) ) {

                return this.each(function( j ) {

                    jQuery( this ).addClass( value.call(this, j, this.className) );

                });

            }

            if ( value && typeof value === "string" ) {

                classNames = value.split( rspace );

                for ( i = 0, l = this.length; i < l; i++ ) {

                    elem = this[ i ];

                    if ( elem.nodeType === 1 ) {

                        if ( !elem.className && classNames.length === 1 ) {

                            elem.className = value;

                        } else {

                            setClass = " " + elem.className + " ";

                            for ( c = 0, cl = classNames.length; c < cl; c++ ) {

                                if ( !~setClass.indexOf( " " + classNames[ c ] + " " ) ) {

                                    setClass += classNames[ c ] + " ";

                                }

                            }

                            elem.className = jQuery.trim( setClass );

                        }

                    }

                }

            }

            return this;

        },

        removeClass: function( value ) {

            var classNames, i, l, elem, className, c, cl;

            if ( jQuery.isFunction( value ) ) {

                return this.each(function( j ) {

                    jQuery( this ).removeClass( value.call(this, j, this.className) );

                });

            }

            if ( (value && typeof value === "string") || value === undefined ) {

                classNames = ( value || "" ).split( rspace );

                for ( i = 0, l = this.length; i < l; i++ ) {

                    elem = this[ i ];

                    if ( elem.nodeType === 1 && elem.className ) {

                        if ( value ) {

                            className = (" " + elem.className + " ").replace( rclass, " " );

                            for ( 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 ( jQuery.isFunction( value ) ) {

                return this.each(function( i ) {

                    jQuery( this ).toggleClass( value.call(this, i, this.className, 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( rspace );

                    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" ) {

                    if ( this.className ) {

                        // store className if set

                        jQuery._data( this, "__className__", this.className );

                    }

                    // toggle whole className

                    this.className = this.className || value === false ? "" : jQuery._data( this, "__className__" ) || "";

                }

            });

        },

        hasClass: function( selector ) {

            var className = " " + selector + " ",

                i = 0,

                l = this.length;

            for ( ; i < l; i++ ) {

                if ( this[i].nodeType === 1 && (" " + this[i].className + " ").replace(rclass, " ").indexOf( className ) > -1 ) {

                    return true;

                }

            }

            return false;

        },

        val: function( value ) {

            var hooks, ret, isFunction,

                elem = this[0];

            if ( !arguments.length ) {

                if ( elem ) {

                    hooks = jQuery.valHooks[ elem.nodeName.toLowerCase() ] || jQuery.valHooks[ elem.type ];

                    if ( hooks && "get" in hooks && (ret = hooks.get( elem, "value" )) !== undefined ) {

                        return ret;

                    }

                    ret = elem.value;

                    return typeof ret === "string" ?

                        // handle most common string cases

                        ret.replace(rreturn, "") :

                        // handle cases where value is null/undef or number

                        ret == null ? "" : ret;

                }

                return;

            }

            isFunction = jQuery.isFunction( value );

            return this.each(function( i ) {

                var self = jQuery(this), val;

                if ( this.nodeType !== 1 ) {

                    return;

                }

                if ( isFunction ) {

                    val = value.call( this, i, self.val() );

                } else {

                    val = value;

                }

                // Treat null/undefined as ""; convert numbers to string

                if ( val == null ) {

                    val = "";

                } else if ( typeof val === "number" ) {

                    val += "";

                } else if ( jQuery.isArray( val ) ) {

                    val = jQuery.map(val, function ( value ) {

                        return value == null ? "" : value + "";

                    });

                }

                hooks = jQuery.valHooks[ this.nodeName.toLowerCase() ] || jQuery.valHooks[ this.type ];

                // If set returns undefined, fall back to normal setting

                if ( !hooks || !("set" in hooks) || hooks.set( this, val, "value" ) === undefined ) {

                    this.value = val;

                }

            });

        }

    });

}


(待续)

本文出自 “老惠” 博客,转载请与作者联系!

以上是关于第五节:JQuery框架源码简析的主要内容,如果未能解决你的问题,请参考以下文章

第五节:JQuery框架源码简析

第五节:JQuery框架源码简析

第五节:JQuery框架源码简析

第五节:从源码的角度理解各种Result(ActionResultJsonResultJavaScriptResult等)

第五节:Java集合框架之优先级队列PriorityQueue(堆)

Volley框架简析