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;
	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 + "\"");
			}
		}
	}
	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) 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) 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) 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) 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) i++;
		if (i == index) break;
	}
	return result;
}

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());
			}
		}
	}
}
