/**
 *	Selector
 *	--------------------------
 */

Selector = {
	REG_TOKENIZE:/([\s>+~.#:*]|[a-z_-]+)/g,
	REG_IMPLIEDALL:/(^|\s)([.#:])/g,
	REG_WHITESPACE:/\s+([>+~])\s+/g,
	
	filters:{},
	pseudos:{},
	find:function(select) {
		var token,tokens = this.tokenize(select);
		var result = [document];
		for(var i=0; (token = tokens[i++]);) {
			result = this.filters[token](result, tokens[i++]);
		}	return result;
	},

	tokenize:function(select) {
		var selector = ' '+select.replace(this.REG_IMPLIEDALL, '$1*$2');
		selector = selector.replace(this.REG_WHITESPACE, '$1');
		return selector.match(this.REG_TOKENIZE);
	},

	register:function(selector, filter) {
		this.filters[selector] = function(nodes, token) {
			var result = [];
			filter(result, nodes, token);
			return result;
		};
	},

	registerPseudo:function(pseudo, filter) {
		this.pseudos[pseudo] = filter;
	}
}

/**
 *	Simple selectors
 *	--------------------------
 */

Selector.register(' ', function(result, nodes, name){
	for(var base,i=0; (base = nodes[i++]);) {
		var j, node, set = base.getElementsByTagName(name);
		for(j=0; (node = set[j++]);) {
			result[result.length] = node;
		}
	}
});

Selector.register('#', function(result, nodes, id) {
	for(var node, i=0; (node = nodes[i++]);) {
		if(node.id && node.id == id) {
			result[result.length] = node;
		}
	}
});

Selector.register('.', function(result, nodes, className) {
	var classReg = new RegExp('\\b'+className+'\\b');
	for(var node, i=0; (node = nodes[i++]);) {
		if(node.className && classReg.test(node.className)) {
			result[result.length] = node;
		}
	}
});

Selector.register('>', function(result, nodes, name) {
	var nameReg = new RegExp('\\b'+name+'\\b', 'i');
	for(var node, i=0; (node = nodes[i++]);) {
		for(var child, j=0; (child = node.childNodes[j++]);) {
			if(child.nodeType == 1 && nameReg.test(child.nodeName)) {
				result[result.length] = child;
			}
		}
	}
});

Selector.register('+', function(result, nodes, name){
	var nameReg = new RegExp('\\b'+name+'\\b', 'i');
	for(var sib,node, i=0; (node = nodes[i++]);) {
		do {
			sib = node.nextSibling;
			if(sib.nodeType == 1) {
				if(nameReg.test(sib.nodeName)) {
					result[result.length] = sib;
				}
				break;
			}
		} while(sib)
	}
});

Selector.register(':', function(result, nodes, pseudo){
	Selector.pseudos[pseudo](result, nodes);
});

/**
 *	Pseudoclasses
 *	--------------------------
 */

Selector.registerPseudo('first-child', function(result, nodes){
	for(var node, first, i=0; (node = nodes[i++]);){
		first = node.parentNode.firstChild;
		while(first && first.nodeType != 1) first = node.nextSibling;
		if(first && first == node) {
			result[result.length] = first;
		}
	}
});

Selector.registerPseudo('last-child', function(result, nodes){
	for(var node, last, i=0; (node = nodes[i++]);){
		last = node.parentNode.lastChild;
		while(last && last.nodeType != 1) last = last.previousSibling;
		if(last && last == node) {
			result[result.length] = last;
		}
	}
});
