/**
Copyright (c) 2010, All Right Reserved, Wong Shek Hei @ shekhei@gmail.com
License: GNU Lesser General Public License (http://www.gnu.org/licenses/lgpl.html)
 **/
(function($) {
	expr = /[.#\w].([\S]*)/g,
		classexpr = /(\.)([^.#[]+)/g,
		idexpr = /(#)[^.#[]+/,
		tagexpr = /^[\w]+/,
		varexpr = /(\w+?)=(['"])([^\2$]*?)\2/,
		simpleselector = /^[\w]+$/,
		rTrimmer = /^[\s]*[\"\']?|[\"\']?[\s]*$/g,
		rSpaceTrim = /^[\s]+|[\s]+$/g;
	if ( !String.prototype.trim) {
		String.prototype.trim = function() {
			return this.replace(rSpaceTrim, '');
		}
	}
	function parseSelector( selector ) {
		//var buffer = [];
		var buffer = {sel:"",val:[]},
			arr = [],
			isVar = false,
			quote,
			varbuff = "",
			pc = '', c = '', i,
			selLen = selector.length;
		for ( i = 0; i < selLen; i++ ) {
			pc = c;
			c = selector.charAt(i);
			if ( isVar ) {
				if ( c === '\\' && i+1 < selLen ) {
					varbuff += selector.charAt(++i);
				}
				else if ( quote === c ) {
					quote = undefined;
					varbuff += c;
				}
				else if ((c === '\'' || c === '\"') && quote === undefined ) {
					quote = c;
					varbuff += c;
				}
				else if ( c === ']' && quote === undefined ) {
					buffer.val.push(varbuff);
					varbuff = "";
					isVar = false;
				}
				else if ( c !== ']' || quote !== undefined ) {
					if ( quote === undefined && c === ',' ) {
						buffer.val.push(varbuff);
						varbuff = "";
					}
					else {
						varbuff += c;
					}
				}
			}
			else if ( c === '\\' && i+1 < selLen ) {
				if ( isVar ) {
					varbuff += selector.charAt(++i);
				}
			}
			else if ( c === '[' && quote === undefined) {
				isVar = true;
			}
			else if ( c === ' ' || c === '+' || c === "<" ) {   // end of a tag, or as a sibling element
				// now lets peek forward
				if ( c === " " ) {
					for ( ; c === " " && i < selLen; i++ ) {
						c = selector.charAt(i);
					}
					if ( i < selLen ) {
						--i;
						c = selector.charAt(i);
					}
				}
				arr.push(buffer);
				if ( c === '+' || c === '<') {
					arr.push({sel:c, val:''});
					while ( i < selLen && ( c = selector.charAt(++i) ) === " " ) {}
					if ( i < selLen ) {
						c = selector.charAt(--i);
					}
				}
				buffer = {sel:"",val:[]};
			}
			else if ( c !== ' ' && c !== ']' ) {
				buffer.sel+= c;
			}
		}
		if ( buffer.sel.length != 0 || buffer.val.length != 0 ) {
			arr.push(buffer);
		}
		var len = arr.length
		for ( i = 0; i < len; i++ ) {
			var sel = arr[i].sel, temp ={};

			if ( sel === '+' || sel ==="<") {
				temp.tag = sel;
			}
			else {
				temp.tag = tagexpr.exec(sel);
				temp.id = idexpr.exec(sel);
				if ( temp.id && $.isArray(temp.id) ) {
					temp.id = temp.id[0].substr(1);
				}
				if ( !temp.tag ) {
					temp.tag = 'div';
				}
				temp.vars = [];
				var classes = [];
				for ( var j = 0; j < arr[i].val.length; j++ ) {
					var index = arr[i].val[j].indexOf('=');
					var key = arr[i].val[j].substr(0, index).trim();
					var val = arr[i].val[j].substr(index+1);
					val = val.replace(rTrimmer, '');
					if ( key === "text" ) {
						temp.text = val;
					} else if ( key === "class" && val.trim().length ) {
						classes.push(val);
					}else {
						temp.vars.push([key,val]);
					}
				}
				var arr2 = sel.match(classexpr);
				if ( arr2 ) {
					for ( var j = 0; j < arr2.length; j++ ) {
						classes.push(arr2[j].substr(1));
					}
					temp.className = classes.join(" ");
				}
			}

			arr[i] = temp;
		}
		return arr;
	};
	function nonArrVer(selector, count) {
		var arr = [], newel = [];
		if ( simpleselector.test(selector) ) {
			arr = [ { tag: selector } ];    //if it is just a simple tag selector, then there is no need to parse it, makes it much more efficient
		}
		else {
			arr = parseSelector(selector);
		}

		if ( typeof count === "undefined" ) {
			count = 1;
		}
		var returns = $();
		var parent = [];
		var lastparent = [];
		var tempholder = document.createElement('div');
		//var buff = [];
		var depth = 0, i = 0, x = 0, len = arr.length;
		for ( i = 0; i < len; i++ ) {
			if ( arr[i].tag == '+' || arr[i].tag === '<' ) { parent = lastparent.slice(); --depth;}
			else {
				for ( x = 0; x < count; x++ ) {

					if ( arr[i].tag == 'input' ) { // special case, needs to create tag by html
						var html = [];
						html.push( "<"+arr[i].tag);
						if ( arr[i].id) {
							html.push( "id=\'"+arr[i].id+"\'");
						}
						if ( arr[i].className || (arr[i].vars && "class" in arr[i].vars)) {
							var val = [];
							if ( arr[i].className ) { val.push(arr[i].className); }
							if ( arr[i].vals["class"] ) { val.push(arr[i].vars["class"]); }
							html.push( "class=\'"+val.join(" ") );
							html.push( "\'");
						}
						if ( arr[i].vars ) {
							for ( var j = 0; j < arr[i].vars.length; j++ ) {
								html.push(arr[i].vars[j][0]+"=\'"+arr[i].vars[j][1]+"\'");
							}
						}
						if ( arr[i].text ) {
							html.push("value=\'"+arr[i].text+"\'");
						}
						html.push("/>");
						lastparent[x] = parent[x];
						if ( !parent[x] ) {
							tempholder.innerHTML = html.join(" ");
							parent[x] = tempholder.removeChild(tempholder.firstChild);
						} else {
							parent[x].innerHTML = parent[x].innerHTML+html.join(" ");
							parent[x]=parent[x].lastChild;
						}
					}
					else {
						var buff = document.createElement(arr[i].tag);
						if ( arr[i].vars ) {
							for ( var j = 0; j < arr[i].vars.length; j++ ) {
								buff.setAttribute(arr[i].vars[j][0], arr[i].vars[j][1]);
							}
						}
						if ( arr[i].id ) {
							buff.id = arr[i].id;
						}
						if ( arr[i].className ) {
							buff.className = arr[i].className;
						}
						if ( arr[i].text ) {
							buff.appendChild(document.createTextNode(arr[i].text));
						}
						lastparent[x] = parent[x];
						if ( parent[x] ) {
							parent[x] = parent[x].appendChild(buff);
						} else {
							parent[x] = buff;
						}
					}
				}
				if ( !depth++ ) {
					Array.prototype.push.apply(returns, parent);
				}
			}
			Array.prototype.push.apply( newel, parent );
		}
		return returns;
	};
	function arrVer(sel, arr, count ) {
		var regex = /{[^}]*}/g;
		var vars = sel.match(regex) || [];
		var newel = $();
		for ( var i = 0; i < arr.length; i++ ) {
			var tsel = sel;
			for ( var j = 0; j < vars.length; j++ ) {
				var vname = vars[j].substr(1,vars[j].length-2).split('.'),
					val = arr[i][vname[0]], oldval;
				if ( vname[0].trim() === "" ) {
					val = arr[i].toString();
				} else {
					for ( var x = 1; x < vname.length; x++ ) {
						oldval = val;
						val = val[vname[x]];
						if (!val) {
							val = "";
							break;
						}
						if ( typeof val === "function" ) {
							val = val.call(oldval)
						}
					}
				}
				if ( $.isArray(val) ) { val = val.join(","); }
				if ( typeof val === "string" ) {
					val = val.replace(/\\['"\\]|['"]/g, function (s) {
						return "\\"+s;
					});
				} else if ( typeof val === "function" ) {
					val = val.call(oldval)
				}
				val = val || "";
				tsel = tsel.replace(vars[j], val);
			}
			Array.prototype.push.apply(newel, nonArrVer(tsel, count));
			// newel = newel.pushStack(nonArrVer(tsel, count));
		}

		return newel;
	};
	var isArray = Array.isArray || function(arr){return toString.call(arr) === "[object Array]";}
	$.jseldom = function(selector) {
		if ( arguments.length == 2 && $.isPlainObject(arguments[1])) {
			return arrVer.apply(this, [arguments[0], [arguments[1]]]);
		}
		else if ( arguments.length == 1 || (arguments.length == 2 && !isArray(arguments[1]))) {
			return nonArrVer.apply( this, arguments );
		}
		else if ( arguments.length == 2 ) {
			return arrVer.apply( this, arguments);
		}
	}
	$.jseldomf = function(selector) {
		var args = [selector, [Array.prototype.slice.call(arguments,1)]];
		return arrVer.apply(this, args);
	}
})(jQuery);