src/selector.js
author Laurens Holst <laurens.hg@grauw.nl>
Thu May 20 13:48:31 2010 +0200 (2 months ago)
changeset 269 d518030b550b
parent 246 39bd2745ca20
permissions -rw-r--r--
uri: Move exports to the end of the file.
     1 /**
     2  * @fileOverview
     3  * 
     4  * Grauw CSS Selectors
     5  * 
     6  * Public methods:
     7  * - gl.selector.match(node, selector)
     8  * - gl.selector.query(node, selector)
     9  * - gl.selector.queryAll(node, selector)
    10  * - gl.selector.queryAncestorOrSelf(node, selector)
    11  * 
    12  * See: http://www.grauw.nl/projects/selectors/
    13  * 
    14  * @version 1.1.3
    15  * @author Laurens Holst (http://www.grauw.nl/)
    16  * 
    17  *   Copyright 2010 Laurens Holst
    18  * 
    19  *   Licensed under the Apache License, Version 2.0 (the "License");
    20  *   you may not use this file except in compliance with the License.
    21  *   You may obtain a copy of the License at
    22  * 
    23  *       http://www.apache.org/licenses/LICENSE-2.0
    24  * 
    25  *   Unless required by applicable law or agreed to in writing, software
    26  *   distributed under the License is distributed on an "AS IS" BASIS,
    27  *   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    28  *   See the License for the specific language governing permissions and
    29  *   limitations under the License.
    30  * 
    31  */
    32 
    33 if (!window.gl)
    34 	gl = {};
    35 
    36 /**
    37  * The main CSS Selectors object.
    38  * Acts as sort of a namespace; all other selector objects are closures of the instance of this object.
    39  */
    40 gl.selector = new function() {
    41 
    42 	var browser_ie = !!window.ActiveXObject;
    43 
    44 	function extend(cBase, fConstructor) {
    45 		var cPrototype = new Function();
    46 		cPrototype.prototype = cBase.prototype;
    47 		fConstructor.prototype = new cPrototype();
    48 		fConstructor.prototype.constructor = fConstructor;
    49 		return fConstructor;
    50 	}
    51 
    52 	/**
    53 	 * Helper function for cross-browser hasAttribute that ignores default values
    54 	 * See: http://www.w3.org/TR/css3-selectors/#def-values
    55 	 * Note: IE’s hasAttribute has issues with ‘class’ vs. ‘className’, ‘for’ vs. ‘htmlFor’, and ‘enctype’ vs. ‘encType’.
    56 	 * Note: other browsers ignore attribute default values, even though spec says they shouldn’t
    57 	 */
    58 	function hasAttribute(oNode, sName) {
    59 		if (browser_ie) {
    60 			var oAttr = oNode.getAttributeNode(sName);
    61 			return !!oAttr && oAttr.specified;
    62 		}
    63 		return oNode.hasAttribute(sName);
    64 	}
    65 
    66 	/**
    67 	 * Helper function for cross-browser getAttribute that ignores default values
    68 	 * See: http://www.w3.org/TR/css3-selectors/#def-values
    69 	 * Note: IE’s getAttribute has issues with ‘class’ vs. ‘className’, ‘for’ vs. ‘htmlFor’, and ‘enctype’ vs. ‘encType’.
    70 	 * Note: other browsers ignore attribute default values, even though spec says they shouldn’t
    71 	 */
    72 	function getAttribute(oNode, sName) {
    73 		if (browser_ie) {
    74 			var oAttr = oNode.getAttributeNode(sName);
    75 			return oAttr && oAttr.specified ? oAttr.nodeValue : '';
    76 		}
    77 		return oNode.getAttribute(sName) || '';
    78 	}
    79 
    80 	/**
    81 	 * Helper function for cross-browser getElementsByTagName('*').
    82 	 * In particular, ignores HTMLCommentElement nodes thrown at you by Internet Explorer.
    83 	 */
    84 	function getAllDescendants(oNode) {
    85 		var aDescendants = oNode.getElementsByTagName('*');
    86 		if (!browser_ie)
    87 			return aDescendants;
    88 		var aResult = [];
    89 		for (var i = 0, len = aDescendants.length; i < len; i++) {
    90 			if (aDescendants[i].nodeType == 1)
    91 				aResult.push(aDescendants[i]);
    92 		}
    93 		return aResult;
    94 	}
    95 
    96 
    97 	/**
    98 	 * Matches a node against a CSS selector or selector group.
    99 	 * Supports all CSS3 Selectors, except for pseudo-elements and the following
   100 	 * pseuro-classes: :link, :visited, :hover, :active, :focus.
   101 	 * @param node The node
   102 	 * @param selector The selector
   103 	 * @return true if the selector matches.
   104 	 */
   105 	function match(oNode, sSelector) {
   106 		var oSelectorGroup = oParser.parse(sSelector);
   107 		return oSelectorGroup ? oSelectorGroup.match(oNode) : false;
   108 	}
   109 
   110 	/**
   111 	 * Queries a CSS selector or selector group against a node.
   112 	 * @param node The node
   113 	 * @param selector The selector
   114 	 * @return An array containing the selected nodes.
   115 	 */
   116 	function queryAll(oNode, sSelector) {
   117 		if (oNode.nodeType == 9)
   118 			oNode = oNode.documentElement;
   119 		var oSelectorGroup = oParser.parse(sSelector);
   120 		return oSelectorGroup ? oSelectorGroup.query(oNode, false) : [];
   121 	}
   122 
   123 	/**
   124 	 * Queries a CSS selector or selector group against a node and returns one node.
   125 	 * @param node The node
   126 	 * @param selector The selector
   127 	 * @return The selected node.
   128 	 */
   129 	function query(oNode, sSelector) {
   130 		if (oNode.nodeType == 9)
   131 			oNode = oNode.documentElement;
   132 		var oSelectorGroup = oParser.parse(sSelector);
   133 		var aResult = oSelectorGroup ? oSelectorGroup.query(oNode, true) : [];
   134 		return aResult.length ? aResult[0] : null;
   135 	}
   136 
   137 	/**
   138 	 * Use a CSS selector to return the first selection found on a node or its ancestors.
   139 	 * @param node The node
   140 	 * @param selector The selector
   141 	 * @param stop Stop querying after arriving at this node (optional)
   142 	 * @return The selected node.
   143 	 */
   144 	function queryAncestorOrSelf(oNode, sSelector, oStop) {
   145 		var oSelectorGroup = oParser.parse(sSelector);
   146 		if (!oSelectorGroup)
   147 			return null;
   148 
   149 		while (oNode && oNode.nodeType == 1) {
   150 			if (oSelectorGroup.match(oNode))
   151 				return oNode;
   152 			oNode = oNode.parentNode;
   153 			if (oNode == oStop)
   154 				break;
   155 		}
   156 		return null;
   157 	}
   158 
   159 	/**
   160 	 * Queries a CSS selector or selector group against the document and returns one node.
   161 	 * @param selector The selector
   162 	 * @return The selected node.
   163 	 */
   164 	function queryDocument(sSelector) {
   165 		return query(document.documentElement, sSelector);
   166 	}
   167 
   168 	/**
   169 	 * Queries a CSS selector or selector group against the document.
   170 	 * @param selector The selector
   171 	 * @return Array containing the selected nodes.
   172 	 */
   173 	function queryDocumentAll(sSelector) {
   174 		return queryAll(document.documentElement, sSelector);
   175 	}
   176 
   177 
   178 
   179 
   180 
   181 	/**
   182 	 * SelectorGroup class
   183 	 * See: http://www.w3.org/TR/css3-selectors/#selector-syntax
   184 	 */
   185 	var SelectorGroup = function() {
   186 		this.selectors = [];
   187 	};
   188 
   189 	/**
   190 	 * Add a selector to the selector group.
   191 	 * @param selector The selector
   192 	 */
   193 	SelectorGroup.prototype.add = function(oSelector) {
   194 		if (!oSelector instanceof Selector)
   195 			throw new Error('Argument must be a Selector.');
   196 		this.selectors[this.selectors.length] = oSelector;
   197 	};
   198 
   199 	/**
   200 	 * Check whether the selector is in a valid final state.
   201 	 * @return true if valid final state
   202 	 */
   203 	SelectorGroup.prototype.validate = function() {
   204 		var iLen = this.selectors.length,
   205 			bValid = iLen > 0;
   206 		for (var i = 0; i < iLen && bValid; i++)
   207 			bValid = this.selectors[i].validate();
   208 		return bValid;
   209 	};
   210 
   211 	/**
   212 	 * Matches a node against this selector group.
   213 	 * @param node The node
   214 	 * @return true if the selector matches.
   215 	 */
   216 	SelectorGroup.prototype.match = function(oNode) {
   217 		var oSelector,
   218 			bMatch = false;
   219 		for (var i = 0, iLen = this.selectors.length; i < iLen && !bMatch; i++) {
   220 			oSelector = this.selectors[i];
   221 			bMatch = oSelector.match(oNode);
   222 		}
   223 		return bMatch;
   224 	};
   225 
   226 	/**
   227 	 * Queries a CSS selector group against a node.
   228 	 * @param node The node
   229 	 * @param singleResult If true, returns immediately when first match is found.
   230 	 * @return The result array
   231 	 */
   232 	SelectorGroup.prototype.query = function(oNode, bSingleResult) {
   233 		var aResult = [],
   234 			bMatch = false;
   235 		for (var i = 0, iLen = this.selectors.length; i < iLen && !(bSingleResult && bMatch); i++)
   236 			bMatch = this.selectors[i].query(oNode, aResult, bSingleResult) || bMatch;
   237 		// reset __queryResult variable
   238 		for (var i = 0, iLen = aResult.length; i < iLen; i++)
   239 			aResult[i].__queryResult = false;
   240 		return aResult;
   241 	};
   242 
   243 
   244 
   245 
   246 	/**
   247 	 * Selector class
   248 	 * See: http://www.w3.org/TR/css3-selectors/#selectors
   249 	 */
   250 	var Selector = function() {
   251 		this.combinators = [];
   252 	};
   253 
   254 	/**
   255 	 * Add a combinator to the selector.
   256 	 * @param combinator The combinator
   257 	 */
   258 	Selector.prototype.add = function(oCombinator) {
   259 		if (!oCombinator instanceof Combinator)
   260 			throw new Error('Argument must be a Combinator.');
   261 		var iLen = this.combinators.length;
   262 		if (iLen > 0) {
   263 			this.combinators[iLen - 1].nextCombinator = oCombinator;
   264 			oCombinator.previousCombinator = this.combinators[iLen - 1];
   265 		}
   266 		this.combinators[iLen] = oCombinator;
   267 	};
   268 
   269 	/**
   270 	 * Check whether the selector is in a valid final state.
   271 	 * @return true if valid final state
   272 	 */
   273 	Selector.prototype.validate = function() {
   274 		var iLen = this.combinators.length,
   275 			bValid = iLen > 0,
   276 			oCombinator;
   277 		for (var i = 0; i < iLen && bValid; i++) {
   278 			oCombinator = this.combinators[i];
   279 			bValid = ((i == 0 && oCombinator instanceof RootCombinator) ||
   280 					 (i > 0 && !(oCombinator instanceof RootCombinator))) &&
   281 					oCombinator.validate();
   282 		}
   283 		return bValid;
   284 	};
   285 
   286 	/**
   287 	 * Matches a node against a selector.
   288 	 * @param node The node
   289 	 * @return true if the selector matches.
   290 	 */
   291 	Selector.prototype.match = function(oNode) {
   292 		return this.combinators[this.combinators.length - 1].match(oNode);
   293 	};
   294 
   295 	/**
   296 	 * Queries a CSS selector group against a node.
   297 	 * @param node The node
   298 	 * @param result The result array
   299 	 * @param singleResult If true, returns immediately when first match is found.
   300 	 * @return true if at least one match
   301 	 */
   302 	Selector.prototype.query = function(oNode, aResult, bSingleResult) {
   303 		return this.combinators[0].query(oNode, aResult, bSingleResult);
   304 	};
   305 
   306 
   307 
   308 
   309 	/**
   310 	 * Combinator class
   311 	 * Abstract base class for combinator implementations
   312 	 * See: http://www.w3.org/TR/css3-selectors/#combinators
   313 	 */
   314 	var Combinator = function() {
   315 		this.simpleSelectors = [];
   316 		this.nextCombinator = null;
   317 		this.previousCombinator = null;
   318 	};
   319 
   320 	/**
   321 	 * Add a simpleSelector to the combinator.
   322 	 * @param simpleSelector The simple selector
   323 	 */
   324 	Combinator.prototype.add = function(oSimpleSelector) {
   325 		if (!oSimpleSelector instanceof SimpleSelector)
   326 			throw new Error('Argument must be a SimpleSelector.');
   327 		this.simpleSelectors[this.simpleSelectors.length] = oSimpleSelector;
   328 	};
   329 
   330 	/**
   331 	 * Check whether the combinator is in a valid final state.
   332 	 * @return true if valid final state
   333 	 */
   334 	Combinator.prototype.validate = function() {
   335 		var iLen = this.simpleSelectors.length,
   336 			bValid = iLen > 0,
   337 			oSimpleSelector;
   338 		for (var i = 0; i < iLen && bValid; i++) {
   339 			oSimpleSelector = this.simpleSelectors[i];
   340 			bValid = (i == 0 || !(oSimpleSelector instanceof UniversalSelector)) &&
   341 					(i == 0 || !(oSimpleSelector instanceof TypeSelector)) &&
   342 					oSimpleSelector.validate();
   343 		}
   344 		return bValid;
   345 	};
   346 
   347 	/**
   348 	 * Matches a node against a combinator.
   349 	 * @param node The node
   350 	 * @param start The first simple selector to match
   351 	 * @return true if the selector matches.
   352 	 */
   353 	Combinator.prototype.matchSimpleSelectors = function(oNode, iStart) {
   354 		var bMatch = true;
   355 		for (var i = iStart || 0, iLen = this.simpleSelectors.length; i < iLen && bMatch; i++)
   356 			bMatch = this.simpleSelectors[i].match(oNode);
   357 		return bMatch;
   358 	};
   359 
   360 	/**
   361 	 * Matches a node against a combinator, and adds it to the query results when it matches.
   362 	 * @param node The node
   363 	 * @param result The result array
   364 	 * @param singleResult If true, returns immediately when first match is found.
   365 	 * @param start The first simpleSelector to match (used to skip selectors that have already been satisfied)
   366 	 * @return true if the selector matches.
   367 	 */
   368 	Combinator.prototype.querySimpleSelectors = function(oNode, aResult, bSingleResult, iStart) {
   369 		if (!this.nextCombinator) {
   370 			// if last combinator
   371 			if (oNode.__queryResult !== true && this.matchSimpleSelectors(oNode, iStart)) {
   372 				// __queryResult makes sure it never returns an element more than once
   373 				aResult[aResult.length] = oNode;
   374 				return oNode.__queryResult = true;
   375 			}
   376 			// if already matched before, pretend it doesn't match and skip
   377 			return false;
   378 		}
   379 		if (this.matchSimpleSelectors(oNode, iStart))
   380 			return this.nextCombinator.query(oNode, aResult, bSingleResult);
   381 		return false;
   382 	};
   383 
   384 	/**
   385 	 * Matches a node against a combinator.
   386 	 * @param node The node
   387 	 * @return true if the selector matches.
   388 	 */
   389 	Combinator.prototype.match = function(oNode) {
   390 		var bMatch = this.matchSimpleSelectors(oNode);
   391 		if (!bMatch)
   392 			return false;
   393 
   394 		return this.matchNext(oNode);
   395 	};
   396 
   397 	/**
   398 	 * Matches on the next combinator.
   399 	 * @param node The node
   400 	 * Note: this method is replaced with a matcher function.
   401 	 */
   402 	Combinator.prototype.matchNext = function(oNode) {
   403 //		throw new Error('Must implement...');
   404 	};
   405 
   406 	/**
   407 	 * Queries a CSS selector group against a selector.
   408 	 * Note: this method is replaced with a matcher function.
   409 	 * @param node The node
   410 	 * @param result The result array
   411 	 * @param singleResult If true, returns immediately when first match is found.
   412 	 */
   413 	Combinator.prototype.query = function(oNode, aResult, bSingleResult) {
   414 //		throw new Error('Must implement...');
   415 	};
   416 	
   417 	/**
   418 	 * Descendant combinator (white space)
   419 	 */
   420 	var DescendantCombinator = function() {
   421 		Combinator.call(this);
   422 	};
   423 	extend(Combinator, DescendantCombinator);
   424 	
   425 	DescendantCombinator.prototype.matchNext = function(oNode) {
   426 		var bMatch = false;
   427 		while (!bMatch && (oNode = oNode.parentNode) && oNode.nodeType == 1)
   428 			bMatch = this.previousCombinator.match(oNode);
   429 		return bMatch;
   430 	};
   431 	
   432 	DescendantCombinator.prototype.query = function(oNode, aResult, bSingleResult) {
   433 		var aNodes,
   434 			oSimpleSelector = this.simpleSelectors[0],
   435 			iStart = 0;
   436 		// Note: possible Firefox optimisation: use .getElementsByClassName
   437 		if (oSimpleSelector instanceof TypeSelector) {
   438 			aNodes = oNode.getElementsByTagName(oSimpleSelector.arg1);
   439 			iStart++;
   440 		} else {
   441 			if (oSimpleSelector instanceof IdSelector) {
   442 				var oNode2 = oNode.ownerDocument.getElementById(oSimpleSelector.arg1);
   443 				if (oNode2 && (oNode.contains ? oNode2 != oNode && oNode.contains(oNode2) : !!(oNode.compareDocumentPosition(oNode2) & 16)))
   444 					return this.querySimpleSelectors(oNode2, aResult, bSingleResult, 1);
   445 			}
   446 			aNodes = getAllDescendants(oNode);
   447 		}
   448 		var bMatch = false;
   449 		for (var i = 0, iLen = aNodes.length; i < iLen && !(bSingleResult && bMatch); i++) {
   450 			bMatch = this.querySimpleSelectors(aNodes[i], aResult, bSingleResult, iStart) || bMatch;
   451 		}
   452 		return bMatch;
   453 	};
   454 	
   455 	/**
   456 	 * Child combinator (>)
   457 	 */
   458 	var ChildCombinator = function() {
   459 		Combinator.call(this);
   460 	};
   461 	extend(Combinator, ChildCombinator);
   462 
   463 	ChildCombinator.prototype.matchNext = function(oNode) {
   464 		return !!(oNode = oNode.parentNode) && oNode.nodeType == 1 && this.previousCombinator.match(oNode);
   465 	};
   466 	
   467 	ChildCombinator.prototype.query = function(oNode, aResult, bSingleResult) {
   468 		var aNodes,
   469 			oSimpleSelector = this.simpleSelectors[0],
   470 			iStart = 0;
   471 		if (browser_ie && oSimpleSelector instanceof TypeSelector) {
   472 			aNodes = oNode.children.tags(oSimpleSelector.arg1);
   473 			iStart++;
   474 		} else {
   475 			if (oSimpleSelector instanceof IdSelector) {
   476 				var oNode2 = oNode.ownerDocument.getElementById(oSimpleSelector.arg1);
   477 				if (oNode2.parentNode == oNode)
   478 					return this.querySimpleSelectors(oNode2, aResult, bSingleResult, 1);
   479 			}
   480 			aNodes = oNode.children || oNode.childNodes;
   481 		}
   482 		var bMatch = false;
   483 		for (var i = 0, iLen = aNodes.length; i < iLen && !(bSingleResult && bMatch); i++) {
   484 			if (aNodes[i].nodeType == 1)
   485 				bMatch = this.querySimpleSelectors(aNodes[i], aResult, bSingleResult, iStart) || bMatch;
   486 		}
   487 		return bMatch;
   488 	};
   489 	
   490 	/**
   491 	 * Sibling combinator (+)
   492 	 */
   493 	var AdjacentCombinator = function() {
   494 		Combinator.call(this);
   495 	};
   496 	extend(Combinator, AdjacentCombinator);
   497 
   498 	AdjacentCombinator.prototype.matchNext = function(oNode) {
   499 		while ((oNode = oNode.previousSibling) && oNode.nodeType != 1)
   500 			;
   501 		return !!oNode && this.previousCombinator.match(oNode);
   502 	};
   503 	
   504 	AdjacentCombinator.prototype.query = function(oNode, aResult, bSingleResult) {
   505 		while (oNode = oNode.nextSibling) {
   506 			if (oNode.nodeType == 1) {
   507 				return this.querySimpleSelectors(oNode, aResult, bSingleResult, 0);
   508 			}
   509 		}
   510 		return false;
   511 	};
   512 	
   513 	/**
   514 	 * Sibling combinator (~)
   515 	 */
   516 	var SiblingCombinator = function() {
   517 		Combinator.call(this);
   518 	};
   519 	extend(Combinator, SiblingCombinator);
   520 
   521 	SiblingCombinator.prototype.matchNext = function(oNode) {
   522 		var bMatch = false;
   523 		while (!bMatch && (oNode = oNode.previousSibling)) {
   524 			if (oNode.nodeType == 1)
   525 				bMatch = this.previousCombinator.match(oNode);
   526 		}
   527 		return bMatch;
   528 	};
   529 	
   530 	SiblingCombinator.prototype.query = function(oNode, aResult, bSingleResult) {
   531 		var bMatch = false;
   532 		while ((oNode = oNode.nextSibling) && !(bSingleResult && bMatch)) {
   533 			if (oNode.nodeType == 1)
   534 				bMatch = this.querySimpleSelectors(oNode, aResult, bSingleResult, 0) || bMatch;
   535 		}
   536 		return bMatch;
   537 	};
   538 	
   539 	/**
   540 	 * Root combinator
   541 	 * This combinator does not actually exist, it just contains the first set of simple selectors.
   542 	 */
   543 	var RootCombinator = function() {
   544 		Combinator.call(this);
   545 	};
   546 	extend(Combinator, RootCombinator);
   547 
   548 	RootCombinator.prototype.matchNext = function(oNode) {
   549 		return true;
   550 	};
   551 	
   552 	RootCombinator.prototype.query = function(oNode, aResult, bSingleResult) {
   553 		var bMatch = this.querySimpleSelectors(oNode, aResult, bSingleResult, 0);
   554 		return DescendantCombinator.prototype.query.call(this, oNode, aResult, bSingleResult) || bMatch;
   555 	};
   556 
   557 
   558 
   559 
   560 
   561 	/**
   562 	 * Simple selector class
   563 	 * Abstract base class for selector implementations
   564 	 * See: http://www.w3.org/TR/css3-selectors/#simple-selectors
   565 	 */
   566 	var SimpleSelector = function() {
   567 	};
   568 
   569 	/**
   570 	 * Check whether the simpleSelector is in a valid final state.
   571 	 * @return true if valid final state
   572 	 */
   573 	SimpleSelector.prototype.validate = function() {
   574 		return true;
   575 	};
   576 
   577 	/**
   578 	 * Matches a node against a simple selector.
   579 	 * Note: this method is replaced with a matcher function.
   580 	 * @param node The node
   581 	 * @return true if the selector matches.
   582 	 */
   583 	SimpleSelector.prototype.match = function(oNode) {
   584 //		throw new Error('Must implement...');
   585 	};
   586 
   587 	/**
   588 	 * Universal selector (*)
   589 	 */
   590 	var UniversalSelector = function() {
   591 //		SimpleSelector.call(this);
   592 	};
   593 	extend(SimpleSelector, UniversalSelector);
   594 	
   595 	UniversalSelector.prototype.match = function(oNode) {
   596 		return true;
   597 	};
   598 
   599 	/**
   600 	 * Type selector (elementName)
   601 	 */
   602 	var TypeSelector = function(sArg1) {
   603 //		SimpleSelector.call(this);
   604 		this.arg1 = sArg1;
   605 	};
   606 	extend(SimpleSelector, TypeSelector);
   607 	
   608 	TypeSelector.prototype.match = function(oNode) {
   609 		var bXHTML = oNode.namespaceURI && oNode.namespaceURI != 'http://www.w3.org/1999/xhtml';
   610 		var sNodeName =  bXHTML ? oNode.nodeName : oNode.nodeName.toLowerCase();
   611 		var sSelectorName = bXHTML ? this.arg1 : this.arg1.toLowerCase();
   612 		return sNodeName == sSelectorName;
   613 	};
   614 
   615 	/**
   616 	 * Class selector (.className)
   617 	 */
   618 	var ClassSelector = function(sArg1) {
   619 //		SimpleSelector.call(this);
   620 		this.arg1 = sArg1;
   621 		
   622 		this.classRegexp = new RegExp('(^|\\s)' + Parser.escapeRegexp(sArg1) + '($|\\s)');
   623 	};
   624 	extend(SimpleSelector, ClassSelector);
   625 	
   626 	ClassSelector.prototype.match = function(oNode) {
   627 		return this.classRegexp.test(oNode.className);
   628 	};
   629 
   630 	/**
   631 	 * ID selector (#id)
   632 	 */
   633 	var IdSelector = function(sArg1) {
   634 //		SimpleSelector.call(this);
   635 		this.arg1 = sArg1;
   636 	};
   637 	extend(SimpleSelector, IdSelector);
   638 	
   639 	IdSelector.prototype.match = function(oNode) {
   640 		return oNode.getAttribute('id') == this.arg1;
   641 	};
   642 
   643 	/**
   644 	 * Attribute exist selector ([name])
   645 	 */
   646 	var AttributeExistSelector = function(sArg1) {
   647 //		SimpleSelector.call(this);
   648 		this.arg1 = sArg1;
   649 	};
   650 	extend(SimpleSelector, AttributeExistSelector);
   651 	
   652 	AttributeExistSelector.prototype.match = function(oNode) {
   653 		return hasAttribute(oNode, this.arg1);
   654 	};
   655 
   656 	/**
   657 	 * Attribute equals selector ([name=value])
   658 	 */
   659 	var AttributeEqualsSelector = function(sArg1, sArg2) {
   660 //		SimpleSelector.call(this);
   661 		this.arg1 = sArg1;
   662 		this.arg2 = sArg2;
   663 	};
   664 	extend(SimpleSelector, AttributeEqualsSelector);
   665 	
   666 	AttributeEqualsSelector.prototype.match = function(oNode) {
   667 		if (!hasAttribute(oNode, this.arg1))
   668 			return false;
   669 		return getAttribute(oNode, this.arg1) == this.arg2;
   670 	};
   671 
   672 	/**
   673 	 * Attribute list selector ([name~=value])
   674 	 */
   675 	var AttributeListSelector = function(sArg1, sArg2) {
   676 //		SimpleSelector.call(this);
   677 		this.arg1 = sArg1;
   678 		this.arg2 = sArg2;
   679 		
   680 		this.attrListRegexp = regexp_whiteSpace.test(sArg2) ?
   681 				/$x^/ : // regexp that doesn’t match anything
   682 				new RegExp('(^|\\s)' + Parser.escapeRegexp(sArg2) + '($|\\s)');
   683 	};
   684 	extend(SimpleSelector, AttributeListSelector);
   685 	
   686 	AttributeListSelector.prototype.match = function(oNode) {
   687 		return !!this.arg2 && this.attrListRegexp.test(getAttribute(oNode, this.arg1));
   688 	};
   689 
   690 	/**
   691 	 * Attribute language selector ([name!=en-US])
   692 	 */
   693 	var AttributeLangSelector = function(sArg1, sArg2) {
   694 //		SimpleSelector.call(this);
   695 		this.arg1 = sArg1;
   696 		this.arg2 = sArg2;
   697 	};
   698 	extend(SimpleSelector, AttributeLangSelector);
   699 	
   700 	AttributeLangSelector.prototype.match = function(oNode) {
   701 		if (!hasAttribute(oNode, this.arg1))
   702 			return false;
   703 		var sAttrValue = getAttribute(oNode, this.arg1);
   704 		return sAttrValue.toLowerCase().indexOf(this.arg2.toLowerCase()) == 0 &&
   705 				(this.arg2.length == sAttrValue.length || sAttrValue.charAt(this.arg2.length) == '-');
   706 	};
   707 
   708 	/**
   709 	 * Attribute start selector ([name^=val])
   710 	 */
   711 	var AttributeStartSelector = function(sArg1, sArg2) {
   712 //		SimpleSelector.call(this);
   713 		this.arg1 = sArg1;
   714 		this.arg2 = sArg2;
   715 	};
   716 	extend(SimpleSelector, AttributeStartSelector);
   717 	
   718 	AttributeStartSelector.prototype.match = function(oNode) {
   719 		return !!this.arg2 && getAttribute(oNode, this.arg1).indexOf(this.arg2) == 0;
   720 	};
   721 
   722 	/**
   723 	 * Attribute end selector ([name$=lue])
   724 	 */
   725 	var AttributeEndSelector = function(sArg1, sArg2) {
   726 //		SimpleSelector.call(this);
   727 		this.arg1 = sArg1;
   728 		this.arg2 = sArg2;
   729 	};
   730 	extend(SimpleSelector, AttributeEndSelector);
   731 	
   732 	AttributeEndSelector.prototype.match = function(oNode) {
   733 		var sAttrValue = getAttribute(oNode, this.arg1);
   734 		return !!this.arg2 && sAttrValue.indexOf(this.arg2) == (sAttrValue.length - this.arg2.length);
   735 	};
   736 
   737 	/**
   738 	 * Attribute contains selector ([name*=alu])
   739 	 */
   740 	var AttributeContainsSelector = function(sArg1, sArg2) {
   741 //		SimpleSelector.call(this);
   742 		this.arg1 = sArg1;
   743 		this.arg2 = sArg2;
   744 	};
   745 	extend(SimpleSelector, AttributeContainsSelector);
   746 	
   747 	AttributeContainsSelector.prototype.match = function(oNode) {
   748 		return !!this.arg2 && getAttribute(oNode, this.arg1).indexOf(this.arg2) != -1;
   749 	};
   750 
   751 	/**
   752 	 * Root pseudo-class selector (:root)
   753 	 */
   754 	var RootSelector = function() {
   755 //		SimpleSelector.call(this);
   756 	};
   757 	extend(SimpleSelector, RootSelector);
   758 	
   759 	RootSelector.prototype.match = function(oNode) {
   760 		return oNode.ownerDocument.documentElement == oNode;
   761 	};
   762 
   763 	/**
   764 	 * Abstract base class for nth-* selectors
   765 	 */
   766 	var NthSelector = function(sArg) {
   767 //		SimpleSelector.call(this);
   768 		
   769 		var a, b, n;
   770 		if (sArg == 'odd') {
   771 			a = 2, b = 1;
   772 		} else if (sArg == 'even') {
   773 			a = 2, b = 0;
   774 		} else {
   775 			n = sArg.indexOf('n');
   776 			a = sArg.substring(0, n);
   777 			b = sArg.substring(n + 1);
   778 			a = n == -1 ? 0 : a == '' ? 1 : a == '-' ? -1 : Number(a);
   779 			b = b == '' ? 0 : Number(b);
   780 		}
   781 		this.a = a;
   782 		this.b = b;
   783 	};
   784 	extend(SimpleSelector, NthSelector);
   785 
   786 	/**
   787 	 * Nth-child pseudo-class selector (:nth-child(3n+1))
   788 	 */
   789 	var NthChildSelector = function(sArg) {
   790 		NthSelector.call(this, sArg);
   791 	};
   792 	extend(NthSelector, NthChildSelector);
   793 	
   794 	NthChildSelector.prototype.match = function(oNode) {
   795 		var iCount = 1, a = this.a, b = this.b;
   796 		if (a == 1 && b == 0)
   797 			return true;
   798 		while ((oNode = oNode.previousSibling) && (a > 0 || iCount <= b))
   799 			if (oNode.nodeType == 1)
   800 				iCount++;
   801 		return a > 0 ? (iCount - b) % a == 0 : a < 0 ? iCount <= b && iCount % a == 0 : iCount - b == 0;
   802 	};
   803 
   804 	/**
   805 	 * Nth-last-child pseudo-class selector (:nth-last-child(3n+1))
   806 	 */
   807 	var NthLastChildSelector = function(sArg) {
   808 		NthSelector.call(this, sArg);
   809 	};
   810 	extend(NthSelector, NthLastChildSelector);
   811 	
   812 	NthLastChildSelector.prototype.match = function(oNode) {
   813 		var iCount = 1, a = this.a, b = this.b;
   814 		if (a == 1 && b == 0)
   815 			return true;
   816 		while ((oNode = oNode.nextSibling) && (a > 0 || iCount <= b))
   817 			if (oNode.nodeType == 1)
   818 				iCount++;
   819 		return a > 0 ? (iCount - b) % a == 0 : a < 0 ? iCount <= b && iCount % a == 0 : iCount - b == 0;
   820 	};
   821 
   822 	/**
   823 	 * Nth of type pseudo-class selector (:nth-of-type(3n+1))
   824 	 */
   825 	var NthOfTypeSelector = function(sArg) {
   826 		NthSelector.call(this, sArg);
   827 	};
   828 	extend(NthSelector, NthOfTypeSelector);
   829 	
   830 	NthOfTypeSelector.prototype.match = function(oNode) {
   831 		var iCount = 1, a = this.a, b = this.b, sNodeName = oNode.nodeName;
   832 		if (a == 1 && b == 0)
   833 			return true;
   834 		while ((oNode = oNode.previousSibling) && (a > 0 || iCount <= b))
   835 			if (oNode.nodeType == 1 && oNode.nodeName == sNodeName)
   836 				iCount++;
   837 		return a > 0 ? (iCount - b) % a == 0 : a < 0 ? iCount <= b && iCount % a == 0 : iCount - b == 0;
   838 	};
   839 
   840 	/**
   841 	 * Nth last of type pseudo-class selector (:nth-last-of-type(3n+1))
   842 	 */
   843 	var NthLastOfTypeSelector = function(sArg) {
   844 		NthSelector.call(this, sArg);
   845 	};
   846 	extend(NthSelector, NthLastOfTypeSelector);
   847 	
   848 	NthLastOfTypeSelector.prototype.match = function(oNode) {
   849 		var iCount = 1, a = this.a, b = this.b, sNodeName = oNode.nodeName;
   850 		if (a == 1 && b == 0)
   851 			return true;
   852 		while ((oNode = oNode.nextSibling) && (a > 0 || iCount <= b))
   853 			if (oNode.nodeType == 1 && oNode.nodeName == sNodeName)
   854 				iCount++;
   855 		return a > 0 ? (iCount - b) % a == 0 : a < 0 ? iCount <= b && iCount % a == 0 : iCount - b == 0;
   856 	};
   857 
   858 	/**
   859 	 * First child pseudo-class selector (:first-child)
   860 	 */
   861 	var FirstChildSelector = function() {
   862 //		SimpleSelector.call(this);
   863 	};
   864 	extend(SimpleSelector, FirstChildSelector);
   865 	
   866 	FirstChildSelector.prototype.match = function firstChildMatch(oNode) {
   867 		do
   868 			oNode = oNode.previousSibling;
   869 		while (oNode && oNode.nodeType != 1);
   870 		return !oNode;
   871 	};
   872 
   873 	/**
   874 	 * Last child pseudo-class selector (:last-child)
   875 	 */
   876 	var LastChildSelector = function() {
   877 //		SimpleSelector.call(this);
   878 	};
   879 	extend(SimpleSelector, LastChildSelector);
   880 	
   881 	LastChildSelector.prototype.match = function lastChildMatch(oNode) {
   882 		do
   883 			oNode = oNode.nextSibling;
   884 		while (oNode && oNode.nodeType != 1);
   885 		return !oNode;
   886 	};
   887 
   888 	/**
   889 	 * First of type pseudo-class selector (:first-of-type)
   890 	 */
   891 	var FirstOfTypeSelector = function() {
   892 //		SimpleSelector.call(this);
   893 	};
   894 	extend(SimpleSelector, FirstOfTypeSelector);
   895 	
   896 	FirstOfTypeSelector.prototype.match = function firstOfTypeMatch(oNode) {
   897 		var sNodeName = oNode.nodeName;
   898 		do
   899 			oNode = oNode.previousSibling;
   900 		while (oNode && (oNode.nodeType != 1 || oNode.nodeName != sNodeName));
   901 		return !oNode;
   902 	};
   903 
   904 	/**
   905 	 * Last of type pseudo-class selector (:last-of-type)
   906 	 */
   907 	var LastOfTypeSelector = function() {
   908 //		SimpleSelector.call(this);
   909 	};
   910 	extend(SimpleSelector, LastOfTypeSelector);
   911 	
   912 	LastOfTypeSelector.prototype.match = function lastOfTypeMatch(oNode) {
   913 		var sNodeName = oNode.nodeName;
   914 		do
   915 			oNode = oNode.nextSibling;
   916 		while (oNode && (oNode.nodeType != 1 || oNode.nodeName != sNodeName));
   917 		return !oNode;
   918 	};
   919 
   920 	/**
   921 	 * Only child pseudo-class selector (:only-child)
   922 	 */
   923 	var OnlyChildSelector = function() {
   924 //		SimpleSelector.call(this);
   925 	};
   926 	extend(SimpleSelector, OnlyChildSelector);
   927 	
   928 	OnlyChildSelector.prototype.match = function(oNode) {
   929 		return FirstChildSelector.prototype.match.call(this, oNode) &&
   930 				LastChildSelector.prototype.match.call(this, oNode);
   931 	};
   932 
   933 	/**
   934 	 * Only of type pseudo-class selector (:only-of-type)
   935 	 */
   936 	var OnlyOfTypeSelector = function() {
   937 //		SimpleSelector.call(this);
   938 	};
   939 	extend(SimpleSelector, OnlyOfTypeSelector);
   940 	
   941 	OnlyOfTypeSelector.prototype.match = function(oNode) {
   942 		return FirstOfTypeSelector.prototype.match.call(this, oNode) &&
   943 				LastOfTypeSelector.prototype.match.call(this, oNode);
   944 	};
   945 
   946 	/**
   947 	 * Empty pseudo-class selector (:empty)
   948 	 */
   949 	var EmptySelector = function() {
   950 //		SimpleSelector.call(this);
   951 	};
   952 	extend(SimpleSelector, EmptySelector);
   953 	
   954 	EmptySelector.prototype.match = function(oNode) {
   955 		oNode = oNode.firstChild;
   956 		while (oNode && oNode.nodeType != 1 && (oNode.nodeType != 3 || oNode.length == 0))
   957 			oNode = oNode.nextSibling;
   958 		return !oNode;
   959 	};
   960 
   961 	/**
   962 	 * Language pseudo-class selector (:lang(nl))
   963 	 */
   964 	var LangSelector = function(sArg1) {
   965 //		SimpleSelector.call(this);
   966 		this.arg1 = sArg1;
   967 	};
   968 	extend(SimpleSelector, LangSelector);
   969 	
   970 	LangSelector.prototype.match = function(oNode) {
   971 		var sLang;
   972 		do {
   973 			sLang = oNode.lang || '';
   974 			oNode = oNode.parentNode;
   975 		} while (!sLang && oNode && oNode.nodeType == 1);
   976 		// TODO: add support for language from META element (Content-Language)
   977 		return sLang.toLowerCase().indexOf(this.arg1.toLowerCase()) == 0 &&
   978 				(this.arg1.length == sLang.length || sLang.charAt(this.arg1.length) == '-');
   979 	};
   980 
   981 	/**
   982 	 * Enabled pseudo-class selector (:enabled)
   983 	 */
   984 	var EnabledSelector = function() {
   985 //		SimpleSelector.call(this);
   986 	};
   987 	extend(SimpleSelector, EnabledSelector);
   988 	
   989 	EnabledSelector.prototype.match = function(oNode) {
   990 		var sTagName = oNode.tagName.toUpperCase();
   991 		return sTagName != 'LINK' && sTagName != 'STYLE' && 'disabled' in oNode && !oNode.disabled;
   992 	};
   993 
   994 	/**
   995 	 * Disabled pseudo-class selector (:disabled)
   996 	 */
   997 	var DisabledSelector = function() {
   998 //		SimpleSelector.call(this);
   999 	};
  1000 	extend(SimpleSelector, DisabledSelector);
  1001 	
  1002 	DisabledSelector.prototype.match = function(oNode) {
  1003 		var sTagName = oNode.tagName.toUpperCase();
  1004 		return sTagName != 'LINK' && sTagName != 'STYLE' && 'disabled' in oNode && oNode.disabled;
  1005 	};
  1006 
  1007 	/**
  1008 	 * Checked pseudo-class selector (:checked)
  1009 	 */
  1010 	var CheckedSelector = function() {
  1011 //		SimpleSelector.call(this);
  1012 	};
  1013 	extend(SimpleSelector, CheckedSelector);
  1014 	
  1015 	CheckedSelector.prototype.match = function(oNode) {
  1016 		var sTagName = oNode.tagName.toUpperCase();
  1017 		return sTagName == 'INPUT' && oNode.checked || sTagName == 'OPTION' && oNode.selected;
  1018 	};
  1019 
  1020 	/**
  1021 	 * Read-only pseudo-class selector (:read-only)
  1022 	 */
  1023 	var ReadOnlySelector = function() {
  1024 //		SimpleSelector.call(this);
  1025 	};
  1026 	extend(SimpleSelector, ReadOnlySelector);
  1027 	
  1028 	ReadOnlySelector.prototype.match = function(oNode) {
  1029 		return !('readOnly' in oNode) || oNode.readOnly;
  1030 	};
  1031 
  1032 	/**
  1033 	 * Read-write pseudo-class selector (:read-write)
  1034 	 */
  1035 	var ReadWriteSelector = function() {
  1036 //		SimpleSelector.call(this);
  1037 	};
  1038 	extend(SimpleSelector, ReadWriteSelector);
  1039 	
  1040 	ReadWriteSelector.prototype.match = function(oNode) {
  1041 		return 'readOnly' in oNode && !oNode.readOnly;
  1042 	};
  1043 
  1044 	/**
  1045 	 * Target pseudo-class selector (:target)
  1046 	 */
  1047 	var TargetSelector = function() {
  1048 //		SimpleSelector.call(this);
  1049 	};
  1050 	extend(SimpleSelector, TargetSelector);
  1051 	
  1052 	TargetSelector.prototype.match = function(oNode) {
  1053 		return '#' + oNode.getAttribute('id') == oNode.ownerDocument.location.hash;
  1054 	};
  1055 
  1056 	/**
  1057 	 * Negation pseudo-class selector (:not)
  1058 	 */
  1059 	var NotSelector = function(oSimpleSelector) {
  1060 //		SimpleSelector.call(this);
  1061 		if (!oSimpleSelector instanceof SimpleSelector)
  1062 			throw new Error('Argument must be a SimpleSelector.');
  1063 		this.simpleSelector = oSimpleSelector;
  1064 	};
  1065 	extend(SimpleSelector, NotSelector);
  1066 
  1067 	NotSelector.prototype.validate = function() {
  1068 		return !(this.simpleSelector instanceof NotSelector);
  1069 	};
  1070 
  1071 	NotSelector.prototype.match = function(oNode) {
  1072 		return !this.simpleSelector.match(oNode);
  1073 	};
  1074 
  1075 
  1076 
  1077 	/**
  1078 	 * Parser object constructor
  1079 	 * @class Parser class
  1080 	 * @constructor
  1081 	 */
  1082 	var Parser = function() {
  1083 	};
  1084 
  1085 	/**
  1086 	 * Parser regexp macros and patterns for the tokenizer
  1087 	 * See: http://www.w3.org/TR/CSS21/syndata.html#tokenization
  1088 	 */
  1089 	// Macros
  1090 	var pattern_w          = '[ \t\r\n\f]*',
  1091 		pattern_S          = '[ \t\r\n\f]+',
  1092 		pattern_escape     = '\\\\[0-9a-fA-F]{1,6}(?:\\r\\n|[ \\n\\r\\t\\f])?|\\\\[^\\n\\r\\f0-9a-fA-F]',
  1093 		pattern_nlescape   = '\\\\(?:\\r\\n|[\\n\\r\\f])',
  1094 		pattern_ident      = '(?:[-]?(?:[_a-zA-Z]|[^\\0-\\177]|' + pattern_escape + ')(?:[_a-zA-Z0-9-]|[^\\0-\\177]|' + pattern_escape + ')*)',
  1095 //		pattern_ns_ident   = '(?:(?:' + pattern_ident + '|\\*)?\\|)?' + pattern_ident,
  1096 		pattern_string1    = '\\"(?:[^\\n\\r\\f\\\\"]|' + pattern_nlescape + '|' + pattern_escape + ')*\\"',
  1097 		pattern_string2    = "\\'(?:[^\\n\\r\\f\\\\']|" + pattern_nlescape + "|" + pattern_escape + ")*\\'",
  1098 		pattern_string     = '(?:' + pattern_string1 + '|' + pattern_string2 + ')',
  1099 //		pattern_nth        = '(?:-?[0-9]*n)?(?:[+\\-][0-9]+)?|odd|even',
  1100 		// Simple selectors
  1101 		pattern_class      = '\\.' + pattern_ident,
  1102 		pattern_id         = '#' + pattern_ident,
  1103 		pattern_attribute  = '\\[(' + pattern_ident + ')(?:([~|\\^\\$*]?=)(' + pattern_ident + '|' + pattern_string + '))?\\]',
  1104 		pattern_pseudo     = ':' + pattern_ident + '(?:\\([^)]+\\))?',
  1105 		pattern_universal  = '\\*',				// '(?:' + pattern_ident + '\\|)\\*'
  1106 		pattern_element    = pattern_ident,		// pattern_ns_ident
  1107 		// Combinators
  1108 		// Note: combinators must be placed inbetween two \s* patterns
  1109 		pattern_child      = pattern_w + '>' + pattern_w,
  1110 		pattern_adjacent   = pattern_w + '\\+' + pattern_w,
  1111 		pattern_sibling    = pattern_w + '~' + pattern_w,
  1112 		pattern_comma      = pattern_w + ',' + pattern_w,
  1113 		pattern_descendant = pattern_S,
  1114 		pattern_negation   = ':not\\((?:' + [pattern_class, pattern_id, pattern_attribute, 
  1115 		                                       pattern_pseudo, pattern_universal,
  1116 		                                       pattern_element].join('|') + ')\\)';
  1117 
  1118 
  1119 	/**
  1120 	 * Regular expression objects for patterns
  1121 	 */
  1122 	var regexp_selector = new RegExp([
  1123 				pattern_class,		// first all selectors
  1124 				pattern_id,
  1125 				pattern_attribute,
  1126 				pattern_negation,
  1127 				pattern_pseudo,
  1128 				pattern_universal,
  1129 				pattern_element,
  1130 				pattern_child,		// then the combinators
  1131 				pattern_adjacent,
  1132 				pattern_sibling,
  1133 				pattern_comma,
  1134 				pattern_descendant	// descendant combinator must be last
  1135 			].join('|'), 'g'),
  1136 		regexp_attribute    = new RegExp(pattern_attribute),
  1137 		regexp_nlescape     = new RegExp(pattern_nlescape, 'g'),
  1138 		regexp_pseudo       = new RegExp(':([^(]+)(?:\\((.+)\\))?'),
  1139 		regexp_trim         = /^\s+|\s+$/gm,
  1140 		regexp_escapeCSS    = /\\([^\n\r\f0-9a-fA-F])|\\([0-9a-fA-F]{1,6})(?:\r\n|[ \n\r\t\f])?/g,
  1141 		regexp_escapeRegexp = /([\[\\\^\$\.\|\?\*\+\(\)\{\}])/g,
  1142 		regexp_whiteSpace   = /\s/;
  1143 
  1144 	var hAttrTypeMap = {
  1145 			'=' : AttributeEqualsSelector,
  1146 			'~=': AttributeListSelector,
  1147 			'|=': AttributeLangSelector,
  1148 			'^=': AttributeStartSelector,
  1149 			'$=': AttributeEndSelector,
  1150 			'*=': AttributeContainsSelector
  1151 		};
  1152 
  1153 	var hPseudoTypeMap = {
  1154 			'root'            : RootSelector,
  1155 			'nth-child'       : NthChildSelector,
  1156 			'nth-last-child'  : NthLastChildSelector,
  1157 			'nth-of-type'     : NthOfTypeSelector,
  1158 			'nth-last-of-type': NthLastOfTypeSelector,
  1159 			'first-child'     : FirstChildSelector,
  1160 			'last-child'      : LastChildSelector,
  1161 			'first-of-type'   : FirstOfTypeSelector,
  1162 			'last-of-type'    : LastOfTypeSelector,
  1163 			'only-child'      : OnlyChildSelector,
  1164 			'only-of-type'    : OnlyOfTypeSelector,
  1165 			'empty'           : EmptySelector,
  1166 			'lang'            : LangSelector,
  1167 			'enabled'         : EnabledSelector,
  1168 			'disabled'        : DisabledSelector,
  1169 			'checked'         : CheckedSelector,
  1170 			'read-only'       : ReadOnlySelector,
  1171 			'read-write'      : ReadWriteSelector,
  1172 			'target'          : TargetSelector
  1173 //			'not'             : NotSelector
  1174 		};
  1175 
  1176 	/**
  1177 	 * Parses a CSS selector or selector group.
  1178 	 * Uses caching to speed up subsequent calls.
  1179 	 * @param selector The selector.
  1180 	 * @return SelectorGroup object, null if unable to parse the selector (error message in errorMessage property).
  1181 	 */
  1182 	Parser.prototype.parse = function(sSelector) {
  1183 		// segment selector
  1184 		var aSelectorParts = sSelector.match(regexp_selector);
  1185 		// check whether selector was parsed completely...
  1186 		if (sSelector != aSelectorParts.join(''))
  1187 			throw new ParsingException(sSelector);
  1188 
  1189 		// instantiate initial object model
  1190 		var oSelectorGroup = new SelectorGroup(),
  1191 			oSelector = new Selector(),
  1192 			oCombinator = new RootCombinator();
  1193 		oSelectorGroup.add(oSelector);
  1194 		oSelector.add(oCombinator);
  1195 
  1196 		// post-process segments
  1197 		for (var i = 0, iLen = aSelectorParts.length; i < iLen; i++) {
  1198 			// trim combinators with excessive space, note that descendant combinator becomes ''
  1199 			var sSelectorPart = aSelectorParts[i].replace(regexp_trim, '');
  1200 			switch (sSelectorPart.charAt(0)) {
  1201 				// selector group
  1202 				case ',':
  1203 					oSelector = new Selector();
  1204 					oCombinator = new RootCombinator();
  1205 					oSelectorGroup.add(oSelector);
  1206 					oSelector.add(oCombinator);
  1207 					break;
  1208 				// selectors
  1209 				case '*':
  1210 					oCombinator.add(new UniversalSelector());
  1211 					break;
  1212 				case '.':
  1213 					oCombinator.add(new ClassSelector(Parser.decodeIdent(sSelectorPart.substr(1))));
  1214 					break;
  1215 				case '#':
  1216 					oCombinator.add(new IdSelector(Parser.decodeIdent(sSelectorPart.substr(1))));
  1217 					break;
  1218 				case '[':
  1219 					oCombinator.add(this.parseAttributeSelector(sSelectorPart));
  1220 					break;
  1221 				case ':':
  1222 					try {
  1223 						oCombinator.add(this.parsePseudoSelector(sSelectorPart));
  1224 					} catch(e) {
  1225 						if (e instanceof ParsingException)
  1226 							throw new ParsingException(sSelector);
  1227 						throw e;
  1228 					}
  1229 					break;
  1230 				// combinators
  1231 				case '':
  1232 					oCombinator = new DescendantCombinator();
  1233 					oSelector.add(oCombinator);
  1234 					break;
  1235 				case '>':
  1236 					oCombinator = new ChildCombinator();
  1237 					oSelector.add(oCombinator);
  1238 					break;
  1239 				case '+':
  1240 					oCombinator = new AdjacentCombinator();
  1241 					oSelector.add(oCombinator);
  1242 					break;
  1243 				case '~':
  1244 					oCombinator = new SiblingCombinator();
  1245 					oSelector.add(oCombinator);
  1246 					break;
  1247 				// element selector (no single prefix)
  1248 				default:
  1249 					oCombinator.add(new TypeSelector(Parser.decodeIdent(sSelectorPart)));
  1250 					break;
  1251 			}
  1252 		}
  1253 		if (!oSelectorGroup.validate())
  1254 			throw new ParsingException(sSelector);
  1255 
  1256 		return oSelectorGroup;
  1257 	};
  1258 
  1259 	Parser.prototype.parseAttributeSelector = function(sSelectorPart) {
  1260 		var aParsedValues = regexp_attribute.exec(sSelectorPart);
  1261 		var sName = Parser.decodeIdent(aParsedValues[1]);
  1262 		if (!aParsedValues[2]) {
  1263 			return new AttributeExistSelector(sName);
  1264 		} else {
  1265 			var sValue = aParsedValues[3].charAt(0) == '"' || aParsedValues[3].charAt(0) == "'" ?
  1266 						Parser.decodeString(aParsedValues[3]) :
  1267 						Parser.decodeIdent(aParsedValues[3]);
  1268 			return this.createAttributeSelector(aParsedValues[2], sName, sValue);
  1269 		}
  1270 	};
  1271 	
  1272 	/**
  1273 	 * Returns a new Attribute*Selector instance that matches on name and value.
  1274 	 * Note that the grammar parsing has already prevented any non-existant identifier
  1275 	 * from occurring here, so no existence check needs to be done.
  1276 	 * @param sIdentifier The bit between the name and value in the selector (e.g. ^=)
  1277 	 * @param sName Name to match on
  1278 	 * @param sValue Value to match on
  1279 	 * @return A new Attribute*Selector instance.
  1280 	 */
  1281 	Parser.prototype.createAttributeSelector = function(sIdentifier, sName, sValue) {
  1282 		return new hAttrTypeMap[sIdentifier](sName, sValue);
  1283 	};
  1284 	
  1285 	Parser.prototype.parsePseudoSelector = function(sSelectorPart) {
  1286 		var aParsedPseudo = regexp_pseudo.exec(sSelectorPart),
  1287 			sIdentifier = Parser.decodeIdent(aParsedPseudo[1]);
  1288 		if (sIdentifier == 'not') {
  1289 			var oTempGroup = this.parse(aParsedPseudo[2]);
  1290 			var oTempCombinator = oTempGroup.selectors[0].combinators[0];
  1291 			if (oTempCombinator.simpleSelectors.length != 1)
  1292 				throw new ParsingException(sSelectorPart);
  1293 			return new NotSelector(oTempCombinator.simpleSelectors[0]);
  1294 		} else {
  1295 			return this.createPseudoSelector(sIdentifier, aParsedPseudo[2]);
  1296 		}
  1297 	};
  1298 	
  1299 	/**
  1300 	 * Returns a SimpleSelector instance matching the passed pseudo-class name
  1301 	 * @param sIdentifier Pseudo-class name
  1302 	 * @param sArgument Argument to pass to the pseudo-class
  1303 	 * @param sSelector to use by the 
  1304 	 * @return A new SimpleSelector instance
  1305 	 */
  1306 	Parser.prototype.createPseudoSelector = function(sIdentifier, sArgument) {
  1307 		if (!hPseudoTypeMap[sIdentifier])
  1308 			throw new ParsingException(sIdentifier);
  1309 		return new hPseudoTypeMap[sIdentifier](sArgument);
  1310 	};
  1311 
  1312 	/**
  1313 	 * Decode a CSS identifier
  1314 	 * @param identifier The identifier
  1315 	 * @return Decoded string
  1316 	 */
  1317 	Parser.decodeIdent = function(sIdentifier) {
  1318 		sIdentifier = sIdentifier.replace(regexp_escapeCSS,
  1319 				function(sEsc, sCharacter, sUnicode) {
  1320 					if (sCharacter)
  1321 						return sCharacter;
  1322 					else
  1323 						return String.fromCharCode(parseInt(sUnicode, 16));
  1324 				}
  1325 			);
  1326 		return sIdentifier;
  1327 	};
  1328 
  1329 	/**
  1330 	 * Decode a CSS string
  1331 	 * @param string The string (including quotes)
  1332 	 * @return Decoded string
  1333 	 */
  1334 	Parser.decodeString = function(sString) {
  1335 		/* Atm this check is not necessary, because
  1336 		 * the parser patterns do not allow this...
  1337 		var sFirstChar = sString.charAt(0),
  1338 			sLastChar = sString.charAt(sString.length - 1);
  1339 		if (sString.length < 2 ||
  1340 			!((sFirstChar == "'" && sLastChar == "'") ||
  1341 		      (sFirstChar == '"' && sLastChar == '"')))
  1342 			throw new ParsingException(sString);
  1343 		*/
  1344 		sString = sString.substring(1, sString.length - 1);
  1345 		sString = sString.replace(regexp_nlescape, '');
  1346 		return Parser.decodeIdent(sString);
  1347 	};
  1348 
  1349 	/**
  1350 	 * Escapes a string for use in a regular expression.
  1351 	 * @param string The string.
  1352 	 * @return The escaped string.
  1353 	 */
  1354 	Parser.escapeRegexp = function(sString) {
  1355 		return sString.replace(regexp_escapeRegexp, '\\$1');
  1356 	};
  1357 
  1358 
  1359 
  1360 	/**
  1361 	 * A subclass of Parser that provides caching of parsed selectors.
  1362 	 */
  1363 	var CachingParser = function() {
  1364 		this.cache = {};
  1365 		Parser.call(this);
  1366 	};
  1367 	extend(Parser, CachingParser);
  1368 	
  1369 	CachingParser.prototype.parse = function(sSelector) {
  1370 		if (!(sSelector in this.cache))
  1371 			this.cache[sSelector] = Parser.prototype.parse.call(this, sSelector);
  1372 		return this.cache[sSelector];
  1373 	};
  1374 
  1375 
  1376 
  1377 	/**
  1378 	 * An exception object that is thrown when there is a parse error.
  1379 	 */
  1380 	var ParsingException = function(sSelector) {
  1381 		this.selector = sSelector;
  1382 	};
  1383 	
  1384 	ParsingException.prototype.toString = function() {
  1385 		return 'CSS Selector parsing error: ' + this.selector;
  1386 	};
  1387 
  1388 
  1389 
  1390 	// instantiate caching parser
  1391 	var oParser = new CachingParser();
  1392 	
  1393 	
  1394 	
  1395 	
  1396 	// exports
  1397 	this.match = match;
  1398 	this.queryAll = queryAll;
  1399 	this.query = query;
  1400 	this.queryAncestorOrSelf = queryAncestorOrSelf;
  1401 	this.queryDocument = queryDocument;
  1402 	this.queryDocumentAll = queryDocumentAll;
  1403 	this.SelectorGroup = SelectorGroup;
  1404 	this.Selector = Selector;
  1405 	this.Combinator = Combinator;
  1406 	this.DescendantCombinator = DescendantCombinator;
  1407 	this.ChildCombinator = ChildCombinator;
  1408 	this.AdjacentCombinator = AdjacentCombinator;
  1409 	this.SiblingCombinator = SiblingCombinator;
  1410 	this.RootCombinator = RootCombinator;
  1411 	this.SimpleSelector = SimpleSelector;
  1412 	this.UniversalSelector = UniversalSelector;
  1413 	this.TypeSelector = TypeSelector;
  1414 	this.ClassSelector = ClassSelector;
  1415 	this.IdSelector = IdSelector;
  1416 	this.AttributeExistSelector = AttributeExistSelector;
  1417 	this.AttributeEqualsSelector = AttributeEqualsSelector;
  1418 	this.AttributeListSelector = AttributeListSelector;
  1419 	this.AttributeLangSelector = AttributeLangSelector;
  1420 	this.AttributeStartSelector = AttributeStartSelector;
  1421 	this.AttributeEndSelector = AttributeEndSelector;
  1422 	this.AttributeContainsSelector = AttributeContainsSelector;
  1423 	this.RootSelector = RootSelector;
  1424 	this.NthSelector = NthSelector;
  1425 	this.NthChildSelector = NthChildSelector;
  1426 	this.NthLastChildSelector = NthLastChildSelector;
  1427 	this.NthOfTypeSelector = NthOfTypeSelector;
  1428 	this.NthLastOfTypeSelector = NthLastOfTypeSelector;
  1429 	this.FirstChildSelector = FirstChildSelector;
  1430 	this.LastChildSelector = LastChildSelector;
  1431 	this.FirstOfTypeSelector = FirstOfTypeSelector;
  1432 	this.LastOfTypeSelector = LastOfTypeSelector;
  1433 	this.OnlyChildSelector = OnlyChildSelector;
  1434 	this.OnlyOfTypeSelector = OnlyOfTypeSelector;
  1435 	this.EmptySelector = EmptySelector;
  1436 	this.LangSelector = LangSelector;
  1437 	this.EnabledSelector = EnabledSelector;
  1438 	this.DisabledSelector = DisabledSelector;
  1439 	this.CheckedSelector = CheckedSelector;
  1440 	this.ReadOnlySelector = ReadOnlySelector;
  1441 	this.ReadWriteSelector = ReadWriteSelector;
  1442 	this.TargetSelector = TargetSelector;
  1443 	this.NotSelector = NotSelector;
  1444 	this.Parser = Parser;
  1445 	this.CachingParser = CachingParser;
  1446 	this.ParsingException = ParsingException;
  1447 	this.parser = oParser;
  1448 	
  1449 };