
/**
 * 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) {
		// Internet Explorer (<8) do not support URLs longer than 2048 (2^11)
		// characters. IE6 gives syntax error, IE7 says "cannot display
		// the webpage"
		// Can switch to 'POST' to get around this, but 'GET' is more
		// correct since we do not change data.

		// @todo How to get URL? form.action is empty. Then we can check
		// the length and swithc to post only when needed.
		// There are also lots of empty form.input=hidden that can be
		// removed to greatly reduce the GET URL length.

		// IE7 fix (always use post, even when we don't need it)
		if (navigator.appVersion.indexOf("MSIE 7.") >= 0) {
			form.method = 'post';
			form.submit();
			return;
		}

		// IE6 fix
		try {
			form.method = 'get';
			form.submit();
		} catch (ex) {
			if (! ex instanceof SyntaxError) throw ex; // rethrow
			form.method = 'post';
			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 = eonvar.text.saving + ' <img src="/kernel/images/loading.gif"/>'; // @todo: translate
		d1.id = 'saving_inner';
		d0.appendChild(d1);

//alert('ons!');
		// Callback
		var callback = function(result, response, form) {
//alert('cb');
			// 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') {
//alert('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
//alert('ap: ' + eon.form.AJAX_POST);
				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':
						// fall back on refresh if no history (i.e. one page in history - the current one)
						if (history.length < 2)
							location.reload(true);
						else
							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) {
		//options = 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 = {

	new_prod_input: null,
	new_prod_type: null,
	new_prod_psid: null,
	new_prod_steps: null,
	new_prod_active: null,

	inventory_step3_onload: function() {
		for(var i=0; i < eonvar['inventory_hide_ids'].length; i++) {
			var id = eonvar['inventory_hide_ids'][i];
			var elem = document.getElementById(id);
			if (!elem) continue;
//elem.style.border = '1px solid red'; continue;

			elem = elem.parentNode;
			if (!elem) continue;
//alert('hide:'+id + ' : ' + elem.className);
			elem.style.display = 'none';
		}
	},

	inventory_product_select_callback: function(prod_id, elem_id) {
		var url = '/' + prod_id + '/product/prod_cost/prod_cost';
		var prefix = elem_id.replace('Xproduct_id', 'X');

		// Check new row input (if any)
		var new_elem = document.getElementById(prefix + '_new');
		if (new_elem)
			new_elem.checked = true;

		// If product is selected then type must be stock, so
		// auto-set it for user's convenience.
		var type_elem = document.getElementById(prefix + 'type');
		for (var i=0; i < type_elem.options.length; i++) {
			if (type_elem.options[i].value == 'stock') {
				type_elem.selectedIndex = i;
				break;
			}
		}

		// Clear out cost value from possibly previous select
		var cost_elem = document.getElementById(prefix + 'cost');
		cost_elem.value = '';

		// Fetch product cost in shop's native currency 
		// and update cost input
		eon.rpc.call(url, function(res) {
			if (res.type != 'ok') {
				alert('error: ' + res.message);
				return;
			}
			cost_elem.value = res.data;
		});
	},

	inventory_change_type: function(elem) {
		var id = elem.id;
		var pre = id.replace('Xtype', 'X');
		var pe = document.getElementById(pre + 'product_id').parentNode;
		var ce = document.getElementById(pre + 'cost').parentNode;
		var re = document.getElementById(pre + 'ref');
		var ne = document.getElementById(pre + 'number');
		if (elem.value == 'stock') {
			pe.style.display = 'block';
			ce.style.display = 'block';
			re.style.display = 'none';
			ne.parentNode.style.display = 'block';
		} else {
			pe.style.display = 'none';
			ce.style.display = 'none';
			re.style.display = '';
			ne.value = 1;
			ne.parentNode.style.display = 'none';
		}
	},

	new_prod_ajax_save: function(obj) {
		// Check response ({id:123, result=true, type=ok, message=Added}
		if (obj.type != 'ok') {
			alert(obj.message);
			return;
		}

		var url = '/' + obj['_id'] + '/product/prod_info/prod_info?f=' + eon.shop.new_prod_steps.join(',');
		eon.rpc.call(url, function(res) {
			if (res.type != 'ok') {
				alert('error: ' + res.message);
				return;
			}
			eon.shop._new_prod_ajax_save(obj, res.data);
		});
	},

	_new_prod_ajax_save: function(obj, prod) {

		var el = document.getElementById(eon.shop.new_prod_input);

		// TMP stuff - todo fixme get as passed args?
		var steps = eon.shop.new_prod_steps;
		var type = eon.shop.new_prod_type;
		var psid = eon.shop.new_prod_psid;
		var args = [];
		for(var i=0; i < steps.length; i++) {
			args[i] = [ steps[i], prod[steps[i]] ];
		}

		// Show all selects
		var sels = document.getElementsByClassName('x_prod_select_'+psid);
		for(var i=0; i < sels.length; i++) {
			sels[i].style.display = '';	
		}

		ajax_popup_close();

		// Fill all selects (calls itself untill done and closes ajax popup)
		eon.shop.fill_prod_select(0, steps, psid, type, args);
	},

	fill_prod_select: function(step_n, steps, psid, type, args) {
		var step = steps[step_n];
		var url = '/U/product/prod_opt/prod_opt?step=' + step + '&ajax=action&type=' + type + '&active=';
		if (eon.shop.new_prod_active)
			url += '1';
		else
			url += '0';
		for(var a=0; a<args.length; a++) {
			url += '&' + args[a][0] + '=' + escape(args[a][1]);
		}
		var nextelem = document.getElementById(eon.shop.new_prod_input + '_ps_' + step);
		//nextelem.innerHTML = '<option value="">Loading...</option>';
		nextelem.options.length = 0;
		nextelem.options[0] = new Option('Loading ' + step + 's...', '');
		eon.rpc.call(url, function(res) {
			if (res.type == 'exception') {
				alert('error: ' + res.message);
				return;
			}

			//var nextelem = document.getElementById('prod_sel_' + psid + '_' + step);
			var nextelem = document.getElementById(eon.shop.new_prod_input + '_ps_' + step);
			if (res && res.data && res.data.options) {
				//nextelem.innerHTML = '<option value="">(Please select ' + step + ')</option>' + res.data.options;
				nextelem.options[0] = new Option('(Please select ' + step + ')', '');
				for(var j=0; j < res.data.options.length; j++) {
					nextelem.options[j+1] = new Option(res.data.options[j]['text'], res.data.options[j]['value']);
				}
				var argv = null;
				for (var a=0; a<args.length; a++) {
					if (args[a][0] == step) {
						argv = args[a][1];
						break;
					}
				}
				for(var o=0; o<nextelem.options.length; o++) {
					var opt_done = false;
					if (nextelem.options[o].value == argv) {
						nextelem.selectedIndex = o;
						opt_done = true;
						break;
					}
					if (opt_done)
						break;
				}
			} else {
				//nextelem.innerHTML = '<option value="">(None)</option>';
				nextelem.options[0] = new Option('(None)', '');
			}

			var realelem = document.getElementById(eon.shop.new_prod_input);
			if (step_n == steps.length-1) {
				realelem.value = argv;
				var newelem = document.getElementById( realelem.id.replace('Xproduct', 'X_new') );
				if (newelem)
					newelem.checked = true;
				if (eon.shop.new_prod_callback) {
					eon.shop.new_prod_callback(realelem.value, realelem.id);	
				}
			} else {
				realelem.value = '';
				eon.shop.fill_prod_select(step_n+1, steps, psid, type, args);
			}
		});

	},

	// TMP
	_debug: function(msg) {
		var el = document.getElementById('debug');
		el.innerHTML += msg + '<br/>';
	},

/*
	foodebug: function(form) {
		var txt = "";
		for(var i=0; i < form.elements.length; i++) {
			var tmp = form.elements[i].id.split('X');
			tmp = tmp[tmp.length-1];
			if (tmp == 'product_id') {
				//form.elements[i].type = 'text';
				txt += form.elements[i].id + ' (' + form.elements[i].parentNode.className + '): ' + form.elements[i].value + "<br/>\n";
			}
		}
		document.getElementById('foodebug').innerHTML = txt;
	},
*/

	// Stupid IE is painfully slow if there are many arguments, so receive as a single arg that is a list of the real args
	//prod_sel_change: function(step, nextparam, elem, type, id, argdef, real_field_id, psid, active, real_input_id) {
	prod_sel_change: function(args) {

		var step = args[0];
		var nextparam = args[1];
		var elem = args[2];
		var type = args[3];
		var id = args[4];
		var argdef = args[5];
		var real_field_id = args[6];
		var psid = args[7];
		var active = args[8];
		var real_input_id = args[9];
		var steps = args[10];
		var cb = args[11];

		var realelem = document.getElementById(real_input_id);
		realelem.value = '';

		// hack for last elem
		var nextelem = document.getElementById(id);
		if (!nextelem) {
			realelem.value = elem.value;
			//realelem.type = 'text';
			var newelem = document.getElementById( realelem.id.replace('Xproduct', 'X_new') );
//alert('LAST@exist: ' + realelem.id + ' = ' + realelem.value); // tuba
			if (newelem)
				newelem.checked = true;
			if (cb) {
//alert('doing cb for exist'); // tuba
				cb(realelem.value, realelem.id);
			}
			return;
		}

		nextelem.options.length = 0;
		nextelem.options[0] = new Option('Loading ' + step + 's...', '');

		var url = '/U/product/prod_opt/prod_opt?step=' + step + '&ajax=action&type=' + type + '&active=';
		if (active)
			url += '1';
		else
			url += '0';
		var args = {};

		//var sels = document.getElementsByClassName('x_prod_select_'+psid); // SLOW in IE!
		var sels = [];
		for (var si=0; si < steps.length; si++) {
			sels[si] = document.getElementById(real_input_id + '_ps_' + steps[si]);
		}

		for(var i=0; i < argdef.length; i++) {
			var ak = argdef[i][0];
			var av = document.getElementById(argdef[i][1]).value;
			url += '&' + ak + '=' + escape(av);
		}
		for(var i=0; i < argdef.length+2; i++) {
			if (sels && sels[i]) {
				sels[i].style.display = '';
			}
		}
		if (sels && argdef && sels.length > 0 && sels.length > argdef.length) {
			for(var i=argdef.length+2; i<sels.length; i++) {
				sels[i].style.display = 'none';
			}
		}
		url += '&' + nextparam + '=' + escape(elem.value);

		eon.rpc.call(url, function(res) {
			if (res.type == 'exception') {
				alert('error: ' + res.message);
				return;
			}
			var nextelem = document.getElementById(id);
			nextelem.options.length = 0;
			if (res && res.data && res.data.options) {
				//nextelem.innerHTML = '<option value="">(Please select ' + step + ')</option>' + res.data.options;
				nextelem.options[0] = new Option('(Please select ' + step + ')', '');
				for(var j=0; j < res.data.options.length; j++) {
					nextelem.options[j+1] = new Option(res.data.options[j]['text'], res.data.options[j]['value']);
				}
				if (res.data.count == 1) {
					nextelem.selectedIndex = 1;
					nextelem.onchange();
				}
			} else {
				//nextelem.innerHTML = '<option value="">(None)</option>';
				nextelem.options[0] = new Option('(None)', '');
			}
		});
	},

	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;
		if (eonvar.order_checkout_storage)
			url += '&storage=' + eonvar.order_checkout_storage;
		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;
		});
	},

	pas_always_show_onchange: function(chk) {
		var val = '0';
		if (chk.checked) val = '1';
		var url = '/N0/product/pas_always_show_onchange/onchange?ajax=action&val=' + val;
		eon.rpc.call(url, function(res) {
		});
	},

	pas_choose: function(a, k) {
		var val = document.getElementById('pas_' + k + '_hid').value;
		ajax_popup(a, '/U/product/pas_choose_' + k + '?ajax=1&val=' + val);
		this.pas_choose_k = k;
	},
	pas_choose_submit: function(e, is_all) {
		ajax_popup_close();
		var span = document.getElementById('pas_' + this.pas_choose_k + '_span');
		var input = document.getElementById('pas_' + this.pas_choose_k + '_hid');
		if (is_all) {
			span.innerHTML = eonvar['text']['all'];
			input.value = '';
		} else {
			var data = this.pas_choose_parse(e);
			var ids = data.join(',');
			input.value = ids;
			span.innerHTML = 'Loading...';
			var url = '/U/product/pas_get_text/get_text?k=' + this.pas_choose_k + '&i=' + ids;
			eon.rpc.call(url, function(res) {
				if (res.type != 'ok')
					return;
				span.innerHTML = res.data;
			});
		}
	},

	pas_choose_parse: function(e) {
		var form = e.parentNode.parentNode;
		var data = [];
		for (var i = 0; i < form.elements.length; i++) {
			var input = form.elements[i];
			if (input.type != 'checkbox' || !input.checked) continue;
			var tmp = input.id.split('_');
			var txt = '';
			for (j=1; j < tmp.length; j++) {
				if (j > 1) txt += '_';
				txt += tmp[j];
			}
			data[data.length] = txt;
		}
		return data;
	},

	pas_sort_locations: function(a) {
		var elem1 = document.getElementById('pas_sort_locations_tmp_hid');
		var elem2 = document.getElementById('pas_sort_locations_hid');
		elem1.value = elem2.value;
		var val = document.getElementById('pas_sort_locations_hid').value;
		var filt = document.getElementById('pas_location_hid').value;
		ajax_popup(a, '/U/product/pas_sort_locations?ajax=1&val=' + val + '&filt=' + filt + '&ts=' + (new Date()).getTime(), null, null, function() {
			Sortable.create('pas_sort_locations', {
				onUpdate: eon.shop.pas_sort_locations_onupdate
			});
		});
	},
	pas_sort_locations_onupdate: function(list) {
		var data = [];
		Sortable.sequence(list).each(function (value) {
			data.push(value);
		});
		var elem = document.getElementById('pas_sort_locations_tmp_hid');
		elem.value = data.join(',');
	},
	pas_sort_locations_submit: function(a, is_all) {
		ajax_popup_close();
		var span = document.getElementById('pas_sort_location_span');
		var elem1 = document.getElementById('pas_sort_locations_tmp_hid');
		var elem2 = document.getElementById('pas_sort_locations_hid');
		if (is_all) {
			span.innerHTML = eonvar.text['no_sorting'];
			elem2.value = '';
		} else {
			elem2.value = elem1.value;
			span.innerHTML = 'Loading...';
			var url = '/U/product/pas_get_text/get_text?k=location&i=' + elem1.value;
			eon.rpc.call(url, function(res) {
				if (res.type != 'ok')
					return;
				span.innerHTML = res.data;
			});
		}
	},

	pas_sort_onupdate: function(list) {
		var data = [];
		Sortable.sequence(list).each(function (value) {
			data.push(value);
		});
		var elem = document.getElementById('pas_sort_hid');
		elem.value = data.join(',');
	},

	pas_reset: function() {
		var url = '/U/product/pas_submit/reset';
		var rem_el = document.getElementById('pas_remember');
		if (rem_el.checked)
			url += '?remember=1';
		else
			url += '?remember=0';
		eon.rpc.call(url, function(res) {
			if (res.type != 'ok')
				return;
			document.location.reload();
		});
	},

	pas_submit: function() {
		var data = {};
		// Hidden inputs for sorting and filters
		var keys = ['sort', 'sort_locations', 'location', 'supplier', 'category', 'brand', 'attrib'];
		for (var i=0; i < keys.length; i++) {
			var elem = document.getElementById('pas_' + keys[i] + '_hid');
			data[keys[i]] = elem.value;
		}
		// Visible radio inputs for sorting
		keys = ['price', 'stock'];
		var vals = ['lo', 'hi'];
		for (var i=0; i < keys.length; i++) {
			for (var j=0; j < vals.length; j++) {	
				var eid = 'pas_sort_' + keys[i] + '_' + vals[j];
				var elem = document.getElementById(eid);
				if (elem.checked)
					data['sort_' + keys[i]] = vals[j];
			}
		}
		// Visible inputs for filter
		keys = ['number'];
		for (var i=0; i < keys.length; i++) {
			var elem = document.getElementById('pas_' + keys[i]);
			data[keys[i]] = elem.value;
		}
		// Visible checkbox inputs for filter
		keys = ['class'];	
		for (var i=0; i < keys.length; i++) {
			var eid = 'pas_' + keys[i];
			var vals = document.getElementById(eid + '_vals').value.split(',');
			data[keys[i]] = [];
			for (var j=0; j < vals.length; j++) {	
				var eid2 = eid + '_' + vals[j];
				var elem = document.getElementById(eid2);
				if (elem.checked) {
					data[keys[i]][ data[keys[i]].length ] = vals[j];
				}
			}
			data[keys[i]] = data[keys[i]].join(',');
		}
		var rem_el = document.getElementById('pas_remember');
		if (rem_el.checked)
			data['remember'] = 1;
		else
			data['remember'] = 0;
		// Make URL
		var url = '/U/product/pas_submit/submit?';
		for (var k in data) {
			if (typeof(data[k]) == 'function') continue;
			url += k + '=' + data[k] + '&';
		}
		eon.rpc.call(url, function(res) {
			if (res.type != 'ok')
				return;
			document.location.reload();
		});
	},

	pas_hide_advanced: function() {
		var elem = document.getElementById('pas_content');
		elem.style.display = 'none';
		elem = document.getElementById('pas_head');
		elem.style.display = 'block';
	},

	pas_show_advanced: function() {
		var elem = document.getElementById('pas_content');
		elem.style.display = 'block';
		elem = document.getElementById('pas_head');
		elem.style.display = 'none';
	}

/*
	// 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, restrict_from_field) {
		eon.datatype.select._new = {
			inid: elid,
			input: document.getElementById(elid),
			module: mod,
			view: view,
			id: id,
			field: field,
			restrict_from_id: null,
			restrict_from_input: null
		};
//eon.log('restrict from id: ' + eon.datatype.select._new.restrict_from_id);
		if (restrict_from_field) {
			eon.datatype.select._new.restrict_from_id = elid.replace(field, restrict_from_field);
			eon.datatype.select._new.restrict_from_input = document.getElementById(eon.datatype.select._new.restrict_from_id);
			url = url.replace('###RESTRICT_VAL###', eon.datatype.select._new.restrict_from_input.value);
		}
//alert(url);
//eon.log('restrict from input: ' + eon.datatype.select._new.restrict_from_input);
		//ajax_popup(a, url);
		ajax_popup(a, url, null, 'w_ajax_popup_sel_new');
	},
	new_save_cb: function(res) {
		if (!res['result'])
			return;
		var res_id = res['_id'];
		var input = eon.datatype.select._new.input;
		var el = input.parentNode;
		el.innerHTML = 'Loading...';
		// NB: close ajax popup before making url to avoid naming conflicts with input ids!
		// (Still not fully safe though, can in theory get conflict with main form or other ajax windows)
		var url = eon.datatype.select.make_url(eon.datatype.select._new);
//eon.log(url);
		ajax_popup_close('w_ajax_popup_sel_new');
        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;
					}
				}
				if (input.onchange)
					input.onchange();
			//	console.log('done: ' + res_id);
            }
		});
	},

	make_url: function(def) {
		var res_param = null;
//eon.log('restrict from id2: ' + def.restrict_from_id);
//eon.log('restrict from input2: ' + def.restrict_from_input);
		if (def.restrict_from_input)
			res_param = 'restrict_val=' + def.restrict_from_input.value;
		return '/' + def['id'] + '/' + def['module'] + '/' + def['view'] + '/null/root/ajaxedit=1|ae_field=' + def['field'] + '|' + res_param;
	}

}

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();
		if (!v.match(p)) {
			ex.message = 'Invalid dimension';
			throw ex;
		}
		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');
		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
*/

function select_restrict_ajax(from_elem, to_id, url) {
	url = url.replace('###VALUE###', from_elem.value); // todo: ugly... is there a better way? see also datatype.make_select_input()
	var to_elem = document.getElementById(to_id);
	//to_elem.innerHTML = '<option value="">Loading...</option>';
	to_elem.options.length = 0;
	to_elem.options[0] = new Option('Loading...', '');
	eon.rpc.call(url, function(res) {
		if (res.type != 'ok')
			return;
		//to_elem.innerHTML = res.data;
		to_elem.options.length = 0;
		for(var i=0; i < res.data.length; i++) {
			var opt = res.data[i];
			to_elem.options[i] = new Option(opt['name'], opt['_id']);
		}
	});
}

var select_hold = new Array();
function select_restrict(obj, sid, use_ajax_url){
	if (use_ajax_url) {
		select_restrict_ajax(obj, sid, use_ajax_url);
		return;
	}
	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 iclasses = input.className.split(" ");
	for(var i=0; i < iclasses.length; i++) {
		if (iclasses[i] == 'x_novalidate')
			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);
}


var ajax_popup_a;
var ajax_popup_id = 1;

function ajax_popup_multi(a, url) {
	ajax_popup_id += 1;
	ajax_popup(a, url, false, "w_ajax_popup_" + ajax_popup_id);
}

function ajax_popup(a, url, recycle, wid, onComplete) {

	if (a == 'last')
		a = ajax_popup_a;
	ajax_popup_a = a;

	// New generation - recycle only if existing id
	if (wid) {
		var el = document.getElementById(wid);
		if (el)
			recycle = true;
		else
			recycle = false;
	} else {
		wid = 'w_ajax_popup';	
		recycle = false;
	}

	var opt = {};
	if (a) { // only move window to link position if a link is supplied
		opt.onComplete = function(res) {
			var div = $(wid);
			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();
			if (onComplete) onComplete();
		}
	} else {
		opt.onComplete = function(res) {
			var div = $(wid);
			d.style.top = '10px';
			d.style.left = '10px';
			div.style.display = '';
			if (onComplete) onComplete();
		}
	}
	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';
		d.className = 'w_ajax_popup';
		document.body.appendChild(d);
	}
//	d.innerHTML = 'Loading ...'; // no need because not visible before loaded
	new Ajax.Updater(d.id, url, opt);
}

function ajax_popup_close_by_child(a) {
	var wid;
	var w = get_ancestor(a, 'w_ajax_popup');
	if (w) {
		wid = w.id;
	} else {
		alert('error: elem ' + a + ' has no ancestor with class w_ajax_popup');
		return;
	}
	ajax_popup_close(wid);
}

function ajax_popup_close(wid) {
	if (!wid)
		wid = 'w_ajax_popup';
	document.body.removeChild( $(wid) );
}

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)
{
	// Find the tbody
	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 (the last) NEW_ID in the row's ID.
	var tmp = row.id.split('N');             // row.id example: r_76XpositionsX1089XcontentsXN0
	var id = parseInt(tmp[tmp.length-1]);    // ['r_76XpositionsX1089XcontentsX',0][1]+1 = 1
	var node = row.cloneNode(true);          // (note: only need one step cloning, not full)
	var pre = '';
	for(var i=0; i < (tmp.length-1); i++) {  // re-join possible N steps that were not increased
		if (i != 0) pre += 'N';
		pre += tmp[i];
	}                                       // pre is now r_76XpositionsX1089XcontentsX
	node.id = pre + 'N' + (id+1);           // add the increased value. id is now: r_76XpositionsX1089XcontentsXN1

	// 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');

	// Loop thru all TDs and replace
	var list = node.childNodes;
	var prefix;
	for (var i=0; i<list.length; i++) {
		list[i].innerHTML = list[i].innerHTML.replace(src, rep);
		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;
//if (i == 4) document.getElementById('foodebug').innerHTML = '<pre>' + escape(list[i].innerHTML) + '</pre>';
	}
//alert(prefix);

	// 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;
		if (!inps[j].name.match(pat))
			continue;
		// If hidden input is actually inside field div (hence inside row),
		// then do not clone it, as it has already been handled by regexp in code block above.
		if (has_class(inps[j].parentNode), 't_field')
			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;
		inps2[n] = inp;
		n++;
	}
	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;
	}
}
