PageRenderTime 51ms CodeModel.GetById 10ms RepoModel.GetById 0ms app.codeStats 0ms

/testing/selenium-core/lib/cssQuery/src/cssQuery.js

http://datanucleus-appengine.googlecode.com/
JavaScript | 356 lines | 232 code | 42 blank | 82 comment | 77 complexity | 816593f0a57363036be40fbfbe909a76 MD5 | raw file
Possible License(s): Apache-2.0
  1. /*
  2. cssQuery, version 2.0.2 (2005-08-19)
  3. Copyright: 2004-2005, Dean Edwards (http://dean.edwards.name/)
  4. License: http://creativecommons.org/licenses/LGPL/2.1/
  5. */
  6. // the following functions allow querying of the DOM using CSS selectors
  7. var cssQuery = function() {
  8. var version = "2.0.2";
  9. // -----------------------------------------------------------------------
  10. // main query function
  11. // -----------------------------------------------------------------------
  12. var $COMMA = /\s*,\s*/;
  13. var cssQuery = function($selector, $$from) {
  14. try {
  15. var $match = [];
  16. var $useCache = arguments.callee.caching && !$$from;
  17. var $base = ($$from) ? ($$from.constructor == Array) ? $$from : [$$from] : [document];
  18. // process comma separated selectors
  19. var $$selectors = parseSelector($selector).split($COMMA), i;
  20. for (i = 0; i < $$selectors.length; i++) {
  21. // convert the selector to a stream
  22. $selector = _toStream($$selectors[i]);
  23. // faster chop if it starts with id (MSIE only)
  24. if (isMSIE && $selector.slice(0, 3).join("") == " *#") {
  25. $selector = $selector.slice(2);
  26. $$from = _msie_selectById([], $base, $selector[1]);
  27. } else $$from = $base;
  28. // process the stream
  29. var j = 0, $token, $filter, $arguments, $cacheSelector = "";
  30. while (j < $selector.length) {
  31. $token = $selector[j++];
  32. $filter = $selector[j++];
  33. $cacheSelector += $token + $filter;
  34. // some pseudo-classes allow arguments to be passed
  35. // e.g. nth-child(even)
  36. $arguments = "";
  37. if ($selector[j] == "(") {
  38. while ($selector[j++] != ")" && j < $selector.length) {
  39. $arguments += $selector[j];
  40. }
  41. $arguments = $arguments.slice(0, -1);
  42. $cacheSelector += "(" + $arguments + ")";
  43. }
  44. // process a token/filter pair use cached results if possible
  45. $$from = ($useCache && cache[$cacheSelector]) ?
  46. cache[$cacheSelector] : select($$from, $token, $filter, $arguments);
  47. if ($useCache) cache[$cacheSelector] = $$from;
  48. }
  49. $match = $match.concat($$from);
  50. }
  51. delete cssQuery.error;
  52. return $match;
  53. } catch ($error) {
  54. cssQuery.error = $error;
  55. return [];
  56. }};
  57. // -----------------------------------------------------------------------
  58. // public interface
  59. // -----------------------------------------------------------------------
  60. cssQuery.toString = function() {
  61. return "function cssQuery() {\n [version " + version + "]\n}";
  62. };
  63. // caching
  64. var cache = {};
  65. cssQuery.caching = false;
  66. cssQuery.clearCache = function($selector) {
  67. if ($selector) {
  68. $selector = _toStream($selector).join("");
  69. delete cache[$selector];
  70. } else cache = {};
  71. };
  72. // allow extensions
  73. var modules = {};
  74. var loaded = false;
  75. cssQuery.addModule = function($name, $script) {
  76. if (loaded) eval("$script=" + String($script));
  77. modules[$name] = new $script();;
  78. };
  79. // hackery
  80. cssQuery.valueOf = function($code) {
  81. return $code ? eval($code) : this;
  82. };
  83. // -----------------------------------------------------------------------
  84. // declarations
  85. // -----------------------------------------------------------------------
  86. var selectors = {};
  87. var pseudoClasses = {};
  88. // a safari bug means that these have to be declared here
  89. var AttributeSelector = {match: /\[([\w-]+(\|[\w-]+)?)\s*(\W?=)?\s*([^\]]*)\]/};
  90. var attributeSelectors = [];
  91. // -----------------------------------------------------------------------
  92. // selectors
  93. // -----------------------------------------------------------------------
  94. // descendant selector
  95. selectors[" "] = function($results, $from, $tagName, $namespace) {
  96. // loop through current selection
  97. var $element, i, j;
  98. for (i = 0; i < $from.length; i++) {
  99. // get descendants
  100. var $subset = getElementsByTagName($from[i], $tagName, $namespace);
  101. // loop through descendants and add to results selection
  102. for (j = 0; ($element = $subset[j]); j++) {
  103. if (thisElement($element) && compareNamespace($element, $namespace))
  104. $results.push($element);
  105. }
  106. }
  107. };
  108. // ID selector
  109. selectors["#"] = function($results, $from, $id) {
  110. // loop through current selection and check ID
  111. var $element, j;
  112. for (j = 0; ($element = $from[j]); j++) if ($element.id == $id) $results.push($element);
  113. };
  114. // class selector
  115. selectors["."] = function($results, $from, $className) {
  116. // create a RegExp version of the class
  117. $className = new RegExp("(^|\\s)" + $className + "(\\s|$)");
  118. // loop through current selection and check class
  119. var $element, i;
  120. for (i = 0; ($element = $from[i]); i++)
  121. if ($className.test($element.className)) $results.push($element);
  122. };
  123. // pseudo-class selector
  124. selectors[":"] = function($results, $from, $pseudoClass, $arguments) {
  125. // retrieve the cssQuery pseudo-class function
  126. var $test = pseudoClasses[$pseudoClass], $element, i;
  127. // loop through current selection and apply pseudo-class filter
  128. if ($test) for (i = 0; ($element = $from[i]); i++)
  129. // if the cssQuery pseudo-class function returns "true" add the element
  130. if ($test($element, $arguments)) $results.push($element);
  131. };
  132. // -----------------------------------------------------------------------
  133. // pseudo-classes
  134. // -----------------------------------------------------------------------
  135. pseudoClasses["link"] = function($element) {
  136. var $document = getDocument($element);
  137. if ($document.links) for (var i = 0; i < $document.links.length; i++) {
  138. if ($document.links[i] == $element) return true;
  139. }
  140. };
  141. pseudoClasses["visited"] = function($element) {
  142. // can't do this without jiggery-pokery
  143. };
  144. // -----------------------------------------------------------------------
  145. // DOM traversal
  146. // -----------------------------------------------------------------------
  147. // IE5/6 includes comments (LOL) in it's elements collections.
  148. // so we have to check for this. the test is tagName != "!". LOL (again).
  149. var thisElement = function($element) {
  150. return ($element && $element.nodeType == 1 && $element.tagName != "!") ? $element : null;
  151. };
  152. // return the previous element to the supplied element
  153. // previousSibling is not good enough as it might return a text or comment node
  154. var previousElementSibling = function($element) {
  155. while ($element && ($element = $element.previousSibling) && !thisElement($element)) continue;
  156. return $element;
  157. };
  158. // return the next element to the supplied element
  159. var nextElementSibling = function($element) {
  160. while ($element && ($element = $element.nextSibling) && !thisElement($element)) continue;
  161. return $element;
  162. };
  163. // return the first child ELEMENT of an element
  164. // NOT the first child node (though they may be the same thing)
  165. var firstElementChild = function($element) {
  166. return thisElement($element.firstChild) || nextElementSibling($element.firstChild);
  167. };
  168. var lastElementChild = function($element) {
  169. return thisElement($element.lastChild) || previousElementSibling($element.lastChild);
  170. };
  171. // return child elements of an element (not child nodes)
  172. var childElements = function($element) {
  173. var $childElements = [];
  174. $element = firstElementChild($element);
  175. while ($element) {
  176. $childElements.push($element);
  177. $element = nextElementSibling($element);
  178. }
  179. return $childElements;
  180. };
  181. // -----------------------------------------------------------------------
  182. // browser compatibility
  183. // -----------------------------------------------------------------------
  184. // all of the functions in this section can be overwritten. the default
  185. // configuration is for IE. The functions below reflect this. standard
  186. // methods are included in a separate module. It would probably be better
  187. // the other way round of course but this makes it easier to keep IE7 trim.
  188. var isMSIE = true;
  189. var isXML = function($element) {
  190. var $document = getDocument($element);
  191. return (typeof $document.mimeType == "unknown") ?
  192. /\.xml$/i.test($document.URL) :
  193. Boolean($document.mimeType == "XML Document");
  194. };
  195. // return the element's containing document
  196. var getDocument = function($element) {
  197. return $element.ownerDocument || $element.document;
  198. };
  199. var getElementsByTagName = function($element, $tagName) {
  200. return ($tagName == "*" && $element.all) ? $element.all : $element.getElementsByTagName($tagName);
  201. };
  202. var compareTagName = function($element, $tagName, $namespace) {
  203. if ($tagName == "*") return thisElement($element);
  204. if (!compareNamespace($element, $namespace)) return false;
  205. if (!isXML($element)) $tagName = $tagName.toUpperCase();
  206. return $element.tagName == $tagName;
  207. };
  208. var compareNamespace = function($element, $namespace) {
  209. return !$namespace || ($namespace == "*") || ($element.scopeName == $namespace);
  210. };
  211. var getTextContent = function($element) {
  212. return $element.innerText;
  213. };
  214. function _msie_selectById($results, $from, id) {
  215. var $match, i, j;
  216. for (i = 0; i < $from.length; i++) {
  217. if ($match = $from[i].all.item(id)) {
  218. if ($match.id == id) $results.push($match);
  219. else if ($match.length != null) {
  220. for (j = 0; j < $match.length; j++) {
  221. if ($match[j].id == id) $results.push($match[j]);
  222. }
  223. }
  224. }
  225. }
  226. return $results;
  227. };
  228. // for IE5.0
  229. if (![].push) Array.prototype.push = function() {
  230. for (var i = 0; i < arguments.length; i++) {
  231. this[this.length] = arguments[i];
  232. }
  233. return this.length;
  234. };
  235. // -----------------------------------------------------------------------
  236. // query support
  237. // -----------------------------------------------------------------------
  238. // select a set of matching elements.
  239. // "from" is an array of elements.
  240. // "token" is a character representing the type of filter
  241. // e.g. ">" means child selector
  242. // "filter" represents the tag name, id or class name that is being selected
  243. // the function returns an array of matching elements
  244. var $NAMESPACE = /\|/;
  245. function select($$from, $token, $filter, $arguments) {
  246. if ($NAMESPACE.test($filter)) {
  247. $filter = $filter.split($NAMESPACE);
  248. $arguments = $filter[0];
  249. $filter = $filter[1];
  250. }
  251. var $results = [];
  252. if (selectors[$token]) {
  253. selectors[$token]($results, $$from, $filter, $arguments);
  254. }
  255. return $results;
  256. };
  257. // -----------------------------------------------------------------------
  258. // parsing
  259. // -----------------------------------------------------------------------
  260. // convert css selectors to a stream of tokens and filters
  261. // it's not a real stream. it's just an array of strings.
  262. var $STANDARD_SELECT = /^[^\s>+~]/;
  263. var $$STREAM = /[\s#.:>+~()@]|[^\s#.:>+~()@]+/g;
  264. function _toStream($selector) {
  265. if ($STANDARD_SELECT.test($selector)) $selector = " " + $selector;
  266. return $selector.match($$STREAM) || [];
  267. };
  268. var $WHITESPACE = /\s*([\s>+~(),]|^|$)\s*/g;
  269. var $IMPLIED_ALL = /([\s>+~,]|[^(]\+|^)([#.:@])/g;
  270. var parseSelector = function($selector) {
  271. return $selector
  272. // trim whitespace
  273. .replace($WHITESPACE, "$1")
  274. // e.g. ".class1" --> "*.class1"
  275. .replace($IMPLIED_ALL, "$1*$2");
  276. };
  277. var Quote = {
  278. toString: function() {return "'"},
  279. match: /^('[^']*')|("[^"]*")$/,
  280. test: function($string) {
  281. return this.match.test($string);
  282. },
  283. add: function($string) {
  284. return this.test($string) ? $string : this + $string + this;
  285. },
  286. remove: function($string) {
  287. return this.test($string) ? $string.slice(1, -1) : $string;
  288. }
  289. };
  290. var getText = function($text) {
  291. return Quote.remove($text);
  292. };
  293. var $ESCAPE = /([\/()[\]?{}|*+-])/g;
  294. function regEscape($string) {
  295. return $string.replace($ESCAPE, "\\$1");
  296. };
  297. // -----------------------------------------------------------------------
  298. // modules
  299. // -----------------------------------------------------------------------
  300. // -------- >> insert modules here for packaging << -------- \\
  301. loaded = true;
  302. // -----------------------------------------------------------------------
  303. // return the query function
  304. // -----------------------------------------------------------------------
  305. return cssQuery;
  306. }(); // cssQuery