/**
 * 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)
 *
 * @note the global namespace is sloooow
 * @note DOMNodeList is "live" so remember to cache the length property
 *       outside the iterating loop
 * @note object.each (callback) is 3-5 times slower than a plain loop
 */


// The eonBIT namespace
var eon = { };

eon.fck_fields = []; // populated by FCKeditor_OnComplete


// ----------------------------------------
// HELPER FUNCTIONS
// ----------------------------------------

// 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!
// A: this will not work!
if (typeof($) === 'undefined') { var $ = document.getElementById; }

// bool Array.contains (item)
Array.prototype.contains = function (item)
{
	return this.indexOf (item) >= 0;
}
/* Need this when nuking the Prototype library. (Or use jQuery.indexOf())
Array.prototype.indexOf = function (item)
{
    for (var i=0, len=this.length; i<len; ++i)
	if (item === this[i]) return i; // @note strict check
    return -1;
}
*/

// string String.trim (string)
String.prototype.trim = function() {
	return this.replace (/^\s+|\s+$/g,"");
}


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);
	}
	// this is what is messing up ajax post in some views at eontyre.
	// no idea why, but chrome needs it the way it's written above!
	// (stian)
	//eon.log = console.log; // "function alias"
} 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()
 */
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;
}

// @todo "forward" to a php exception and reuse existing error reporting?
// @todo if (!str) => get backtrace (if possible)
eon.assert = function (expr, str)
{
	if (expr) return;
	str = str || '(no reason given)';
	console.log ("Assertion failed: "+str);
}


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

	progress_width_max: 490,
	progress_width_start: 2,

	serialize: function(form) {
		if (!form) return '';

		if (form.elements) {

			// Fix for FCKeditor fields.
			if (eon.fck_fields)
			{
				var list = eon.fck_fields;
				for (var i=0, len=list.length; list[i]; ++i) {
					var fck = FCKeditorAPI.GetInstance (list[i]);
					if (fck.IsDirty())
						document.getElementById (list[i]).value = fck.GetHTML (false);
				}
			}

			return Form.serialize(form); // @todo: change to jquery
		// or array of inputs
		} else if (typeof(form) == 'object' && form.length != undefined) {
			return Form.serializeElements(form);
		// or hash/object
		} else if (typeof(form) == 'object' && form.length == undefined) {
			var str = '';
			for (var k in form) {
				if (typeof(form[k]) == 'function')
					continue;
				if (typeof(form[k]) == 'object')
					continue;
				str += encodeURIComponent(k) + '=' + encodeURIComponent(form[k]) + '&';
			}
			return str;
		}
	},

	// Called when a (list) filter is changed. Filter uses GET by default,
	// and falls backs to POST via AJAX if the url is longer than 2048-1
	// characters. (To avoid annoying "Page has expired"-message in IE.)
	//
	// @note _add_filter=2 => clear_filter :)
	//
	// @todo misleading name. it's not an filter function executed on submit
	// @todo Use jquery to be more robust.
	onsubmit_filter: function(form) {

		// This will fail in Chrome+Prototype, even tough it is the
		// correct (w3c) way to do it. getElementsByClassName shall
		// return a NodeList, but looks like prototype overrides (form) and
		// return something else (deprecated in Prototype 1.6).
		// @see http://ejohn.org/blog/getelementsbyclassname-pre-prototype-16/
//		var tr = form.getElementsByClassName ('t_list_filter').item(0);

		// And IE<9 don't have getElementsByClassName, so we must use Prototype
//		var tr = form.getElementsByClassName ('t_list_filter')[0];
		var tr = $(form).getElementsByClassName ('t_list_filter')[0];

		var prefix = tr.getAttribute ('_filter_prefix');
		var op = tr.lastChild.firstChild.value; // fragile. must clean up backend before better solution is possible

		/* @may remove filter from url instead of setting _add_filter=2 (if possible)
		if (op == 2) {
			//form.submit(); // why not working?
			var loc = document.location;
			var url = loc.protocol + '//' + loc.hostname + loc.pathname + 
					  '?' + prefix + '_add_filter=2';
			document.location.href = url;
			return;
		}
		*/

		// Build query string for filter fields
		var qstring = '';
		var node = tr.firstChild;
		eon.assert (node);
		do {
			if (node.nodeType != 1) continue;	// filter on DOMElement
//			var list = node.getElementsByClassName ('t_input');
			var list = $(node).getElementsByClassName ('t_input');
			if (!list.length) continue;			// no filter for this field
			eon.assert (list.length==1);
//			var e = list.item(0); // chrome+prototype bug
			var e = list[0];
			if (!e.value) continue;	// empty value for filter. @todo trim()?
			qstring += encodeURIComponent (e.id) + '=' +
					   encodeURIComponent (e.value) + '&';
		} while (node = node.nextSibling);

		qstring += prefix + '_add_filter=' + op;

		// Manually "post" form with method=get
		var loc = document.location;
		var url = loc.protocol + '//' + loc.hostname +
			      loc.pathname + '?'  + qstring;
		if (url.length < 2048) {
			document.location.href = url;
			return;
		}

		// Fallback to POST when url is to long for GET
		// But must use AJAX so browser don't show "confirm resend"
		// Q: how? not possible to replace whole page
		form.submit();

		// works in ff and chrome, but not IE
		// kind of logical that html.innerHTML it's read only. (body.innerHTML is read/write)
//		document.firstChild.nextSibling.innerHTML = '<p>A test</p>';
	},

	toggle_submit_actions: function(enable) {
		if (!eonvar.action_disable_on_submit)
			return;
		for(var i=0; i < eonvar.action_disable_on_submit.length; i++) {
			var a = document.getElementById(eonvar.action_disable_on_submit[i]);
			if (a) {
				if (enable) {
					a.disabled = false;
				} else {
					a.disabled = true;
				}
			}
		}
	},

	onprogress: function() {
		var url = '/U/base/progress/progress?PHPSESSID=1&ajax=action&id=' + eon.form.AJAX_PROGRESS;
		eon.rpc.call(url, function(res) {
			var el_i = document.getElementById('progress_i');
			var el_t = document.getElementById('progress_t');
			if (res.data.msg)
				el_t.innerHTML = res.data.pct + '%: ' + res.data.msg;
			else if (res.data.pct)
				el_t.innerHTML = res.data.pct + '%';
			else
				el_t.innerHTML = eonvar.text.waiting_for_progress;
			var w = (eon.form.progress_width_max*(res.data.pct/100));
			if (w < 2) w = 2;
			el_i.style.width = w + 'px';
			if (!eon.form.AJAX_PROGRESS)
				return;
			setTimeout('eon.form.onprogress()', 1000);
		});
	},

	onsubmit: function(form) {

		this.toggle_submit_actions(false);

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

		// Progress
		if (eon.form.AJAX_PROGRESS) {
			var prog_inp = document.createElement('input');
			prog_inp.type = 'hidden';
			prog_inp.name = '_progress';
			prog_inp.value = eon.form.AJAX_PROGRESS;
			form.appendChild(prog_inp);

			var h = '<div id="progress_o" style="width: ' + eon.form.progress_width_max + 'px">';
			h += '<div id="progress_i" style="width: ' + eon.form.progress_width_start + 'px">&nbsp;</div>';
			h += '<div id="progress_t" style="width: ' + (eon.form.progress_width_max) + 'px"></div>';
			h += '</div>';
			d1.innerHTML += h;

			//setTimeout('eon.form.onprogress()', 1000);
		}

		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); 
			}

			// Stop progress
			if (eon.form.AJAX_PROGRESS)
				eon.form.AJAX_PROGRESS = null;

			// 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 = eon.form.subst_redir_url_id(
						unescape(result['redirect']), result['_id']
					);
					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);
						}
						eon.form.toggle_submit_actions(true);
						break;
					// Stay and close ajax popup
					case 'close':
						ajax_popup_close();
						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.subst_redir_url_id(eon.form.AJAX_POST, result['_id']);
				}
			// On error, stay and show error message in loading-div
			} else {
				eon.form.toggle_submit_actions(true);
				if (result['status'] == 'login_required') {
					var els = $('saving_outer');
					if (els)
						els.parentNode.removeChild(els);
					popup_login();
				} else {
					elem2.className = 'x_error';
					elem2.innerHTML = result['message'];
					if (result['form_errors']) {
						eon.form.mark_form_errors(result['form_errors']);
					}
				}
			}
		};

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

	// Need to substitute ##ID## if apropriate
	// Backend will make N0 to ##ID## if !action.params.no_id_subst.
	// Equivalent of substitution from mod_base.redirect_action()
	// but for ajax post requests. (sms 2011-08-03)
	subst_redir_url_id: function(url, id) {
		if (!id)
			return url;
		return url.replace('/##ID##/', '/'+id+'/');
	},

	mark_form_errors: function(errors, prefix) {
		if (!prefix)
			prefix = '';
		for(var id in errors) {
			if (typeof(errors[id]) == 'function')
				continue;
			for (var field in errors[id]) {
				if (typeof(errors[id][field]) == 'function')
					continue;
				var msg = errors[id][field];
				if (typeof(msg) == 'object') {
					var prefix2 = '';
					if (prefix)
						prefix2 = prefix + 'X';
					else
						prefix2 = id + 'X' + field;
					eon.form.mark_form_errors(msg, prefix2);
				} else {
					var eid = '_muxX';
					if (prefix)
						eid += prefix + 'X';
					eid += id + 'X' + field;
					var input = document.getElementById(eid);
					var element = get_ancestor(input, 't_element');
					_validate_update_class(element, false, input);
					if (msg) {
						eon.form.set_field_errmsg(input, msg);
/*
						var errmsgid = input.id + '_errmsg';
						var errmsg = document.getElementById(errmsgid);
						if (!errmsg) {
							errmsg = document.createElement('div');
							errmsg.id = errmsgid;
							errmsg.className = 't_field_errmsg';
							input.parentNode.appendChild(errmsg);
						}
						errmsg.innerHTML = msg;
*/
					}
				}
				//var eid = '_muxX' + id + 'X' + field;
			}
		}
	},

	set_field_errmsg: function(input, msg) {
		var errmsgid = input.id + '_errmsg';
		var errmsg = document.getElementById(errmsgid);
		if (!errmsg) {
			errmsg = document.createElement('div');
			errmsg.id = errmsgid;
			errmsg.className = 't_field_errmsg';
			input.parentNode.appendChild(errmsg);
		}
		errmsg.innerHTML = msg;
		errmsg.style.display = '';
	},
	clear_field_errmsg: function(input) {
		var errmsgid = input.id + '_errmsg';
		var errmsg = document.getElementById(errmsgid);
		if (errmsg) {
			errmsg.innerHTML = '';
			errmsg.style.display = 'none';
		}
	},

	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);
			}
		}
	},

	set_new_indicator: function(id) {
		var elem = document.getElementById(id);
		if (elem.type == 'checkbox')
			elem.checked = true;
		else
			elem.value = 1;
	}

}

// ----------------------------------------
// 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, method, synchronous) {
		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 (method)
			opt.method = method;
		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) {
			if (opt.method == 'post')
				opt.postBody = eon.form.serialize(form);
			else
				url += '&' + eon.form.serialize(form);
		}
		if (synchronous)
			opt.asynchronous = false;
		else
			opt.asynchronous = true;
		eon.ajax.request(url, opt); // why do request if no form? (tbl)
	},

	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');
			eon.rpc.display_error({'message':'Empty response (1)'});
			return false;
		}
		var res = response.responseText;
		if (!res) {
			eon.log('RPC error: got no response.responseText');
			eon.rpc.display_error({'message':'Empty response (2)'});
			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); }

// @todo: move to separate shop.js
eon.shop = {

	new_prod_input: null,
	new_prod_type: null,
	new_prod_psid: null,
	new_prod_steps: null,
	new_prod_active: null,
	//pay_mode_onchange_is_start: null,
	//inv_ref_id: null,

	onchange_customer_dupe_check: function(input) {
		var key = input.name.split('X');
		key = key[2];
		var url = '/U/shop_customer/new/dedup?k=' + key + '&v=' + encodeURIComponent(input.value) + '&ajax=action';
		eon.rpc.call(url, function(res) {
			if (res.data) {
				eon.form.set_field_errmsg(input, res['data']);
			}
		});
	},

	onchange_bill_batch_date: function(elem) {
		var tmp = elem.id.split('X');
		var pre = '';
		for(var i=0; i < (tmp.length-1); i++) {
			pre += tmp[i] + 'X';
		}
		var e_from = document.getElementById(pre+'from_date');
		var e_to   = document.getElementById(pre+'to_date');
		var e_deal = document.getElementById(pre+'dealer');
		var e_prev = document.getElementById('x_bill_batch_preview');
		var e_prev_det = document.getElementById('x_bill_batch_preview_details');
		e_prev.innerHTML = 'Loading...';
		e_prev_det.innerHTML = '';
		var url = '/U/order/bill_batch/get_preview?from=' + e_from.value + '&to=' + e_to.value + '&dealer=' + e_deal.value;
		eon.rpc.call(
			url,
			function(res) {
				if (!res.result) {
					alert(res.message);
					return;
				}
				e_prev.innerHTML = res.data;
			}
		);
	},

	preview_bill_batch: function(from_date, to_date, dealer, is_cancel) {
		//var el = document.getElementById('x_bill_batch_preview_details');
		//el.innerHTML = 'Loading...';
		var url = '/U/order/list_to_bill?ajax=1&from=' + from_date + '&to=' + to_date + '&dealer=' + dealer + '&is_cancel=' + is_cancel;
        //new Ajax.Updater(el, url);
		ajax_popup(null, url, null, null, null, {width: 900, height: 600});
	},

	parcel_pending_onchange: function(elem, key) {
		var sel_e = document.getElementById(
			elem.id.replace('X'+key, 'Xselector')
		);
		var num_e = document.getElementById(
			elem.id.replace('X'+key, 'Xnumber')
		);
		var pos_e = document.getElementById(
			elem.id.replace('X'+key, 'Xpos_id')
		);
		if (pos_e.value && parseInt(num_e.value) > 0)
			sel_e.checked = true;
		else
			sel_e.checked = false;
	},

	parcel_pending_selector_onclick: function(elem) {
		var key = 'selector';
		var num_e = document.getElementById(
			elem.id.replace('X'+key, 'Xnumber')
		);
		var pos_e = document.getElementById(
			elem.id.replace('X'+key, 'Xpos_id')
		);
		if (!pos_e.value || parseInt(num_e.value) < 1) {
			if (elem.checked) {
				alert(eonvar.text.must_select_pos_and_num);
				elem.checked = false;
			}
		}
	},

	pay_manual_mode_onchange: function(elem) {
		var pre = elem.id.replace('Xpay_mode', 'X');
		var val = elem.value;
		elem = document.getElementById(pre + 'notify_mail');
//alert(val);
		if (val != 1 && val != 3 && val != 5 && val != 7 && val != 8)
			elem.checked = true;
		else
			elem.checked = false;
	},

	inventory_reg_no_onkeypress: function(event, el) {
		if (event.keyCode != 13)
			return true;
		el.blur();
		//eon.shop.inventory_reg_no_onchange(el);
		//eon.shop._inv_reg_no_open = true;
		return false;
	},
	inventory_reg_no_onblur: function(elem) {
		var inv_ref_elem;
		var n = 0;
		while (true) {
			var tmp = document.getElementById(
				elem.id.replace('Xreg_no','XcontentsXN' + n + 'Xref')
			);
			if (!tmp)
				break;
			inv_ref_elem = tmp;
			n++;
		}
		eon.shop.inv_ref_elem = inv_ref_elem;
		ajax_popup(
			elem, 
			'/U/stock_inventory/reg_no_list?ajax=1&reg_no=' + elem.value,
			null, null, null,
			{width: 500, height: 400}
		);
	},
/*
	inventory_reg_no_onchange_submit: function(elem) {
		document.getElementById(this.inv_ref_id).value = elem.innerHTML;
		eon.shop.inventory_regno_close();
	},
*/
	inventory_reg_no_close: function(elem) {
		ajax_popup_close();
		//eon.shop._inv_reg_no_open = false;
	},
/*
	inventory_reg_no_show_btn: function(elem) {
		alert(elem.parentNode.parentNode.className);
	},
*/
	inventory_reg_no_choose: function(elem, type, reg_no) {

		var ids = [];
		var prods = {};
		for(var i=0; i < elem.form.elements.length; i++) {
			var e = elem.form.elements[i];
			var n = e.name.split('X');
			if (n[0] != '_mux') continue;
			if (type == 'selected') {
				if (n[2] == 'selector' && e.checked)
					ids[ids.length] = n[1];
			} else {
				if (n[2] == 'season') {
					if (e.value == 1 && type == 'summer')
						ids[ids.length] = n[1];
					else if (e.value == 2 && type == 'winter')
						ids[ids.length] = n[1];
				}
			}
			if (n[2] == 'product') {
				//prods[n[1]] = e.value; // this is id
				prods[n[1]] = '';
				var tmp = e.parentNode;
				if (tmp) {
					tmp = tmp.childNodes[0];
					if (tmp)
						prods[n[1]] = tmp.nodeValue;
				}
			}
		}
		if (ids.length < 1) {
			if (type == 'selected')
				alert('No wheels selected');
			else
				alert('No ' + type + ' wheels');
			return;
		}

		elem.value = 'Loading...';
		elem.disabled = true;

		var ref_el = eon.shop.inv_ref_elem;
		var typ_el = document.getElementById(
			ref_el.id.replace('Xref', 'Xtype')
		);
		var first_tr = false;
		if (ref_el.value == '' && typ_el.value == '')
			first_tr = ref_el.parentNode.parentNode.parentNode;
		var tbody = ref_el.parentNode.parentNode.parentNode.parentNode;

		var tr;
		for(var i=0; i < ids.length; i++) {
			if (i == 0 && first_tr)
				tr = first_tr;
			else
				tr = _table_add_row(tbody, true, true);
			var pre = tr.id.replace(/^r_/, '_muxX'); // + 'Xref';
			var ref_el = document.getElementById(pre + 'Xref');
			var typ_el = document.getElementById(pre + 'Xtype');
			ref_el.value = ids[i];
			typ_el.value = 'tyre_hotel';
			typ_el.onchange();

/* todo: does not work properly
         because prod/num field is usually editable and used for type=stock :\
			for(var j=0; j < tr.childNodes.length; j++) {
				var td = tr.childNodes[j];
				if (td.tagName != 'TD')
					continue;
				var tddiv;
				for(var k=0; k < td.childNodes.length; k++) {
					if (has_class(td.childNodes[k], 't_field')) {
						tddiv = td.childNodes[k];
						break;
					}
				}
				if (has_class(td, 'f_reg_no')) {
					tddiv.innerHTML = reg_no;
				} else if (has_class(td, 'f_product_id')) {
					tddiv.innerHTML = prods[ ids[i] ];
					tddiv.style.display = '';
				} else if (has_class(td, 'f_number')) {
					tddiv.innerHTML = '1';
					tddiv.style.display = '';
				}
			}
*/

		}

		eon.shop.inventory_reg_no_close();
	},

	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';
		}
	},

	delti_setup_product_onchange: function(elem) {
		var prefix = elem.id.replace('Xproduct_id', 'X');
		var url = '/' + elem.value + '/product/prod_cost/prod_cost?currency=';
		url = url + document.getElementById(prefix + 'product_currency').value;
		var cost_elem = document.getElementById(prefix + 'product_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_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');
		if (pe)
			pe = pe.parentNode;
		var ce = document.getElementById(pre + 'cost');
		if (ce)
			ce = ce.parentNode;
		var re = document.getElementById(pre + 'ref');
		var ne = document.getElementById(pre + 'number');
		if (elem.value == 'stock') {
			if (pe)
				pe.style.display = 'block';
			if (ce)
				ce.style.display = 'block';
			if (re)
				re.style.display = 'none';
			if (ne)
				ne.parentNode.style.display = 'block';
		} else {
			if (pe)
				pe.style.display = 'none';
			if (ce)
				ce.style.display = 'none';
			if (re)
				re.style.display = '';
			if (ne) {
				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);
			if (newelem)
				newelem.checked = true;
			if (cb) {
				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) {

		// Update customer data
		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','email'];
			}
			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;
			}
/*
alert(2);
			var el_chk = document.getElementById(prefix + 'upd_cust');
alert(3);
			if (cuel && el_chk) {
alert(66);
				if (cuel.value)
					el_chk.disabled = false;
				else
					el_chk.disabled = true;
			}
alert(4);
*/
			var ref_el = document.getElementById(prefix + 'invoice_reference');
			if (data['use_invoice_reference'] != 0 && data['use_invoice_reference'] != '0') {
				ref_el.parentNode.parentNode.style.display = '';
				if (data['use_invoice_reference'] == 2)
					eon.form._set_required_field(ref_el, true);
				else
					eon.form._set_required_field(ref_el, false);
			} else {
				ref_el.parentNode.parentNode.style.display = 'none';
			}
		});

		// Check invoice allowed
		if (is_cust)
			this.checkout_check_pay_terms(prefix);
	},

/* deprecated
	cust_new_type_onchange: function(elem) {
		var pre = elem.id.replace('Xtype', 'X');
		var e_inv = document.getElementById(pre + 'invoice_allowed');
		if (elem.value == 1) {
			e_inv.checked = true;
		} else {
			e_inv.checked = false;
		}
	},
*/
	checkout_new_cust_onclick: function(elem) {
		var pre = elem.id.replace('Xnew_cust', 'X');
		var e_ia = document.getElementById(pre + 'save_cust_allow_invoice').parentNode.parentNode.parentNode;
		if (elem.checked) {
			e_ia.style.display = '';
		} else {
			e_ia.style.display = 'none';
		}
		eon.shop.checkout_check_pay_terms(pre);
	},
	checkout_upd_cust_onclick: function(elem) {
		var pre = elem.id.replace('Xupd_cust', 'X');
		var e_ia = document.getElementById(pre + 'save_cust_allow_invoice').parentNode.parentNode.parentNode;
		if (elem.checked) {
			e_ia.style.display = '';
		} else {
			e_ia.style.display = 'none';
		}
		eon.shop.checkout_check_pay_terms(pre);
	},
	checkout_save_cust_allow_invoice_onclick: function(elem) {
		var pre = elem.id.replace('Xsave_cust_allow_invoice', 'X');
		eon.shop.checkout_check_pay_terms(pre);
	},

	checkout_check_pay_terms: function(prefix) {

		if (!eon.shop.pay_mode_onchange_is_start) {
			var tmp = document.getElementById('action_msg_is_pay_terms_err');
			if (tmp)
				document.getElementById('action_message').style.display = 'none';
		}

//alert('mha: '+eon.shop.pay_mode_onchange_is_start);
		var e_cust = document.getElementById(prefix + 'customer_id');
		if (!e_cust.value && eon.shop.pay_mode_onchange_is_start)
			return;

		var e_pay  = document.getElementById(prefix + 'pay_mode');
		var is_invoice = false;	
		for(var i=0; i < eonvar.invoice_pay_modes.length; i++) {
			if (eonvar.invoice_pay_modes[i] == e_pay.value) {
				is_invoice = true;
				break;
			}
		}
		var url = '/U/order/rpc/check_pay_terms?ajax=action&cust=' + e_cust.value;
		eon.rpc.call(url, function(res) {
			if (!res.data.active) {
				eon.shop.checkout_set_pay_terms_msg('customer_inactive');
				return;
			}
			if (!is_invoice) {
				eon.shop.checkout_set_pay_terms_msg(false);
				return;
			}
			var e_new_cust = document.getElementById(prefix + 'new_cust');
			var e_upd_cust = document.getElementById(prefix + 'upd_cust');
			var e_save_cust_ia = document.getElementById(prefix + 'save_cust_allow_invoice');
			if ((!res.data.invoice_allowed || res.data.invoice_allowed=='0') && !((e_new_cust.checked||e_upd_cust.checked) && e_save_cust_ia.checked)) {
				eon.shop.checkout_set_pay_terms_msg('invoice_not_allowed');
			} else if (res.data.invoice_limit_order_over) {
				eon.shop.checkout_set_pay_terms_msg('exceeds_invoice_limit_order');
			} else if (res.data.invoice_limit_month_over) {
				eon.shop.checkout_set_pay_terms_msg('exceeds_invoice_limit_month');
			} else {
				eon.shop.checkout_set_pay_terms_msg(false);
			}
		});
	},

	checkout_set_pay_terms_msg: function(type) {

		var text = '';
		if (type) {
			var wiki = null;
			if (type == 'customer_inactive') wiki = 'CustomerInactive';
			else if (type == 'invoice_not_allowed') wiki = 'InvoiceNotAllowed';
			else if (type == 'exceeds_invoice_limit_order') wiki = 'ExceedsInvoiceLimitOrder';
			else if (type == 'exceeds_invoice_limit_month') wiki = 'ExceedsInvoiceLimitMonth';
			if (wiki) {
				wiki = '/U/' + eonvar.pay_terms_wiki_module + '/apopup?ajax=1&page=CheckoutHelp' + wiki;
				wiki = "ajax_popup(this, '" + wiki + "')";
				wiki = ' <a href="javascript:noop()" onclick="' + wiki + '">';
				wiki += '<img src="/kernel/images/action/help.gif"/></a>';
			}
			var text = eonvar.text[type] + wiki;
		}

		var e_suf_cust = document.getElementById('checkoutCustSuffix');
		e_suf_cust.innerHTML = text;
		if (type != 'customer_inactive') {
			var e_suf_pay  = document.getElementById('checkoutPayModeSuffix');
			e_suf_pay.innerHTML = text;
		}
	},

	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;
			}	
		});
	}
*/
};

eon.booking = {}; // other js files will add stuff inside

// @todo: move to separate file (together with shop?)
// @todo: move stuff from eon.shop to here
eon.stock = {

	deliv_notes_out_container_parent: null,
	deliv_notes_in_container_parent: null,

	inventory_timeout_onchange: function(elem) {
		var pre = elem.id.replace('Xtimeout_min', '');
		var elem2 = document.getElementById(pre + 'Xtimeout_warn_min');
		if (elem2.value == '' && elem.value)
			elem2.value = elem.value;
	},

	transport_front_onchange: function(elem) {
		var elems = {};
		//var keys = ['send_wait','send_recv','send_sent','recv_wait','recv_sent','recv_recv'];
		var keys = ['out_wait','out_sent','out_recv','out_cancel','in_wait','in_sent','in_recv','in_cancel'];
		for (var i=0; i < keys.length; i++) {
			elems[keys[i]] = document.getElementById('tsp_num_' + keys[i]);
			elems[keys[i]].innerHTML = '&nbsp;';
		}
		var cb = function(res) {

			// Check numbers and find elements to set OK (done) class for
			var d = res['data'];
			var ok = {};
			var keys2 = ['out','in'];
			for (var i=0; i < keys2.length; i++) {
				var k = keys2[i];
				if (d[k+'_wait'] == 0)
					ok[k+'_wait'] = true;
				if (d[k+'_sent'] == d[k+'_all'])
					ok[k+'_sent'] = true;
				if (d[k+'_recv'] == d[k+'_all'])
					ok[k+'_recv'] = true;
			}

			// Update numbers from RPC result and set OK classes
			for (var i=0; i < keys.length; i++) {
				elems[keys[i]].innerHTML = d[keys[i]];
				if (ok[keys[i]])
					add_class(elems[keys[i]], 'x_ok');
				else
					remove_class(elems[keys[i]], 'x_ok');
			}

			// Get field prefix
			var pre0 = elem.id.split('X');
			var pre = '';
			for (var i=0; i < pre0.length-1; i++) {
				pre += pre0[i] + 'X';
			}

			// Get locations and date from inputs
			var my_loc = document.getElementById(pre + 'my_location').value;
			var other_loc = document.getElementById(pre + 'other_location').value;
			var dt_from;
			var dt_to;
			dt_from = document.getElementById(pre + 'from_date');
			dt_to = document.getElementById(pre + 'to_date');
			if (dt_from && dt_to) {
				dt_from = dt_from.value;
				dt_to = dt_to.value;
			} else {
				dt_from = document.getElementById(pre + 'date').value;
				dt_to = dt_from;
			}

			// Load send lists
			var types = ['out','in'];
			for (var i=0; i < types.length; i++) {
				var url = eonvar['transport_deliv_notes_' + types[i] + '_url_base'];
				url += '?ajax=1&submit=1&my_location=' + escape(my_loc) + '&other_location=' + escape(other_loc) + '&from_date=' + escape(dt_from) + '&to_date=' + escape(dt_to);
				var cpkey = 'stock_deliv_notes_' + types[i] + '_parent';
				if (!eon.stock[cpkey])
					eon.stock[cpkey] = document.getElementById(eonvar['transport_deliv_notes_' + types[i] + '_container_id']);
				ajax_load_to_container(
					eon.stock[cpkey],
					url,
					null,
					{evalScripts: false}
				);
			}
		};
		eon.rpc.call(
			'/U/transport_front/front/rpc_stats',
			cb,
			elem.form
		);
	},

	transport_remember_locations: function() {
		var cb = function(res) {
			alert('Ok, now remembering your locations');
		};
		eon.rpc.call(
			'/N0/transport_front/front/rpc_remember',
			cb,
			document.getElementById('mainform')
		);
	},

	transport_goto_advanced: function() {
		var form = document.getElementById('mainform');
		var url = '/U/transport_front/front_adv?';
		var data = {};
		for (var i=0; i < form.elements.length; i++) {
			var el = form.elements[i];
			var tmp = el.name.split('X');
			data[tmp[2]] = el.value;
		}
		url += 'my_location=' + data['my_location'];
		url += '&from_date=' + data['date'];
		url += '&to_date=' + data['date'];
		document.location = url;
	},

	transport_copy_pos_by_reg_no: function(a) {
		var tr = get_ancestor(a, 't_row');
		var idbase = tr.id.substr(2);
		var pos = document.getElementById('_muxX' + idbase + 'Xto_position_id').value;
/* allow blank pos copy
		if (!pos) {
			alert('No position to copy from');
			return;
		}
*/
		var tbl = tr.parentNode;
		var started = false;
		var reg_no = null;
		for (var i=0; i < tbl.rows.length; i++) {
			if (tr == tbl.rows[i]) {
				started = 2;
			}
			if (started) {
				for (var j=0; j < tbl.rows[i].cells.length; j++) {
					var td = tbl.rows[i].cells[j];
					if (has_class(td, 'f_reg_no')) {
						var el = td.childNodes[0];
//alert('regno.inner: ' + el.innerHTML);
						if (started == 2) {
							reg_no = el.innerHTML;
							if (reg_no == '' || reg_no == '&nbsp;') {
								alert('No reg no to copy for');
								return;
							}
//alert('found first reg no: ' +reg_no);
						} else if (el.innerHTML != reg_no) {
//alert('break coz nonfirst regno is not equal: ' + el.innerHTML + ' != ' +reg_no);
							started = 3;
							break;
						}
					} else if (has_class(td, 'f_to_position_id')) {
						var el = td.childNodes[0];
						if (started != 2) {
							el = el.childNodes[0];
//alert('set pos for nonfirst pos: ' + el.className + ' = ' + pos);
							for (var k=0; k<el.options.length; k++) {
								if (el.options[k].value == pos) {
									el.selectedIndex = k;
									el.onchange();
									break;
								}
							}
						}
					}
				}
				if (started == 3)
					break;
				if (started == 2)
					started = true;
			}
		}
	},

	transport_to_position_onchange: function(elem) {
		var elem2 = document.getElementById(
			elem.id.replace('Xto_position_id', 'Xselector')
		);
		if (elem.value)
			elem2.checked = true;
		else
			elem2.checked = false;
	}

};

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

eon.datatype = {};

eon.datatype.select = {

	new_popup_elem: null,
	new_save_cb_input_ids: null,
	new_save_cb_input_id: null,
	new_save_cb_value: null,
	new_idx: 0,
	new_data: {},

	new_popup: function(url, elid, a, mod, view, id, field, restrict_from_field, ap_opt) {
		eon.datatype.select.new_idx++;
		var idx = eon.datatype.select.new_idx;
		var tmp = {
			inid: elid,
			input: document.getElementById(elid),
			module: mod,
			view: view,
			id: id,
			field: field,
			restrict_from_id: null,
			restrict_from_input: null,
			wid: 'w_ajax_popup_selnew_' + idx
		};
		if (restrict_from_field) {
			tmp.restrict_from_id = elid.replace(field, restrict_from_field);
			tmp.restrict_from_input = document.getElementById(tmp.restrict_from_id);
			url = url.replace('###RESTRICT_VAL###', tmp.restrict_from_input.value);
		}
		url = url.replace('###POP_ID###', idx);
		eon.datatype.select.new_data[idx] = tmp;
		//ajax_popup(a, url, null, 'w_ajax_popup_sel_new', null, ap_opt);
		ajax_popup(a, url, null, tmp.wid, null, ap_opt);
	},

	new_save_cb_ax: function(res) {
		if (!res['result'])
			return;
		var pop_id = parseInt(res['pop_id']);
		var tmp = eon.datatype.select.new_data[pop_id];
		ajax_popup_close(tmp.wid);
		var vinput = document.getElementById(tmp.input.id);
		vinput.value = res['_id'];
		var tinput = document.getElementById('_ax_' + vinput.id);
		tinput.value = res['data'];
		remove_class(tinput, 'new');
		vinput.onchange();
	},

	new_save_cb: function(res) {
		if (!res['result'])
			return;
		var pop_id = parseInt(res['pop_id']);
		var tmp = eon.datatype.select.new_data[pop_id];
		eon.datatype.select.new_save_cb_input_ids = get_similar_input_ids(tmp.input.id, 'select');
		eon.datatype.select.new_save_cb_input_id = 0;
		eon.datatype.select.new_save_cb_value = res['_id'];
		eon.datatype.select.new_save_cb_iterate(tmp);
	},

	new_save_cb_get_cur_input: function() {
		var id = eon.datatype.select.new_save_cb_input_ids[ eon.datatype.select.new_save_cb_input_id ];
		return document.getElementById(id);
	},

	new_save_cb_iterate: function(tmp) {

		var input = eon.datatype.select.new_save_cb_get_cur_input();
		if (!input)
			return;

		// get id for def to make_url below
		var idtmp = input.id.split('X');
		var item_id = idtmp[ idtmp.length-2 ];
		// and save value for inputs not setting to new value
		var old_val = input.value;

		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(tmp, item_id);
		if (eon.datatype.select.new_save_cb_input_id == 0)
			ajax_popup_close(tmp.wid);

        new Ajax.Updater(el, url, {
            onComplete: function() {
				var input = eon.datatype.select.new_save_cb_get_cur_input();
				var select_val = null;
				if (input.id == tmp.inid) {
					select_val = eon.datatype.select.new_save_cb_value;
				} else {
					select_val = old_val;
				}
				for (var i=0; i < input.options.length; i++) {
					if (input.options[i].value == select_val) {
						input.selectedIndex = i;
						break;
					}
				}
				if (select_val != old_val && input.onchange)
					input.onchange();
				eon.datatype.select.new_save_cb_input_id += 1;
				eon.datatype.select.new_save_cb_iterate();
            }
		});
	},

	make_url: function(def, item_id) {
		var res_param = '';
		if (def.restrict_from_input)
			res_param = 'restrict_val=' + def.restrict_from_input.value;
		return '/' + item_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_or_rim_dimension = {
	validate: function(el) {
		if (el.value.match(/^[0-9][0-9]?$/))
			return el.value;
		return eon.datatype.tyre_dimension.validate(el);
	}
}

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'];
	}
}

eon.datatype.barcode = {

	config: {}, // config: {field1: {conf}, field2: {conf}, ...}
	data: {},   // data:   {field1: [data], field2: [data], ...}
	data_input: {},

	onkeypress: function(e, input) {
		var id = input.id.replace(/\-input$/,'');
		var conf = this.config[id];
        var suf = document.getElementById(conf['suffix_id']);
		var res = document.getElementById(conf['result_id']);
		if (!this.data_input[id])
			this.data_input[id] = document.getElementById(conf['data_id']);
        if (suf)
			suf.innerHTML = '';
        var keynum;
        if(window.event) // IE
            keynum = e.keyCode;
        else if(e.which) // Netscape/Firefox/Opera
            keynum = e.which;
        if (this.isEnterAndNotTerminator(keynum, conf))
            return false;
        var keychar = String.fromCharCode(keynum);
        if (!this.isTerminator(keynum, conf))
            return true;
        var txt = input.value;
		if (!this.isEnter(keynum))
			txt += keychar;
        this.oncomplete(txt, suf, res, conf);
        input.value = '';
        return false;
	},

	load: function(value, id) {
		var conf = this.config[id];
        var suf = document.getElementById(conf['suffix_id']);
		var res = document.getElementById(conf['result_id']);
		if (!this.data_input[id])
			this.data_input[id] = document.getElementById(conf['data_id']);
        if (suf)
			suf.innerHTML = '';
        this.oncomplete(value, suf, res, conf, true);
	},

	loadMulti: function(values, id) {
		var conf = this.config[id];
        var suf = document.getElementById(conf['suffix_id']);
		var res = document.getElementById(conf['result_id']);
		if (!this.data_input[id])
			this.data_input[id] = document.getElementById(conf['data_id']);
        if (suf)
			suf.innerHTML = '';
        this.oncomplete(values, suf, res, conf, true, true);
	},

	isTerminator: function(keynum, conf) {
		if (conf['terminator']) {
        	var keychar = String.fromCharCode(keynum);
			if (keychar == conf['terminator'])
				return true;
		} else {
			if (keynum == 13) // enter
				return true;
		}
	},

	isEnterAndNotTerminator: function(keynum, conf) {
		if (conf['terminator'] && this.isEnter(keynum))
			return true;
		return false;
	},

	isEnter: function(keynum) {
		if (keynum == 13)
			return true;
		return false;
	},

	oncomplete: function(val, e_suf, e_res, conf, isLoad, isMulti) {
		if (conf['unique'] && this.exists(conf['id'], val)) {
			e_suf.innerHTML = eonvar.text.barcode_already_scanned;
			e_suf.className = 'x_error';
			return false;
		}
		if (isMulti) {
			val = val.join(',');
		}
		var url = conf['callback'] + val;
		if (isLoad)
			url += '&load=' + isLoad;
		if (isMulti)
			url += '&multi=' + isMulti;
        eon.rpc.call(
            url,
            function(res) {
                if (!res.result) {
                    e_suf.innerHTML = res.message;
					e_suf.className = 'x_error';
                    return;
                }

				var items;
				if (isMulti) {
					items = res.data;
				} else {
					items = [res.data];
				}

				for (var j=0; j < items.length; j++) {

					var tr = document.createElement('tr');
					tr.id = conf['id'] + '-tr-' + items[j]['_barcode'];
					for(var i=0; i < conf['columns'].length; i++) {
						var k = conf['columns'][i];
						//var v = res.data[k];
						var v = items[j][k];
						if (!v) v = '';
						var td = document.createElement('td');
						td.innerHTML = v;
						tr.appendChild(td);
					}

					var td = document.createElement('td');
					td.innerHTML = '<a onclick="eon.datatype.barcode.remove(\'' + conf['id'] + '\', \'' + items[j]['_barcode'] + '\')">Remove</a>';
					tr.appendChild(td);

					e_res.appendChild(tr);

					eon.datatype.barcode.add(conf['id'], items[j]['_barcode']);
					if (!isLoad) {
						e_suf.innerHTML = eonvar.text.barcode_scanned;
						e_suf.className = 'x_ok';
					}
				}
            }
        );
	},

	remove: function (i_id, val) {
		var data2 = [];
		var hid = '';
		for(var i=0; i < this.data[i_id].length; i++) {
			var v = this.data[i_id][i];
			if (v != val) {
				data2[data2.length] = v;
				hid += v + "\n";
			}
		}
		this.data[i_id] = data2;
console.log(hid); // tuba
		this.data_input[i_id].value = hid;
		var tr = document.getElementById(i_id + '-tr-' + val);
		tr.parentNode.removeChild(tr);
	},
	add: function(i_id, val) {
		if (!this.data[i_id])
			this.data[i_id] = [];
		this.data[i_id][ this.data[i_id].length ] = val;
		this.data_input[i_id].value += val + "\n";
	},
	exists: function(i_id, val) {
		if (!this.data[i_id])
			return false;
		for (var i=0; i < this.data[i_id].length; i++) {
			if (this.data[i_id][i] == val)
				return true;
		}
		return false;
	}

}

eon.datatype.comments = {
	offsets: {},
	attrib_visible: false,
	add: function(id) {
		var el = document.getElementById('x_add_comment-' + id);
		el.style.display = 'block';
		var ta = document.getElementById('x_comment_text-' + id);
		ta.focus();
	},
	submit: function(id) {
		var ta = document.getElementById('x_comment_text-' + id);
		if (!ta.value) {
			alert(eonvar.text.please_enter_comment_body);
			return;
		}
		var mi = document.getElementById('x_comment_params-' + id);
		mi = mi.innerHTML.split(':');
		var cb = function(res) {
			var items = document.getElementById('x_comment_items-' + id);
			var obj = document.createElement('div');
			obj.innerHTML = res.data.html;
			items.insertBefore(obj, items.firstChild);
			eon.datatype.comments.close(id);
		};
		var params = {
			'module': mi[0],
			'id': mi[1],
			'comment': ta.value,
			'input_id': id
		};
		var fields = this.get_attrib_fields(id);
		for (var i=0; i < fields.length; i++) {
			var a_el = document.getElementById(fields[i][2]);
			var v = null;
			if (a_el.type == 'checkbox')	
				v = (a_el.checked) ? 1 : 0;
			else
				v = a_el.value;
			params['attrib_' + fields[i][0]] = v;
		}
		eon.rpc.call('/N0/comment/rpc/submit', cb, params, null, 'get');
	},
	cancel: function(id) {
		this.close(id);
	},
	close: function(id) {
		var ta = document.getElementById('x_comment_text-' + id);
		ta.value = '';
		var el = document.getElementById('x_add_comment-' + id);
		el.style.display = 'none';
	},
	more: function(id) {
		var cb = function(res) {
			var items = document.getElementById('x_comment_items-' + id);
			for(var i=0; i < res.data.html.length; i++) {
				var obj = document.createElement('div');
				obj.innerHTML = res.data.html[i];
				items.appendChild(obj);
			}
			var e_num = document.getElementById('x_comment_more_num-' + id);
			if (res.data.num == 0) {
				e_num.innerHTML = 'No';
				var a_num = document.getElementById('x_comment_more-' + id);
				//a_num.onclick = function(){};
				a_num.parentNode.removeChild(a_num);
			} else {
				e_num.innerHTML = res.data.num;
			}
			
		};

		var mi = document.getElementById('x_comment_params-' + id);
		mi = mi.innerHTML.split(':');
		mi[2] = parseInt(mi[2]);

		if (!this.offsets[id])
			this.offsets[id] = 0;
		this.offsets[id] += mi[2]; // todo: get from backend

		var params = {
			'module': mi[0],
			'id': mi[1],
			'numposts': mi[2],
			'offset': this.offsets[id],
			'input_id': id
		};
		eon.rpc.call('/N0/comment/rpc/more', cb, params, null, 'get');
	},
	del: function(input_id, comment_id) {
		if (!confirm(eonvar.text.del_comment_are_you_sure))
			return;
		eon.rpc.call('/N0/comment/rpc/del?id=' + comment_id, function(res) {
			if (res.result) {
				var e_comment = document.getElementById('x_comment-' + comment_id);
				if (e_comment)
					e_comment.parentNode.removeChild(e_comment);
				if (!eon.datatype.comments.offsets[input_id]) {
					eon.datatype.comments.offsets[input_id] = 0;
				}
				eon.datatype.comments.offsets[input_id] -= 1;
			}
		});
	},
	onclick_attrib_more: function(a, input_id) {
		this.attrib_visible = !this.attrib_visible;
		var fields = this.get_attrib_fields(input_id);
		for (var i=0; i < fields.length; i++) {
			if (fields[i][3])
				continue;
			var a_el = document.getElementById(fields[i][1]);
			if (this.attrib_visible) {
				a_el.style.display = '';
			} else {
				a_el.style.display = 'none';
			}
		}
		if (this.attrib_visible) {
			a.innerHTML = eonvar.text.comment_attrib_less;
		} else {
			a.innerHTML = eonvar.text.comment_attrib_more;
		}
	},
	get_attrib_fields: function(input_id) {
		var fields = eonvar['comment-toggle-attrib-' + input_id];
		if (fields)
			return fields;
		else
			return [];
	}
}

eon.datatype.car_reg_no = {
	onchange: function(elem) {
		var val = elem.value;
		val = val.toUpperCase();
		val = val.replace(/[^A-Z0-9]/g, '');
		elem.value = val;
	}
}


// === 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();
	}
}

eon.gallery = {
	onmouseover: function(id) {
		if (eonvar['gallery_image_action_hover']) {
			var e_act = eon.gallery.get_act_elem(id);
			e_act.style.display = 'block';
		}
	},
	onmouseout: function(id) {
		if (eonvar['gallery_image_action_hover']) {
			var e_act = eon.gallery.get_act_elem(id);
			e_act.style.display = 'none';
		}
	},
	get_act_elem: function(id) {
		return document.getElementById('x_gallery_act_' + id);
	}
}


// used by media_selected()
// Q: why own function. only used once
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_related(from_elem, to_id, url, trigger_onchange) {
	url = url.replace('###VALUE###', from_elem.value); // todo: ugly... is there a better way? see also datatype.make_select_input()
	if (!from_elem.value)
		return;
	var to_elem = document.getElementById(to_id);
	eon.rpc.call(url, function(res) {
		if (res.type != 'ok')
			return;
		var to_idx_before = to_elem.selectedIndex;
		for(var i=0; i < to_elem.options.length; i++) {
			if (to_elem.options[i].value == res.data) {
				to_elem.selectedIndex = i;
				if (trigger_onchange && to_idx_before != to_elem.selectedIndex)
					to_elem.onchange();
				break;
			}
		}
	});
}

function select_restrict_ajax(from_elem, to_id, url, trigger_onchange) {
	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);
	var to_value_old = eonvar['select_restrict_default_' + to_elem.name];
	//to_elem.innerHTML = '<option value="">Loading...</option>';
	var to_value_old_real = to_elem.value;
	var to_opt = eonvar['select_restrict_opt_' + to_elem.name];
	var a_el = document.getElementById('sel_new_a_' + to_elem.id);
	if (!from_elem.value) {
		if (!to_opt.all_if_none) {
			to_elem.options.length = 0;
			to_elem.options[0] = new Option('(' + eonvar.text.select_above + ')', '');
			if (trigger_onchange && to_value_old_real)
				to_elem.onchange();
			to_elem.disabled = true;
			if (a_el) 
				a_el.style.display = 'none';
			return;
		}
	}
	to_elem.disabled = false;
	if (a_el) 
		a_el.style.display = '';
	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;
		var sel_idx = null;
		var sel_idx_backend = null;
		var new_value = null;
		for(var i=0; i < res.data.length; i++) {
			var opt = res.data[i];
			to_elem.options[i] = new Option(opt['name'], opt['_id']);
			if (to_value_old == opt['_id']) {
				sel_idx = i;
				new_value = opt['_id'];
			}
			if (opt['selected'] == 1)
				sel_idx_backend = i;
		}
		if (sel_idx_backend)
			to_elem.selectedIndex = sel_idx_backend;
		else if (sel_idx)
			to_elem.selectedIndex = sel_idx;
		if (trigger_onchange && to_value_old_real != new_value)
			to_elem.onchange();
	});
}

var select_hold = new Array();
function select_restrict(obj, sid, use_ajax_url, trigger_onchange){
	if (use_ajax_url) {
		select_restrict_ajax(obj, sid, use_ajax_url, trigger_onchange);
		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)
 */
// ajax_params is used to optionally pass params the mod_media::action_ajax.
// This action handler then renders the «add button» action (with the
// parameters), that again can be picked up by mod_image::action_update().
// Currently it's used to pass category or category_id to force an image
// to a specific category.
//function media_get(media, field, id, multi, hide_name)
function media_get(media, field, id, multi, hide_name, ajax_params, run_onchange)
{
	if (!id)
		id = 0; // what is this for?

	var params = {
		value:		$(field).value,
		field:		field,
		multi:		multi,
		hide_name:	hide_name
	};
	if (ajax_params)
		params = Object.extend (params, ajax_params); // jQuery: $.extend

	new Ajax.Request ('/'+id+'/mod_'+media+'/ajax/ajax', {
		parameters: params,
		_field: field,
		onSuccess: function(res) { media_show(res, multi, run_onchange); },
		onFailure: media_error
//		onException: media_exception
	});
}

var media_show = function(t, multi, run_onchange) {
	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;
	if (run_onchange)
		$(field).onchange();

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

/* disabled 'cause media_make_sortable() triggers error when list is empty
var media_exception = function(request, message) {
	$('_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,null,null,true);
	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,null,null,true);
}
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,{});
}



// ----------------------------------------
// FCK EDITOR
// ----------------------------------------

// Called when editor loading phase is complete
function FCKeditor_OnComplete (instance)
{
	eon.fck_fields.push (instance.Name);
	// called on post (but after ajax serialization, i.e., to late)
//	instance.Events.AttachEvent ('OnAfterLinkedFieldUpdate', foo);
}


/**
 * 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, input);
	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, input) {
	// nb: change_class will add the second class even though the first is not found
        if ( result ) {
                change_class(element, 'x_formerror', 'x_validated');
				eon.form.clear_field_errmsg(input);
/*
				var errmsgid = input.id + '_errmsg';
				var errmsg = document.getElementById(errmsgid);
				if (errmsg)
					errmsg.innerHTML = '';
*/
        } 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 new_indicator = $( prefix + 'X_new' );
		if (new_indicator) {
			if (new_indicator.type == 'checkbox') {
				if (new_indicator.checked)
					result.new_row = 1; // checked checkbox indicator
				else
					result.new_row = 2; // unchecked checkbox indicator
			} else {
				if (new_indicator.value == 1)
					result.new_row = 1; // "checked" hidden indicator
				else
					result.new_row = 2; // "unchecked" hidden indicator
			}
		} else {
			result.new_row = 1; // no indicator (but is new row)
		}
	}

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

	ajax_load_to_container(element, url, submit_form);
}

function ajax_load_to_container(element, url, submit_form, opt) {

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


var ajax_popup_a;
var ajax_popup_id = 1;
var ajax_popup_new_windows = {};

function ajax_popup_new(a, url, recycle, wid, onComplete, options) {

	if (!options)
		options = {};
	if (!options.width)
		options.width = 450;
	if (!options.height)
		options.height = 350;
	if (!options.className)
		options.className = 'mac_os_x';
	if (!options.showEffect)
		options.showEffect = Element.show;
	if (!options.hideEffect)
		options.hideEffect = Element.hide;
	if (options.minimizable == undefined)
		options.minimizable = false;
	if (options.maximizable == undefined)
		options.maximizable = false;

	if (!recycle) {
		var win = new Window(wid+'_outer', options);
		var inner = win.getContent();
		inner.innerHTML = '<div class="w_ajax_popup_new" id="' + wid + '_inner">Loading...</div>';
		win.setDestroyOnClose();
		win.showCenter();
		ajax_popup_new_windows[wid] = win;
	}
	//new Ajax.Updater(wid+'_inner', url, {onComplete: onComplete});
	new Ajax.Updater(wid+'_inner', url, {onComplete: onComplete, evalScripts: true});
}

function ajax_popup_new_close(wid) {
	if (ajax_popup_new_windows[wid]) {
		ajax_popup_new_windows[wid].destroy();
	}
}

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, new_opt) {

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

	if (eonvar.new_ajax_popup) {
		if (!wid) {
			ajax_popup_id += 1;
			wid = 'w_ajax_popup_' + ajax_popup_id;
		}
		if (recycle) {
			var tmp = document.getElementById(wid+'_outer');
			if (!tmp)
				recycle = false;
		}
		ajax_popup_new(a, url, recycle, wid, onComplete, new_opt);
		return;
	} else {
		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;
	if (eonvar.new_ajax_popup) {
		var inner = get_ancestor(a, 'w_ajax_popup_new');
		if (inner) {
			var tmp = inner.id.split('_');
			var tmp2 = '';
			for(var i=0; i < (tmp.length-1); i++) {
				if (i > 0)
					tmp2 += '_';
				tmp2 += tmp[i];
			}
			ajax_popup_new_close(tmp2);
		}
		return;
	}

	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';
	if (eonvar.new_ajax_popup) {
		ajax_popup_new_close(wid);
		return;
	}
	var el = $(eid);
	if (el) {
		document.body.removeChild(el);
	}
}

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 get_child(element, child_class, n) {
	if (!element)
		return;
	if (!n)
		n = 0;
	n += 1;

	var max = 1000;
	if (n > max) {
		alert('timed out after looking for child ' + child_class + ' in ' +max+' levels');
		return;
	}	
		
	var child;
	for (var i=0; i < element.childNodes.length; i++) {
		child = element.childNodes[i];
		if (has_class(child, child_class))
			return child;
	}	
	for (var i=0; i < element.childNodes.length; i++) {
		child = element.childNodes[i];
		var result = get_child(child, child_class, n);
		if (result)
			return result;
	}	
}

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_wipe(eid) {
	$('_ax_' + eid).value = '';
	$('_ax_' + eid).focus();
	$(eid).value = '';
}

function ax_clear(eid) {
	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;
}

var ax2_hasfocus = null;
var ax_status = {};
function ax2_get_value_input(text_input) {
	return $(text_input.id.substr(4));
}

function ax2_focus(input) {
	ax2_hasfocus = input;
	if (has_class(input, 'new')) {
		input.value = '';
		remove_class(input, 'new');
	}
}

function ax2_blur(input) {
	ax2_hasfocus = null;
	ax2_onchange(input);
}

function ax2_select(input, li) {
	var field = ax2_get_value_input(input);
	field.value = li.id;
	var new_row = field.id.match(/.*XN\d?X/)+'_new';
	if($(new_row) && $(new_row).value){
		$(new_row).checked = true;
	}
	ax2_setfound(input, true);
	return false;
}

function ax2_callback(input, query) {
	var field = ax2_get_value_input(input);
	field.value = '';
	if (!ax2_hasfocus || ax2_hasfocus.id != input.id) {
		ax2_setfound(input, false);
	} else {
		ax2_setfound(input, 2);
	}
	return query;
}

function ax2_onchange(input) {
	var field = ax2_get_value_input(input);
	if (input.value == '')
		field.value = '';
	if (field.value) {
		ax2_setfound(input, true);
	} else {
		ax2_setfound(input, false);
	}
}

function ax2_setfound(input, is_found) {

//alert(ax_status[input.id] + ' vs ' + is_found);
	if (ax_status[input.id] == is_found)
		return;

	var element = input.parentNode.parentNode;
	var vinput = ax2_get_value_input(input);
/*
	var errmsgid = vinput.id + '_errmsg';
	var errmsg = document.getElementById(errmsgid);
	if (!errmsg) {
		errmsg = document.createElement('div');
		errmsg.id = errmsgid;
		errmsg.className = 't_field_errmsg';
		vinput.parentNode.appendChild(errmsg);
	}
*/

	if (is_found == 2) {
		remove_class(element, 'x_formerror');
		remove_class(element, 'x_validated');
		//errmsg.style.display = 'none';
		eon.form.clear_field_errmsg(vinput);
	} else if (is_found) {
		change_class(element, 'x_formerror', 'x_validated');
		vinput.onchange();
		//errmsg.style.display = 'none';
		eon.form.clear_field_errmsg(vinput);
	} else {
		change_class(element, 'x_validated', 'x_formerror');
		//errmsg.style.display = '';
		//errmsg.innerHTML = 'Customer not found';
		eon.form.set_field_errmsg(vinput, 'Not found');
	}

	ax_status[input.id] = is_found;

}

function ax2_onhide(elem,update) {
	var win = get_ancestor(elem, 'mac_os_x_content');
	if (win) {
		win.style.overflow = 'auto';
		win = win.parentNode;
		if (win) {
			win.style.overflow = 'auto';
		}
	}
	Element.hide(update);
}
function ax2_onshow(elem,update) {
	var win = get_ancestor(elem, 'mac_os_x_content');
	if (win) {
		win.style.overflow = 'inherit';
		win = win.parentNode;
		if (win) {
			win.style.overflow = 'inherit';
		}
	}
	Element.show(update);
}


/**
 * 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);
}

// todo: types from backend
function handle_order_deliv_to(elem) {
	return handle_order_field(elem, 'deliv_to', ['none','cust', 'manual', 'pickup', 'workshop']);
}
// todo: types from backend
function handle_order_pay_mode(elem) {
	var ret = handle_order_field(elem, 'pay_mode', ['none', 'test', 'card', 'invoice', 'cash', 'offer', 'card_terminal', 'down_payment', 'offer_with_ofer', 'cash_cashinvoice', 'card_cashinvoice']);
	var prefix = elem.id.replace('Xpay_mode', 'X');
	eon.shop.checkout_check_pay_terms(prefix);
	return ret;
}
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, obj_is_tbody)
{
	// Find the tbody
	var table;
	if (obj_is_tbody)
		table = obj;
	else
		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;
	}

	// 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'); // not optimal. may be a lot of input elements in document!
	var pat = new RegExp(prefix + '.+');
	var par;
	var inps2 = [];
	var n = 0;
	var i_len = inps.length;
	for(var j=0; j < i_len; 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.
//alert(inps[j].name + ' : ' + inps[j].parentNode.className);
		if (has_class(inps[j].parentNode, 't_field') || has_class(inps[j].parentNode, 't_actioncell'))
			continue;
//var src2 = pre.replace(/^r_/, '_muxX') + 'N' + id;
//alert('PROCEED: ' + inps[j].name + ' : ' + src + ' : ' + rep);

		var inp = inps[j].cloneNode(true);
/*
		inp.name = inp.name.replace(
			new RegExp('XN'+id+'X','g'), 'XN'+(id+1)+'X'
		);
*/
		inp.name = inp.name.replace(src, rep);
		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++) {
//alert('ADD: ' + inps2[j].name);
			par.appendChild(inps2[j]);
		}
	}
	table.appendChild(node);
	return 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;
	}
}

function dd(obj, ret) {
	var od = _dd(obj);
	if (ret) return od.dump;
    else alert(od.dump);
}

function _dd(obj) {
  var od = new Object;
  var result = "";
  var len = 0;

  for (var property in obj) {
    var value = obj[property];
    if (typeof value == 'function')
      continue;
    else if (typeof value == 'string')
      value = "'" + value + "'";
    else if (typeof value == 'object') {
      if (value instanceof Array)
      {
        value = "[ " + value + " ]";
      }
      else
      {
        var ood = _dd(value);
        value = "{ " + ood.dump + " }";
      }
    }
    result += "'" + property + "' : " + value + ", ";
    len++;
  }
  od.dump = result.replace(/, $/, "");
  od.len = len;
  return od;
}

// From _muxXN0Xmyfield get _muxX*Xmyfield
// From _muxX1XmysubX2Xmyfield, get _muxX*XmysubX*Xmyfield
// etc
// :-S
// todo: better name
function get_similar_input_ids(id, tag) {

	var pat = '_muxX[^X]+X';
	var tmp = id.split('X');
	for(var i=2; i < (tmp.length-1); i = i+2) {
		pat += tmp[i] + 'X[^X]+X';
	}
	pat += tmp[tmp.length-1];
	pat = new RegExp(pat);

	var ids = [];
	var elems = document.getElementsByTagName(tag);
	for(var i=0; i < elems.length; i++) {
		var elem = elems[i];
		if (!elem.id)
			continue;
		if (!elem.id.match(pat))
			continue;
		ids[ids.length] = elem.id;
		
	}

	return ids;
}

// used instead of # in href where # should not be appended to url
function noop() {}

eon.lightbox = {
	getImageText: function(img) {
		var id = eon.lightbox.imgSrcToId(img);
		var el = document.getElementById('x_lbtxt_' + id);
		return el.innerHTML;
	},
	getImageActions: function(img) {
		var id = eon.lightbox.imgSrcToId(img);
		var el = document.getElementById('x_lbact_' + id);
		return el.innerHTML;
	},
	imgSrcToId: function(img) {
		var id = img.src.split('://');
		id = id[1].split('/');
		id = id[1];
		return id;
	}
}

function popup_login() {
	var url = '/N0/user/login_popup?ajax=1';
	ajax_popup(null, url);
}

function add_url_param(url, param, value) {
	var tmp = url.split('?');
	var url2 = tmp[0] + '?';
	var params = tmp[1];
	if (!params)
		params = '';
	params = params.split('&');
	for (var p=0; p<params.length; p++) {
		var part = params[p].split('=');
		if (part[0] == param)
			continue;
		url2 += params[p] + '&';
	}
	url2 += param + '=' + encodeURIComponent(value);
	return url2;
}

// todo: move to eontyre
/*
function tyre_hotel_line_on_delete(res) {
	ajax_popup_close_by_child(eonvar.tyre_hotel_line_delete_a);
	var prefix = '_muxX' + eonvar.th_id + 'XlinesX' + res._id;
	var input = document.getElementById( prefix + 'Xproduct_brand');
	var td = get_ancestor(input, 't_item');
	td.parentNode.removeChild(td);
	th_sync_mode(eonvar.th_sync_mode);
}
*/

