Upgrade to ExtJS 3.1.1 - Released 02/08/2010
[extjs.git] / docs / source / DomQuery.html
index 65d2d82..01fe025 100644 (file)
@@ -59,6 +59,7 @@ All selectors, attribute filters and pseudos below can be combined infinitely in
     <li> <b>E:has(S)</b> an E element that has a descendent that matches simple selector S</li>\r
     <li> <b>E:next(S)</b> an E element whose next sibling matches simple selector S</li>\r
     <li> <b>E:prev(S)</b> an E element whose previous sibling matches simple selector S</li>\r
+    <li> <b>E:any(S1|S2|S2)</b> an E element which matches any of the simple selectors S1, S2 or S3//\\</li>\r
 </ul>\r
 <h4>CSS Value Selectors:</h4>\r
 <ul class="list">\r
@@ -83,18 +84,20 @@ Ext.DomQuery = function(){
        nthRe = /(\d*)n\+?(\d*)/, \r
        nthRe2 = /\D/,\r
        // This is for IE MSXML which does not support expandos.\r
-           // IE runs the same speed using setAttribute, however FF slows way down\r
-           // and Safari completely fails so they need to continue to use expandos.\r
-           isIE = window.ActiveXObject ? true : false,\r
-           key = 30803;\r
-           \r
+       // IE runs the same speed using setAttribute, however FF slows way down\r
+       // and Safari completely fails so they need to continue to use expandos.\r
+       isIE = window.ActiveXObject ? true : false,\r
+       key = 30803;\r
+    \r
     // this eval is stop the compressor from\r
-       // renaming the variable to something shorter\r
-       eval("var batch = 30803;");     \r
+    // renaming the variable to something shorter\r
+    eval("var batch = 30803;");        \r
 \r
-    function child(p, index){\r
+    // Retrieve the child node from a particular\r
+    // parent at the specified index.\r
+    function child(parent, index){\r
         var i = 0,\r
-               n = p.firstChild;\r
+            n = parent.firstChild;\r
         while(n){\r
             if(n.nodeType == 1){\r
                if(++i == index){\r
@@ -104,53 +107,65 @@ Ext.DomQuery = function(){
             n = n.nextSibling;\r
         }\r
         return null;\r
-    };\r
+    }\r
 \r
-    function next(n){\r
+    // retrieve the next element node\r
+    function next(n){  \r
         while((n = n.nextSibling) && n.nodeType != 1);\r
         return n;\r
-    };\r
+    }\r
 \r
+    // retrieve the previous element node \r
     function prev(n){\r
         while((n = n.previousSibling) && n.nodeType != 1);\r
         return n;\r
-    };\r
+    }\r
+\r
+    // Mark each child node with a nodeIndex skipping and\r
+    // removing empty text nodes.\r
+    function children(parent){\r
+        var n = parent.firstChild,\r
+           nodeIndex = -1,\r
+           nextNode;\r
+       while(n){\r
+           nextNode = n.nextSibling;\r
+           // clean worthless empty nodes.\r
+           if(n.nodeType == 3 && !nonSpace.test(n.nodeValue)){\r
+               parent.removeChild(n);\r
+           }else{\r
+               // add an expando nodeIndex\r
+               n.nodeIndex = ++nodeIndex;\r
+           }\r
+           n = nextNode;\r
+       }\r
+       return this;\r
+    }\r
 \r
-    function children(d){\r
-        var n = d.firstChild, ni = -1,\r
-               nx;\r
-           while(n){\r
-               nx = n.nextSibling;\r
-               if(n.nodeType == 3 && !nonSpace.test(n.nodeValue)){\r
-                   d.removeChild(n);\r
-               }else{\r
-                   n.nodeIndex = ++ni;\r
-               }\r
-               n = nx;\r
-           }\r
-           return this;\r
-       };\r
-\r
-    function byClassName(c, a, v){\r
-        if(!v){\r
-            return c;\r
-        }\r
-        var r = [], ri = -1, cn;\r
-        for(var i = 0, ci; ci = c[i]; i++){\r
-            if((' '+ci.className+' ').indexOf(v) != -1){\r
-                r[++ri] = ci;\r
+\r
+    // nodeSet - array of nodes\r
+    // cls - CSS Class\r
+    function byClassName(nodeSet, cls){\r
+        if(!cls){\r
+            return nodeSet;\r
+        }\r
+        var result = [], ri = -1;\r
+        for(var i = 0, ci; ci = nodeSet[i]; i++){\r
+            if((' '+ci.className+' ').indexOf(cls) != -1){\r
+                result[++ri] = ci;\r
             }\r
         }\r
-        return r;\r
+        return result;\r
     };\r
 \r
     function attrValue(n, attr){\r
+       // if its an array, use the first node.\r
         if(!n.tagName && typeof n.length != "undefined"){\r
             n = n[0];\r
         }\r
         if(!n){\r
             return null;\r
         }\r
+\r
         if(attr == "for"){\r
             return n.htmlFor;\r
         }\r
@@ -161,15 +176,23 @@ Ext.DomQuery = function(){
 \r
     };\r
 \r
+\r
+    // ns - nodes\r
+    // mode - false, /, >, +, ~\r
+    // tagName - defaults to "*"\r
     function getNodes(ns, mode, tagName){\r
         var result = [], ri = -1, cs;\r
         if(!ns){\r
             return result;\r
         }\r
         tagName = tagName || "*";\r
+       // convert to array\r
         if(typeof ns.getElementsByTagName != "undefined"){\r
             ns = [ns];\r
         }\r
+       \r
+       // no mode specified, grab all elements by tagName\r
+       // at any depth\r
         if(!mode){\r
             for(var i = 0, ni; ni = ns[i]; i++){\r
                 cs = ni.getElementsByTagName(tagName);\r
@@ -177,7 +200,9 @@ Ext.DomQuery = function(){
                     result[++ri] = ci;\r
                 }\r
             }\r
-        }else if(mode == "/" || mode == ">"){\r
+       // Direct Child mode (/ or >)\r
+       // E > F or E/F all direct children elements of E that have the tag     \r
+        } else if(mode == "/" || mode == ">"){\r
             var utag = tagName.toUpperCase();\r
             for(var i = 0, ni, cn; ni = ns[i]; i++){\r
                 cn = ni.childNodes;\r
@@ -187,6 +212,8 @@ Ext.DomQuery = function(){
                     }\r
                 }\r
             }\r
+       // Immediately Preceding mode (+)\r
+       // E + F all elements with the tag F that are immediately preceded by an element with the tag E\r
         }else if(mode == "+"){\r
             var utag = tagName.toUpperCase();\r
             for(var i = 0, n; n = ns[i]; i++){\r
@@ -195,6 +222,8 @@ Ext.DomQuery = function(){
                     result[++ri] = n;\r
                 }\r
             }\r
+       // Sibling mode (~)\r
+       // E ~ F all elements with the tag F that are preceded by a sibling element with the tag E\r
         }else if(mode == "~"){\r
             var utag = tagName.toUpperCase();\r
             for(var i = 0, n; n = ns[i]; i++){\r
@@ -206,7 +235,7 @@ Ext.DomQuery = function(){
             }\r
         }\r
         return result;\r
-    };\r
+    }\r
 \r
     function concat(a, b){\r
         if(b.slice){\r
@@ -225,69 +254,81 @@ Ext.DomQuery = function(){
         if(!tagName){\r
             return cs;\r
         }\r
-        var r = [], ri = -1;\r
+        var result = [], ri = -1;\r
         tagName = tagName.toLowerCase();\r
         for(var i = 0, ci; ci = cs[i]; i++){\r
-            if(ci.nodeType == 1 && ci.tagName.toLowerCase()==tagName){\r
-                r[++ri] = ci;\r
+            if(ci.nodeType == 1 && ci.tagName.toLowerCase() == tagName){\r
+                result[++ri] = ci;\r
             }\r
         }\r
-        return r;\r
-    };\r
+        return result;\r
+    }\r
 \r
-    function byId(cs, attr, id){\r
+    function byId(cs, id){\r
         if(cs.tagName || cs == document){\r
             cs = [cs];\r
         }\r
         if(!id){\r
             return cs;\r
         }\r
-        var r = [], ri = -1;\r
-        for(var i = 0,ci; ci = cs[i]; i++){\r
+        var result = [], ri = -1;\r
+        for(var i = 0, ci; ci = cs[i]; i++){\r
             if(ci && ci.id == id){\r
-                r[++ri] = ci;\r
-                return r;\r
+                result[++ri] = ci;\r
+                return result;\r
             }\r
         }\r
-        return r;\r
-    };\r
+        return result;\r
+    }\r
 \r
+    // operators are =, !=, ^=, $=, *=, %=, |= and ~=\r
+    // custom can be "{"\r
     function byAttribute(cs, attr, value, op, custom){\r
-        var r = [], \r
-               ri = -1, \r
-               st = custom=="{",\r
-               f = Ext.DomQuery.operators[op];\r
+        var result = [], \r
+            ri = -1, \r
+            useGetStyle = custom == "{",           \r
+            fn = Ext.DomQuery.operators[op],       \r
+            a,     \r
+            innerHTML;\r
         for(var i = 0, ci; ci = cs[i]; i++){\r
+           // skip non-element nodes.\r
             if(ci.nodeType != 1){\r
                 continue;\r
             }\r
-            var a;\r
-            if(st){\r
-                a = Ext.DomQuery.getStyle(ci, attr);\r
-            }\r
-            else if(attr == "class" || attr == "className"){\r
-                a = ci.className;\r
-            }else if(attr == "for"){\r
-                a = ci.htmlFor;\r
-            }else if(attr == "href"){\r
-                a = ci.getAttribute("href", 2);\r
+           \r
+            innerHTML = ci.innerHTML;\r
+            // we only need to change the property names if we're dealing with html nodes, not XML\r
+            if(innerHTML !== null && innerHTML !== undefined){\r
+                if(useGetStyle){\r
+                    a = Ext.DomQuery.getStyle(ci, attr);\r
+                } else if (attr == "class" || attr == "className"){\r
+                    a = ci.className;\r
+                } else if (attr == "for"){\r
+                    a = ci.htmlFor;\r
+                } else if (attr == "href"){\r
+                   // getAttribute href bug\r
+                   // http://www.glennjones.net/Post/809/getAttributehrefbug.htm\r
+                    a = ci.getAttribute("href", 2);\r
+                } else{\r
+                    a = ci.getAttribute(attr);\r
+                }\r
             }else{\r
                 a = ci.getAttribute(attr);\r
             }\r
-            if((f && f(a, value)) || (!f && a)){\r
-                r[++ri] = ci;\r
+            if((fn && fn(a, value)) || (!fn && a)){\r
+                result[++ri] = ci;\r
             }\r
         }\r
-        return r;\r
-    };\r
+        return result;\r
+    }\r
 \r
     function byPseudo(cs, name, value){\r
         return Ext.DomQuery.pseudos[name](cs, value);\r
-    };\r
+    }\r
 \r
     function nodupIEXml(cs){\r
         var d = ++key, \r
-               r;\r
+            r;\r
         cs[0].setAttribute("_nodup", d);\r
         r = [cs[0]];\r
         for(var i = 1, len = cs.length; i < len; i++){\r
@@ -338,7 +379,7 @@ Ext.DomQuery = function(){
 \r
     function quickDiffIEXml(c1, c2){\r
         var d = ++key,\r
-               r = [];\r
+            r = [];\r
         for(var i = 0, len = c1.length; i < len; i++){\r
             c1[i].setAttribute("_qdiff", d);\r
         }        \r
@@ -380,7 +421,7 @@ Ext.DomQuery = function(){
            return d.getElementById(id);\r
         }\r
         ns = getNodes(ns, mode, "*");\r
-        return byId(ns, null, id);\r
+        return byId(ns, id);\r
     }\r
 \r
     return {\r
@@ -397,110 +438,141 @@ Ext.DomQuery = function(){
         compile : function(path, type){\r
             type = type || "select";\r
 \r
+           // setup fn preamble\r
             var fn = ["var f = function(root){\n var mode; ++batch; var n = root || document;\n"],\r
-               q = path, mode, lq,\r
-               tk = Ext.DomQuery.matchers,\r
-               tklen = tk.length,\r
-               mm,\r
+               mode,           \r
+               lastPath,\r
+               matchers = Ext.DomQuery.matchers,\r
+               matchersLn = matchers.length,\r
+               modeMatch,\r
                // accept leading mode switch\r
-               lmode = q.match(modeRe);\r
+               lmode = path.match(modeRe);\r
             \r
             if(lmode && lmode[1]){\r
                 fn[fn.length] = 'mode="'+lmode[1].replace(trimRe, "")+'";';\r
-                q = q.replace(lmode[1], "");\r
+                path = path.replace(lmode[1], "");\r
             }\r
+           \r
             // strip leading slashes\r
             while(path.substr(0, 1)=="/"){\r
                 path = path.substr(1);\r
             }\r
 \r
-            while(q && lq != q){\r
-                lq = q;\r
-                var tm = q.match(tagTokenRe);\r
+            while(path && lastPath != path){\r
+                lastPath = path;\r
+                var tokenMatch = path.match(tagTokenRe);\r
                 if(type == "select"){\r
-                    if(tm){\r
-                        if(tm[1] == "#"){\r
-                            fn[fn.length] = 'n = quickId(n, mode, root, "'+tm[2]+'");';\r
+                    if(tokenMatch){\r
+                       // ID Selector\r
+                        if(tokenMatch[1] == "#"){\r
+                            fn[fn.length] = 'n = quickId(n, mode, root, "'+tokenMatch[2]+'");';                        \r
                         }else{\r
-                            fn[fn.length] = 'n = getNodes(n, mode, "'+tm[2]+'");';\r
+                            fn[fn.length] = 'n = getNodes(n, mode, "'+tokenMatch[2]+'");';\r
                         }\r
-                        q = q.replace(tm[0], "");\r
-                    }else if(q.substr(0, 1) != '@'){\r
+                        path = path.replace(tokenMatch[0], "");\r
+                    }else if(path.substr(0, 1) != '@'){\r
                         fn[fn.length] = 'n = getNodes(n, mode, "*");';\r
                     }\r
+               // type of "simple"\r
                 }else{\r
-                    if(tm){\r
-                        if(tm[1] == "#"){\r
-                            fn[fn.length] = 'n = byId(n, null, "'+tm[2]+'");';\r
+                    if(tokenMatch){\r
+                        if(tokenMatch[1] == "#"){\r
+                            fn[fn.length] = 'n = byId(n, "'+tokenMatch[2]+'");';\r
                         }else{\r
-                            fn[fn.length] = 'n = byTag(n, "'+tm[2]+'");';\r
+                            fn[fn.length] = 'n = byTag(n, "'+tokenMatch[2]+'");';\r
                         }\r
-                        q = q.replace(tm[0], "");\r
+                        path = path.replace(tokenMatch[0], "");\r
                     }\r
                 }\r
-                while(!(mm = q.match(modeRe))){\r
+                while(!(modeMatch = path.match(modeRe))){\r
                     var matched = false;\r
-                    for(var j = 0; j < tklen; j++){\r
-                        var t = tk[j];\r
-                        var m = q.match(t.re);\r
+                    for(var j = 0; j < matchersLn; j++){\r
+                        var t = matchers[j];\r
+                        var m = path.match(t.re);\r
                         if(m){\r
                             fn[fn.length] = t.select.replace(tplRe, function(x, i){\r
-                                                    return m[i];\r
-                                                });\r
-                            q = q.replace(m[0], "");\r
+                               return m[i];\r
+                           });\r
+                            path = path.replace(m[0], "");\r
                             matched = true;\r
                             break;\r
                         }\r
                     }\r
                     // prevent infinite loop on bad selector\r
                     if(!matched){\r
-                        throw 'Error parsing selector, parsing failed at "' + q + '"';\r
+                        throw 'Error parsing selector, parsing failed at "' + path + '"';\r
                     }\r
                 }\r
-                if(mm[1]){\r
-                    fn[fn.length] = 'mode="'+mm[1].replace(trimRe, "")+'";';\r
-                    q = q.replace(mm[1], "");\r
+                if(modeMatch[1]){\r
+                    fn[fn.length] = 'mode="'+modeMatch[1].replace(trimRe, "")+'";';\r
+                    path = path.replace(modeMatch[1], "");\r
                 }\r
             }\r
+           // close fn out\r
             fn[fn.length] = "return nodup(n);\n}";\r
+           \r
+           // eval fn and return it\r
             eval(fn.join(""));\r
             return f;\r
         },\r
 \r
-        <div id="method-Ext.DomQuery-select"></div>/**\r
+        <div id="method-Ext.DomQuery-jsSelect"></div>/**\r
          * Selects a group of elements.\r
          * @param {String} selector The selector/xpath query (can be a comma separated list of selectors)\r
-         * @param {Node} root (optional) The start of the query (defaults to document).\r
+         * @param {Node/String} root (optional) The start of the query (defaults to document).\r
          * @return {Array} An Array of DOM elements which match the selector. If there are\r
          * no matches, and empty Array is returned.\r
          */\r
-        select : function(path, root, type){\r
-            if(!root || root == document){\r
-                root = document;\r
-            }\r
+       jsSelect: function(path, root, type){\r
+           // set root to doc if not specified.\r
+           root = root || document;\r
+           \r
             if(typeof root == "string"){\r
                 root = document.getElementById(root);\r
             }\r
             var paths = path.split(","),\r
                results = [];\r
-            for(var i = 0, len = paths.length; i < len; i++){\r
-                var p = paths[i].replace(trimRe, "");\r
-                if(!cache[p]){\r
-                    cache[p] = Ext.DomQuery.compile(p);\r
-                    if(!cache[p]){\r
-                        throw p + " is not a valid selector";\r
+               \r
+           // loop over each selector\r
+            for(var i = 0, len = paths.length; i < len; i++){          \r
+                var subPath = paths[i].replace(trimRe, "");\r
+               // compile and place in cache\r
+                if(!cache[subPath]){\r
+                    cache[subPath] = Ext.DomQuery.compile(subPath);\r
+                    if(!cache[subPath]){\r
+                        throw subPath + " is not a valid selector";\r
                     }\r
                 }\r
-                var result = cache[p](root);\r
+                var result = cache[subPath](root);\r
                 if(result && result != document){\r
                     results = results.concat(result);\r
                 }\r
             }\r
+           \r
+           // if there were multiple selectors, make sure dups\r
+           // are eliminated\r
             if(paths.length > 1){\r
                 return nodup(results);\r
             }\r
             return results;\r
         },\r
+       isXml: function(el) {\r
+           var docEl = (el ? el.ownerDocument || el : 0).documentElement;\r
+           return docEl ? docEl.nodeName !== "HTML" : false;\r
+       },\r
+        select : document.querySelectorAll ? function(path, root, type) {\r
+           root = root || document;\r
+           if (!Ext.DomQuery.isXml(root)) {\r
+               try {\r
+                   var cs = root.querySelectorAll(path);\r
+                   return Ext.toArray(cs);\r
+               }\r
+               catch (ex) {}           \r
+           }       \r
+           return Ext.DomQuery.jsSelect.call(this, path, root, type);\r
+       } : function(path, root, type) {\r
+           return Ext.DomQuery.jsSelect.call(this, path, root, type);\r
+       },\r
 \r
         <div id="method-Ext.DomQuery-selectNode"></div>/**\r
          * Selects a single element.\r
@@ -526,7 +598,11 @@ Ext.DomQuery = function(){
             }\r
             var n = valueCache[path](root), v;\r
             n = n[0] ? n[0] : n;\r
-            \r
+                   \r
+           // overcome a limitation of maximum textnode size\r
+           // Rumored to potentially crash IE6 but has not been confirmed.\r
+           // http://reference.sitepoint.com/javascript/Node/normalize\r
+           // https://developer.mozilla.org/En/DOM/Node.normalize          \r
             if (typeof n.normalize == 'function') n.normalize();\r
             \r
             v = (n && n.firstChild ? n.firstChild.nodeValue : null);\r
@@ -580,10 +656,12 @@ Ext.DomQuery = function(){
 \r
         <div id="prop-Ext.DomQuery-matchers"></div>/**\r
          * Collection of matching regular expressions and code snippets.\r
+         * Each capture group within () will be replace the {} in the select\r
+         * statement as specified by their index.\r
          */\r
         matchers : [{\r
                 re: /^\.([\w-]+)/,\r
-                select: 'n = byClassName(n, null, " {1} ");'\r
+                select: 'n = byClassName(n, " {1} ");'\r
             }, {\r
                 re: /^\:([\w-]+)(?:\(((?:[^\s>\/]*|.*?))\))?/,\r
                 select: 'n = byPseudo(n, "{1}", "{2}");'\r
@@ -592,7 +670,7 @@ Ext.DomQuery = function(){
                 select: 'n = byAttribute(n, "{2}", "{4}", "{3}", "{1}");'\r
             }, {\r
                 re: /^#([\w-]+)/,\r
-                select: 'n = byId(n, null, "{1}");'\r
+                select: 'n = byId(n, "{1}");'\r
             },{\r
                 re: /^@([\w-]+)/,\r
                 select: 'return {firstChild:{nodeValue:attrValue(n, "{1}")}};'\r