//WARNING: This is pre-alpha version, anything might change, just testing ideas

//param names: r=resource, m=method, p=params, c=callback, gp=get-params,

var jsgCore = {
	map: function (f, o) {
		var r = this.clone(o);
		for (i in o) { r[i] = f(o[i]); }
		return r;
	},

	reduce: function (f, o, s) {
		var r = s;
		for (i in o) { r = f( r, o[i], i ); }  
		return r;
	},

	doeach: function ( f, o ) {
		for (i in o) { f( o[i], i ); }
	},
	
	seek: function (f, o) {
		for (i in o) { var t = f(o[i], i); if (t) return t; }
		return false;
	},

	has: function (n, o) {
		return this.seek(function(e, i){ return e == n; }, o);
	},

	apply: function (d, c) { 
		var r = this.clone(d);
		for (n in r) if (c[n]) r[n] = c[n](r[n], n);
		return r;
	},
	
	applyL: function (d, c) { 
		var r = this.clone(d);
		for(var i=0;i<r.length;i++) {
			for (n in r[i]) if (c[n]) r[i][n] = c[n](r[i][n], i, n);
		}
		return r;
	},
	
	injectL: function (d, c) {
		var r = this.clone(d);
		for(var i=0;i<r.length;i++) {
			for (k in c) r[i][k] = c[k](r[i], i);
		}
		return r;
	},
	
	clone: function(o) {
		var n = (o instanceof Array) ? [] : {};
		for (i in o) {
			if (o[i] && typeof o[i] == "object") {
				n[i] = this.clone(o[i]);
			} else n[i] = o[i]
		} return n;
	},

	equal: function (o1, o2) {
		return ! ( 
		o1 instanceof Array && o2 instanceof Array || 
		o1 instanceof Object && o2 instanceof Object ?
			jsgCore.seek( function ( o, i ) { return ! jsgCore.equal(o, o2[i] ) }, o1 )
		: o1 != o2 );
	},
	
	//html related - here so that jsgAjax has just jsgCore as dependancy 
	isInputField: function ( e ) {
		return jsgCore.has(e.tagName.toLowerCase(), ['input', 'textarea', 'select', ]);
	}
};

var jsgData = 
{

	setLoc: function (r, m, p, data) {
		var d = jsgDATA;
		if (d[r] == null) d[r] = { };        //undefined , but IE doesn't work
		if (d[r][m] == null) d[r][m] = { }; //undefined , but IE doesn't work
		d[r][m][p] = data;
	},
	
	getLoc: function (r, m, p, def) {
		var d = jsgDATA;
		return d[r][m][p]!=null?d[r][m][p]:(def?def:null);
	},
	
	getDo: function (r, m, p, c) {
		var d = jsgDATA;
		if (d[r][m][p] != null) { return c(d[r][m][p]); } else { this.getDoSrv(r,m,p,c); return null; }
	},
	
	getDoSrv: function (r, m, p, c) {
		jsgAjax.call("/_rdb/"+r+"/"+m+"?"+p, 
		function (d){ var de=eval(d); this.setLoc(r,m,p,de); if(c){ c(de); } });
	},
	
	invalLoc: function (r, m, p) {
		var d = jsgDATA;
		if (p) { d[r][m][p] = null; } else { if (m) { d[r][m] = null;} else { d[r] = null; } } 
	},
	
	sendSrv: function (r, m, p, c, gp) {
		jsgAjax.call("/_rdb/"+r+"/"+m+(gp?"?"+gp:''), 
		function (d){ var de=eval(d); if(gp){this.setLoc(r,m,gp,de);} if (c){ c(de); } }, p);
	},
	
	
	selectById: function (data, id) {
		return jsgCore.seek(function(x, i){ return x.id==id?x:false; }, data);
	},

	deleteById: function (data, id) {
		return jsgCore.seek(function(x, i){ if (x.id==id){ data.splice(i, 1); return true } else { return false } }, data);
	}
};

//param names: l=label, h=href, c=callback, d=data, tpl=template, def=default, o=object
//             v=value, t=tag, s=string
var jsg = {
	
	a: function ( l, h, c, pass ) {
		return this.wrap( l, 'a '+this.propdef(h, "href", "#")+
		this.propif(c+(pass?'':';return false;'), "onclick") );
	},

	closeLink: function(l, c) {
		return this.wrap( this.a(l?l:'close', '#', 'this.parentNode.parentNode.style.display = "none"; '+(c?c:'')), 'div style="float: right;"');
	},
	
	brclear: function () { return '<br style="clear: both;" />'; },
	
	tr: function ( d, inner ) { return this.lis(d, inner?inner:'td', 'tr'); },
	
	lis: function (d, inner, outer) { 
		var r = ''; 
		inner = inner?inner:'li'; outer = outer?outer:'ul';
		for(var i=0;i<d.length;i++){ r += this.wrap(d[i]?d[i]:'&nbsp;', inner); }
		return this.wrap(r, outer);
	},
	
	unit: function (du, tpl, def) {
		var t = tpl;
		for (var p in du){
			t = t.replace(new RegExp("{"+p+"}", 'g'), du[p]?du[p]:(def?def:'..'));
		}
		return t;
	},
	
	list: function (d, tpl, outer, def) {
		var r = '';
		for(var i=0;i<d.length;i++) {
			r += this.unit(d[i], tpl, def); 
		}
		return outer?this.wrap(r, outer):r;
	},

	$: function (id) { return document.getElementById(id); },
	into: function (el, d) { el.innerHTML = d; },
	$into: function (id, d) { $(id).innerHTML = d; },

	getparam: function (n, def) {
		var match = window.location.search.match(new RegExp("[?|&]?"+n+"=([^&]*)"))
		return match?match[1]:def;
	},


	tag: function (t) {return t?'<'+t+'>':''; },
	etag: function (t) {return t?'</'+t+'>':''; },
	wrap: function (v, t) { return this.tag(t)+v+this.etag(this._firstword(t)); },
	wrapif: function (v, t) { return v?this.wrap(v, t):''; },
	prop: function (v, n) { return " "+n+"='"+v+"'"; },
	propif: function (v, n) { return v?this.prop(v,n):''; },
	propdef: function (v, n, def) { return this.prop(v?v:def,n); },
	
	_firstword: function (s) { var p=s.indexOf(' '); return p>=0?s.substring(0,p):s; }
};

//param names: l=line, f=field, i=input, os=options, o=option

var jsgForm = {
	
	render: function ( d, id, onsub, action ) {
		var _ = jsg;
		return _.wrap( this.renderHidden(d.hidden) + this.renderFields(d.fields)+_.wrap('', 'div class="row"'), 
		'form '+_.propdef(d.method, 'method', 'post')+
		(action?_.prop(action, 'action'):_.propif(d.action, 'action'))+
		(id?_.prop(id, 'id'):_.propif(d.id, 'id'))+
		(onsub?_.prop("return "+onsub.toString()+"(this)", 'onsubmit'):_.propif(d.onsubmit, 'onsubmit'))
		);
	},

	renderHidden: function ( f ) {
		var r = '';
		for (id in f) {
			r +=  "<input type='hidden' "+_.prop(id, 'id')+
			_.propif(f[id].name, 'name')+_.propif(f[id].value, 'value')+" />";
		}
		return r+"\n";
	},
	
	renderFields: function ( f ) {
		var r = '';
		for (id in f) {
			r += this.renderLine( id, f[id]);
		}
		return r;
	},
	
	renderLine: function ( id, l ) {
		var _ = jsg;
		return _.wrap(
		_.wrap(l['label']?l.label:'&nbsp;', 'label for="'+id+'"')+
		_.wrap(
		(l.inp.name?_.wrap('', 'span class="vnote" title="'+l.inp.name+'"'):'')+
		this.renderField(id, l), 'div class="field"'), 
		'div class="row"');
	},
	
	renderField: function ( id, l ) {
		var _ = jsg;
		return this.renderInput(id, l.inp)+_.wrapif(l.help, 'em')+"\n";
	},
	
	renderInput: function ( id, i ) {
		var _ = jsg;
		switch (i.type) {
		case 'text': case 'radio': case 'checkbox': case 'submit': case 'reset':
			return "<input "+_.propif(i.type, 'type')+this._inputBaseProps(id, i)+
			_.propif(i.value, 'value')+_.propif(i.cls, 'class')+ 
			_.propif(i.checked?'checked':null, 'checked')+" />";
			break;
		case 'textarea':
			return "<textarea "+this._inputBaseProps(id, i)+
			_.propif(i.cols, 'cols')+_.propif(i.rows, 'rows')+">"+
			(i.value?i.value:'')+"</textarea>";
			break;
		case 'select':
			return "<select "+this._inputBaseProps(id, i)+">"+
			this._renderOptions(i.options)+"</select>";
			break;
		}
		return '';
	},

	_renderOptions: function ( os ) {
		var r = '', _ = jsg;
		if (os instanceof Array){
			for (o in os) {
				if (! (os[o] instanceof Function)){
					var isarr = os[o] instanceof Array;
					r += _.wrap(isarr?os[o][1]:os[o], "option"+(isarr?" value='"+os[o][0]+"'":''));
				}
			}
		} else {
			var ds = DATA[os.data], v = os.values, l = os.labels;  //BAD BAD BAD .. DATA hardcoded :/
			for (d in ds) {
				if (! (ds[d] instanceof Function)){
					r += _.wrap(ds[d][l], "option"+(v?" value='"+ds[d][v]+"'":''));
				}
			}			
		}
		return r;       
	},

	_inputBaseProps: function ( id, i ) {
		var _ = jsg;
		return _.propif(id, 'id')+_.propif(i.name, 'name')+_.propif(i.size, 'size')
		+_.propif(i.onclick, 'onclick')+_.propif(i.onchange, 'onchange')+
		_.propif(i.onkeydown, 'onkeydown');
	},

	setValues: function ( f, d ) {
		return jsgCore.doeach(function (e,i) {
			if (jsgCore.isInputField(e)) if (e.type!='submit') e.value = d[e.name]?d[e.name]:'';
		}, f.getElementsByTagName('*'));
	},
	
	setVNotes: function ( f, d, lang ) {
		return jsgCore.doeach(function (e,i) {
			if (e.className=='vnote' && d[e.title]) {
				e.style['display'] = 'block';
				_.into(e, lang?lang[d[e.title]]:d[e.title]);
			}
		}, f.getElementsByTagName('span'));
	},

	setDRMethod: function ( f, m ) {
		var a = f.action;
		var s = a.indexOf("_m=")+3;
		f.action = a.substring(0, s) + m + a.substring(a.indexOf("&", s));
	}

};

var jsgDebug = {
	pprint: function (o) {
		var r='', isarr=o instanceof Array, frs=true;
		if (isarr || o instanceof Object) { 
			r+=(isarr?'[':'{');
			for (i in o) { 
				r += (frs?' ':', ')+(!isarr?this.pprint(i)+': ':'')+this.pprint(o[i]);
				frs=false;
			}
			r+=(isarr?' ]':' }');
		} else { 
			var q = typeof o == "string"?'"':''; 
			r+=q+o+q; 
		}
		return r;
	}
};

var jsgTest = {
	go: function ( tests ) { 
		return jsgCore.reduce(
			function( s, test ) {
				var res = jsgCore.equal(test.f(), test.r)?'passed':'failed' ;
				return s + jsg.wrap( test.n+' '+jsg.wrap(res,'b')+
					(res=='failed'?' | expected: <b>' + jsgDebug.pprint(test.r) + 
					'</b> -- ': ' -- expected and ' ) + ' got: <b>'+ jsgDebug.pprint(test.f()) + '</b>',
					'div class="'+res+'"'
				);
			}, tests, ""
		)
	}
};


//param names: r=response, u=url, c=callback, gp=get-params, pp=post-params, e=error

var jsgAjax = {
	getRequestInst: function(){
		return window.XMLHttpRequest ? new XMLHttpRequest() : new ActiveXObject('Microsoft.XMLHTTP');  
	},
	
	get:  function(u, c, gp) { this.call(u,c,gp); },
	post: function(u, c, p) { this.call(u,c,null,p); },
	
	call: function(u, c, gp, pp) {
		var t = this, r = t.getRequestInst(), gd='', pd='';
		if (!r) { alert('No Ajax?'); return; }
		r.onreadystatechange = c ? function() { t.onChange(r, c); } : null;

		if (pp) { for (var n in pp) {pd += (pd?'&':'')+n+'='+encodeURI(pp[n]); } }
		if (gp) { for (var n in gp) {gd += (gp?'&':'')+n+'='+encodeURI(gp[n]); } }
		
		r.open(pp?'POST':'GET', u+(gd?"?"+gd:''), true);
		r.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
		r.setRequestHeader("Content-length", pd.length);
		r.setRequestHeader("Connection", "close");
		r.send(pd);
	},

	onChange: function(r, c) {
		if (r.readyState == 4) {
			if (r.responseText) {
				c(r.responseText);
			} else { this.onError(); }
		}
	},

	onError: function (e) { alert(e?"ajax err:"+e:'jsgAjax error.'); },
	
	suckForm: function (f) {
		return jsgCore.reduce(function (s, e) {
			var t = e.tagName.toLowerCase(), val = e.value;
			if (jsgCore.isInputField(e)) 
			s[e.name] = (e.type=='checkbox'||e.type=='radio'?(e.checked?e.value:''):e.value);
			return s;
		}, f.getElementsByTagName('*'), {});
	},
	
	postForm: function (f, c) {
		this.post(f.action, c, this.suckForm(f)); 
	}

};

var jsgDom = {
	seekFwd: function (el, name, limit) {
		return this.seek('fwd', el, name, limit);
	},
	
	seekBack: function (el, name, limit) {
		return this.seek('back', el, name, limit);
	},
	
	seek: function (dir, el, name, limit) {
		limit = limit || 10; name = name.toLowerCase();
		var m = ''; switch ( dir ){ 
		case "back": m = 'previousSibling'; break;
		case "fwd": m = 'nextSibling'; break;
		}
		while(el || limit > 0) {
			el = el[m];
			if (el.nodeName.toLowerCase() == name) return el;
			limit -= 1;
		} return null;
	}
};

//fo - format , d - date , v - value 

var jsgDate = {
	format: function (d, fo) {
		var r = '';
		for(var i=0;i<fo.length;i++){
			switch(fo.substr(i, 1)) {
			case 'Y': r += d.getFullYear(); break;
			case 'y': r += d.getYear(); break;
			case 'd': r += this._padTo2(d.getDate()); break;
			case 'm': r += this._padTo2(d.getMonth() + 1); break;
			default: r += fo.substr(i, 1);
			}
		}
		return r;
	},
	
	today: function (fo) {
		return this.format(new Date(), fo);
	},
	_padTo2: function (num) {
		var n = ""+num;
		return n.length == 1 ? "0"+n : n ;
	}
}

var jsgFormat = {
	toMoney: function(n, c, d, t, cur) { //written by http://www.joninhas.ath.cx
		var m = (c = Math.abs(c) + 1 ? c : 2, d = d || ",", t = t || ".", cur = cur || '€',
		/(\d+)(?:(\.\d+)|)/.exec(n + "")), x = m[1].length > 3 ? m[1].length % 3 : 0;
		return (x ? m[1].substr(0, x) + t : "") + m[1].substr(x).replace(/(\d{3})(?=\d)/g,
		"$1" + t) + (c ? d + (+m[2] || 0).toFixed(c).substr(2) : "") + cur;	
	}
}

//utils-shortcuts -- remove if you need to 
var $ = jsg.$;

var _c = jsgCore;
var _ = jsg;
var _f = jsgForm;
var _data = jsgData;
var _a = jsgAjax;
var _d = jsgDebug;
var _a = jsgAjax;
var _test = jsgTest;
var _dom = jsgDom;
var _date = jsgDate;
var _format = jsgFormat;
