X-Git-Url: http://git.asbjorn.biz/?a=blobdiff_plain;f=src%2Fselector.js;h=085f0b32edf7abd46089dae5cad3d09a2f3c551b;hb=944484c3fe001bcfea52fd85612042d7475597a1;hp=c89b92eea0df9edc26a5f83f37632c66e194cd34;hpb=c85243dfc4b09e6bb87532f2025f686b6ae45a22;p=jquery.git diff --git a/src/selector.js b/src/selector.js index c89b92e..085f0b3 100644 --- a/src/selector.js +++ b/src/selector.js @@ -1,25 +1,16 @@ -/* - * Sizzle CSS Selector Engine - v0.9 - * Copyright 2009, John Resig (http://ejohn.org/) - * released under the MIT License +/*! + * Sizzle CSS Selector Engine - v0.9.1 + * Copyright 2009, The Dojo Foundation + * Released under the MIT, BSD, and GPL Licenses. + * More information: http://sizzlejs.com/ */ (function(){ -var chunker = /((?:\((?:\([^()]+\)|[^()]+)+\)|\[(?:\[[^[\]]+\]|[^[\]]+)+\]|\\.|[^ >+~,(\[]+)+|[>+~])(\s*,\s*)?/g; - -var cache = null; -var done = 0; - -if ( document.addEventListener && !document.querySelectorAll ) { - cache = {}; - function invalidate(){ cache = {}; } - document.addEventListener("DOMAttrModified", invalidate, false); - document.addEventListener("DOMNodeInserted", invalidate, false); - document.addEventListener("DOMNodeRemoved", invalidate, false); -} +var chunker = /((?:\((?:\([^()]+\)|[^()]+)+\)|\[(?:\[[^[\]]*\]|[^[\]]+)+\]|\\.|[^ >+~,(\[]+)+|[>+~])(\s*,\s*)?/g, + done = 0, + toString = Object.prototype.toString; var Sizzle = function(selector, context, results, seed) { - var doCache = !results; results = results || []; context = context || document; @@ -30,12 +21,7 @@ var Sizzle = function(selector, context, results, seed) { return results; } - if ( cache && context === document && cache[ selector ] ) { - results.push.apply( results, cache[ selector ] ); - return results; - } - - var parts = [], m, set, checkSet, check, mode, extra; + var parts = [], m, set, checkSet, check, mode, extra, prune = true; // Reset the position of the chunker regexp (start from head) chunker.lastIndex = 0; @@ -59,24 +45,24 @@ var Sizzle = function(selector, context, results, seed) { selector = selector.replace( Expr.match.POS, "" ); } - checkSet = Sizzle.filter( later, Sizzle( selector, context, results ) ); + set = Sizzle.filter( later, Sizzle( selector, context ) ); } else { - checkSet = Expr.relative[ parts[0] ] ? + set = Expr.relative[ parts[0] ] ? [ context ] : Sizzle( parts.shift(), context ); while ( parts.length ) { - set = []; + var tmpSet = []; selector = parts.shift(); if ( Expr.relative[ selector ] ) selector += parts.shift(); - for ( var i = 0, l = checkSet.length; i < l; i++ ) { - Sizzle( selector, checkSet[i], set ); + for ( var i = 0, l = set.length; i < l; i++ ) { + Sizzle( selector, set[i], tmpSet ); } - checkSet = set; + set = tmpSet; } } } else { @@ -87,6 +73,8 @@ var Sizzle = function(selector, context, results, seed) { if ( parts.length > 0 ) { checkSet = makeArray(set); + } else { + prune = false; } while ( parts.length ) { @@ -102,20 +90,22 @@ var Sizzle = function(selector, context, results, seed) { pop = context; } - Expr.relative[ cur ]( checkSet, pop ); + Expr.relative[ cur ]( checkSet, pop, isXML(context) ); } + } - if ( !checkSet ) { - checkSet = set; - } + if ( !checkSet ) { + checkSet = set; } if ( !checkSet ) { throw "Syntax error, unrecognized expression: " + (cur || selector); } - if ( checkSet instanceof Array ) { - if ( context.nodeType === 1 ) { + if ( toString.call(checkSet) === "[object Array]" ) { + if ( !prune ) { + results.push.apply( results, checkSet ); + } else if ( context.nodeType === 1 ) { for ( var i = 0; checkSet[i] != null; i++ ) { if ( checkSet[i] && (checkSet[i] === true || checkSet[i].nodeType === 1 && contains(context, checkSet[i])) ) { results.push( set[i] ); @@ -133,11 +123,7 @@ var Sizzle = function(selector, context, results, seed) { } if ( extra ) { - Sizzle( extra, context, results ); - } - - if ( cache && doCache ) { - cache[selector] = results.slice(0); + Sizzle( extra, context, results, seed ); } return results; @@ -154,23 +140,8 @@ Sizzle.find = function(expr, context){ return []; } - var later = "", match; - - // Pseudo-selectors could contain other selectors (like :not) - while ( (match = Expr.match.PSEUDO.exec( expr )) ) { - var left = RegExp.leftContext; - - if ( left.substr( left.length - 1 ) !== "\\" ) { - later += match[0]; - expr = expr.replace( Expr.match.PSEUDO, "" ); - } else { - // TODO: Need a better solution, fails: .class\:foo:realfoo(#id) - break; - } - } - for ( var i = 0, l = Expr.order.length; i < l; i++ ) { - var type = Expr.order[i]; + var type = Expr.order[i], match; if ( (match = Expr.match[ type ].exec( expr )) ) { var left = RegExp.leftContext; @@ -178,7 +149,6 @@ Sizzle.find = function(expr, context){ if ( left.substr( left.length - 1 ) !== "\\" ) { match[1] = (match[1] || "").replace(/\\/g, ""); set = Expr.find[ type ]( match, context ); - if ( set != null ) { expr = expr.replace( Expr.match[ type ], "" ); break; @@ -191,27 +161,30 @@ Sizzle.find = function(expr, context){ set = context.getElementsByTagName("*"); } - expr += later; - return {set: set, expr: expr}; }; -Sizzle.filter = function(expr, set, inplace){ - var old = expr, result = [], curLoop = set, match; +Sizzle.filter = function(expr, set, inplace, not){ + var old = expr, result = [], curLoop = set, match, anyFound; while ( expr && set.length ) { for ( var type in Expr.filter ) { if ( (match = Expr.match[ type ].exec( expr )) != null ) { - var anyFound = false, filter = Expr.filter[ type ], goodArray = null; + var filter = Expr.filter[ type ], goodArray = null, goodPos = 0, found, item; + anyFound = false; if ( curLoop == result ) { result = []; } if ( Expr.preFilter[ type ] ) { - match = Expr.preFilter[ type ]( match, curLoop ); + match = Expr.preFilter[ type ]( match, curLoop, inplace, result, not ); - if ( match[0] === true ) { + if ( !match ) { + anyFound = found = true; + } else if ( match === true ) { + continue; + } else if ( match[0] === true ) { goodArray = []; var last = null, elem; for ( var i = 0; (elem = curLoop[i]) !== undefined; i++ ) { @@ -221,26 +194,28 @@ Sizzle.filter = function(expr, set, inplace){ } } } - } - var goodPos = 0, found, item; - - for ( var i = 0; (item = curLoop[i]) !== undefined; i++ ) { - if ( item ) { - if ( goodArray && item != goodArray[goodPos] ) { - goodPos++; - } - - found = filter( item, match, goodPos, goodArray ); - if ( inplace && found != null ) { - curLoop[i] = found ? curLoop[i] : false; - if ( found ) { + if ( match ) { + for ( var i = 0; (item = curLoop[i]) !== undefined; i++ ) { + if ( item ) { + if ( goodArray && item != goodArray[goodPos] ) { + goodPos++; + } + + found = filter( item, match, goodPos, goodArray ); + var pass = not ^ !!found; + + if ( inplace && found != null ) { + if ( pass ) { + anyFound = true; + } else { + curLoop[i] = false; + } + } else if ( pass ) { + result.push( item ); anyFound = true; } - } else if ( found ) { - result.push( item ); - anyFound = true; } } } @@ -261,12 +236,15 @@ Sizzle.filter = function(expr, set, inplace){ } } - expr = expr.replace(/\s*,\s*/, ""); // Improper expression if ( expr == old ) { - throw "Syntax error, unrecognized expression: " + expr; + if ( anyFound == null ) { + throw "Syntax error, unrecognized expression: " + expr; + } else { + break; + } } old = expr; @@ -278,17 +256,18 @@ Sizzle.filter = function(expr, set, inplace){ var Expr = Sizzle.selectors = { order: [ "ID", "NAME", "TAG" ], match: { - ID: /#((?:[\w\u0128-\uFFFF_-]|\\.)+)/, - CLASS: /\.((?:[\w\u0128-\uFFFF_-]|\\.)+)/, - NAME: /\[name=((?:[\w\u0128-\uFFFF_-]|\\.)+)\]/, - ATTR: /\[((?:[\w\u0128-\uFFFF_-]|\\.)+)\s*(?:(\S{0,1}=)\s*(['"]*)(.*?)\3|)\]/, - TAG: /^((?:[\w\u0128-\uFFFF\*_-]|\\.)+)/, - CHILD: /:(only|nth|last|first)-child\(?(even|odd|[\dn+-]*)\)?/, - POS: /:(nth|eq|gt|lt|first|last|even|odd)\(?(\d*)\)?(?:[^-]|$)/, - PSEUDO: /:((?:[\w\u0128-\uFFFF_-]|\\.)+)(?:\((['"]*)((?:\([^\)]+\)|[^\2\(\)]*)+)\2\))?/ + ID: /#((?:[\w\u00c0-\uFFFF_-]|\\.)+)/, + CLASS: /\.((?:[\w\u00c0-\uFFFF_-]|\\.)+)/, + NAME: /\[name=['"]*((?:[\w\u00c0-\uFFFF_-]|\\.)+)['"]*\]/, + ATTR: /\[((?:[\w\u00c0-\uFFFF_-]|\\.)+)\s*(?:(\S?=)\s*(['"]*)(.*?)\3|)\]/, + TAG: /^((?:[\w\u00c0-\uFFFF\*_-]|\\.)+)/, + CHILD: /:(only|nth|last|first)-child(?:\((even|odd|[\dn+-]*)\))?/, + POS: /:(nth|eq|gt|lt|first|last|even|odd)(?:\((\d*)\))?(?=[^-]|$)/, + PSEUDO: /:((?:[\w\u00c0-\uFFFF_-]|\\.)+)(?:\((['"]*)((?:\([^\)]+\)|[^\2\(\)]*)+)\2\))?/ }, attrMap: { - "class": "className" + "class": "className", + "for": "htmlFor" }, relative: { "+": function(checkSet, part){ @@ -309,9 +288,9 @@ var Expr = Sizzle.selectors = { Sizzle.filter( part, checkSet, true ); } }, - ">": function(checkSet, part){ + ">": function(checkSet, part, isXML){ if ( typeof part === "string" && !/\W/.test(part) ) { - part = part.toUpperCase(); + part = isXML ? part : part.toUpperCase(); for ( var i = 0, l = checkSet.length; i < l; i++ ) { var elem = checkSet[i]; @@ -335,21 +314,21 @@ var Expr = Sizzle.selectors = { } } }, - "": function(checkSet, part){ + "": function(checkSet, part, isXML){ var doneName = "done" + (done++), checkFn = dirCheck; if ( !part.match(/\W/) ) { - var nodeCheck = part = part.toUpperCase(); + var nodeCheck = part = isXML ? part : part.toUpperCase(); checkFn = dirNodeCheck; } checkFn("parentNode", part, doneName, checkSet, nodeCheck); }, - "~": function(checkSet, part){ + "~": function(checkSet, part, isXML){ var doneName = "done" + (done++), checkFn = dirCheck; if ( typeof part === "string" && !part.match(/\W/) ) { - var nodeCheck = part = part.toUpperCase(); + var nodeCheck = part = isXML ? part : part.toUpperCase(); checkFn = dirNodeCheck; } @@ -364,21 +343,33 @@ var Expr = Sizzle.selectors = { } }, NAME: function(match, context){ - return context.getElementsByName(match[1]); + return context.getElementsByName ? context.getElementsByName(match[1]) : null; }, TAG: function(match, context){ return context.getElementsByTagName(match[1]); } }, preFilter: { - CLASS: function(match){ - return new RegExp( "(?:^|\\s)" + match[1] + "(?:\\s|$)" ); + CLASS: function(match, curLoop, inplace, result, not){ + match = " " + match[1].replace(/\\/g, "") + " "; + + for ( var i = 0; curLoop[i]; i++ ) { + if ( not ^ (" " + curLoop[i].className + " ").indexOf(match) >= 0 ) { + if ( !inplace ) + result.push( curLoop[i] ); + } else if ( inplace ) { + curLoop[i] = false; + } + } + + return false; }, ID: function(match){ - return match[1]; + return match[1].replace(/\\/g, ""); }, - TAG: function(match){ - return match[1].toUpperCase(); + TAG: function(match, curLoop){ + for ( var i = 0; !curLoop[i]; i++ ){} + return isXML(curLoop[i]) ? match[1] : match[1].toUpperCase(); }, CHILD: function(match){ if ( match[1] == "nth" ) { @@ -410,9 +401,20 @@ var Expr = Sizzle.selectors = { return match; }, - PSEUDO: function(match){ + PSEUDO: function(match, curLoop, inplace, result, not){ if ( match[1] === "not" ) { - match[3] = match[3].split(/\s*,\s*/); + // If we're dealing with a complex expression, or a simple one + if ( match[3].match(chunker).length > 1 ) { + 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] ) ) { + return true; } return match; @@ -511,9 +513,9 @@ var Expr = Sizzle.selectors = { CHILD: function(elem, match){ var type = match[1], parent = elem.parentNode; - var doneName = match[0]; + var doneName = "child" + parent.childNodes.length; - if ( parent && !parent[ doneName ] ) { + if ( parent && (!parent[ doneName ] || !elem.nodeIndex) ) { var count = 1; for ( var node = parent.firstChild; node; node = node.nextSibling ) { @@ -553,14 +555,14 @@ var Expr = Sizzle.selectors = { var name = match[1], filter = Expr.filters[ name ]; if ( filter ) { - return filter( elem, i, match, array ) + return filter( elem, i, match, array ); } else if ( name === "contains" ) { return (elem.textContent || elem.innerText || "").indexOf(match[3]) >= 0; } else if ( name === "not" ) { var not = match[3]; for ( var i = 0, l = not.length; i < l; i++ ) { - if ( Sizzle.filter(not[i], [elem]).length > 0 ) { + if ( not[i] === elem ) { return false; } } @@ -609,6 +611,10 @@ var Expr = Sizzle.selectors = { } }; +for ( var type in Expr.match ) { + Expr.match[ type ] = RegExp( Expr.match[ type ].source + /(?![^\[]*\])(?![^\(]*\))/.source ); +} + var makeArray = function(array, results) { array = Array.prototype.slice.call( array ); @@ -630,7 +636,7 @@ try { makeArray = function(array, results) { var ret = results || []; - if ( array instanceof Array ) { + if ( toString.call(array) === "[object Array]" ) { Array.prototype.push.apply( ret, array ); } else { if ( typeof array.length === "number" ) { @@ -712,21 +718,22 @@ try { if ( document.querySelectorAll ) (function(){ var oldSizzle = Sizzle; - Sizzle = function(query, context, extra){ + Sizzle = function(query, context, extra, seed){ context = context || document; - if ( context.nodeType === 9 ) { + if ( !seed && context.nodeType === 9 ) { try { - return makeArray( context.querySelectorAll(query) ); + return makeArray( context.querySelectorAll(query), extra ); } catch(e){} } - return oldSizzle(query, context, extra); + return oldSizzle(query, context, extra, seed); }; Sizzle.find = oldSizzle.find; Sizzle.filter = oldSizzle.filter; Sizzle.selectors = oldSizzle.selectors; + Sizzle.matches = oldSizzle.matches; })(); if ( document.documentElement.getElementsByClassName ) { @@ -740,7 +747,7 @@ function dirNodeCheck( dir, cur, doneName, checkSet, nodeCheck ) { for ( var i = 0, l = checkSet.length; i < l; i++ ) { var elem = checkSet[i]; if ( elem ) { - elem = elem[dir] + elem = elem[dir]; var match = false; while ( elem && elem.nodeType ) { @@ -770,7 +777,7 @@ function dirCheck( dir, cur, doneName, checkSet, nodeCheck ) { for ( var i = 0, l = checkSet.length; i < l; i++ ) { var elem = checkSet[i]; if ( elem ) { - elem = elem[dir] + elem = elem[dir]; var match = false; while ( elem && elem.nodeType ) { @@ -805,7 +812,12 @@ function dirCheck( dir, cur, doneName, checkSet, nodeCheck ) { var contains = document.compareDocumentPosition ? function(a, b){ return a.compareDocumentPosition(b) & 16; } : function(a, b){ - return a !== b && a.contains(b); + return a !== b && (a.contains ? a.contains(b) : true); +}; + +var isXML = function(elem){ + return elem.documentElement && !elem.body || + elem.tagName && elem.ownerDocument && !elem.ownerDocument.body; }; // EXPOSE @@ -826,18 +838,18 @@ Sizzle.selectors.filters.visible = function(elem){ jQuery.css(elem, "visibility") !== "hidden"; }; +Sizzle.selectors.filters.animated = function(elem){ + return jQuery.grep(jQuery.timers, function(fn){ + return elem === fn.elem; + }).length; +}; + jQuery.multiFilter = function( expr, elems, not ) { if ( not ) { - return jQuery.multiFilter( ":not(" + expr + ")", elems ); - } - - var exprs = expr.split(/\s*,\s*/), cur = []; - - for ( var i = 0; i < exprs.length; i++ ) { - cur = jQuery.merge( cur, jQuery.filter( exprs[i], elems ) ); + expr = ":not(" + expr + ")"; } - return cur; + return Sizzle.matches(expr, elems); }; jQuery.dir = function( elem, dir ){