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