// 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 jsGooC = {
map: function (f, arr) {
		var r = this.clone(arr);
		for (i=0;i< arr.length;i++) { r[i] = f(arr[i]); }
		return r;
	},

reduce: function (f, arr, s) {
		var r = s;
		for (var i = 0; i < arr.length; i++) { r = f( r, arr[i] ); }  
		return r;
	},

doeach: function ( f, arr ) {
		for (var i = 0; i < arr.length; i++) { f( arr[i], i ); }
	},

doeachstep: function ( f, arr ) {
		var i = 0;
		while (i < arr.length) { i = f( arr[i], i ); } //arr.length in loop because you can delete items in f()
	},
	
seek: function (f, arr) {
		for (var i = 0; i < arr.length; i++) { var t = f(arr[i], i); if (t) return t; }
		return false;
	},

has: function (n, arr) {
		return this.seek(function(e, i){ return e == n; }, arr);
	},

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;
	},
	
inject: function (d, c) {
		var r = this.clone(d);
		for (k in c) r[k] = c[k](r);
		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;
	},
strpad: function (s, fil, len) {
		return (s.length < len) ? this.strpad(fil+s, fil, len) : s;
	},
	
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 jsGoo Core as dependancy 
isInputField: function ( e ) {
		return jsGooC.has(e.tagName.toLowerCase(), ['input', 'textarea', 'select', ]);
	}
};

var jsGooDR = 
{

setLoc: function (r, m, p, data) {
		var d = jsGooDR_DATA;
		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 = jsGooDR_DATA;
		return d[r][m][p]!=null?d[r][m][p]:(def?def:null);
	},
	
getDo: function (r, m, p, c) {  
		var d = jsGooDR_DATA;
		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 = jsGooDR_DATA;
		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 jsGooC.seek(function(x, i){ return x.id==id?x:false; }, data);
	},

deleteById: function (data, id) {
		return jsGooC.seek(function(x, i){ if (x.id==id){ data.splice(i, 1); return true } else { return false } }, data);
	},
	
updateById: function (data, unit) {
		return jsGooC.seek(function(x, i){ if (x.id==unit.id){ data[i] = unit; 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 jsGoo = {
	
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;
	},

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; },
	
k: function (evt){ // kill event
		if (evt.stopPropagation){
			evt.stopPropagation(); evt.preventDefault();
		}else if(typeof evt.cancelBubble != "undefined"){
			evt.cancelBubble = true; evt.returnValue = false;
		}
		return false;
	}
};

//param names: l=line, f=field, i=input, os=options, o=option

var jsGooF = {
	
render: function ( d, id, onsub, action ) {
		var _ = jsGoo;
		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(this._genOnSubmit(onsub), '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 _ = jsGoo;
		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 _ = jsGoo;
		return this.renderInput(id, l.inp)+_.wrapif(l.help, 'em')+"\n";
	},
	
renderInput: function ( id, i ) {
		var _ = jsGoo;
		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 = '', _ = jsGoo;
		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 _ = jsGoo;
		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 jsGooC.doeach(function (e,i) {
			if (jsGooC.isInputField(e)) if (e.type!='submit') e.value = d[e.name]?d[e.name]:'';
		}, f.getElementsByTagName('*'));
	},
	
setVNotes: function ( f, d, lang ) {
		return jsGooC.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;
		var i2 = a.indexOf("&", s);
		f.action = a.substring(0, s) + m + ( i2 >= 0 ? a.substring(i2) : '' );
	},

setOnSubmit: function ( f, onsub ) {
		f.onsubmit = onsub;
	},

	_genOnSubmit: function ( onsub ) { return "jsGoo.k(event); return "+onsub.toString()+"(this)" }
	
};

var jsGooDbg = {
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 jsGooUT = {
test: function ( tests ) { return jsGooC.reduce(function( s, test ) {
			var res = (test.f() == test.r)?'pass':'fail' ;
			return s + jsGoo.wrap( test.n+' '+res+
			(res=='fail'?' | expected: <b>' + jsGooDbg.pprint(test.r) + 
			'</b> | got: <b>'+ jsGooDbg.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 jsGooC.reduce(function (s, e) {
			var t = e.tagName.toLowerCase(), val = e.value;
			if (jsGooC.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);
	},

seekOut: function (el, name, limit) {
		return this.seek('out', el, name, limit);
	},

seekIn: function (el, name, limit) {
		return this.seek('in', 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;
		case "out": m = 'parentNode'; break;
		case "in": m = 'firstChild'; break;
		}
		el = el[m];
		while(el && limit > 0) {
			if (el.nodeName.toLowerCase() == name) return el;
			limit -= 1;
			el = el[m];
		} return null;
	},

seekWith: function (dir, el, f, limit) {
		limit = limit || 10;
		var m = ''; switch ( dir ){ 
		case "back": m = 'previousSibling'; break;
		case "fwd": m = 'nextSibling'; break;
		case "out": m = 'parentNode'; break;
		case "in": m = 'firstchild'; break;
		}
		el = el[m];
		while(el && limit > 0) {
			if (f(el)) return el;
			limit -= 1;
			el = el[m];
		} return null;
	}
};

/*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': case 'y': r += d.getFullYear(); 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;
		if (n.length == 1) return "0"+n;
		else return n;
	},
ensureFormat: function (d, fo) // leave much to improve
	{	
		var sepa = fo.substr(1,1), fa = fo.split(sepa), da = d.split(sepa), dr = new Array(0,0,0);
		for (var ix=0; ix<3; ix++)
		{
			if (fa[ix] == 'y') dr[0] = da[ix];
			if (fa[ix] == 'm') dr[1] = da[ix];
			if (fa[ix] == 'd') dr[2] = da[ix];
		}
		return dr[0]+'/'+dr[1]+'/'+dr[2];
	},
to: function (ds, fo)
	{
		var d = new Date();
		d.setTime(Date.parse(this.ensureFormat(ds, fo)));
		return d;
	},
clearTime: function (d) {
		d.setHours(0); d.setMinutes(0); d.setSeconds(0); d.setMilliseconds(0);
		return d;
	},
compare: function (d1, d2) { //fc- format char
		this.clearTime(d1); this.clearTime(d2);
		return (d1.getTime() - d2.getTime()) / (60*60*24*1000);
	}
}

var jsgFormat = {
toMoney: function(n, c, d, t, cur) { //written by http://www.joninhas.ath.cx
		n = parseFloat(n);
		if (n || n == 0) {
			var m = (c = Math.abs(c) + 1 ? c : 2, d = d || ",", t = t || ".", cur = (cur == null ? '€' : ''),
			/(\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;	
		}
		return n;
	}
}

//utils-shortcuts -- remove if you need to 
function $(id) { return document.getElementById(id); }
var _c = jsGooC;
var _ = jsGoo;
var _f = jsGooF;
var _a = jsgAjax;
var _dr = jsGooDR;
var _dbg = jsGooDbg;
var _date = jsgDate;
var _format = jsgFormat;
var _dom = jsgDom;