PageRenderTime 50ms CodeModel.GetById 16ms app.highlight 27ms RepoModel.GetById 0ms app.codeStats 0ms

/javascripts/lib/src/ext-core/src/core/DomQuery.js

https://bitbucket.org/ksokmesa/sina-asian
JavaScript | 926 lines | 649 code | 63 blank | 214 comment | 264 complexity | a10b1230abaa6db6974ddfb165accda4 MD5 | raw file
Possible License(s): GPL-3.0
  1/*!
  2 * Ext JS Library 3.2.1
  3 * Copyright(c) 2006-2010 Ext JS, Inc.
  4 * licensing@extjs.com
  5 * http://www.extjs.com/license
  6 */
  7/*
  8 * This is code is also distributed under MIT license for use
  9 * with jQuery and prototype JavaScript libraries.
 10 */
 11/**
 12 * @class Ext.DomQuery
 13Provides high performance selector/xpath processing by compiling queries into reusable functions. New pseudo classes and matchers can be plugged. It works on HTML and XML documents (if a content node is passed in).
 14<p>
 15DomQuery supports most of the <a href="http://www.w3.org/TR/2005/WD-css3-selectors-20051215/#selectors">CSS3 selectors spec</a>, along with some custom selectors and basic XPath.</p>
 16
 17<p>
 18All selectors, attribute filters and pseudos below can be combined infinitely in any order. For example "div.foo:nth-child(odd)[@foo=bar].bar:first" would be a perfectly valid selector. Node filters are processed in the order in which they appear, which allows you to optimize your queries for your document structure.
 19</p>
 20<h4>Element Selectors:</h4>
 21<ul class="list">
 22    <li> <b>*</b> any element</li>
 23    <li> <b>E</b> an element with the tag E</li>
 24    <li> <b>E F</b> All descendent elements of E that have the tag F</li>
 25    <li> <b>E > F</b> or <b>E/F</b> all direct children elements of E that have the tag F</li>
 26    <li> <b>E + F</b> all elements with the tag F that are immediately preceded by an element with the tag E</li>
 27    <li> <b>E ~ F</b> all elements with the tag F that are preceded by a sibling element with the tag E</li>
 28</ul>
 29<h4>Attribute Selectors:</h4>
 30<p>The use of &#64; and quotes are optional. For example, div[&#64;foo='bar'] is also a valid attribute selector.</p>
 31<ul class="list">
 32    <li> <b>E[foo]</b> has an attribute "foo"</li>
 33    <li> <b>E[foo=bar]</b> has an attribute "foo" that equals "bar"</li>
 34    <li> <b>E[foo^=bar]</b> has an attribute "foo" that starts with "bar"</li>
 35    <li> <b>E[foo$=bar]</b> has an attribute "foo" that ends with "bar"</li>
 36    <li> <b>E[foo*=bar]</b> has an attribute "foo" that contains the substring "bar"</li>
 37    <li> <b>E[foo%=2]</b> has an attribute "foo" that is evenly divisible by 2</li>
 38    <li> <b>E[foo!=bar]</b> has an attribute "foo" that does not equal "bar"</li>
 39</ul>
 40<h4>Pseudo Classes:</h4>
 41<ul class="list">
 42    <li> <b>E:first-child</b> E is the first child of its parent</li>
 43    <li> <b>E:last-child</b> E is the last child of its parent</li>
 44    <li> <b>E:nth-child(<i>n</i>)</b> E is the <i>n</i>th child of its parent (1 based as per the spec)</li>
 45    <li> <b>E:nth-child(odd)</b> E is an odd child of its parent</li>
 46    <li> <b>E:nth-child(even)</b> E is an even child of its parent</li>
 47    <li> <b>E:only-child</b> E is the only child of its parent</li>
 48    <li> <b>E:checked</b> E is an element that is has a checked attribute that is true (e.g. a radio or checkbox) </li>
 49    <li> <b>E:first</b> the first E in the resultset</li>
 50    <li> <b>E:last</b> the last E in the resultset</li>
 51    <li> <b>E:nth(<i>n</i>)</b> the <i>n</i>th E in the resultset (1 based)</li>
 52    <li> <b>E:odd</b> shortcut for :nth-child(odd)</li>
 53    <li> <b>E:even</b> shortcut for :nth-child(even)</li>
 54    <li> <b>E:contains(foo)</b> E's innerHTML contains the substring "foo"</li>
 55    <li> <b>E:nodeValue(foo)</b> E contains a textNode with a nodeValue that equals "foo"</li>
 56    <li> <b>E:not(S)</b> an E element that does not match simple selector S</li>
 57    <li> <b>E:has(S)</b> an E element that has a descendent that matches simple selector S</li>
 58    <li> <b>E:next(S)</b> an E element whose next sibling matches simple selector S</li>
 59    <li> <b>E:prev(S)</b> an E element whose previous sibling matches simple selector S</li>
 60    <li> <b>E:any(S1|S2|S2)</b> an E element which matches any of the simple selectors S1, S2 or S3//\\</li>
 61</ul>
 62<h4>CSS Value Selectors:</h4>
 63<ul class="list">
 64    <li> <b>E{display=none}</b> css value "display" that equals "none"</li>
 65    <li> <b>E{display^=none}</b> css value "display" that starts with "none"</li>
 66    <li> <b>E{display$=none}</b> css value "display" that ends with "none"</li>
 67    <li> <b>E{display*=none}</b> css value "display" that contains the substring "none"</li>
 68    <li> <b>E{display%=2}</b> css value "display" that is evenly divisible by 2</li>
 69    <li> <b>E{display!=none}</b> css value "display" that does not equal "none"</li>
 70</ul>
 71 * @singleton
 72 */
 73Ext.DomQuery = function(){
 74    var cache = {}, 
 75    	simpleCache = {}, 
 76    	valueCache = {},
 77    	nonSpace = /\S/,
 78    	trimRe = /^\s+|\s+$/g,
 79    	tplRe = /\{(\d+)\}/g,
 80    	modeRe = /^(\s?[\/>+~]\s?|\s|$)/,
 81    	tagTokenRe = /^(#)?([\w-\*]+)/,
 82    	nthRe = /(\d*)n\+?(\d*)/, 
 83    	nthRe2 = /\D/,
 84    	// This is for IE MSXML which does not support expandos.
 85	// IE runs the same speed using setAttribute, however FF slows way down
 86	// and Safari completely fails so they need to continue to use expandos.
 87	isIE = window.ActiveXObject ? true : false,
 88	key = 30803;
 89    
 90    // this eval is stop the compressor from
 91    // renaming the variable to something shorter
 92    eval("var batch = 30803;");    	
 93
 94    // Retrieve the child node from a particular
 95    // parent at the specified index.
 96    function child(parent, index){
 97        var i = 0,
 98            n = parent.firstChild;
 99        while(n){
100            if(n.nodeType == 1){
101               if(++i == index){
102                   return n;
103               }
104            }
105            n = n.nextSibling;
106        }
107        return null;
108    }
109
110    // retrieve the next element node
111    function next(n){	
112        while((n = n.nextSibling) && n.nodeType != 1);
113        return n;
114    }
115
116    // retrieve the previous element node 
117    function prev(n){
118        while((n = n.previousSibling) && n.nodeType != 1);
119        return n;
120    }
121
122    // Mark each child node with a nodeIndex skipping and
123    // removing empty text nodes.
124    function children(parent){
125        var n = parent.firstChild,
126	    nodeIndex = -1,
127	    nextNode;
128	while(n){
129	    nextNode = n.nextSibling;
130	    // clean worthless empty nodes.
131	    if(n.nodeType == 3 && !nonSpace.test(n.nodeValue)){
132		parent.removeChild(n);
133	    }else{
134		// add an expando nodeIndex
135		n.nodeIndex = ++nodeIndex;
136	    }
137	    n = nextNode;
138	}
139	return this;
140    }
141
142
143    // nodeSet - array of nodes
144    // cls - CSS Class
145    function byClassName(nodeSet, cls){
146        if(!cls){
147            return nodeSet;
148        }
149        var result = [], ri = -1;
150        for(var i = 0, ci; ci = nodeSet[i]; i++){
151            if((' '+ci.className+' ').indexOf(cls) != -1){
152                result[++ri] = ci;
153            }
154        }
155        return result;
156    };
157
158    function attrValue(n, attr){
159	// if its an array, use the first node.
160        if(!n.tagName && typeof n.length != "undefined"){
161            n = n[0];
162        }
163        if(!n){
164            return null;
165        }
166
167        if(attr == "for"){
168            return n.htmlFor;
169        }
170        if(attr == "class" || attr == "className"){
171            return n.className;
172        }
173        return n.getAttribute(attr) || n[attr];
174
175    };
176
177
178    // ns - nodes
179    // mode - false, /, >, +, ~
180    // tagName - defaults to "*"
181    function getNodes(ns, mode, tagName){
182        var result = [], ri = -1, cs;
183        if(!ns){
184            return result;
185        }
186        tagName = tagName || "*";
187	// convert to array
188        if(typeof ns.getElementsByTagName != "undefined"){
189            ns = [ns];
190        }
191	
192	// no mode specified, grab all elements by tagName
193	// at any depth
194        if(!mode){
195            for(var i = 0, ni; ni = ns[i]; i++){
196                cs = ni.getElementsByTagName(tagName);
197                for(var j = 0, ci; ci = cs[j]; j++){
198                    result[++ri] = ci;
199                }
200            }
201	// Direct Child mode (/ or >)
202	// E > F or E/F all direct children elements of E that have the tag 	
203        } else if(mode == "/" || mode == ">"){
204            var utag = tagName.toUpperCase();
205            for(var i = 0, ni, cn; ni = ns[i]; i++){
206                cn = ni.childNodes;
207                for(var j = 0, cj; cj = cn[j]; j++){
208                    if(cj.nodeName == utag || cj.nodeName == tagName  || tagName == '*'){
209                        result[++ri] = cj;
210                    }
211                }
212            }
213	// Immediately Preceding mode (+)
214	// E + F all elements with the tag F that are immediately preceded by an element with the tag E
215        }else if(mode == "+"){
216            var utag = tagName.toUpperCase();
217            for(var i = 0, n; n = ns[i]; i++){
218                while((n = n.nextSibling) && n.nodeType != 1);
219                if(n && (n.nodeName == utag || n.nodeName == tagName || tagName == '*')){
220                    result[++ri] = n;
221                }
222            }
223	// Sibling mode (~)
224	// E ~ F all elements with the tag F that are preceded by a sibling element with the tag E
225        }else if(mode == "~"){
226            var utag = tagName.toUpperCase();
227            for(var i = 0, n; n = ns[i]; i++){
228                while((n = n.nextSibling)){
229                    if (n.nodeName == utag || n.nodeName == tagName || tagName == '*'){
230                        result[++ri] = n;
231                    }
232                }
233            }
234        }
235        return result;
236    }
237
238    function concat(a, b){
239        if(b.slice){
240            return a.concat(b);
241        }
242        for(var i = 0, l = b.length; i < l; i++){
243            a[a.length] = b[i];
244        }
245        return a;
246    }
247
248    function byTag(cs, tagName){
249        if(cs.tagName || cs == document){
250            cs = [cs];
251        }
252        if(!tagName){
253            return cs;
254        }
255        var result = [], ri = -1;
256        tagName = tagName.toLowerCase();
257        for(var i = 0, ci; ci = cs[i]; i++){
258            if(ci.nodeType == 1 && ci.tagName.toLowerCase() == tagName){
259                result[++ri] = ci;
260            }
261        }
262        return result;
263    }
264
265    function byId(cs, id){
266        if(cs.tagName || cs == document){
267            cs = [cs];
268        }
269        if(!id){
270            return cs;
271        }
272        var result = [], ri = -1;
273        for(var i = 0, ci; ci = cs[i]; i++){
274            if(ci && ci.id == id){
275                result[++ri] = ci;
276                return result;
277            }
278        }
279        return result;
280    }
281
282    // operators are =, !=, ^=, $=, *=, %=, |= and ~=
283    // custom can be "{"
284    function byAttribute(cs, attr, value, op, custom){
285        var result = [], 
286            ri = -1, 
287            useGetStyle = custom == "{",	    
288            fn = Ext.DomQuery.operators[op],	    
289            a,	    
290            innerHTML;
291        for(var i = 0, ci; ci = cs[i]; i++){
292	    // skip non-element nodes.
293            if(ci.nodeType != 1){
294                continue;
295            }
296	    
297            innerHTML = ci.innerHTML;
298            // we only need to change the property names if we're dealing with html nodes, not XML
299            if(innerHTML !== null && innerHTML !== undefined){
300                if(useGetStyle){
301                    a = Ext.DomQuery.getStyle(ci, attr);
302                } else if (attr == "class" || attr == "className"){
303                    a = ci.className;
304                } else if (attr == "for"){
305                    a = ci.htmlFor;
306                } else if (attr == "href"){
307		    // getAttribute href bug
308		    // http://www.glennjones.net/Post/809/getAttributehrefbug.htm
309                    a = ci.getAttribute("href", 2);
310                } else{
311                    a = ci.getAttribute(attr);
312                }
313            }else{
314                a = ci.getAttribute(attr);
315            }
316            if((fn && fn(a, value)) || (!fn && a)){
317                result[++ri] = ci;
318            }
319        }
320        return result;
321    }
322
323    function byPseudo(cs, name, value){
324        return Ext.DomQuery.pseudos[name](cs, value);
325    }
326
327    function nodupIEXml(cs){
328        var d = ++key, 
329            r;
330        cs[0].setAttribute("_nodup", d);
331        r = [cs[0]];
332        for(var i = 1, len = cs.length; i < len; i++){
333            var c = cs[i];
334            if(!c.getAttribute("_nodup") != d){
335                c.setAttribute("_nodup", d);
336                r[r.length] = c;
337            }
338        }
339        for(var i = 0, len = cs.length; i < len; i++){
340            cs[i].removeAttribute("_nodup");
341        }
342        return r;
343    }
344
345    function nodup(cs){
346        if(!cs){
347            return [];
348        }
349        var len = cs.length, c, i, r = cs, cj, ri = -1;
350        if(!len || typeof cs.nodeType != "undefined" || len == 1){
351            return cs;
352        }
353        if(isIE && typeof cs[0].selectSingleNode != "undefined"){
354            return nodupIEXml(cs);
355        }
356        var d = ++key;
357        cs[0]._nodup = d;
358        for(i = 1; c = cs[i]; i++){
359            if(c._nodup != d){
360                c._nodup = d;
361            }else{
362                r = [];
363                for(var j = 0; j < i; j++){
364                    r[++ri] = cs[j];
365                }
366                for(j = i+1; cj = cs[j]; j++){
367                    if(cj._nodup != d){
368                        cj._nodup = d;
369                        r[++ri] = cj;
370                    }
371                }
372                return r;
373            }
374        }
375        return r;
376    }
377
378    function quickDiffIEXml(c1, c2){
379        var d = ++key,
380            r = [];
381        for(var i = 0, len = c1.length; i < len; i++){
382            c1[i].setAttribute("_qdiff", d);
383        }        
384        for(var i = 0, len = c2.length; i < len; i++){
385            if(c2[i].getAttribute("_qdiff") != d){
386                r[r.length] = c2[i];
387            }
388        }
389        for(var i = 0, len = c1.length; i < len; i++){
390           c1[i].removeAttribute("_qdiff");
391        }
392        return r;
393    }
394
395    function quickDiff(c1, c2){
396        var len1 = c1.length,
397        	d = ++key,
398        	r = [];
399        if(!len1){
400            return c2;
401        }
402        if(isIE && typeof c1[0].selectSingleNode != "undefined"){
403            return quickDiffIEXml(c1, c2);
404        }        
405        for(var i = 0; i < len1; i++){
406            c1[i]._qdiff = d;
407        }        
408        for(var i = 0, len = c2.length; i < len; i++){
409            if(c2[i]._qdiff != d){
410                r[r.length] = c2[i];
411            }
412        }
413        return r;
414    }
415
416    function quickId(ns, mode, root, id){
417        if(ns == root){
418           var d = root.ownerDocument || root;
419           return d.getElementById(id);
420        }
421        ns = getNodes(ns, mode, "*");
422        return byId(ns, id);
423    }
424
425    return {
426        getStyle : function(el, name){
427            return Ext.fly(el).getStyle(name);
428        },
429        /**
430         * Compiles a selector/xpath query into a reusable function. The returned function
431         * takes one parameter "root" (optional), which is the context node from where the query should start.
432         * @param {String} selector The selector/xpath query
433         * @param {String} type (optional) Either "select" (the default) or "simple" for a simple selector match
434         * @return {Function}
435         */
436        compile : function(path, type){
437            type = type || "select";
438
439	    // setup fn preamble
440            var fn = ["var f = function(root){\n var mode; ++batch; var n = root || document;\n"],
441		mode,		
442		lastPath,
443            	matchers = Ext.DomQuery.matchers,
444            	matchersLn = matchers.length,
445            	modeMatch,
446            	// accept leading mode switch
447            	lmode = path.match(modeRe);
448            
449            if(lmode && lmode[1]){
450                fn[fn.length] = 'mode="'+lmode[1].replace(trimRe, "")+'";';
451                path = path.replace(lmode[1], "");
452            }
453	    
454            // strip leading slashes
455            while(path.substr(0, 1)=="/"){
456                path = path.substr(1);
457            }
458
459            while(path && lastPath != path){
460                lastPath = path;
461                var tokenMatch = path.match(tagTokenRe);
462                if(type == "select"){
463                    if(tokenMatch){
464			// ID Selector
465                        if(tokenMatch[1] == "#"){
466                            fn[fn.length] = 'n = quickId(n, mode, root, "'+tokenMatch[2]+'");';			
467                        }else{
468                            fn[fn.length] = 'n = getNodes(n, mode, "'+tokenMatch[2]+'");';
469                        }
470                        path = path.replace(tokenMatch[0], "");
471                    }else if(path.substr(0, 1) != '@'){
472                        fn[fn.length] = 'n = getNodes(n, mode, "*");';
473                    }
474		// type of "simple"
475                }else{
476                    if(tokenMatch){
477                        if(tokenMatch[1] == "#"){
478                            fn[fn.length] = 'n = byId(n, "'+tokenMatch[2]+'");';
479                        }else{
480                            fn[fn.length] = 'n = byTag(n, "'+tokenMatch[2]+'");';
481                        }
482                        path = path.replace(tokenMatch[0], "");
483                    }
484                }
485                while(!(modeMatch = path.match(modeRe))){
486                    var matched = false;
487                    for(var j = 0; j < matchersLn; j++){
488                        var t = matchers[j];
489                        var m = path.match(t.re);
490                        if(m){
491                            fn[fn.length] = t.select.replace(tplRe, function(x, i){
492				return m[i];
493			    });
494                            path = path.replace(m[0], "");
495                            matched = true;
496                            break;
497                        }
498                    }
499                    // prevent infinite loop on bad selector
500                    if(!matched){
501                        throw 'Error parsing selector, parsing failed at "' + path + '"';
502                    }
503                }
504                if(modeMatch[1]){
505                    fn[fn.length] = 'mode="'+modeMatch[1].replace(trimRe, "")+'";';
506                    path = path.replace(modeMatch[1], "");
507                }
508            }
509	    // close fn out
510            fn[fn.length] = "return nodup(n);\n}";
511	    
512	    // eval fn and return it
513            eval(fn.join(""));
514            return f;
515        },
516
517        /**
518         * Selects a group of elements.
519         * @param {String} selector The selector/xpath query (can be a comma separated list of selectors)
520         * @param {Node/String} root (optional) The start of the query (defaults to document).
521         * @return {Array} An Array of DOM elements which match the selector. If there are
522         * no matches, and empty Array is returned.
523         */
524	jsSelect: function(path, root, type){
525	    // set root to doc if not specified.
526	    root = root || document;
527	    
528            if(typeof root == "string"){
529                root = document.getElementById(root);
530            }
531            var paths = path.split(","),
532            	results = [];
533		
534	    // loop over each selector
535            for(var i = 0, len = paths.length; i < len; i++){		
536                var subPath = paths[i].replace(trimRe, "");
537		// compile and place in cache
538                if(!cache[subPath]){
539                    cache[subPath] = Ext.DomQuery.compile(subPath);
540                    if(!cache[subPath]){
541                        throw subPath + " is not a valid selector";
542                    }
543                }
544                var result = cache[subPath](root);
545                if(result && result != document){
546                    results = results.concat(result);
547                }
548            }
549	    
550	    // if there were multiple selectors, make sure dups
551	    // are eliminated
552            if(paths.length > 1){
553                return nodup(results);
554            }
555            return results;
556        },
557	isXml: function(el) {
558	    var docEl = (el ? el.ownerDocument || el : 0).documentElement;
559	    return docEl ? docEl.nodeName !== "HTML" : false;
560	},
561        select : document.querySelectorAll ? function(path, root, type) {
562	    root = root || document;
563	    if (!Ext.DomQuery.isXml(root)) {
564		try {
565		    var cs = root.querySelectorAll(path);
566		    return Ext.toArray(cs);
567		}
568		catch (ex) {}		
569	    }	    
570	    return Ext.DomQuery.jsSelect.call(this, path, root, type);
571	} : function(path, root, type) {
572	    return Ext.DomQuery.jsSelect.call(this, path, root, type);
573	},
574
575        /**
576         * Selects a single element.
577         * @param {String} selector The selector/xpath query
578         * @param {Node} root (optional) The start of the query (defaults to document).
579         * @return {Element} The DOM element which matched the selector.
580         */
581        selectNode : function(path, root){
582            return Ext.DomQuery.select(path, root)[0];
583        },
584
585        /**
586         * Selects the value of a node, optionally replacing null with the defaultValue.
587         * @param {String} selector The selector/xpath query
588         * @param {Node} root (optional) The start of the query (defaults to document).
589         * @param {String} defaultValue
590         * @return {String}
591         */
592        selectValue : function(path, root, defaultValue){
593            path = path.replace(trimRe, "");
594            if(!valueCache[path]){
595                valueCache[path] = Ext.DomQuery.compile(path, "select");
596            }
597            var n = valueCache[path](root), v;
598            n = n[0] ? n[0] : n;
599            	    
600	    // overcome a limitation of maximum textnode size
601	    // Rumored to potentially crash IE6 but has not been confirmed.
602	    // http://reference.sitepoint.com/javascript/Node/normalize
603	    // https://developer.mozilla.org/En/DOM/Node.normalize	    
604            if (typeof n.normalize == 'function') n.normalize();
605            
606            v = (n && n.firstChild ? n.firstChild.nodeValue : null);
607            return ((v === null||v === undefined||v==='') ? defaultValue : v);
608        },
609
610        /**
611         * Selects the value of a node, parsing integers and floats. Returns the defaultValue, or 0 if none is specified.
612         * @param {String} selector The selector/xpath query
613         * @param {Node} root (optional) The start of the query (defaults to document).
614         * @param {Number} defaultValue
615         * @return {Number}
616         */
617        selectNumber : function(path, root, defaultValue){
618            var v = Ext.DomQuery.selectValue(path, root, defaultValue || 0);
619            return parseFloat(v);
620        },
621
622        /**
623         * Returns true if the passed element(s) match the passed simple selector (e.g. div.some-class or span:first-child)
624         * @param {String/HTMLElement/Array} el An element id, element or array of elements
625         * @param {String} selector The simple selector to test
626         * @return {Boolean}
627         */
628        is : function(el, ss){
629            if(typeof el == "string"){
630                el = document.getElementById(el);
631            }
632            var isArray = Ext.isArray(el),
633            	result = Ext.DomQuery.filter(isArray ? el : [el], ss);
634            return isArray ? (result.length == el.length) : (result.length > 0);
635        },
636
637        /**
638         * Filters an array of elements to only include matches of a simple selector (e.g. div.some-class or span:first-child)
639         * @param {Array} el An array of elements to filter
640         * @param {String} selector The simple selector to test
641         * @param {Boolean} nonMatches If true, it returns the elements that DON'T match
642         * the selector instead of the ones that match
643         * @return {Array} An Array of DOM elements which match the selector. If there are
644         * no matches, and empty Array is returned.
645         */
646        filter : function(els, ss, nonMatches){
647            ss = ss.replace(trimRe, "");
648            if(!simpleCache[ss]){
649                simpleCache[ss] = Ext.DomQuery.compile(ss, "simple");
650            }
651            var result = simpleCache[ss](els);
652            return nonMatches ? quickDiff(result, els) : result;
653        },
654
655        /**
656         * Collection of matching regular expressions and code snippets.
657         * Each capture group within () will be replace the {} in the select
658         * statement as specified by their index.
659         */
660        matchers : [{
661                re: /^\.([\w-]+)/,
662                select: 'n = byClassName(n, " {1} ");'
663            }, {
664                re: /^\:([\w-]+)(?:\(((?:[^\s>\/]*|.*?))\))?/,
665                select: 'n = byPseudo(n, "{1}", "{2}");'
666            },{
667                re: /^(?:([\[\{])(?:@)?([\w-]+)\s?(?:(=|.=)\s?['"]?(.*?)["']?)?[\]\}])/,
668                select: 'n = byAttribute(n, "{2}", "{4}", "{3}", "{1}");'
669            }, {
670                re: /^#([\w-]+)/,
671                select: 'n = byId(n, "{1}");'
672            },{
673                re: /^@([\w-]+)/,
674                select: 'return {firstChild:{nodeValue:attrValue(n, "{1}")}};'
675            }
676        ],
677
678        /**
679         * Collection of operator comparison functions. The default operators are =, !=, ^=, $=, *=, %=, |= and ~=.
680         * New operators can be added as long as the match the format <i>c</i>= where <i>c</i> is any character other than space, &gt; &lt;.
681         */
682        operators : {
683            "=" : function(a, v){
684                return a == v;
685            },
686            "!=" : function(a, v){
687                return a != v;
688            },
689            "^=" : function(a, v){
690                return a && a.substr(0, v.length) == v;
691            },
692            "$=" : function(a, v){
693                return a && a.substr(a.length-v.length) == v;
694            },
695            "*=" : function(a, v){
696                return a && a.indexOf(v) !== -1;
697            },
698            "%=" : function(a, v){
699                return (a % v) == 0;
700            },
701            "|=" : function(a, v){
702                return a && (a == v || a.substr(0, v.length+1) == v+'-');
703            },
704            "~=" : function(a, v){
705                return a && (' '+a+' ').indexOf(' '+v+' ') != -1;
706            }
707        },
708
709        /**
710         * <p>Object hash of "pseudo class" filter functions which are used when filtering selections. Each function is passed
711         * two parameters:</p><div class="mdetail-params"><ul>
712         * <li><b>c</b> : Array<div class="sub-desc">An Array of DOM elements to filter.</div></li>
713         * <li><b>v</b> : String<div class="sub-desc">The argument (if any) supplied in the selector.</div></li>
714         * </ul></div>
715         * <p>A filter function returns an Array of DOM elements which conform to the pseudo class.</p>
716         * <p>In addition to the provided pseudo classes listed above such as <code>first-child</code> and <code>nth-child</code>,
717         * developers may add additional, custom psuedo class filters to select elements according to application-specific requirements.</p>
718         * <p>For example, to filter <code>&lt;a></code> elements to only return links to <i>external</i> resources:</p>
719         * <code><pre>
720Ext.DomQuery.pseudos.external = function(c, v){
721    var r = [], ri = -1;
722    for(var i = 0, ci; ci = c[i]; i++){
723//      Include in result set only if it's a link to an external resource
724        if(ci.hostname != location.hostname){
725            r[++ri] = ci;
726        }
727    }
728    return r;
729};</pre></code>
730         * Then external links could be gathered with the following statement:<code><pre>
731var externalLinks = Ext.select("a:external");
732</code></pre>
733         */
734        pseudos : {
735            "first-child" : function(c){
736                var r = [], ri = -1, n;
737                for(var i = 0, ci; ci = n = c[i]; i++){
738                    while((n = n.previousSibling) && n.nodeType != 1);
739                    if(!n){
740                        r[++ri] = ci;
741                    }
742                }
743                return r;
744            },
745
746            "last-child" : function(c){
747                var r = [], ri = -1, n;
748                for(var i = 0, ci; ci = n = c[i]; i++){
749                    while((n = n.nextSibling) && n.nodeType != 1);
750                    if(!n){
751                        r[++ri] = ci;
752                    }
753                }
754                return r;
755            },
756
757            "nth-child" : function(c, a) {
758                var r = [], ri = -1,
759                	m = nthRe.exec(a == "even" && "2n" || a == "odd" && "2n+1" || !nthRe2.test(a) && "n+" + a || a),
760                	f = (m[1] || 1) - 0, l = m[2] - 0;
761                for(var i = 0, n; n = c[i]; i++){
762                    var pn = n.parentNode;
763                    if (batch != pn._batch) {
764                        var j = 0;
765                        for(var cn = pn.firstChild; cn; cn = cn.nextSibling){
766                            if(cn.nodeType == 1){
767                               cn.nodeIndex = ++j;
768                            }
769                        }
770                        pn._batch = batch;
771                    }
772                    if (f == 1) {
773                        if (l == 0 || n.nodeIndex == l){
774                            r[++ri] = n;
775                        }
776                    } else if ((n.nodeIndex + l) % f == 0){
777                        r[++ri] = n;
778                    }
779                }
780
781                return r;
782            },
783
784            "only-child" : function(c){
785                var r = [], ri = -1;;
786                for(var i = 0, ci; ci = c[i]; i++){
787                    if(!prev(ci) && !next(ci)){
788                        r[++ri] = ci;
789                    }
790                }
791                return r;
792            },
793
794            "empty" : function(c){
795                var r = [], ri = -1;
796                for(var i = 0, ci; ci = c[i]; i++){
797                    var cns = ci.childNodes, j = 0, cn, empty = true;
798                    while(cn = cns[j]){
799                        ++j;
800                        if(cn.nodeType == 1 || cn.nodeType == 3){
801                            empty = false;
802                            break;
803                        }
804                    }
805                    if(empty){
806                        r[++ri] = ci;
807                    }
808                }
809                return r;
810            },
811
812            "contains" : function(c, v){
813                var r = [], ri = -1;
814                for(var i = 0, ci; ci = c[i]; i++){
815                    if((ci.textContent||ci.innerText||'').indexOf(v) != -1){
816                        r[++ri] = ci;
817                    }
818                }
819                return r;
820            },
821
822            "nodeValue" : function(c, v){
823                var r = [], ri = -1;
824                for(var i = 0, ci; ci = c[i]; i++){
825                    if(ci.firstChild && ci.firstChild.nodeValue == v){
826                        r[++ri] = ci;
827                    }
828                }
829                return r;
830            },
831
832            "checked" : function(c){
833                var r = [], ri = -1;
834                for(var i = 0, ci; ci = c[i]; i++){
835                    if(ci.checked == true){
836                        r[++ri] = ci;
837                    }
838                }
839                return r;
840            },
841
842            "not" : function(c, ss){
843                return Ext.DomQuery.filter(c, ss, true);
844            },
845
846            "any" : function(c, selectors){
847                var ss = selectors.split('|'),
848                	r = [], ri = -1, s;
849                for(var i = 0, ci; ci = c[i]; i++){
850                    for(var j = 0; s = ss[j]; j++){
851                        if(Ext.DomQuery.is(ci, s)){
852                            r[++ri] = ci;
853                            break;
854                        }
855                    }
856                }
857                return r;
858            },
859
860            "odd" : function(c){
861                return this["nth-child"](c, "odd");
862            },
863
864            "even" : function(c){
865                return this["nth-child"](c, "even");
866            },
867
868            "nth" : function(c, a){
869                return c[a-1] || [];
870            },
871
872            "first" : function(c){
873                return c[0] || [];
874            },
875
876            "last" : function(c){
877                return c[c.length-1] || [];
878            },
879
880            "has" : function(c, ss){
881                var s = Ext.DomQuery.select,
882                	r = [], ri = -1;
883                for(var i = 0, ci; ci = c[i]; i++){
884                    if(s(ss, ci).length > 0){
885                        r[++ri] = ci;
886                    }
887                }
888                return r;
889            },
890
891            "next" : function(c, ss){
892                var is = Ext.DomQuery.is,
893                	r = [], ri = -1;
894                for(var i = 0, ci; ci = c[i]; i++){
895                    var n = next(ci);
896                    if(n && is(n, ss)){
897                        r[++ri] = ci;
898                    }
899                }
900                return r;
901            },
902
903            "prev" : function(c, ss){
904                var is = Ext.DomQuery.is,
905                	r = [], ri = -1;
906                for(var i = 0, ci; ci = c[i]; i++){
907                    var n = prev(ci);
908                    if(n && is(n, ss)){
909                        r[++ri] = ci;
910                    }
911                }
912                return r;
913            }
914        }
915    };
916}();
917
918/**
919 * Selects an array of DOM nodes by CSS/XPath selector. Shorthand of {@link Ext.DomQuery#select}
920 * @param {String} path The selector/xpath query
921 * @param {Node} root (optional) The start of the query (defaults to document).
922 * @return {Array}
923 * @member Ext
924 * @method query
925 */
926Ext.query = Ext.DomQuery.select;