Upgrade to ExtJS 3.1.1 - Released 02/08/2010
[extjs.git] / docs / source / DomQuery.html
index 712f228..01fe025 100644 (file)
@@ -1,5 +1,6 @@
 <html>\r
 <head>\r
+  <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />    \r
   <title>The source code</title>\r
     <link href="../resources/prettify/prettify.css" type="text/css" rel="stylesheet" />\r
     <script type="text/javascript" src="../resources/prettify/prettify.js"></script>\r
@@ -58,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
@@ -82,19 +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
-        isOpera = Ext.isOpera,\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,16 +200,20 @@ 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 = isOpera ? ni.childNodes : (ni.children || ni.childNodes);\r
+                cn = ni.childNodes;\r
                 for(var j = 0, cj; cj = cn[j]; j++){\r
                     if(cj.nodeName == utag || cj.nodeName == tagName  || tagName == '*'){\r
                         result[++ri] = cj;\r
                     }\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
@@ -360,7 +401,7 @@ Ext.DomQuery = function(){
         if(!len1){\r
             return c2;\r
         }\r
-        if(isIE && c1[0].selectSingleNode){\r
+        if(isIE && typeof c1[0].selectSingleNode != "undefined"){\r
             return quickDiffIEXml(c1, c2);\r
         }        \r
         for(var i = 0; i < len1; i++){\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
@@ -524,9 +596,15 @@ Ext.DomQuery = function(){
             if(!valueCache[path]){\r
                 valueCache[path] = Ext.DomQuery.compile(path, "select");\r
             }\r
-            var n = valueCache[path](root),\r
-               v;\r
+            var n = valueCache[path](root), v;\r
             n = n[0] ? n[0] : n;\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
             return ((v === null||v === undefined||v==='') ? defaultValue : v);\r
         },\r
@@ -578,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
@@ -590,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
@@ -629,8 +709,29 @@ Ext.DomQuery = function(){
         },\r
 \r
         <div id="prop-Ext.DomQuery-pseudos"></div>/**\r
-         * Collection of "pseudo class" processors. Each processor is passed the current nodeset (array)\r
-         * and the argument (if any) supplied in the selector.\r
+         * <p>Object hash of "pseudo class" filter functions which are used when filtering selections. Each function is passed\r
+         * two parameters:</p><div class="mdetail-params"><ul>\r
+         * <li><b>c</b> : Array<div class="sub-desc">An Array of DOM elements to filter.</div></li>\r
+         * <li><b>v</b> : String<div class="sub-desc">The argument (if any) supplied in the selector.</div></li>\r
+         * </ul></div>\r
+         * <p>A filter function returns an Array of DOM elements which conform to the pseudo class.</p>\r
+         * <p>In addition to the provided pseudo classes listed above such as <code>first-child</code> and <code>nth-child</code>,\r
+         * developers may add additional, custom psuedo class filters to select elements according to application-specific requirements.</p>\r
+         * <p>For example, to filter <code>&lt;a></code> elements to only return links to <i>external</i> resources:</p>\r
+         * <code><pre>\r
+Ext.DomQuery.pseudos.external = function(c, v){\r
+    var r = [], ri = -1;\r
+    for(var i = 0, ci; ci = c[i]; i++){\r
+//      Include in result set only if it's a link to an external resource\r
+        if(ci.hostname != location.hostname){\r
+            r[++ri] = ci;\r
+        }\r
+    }\r
+    return r;\r
+};</pre></code>\r
+         * Then external links could be gathered with the following statement:<code><pre>\r
+var externalLinks = Ext.select("a:external");\r
+</code></pre>\r
          */\r
         pseudos : {\r
             "first-child" : function(c){\r