// 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 ); }
    },
    
    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);
    },
    
    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;
    },

    //html related - here so that Minijax has just this as dependancy 
    isInputField: function ( e ) {
        return jsGooC.has(e.tagName.toLowerCase(), ['input', 'textarea', 'select', ]);
    },
	
	trim: function(t){return t.replace(/^\s+|\s+$/g,'');}
};

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) { 
	Minijax.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) { 
	Minijax.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 (id, data) {
	return jsGooC.seek(function(x, i){ return x.id==id?x: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("{"+p+"}", 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; }
};

//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("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 _ = 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;
	for (o in os) {
	    var isarr = o instanceof Array;
	    r += _.wrap(isarr?o[1]:o, "option"+(isarr?" value='"+o[0]+"'":''))
		}
	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 ) {
	return jsGooC.doeach(function (e,i) {
		if (e.className=='vnote' && d[e.title]) {
		    e.style['display'] = 'block';
		    _.into(e, d[e.title]);
		}
	    }, f.getElementsByTagName('span'));
    }
};

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 Minijax = {
    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:'Minijax 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);
	},
	
	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;
		}
		while(el || limit > 0) {
			el = el[m];
			if (el.nodeName.toLowerCase() == name) return el;
			limit -= 1;
		} return null;
	}
};


//utils-shortcuts -- remove if you need to 
    function $(id) { return document.getElementById(id); }
var _c = jsGooC;
var _ = jsGoo;
var _f = jsGooF;
var _a = Minijax;
var _dr = jsGooDR;
var _dbg = jsGooDbg;
var _dom = jsgDom;