
/**
 * Core JavaScript functions
 *
 * @todo Wrap all functions inside the eon namespace.
 * @todo Split up this file? kernel, validate, media, ajax, etc.
 * @todo Functions to get/reach all fields. (e.g. url field of item 4)
 */


// The eonBIT namespace
var eon = { };

// Define this if Prototype has not done it already. Move to eon namespace?
// Note: prototypes version is not equal to this. So maybe we shall drop it!
if (typeof($) == 'undefined') {
	var $ = document.getElementById;
    //function $(id) { return document.getElementById(id); }
}

var validate_patterns = new Object();
var hotkey_active = false; // see hotkeys.js


/**
 * Write to the log
 *
 * @note	For some wierd reason errors inside this function is *not*
 *			reported by firebug. Use Firefox's error console.
 *
 * @param	Dump all parameters to the log
 */
if (window.console) {
	eon.log = function () { 
		// this does not work in some forms (f.ex. email_admin.user_new) on my setup (sms). see r4069
		//console.log.apply(this, arguments); 
		console.log(arguments);
	}
} else {
	eon.log = function () {
		var e = $('_eon_js_log');
		if (!e) return; // fixme: need to buffer until onload event
		
		var a = []; // arguments is not a real array, so we cant use join
		// use prototypes $A() ?
		for (var i=0; i<arguments.length; i++) {
			a.push(arguments[i]);
		}
		//e.innerHTML += '<br/>' + message;
		e.appendChild( document.createTextNode(a.join(', ')) );
		e.appendChild( document.createElement('br') ); // todo: create <br/>
	}
}


/**
 * Trigger an error by casting an exception of type Error()
 *
 * todo: add eon.assert() ?
 */
eon.error = function (message)
{
	// todo: use console.error
	eon.log('<b>ERROR: ' + message + '</b>');
	var e = new Error(message);
	if (e.stack) {
		// todo: pritty print stack trace
		//eon.log(e.stack);
		// or use firebugs console.trace()
	}
	throw e;
}

// ----------------------------------------
// SITES
// @todo: use separate files
// ----------------------------------------
eon.site = {}

// ----------------------------------------
// FORM
// ----------------------------------------

eon.form = {

	AJAX_POST: null, // set by action onclick (back, refresh, stay, <url>)
	AJAX_POST_CB: null, // callback function (optional)

	serialize: function(form) {
		// form
		if (!form) {
			return '';
		}
		if (form.elements) {
			// Check for FCKeditor fields, and put it's value into the
			// field where we expect to find the value, or ajaxedit won't work.
/* Disabled for now. More work is needed for ajaxedit on fckfields to work smoothly.
			var elem, e;
			var len = form.length;
			for (var i=0; i<len; ++i) {
				elem = form.elements[i];
				e = elem.nextElementSibling;
				if (e && e.id == elem.id+'___Config')
					elem.value = FCKeditorAPI.GetInstance(elem.id).GetHTML(false);
			}
*/
			return Form.serialize(form); // @todo: change to jquery
		// or array of inputs
		} else {
			return Form.serializeElements(form);
		}
	},

	onsubmit_filter: function(form) {
		form.method = 'get';
		form.submit();
	},

	onsubmit: function(form) {

		if (!eon.form.AJAX_POST || eon.form.AJAX_POST == 'none')
			return true;

		// Loading indicator
		var d0;
		d0 = document.getElementById('saving_outer');
		if (d0) {
			d0.innerHTML = '';
		} else {
 			d0 = document.createElement('div');
			d0.id = 'saving_outer';
			form.insertBefore(d0, form.firstChild);
		}
		var d1 = document.createElement('div');
		d1.innerHTML = 'Saving <img src="/kernel/images/loading.gif"/>'; // @todo: translate
		d1.id = 'saving_inner';
		d0.appendChild(d1);

		// Callback
		var callback = function(result, response, form) {
			// Get outer and inner div, and allow closing by clicking it
			var elem = $('saving_outer');
			var elem2 = $('saving_inner');
			elem.onclick = function() { 
				var e = $('saving_outer'); 
				e.parentNode.removeChild(e); 
			}
			// On success, do after-action (AJAX_POST)
			if (result['type'] == 'ok') {
				// If action itself returns a redirect, it takes precedence
				if (result['redirect']) {
					document.location.href = unescape(result['redirect']);
					return;
				}
				// Or do what the view that called the action tells us to do
				switch (eon.form.AJAX_POST) {
					// Stay - show message in loading-div
					case 'stay':
						elem2.className = 'x_ok';
						elem2.innerHTML = result['message'];
						var func = function() {
							var e = $('saving_outer');
							if (e)
								e.parentNode.removeChild(e);
						};		
						setTimeout(func, 6000);
						if (eon.form.AJAX_POST_CB) {
							eon.form.AJAX_POST_CB(result);
						}
						break;
					// Refresh/back
					case 'refresh':
						location.reload(true);
						break;
					case 'back':
						history.go(-1);
						break;
					// Or redirect to explicit url
					default:
						window.location = eon.form.AJAX_POST;
				}
			// On error, stay and show error message in loading-div
			} else {
				elem2.className = 'x_error';
				elem2.innerHTML = result['message'];
			}
		};

		// Run RPC
		eon.rpc.call(form.action, callback, form);
		return false;
	},

	set_required_onchange: function(elem, field, conf) {
		if (!elem.value)
			return;
		var prefix = elem.id.replace('X'+field, 'X');
		this._set_required_fields_off(elem.form);
		this._set_required_fields_on(prefix, conf[elem.value]);
		this._set_required_fields_on(prefix, conf['__always']);
	},
	_set_required_fields_off: function(form) {
		for (var i=0; i < form.elements.length; i++) {
			var input = form.elements[i];
			if (!input.id || !input.id.match(/^_muxX/)) continue;
			this._set_required_field(input, false);
		}
	},
	_set_required_fields_on: function(prefix, fields) {
		if (!fields)
			return;
		for(var i=0; i < fields.length; i++) {
			var input = document.getElementById(prefix + fields[i]);
			if (!input) continue;
			this._set_required_field(input, true);
		}
	},
	_set_required_field: function(input, is_required) {
		var div = input.parentNode.parentNode;
		if (is_required)
			add_class(div, 'x_required');
		else
			remove_class(div, 'x_required');
		for(var j=0; j < div.childNodes.length; j++) {
			var cn = div.childNodes[j];
			if (cn.className != 't_label') continue;
			var gotreq = false;
			for(var k=0; k < cn.childNodes.length; k++) {
				if (cn.childNodes[k].className == 't_reqmark')
					gotreq = cn.childNodes[k];
			}
			if (is_required) {
				if (!gotreq)
					cn.innerHTML += '<span class="t_reqmark">*</span>';
			} else {
				if (gotreq)
					cn.removeChild(gotreq);
			}
		}
	}

}

// ----------------------------------------
// AJAX
// ----------------------------------------

eon.ajax = {

	request: function(url, options) {
		new Ajax.Request(url, options); // @todo: change to jquery
	},

	post: function(url, data, options) {
		if (!options)
			options = {}
		if (data)
			options.postBody = Hash.toQueryString(data);
		return this.request(url, options);
	}

}

// ----------------------------------------
// RPC
// ----------------------------------------

eon.json = {

	REDIR_TOKEN: '####',

	decode: function(txt) {
		if (!txt)
			return null;
		return eval('(' + txt + ')');
	}
}

eon.rpc = {

	makeurl: function(url) {
		if (url.indexOf('?') > -1)
			url += '&';
		else
			url += '?';
		url += 'ajax=action';
		return url;
	},

	callback: function(response, action, form) {
		var res;
		if (this.is_action_function(action)) {
			res = this.check_response(response, true);
			if (res != self.REDIR_TOKEN)
				action(res, response, form);
		} else {
			res = this.check_response(response)
			if (res && res != self.REDIR_TOKEN) {
				if (action == 'repost') {
					form.submit();
				} else if (action == 'refresh') {
					document.refresh();
				} else if (action == 'back') {
					history.back();
				}
			}
		}
	},


	call: function(url, real_callback, form, prompt_def) {
		url = this.makeurl(url);
		var opt = { 
			method: 'post',
			onComplete: function(res) {
				eon.rpc.callback(res, real_callback, form);
			},
			onError: function(res) {
				eon.rpc.callback(res, real_callback, form);
			}
		};
		if (prompt_def) {
			var def = (prompt_def['default']) ? prompt_def['default'] : '';
			var val = prompt(prompt_def['message'], def);
			if (!val)
				return;
			var inp = document.createElement('input');
			inp.type = 'hidden';
			inp.name = prompt_def['field'];
			inp.value = val;
			submit_form.appendChild(inp);
		}
        if (form)
			opt.postBody = eon.form.serialize(form);
		eon.ajax.request(url, opt);
	},

	is_action_function: function(action) {
		// @todo: return true if type(action) == js function
		if (action == 'repost' || action == 'refresh' || action == 'back' || !action)
			return false;
		else
			return true;
	},

	parse_response: function(response) {
		return eon.rpc.check_response(response, true);
	},
	check_response: function(response, return_raw) {
		if (!response) {
			eon.log('RPC error: got no response object');
			return false;
		}
		var res = response.responseText;
		if (!res) {
			eon.log('RPC error: got no response.responseText');
			return false;
		}
		res = eon.json.decode(res);
		if (res['type'] == 'redirect') {
			document.location.href = res['url'];
			return self.REDIR_TOKEN;
		}
		if (return_raw)
			return res;
		switch (res['type']) {
			case 'ok':
				return res;
			case 'error':
			case 'exception':
				eon.rpc.display_error(res);
				return false;
			default:
				eon.log('RPC error: unknown response type: ' + res['type']);
				return false;
		}
	},

	display_error: function(res) {
		alert(res['message']);
	},

	XXX_check_response: function(response) {
		var separator = ';';
		if (!response.responseText || response.responseText == '') {
			eon.log('Got empty RPC response: ' + response.responseText);
			return false;
		}
		var result = response.responseText.split(separator);
		if (result.length < 2 || result[0].length > 2) {
			eon.log('Invalid RPC response: ' + response.responseText);
			return false;
		}
		var data = {};
		data.status = parseInt(result[0]);
		var msg_start = null;
		if (data.status < 0) {
			eon.log('RPC error: ' + response.responseText);
			data.exception = result[1];
			msg_start = 3;
		} else {
			msg_start = 1;
		}
		data.message = '';
		for(var i=msg_start; i<result.length; i++) {
			if (i > msg_start)
				data.message += separator;
			data.message += result[i];
		}
		return data;
	}

}

function ajax_rpc(url, cb_action, submit_form, prompt_def) { return eon.rpc.call(url, cb_action, submit_form, prompt_def); }
function ajax_rpc_cb(response, action, form) { return eon.rpc.call(url, cb_action, submit_form, prompt_def); }

eon.shop = {
	checkout_change_init: function(cel) {
		var form = $('mainform');
		for(var i=0; i < form.elements.length; i++) {
			if (form.elements[i].id && form.elements[i].id.match('Xcustomer_id')) {
				this.checkout_change_customer(form.elements[i]);
				return;
			}
		}
	},
	checkout_change_customer: function(cuel) {
		this.checkout_change_customer_or_contact(cuel, true);
	},
	checkout_change_contact: function(coel) {
		this.checkout_change_customer_or_contact(coel, false);
	},
	checkout_change_customer_or_contact: function(coel, is_cust) {
		var url = '/U/order/checkout_poll/checkout_poll?';
		var suf = '';
		if (is_cust) {
			url += 'cuid=';
			suf = 'customer_id';
		} else {
			url += 'coid=';
			suf = 'cust_contact';
		}
		url += coel.value;
		var prefix = coel.id.replace('X' + suf, 'X');
		eon.rpc.call(url, function(res) {
			var data = res.data;
			var keys = [];
			if (is_cust) {
				for(var key in data) {
					var val = data[key];
					if (typeof(val) == 'function') continue;
					keys[keys.length] = key;
				}
			} else {
				keys = ['phone','mobile','car_reg_no','car_km'];
			}
			for(var i=0; i < keys.length; i++) {
				var key = keys[i];
				var val = data[key];
				if (val == null) val = '';
				var id = prefix + 'cust_' + key;
				var inp = $(id);
				if (!inp)
					continue;
				inp.value = val;
			}
			var el_chk = document.getElementById(prefix + 'upd_cust');
			if (cuel.value)
				el_chk.disabled = false;
			else
				el_chk.disabled = true;
		});
	}
/*
	// deprecated by customer
	checkout_change_company: function(cel) {
		var uel = $(cel.id.replace('Xcust_company', 'Xcust_user'));
		this.checkout_poll_info(cel, uel);
	},
	checkout_change_user: function(uel) {
		var cel = $(uel.id.replace('Xcust_user', 'Xcust_company'));
		this.checkout_poll_info(cel, uel);
	},
	checkout_poll_info: function(ce, ue) {
		var url = '/U/order/checkout_poll/checkout_poll?cid=' + ce.value + '&uid=' + ue.value;
		var prefix = ce.id.replace('Xcust_company', 'X');
		eon.rpc.call(url, function(res) {
			// hack.. @todo: rpc for fetching data. json? and remove 4d-stuff. make generic function for filling fields from select.
			var tmp = res.message.split(':#:');
			var keys = ['email', 'name','address','address2','zipcode','city','state','country','company_reg_no','phone','mobile','car_reg_no','car_model','car_km','ref'];
			var data = {};
			for(var i=0; i < tmp.length; i++) {
				var key = keys[i];
				var val = tmp[i];
				var id = prefix + 'cust_' + key;
				var inp = $(id);
				if (!inp)
					continue;
				inp.value = val;
			}	
		});
	}
*/
}

// ----------------------------------------
// DATATYPES
// ----------------------------------------

eon.datatype = {};

eon.datatype.select = {

	new_popup_elem: null,

	new_popup: function(url, elid, a, mod, view, id, field) {
		eon.datatype.select._new = {
			inid: elid,
			input: document.getElementById(elid),
			module: mod,
			view: view,
			id: id,
			field: field
		};
//console.log(eon.datatype.select._new);
//console.log(url);
		ajax_popup(a, url);
	},
	new_save_cb: function(res) {
		if (!res['result'])
			return;
		var res_id = res['_id'];
		var url = eon.datatype.select.make_url(eon.datatype.select._new);
		var input = eon.datatype.select._new.input;
		var el = input.parentNode;
		el.innerHTML = 'Loading...';
		ajax_popup_close();
        new Ajax.Updater(el, url, {
            onComplete: function() {
                //Ajaxedit.init_input($('ae'+e.__ae.id));
				var input = document.getElementById(eon.datatype.select._new.inid);
				for (var i=0; i < input.options.length; i++) {
					if (input.options[i].value == res_id) {
						input.selectedIndex = i;
						break;
					}
				}
			//	console.log('done: ' + res_id);
            }
		});
	},

	make_url: function(def) {
		return '/' + def['id'] + '/' + def['module'] + '/' + def['view'] + '/null/root/ajaxedit=1|ae_field=' + def['field'];
	}

}

eon.datatype.wiki = {

	interval_update: 2500,
	interval_check: 500,
	previews: {},

	preview_add: function(textarea_id, preview_id, url) {
		this.previews[textarea_id] = {
			'textarea': $(textarea_id), 
			'preview': $(preview_id), 
			'url': url, 
			'changed': 0,
			'updated': 0
		};
	},
	preview_init: function() {
		setInterval(this.preview_update_check.bind(this), this.interval_check);
	},
	preview_onkeyup: function(elem) {
		this.previews[elem.id]['changed'] = (new Date()).getTime();
	},

	preview_update_check: function() {
		for(var p in this.previews) {
			var preview = this.previews[p];
			var diff = preview['changed']-preview['updated'];
			if (diff < 1)
				continue;
			diff = (new Date()).getTime()-preview['updated'];
			if (diff < this.interval_update)
				continue;
			this.preview_update(preview);
		}
	},

	preview_update: function(def) {
		var data = {'data': def['textarea'].value};
		var options = {
			onSuccess: function(res) { 
				def['preview'].innerHTML = res.responseText;
			}
		};
		def['updated'] = (new Date()).getTime();
		eon.ajax.post(def['url'], data, options);
	}
}

eon.datatype.tyre_dimension = {
	validate: function(el) {
		var p = /^ *([0-9][0-9][0-9]?)[\/\- \.]?([0-9][0-9])([\/\-\.R ])?([0-9]+) *$/;
		var v = el.value;
		var ex = new Object(); // tuba
		if (!v.match(p)) {
			ex.message = 'Invalid dimension';
			throw ex; // tuba
		}
		var data = {};
		var keys = ['w', 'h', 'r', 'd'];
		for(var i=0; i < keys.length; i++) {
			data[keys[i]] = v.replace(p, '$'+(i+1));
		}
		// @todo: check/tune values
		if (data['w'] < 100 || data['w'] > 400)
			throw ex;
		if (data['h'] < 10 || data['h'] > 99)
			throw ex;
		if (data['d'] < 5 || data['d'] > 50)
			throw ex;
		if (!data['r'] || data['r'].match(/[\/\-\. ]/))
			data['r'] = '-';
		return data['w'] + '/' + data['h'] + data['r'] + data['d'];
	}
}


// === REPORT ===

eon.report = {};

eon.report.filter = {

	num: -1,

	init: function() {
		Sortable.create('dfmain', {
			//onUpdate: function(a) { 
			//	eon.report.field.onupdate(); 
			//}
		});
		//eon.report.field.onupdate();
	},

	add: function() {
		var orig = $('df_N0'); // tuba
		var clone = orig.cloneNode(true);
		var num = clone.innerHTML.replace(/.* name="field([0123456789]+)".*/, '$1');
		if (eon.report.filter.num == -1)
			eon.report.filter.num = parseInt(num);
		eon.report.filter.num += 1;
		var keys = ['field','operator','value','input'];
		for(var i=0; i < keys.length; i++) {
			var pat = new RegExp('name="' + keys[i] + '([0123456789]+)"');
			var rep = 'name="' + keys[i] + (eon.report.filter.num) + '"';
			clone.innerHTML = clone.innerHTML.replace(pat, rep);
			//clone.innerHTML = clone.innerHTML.replace(/name="field([0123456789]+)"/, 'name="fieldBOLLE"');
		}
		clone.style.display = 'block';
		orig.parentNode.appendChild(clone);
	},

	del: function(elem) {
		var li = elem.parentNode.parentNode;
		li.parentNode.removeChild(li);
	}

/*
	save: function() {
		var newrow = $('df_N0');
		newrow.parentNode.removeChild(newrow);
		$('df_form').submit();
	}
*/
};

eon.report.field = {

	data: [],

	init: function() {
		Sortable.create('dfmain', {
			onUpdate: function(a) { 
				eon.report.field.onupdate(); 
			}
		});
		eon.report.field.onupdate();
	},

	toggleclean: function(elem) {
		var li = elem.parentNode.parentNode;
		var efmt = document.getElementById(li.id + '_value_fmt');
		var ecln = document.getElementById(li.id + '_value_clean');
		if (!efmt) {
				eon.log('missing ' + li.id + '_value_fmt');
				return;
		}
		if (!ecln) {
				eon.log('missing ' + li.id + '_value_clean');
				return;
		}
		if (ecln.style.display == 'none') {
				ecln.style.display = 'block';
				efmt.style.display = 'none';
				elem.innerHTML = eonvar.dfcleanlabels['c'];
		} else {
				ecln.style.display = 'none';
				efmt.style.display = 'block';
				elem.innerHTML = eonvar.dfcleanlabels['f'];
		}
		eon.report.field.onupdate();
	},

	del: function(elem) {
		var li = elem.parentNode.parentNode;
		var par = li.parentNode;
		li.parentNode.removeChild(li);
		var isdel = true;
		if (par.id == 'dfinactive') {
				par = 'dfmain';
				isdel = false;
		} else {
				par = 'dfinactive';
		}
		$(par).appendChild(li);
		var so = $(li.id + '_sort');
		so.innerHTML = '&nbsp;';
		var de = $(li.id + '_del');
		if (!isdel) {
				de.innerHTML = 'X';
				de.className = 'rs_a_del';
		} else {
				de.innerHTML = '+';
				de.className = 'rs_a_add';
		}
		eon.report.field.init();
		//eon.report.field.onupdate();
	},

	onupdate: function() {
		var elem = $('dfmain');
		eon.report.field.data = [];
		for(var i=0; i < elem.childNodes.length; i++) {
				var c = elem.childNodes[i];
				var s = document.getElementById(c.id + '_sort');
				if (eonvar.dfnums) {
						s.innerHTML = eonvar.dfnums[i];
				} else {
						s.innerHTML = (i+1);
				}
				var item = {'clean': 0, 'field': c.id.replace(/^df_/, '')};
				var clean = document.getElementById(c.id + '_value_clean');
				if (clean.style.display == 'block')
						item['clean'] = 1;
				eon.report.field.data[eon.report.field.data.length] = item;
		}
	},

	save: function() {
		var form = $('df_form');
		var keys = ['field','clean','defval'];
		for(var i=0; i < eon.report.field.data.length; i++) {
				var item = eon.report.field.data[i];
				for(var k=0; k < keys.length; k++) {
						var inp = document.createElement('input');
						inp.type = 'hidden';
						inp.name = keys[k] + (i+1);
						if (keys[k] == 'defval') {
								var el = $('df_'+item['field']+'_defval');
								var val = el.value;
								if (!val)
										val = '';
								inp.value = val;
						} else {
								inp.value = item[keys[k]];
						}
						form.appendChild(inp);
				}
		}
		form.submit();
	}
}



/* TODO: delete
function test_selection() {
	var selection = get_selection(1);
	debug_array(selection, 'You have selected:');
} */

// used by media_selected()
function get_selection(depth) {
	var mux_prefix = '_mux'; // todo: use MUX_PREFIX & MUX_SEPARATOR from PHP config
	var mux_separator = 'X';
	if (!depth) depth = 1;
	var inputs = document.getElementsByTagName('input');
	var selection = [];
	for (var i=0; i < inputs.length; i++) {
		if ( inputs[i].type != 'checkbox' || ! inputs[i].className.match(/(^| )selector( |$)/) )
			continue;
		if (!inputs[i].checked)
			continue;
		var field_parts = inputs[i].id.split(mux_separator);
		if (field_parts[0] != mux_prefix)
			continue;
		for (var j = 0; j < depth; j=j+2) {
			var id = field_parts[j+1];
			selection[selection.length] = id;
		}
	}
	return selection;
}

/* todo: delete or use together with eon.log() and no firebug
function debug_array(array, pretext) {
	if (pretext) str = pretext+' ';
	else str = '';
	var started = false;
	for (var i = 0; i < array.length; i++) {
		if (started) str += ', ';
		else started = true;
		str += array[i];
	}
	alert(str);
}


/*
	Functions to manipulate dropdown lists
*/
var select_hold = new Array();
function select_restrict(obj,sid){
	var s=document.getElementById(sid);
	var v=obj.value;
	var sv=s.options[s.selectedIndex].value;
	select_rem_all(s);
	select_show_grp(s,v);
	s.selectedIndex=select_set_selected(s,sv);
}
function select_set_selected(s,sv){
	var ix=0;
	for(var i=s.options.length-1;i>=1;i--){ // if(s.options[i].value != ''){
		if(s.options[i].value==sv)
			ix=i;
	}
	return ix;
}
function select_rem_all(s){
	for(var i=s.options.length-1;i>=1;i--){ // if(s.options[i].value != ''){
			select_hold[i] = new Array();
			select_hold[i]['grp'] = s.options[i].getAttribute('grp');
			select_hold[i]['value'] = s.options[i].value;
			select_hold[i]['text'] = s.options[i].innerHTML;
			s.remove(i); // }
	}
}
function select_show_grp(s,v){
	for(var i=select_hold.length-1;i>=1;i--){
		if(select_hold[i]['grp']==v ){
			select_add_opt(s,select_hold[i]);
		}
	}
}
function select_add_opt(s,h){
	var opt = document.createElement("OPTION");
	opt.text = h.text;
	opt.value = h.value;
	opt.setAttribute('grp',h.grp);
	s.options.add(opt);
}

/*
 * Popup window
 */
function popup_window(url,name,params){
	var popwin=window.open(url,name,params);
	if(window.focus){popwin.focus()}
	return false;
}

function popup_image(id) {
	var view = (eonvar['image_popup_view']) ? eonvar['image_popup_view'] : 'popup';
	var url = '/' + id + '/mod_image/' + view;
	popup_window(url, 'image_popup', 'height=300,width=300,resizable=1');
}


/***********************
 * Media handling code
 */

/**
 * Get media and insert into page
 *
 * @param media		media type
 * @param field		where to insert (DOM id of node)
 */
function media_get(media, field, id, multi, hide_name)
{
	if (!id)
		id = 0; // what is this for?

	var value = $(field).value;
	var url = '/'+id+'/mod_'+media+'/ajax/ajax';

	new Ajax.Request(url, {
		parameters: 'value='+value+'&field='+field+'&multi='+multi+'&hide_name='+hide_name,
		_field: field,
		onSuccess: function(res) { media_show(res, multi); },
		onFailure: media_error,
		onException: media_exception
	});
}

var media_show = function(t, multi) {
	var i = t.responseText.indexOf("\n");
	var field = t.responseText.substr(0, i);
	var content = t.responseText.substr(i+1);

	i = content.indexOf("\n");
	var data = content.substr(0, i);
	content = content.substr(i+1);
	$(field).value = data;

	$('_media_'+field).innerHTML = content;
	if (content.indexOf('Empty list') != -1)
		return;
	if (multi && multi!=0)
		media_make_sortable(field);
}

var media_exception = function(request, message) {
	// tmp disabled 'cause media_make_sortable() triggers error when
	// list is empty
	//$('_media_'+request.options._field).innerHTML = 'ERROR: ' + message;
}
var media_error = function(t) {
	eon.log(t.responseText);
	alert('fixme. media_error(). status: ' + t.status); // + "\n\n" + t.responseText);
}

function media_selected(media,id){
	if( ! id ){
		id = get_selection(1);
	}
	var parent_field = window.opener.document.getElementById(media_field);
	if( parent_field.value.length > 0 && media_multi)
		parent_field.value += ','+id;
	else
		parent_field.value = id;
	window.opener.media_get(media,media_field,media_pid,media_multi);
	if(window.opener.focus){window.opener.focus()}

	setTimeout('self.close()',500);
}
function media_remove(media,item_field,item_id,media_id,multi){
	var value = $(item_field).value;
	var regex = /,/;
	var a = new Array();
	if( regex.test(value) ){
		a = value.split(regex);
	}else{
		a[0] = value;
	}
	var b = new Array();
	for( var i=0 ; i < a.length ; i++ ){
		if( a[i] != media_id )
			b.push( a[i] );
	}
	$(item_field).value = b.join(',');
	media_get(media,item_field,item_id,multi);
}
function media_make_sortable(field){
	Sortable.create('_ul_'+field,{onUpdate:function(){
				media_sort_field(field)
			}
		}
	);
}

function media_sort_field(field){
	var ids = new Array();
	var list = document.getElementById('_ul_'+field);
	for (var i=0; i<list.childNodes.length; i++) {
		var node = list.childNodes[i];
		if (node.nodeName=="LI") {
			ids.push(node.id.replace(field.slice(1)+'_',''));
		}
	}
	$(field).value = ids.join(',');
	new Effect.Highlight('_ul_'+field,{});
}

/**
 * Insert link to file into FCK
 *
 * @param obj	Select button object
 *
 * We get the id-attribute of the current row (r_123), and parse out the
 * item id for the file. Then we get the file title/name by looking for
 * a css-class of f_name. THIS IS NOT TRUE ANYMORE, IE BUG
 *
 * @todo if active selection in fck, use that as link text
 */
function fck_add_file(obj)
{
	var id = obj.parentNode.parentNode.getAttribute('id').substring(2);
	//var filetype = document.getElementsByName('_muxX'+id+'Xfiletype').item(0).value;

	/* Use current selection as link text.
	   MAKE CODE MORE ROBUST BEFORE WE ACTIVE IT
	var sel = window.opener.FCK.EditorWindow.getSelection();
	if (!sel.isCollapsed) {
		var node = sel.anchorNode;
		var start = sel.anchorOffset;
		var stop = sel.focusOffset;
		var text_f = node.nodeValue.substring(0, start);
		var text_m = node.nodeValue.substring(start, stop);
		var text_e = node.nodeValue.substring(stop);
		
		var span = window.opener.FCK.EditorWindow.document.createElement('span');
		var a = window.opener.FCK.EditorWindow.document.createElement('a');
		a.setAttribute('href', '/'+id+'/mod_file/item/open');
		a.appendChild( fck_text_node(text_m) );
		span.appendChild( fck_text_node(text_f) );
		span.appendChild(a);
		span.appendChild( fck_text_node(text_e) );
	
		node.parentNode.replaceChild(span, node);
		self.close();
		return;
	}
	*/

	// Get item name. Another aproach would be to include a hidden element
	// with the same information, and get it by id: '_muxX'+id+'Xname'
	/* IE FUCK
	var list = obj.parentNode.parentNode.childNodes;
	for (var i=0; i<list.length; i++) {
		if (!list[i].getAttribute('class'))
			continue;
		if (list[i].getAttribute('class').indexOf('f_name') != -1)
			break;
	}
	var name = list[i].firstChild.firstChild.nodeValue;
	*/

	var name = document.getElementsByName('_muxX'+id+'Xname2').item(0).value;

	fck_add_file_2(id, name);
}

function fck_add_file_2(id, name)
{
	var a = window.opener.FCK.CreateElement('a');
	a.setAttribute('href', '/'+id+'/file/item/open');
	a.appendChild( fck_text_node(name) );
	self.close();
}

/**
 * Insert image into FCK
 */
function fck_add_img(id, filetype, make_popup)
{
	// pass id back to opener
	//tmp disabled: var e = window.opener.parent.$( _fck_get_mux_prefix() + 'relimg_new' );
	//tmp hack to force user to save item before adding images
	var mux = _fck_get_mux_prefix();
	if (mux == '_muxXN0X') {
		alert('Sorry! You can not insert images into a new item.\nYou need to save it first.\nThis will be fixed in a future version.');
		self.close();
		return;
	}
	var e = window.opener.parent.$(mux + 'relimg_new');
	if (e) {
		e.value = (!e.value) ? id : e.value + ',' + id;
	} else {
		eon.error("FIXME: You need to add a field with name 'relimg' and datatype 'rel_image' to the edit view.\nThis will be fixed in a future version!");
	}

	var img = window.opener.FCK.CreateElement('img');
	img.setAttribute('src', '/images/content/'+id+'/normal.'+filetype);

	if (make_popup) { // this seems to work (sms)
		var a = window.opener.FCK.CreateElement('a');
		a.setAttribute('href', 'javascript:popup_image(' + id + ')');
		a.appendChild(img);
	}

	self.close();
}

// create DOM text node for fck
function fck_text_node(text) {
	return window.opener.FCK.EditorDocument.createTextNode(text);
}

// hack to get mux prefix. need this to send data from selector window
// opened from fck, back to parent window
function _fck_get_mux_prefix()
{
	var match;
	var muxprefix;
	if ((match = /InstanceName=(_[^&]+)&/.exec(window.opener.location.search)))
		muxprefix = /_muxX[\dN]+X/.exec(match[1]);
	if (muxprefix)
		return muxprefix;
	
	eon.error('Fatal error: Can not find MUX prefix');
}



//// VALIDATION - START ////


/**
 * Validate a form input 
 */
function validate_input(input) {

	// only validate these input.types: text, select-one
	// is there any point in validating checkboxes (and radiobuttons)? (tbl)
	// todo: create static array: chk_types = ['text', 'select-one'];
	//         if (chk_types.indexOf(input.type) == -1) return;
	//if (! (input.type=='text' || input.type=='select-one'))
	//	return;

	// Get fieldname, datatype etc. from form input class/id
	var obj = _validate_parse_input(input);

	if ( !obj )
		return null;

	// Ignore inputs without a validate_<handler> class (buttons etc.)
	if ( ! obj.validate )
		return null;

	// Ignore view fields
	if ( obj.datatype == 'view' )
		return null;

	// Ignore inputs that are in new row when new checkbox is not checked
	if ( obj.new_row == 2 )
		return null;

	// Ignore hidden, if configured
	if (eonvar.no_validate_hidden && obj.element.style.display == 'none') {
		input.disabled = true; // prevent post
		return null;
	}

	// Validate
	var res;

	var msg;
	if ( obj.value == '' ) {
		if ( obj.required ) {
			msg = "Required input empty: " + obj.field;
			res = false;
			// tmp workaround. let backend handle required checkbox validation.
			if ( obj.datatype == 'checkbox' ) {
				msg = '';
				res = true;
			}
		} else {
			res = true;
		}
	} else {
		// Custom JS validation handlers
		if (eon && eon.datatype && obj && obj.datatype && eon.datatype[obj.datatype] && eon.datatype[obj.datatype]['validate']) {
			try {
				input.value = eon.datatype[obj.datatype]['validate'](obj);
				res = true;
			} catch (e) {
				msg = e.message;
				res = false;
			}
		} else {
			// Unhandled, let backend do it
			if ( obj.validate != 'pattern' && obj.validate != 'datetime' ) {
				// TODO: Custom validate handlers. Assume true for now and let PHP handle it. (sms)
				res = true;
			// Pattern
			} else {
				// TMP HACK while waiting for date overhaul
				if (obj.validate == 'datetime') {
					_validate_add_pattern('datetime', '/^[0-9]{1,2}\.[0-9]{1,2}\.[0-9]{4}( [0-9]{2}:[0-9]{2}(:[0-9]{2})?)?$/');
					_validate_add_pattern('date', '/^[0-9]{1,2}\.[0-9]{1,2}\.[0-9]{4}$/');
				}
				if ( validate_pattern(obj) ) {
					res = true;
				} else {
					msg = "Invalid input for field " + obj.field + ": " + obj.value + " (datatype: " + obj.datatype + " pattern: " + validate_patterns[obj.datatype] + ")";
					res = false;
				}
			}
		}
	}
	if (msg) eon.log(msg);

	// Update CSS class
	_validate_update_class(obj.element, res);
	return res;
}

/**
 * Validation handler: pattern
 */
function validate_pattern(obj) {
	if ( !validate_patterns[obj.datatype] ) {
		eon.log('No pattern for datatype ' + obj.datatype + ' - ignoring & assuming valid');
		return true;
	}
//eon.log('fi: '+obj.field+' dt: '+obj.datatype+' pat: '+validate_patterns[obj.datatype]); // TMP
	return obj.value.match( validate_patterns[obj.datatype] );
}

/**
 * Set class to validate_ok or validate_error after an input has been changed and attempted validated
 */
function _validate_update_class(element, result) {
	// nb: change_class will add the second class even though the first is not found
        if ( result )
                change_class(element, 'x_formerror', 'x_validated');
        else
                change_class(element, 'x_validated', 'x_formerror');
/*
	if ( $('label' + input.id) ) {
		if ( result )
			change_class( $('label' + input.id), 'error', 'ok' );
		else
			change_class( $('label' + input.id), 'ok', 'error' );
	}
*/
}

/**
 * Parses a form input's id and class fields and returns:
 *
 *  result.field        string  fieldname
 *  result.datatype	string	the datatype of the field
 *  result.required	boolean	if field is required
 *  result.value	mixed	value of the input
 *  result.validate	string	validation handler
 *  result.new_row	int	is this input in new_row? (0: no, 1: yes, and checkbox is checked, 2: yes, but checkbox is not checked)
 *
 */
function _validate_parse_input(input) {

	if ( input.id.indexOf('_mux') != 0 )
		return;
	if ( input.type == 'button' )
		return;

	var element = get_ancestor(input, 't_element');

	if (!element)
		return;

	var result = new Object();
	// Datatype, validation handler & required
	result.required = false;
	var classes = element.className.split(" ");
	for ( var i = 0; i < classes.length; i++ ) {
		if ( classes[i].match(/^d_.+$/) ) {
			result.datatype = classes[i].replace(/^d_/, '');
		} else if ( classes[i].match(/^validate_.+$/) ) {
			result.validate = classes[i].replace(/^validate_/, '');
		} else if ( classes[i] == 'x_required' ) {
			result.required = true;
		}
	}

	// Fieldname
	result.field = input.id.replace(/^.+X/, ''); // todo: use PHP constant MUX_SEPARATOR instead of X (sms)

	// In new row?
	// - TODO: use MUX constants from PHP
	result.new_row = 0;
	var pat = new RegExp('N(\\d+)X' + result.field + '$');
	if ( input.id.match(pat) ) {
		var prefix = input.id.replace( new RegExp('X' + result.field + '$'), '');
		var checkbox = $( prefix + 'X_new' );
		if ( !checkbox || checkbox.type == 'hidden' || checkbox.checked )
			result.new_row = 1;
		else
			result.new_row = 2;
	}

	// Value
	if ( input.type == 'checkbox' ) {
		if ( input.checked )
			result.value = true;
		else
			result.value = false;
	} else {
		result.value = input.value;
	}

	result.element = element;
	return result;
}

/**
 * Add to list of validation patterns
 */
function _validate_add_pattern(datatype, pattern) {
	pattern = pattern.replace(/^\//,'').replace(/\/$/,'');
	validate_patterns[datatype] = new RegExp(pattern);
}

/**
 * Validate all inputs in form before submit
 */
function _validate_form(form) {
	var result = true;
	for ( var i = 0; i < form.elements.length; i++ ) {
		if ( validate_input(form.elements[i]) == false ) {
			result = false;
		}
	}
	if ( result )
		return true;
	alert(eonvar.text.form_error);
	return false;
}


//// VALIDATION - END ////


/**
 * Remove one of possibly many css classes for an element
 */
function remove_class(element, class_name) {
	var old_classes = get_classes(element);
	var new_classes = '';
	var started = false;
	for( var i = 0; i < old_classes.length; i++ ) {
		if ( old_classes[i] == class_name )
			continue;
		var n = new_classes.length;
		if ( started )
			new_classes += ' ';
		else
			started = true;
		new_classes += old_classes[i];
	}
	element.className = new_classes;
}

/**
 * Add css class to an element
 */
function add_class(element, class_name) {
	remove_class(element, class_name);
	element.className += ' ' + class_name;
}

/**
 * Replace one css class with another on an element
 */
function change_class(element, from_class, to_class) {
	remove_class(element, from_class);
	add_class(element, to_class);
}

/**
 * Check if an element contains a class
 */
function has_class(element, class_name) {
	var classes = get_classes(element);
	for( var i = 0; i < classes.length; i++ ) {
		if ( classes[i] == class_name )
			return true;
	}
	return false;
}

function get_classes(element) {
	if (!element || !element.className)
		return [];
	return element.className.split(' ');
}

function row_over(row, is_mouseout) {
	if ( typeof(Hotkeys) != 'undefined' )
		Hotkeys.list_over(row, is_mouseout);
}

function toggle_debug(type) {
	var e = document.getElementsByTagName('div');
	var last = null;
	for (var i = 0; i < e.length; i++) {
		if ( e[i].className == 'msg type'+type ) {
			toggle_display(e[i]);
			if ( last.className == 'line' )
				toggle_display(last);
		}
		last = e[i];
	}
}
function toggle_display(e) {
	e = $(e);
	e.style.display = (e.style.display == 'none') ? '' : 'none';
}

function ajax_load(a, url, submit_form) {
	if (!a) {
		alert('no a element');
		return;
	}
	var element = (has_class(a,'t_container')) ? a : get_ancestor(a, 't_container');

	if (!element) {
		alert('Could not find mod container div');
		return;
	}

	var parent = element.parentNode;
	parent.removeChild(element);
	element = parent;

	var opt = {
		evalScripts: true // @todo.. 
	};
	if (submit_form) {
		opt.postBody = Form.serialize(submit_form);
	}
	element.innerHTML = '<img src="/kernel/images/loading.gif" />';
	new Ajax.Updater(element.id, url, opt);
}


function ajax_popup(a, url, recycle) {
	var opt = {};
	if (a) { // only move window to link position if a link is supplied
		opt.onComplete = function(res) {
			var div = $('w_ajax_popup');
			var pos = Position.cumulativeOffset(a);
			var x = pos[0];
			var y = pos[1];
			var divdim = Element.getDimensions(div);
			var x = pos[0] - (divdim.width/2);
			var y = pos[1] - (divdim.height/2);
			var windim = get_window_dimensions();
			var maxx = windim.width - divdim.width - 10;
			var maxy = windim.height - divdim.height - 10;
			if ( x > maxx )
				x = maxx;
			if ( y > maxy )
				y = maxy;
			if ( x < 10 )
				x = 10;
			if ( y < 10 )
				y = 10;
			div.style.top = y + 'px';
			div.style.left = x + 'px';			
			div.style.display = '';
			Hotkeys.init();
		}
	} else {
		opt.onComplete = function(res) {
			var div = $('w_ajax_popup');
			d.style.top = '10px';
			d.style.left = '10px';
			div.style.display = '';
		}
	}
	var wid = 'w_ajax_popup';
	var d;
	if (recycle)
		d = document.getElementById(wid);
	if (!d) {
		d = document.createElement('div');
		d.id = wid;
		d.style.position = 'absolute';
		d.style.display = 'none';
		document.body.appendChild(d);
	}
//	d.innerHTML = 'Loading ...';
	new Ajax.Updater(d.id, url, opt);
}

function ajax_popup_close() {
	document.body.removeChild( $('w_ajax_popup') );
}

function get_window_dimensions() {

        var obj = new Object();
        obj.width = 0;
        obj.height = 0;

        // Non-IE
        if( typeof( window.innerWidth ) == 'number' ) {
                obj.width = window.innerWidth;
                obj.height = window.innerHeight;
        // IE 6 standards compliant mode
        } else if( document.documentElement && ( document.documentElement.clientWidth || document.documentElement.clientHeight ) ) {
                obj.width = document.documentElement.clientWidth;
                obj.height = document.documentElement.clientHeight;
        //IE 4
        } else if( document.body && ( document.body.clientWidth || document.body.clientHeight ) ) {
                obj.width = document.body.clientWidth;
                obj.height = document.body.clientHeight;
        }

        return obj;

}

function popup_resize() {
	var img = document.images[0];
	var win = get_window_dimensions();
	var offset_x = (eonvar['popup_resize_offset_x']) ? eonvar['popup_resize_offset_x'] : 0;
	var offset_y = (eonvar['popup_resize_offset_y']) ? eonvar['popup_resize_offset_y'] : 0;
	window.resizeBy(img.width - win.width + offset_x, img.height - win.height + offset_y);
	self.focus(); 
}

function get_ancestor(element, ancestor_class) {
	if (!element)
		return;
	var body = document.body;
	var n = 0;
	var max = 1000;
	while (n<max) {
		element = element.parentNode;
		if ( element == body )
			return;
		if (has_class( element, ancestor_class ))
			return element;
	}
	alert('timed out after looking for ancestor ' + ancesor_class + ' in ' +max+' levels');
}

function admin_settings_popup() {
	if ($('w_ajax_popup'))
		ajax_popup_close();
	else
		ajax_popup(null, '/N0/mod_admin/settings/null/root/ajax=1');
}

// DEPRECATED, use eon.rpc.parse_response
function api_parse(result) {
	return eon.rpc.parse_response(result);
}
function XXX_api_parse(result, no_alert_on_error) {
	var data = result.responseText.split(';');
	if (data[0] == -1) {
		var msg = (data[1]) ? data[1] : 'Unknown exception';
		if (data[2])
			msg += ' #' + data[2];
		if (data[3])
			msg += ' : ' + data[3];
		alert(msg);
		return false;
        }
	if (data[0] == 0 && !no_alert_on_error) {
		alert('Error: ' + data[1]);
	}
	if (result.status != 200 && !no_alert_on_error) {
		switch (result.status) {
			case 401:
				alert('Authorization required (401)');
				break;
			case 404:
				alert('Not found (404)');
				break;
			default:
				alert('HTTP status ' + result.status);
		}
	}
	var ret = {};
	ret.http_status = result.status;
	ret.status = data[0];
	if (data.length > 2) {
		var shifted = [];
		for (var i=1; i<data.length; i++) {
			shifted[i-1] = data[i];
		}
		ret.data = shifted;
	} else {
		ret.data = data[1];
	}
	return ret;
}

/**
 * Bookmarking
 */
function add2bookmark() {
	// THIS DOES NOT WORK IN FIREFOX
	window.external.AddFavorite(location.href, document.title);
	return false;
}
function add2facebook() {
	var u=location.href;
	var t=document.title;
	window.open('http://www.facebook.com/sharer.php?u='+encodeURIComponent(u)+'&t='+encodeURIComponent(t),'sharer','toolbar=0,status=0,width=626,height=436');
	return false;
}
function add2delicious() {
	var u=location.href;
	var t=document.title;
	window.open('http://del.icio.us/post?v=4&noui&jump=close&url='+encodeURIComponent(u)+'&title='+encodeURIComponent(t), 'delicious','toolbar=no,width=700,height=400');
	return false;
}
function add2digg() {
	var u=location.href;
	var t=document.title;
	window.open('http://digg.com/submit?url='+encodeURIComponent(u)+'&title='+encodeURIComponent(t),'sharer','toolbar=0,status=0,width=626,height=436');
	return false;
}
function add2twitter() {
	var u=location.href;
	var t=document.title.replace('/null','');
	window.open('http://twitter.com/home?status='+encodeURIComponent(t)+'+'+encodeURIComponent(u),'sharer','toolbar=0,status=0,width=800,height=436');
	return false;
}

/**
 * Ajax select
 */
function ax_select(text, li) {
	var field = text.id.substr(4);
	$(field).value = li.id;
	var new_row = field.match(/.*XN\d?X/)+'_new';
	if($(new_row) && $(new_row).value){
		$(new_row).checked = true;
	}
	return false;
}

function ax_reset(eid, value, id) {
	$('_ax_' + eid).value = value;
	$(eid).value = id;
}

function ax_clear(eid) {
//alert(eid.value+'\n'+eid.name);
	if($('_ax_' + eid.name).value == '')
	eid.value = '';
}

function ax_new_callback(input, query) {
	var id = input.id.substr(4);
	var elem = $(id);
	if (elem)
		elem.value = 'N0';
	else
		eon.log('ax_new_callback - not found: ' + id);
	return query;
}

/**
 * Desktop
 */
function dt_min(mod, obj, win_id) {
	if($('dt_b_'+win_id).style.display == 'none'){
		Effect.toggle('dt_b_'+win_id, 'blind');
		obj.src = '/kernel/images/mod_your_page/btn_min.gif';
		new Ajax.Request('/U/'+mod+'/desktop/save_state', {
			method: "post",
			parameters: { max: win_id }
			});
	}else{
		Effect.toggle('dt_b_'+win_id, 'blind');
		obj.src = '/kernel/images/mod_your_page/btn_max.gif';
		new Ajax.Request('/U/'+mod+'/desktop/save_state', {
			method: "post",
			parameters: { min: win_id }
			});
	}
	return false;
}
function dt_close(mod, win_id) {
//	Effect.SwitchOff('dt_w_'+win_id);
	Effect.toggle('dt_w_'+win_id,'slide');
	new Ajax.Request('/U/'+mod+'/desktop/save_state', {
		method: "post",
		parameters: { close: win_id }
		});
	$('dt_cw_'+win_id).style.display='list-item'
	return false;
}
function dt_move(mod, col) {
	//$(col.id+'_order').innerHTML = Sortable.serialize(col.id);
	new Ajax.Request('/U/'+mod+'/desktop/save_order', {
		method: "post",
		parameters: { data: Sortable.serialize(col.id) }
		});  
}
function dt_open(mod, win_id) {
	new Ajax.Request('/U/'+mod+'/desktop/save_state', {
		method: "post",
		parameters: { open: win_id }
		});
//	$('dt_w_'+win_id).style.display=''
	if($('dt_w_'+win_id)){
		Effect.toggle('dt_w_'+win_id,'slide');
		$('dt_cw_'+win_id).style.display='none';
	}else{
		var win;
		var hasInnerText = (document.getElementsByTagName("body")[0].innerText != undefined) ? true : false;
		if(hasInnerText)
			win=$('dt_cw_'+win_id).innerText;
		else
			win=$('dt_cw_'+win_id).textContent;
		$('dt_cw_'+win_id).innerHTML='<b>'+win+'</b>';
	}
	return false;
}
/**
 * Calendar
 */
function cal_browse(mod, div, cid, new_date) {
	document.getElementById(div).innerHTML = '<div style="width:100%;padding-top:56px;padding-bottom:56px;text-align:center;"><img src="/kernel/images/loading.gif" alt="Loading..." /></div>';
	new Ajax.Updater(
		div, 
		'/'+cid+'/'+mod+'/ax_calendar', {
			asynchronous:true,
			parameters: { date: new_date, cal_ret: div, cid: cid }
		});
	return false;
}

function translation_onload() {
	// wait a bit to avoid clash with media list loading
	setTimeout("_translation_onload()", 1000);
}
function _translation_onload() {
	eon.log('translation onload');
	var elem = document.body;
	translation_traverse(elem);
}
function translation_traverse(elem) {
//	eon.log('translation traverse: ' + elem.childNodes);
	if (elem.childNodes.length == 0) {
		translation_elem(elem);
		return;
	}
	for(var i=0; i < elem.childNodes.length; i++) {
		var elem2 = elem.childNodes[i];
		translation_traverse(elem2);
	}
}
function translation_elem(elem) {
//	eon.log('elem: ' + elem + ' : ' + typeof(elem) + ' : ' + elem.tagName + ' : ' + elem.type);

	// Ignore non-text and empty nodes
	if (elem.nodeType != elem.TEXT_NODE)
		return;
	var val = elem.nodeValue;
	if (!val || val == '' || val.match(/^ +[\r\n]*$/) || val.match(/^[\r\n]+$/))
		return;

	// Look for untranslated & make links
	if (val.match(/^\[.+\]$/)) {
		translation_make(elem, val);
	}

//	eon.log('val: |' + val + '|');
//	for(var i in elem) {
//		if (typeof(elem[i]) != 'function')
//			eon.log('   ' + i + ': ' + elem[i]);
//	}
}
function translation_make(elem, val) {
	eon.log(elem.parentNode);
	if (!elem.parentNode) {
		eon.log('translation - got no parent node for element: ' + elem);
		return;
	}
	var link = document.createElement('a');
	link.innerHTML = val;
	val = val.replace(/^\[/, '').replace(/\]$/, '');
	link.href = "javascript:translation_ajax(this, '" + val + "')";
	elem.parentNode.replaceChild(link, elem);
}
function translation_ajax(link, val) {
	var url = '/U/translation/ajax_one?ajax=1&text=' + escape(val);
	ajax_popup(link, url, true);
}

// toggle selector checkboxes
function select_set(val) {
	var items = document.getElementsByTagName('input');
	for (var i=0; i < items.length; i++) {
		if (items[i].type != 'checkbox')
			continue;
		if (!has_class(items[i], 'selector'))
			continue;
		items[i].checked = val;
	}
}
function select_all() {
	select_set(true);
}
function select_none() {
	select_set(false);
}

function handle_order_deliv_to(elem) {
	return handle_order_field(elem, 'deliv_to', ['none','cust', 'manual', 'pickup', 'workshop']);
}
function handle_order_pay_mode(elem) {
	return handle_order_field(elem, 'pay_mode', ['none', 'test', 'card', 'invoice', 'cash', 'offer', 'card_terminal', 'down_payment', 'offer_with_ofer', 'cash_cashinvoice', 'card_cashinvoice']);
}
function handle_order_field(elem, key, types) {
	if (!elem) {
		elem = $('_muxXN0X' + key);
	}
	if (!elem) {
		eon.log('no element!');
		return;
	}
	var type = types[elem.value];
	var tmp = document.getElementsByTagName('div');
	for(var i=0; i < tmp.length; i++) {
		if (key == 'deliv_to') {
			if ( tmp[i].className.match(/(^| )f_workshop/) ) {
				tmp[i].style.display = (type=='workshop') ? '' : 'none';
				continue;
			}
			if ( !tmp[i].className.match(/(^| )f_deliv_/) )
				continue;
			if ( tmp[i].className.match(/(^| )f_deliv_(head|to|info)/) )
				continue;
			tmp[i].style.display = (type=='manual') ? '' : 'none';
		} else {
			if ( !tmp[i].className.match(/(^| )f_cash/) )
				continue;
			tmp[i].style.display = (type=='cash' || type=='cash_cashinvoice') ? '' : 'none';
		}
	}
}

function workshop_popup() {
        var val = $('_muxXN0Xworkshop').value;
        if (!val) {
                alert(eonvar.select_dealer);
                return;
        }
        var url = '/' + val + '/dealer/item?popup=1';
        popup_window(url, 'workshop', 'height=500,width=400,resizable=1');
}

/* fs - fieldset functions */
function fs_close_all(open_set) {
	var fs = document.getElementsByTagName('div');
	for (var i=0; i < fs.length; i++) {
		if(fs[i].className != 'x_fieldset')
			continue;
		if(fs[i].id==open_set)
			fs[i].style.display='';
		else
			fs[i].style.display='none';
	}
//	document.location='#'+open_set;
}

function fs_open_all(close_set) {
	var fs = document.getElementsByTagName('div');
	for (var i=0; i < fs.length; i++) {
		if(fs[i].className != 'x_fieldset')
			continue;
		if(fs[i].id==close_set)
			fs[i].style.display='none';
		else
			fs[i].style.display='';
	}
//	document.location='#'+open_set;
}

function fs_hide_all(show_set) {
	var fs = document.getElementsByTagName('fieldset');
	for (var i=0; i < fs.length; i++) {
		if(fs[i].id==show_set)
			fs[i].style.display='';
		else
			fs[i].style.display='none';
	}
}

function fs_show_all(hide_set) {
	var fs = document.getElementsByTagName('fieldset');
	for (var i=0; i < fs.length; i++) {
		if(fs[i].id==hide_set)
			fs[i].style.display='none';
		else
			fs[i].style.display='';
	}
}

function set_homepage(a_element, url) {
	a_element.style.behavior='url(#default#homepage)';
	a_element.setHomePage(url);
}


/**
 * Add new row to editable list (new_row). Works by cloning last row,
 * and appending it to the table.
 *
 * @note Firefox don't support writing innerHTML for table rows,
 * therefor we must do it for each table cell.
 *
 * @todo use second last row (so alt. coloring works)?
 * @bug	will copy last row, so make sure its empty (not anymore. innerHTML?)
 */
function _table_add_row(obj, clone_input_values)
{
	var table = obj.parentNode.previousSibling.firstChild; // tbody
	// if (table.nodeName != 'TBODY') eon.warn(); return;
	if (table.nodeValue == "\n") // use helper for this
		table = table.nextSibling;
	
	// find the last row
	var row = table.lastChild;
	while (row.nodeName != 'TR')  // last can be #text, so step backwards
		row = row.previousSibling;

	// Clone node and increment NEW_ID for the row.
	var tmp = row.id.split('N');
	var id = parseInt(tmp[tmp.length-1]); // this does?
	var node = row.cloneNode(true); // (note: only need one step cloning, not full)
	var pre = '';
	for(var i=0; i < (tmp.length-1); i++) { // this does?
		if (i != 0) pre += 'N';
		pre += tmp[i];
	}
	node.id = pre + 'N' + (id+1);

	// To handle sublists, we must only replace the correct NXi, not all.
	// If f.ex. TR is r_N0XlinesXN0, we want to replace
	//   _muxXN0XlinesXN0Xname -> _muxXN0XlinesXN1Xname
	//   _muxXN0XlinesXN0XlisttwoXN0Xname -> _muxXN0XlinesXN1XlisttwoXN0Xname
	//   etc.
	var src = pre.replace(/^r_/, '_muxX') + 'N' + id;
	var rep = pre.replace(/^r_/, '_muxX') + 'N' + (id+1);
	src = new RegExp(src, 'g');

	var list = node.childNodes;
	var prefix;
	for (var i=0; i<list.length; i++) {
		list[i].innerHTML = list[i].innerHTML.replace(src, rep);

/*
		list[i].innerHTML = list[i].innerHTML.replace(
			// only replace the last XNx, not all of them!
			// f.ex. _muxXN0XlinesXN0Xname becomes _muxXN0XlinesXN1Xname, not _muxXN1XlinesXN1Xname
			new RegExp('(.+)XN'+id+'X([^N]+)','g'), '$1XN'+(id+1)+'X$2');
			// must use regexp for "replace all"
			//new RegExp('XN'+id+'X','g'), 'XN'+(id+1)+'X');
			//new RegExp('_muxXN'+id,'g'), '_muxXN'+(id+1));
		// handle line number column
		// getAttribute('class') don't work in IE!!
		//if (list[i].getAttribute('class').indexOf('d_linenumber') != -1)
*/
		if (list[i].className.indexOf('d_linenumber') != -1) {
			list[i].firstChild.innerHTML = Number(list[i].firstChild.innerHTML)+1;
		}

		var tmp = clone_td_input_values(list[i], row.childNodes[i], !clone_input_values);
		if (tmp) prefix = tmp;
	}
	// There may be hidden inputs belonging to the original row (default values).
	// Unfortunately they are not inside the row, but we can find them by prefix.
	var inps = document.getElementsByTagName('INPUT');
	var pat = new RegExp(prefix + '.+');
	var par;
	var inps2 = [];
	var n = 0;
	for(var j=0; j < inps.length; j++) {
		if (inps[j].type != 'hidden')
			continue;
//eon.log(inps[j]);
//eon.log('vs ',pat);
		if (!inps[j].name.match(pat))
			continue;
		var inp = inps[j].cloneNode(true);
		inp.name = inp.name.replace(
			new RegExp('XN'+id+'X','g'), 'XN'+(id+1)+'X'
		);
		if (inps[j].parentNode.nodeName == 'DIV')
			par = inps[j].parentNode;
		else
			par = inps[j].parentNode.parentNode;
//eon.log('clone hidden: ', inp);
		inps2[n] = inp;
		n++;
	}
//eon.log('hiddens: ',inps2,par);
	if (inps2.length > 0) {
		for (var j=0; j < inps2.length; j++) {
			par.appendChild(inps2[j]);
		}
	}
	table.appendChild(node);
}

/* TMP TMP new version that keeps select.selectionIndex in IE
function _table_add_row(obj)
{
	var table = obj.parentNode.previousSibling.firstChild; // tbody
	// if (table.nodeName != 'TBODY') eon.warn(); return;
	if (table.nodeValue == "\n") // use helper for this
		table = table.nextSibling;

	// find the last row
	var row = table.lastChild;
	while (row.nodeName != 'TR')  // last can be #text, so step backwards
		row = row.previousSibling;

	// clone node and increment NEW_ID (for all children)
	var tmp = row.getAttribute('id').split('N');
	var id = parseInt(tmp[1]);

	var node = row.cloneNode(true); // (note: only need one step cloning, not full)
	node.setAttribute('id', tmp[0] + 'N' + (id+1));
	var list = node.childNodes;
	for (var i=0; i<list.length; i++) {
		list[i].innerHTML = list[i].innerHTML.replace(
			new RegExp('XN'+id+'X','g'), 'XN'+(id+1)+'X');	// must use regexp for "replace all"
			//new RegExp('_muxXN'+id,'g'), '_muxXN'+(id+1));
		// fix seleced item for IE
		if (list[i].className.indexOf('d_country') != -1)
		{
			var idx;
			var lst = table.getElementsByTagName('SELECT');
			for (var ii=0; ii<lst.length; ii++) {
				if (lst[ii].parentNode.parentNode.className.indexOf('d_country') != -1) {
					idx = lst[ii].selectedIndex;
				}
			}
			list[i].firstChild.firstChild.selectedIndex = idx;
		}

	}

	//var i = e.item(0).selectedIndex;
}
*/

function clone_td_input_values(newtd, oldtd, simulate) {
	var list = newtd.childNodes; //[0].childNodes;
	var oldlist = oldtd.childNodes;
	if (!oldtd.className.match(/t_actioncell/)) {
		list = list[0].childNodes;	
		oldlist = oldlist[0].childNodes;
	}
	var somenode;
	for (var i=0; i < list.length; i++) {
		var node = list[i];
		var oldnode = oldlist[i];
		if (node.nodeName == 'DIV') {
			for (var j=0; j < node.childNodes.length; j++) {
				if (node.childNodes[j].nodeName == 'INPUT' || node.childNodes[j].nodeName == 'SELECT') {
					node = node.childNodes[j];
					oldnode = oldnode.childNodes[j];
					break;
				}
			}
		}
		if (node.nodeName != 'INPUT' && node.nodeName != 'SELECT')
			continue;
		if (!simulate) {
			if (node.type == 'checkbox')
				node.checked = oldnode.checked;
			else
				node.value = oldnode.value;
		}
		somenode = oldnode;
	}
	if (!somenode)
		return false;
	var tmp = somenode.name.split('X');
	var str = tmp[0];
	for (var i=1; i < (tmp.length-1); i++) {
		str += 'X' + tmp[i];
	}
	return str + 'X';
}

function format_tc(e, obj){
	var KeyID = (window.event) ? event.keyCode : e.keyCode;
	if (KeyID == 8)
		return;
	if(obj.value.match(/^\d\d$/) || obj.value.match(/^\d\d:\d\d$/) || obj.value.match(/^\d\d:\d\d:\d\d$/))
		obj.value = obj.value+':'
}

function increment_element_ids(node) {
	var list = node.childNodes;
	//var prefix;
	//var pat = new RegExp('(.+)XN'+id+'X([^N]+)','g');
	for (var i=0; i<list.length; i++) {
		if (list[i].id) {
eon.log('SRC: ',list[i].id);
			list[i].id = list[i].id.replace(
				new RegExp('(.+)XN'+id+'X([^N]+)','g'),
				'$1XN'+(id+1)+'X$2'
			);
eon.log('DST: ',list[i].id);
		}
		if (list[i].childNodes) {
			for(var j=0; j<list[i].childNodes.length; j++) {
				increment_element_ids(list[i].childNodes[j]);
			}
		}
		if (list[i].className && list[i].className.indexOf('d_linenumber') != -1) {
			list[i].firstChild.innerHTML = Number(list[i].firstChild.innerHTML)+1;
		}
	}
}

// shop
function on_shop_price_change(elem) {
	if (!elem || !elem.form)
		return;
	var name = elem.name + '_updated';
	var elem2;
	for(var i=0; i < elem.form.elements.length; i++) {
		if (elem.form.elements[i].name == name) {
			elem2 = elem.form.elements[i];
			break;
		}
	}
	if (elem2) {
		elem2.value = 1;
	}
}
