
/*
reg library version 1.0
copyright @ga-copyright@ Sun Microsystems
this lib does:
 - dom helper functions
 - event utilities
 - page load & traversal
 - javascript behavior simulation
 - css-like element selection and matching
*/

// ############################# HELPER FUNCTIONS ############################

// TEST FOR CLASS NAME
(function(){
	var patts={};// cache regexps for performance
	window.hasClassName=function(element, className){
		if(!patts[className]){patts[className]=new RegExp("(^|\\s)"+className+"($|\\s)");}
		return element.className && patts[className].test(element.className);
	};
})();

// ADD CLASSES TO OBJECTS
function addClassName(element, className){
	if (hasClassName(element, className)) { return false; }
	if (!element.className) { element.className = className; }
	else { element.className += ' '+className; }
	return true;
}

// REMOVE CLASSES FROM OBJECTS
function removeClassName(element, className){
	if (!hasClassName(element, className)) { return false; }
	var classNames = element.className.split(' ');
	var newClassNames = [];
	for (var a=0; a<classNames.length; a++){
		if (classNames[a] != className) { newClassNames.push(classNames[a]); }
	}
	element.className = newClassNames.join(' ');
	return true;
}

// TOGGLE CLASS NAME
function toggleClassName(element, className) {
	if (hasClassName(element, className)) { removeClassName(element, className); }
	else { addClassName(element, className); }
}

// SWITCH CLASS NAME A->B, B->A
function switchClassName(element, className1, className2) {
	if (className1 == className2) { throw "className1 and className2 both equal "+className1; }
	var has1 = hasClassName(element, className1);
	var has2 = hasClassName(element, className2);
	if (has1 && has2) { removeClassName(element, className2); }
	else if (!has1 && !has2) { addClassName(element, className1); }
	else if (has1) { removeClassName(element, className1); addClassName(element, className2); }
	else { removeClassName(element, className2); addClassName(element, className1); }
}

// TEST FOR CLASS NAME BY PATTERN
function matchClassName(element, pattern){
	var classNames = element.className.split(' ');
	for (var a=0; a<classNames.length; a++){
		var matches = classNames[a].match(pattern);
		if (matches) { return matches; }
	}
	return null;
}

// FIND PREVIOUS ELEMENT
function prevElem(el) {
	var prev = el.previousSibling;
	while(prev && prev.nodeType!=1){prev=prev.previousSibling;}
	return prev;
}

// FIND NEXT ELEMENT
function nextElem(el) {
	var next = el.nextSibling;
	while(next && next.nodeType!=1){next=next.nextSibling;}
	return next;
}

// SHORTCUT FOR BUILDING ELEMENTS
function elem(name, atts, content) {
	// name: a tag name, with optional class or id: 'div', 'div.foo', 'div#bar', 'div.foo#bar', 'div#bar.foo'
	// atts: optional. object where keys=attribute names, values=attribute values: {'href':'page.html','target':'_blank'}
	// content: optional. either a string, or an element, or an arry of strings or elements
	if (name.indexOf('.') + name.indexOf('#') > -2) {
		var className = (name.indexOf('.') > -1) ? name.replace(/^.*\.([^\.#]*).*$/,"$1") : "";
		var id = (name.indexOf('#') > -1) ? name.replace(/^.*#([^\.#]*).*$/,"$1") : "";
		name = name.replace(/^([^\.#]*).*$/,'$1');
	}
	var e = document.createElement(name);
	if (className) { e.className = className; }
	if (id) { e.id = id; }
	if (atts) {
		var key;
		for (key in atts) {
			// setAttribute() has shaky support, try direct methods first
			if (key == 'class') { e.className = atts[key]; }
			else if (key == 'id') { e.id = atts[key]; }
			else if (key == 'href') { e.href = atts[key]; }
			else if (key == 'action') { e.action = atts[key]; }
			else if (key == 'method') { e.method = atts[key]; }
			else if (key == 'title') { e.title = atts[key]; }
			else if (key == 'alt') { e.alt = atts[key]; }
			else if (key == 'border') { e.border = atts[key]; }
			else if (key == 'caption') { e.caption = atts[key]; }
			else if (key == 'cellspacing') { e.cellspacing = atts[key]; }
			else if (key == 'for') { e.htmlFor = atts[key]; }
			else { e.setAttribute(key, atts[key]); }
		}
	}
	if (content) {
		if (!(content instanceof Array)) {
			content = [content];
		}
		for (var a=0; a<content.length; a++) {
			if (typeof content[a] == 'string') {
				e.appendChild(document.createTextNode(content[a]));
			}else{
				e.appendChild(content[a]);
			}
		}
	}
	if (name.toLowerCase() == 'img' && !e.alt) { e.alt = ''; }
	return e;
}

// GRAB JUST THE TEXTUAL DATA OF AN ELEMENT
function elemText(el) {
	// <a id="foo" href="page.html">click <b>here</b></a>
	// elemText(document.getElementById('foo')) == "click here"
	var r = el.innerHTML.replace(/<img[^>]+alt="([^">]+)[^>]+>/ig,'$1').replace(/<[^>]+>/ig,'');
	var d = elem('div');
	d.innerHTML = r;
	r = (d.childNodes[0]) ? d.childNodes[0].data : '';
	d = null;
	return r;
}


// GET SAFELY ENCODED STRINGS
function getSafelyEncodedString(s) {
	s = encodeURIComponent(s);
	s = s.replace(/&/,"&amp;").replace(/"/,"&quot;").replace(/</,"&lt;").replace(/>/,"&gt;");
	return s;
}

// SHORTHAND FOR VERBOSE DOM FUNCTIONS
function gebi(id){ return document.getElementById(id); }
function gebtn(tag, el){ if(!el){el=document;} return el.getElementsByTagName(tag); }

// ############################# EVENT UTILITIES #############################

// GET THE ELEMENT ON WHICH THE EVENT OCCURRED
function getTarget(e) {
	if (!e) { e = window.event; }
	if (e.target) { var targ = e.target; }
	else if (e.srcElement) { var targ = e.srcElement; }
	if (targ.nodeType == 3) { targ = targ.parentNode; } // safari hack
	return targ;
}

// CANCEL DEFAULT ACTION
function cancelDefault(e){
	if (typeof e.preventDefault != 'undefined') { e.preventDefault(); return; }
	e.returnValue=false;
}

// GENERIC EVENT ADDER, PLUS MEMORY LEAK PREVENTION
(function(){
	var evtLst = [];
	function rememberEvent(elmt,evt,hndl,cptr){
		evtLst.push([elmt,evt,hndl,cptr]);
	}
	function cleanup(){
		for(var a=0; a<evtLst.length; a++){
			var evt = evtLst[a];
			var elmt=evt[0];
			if(elmt.removeEventListener){
				elmt.removeEventListener(evt[1], evt[2], evt[3]);
			}
			if(elmt.detachEvent){
				elmt.detachEvent('on'+evt[1], evt[2]);
			}
		}
	}
	window.addEvent=function(elmt,evt,hndl,cptr) {
		cptr=(cptr)?true:false;
		if(elmt.addEventListener){
			elmt.addEventListener(evt,hndl,cptr);
			rememberEvent(elmt,evt,hndl,cptr);
		}else if(elmt.attachEvent){
			elmt.attachEvent("on"+evt,function(){hndl.call(elmt,window.event);});
			rememberEvent(elmt,evt,hndl);
		}
	}
	addEvent(window,'unload',cleanup);
})();

// ############################# LOAD AND TRAVERSAL ##############################

(function(){

	// we build these lists here where nobody can mess with them
	var preSetupQueue=[];
	var setupQueue={};
	var postSetupQueue=[];

	// just in case this isn't defined
	if(window.reg===undefined){window.reg={};}

	// traverse and act onload
	window.reg.setup=function(selector, setup, firstTimeOnly){
		firstTimeOnly=(firstTimeOnly)?true:false;
		var sq=setupQueue;
		var parsedSel = new Selector(selector);
		var tagNames=parsedSel.getTagNames();
		var regObj={
			selector:parsedSel,
			setup:setup,
			ran:false,
			firstTimeOnly:firstTimeOnly,
			isSimple:parsedSel.isSimple() // 'div.foo' is simple, 'div.foo > p' is not
		};
		for(var a=0;a<tagNames.length;a++){
			var tagName = tagNames[a];
			if(!sq[tagName]){sq[tagName]=[regObj];}
			else{sq[tagName][sq[tagName].length]=regObj;}
		}
	};
	// do this before setup
	window.reg.preSetup=function(fn){preSetupQueue.push(fn);};
	// do this after setup
	window.reg.postSetup=function(fn){postSetupQueue.push(fn);};
	// traversal helper
	var gebsHash = {}; // cache for perf.
	window.reg.getElementsBySelector = function(selString, startHere){
		if (!gebsHash[selString]) { gebsHash[selString] = new Selector(selString); }
		return gebsHash[selString].getMatchingElements(startHere);
	};

	// (re)run setup functions
	var runSetupFunctions = window.reg.rerun = function(el, cancelIfAlreadyRan){
		function runIt(el, regObj){
			regObj.setup.call(el);
			regObj.ran=true;
		}
		function shouldIgnore(el){
			// helps to not waste cycles checking simple elements like <p>
			return (!el.className && !el.id && el.nodeName != 'img');
		}
		var start = new Date().getTime();
		if (typeof el.setupFunctionsAlreadyRanHere != 'undefined' && el.setupFunctionsAlreadyRanHere && cancelIfAlreadyRan) { return; }
		var doc=(el)?el:document;
		var sq=setupQueue;
		var els=doc.getElementsByTagName('*');

		// crawl the dom
		for(var a=0;a<els.length;a++){
			var elmt=els[a];
			if (elmt.nodeType!=1){continue;}// ie7 does this for some reason
			var lcNodeName=elmt.nodeName.toLowerCase();
			var regObjArrayAll=sq['*'];
			var regObjArrayTag=sq[lcNodeName];
			var uninteresting = shouldIgnore(elmt);

			// any wildcards?
			if(regObjArrayAll){
				for(var b=0;b<regObjArrayAll.length;b++){
					var regObj=regObjArrayAll[b];
					if(uninteresting && regObj.isSimple){continue;}
					if(regObj.firstTimeOnly && regObj.ran){continue;}
					var matches = regObj.selector.matchesElement(elmt);
					if(matches){runIt(elmt, regObj);}
				}
			}

			// any items match this specific tag?
			if(regObjArrayTag){
				for(var b=0;b<regObjArrayTag.length;b++){
					var regObj=regObjArrayTag[b];
					if(uninteresting && regObj.isSimple){continue;}
					if(regObj.firstTimeOnly && regObj.ran){continue;}
					var matches = regObj.selector.matchesElement(elmt);
					if(matches){runIt(elmt, regObj);}
				}
			}
		}
		el.setupFunctionsAlreadyRanHere = true;
		var runtime = new Date().getTime() - start;
		if(!reg.setupRuntime){ reg.setupRuntime=runtime; }
		reg.lastSetupRuntime=runtime;
	}

	var loadFuncRan = false;
	function loadFunc(e) {
		if (!loadFuncRan) {
			for(var a=0;a<preSetupQueue.length;a++){
				preSetupQueue[a]();
			}
			runSetupFunctions(document, true);
			for(var a=0;a<postSetupQueue.length;a++){
				postSetupQueue[a]();
			}
			// postCrawl needs to go away ASAP
			if (typeof postCrawl != 'undefined') { var func; for (func in postCrawl) { postCrawl[func](); } }
			loadFuncRan = true;
		}
	}
	
	// contents of loadFunc only execute once, this sidesteps user agent sniffing 
	addEvent(window, 'load', loadFunc);
	addEvent(window, 'DOMContentLoaded', loadFunc);
})();

// ############################# BEHAVIOR SIMULATION ##############################

(function() {

	// local data, the heart and soul of our operation
	var clickHandlers = {};
	var mouseOverHandlers = {};
	var mouseOutHandlers = {};
	var focusHandlers = {};
	var blurHandlers = {};

	// just in case this isn't already defined
	if(window.reg===undefined){window.reg={};}
	
	// scrubber for convenience and consistency
	function getDepth(d){
		var result=d;
		if(result===null||result===undefined){result=-1;}
		result=parseInt(result);
		if(isNaN(result)){throw "bad arg for depth, "+d+" is not a number";}
		if(result<-1){throw "bad arg for depth, "+d+" is invalid, must be -1 or higher";}
		return result;
	}

	// register click behavior
	window.reg.click=function(sel, handlerFunction, depth){
		depth = getDepth(depth);
		var parsedSel = new Selector(sel);
		if(!clickHandlers[sel]) {clickHandlers[sel]=[];}
		clickHandlers[sel].push({selector:parsedSel,handle:handlerFunction,depth:depth});
	};
	// register mouseover and mouseout behavior
	window.reg.hover=function(sel, overFunc, outFunc, depth){
		depth = getDepth(depth);
		var parsedSel = new Selector(sel);
		if (overFunc) {
			if(!mouseOverHandlers[sel]) {mouseOverHandlers[sel]=[];}
			mouseOverHandlers[sel].push({selector:parsedSel,handle:overFunc,depth:depth});
		}
		if (outFunc) {
			if(!mouseOutHandlers[sel]) {mouseOutHandlers[sel]=[];}
			mouseOutHandlers[sel].push({selector:parsedSel,handle:outFunc,depth:depth});
		}
	};
	// register focus and blur behavior
	window.reg.focusblur=function(sel, focusFunc, blurFunc, depth){
		depth = getDepth(depth);
		var parsedSel = new Selector(sel);
		if (focusFunc) {
			if(!focusHandlers[sel]) {focusHandlers[sel]=[];}
			focusHandlers[sel].push({selector:parsedSel,handle:focusFunc,depth:depth});
		}
		if (blurFunc) {
			if(!blurHandlers[sel]) {blurHandlers[sel]=[];}
			blurHandlers[sel].push({selector:parsedSel,handle:blurFunc,depth:depth});
		}
	};

	// the delegator
	function delegate(selectionHandlers, event) {
		var targ = getTarget(event);
		var sel; // make it local, otherwise mass confusion ensues
		if (selectionHandlers) {
			selectors:for (sel in selectionHandlers) {
				for(var a=0; a<selectionHandlers[sel].length; a++){
					var selHandler=selectionHandlers[sel][a];
					var depth = (selHandler.depth==-1) ? 100 : selHandler.depth;
					var el = targ;
					for (var b=-1; b<depth && el && el.nodeType == 1; b++, el=el.parentNode) {
						if (selHandler.selector.matchesElement(el)) {
							var retVal=selHandler.handle.call(el,event);
							// if they return false from the handler, cancel default
							if(retVal!==undefined && !retVal){
								cancelDefault(event);
							}
							break;
						}
					}
				}
			}
		}
	}

	// attach the events
	addEvent(document.documentElement,'click',function(e){
		delegate(clickHandlers, e);
	});
	addEvent(document.documentElement,'mouseover',function(e){
		delegate(mouseOverHandlers, e);
	});
	addEvent(document.documentElement,'mouseout',function(e){
		delegate(mouseOutHandlers, e);
	});
	if(typeof document.onactivate == 'object'){
		var focus = 'activate';
		var blur = 'deactivate';
	}else{
		var focus = 'focus';
		var blur = 'blur';
	}
	addEvent(document.documentElement,focus,function(e){
		delegate(focusHandlers, e);
	},true);
	addEvent(document.documentElement,blur,function(e){
		delegate(blurHandlers, e);
	},true);
	
	// handy for css
	addClassName(document.documentElement, 'regenabled');
})();

// ################################ SELECTORS ################################

(function(){

	// precompile these for performance
	var expressions = {
		leadSpace:  new RegExp("^\\s+"),
		tagName:    new RegExp("^([a-z_][a-z0-9_-]*)","i"),
		wildCard:   new RegExp("^\\*([^=]|$)"),
		className:  new RegExp("^(\\.([a-z0-9_-]+))","i"),
		id:         new RegExp("^(#([a-z0-9_-]+))","i"),
		att:        new RegExp("^(@([a-z_][a-z0-9_-]*))","i"),
		matchType:  new RegExp("(^\\^=)|(^\\$=)|(^\\!=)|(^\\*=)|(^=)"),
		spaceQuote: new RegExp("^\\s+['\"]")
	};

	// THIS CONSTRUCTOR CREATES PARSED REPRESENTATIONS OF CSS-LIKE SELECTORS
	window.Selector=function(selString) {
		var exp = expressions;
		this.items = []; // for each comma-separated selector, this array has an item
		var itms = []; // this will be added to this.items
		var count = 0;
		var origSel = selString;
		while (selString.length>0) {
			if (count > 100) { throw "failed parsing '"+origSel+"' stuck at '"+selString+"'"; }
			// get rid of any leading spaces
			if (exp.leadSpace.test(selString)) { selString=selString.replace(exp.leadSpace,''); }

			// find tag name
			var tagNameMatch = exp.tagName.exec(selString);
			if (tagNameMatch) {
				if (itms.length > 0 && itms[itms.length-1].name=='tag') { itms.push({name:'descendant'}); }
				itms.push({name:'tag',tagName:tagNameMatch[1].toLowerCase()});
				selString=selString.substring(tagNameMatch[1].length);
				tagNameMatch=null;
				continue;
			}
			// explicit wildcard selector
			if (exp.wildCard.test(selString)) {
				if (itms.length > 0 && itms[itms.length-1].name=='tag') { itms.push({name:'descendant'}); }
				itms.push({name:'tag',tagName:'*'});
				selString = selString.substring(1);
				continue;
			}
			var classMatch = exp.className.exec(selString);
			var idMatch = exp.id.exec(selString);
			var attMatch = exp.att.exec(selString);
			// create a tag wildcard * if necessary
			if (classMatch || idMatch || attMatch) {
				if (itms.length==0 || itms[itms.length-1].name!='tag') { itms.push({name:'tag',tagName:'*'}); }
				var lastTag = itms[itms.length-1];
			}
			// find class name, like .entry
			if (classMatch) {
				lastTag.className=classMatch[2];
				selString=selString.substring(classMatch[1].length);
				classMatch=null;
				continue;
			}
			// find id, like #content
			if (idMatch) {
				lastTag.id=idMatch[2];
				selString=selString.substring(idMatch[1].length);
				idMatch=null;
				continue;
			}
			// find attribute selector, like @src
			if (attMatch) {
				lastTag.attributeName=attMatch[2];
				selString=selString.substring(attMatch[1].length);
				attMatch=null;
				continue;
			}
			// find attribute value specifier
			var matchTypeMatch=exp.matchType.exec(selString);
			if (matchTypeMatch) {
				// this will determine how the matching is done
				lastTag.matchType = matchTypeMatch[1] || matchTypeMatch[2] || matchTypeMatch[3] || matchTypeMatch[4] || matchTypeMatch[5];
				if(typeof lastTag.attributeName!='undefined'){
					selString=selString.substring(lastTag.matchType.length);
					if(selString.charAt(0)!='"'&&selString.charAt(0)!="'"){
						if(exp.spaceQuote.test(selString)){selString=selString.replace(exp.leadSpace,'');}
						else{throw origSel+" is invalid, single or double quotes required around attribute values";}
					}
					// it is enclosed in quotes, end is closing quote
					var q=selString.charAt(0);
					var lastQInd=selString.indexOf(q,1);
					if(lastQInd==-1){throw origSel+" is invalid, missing closing quote";}
					while(selString.charAt(lastQInd-1)=='\\'){
						lastQInd=selString.indexOf(q,lastQInd+1);
						if(lastQInd==-1){throw origSel+" is invalid, missing closing quote";}
					}
					lastTag.attributePattern=selString.substring(1,lastQInd);// lastTag should still be hanging around
					selString=selString.substring(lastTag.attributePattern.length+2);// +2 for the quotes
					continue;
				}
				matchTypeMatch=null;
			}
			// find child selector
			if (selString.charAt(0) == '>') {
				itms.push({name:'child'});
				selString=selString.substring(1);
				continue;
			}
			// find next sibling selector
			if (selString.charAt(0) == '+') {
				itms.push({name:'nextSib'});
				selString=selString.substring(1);
				continue;
			}
			// find after sibling selector
			if (selString.charAt(0) == '~') {
				itms.push({name:'followingSib'});
				selString=selString.substring(1);
				continue;
			}
			// find the comma separator
			if (selString.charAt(0) == ',') {
				this.items.push(itms);
				itms = [];
				selString = selString.substring(1);
				continue;
			}
			count++;
		}
		this.items.push(itms);
		this.selectorString=origSel;
		// do some structural validation
		for (var a=0;a<this.items.length;a++){
			var itms = this.items[a];
			if (itms.length==0) { throw "illegal structure: '"+origSel+"' contains an empty set"; }
			if (itms[0].name!='tag') { throw "illegal structure: '"+origSel+"' contains a dangling relation"; }
			if (itms[itms.length-1].name!='tag') { throw "illegal structure: '"+origSel+"' contains a dangling relation"; }
			for(var b=1;b<itms.length;b++){
				if(itms[b].name!='tag'&&itms[b-1].name!='tag'){ throw "illegal structure: '"+origSel+"' contains doubled up relations"; }
			}
		}
	}
})();

// FOR DEBUGGING, ETC
Selector.prototype.toString=function(){
	var result = this.selectorString+"\n======\n";
	for (var a=0;a<this.items.length;a++){
		for (var b=0;b<this.items[a].length;b++){
			var itm = this.items[a][b];
			result += itm.name;
			if(itm.name=='tag'){result+='='+itm.tagName;}
			if(itm.id){result+=' id='+itm.id;}
			if(itm.className){result+=' class='+itm.className;}
			if(itm.attributeName){result+=' att='+itm.attributeName;}
			if(itm.attributePattern){result+=' attPatt='+itm.attributePattern;}
			result+=' ';
		}
		result += "\n";
	}
	return result;
};

(function(){

	// SUBROUTINE FOR matchesElement()
	function matchIt(el, itm) {
		// try to falsify as soon as possible
		if (!el) { return false; }
		if (el.nodeName.toLowerCase()!=itm.tagName && itm.tagName!='*') { return false; }
		if (itm.className && !hasClassName(el,itm.className)) { return false; }
		if (itm.id && el.id != itm.id) { return false; }
		if (itm.attributeName) {
			if (typeof el.hasAttribute != 'undefined') {
				if (!el.hasAttribute(itm.attributeName)) { return false; }
				var att = el.getAttribute(itm.attributeName);
			}else{
				if(el.nodeType!=1) {return false;}
				var att = el.getAttribute(itm.attributeName);
				if(itm.attributeName=='class'){att=el.className;}
				else if(itm.attributeName=='for'){att=el.htmlFor;}
				if(!att){return false;}
			}
			if (itm.attributePattern) {
				if (itm.matchType=='!='){
					if (att==itm.attributePattern){return false;}
				} else if (itm.matchType=='^='){
					if (att.indexOf(itm.attributePattern)!=0){return false;}
				} else if (itm.matchType=='*='){
					if (att.indexOf(itm.attributePattern)==-1){return false;}
				} else if (itm.matchType=='$='){
					if (att.indexOf(itm.attributePattern)!=att.length-itm.attributePattern.length){return false;}
				} else if (itm.matchType=='='){
					if (att!=itm.attributePattern){return false;}
				}else{
					if(!itm.matchType){throw "illegal structure, parsed selector cannot have null or empty attribute match type";}
					else{throw "illegal structure, parsed selector cannot have '"+itm.matchType+"' as an attribute match type";}
				}
			}
		}
		return true;
	}

	// PASS IT AN ELEMENT TO SEE IF IT MATCHES
	Selector.prototype.matchesElement = function(el) {
		if (!el) { throw this.selectorString+' cannot be evaluated against '+el; }
		if (el.nodeType != 1) { throw this.selectorString+' cannot be evaluated against element of type '+el.nodeType; }
		var result = new Array(this.items.length); // array of nulls. if one or more get set true, return true
		commas:for (var a=0;a<this.items.length;a++) { // for each comma-separated selector
			var tempEl = el;
			var itms = this.items[a];
			for (var b=itms.length-1; b>=0; b--) { // loop backwards through the items
				var itm = itms[b];
				if (itm.name == 'tag') {
					if (!matchIt(tempEl, itm)) {
						// these relational selectors require more extensive searching
						if (tempEl && b < itms.length-1 && itms[b+1].name=='descendant') { tempEl=tempEl.parentNode; b++; continue; }
						else if (tempEl && b < itms.length-1 && itms[b+1].name=='followingSib') { tempEl=tempEl.previousSibling; b++; continue; }
						else { continue commas; } // fail this one
					}
				}
				else if (itm.name == 'nextSib') { tempEl = prevElem(tempEl); }
				else if (itm.name == 'followingSib') { tempEl = prevElem(tempEl); }
				else if (itm.name == 'child') { tempEl = tempEl.parentNode; }
				else if (itm.name == 'descendant') { tempEl = tempEl.parentNode; }
			}
			result[a]=true;
		}
		for (var a=0;a<result.length;a++){if(result[a]){return true;}}
		return false;
	};

})();

// GETS THE TAG NAMES THAT THE SELECTOR REPRESENTS
Selector.prototype.getTagNames = function(el) {
	var hash = {}; // this avoids dupes
	for (var a=0;a<this.items.length;a++){
		hash[this.items[a][this.items[a].length-1].tagName]=null;
	}
	var result = [];
	var tag;
	for (tag in hash){result.push(tag);}
	return result;
}

// GETS ALL MATHCING ELEMENTS
Selector.prototype.getMatchingElements = function(startHere){
	if(!startHere){startHere = window.document;}
	var result = [];
	var tagNames = this.getTagNames();
	for (var a=0;a<tagNames.length;a++){
		var els = startHere.getElementsByTagName(tagNames[a]);
		for(var b=0;b<els.length;b++){
			if(this.matchesElement(els[b])){result.push(els[b]);}
		}
	}
	return result;
};

// FOR PERFORMANCE
Selector.prototype.isSimple = function() {
	return (this.items.length == 1 && this.items[0].length == 1);
}
