Made the IE frameElement check more explicit. Fixes #3880.
[jquery.git] / src / selector.js
index eb54517..56c8a2a 100644 (file)
@@ -1,16 +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 done = 0;
+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;
 
@@ -45,7 +45,7 @@ var Sizzle = function(selector, context, results, seed) {
                                selector = selector.replace( Expr.match.POS, "" );
                        }
 
-                       set = Sizzle.filter( later, Sizzle( selector, context ) );
+                       set = Sizzle.filter( later, Sizzle( /\s$/.test(selector) ? selector + "*" : selector, context ) );
                } else {
                        set = Expr.relative[ parts[0] ] ?
                                [ context ] :
@@ -90,7 +90,7 @@ var Sizzle = function(selector, context, results, seed) {
                                pop = context;
                        }
 
-                       Expr.relative[ cur ]( checkSet, pop );
+                       Expr.relative[ cur ]( checkSet, pop, isXML(context) );
                }
        }
 
@@ -102,7 +102,7 @@ var Sizzle = function(selector, context, results, seed) {
                throw "Syntax error, unrecognized expression: " + (cur || selector);
        }
 
-       if ( checkSet instanceof Array ) {
+       if ( toString.call(checkSet) === "[object Array]" ) {
                if ( !prune ) {
                        results.push.apply( results, checkSet );
                } else if ( context.nodeType === 1 ) {
@@ -182,6 +182,8 @@ Sizzle.filter = function(expr, set, inplace, not){
 
                                        if ( !match ) {
                                                anyFound = found = true;
+                                       } else if ( match === true ) {
+                                               continue;
                                        } else if ( match[0] === true ) {
                                                goodArray = [];
                                                var last = null, elem;
@@ -254,17 +256,23 @@ Sizzle.filter = function(expr, set, inplace, not){
 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?=)\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: /\[\s*((?:[\w\u00c0-\uFFFF_-]|\\.)+)\s*(?:(\S?=)\s*(['"]*)(.*?)\3|)\s*\]/,
+               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"
+       },
+       attrHandle: {
+               href: function(elem){
+                       return elem.getAttribute("href");
+               }
        },
        relative: {
                "+": function(checkSet, part){
@@ -285,9 +293,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];
@@ -311,25 +319,25 @@ 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);
+                       checkFn("parentNode", part, doneName, checkSet, nodeCheck, isXML);
                },
-               "~": 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;
                        }
 
-                       checkFn("previousSibling", part, doneName, checkSet, nodeCheck);
+                       checkFn("previousSibling", part, doneName, checkSet, nodeCheck, isXML);
                }
        },
        find: {
@@ -362,10 +370,11 @@ var Expr = Sizzle.selectors = {
                        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" ) {
@@ -409,6 +418,8 @@ var Expr = Sizzle.selectors = {
                                        }
                                        return false;
                                }
+                       } else if ( Expr.match.POS.test( match[0] ) ) {
+                               return true;
                        }
                        
                        return match;
@@ -507,9 +518,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 ) {
@@ -574,7 +585,7 @@ var Expr = Sizzle.selectors = {
                        return match.test( elem.className );
                },
                ATTR: function(elem, match){
-                       var result = elem[ match[1] ] || elem.getAttribute( match[1] ), value = result + "", type = match[2], check = match[4];
+                       var result = Expr.attrHandle[ match[1] ] ? Expr.attrHandle[ match[1] ]( elem ) : elem[ match[1] ] || elem.getAttribute( match[1] ), value = result + "", type = match[2], check = match[4];
                        return result == null ?
                                false :
                                type === "=" ?
@@ -630,7 +641,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" ) {
@@ -679,9 +690,10 @@ try {
        root.removeChild( form );
 })();
 
-// Check to see if the browser returns only elements
-// when doing getElementsByTagName("*")
 (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("") );
@@ -707,6 +719,14 @@ try {
                        return results;
                };
        }
+
+       // Check to see if an attribute returns normalized href attributes
+       div.innerHTML = "<a href='#'></a>";
+       if ( div.firstChild.getAttribute("href") !== "#" ) {
+               Expr.attrHandle.href = function(elem){
+                       return elem.getAttribute("href", 2);
+               };
+       }
 })();
 
 if ( document.querySelectorAll ) (function(){
@@ -737,7 +757,7 @@ if ( document.documentElement.getElementsByClassName ) {
        };
 }
 
-function dirNodeCheck( dir, cur, doneName, checkSet, nodeCheck ) {
+function dirNodeCheck( dir, cur, doneName, checkSet, nodeCheck, isXML ) {
        for ( var i = 0, l = checkSet.length; i < l; i++ ) {
                var elem = checkSet[i];
                if ( elem ) {
@@ -751,7 +771,7 @@ function dirNodeCheck( dir, cur, doneName, checkSet, nodeCheck ) {
                                        break;
                                }
 
-                               if ( elem.nodeType === 1 )
+                               if ( elem.nodeType === 1 && !isXML )
                                        elem[doneName] = i;
 
                                if ( elem.nodeName === cur ) {
@@ -767,7 +787,7 @@ function dirNodeCheck( dir, cur, doneName, checkSet, nodeCheck ) {
        }
 }
 
-function dirCheck( dir, cur, doneName, checkSet, nodeCheck ) {
+function dirCheck( dir, cur, doneName, checkSet, nodeCheck, isXML ) {
        for ( var i = 0, l = checkSet.length; i < l; i++ ) {
                var elem = checkSet[i];
                if ( elem ) {
@@ -781,7 +801,8 @@ function dirCheck( dir, cur, doneName, checkSet, nodeCheck ) {
                                }
 
                                if ( elem.nodeType === 1 ) {
-                                       elem[doneName] = i;
+                                       if ( !isXML )
+                                               elem[doneName] = i;
 
                                        if ( typeof cur !== "string" ) {
                                                if ( elem === cur ) {
@@ -809,6 +830,11 @@ var contains = document.compareDocumentPosition ?  function(a, 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
 jQuery.find = Sizzle;
 jQuery.filter = Sizzle.filter;