function get_obj_by_path(obj, path) {
	path = path;
	var routes = [];
	var step;
	var index;
	var parts = path.split(/\s+/);
	var j;
	for (var i = 0; i < parts.length; i++) {
		step = parts[i];
		index = 0;
		if (i == parts.length - 1) {
			routes[routes.length] = { step: step, index: index };
			break;
		}
		for (j = i + 1; j < parts.length; j++) {
			if (parts[j].search(/^\d+$/gi) == -1) {
				if (j == i + 1) {
					routes[routes.length] = { step: step, index: index };
				}
				break;
			} else {
				routes[routes.length] = { step: step, index: parts[j] }
			}
		}
		i = j - 1;
	}

	var result = obj;
	var route;
	try {
		for (var i = 0; i < routes.length && result; i++) {
			route = routes[i];
			with (route) {
				if (step == "parent") {
					while (index-- >= 0 && result) {
						result = result.parentNode;
					}
				} else if (step == "prev") {
					result = not_text_previous_subling(result, index);
				} else if (step == "next") {
					result = not_text_next_subling(result, index);
				} else if (step == "child") {
					result = not_text_child(result, index);
				} else if (step == "last") {
					result = not_text_last_child(result, index);
				} else if (step == "") {	//	do nothing, continue
				} else {
					alert("get_obj_by_path:\nUnknow step type:\"" + step + "\"");
				}
			}
		}
	} catch (e) {
		return null;
	}
	return result;
}

//	aliases
function not_text_child(obj, index) {
	return not_text_child_by_index(obj, index);
}
function not_text_last_child(obj, index) {
	return not_text_last_child_by_index(obj, index);
}
function not_text_next_subling(obj, index) {
	return not_text_next_subling_by_index(obj, index);
}
function not_text_previous_subling(obj, index) {
	return not_text_previous_subling_by_index(obj, index);
}

function not_text_child_length(obj) {
	var length = 0;
	var result = obj.firstChild;
	for (; result; result = result.nextSibling) {
		if (result.nodeType != 3 && result.nodeType != 8) length++;
	}
	return length;
}

function not_text_child_by_index(obj, index) {
	//	when damn mozilla gets white spaces it creates new node, while IE does not
	if (index == undefined) {
		index = 0;
	}
	var i = -1;
	var result = obj.firstChild;
	for (; result; result = result.nextSibling) {
		if (result.nodeType != 3 && result.nodeType != 8) i++;
		if (i == index) break;
	}
	return result;
}

function not_text_last_child_by_index(obj, index) {
	if (index == undefined) {
		index = 0;
	}
	var i = -1;
	var result = obj.lastChild;
	for (; result; result = result.previousSibling) {
		if (result.nodeType != 3 && result.nodeType != 8) i++;
		if (i == index) break;
	}
	return result;
}

function not_text_next_subling_by_index(obj, index) {
	if (index == undefined) {
		index = 0;
	}
	var i = -1;
	var result = obj.nextSibling;
	for (; result; result = result.nextSibling) {
		if (result.nodeType != 3 && result.nodeType != 8) i++;
		if (i == index) break;
	}
	return result;
}

function not_text_previous_subling_by_index(obj, index) {
	if (index == undefined) {
		index = 0;
	}
	var i = -1;
	var result = obj.previousSibling;
	for (; result; result = result.previousSibling) {
		if (result.nodeType != 3 && result.nodeType != 8) i++;
		if (i == index) break;
	}
	return result;
}


function open_win(win_file, win_title) {
	window.open(win_file, win_title, 'resizable=no,width=550,height=400,toolbar=no,scrollbars=yes,location=no,menubar=no,status=yes');
}


function set_clipboard_text(text) {
	try {
		if (window.clipboardData) {
			window.clipboardData.setData("Text", text);
		} else if (window.netscape) {
			netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
			var clip = Components.classes["@mozilla.org/widget/clipboard;1"].createInstance(Components.interfaces.nsIClipboard);
			if (!clip) return false;
			var trans = Components.classes["@mozilla.org/widget/transferable;1"].createInstance(Components.interfaces.nsITransferable);
			if (!trans) return false;
			trans.addDataFlavor("text/unicode");
			var str = new Object();
			var len = new Object();
			var str = Components.classes["@mozilla.org/supports-string;1"].createInstance(Components.interfaces.nsISupportsString);
			var copytext = text;
			str.data = copytext;
			trans.setTransferData("text/unicode", str, copytext.length * 2);
			var clipid = Components.interfaces.nsIClipboard;
			if (!clip) return false;
			clip.setData(trans, null, clipid.kGlobalClipboard);
		} else {
			return false;
		}
	} catch(e) {
		return false;
	}
	return true;
}

function is_clipboard_allowed() {
	if (window.clipboardData) {
		return true;
	} else {
		return false;
	}
}

function get_dimensions(obj) {
	var result = { "left": 0, "right" : 0, "top": 0, "bottom" : 0, "width" : obj.offsetWidth, "height" : obj.offsetHeight };
	if (obj.offsetParent) {
		while (obj.offsetParent) {
			result.left += obj.offsetLeft;
			result.top += obj.offsetTop;
			obj = obj.offsetParent;
		}
	} else {
		if (obj.x) {
			result.left += obj.x;
		}
		if (obj.y) {
			result.top += obj.y;
		}
	}
	result.right = result.left + result.width;
	result.bottom = result.top + result.height;
	return result;
}

function is_intersected(dim1, dim2) {
	if ((Math.min(dim1.right, dim2.right) > Math.max(dim1.left, dim2.left)) && (Math.min(dim1.bottom, dim2.bottom) > Math.max(dim1.top, dim2.top))) {
		return true;
	} else {
		false;
	}
}

function is_inside(dim, point) {
	if ((dim.left <= point.x) && (dim.right >= point.x) && (dim.top <= point.y) && (dim.bottom >= point.y)) {
		return true;
	} else {
		return false;
	}
}

function format_size(size) {
	var result;
	if (size < 1024) {
		result = size + " B";
	} else if (size < 1048576) {
		result = Math.round(size / 1024) + " KB";
	} else {
		result = Math.round(size / 1048576) + " MB";
	}
	return result;
}

function format_time(time) {
	var seconds = time % 60;
	var minutes = ((time - seconds) / 60) % 60;
	var hours = (time - seconds - minutes * 60) / 3600;
	seconds = String(seconds);
	minutes = String(minutes);
	hours = String(hours);
	if (seconds.length == 1) {
		seconds = "0" + seconds;
	}
	if (minutes.length == 1) {
		minutes = "0" + minutes;
	}
	if (hours.length == 1) {
		hours = "0" + hours;
	}
	return hours + ":" + minutes + ":" + seconds;
}

function full_htmlspecialchars(str) {
     if (typeof str == "undefined") {
          str = "";
     }
     return str.replace(/&/g, "&amp;").replace(/\"/g, "&quot;").replace(/>/g, "&gt;").replace(/</g, "&lt;");
}
function get_cookies() {
	var cookies_array = document.cookie.split(";");
	var cookies = {};
	for (var i = 0; i < cookies_array.length; i++) {
		var cookie_params = cookies_array[i].split("=");
		cookies[trim(cookie_params[0])] = unescape(cookie_params[1]);
	}
	return cookies;
}

function set_cookie(name, value, expires) {
	var cookie = name + "=" + escape(value);
	if (expires != undefined) {
		cookie += ";expires=" + expires.toUTCString();
	}
	document.cookie = cookie;
}

function get_cookie(name) {
	return get_cookies()[name];
}

function get_base_href() {
	var base_href = "";
	if (window.base_href) {	//	already calculated
		base_href = window.base_href;
	} else {
		var bases = document.getElementsByTagName("base");
		if (bases.length) {
			var base = document.getElementsByTagName("base")[0];
			var attribute = base.attributes.getNamedItem("href");
			if (attribute) {
				base_href = attribute.nodeValue.replace(/\/[^\/]*$/i, "");
			}
		}
		window.base_href = base_href;	//	save for future calls
	}
	return base_href;
}

function get_absolute_location(location) {
	var real_location = location;
	if (real_location.search(/^(http:\/)?\//i) !== 0) {	//	relative url
		var base_href = get_base_href();
		if (base_href) {
			real_location = base_href + "/" + real_location;
		}
	}
	return real_location;
}


function change_location(location) {
	window.location.href = get_absolute_location(location);
}

function array_search(needle, haystack, strict) {
	for (var i = 0; i < haystack.length; i++) {
		if (strict && (needle == haystack[i]) || !strict && (needle == haystack[i])) {
			return i;
		}
	}
	return false;
}

function array_unique(array) {
	var result = [];
	var element;
	for (var i = 0; i < array.length; i++) {
		element = array[i];
		if (array_search(element, result, true) === false) {
			result.push(element);
		}
	}
	return result;
}

function swap_class_names(obj, class_name_remove, class_name_add) {
	if (!obj) {
		return false;
	} else {
		var classes = obj.className.split(/\s+/);
		var found = false;
		if (class_name_remove !== undefined) {
			for (var i = classes.length - 1; i >= 0; i--) {
				if (classes[i] == class_name_remove) {
					if (class_name_add !== undefined) {
						classes[i] = class_name_add;
					} else {
						classes[i] = null;
					}
					found = true;
					break;
				}
			}
		}
		if (!found && class_name_add !== undefined) {
			classes.push(class_name_add);
		}
		classes = array_unique(classes);
		obj.className = classes.join(" ");
	}
}

function add_event_listener(el, evname, func) {
	if (el.attachEvent) { // IE
		el.attachEvent("on" + evname, func);
	} else if (el.addEventListener) { // Gecko / W3C
		el.addEventListener(evname, func, true);
	} else {
		el["on" + evname] = func;
	}
}

var log_container;
function add_log(message) {
	if (!log_container) {
		log_container = document.getElementById("log_container");
	}
	if (log_container) {
		log_container.appendChild(document.createTextNode(message));
		log_container.appendChild(document.createElement("br"));
	}
}

function debug(obj, parent) {
	var type = typeof obj;
	if (type != "object" && type != "array") {
		alert(obj);
	} else {
		for (var i in obj) {
			try {
				var text = i;
				if (parent !== undefined) {
					text = parent + "." + text;
				}
				type = typeof obj[i];
				if (type == "object" || type == "array") {
					debug(obj[i], text)
				} else if (!confirm(text + " = " + obj[i])) {
					break;
				}
			} catch(e) {
				alert(i + " = Exception: " + e.toString());
			}
		}
	}
}

function form_keypress_handler(event) {	//	check for Enter here
	var on_enter_callback, on_enter;
	on_enter = this.getAttribute("on_enter");
	if (on_enter) {
		on_enter_callback = window[on_enter];
	} else {
		on_enter_callback = null;
	}
	var event_information = get_event_information(event || window.event);
	if (is_enter_pressed(event_information)) {
		if (on_enter_callback) {
			on_enter_callback();
		}
		return false;
	}
	return true;
}

function form_keydown_handler(event) {	//	check for Esc here
	var on_escape_callback, on_escape;
	on_escape = this.getAttribute("on_escape");
	if (on_escape) {
		on_escape_callback = window[on_escape];
	} else {
		on_escape_callback = null;
	}
	var event_information = get_event_information(event || window.event);
	if (is_escape_pressed(event_information)) {
		if (on_escape_callback) {
			on_escape_callback();
		}
		return false;
	}
	return true;
}

//	the point is to use keydown to catch Esc key and keypress to catch Enter,
//	because Safari and Chrome do not fire keypress for Esc and
//	keydown is fired when item from autocomplete menu is choosen using Enter
function add_form_listeners() {
	for (var i = 0; i < document.forms.length; i++) {
		var form = document.forms[i];
		if (form.getAttribute("on_enter")) {
			form.onkeypress = form_keypress_handler;
		}
		if (form.getAttribute("on_escape")) {
			form.onkeydown = form_keydown_handler;
		}
	}
}

function is_enter_pressed(event_information) {
	var tag_name = event_information.target.tagName.toLowerCase();
	var result = false;
	if (event_information.code == "13" && tag_name == "input" && event_information.target.type.toLowerCase() != "button") {
		result = true;
	}
	return result;
}

function is_escape_pressed(event_information) {
	var result = false;
	if (event_information.code == "27") {
		result = true;
	}
	return result;
}

function get_event_information(e) {
	var result = {};

	if (e.target) {
		result.target = e.target;
	} else if (e.srcElement) {
		result.target = e.srcElement;
	}

	if (browser.is_ie()) {
		result.code = window.event.keyCode;
	} else if (document.layers) {
		result.code = e.which;
	} else {
		result.code = e.keyCode;
	}

	return result;
}

function get_form(obj) {
	form = null;
	while (obj) {
		if (obj.tagName.toLowerCase() == "form") {
			form = obj;
			break;
		}
		obj = obj.parentNode;
	}
	return form;
}

function get_form_element(form, name) {
	if (form && form.elements && form.elements.length) {
		for (var i = 0; i < form.elements.length; i++) {
			if (form.elements[i].name == name) {
				return form.elements[i];
			}
		}
	}
}

function generate_mailto(to, subject, body) {
	if (!(to instanceof Array)) {
		to = [to];
	}
	var result = "mailto:" + to.join(",");
	var fields = {};
	if (subject) {
		fields["subject"] = subject;
	}
	if (body) {
		fields["body"] = body;
	}
	var prepared_fields = [];
	for (var i in fields) {
		if (fields.hasOwnProperty(i)) {
			prepared_fields.push(encodeURIComponent(i) + "=" + encodeURIComponent(fields[i]));
		}
	}
	if (prepared_fields.length) {
		result += "?" + prepared_fields.join("&");
	}
	return result;
}

function is_form_disabled(form) {
	return Boolean(form.is_disabled);
}

function disable_form(form, ignore_disabled_check) {
	if (!ignore_disabled_check && is_form_disabled(form)) {
		return;
	}
	var element;
	for (var i = 0; i < form.elements.length; i++) {
		element = form.elements[i];
		element.old_disabled = element.disabled;
		element.disabled = true;
	}
	form.is_disabled = true;
}

function enable_form(form, ignore_disabled_check) {
	if (!ignore_disabled_check && !is_form_disabled(form)) {
		return;
	}
	var element;
	for (var i = 0; i < form.elements.length; i++) {
		element = form.elements[i];
		element.disabled = element.old_disabled;
	}
	form.is_disabled = false;
}

function set_node_value(node, value, nbsp_if_empty) {
	if ((value === null || value === false || value === "") && nbsp_if_empty) {
		value = "\u00a0";
	}
	var new_node = document.createTextNode(value);
	if (node.firstChild) {
		node.replaceChild(new_node, node.firstChild);
	} else {
		node.appendChild(new_node);
	}
}

function preload_images() {
	if (document.images) {
		if (!document.preloaded_images) {
			document.preloaded_images = [];
		}
		for (var i = 0; i < arguments.length; i++) {
			var img = new Image();
			img.src = arguments[i];
			document.preloaded_images.push(img);
		}
	}
}

function check_all_in_list(form) {
	check_uncheck_all_in_list(form, true);
}

function uncheck_all_in_list(form) {
	check_uncheck_all_in_list(form, false);
}
function check_uncheck_all_in_list(form, checked) {
	var checkboxes = form[name];
	for (var i = 0; i < form.elements.length; i++) {
		var element = form.elements[i];
		if (element.tagName.toLowerCase() == "input" && element.type.toLowerCase() == "checkbox") {
			element.checked = checked;
		}
	}
}
function array_merge(ar1, ar2) {
	var result = ar1;
	for (var i = 0; i < ar2.length; i++) {
		result.push(ar2[i]);
	}
	return result;
}
function trim(s) {
	try {
		return s.replace(/^\s+|\s+$/g,"");
	} catch(e) {
		return s;
	}
}

function focus_on_first_element(form) {
	//	tries to move focus to first form element
	for (var i = 0; i < form.elements.length; i++) {
		try {
			form.elements[i].focus();
			return;
		} catch (e) {
		}
	}
}

function get_selected_value(select) {
	var result = [];
	for (var i = 0; i < select.options.length; i++) {
		if (select.options[i].selected) {
			result.push(select.options[i].value);
		}
	}
	if (result.length == 0) {
		return false;
	} else if (result.length == 1) {
		return result[0];
	} else {
		return result;
	}
}

function array_search(needle, haystack, strict) {
	for (var i = 0; i < haystack.length; i++) {
		if (strict && (needle == haystack[i]) || !strict && (needle == haystack[i])) {
			return i;
		}
	}
	return false;
}

function in_array(needle, haystack, strict) {
	for (var i = 0; i < haystack.length; i++) {
		if (strict && (needle == haystack[i]) || !strict && (needle == haystack[i])) {
			return true;
		}
	}
	return false;
}

function array_unique(array) {
	var result = [];
	var element;
	for (var i = 0; i < array.length; i++) {
		element = array[i];
		if (array_search(element, result, true) === false) {
			result.push(element);
		}
	}
	return result;
}

function array_values(array) {
	var result = [];
	for (var i in array) {
		result.push(array[i]);
	}
	return result;
}

function on_checkbox_text_click(obj, path) {
	if (path === undefined) {
		path = "prev";
	}
	var checkbox = get_obj_by_path(obj, path);
	checkbox.checked ^= 1;
	if (checkbox.onclick) {
		checkbox.onclick();
	}
}

function on_radio_text_click(obj, path) {
	if (path === undefined) {
		path = "prev";
	}
	var radio = get_obj_by_path(obj, path);
	if (!radio.checked) {
		radio.checked = true;
		if (radio.onclick) {
			radio.onclick();
		}
	}
}

function stop_event_propagation(event) {
	if (event.stopPropagation) {
		event.stopPropagation();
	} else {
		event.cancelBubble = true;
	}
}

function is_valid_email(email) {
	return /^[A-Za-z0-9]+([_\.-][A-Za-z0-9]+)*@[A-Za-z0-9]+([_\.-][A-Za-z0-9]+)*\.([A-Za-z]){2,4}$/i.test(email);
}

function is_valid_zip(zip, is_canadian) {
	return (is_canadian ? /^[a-zA-Z]\d[a-zA-Z]\s?\d[a-zA-Z]\d$/ : /^\d{5}$/).test(zip);
}

function get_form_radio_value(form, name) {
	var elements = form[name];
	if (!elements.length) {
		elements = [ elements ];
	}
	for (var i = 0; i < elements.length; i++) {
		if (elements[i].checked) {
			return elements[i].value;
		}
	}
	return false;
}
