/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

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