// JavaScript Document

var LX = { version: "0.1" };

LX.Inputs = {
	setFCK: function (element, base) {
		var fck = new FCKeditor(element.identify());
		fck.BasePath = base;
		fck.ReplaceTextarea()
		return element;
	}, 
	isFCK: function (element) {
		var isfck = element.readAttribute("fck");
		if (isfck) return isfck;
		if (typeof FCKeditorAPI != "undefined" && FCKeditorAPI.GetInstance(element.identify())) {
			isfck = true;
		} else {
			isfck = false;
		}
		element.writeAttribute("fck", isfck);
		return isfck;
	},
	getFCK: function (element) {
		var getfck;
		if (element.isFCK()) {
			getfck = FCKeditorAPI.GetInstance(element.identify());
		}
		return getfck;
	},
	setFocus: function (element) {
		if (element.isFCK()) {
			element.getFCK().Focus();
		} else if (element.type != "hidden" && !element.readAttribute("disabled")) {
			element.focus();
		}
		return element;
	},
	getDownlinks: function (element) {
		var downlinks = new Array;
		element.readAttributeBunch("downlinknames").each( function (name) {
			var downlink;
			if (downlink = element.form.select("[name='" + name + "']")) {
				downlink.each( function (e) {
					if (e.hasClassName("dateselect")) {
						downlinks.push(e.form.down("#" + e.readAttribute("datedate")));
						downlinks.push(e.form.down("#" + e.readAttribute("datemonth")));
						downlinks.push(e.form.down("#" + e.readAttribute("dateyear")));
					}
				} );
				downlinks.push(downlink);
			}
		});
		downlinks = downlinks.flatten();
		return downlinks;
	}, 
	blockDownlinks: function (element) {
		element.getDownlinks().invoke("block");
		return element;
	},
	unblockDownlinks: function (element) {
		element.getDownlinks().invoke("unblock");
		return element;
	},
	getUplink: function (element) {
		element = $(element);
		var uplinkid, uplink;
		if ((uplinkid = element.readAttribute("uplinkid")) && (uplink = element.form.down("#" + uplinkid))) {
			return uplink;
		}
	},
	setValidator: function (element, validator, options) {
		element = $(element);
		element.addAttributeBunch("validatorsattached", validator);
		
		if (options && typeof options == "object") { 
			options = $H(options);
			options.each( function (e) {
				element.writeAttribute(e.key, e.value);
			});
		}
		
		if (!element.form.down("[name='xFake']")) {
			element.form.insert({top: "<div class='hidden'><input type='text' name='xFake' /></div>"}).down("[name='xFake']").setValidator("valFake");
			element.form.observe("submit", LX.Utils.runValidator).observe("LX:validate", LX.Utils.runValidator);
		}
		
		return element;
	}, 
	unsetValidator: function (element, validator, options) {
		element = $(element);
		element.removeAttributeBunchValue("validatorsattached", validator);
		return element;
	}, 
	runValidator: function (element) {
		element.form.clearValidator();
		element.form.writeAttributeBunch("validatorspending_" + element.identify(), element.readAttributeBunch("validatorsattached"));
		element.readAttributeBunch("validatorsattached").each( function (validator) {
			element[validator]("LX:validateonly");
		});
	},
	clearValidator: function (element, validator) {
		element = $(element);
		element.form.removeAttributeBunchValue("validatorspending_" + element.identify(), validator);
		return element;
	},
	setAutocompleter: function (element, klass, list, options) {
		element = $(element);
		var listnew = new Array;
		if (list.length && !Object.isString(list[0])) {
			listnew = list.pluck("name");
		} else {
			listnew = list;
		}
		var div = element.writeAttribute("autocomplete", "off").insert({after: "<div class='" + klass + "'></div>"}).next("div." + klass).hide();
		new Autocompleter.Local(element, div, listnew, options);
		return element;
	},
	// L8R: новый fck для решения проблем с фокусом
	// L8R: красить fck
	valMessageSend: function (element, message) {
		var bookmark, valasterisk, form = element.form;
		
		if (valasterisk = form.down("div.valasterisk")) {
			if (form.readAttribute("lasthilitedid")) {
				// avoid multiple messages and hilites
				return element;
			} else {
				valasterisk.show();
			}
		} else {
			valasterisk = form.insert({top: "<div class='valasterisk'><div class='x'>x</div><div class='message'></div></div>"}).down("div.valasterisk");
			valasterisk.observe("click", function () { // !!! 2do: после енабла формы разбираться с мертво заблокированными делит-баттонами
				var form = this.up("form");
				// form.enable().select(".valhilite").invoke("removeClassName", "valhilite");
				form.undim({simple: true}).select(".valhilite").invoke("removeClassName", "valhilite");
				var lasthilitedid;
				if (lasthilitedid = form.readAttribute("lasthilitedid")) {
					form.down("#" + lasthilitedid).setFocus();
				}
				this.hide();
				form.writeAttribute("lasthilitedid", null);
			});
		}
		bookmark = element.isFCK() ? form.down("#" + element.identify() + "___Frame") : bookmark = element;
		valasterisk.clonePosition(bookmark, { setWidth: false, setHeight: false, offsetLeft: 30, offsetTop: 15 }).down("div.message").update(message);
		
//		element.focus();
		element.valHilite();
		form.writeAttribute("lasthilitedid", element.identify()); 
		// form.disable();
		form.dim({klass: "dimming", opacity: 0.25, simple: true});
		
		bookmark.up().scrollTo();
		
		return element;
	}, 
	valFake: function (element, nextevent) {
		element = $(element);
		element.clearValidator("valFake");
		element.form.tryToSubmit(nextevent);
	},
	valRequired: function (element, nextevent) {
		var value = element.getValue();
		if (!element.hasClassName("dateselect")) {
			if (isEmptyString(value)) {
				element.valMessageSend("Ошибка: поле не заполнено. Введите/выберите значение !");
			} else {
				element.clearValidator("valRequired");
				element.form.tryToSubmit(nextevent);
			}
		} else {
			var date = element.form.down("#" + element.readAttribute("datedate"));
			var month = element.form.down("#" + element.readAttribute("datemonth"));
			var year = element.form.down("#" + element.readAttribute("dateyear"));
			
			if (isEmptyString(date.getValue())) {
				date.valMessageSend("Ошибка: поле не заполнено. Введите/выберите значение !");
			} else if (isEmptyString(month.getValue())) {
				month.valMessageSend("Ошибка: поле не заполнено. Введите/выберите значение !");
			} else if (isEmptyString(year.getValue())) {
				month.valMessageSend("Ошибка: поле не заполнено. Введите/выберите значение !");
			} else {
				element.clearValidator("valRequired");
				element.form.tryToSubmit(nextevent);
			}
		}
	},
	valRequiredAny: function (element, nextevent) {
		element = $(element);
		var value = element.getValue();
		
		var link, valrequiredanycontainer, valrequiredanylink;
		
		element.classNames().find( function (e) {
			return link = e.match(/valRequiredAny-(\w+)-(\w+)/);
		});
		
		valrequiredanycontainer = link[1];
		valrequiredanylink = link[2];		
		
		var brothers = element.up(valrequiredanycontainer).select("input").select( function (e) { return e.hasClassName("valRequiredAny-" + valrequiredanycontainer + "-" + valrequiredanylink); } );
		
		var validated = brothers.any(function (e) { if (!isEmptyString(e.getValue())) { return true; } });
		if (!validated) {
			element.up(valrequiredanycontainer).valHilite();
			brothers.invoke("valHilite");
			element.valMessageSend("Ошибка: ни одно поле не заполнено. Введите/выберите хотя бы одно значение !");
		} else {
			element.clearValidator("valRequiredAny");
			element.form.tryToSubmit(nextevent);
		}
	},
	valDiffList: function (element, nextevent) {
		element = $(element);
		var value = element.getValue();
		var valdifflistcontainer = element.readAttribute("valdifflistcontainer");
		var inputs = 
			element.up(valdifflistcontainer)
			.select("[name='" + element.name + "']" + ":enabled:not([type='hidden'])")
			.without(element)
			.select( function (e) { if (e.value == value) { return true; }});
		if (inputs.size()) {
			element.valHilite();
			inputs.first().valMessageSend("Ошибка: значения совпадают. Введите/выберите отличающееся значение !");
		} else {
			element.clearValidator("valDiffList");
			element.form.tryToSubmit(nextevent);
		}
	},
	valDiffList2: function (element, nextevent) {
		element = $(element);
		var value = element.getValue();
		var valdifflist2container = element.readAttribute("valdifflist2container");
		var valdifflist2uplinkname = element.readAttribute("valdifflist2uplinkname");
		var valdifflist2uplink = element.up(valdifflist2container).down("[name='" + valdifflist2uplinkname + "']");
		
		if (value == valdifflist2uplink.getValue()) {
			valdifflist2uplink.valHilite();
			element.valMessageSend("Ошибка: значения совпадают. Введите/выберите отличающееся значение !");
		} else {
			element.clearValidator("valDiffList2");
			element.form.tryToSubmit(nextevent);
		}
	},
	valEMail: function (element, nextevent) {
		element = $(element);
		var value = element.getValue();
		if (!isEmptyString(value) && !isEmptyString(value.replace(/([\w-\._]+@[\w-\.]+)/, ""))) {
			element.valMessageSend("Ошибка: неверный e-mail. Введите/выберите e-mail в формате aaa@bbb.ccc !");
		} else {
			// element.removeAttributeBunchValue("validators pending", "valEMail");
			element.clearValidator("valEMail");
			element.form.tryToSubmit(nextevent);
		}
	}, 
	valDate: function (element, nextevent) {
		if (!element.hasClassName("dateselect")) {
			var value = element.getValue();
			var parsed = value.match(/(\d{1,2})\.(\d{1,2})\.(\d{4})/);
			var d = Number(RegExp.$1), m = Number(RegExp.$2) - 1, y = Number(RegExp.$3);
			value = value.replace(/0(\d)\.(\d{1,2})\.(\d{4})/, "$1.$2.$3").replace(/(\d{1,2})\.0(\d)\.(\d{4})/, "$1.$2.$3");
			var date = new Date(y, m, d);
			var built = date.getDate() + "." + (date.getMonth() + 1) + "." + date.getFullYear();
			if (!isEmptyString(value) && value != built) {
				element.valMessageSend("Ошибка: неверная дата. Введите дату в формате дд.мм.гггг !");
			} else {
				// element.removeAttributeBunchValue("validators pending", "valDate");
				element.clearValidator("valDate");
				element.form.tryToSubmit(nextevent);
			}
		} else {
			var value = element.getValue();
			var parsed = value.match(/(\d{1,2})\.(\d{1,2})\.(\d{4})/);
			var d = Number(RegExp.$1), m = Number(RegExp.$2) - 1, y = Number(RegExp.$3);
			value = value.replace(/0(\d)\.(\d{1,2})\.(\d{4})/, "$1.$2.$3").replace(/(\d{1,2})\.0(\d)\.(\d{4})/, "$1.$2.$3");
			var date = new Date(y, m, d);
			var built = date.getDate() + "." + (date.getMonth() + 1) + "." + date.getFullYear();
			if (!isEmptyString(value) && value.indexOf("%") == -1 && value != built) {
				element.form.down("#" + element.readAttribute("datedate")).valMessageSend("Ошибка: неверная дата. Введите дату !");
			} else {
				element.clearValidator("valDate");
				element.form.tryToSubmit(nextevent);
			}
		}
	},
	valDateCompare: function (element, nextevent) {
		element = $(element);
		var value = element.getValue();
		var date = value.toDate(), uplink, uplinkvalue, uplinkdate;
		
		var valdatecompareto = element.readAttribute(valdatecompareto);
		if (valdatecompareto) {
			// !!! L8R: дописать получение даты из опшнсов
		} else {
			uplink = element.getUplink();
			uplinkvalue = uplink.getValue();
			uplinkdate = uplinkvalue.toDate();
		}
		
		var valdatecomparemode = element.readAttribute("valdatecomparemode");
		
		if (valdatecomparemode == "GE" && date < uplinkdate) {
			uplink.valHilite();
			element.valMessageSend("Ошибка: неверная дата. Дата должна быть больше или равна " + uplinkvalue + " !");
		} else {
			// element.removeAttributeBunchValue("validators pending", "valDateCompare");
			element.clearValidator("valDateCompare");
			element.form.tryToSubmit(nextevent);
		}
	},
	valRequiredLinked: function (element, nextevent) {
		element = $(element);
		var value = element.getValue();
		var uplink = element.getUplink();
		if (isEmptyString(value) && uplink && (!isEmptyString(uplink.getValue()) || (uplink.type == "radio" && uplink.checked))) {
			uplink.valHilite();
			element.valMessageSend("Ошибка: поле не заполнено. Введите/выберите значение !");
		} else {
			element.clearValidator("valRequiredLinked");
			element.form.tryToSubmit(nextevent);
		}
	}, 
	valEqualLinked: function (element, nextevent) {
		element = $(element);
		var value = element.getValue();
		var uplink = element.getUplink();
		if (!isEmptyString(value) && uplink && value != uplink.getValue()) {
			uplink.valHilite();
			element.valMessageSend("Ошибка: значения не совпадают. Введите/выберите значение !");
		} else {
			// element.removeAttributeBunchValue("validators pending", "valEqualLinked");
			element.clearValidator("valEqualLinked");
			element.form.tryToSubmit(nextevent);
		}
	}, 
	valDiffAjax: function (element, nextevent) { 
		element = $(element);
		var value = element.getValue();
		
		if (!isEmptyString(value)) {
			var valdiffajaxexcludefield = element.readAttribute("valdiffajaxexcludefield"), valdiffajaxincludefield = element.readAttribute("valdiffajaxincludefield"); 
			var excludefield, excludevalue = 0, includefield, includevalue = 0;
			if (excludefield = element.form.down("[name='" + valdiffajaxexcludefield + "']")) {
				excludevalue = excludefield.getValue();
			}
			if (includefield = element.form.down("[name='" + valdiffajaxincludefield + "']")) {
				includevalue = includefield.getValue();
			}
	
			new Ajax.Request(siteAjax + "?ajax=valdup&field=" + element.name + "&value=" + encodeURIComponent(value) + "&excludefield=" + valdiffajaxexcludefield + "&excludevalue=" + excludevalue + "&includefield=" + valdiffajaxincludefield + "&includevalue=" + includevalue, {
				element: element,
				method: "get",
				onSuccess: function (xhr) {
					var result = xhr.responseText;
					if (result == "0") {
						element.valMessageSend("Ошибка: такое значение уже присутствует. Введите/выберите другое значение !");
					} else if (result == "1") { // we need this "1" in explicit form, cause it means that's all OK with xhr.status and no ajax errors occured
						// element.removeAttributeBunchValue("validators pending", "valDiffAjax");
						element.clearValidator("valDiffAjax");
						element.form.tryToSubmit(nextevent);
					}
				}
			});
		}
	}, 
	valInt: function (element, nextevent) {
		element = $(element);
		var value = element.getValue().replace(/\,/g, ".");
	
		var valintmin = Number(element.readAttribute("valintmin")); 
		var valintmax = Number(element.readAttribute("valintmax"));
			
		if (!isEmptyString(value) && (isNaN(value) || Number(value) != Math.ceil(Number(value)) || Number(value) < Number(valintmin) || Number(value) > Number(valintmax))) {
			element.valMessageSend("Ошибка: неверное число. Введите/выберите целое число от " + valintmin + " до " + valintmax + " включительно !");
		} else {
			element.clearValidator("valInt");
			element.form.tryToSubmit(nextevent);
		}
	}
}

LX.Controls = {
	block: function (element) { // !!! 2do: на дизабле/енабле/блоке/анблоке/маске/анмаске разбираться с fck
		if (!element.readAttribute("masked")) {
			element.clear();
			element.disable();
			element.addClassName("grayed");
			element.writeAttribute("blocked");
		}
		return element;
	},
	unblock: function (element) {
		if (!element.readAttribute("masked")) {
			element.removeClassName("grayed");
			element.removeAttribute("blocked");
			element.enable();
		}
		return element;
	},
	mask: function (element) {
		element = $(element);
		element.writeAttribute("masked");
		return element;
	},
	unmask: function (element) {
		element = $(element);
		element.removeAttribute("masked");
		return element;
	},
	// OVERRIDE
	disable: function (element) { 
		element = $(element);
		if (!element.readAttribute("blocked") && !element.readAttribute("masked")) {
			element.blur();
			element.disabled = true;
		}
		return element;
	},
	// OVERRIDE
	enable: function (element) {
		element = $(element);
		if (!element.readAttribute("blocked") && !element.readAttribute("masked")) {
			element.disabled = false;
		}
		return element;
	}
}

LX.Forms = {
	setFlippingUpload: function (form) {
		form.select("a.uploader.flipper").each( function (e) {
			e.observe("click", function () {
				var src = this;
				var tgt = src.next("div.uploader.flipped");
				if (tgt.hasClassName("close")) {
					tgt.addClassName("open").removeClassName("close");
					src.addClassName("open").removeClassName("close");
				} else if (tgt.hasClassName("open")) {
					tgt.addClassName("close").removeClassName("open");
					src.addClassName("close").removeClassName("open");
				}
			});
		});
	}, 
	setFlippingDisplay: function (form) {
		form.select("a.displayer.flipper").each( function (e) {
			e.observe("click", function (ev) {
				var src = this;
				var tgt = src.next("div.displayer.flipped");
				if (tgt.hasClassName("close")) {
					tgt.addClassName("open").removeClassName("close");
					src.addClassName("open").removeClassName("close");
				} else if (tgt.hasClassName("open")) {
					tgt.addClassName("close").removeClassName("open");
					src.addClassName("close").removeClassName("open");
				}
			});
		});
	}, 
	setValidator: function (form /* validatorarray1, validatorarray2, ...*/) { 
		form = $(form);
		var validator, options;
		
		if (!form.down("[name='xFake']")) {
			form.insert({top: "<div class='hidden'><input type='text' name='xFake' /></div>"}).down("[name='xFake']").setValidator("valFake");
			form.observe("submit", LX.Utils.runValidator).observe("LX:validate", LX.Utils.runValidator);
		}
		
		$A(arguments).slice(1).each( function (validatorarray) {

			validator = validatorarray[0];
			options = validatorarray[1];
			if (typeof options != "object") { 
				validatorarray = validatorarray.slice(1);
			} else {
				validatorarray = validatorarray.slice(2);
				options = $H(options);
			}
			
			validatorarray.each( function (name) {
				form.select("[name='" + name + "']").each( function (field) {
					field.addAttributeBunch("validatorsattached", validator);
					if (Object.isHash(options)) {
						options.each( function (e) {
							field.writeAttribute(e.key, e.value);
						});
					}
				});
			});
		});
		return form;
	},
	clearValidator: function (form) {
		form = $(form);
		$A(form.attributes).each( function (e) { if (e.nodeName.indexOf("validatorspending_") == 0) { form.writeAttribute(e.nodeName, null); } });
		return form;
	}, 
	setRadioLink: function (form, uplinkname /* value1, downlinknames1[], value2, downlinknames2[], ... */) {
		var pairs = $A(arguments).slice(2).inject(new Array, function (acc, e, index) {
			if (index % 2 == 0) {
				var object = new Object;
				object.uplinkvalue = e;
				acc.push(object);
			} else {
				if (!Object.isArray(e)) {
					e = new Array(e);
				}
				acc.last().downlinknames = e;
			}
			return acc;
		});

		var fields = form.select("[name='" + uplinkname + "']");
		var checked;

		fields.each( function (field) { 
			pairs.each( function (pair) {
				if (field.value == pair.uplinkvalue) { 
					field.addAttributeBunch("downlinknames", pair.downlinknames);
					pair.downlinknames.each( function (name) {
						// L8R: rewrite for .select/.each
						form.down("[name='" + name + "']").writeAttribute("uplinkid", field.identify());
					});
					// $break;
				}
			});
			field.observe("click", function () {
				var src = this; 
				if (src.checked) {
					src.form.select("[name='" + src.name + "']").without(src).invoke("blockDownlinks");
					src.unblockDownlinks();
				} else {
					src.form.select("[name='" + src.name + "']").invoke("blockDownlinks");
				}
			});
			if (field.checked) {
				checked = field;
			}
		});
		form.select("[name='" + uplinkname + "']").without(checked).invoke("blockDownlinks");
		return form;
	},
	setLink: function (form /* uplinkname1, downlinknames1[], uplinkname2, downlinknames2[], ... */) { 
		form = $(form);
		var pairs = $A(arguments).slice(1).inject(new Array, function (acc, e, index) {
			if (index % 2 == 0) {
				var object = new Object;
				object.uplinkname = e;
				acc.push(object);
			} else {
				if (!Object.isArray(e)) {
					e = new Array(e);
				}
				acc.last().downlinknames = e;
			}
			return acc;
		});
		pairs.each( function (pair) {
			form.select("[name='" + pair.uplinkname + "']").each( function (uplink) {
				uplink.addAttributeBunch("downlinknames", pair.downlinknames);
				pair.downlinknames.each( function (name) {
					// L8R: rewrite for .select/.each
					form.down("[name='" + name + "']").writeAttribute("uplinkid", uplink.identify());
				});
			});
		});
		return form;
	}, 
	// FAKE OVERRIDE: this is an exact copy of original getelements(), but we need it for fieldset
	getElements: function (form) {
		return $A($(form).getElementsByTagName('*')).inject([], function(elements, child) {
			if (Form.Element.Serializers[child.tagName.toLowerCase()]) {
				elements.push(Element.extend(child));
			}
			return elements;
		});
	}, 
	block: function (form) {
		form = $(form);
		Form.getElements(form).invoke("block");
		form.select("a.input").invoke("block");
		return form;
	},
	unblock: function (form) {
		form = $(form);
		Form.getElements(form).invoke("unblock");
		form.select("a.input").invoke("unblock");
		return form;
	},
	mask: function (form) {
		form = $(form);
		Form.getElements(form).invoke("mask");
		form.select("a.input").invoke("mask");
		return form;
	},
	unmask: function (form) {
		form = $(form);
		Form.getElements(form).invoke("unmask");
		form.select("a.input").invoke("unmask");
		return form;
	},
	// OVERRIDE
	disable: function(form) {
		form = $(form);
		Form.getElements(form).invoke("disable");
		form.select("a.input").invoke("disable");
		return form;
	},
	// OVERRIDE
	enable: function(form) {
		form = $(form);
		Form.getElements(form).invoke("enable");
		form.select("a.input").invoke("enable");
		return form;
	},
	tryToSubmit: function (form, nextevent) {
		var pending = $A(form.attributes).find( function (e) { if (e.nodeName.indexOf("validatorspending_") == 0) { return true; } });
		if (!pending) {
			if (!nextevent) {
				form.submit();
			} else {
				form.fire(nextevent);
			}
		}
	}
}

LX.Elements = {
	clearClassName: function (element) {
		element.className = "";
		return element;
	}, 
	addAttributeBunch: function (element, name, values) {
		element = $(element);
		var oldvalues = $w(element.readAttribute(name));
		if (!Object.isArray(values)) {
			values = new Array(values);
		}
		values.each( function (value) {
			value = encodeURI(value);
			if (oldvalues.indexOf(value) == -1) {
				oldvalues.push(value);
			}
		});
		if (oldvalues.size()) {
			element.writeAttribute(name, oldvalues.join(" "));
		}
		return element;
	},
	removeAttributeBunchValue: function (element, name, value) {
		element = $(element);
		var oldvalues = $w(element.readAttribute(name)).without(encodeURI(value));
		element.writeAttributeBunch(name, oldvalues);
		return element;
	},
	writeAttributeBunch: function (element, name, values) {
		element = $(element);
		if (!Object.isArray(values)) {
			values = new Array(values);
		}
		values = values.collect( function (value) {
			return encodeURI(value);
		});
		element.writeAttribute(name, null);
		if (values.size()) {
			element.writeAttribute(name, values.join(" "));
		}
		return element;
	},
	readAttributeBunch: function (element, name) {
		element = $(element);
		var array = $w(element.readAttribute(name));
		array.each( function (e) { e = decodeURI(e); } );
		return array;
	},
	remove: function (element) {
		element = $(element);
		var backfixid, backfix;
		if (backfixid = element.readAttribute("backfixid")) {
			if (backfix = $(backfixid)) {
				backfix.remove();
			}
		}
		element.parentNode.removeChild(element);
		return element;
	},
	replaceBySelect: function (element, array, options) { 
		this.options = { }; Object.extend(this.options, options || { });
		element = $(element);
		var newelement;
		newelement = element
			.insert({after: new Element("select", this.options)})
			.next()
			.insert({bottom: "<option value=''>&nbsp;</option>" + array.asOptions()})
			.clear();
		
		// LX WARNING: используется, т.к. у селекта есть свой ремув
		element.parentNode.removeChild(element);
		
		return element;
	}, 
	valHilite: function (element) {
		element = $(element);
		if (!element.up("form").readAttribute("lasthilitedid")) {
			element.addClassName("valhilite");
		}
		return element;
	}, 
	dim: function (element, _options) {
		var options = Object.extend({
			klass: "waiting", 
			opacity: 0.75, 
			simple: false,
			effect: (Effect ? Effect.Appear : Element.show), 
			duration: 0.10
		}, _options || {});
		if (options.simple) { options.effect = Element.show; }
		
		var dimmer;
		if (!element.readAttribute("dimmerid")) {
			if (element.tagName.toUpperCase() == "BODY") {
				dimmer = element.insert({top: "<div class='" + options.klass + "'></div>"}).down("div." + $w(options.klass).join("."));
				dimmer.hide().setOpacity(options.opacity).clonePosition(element, {setWidth: true, setHeight: true});
			} else {
				dimmer = element.insert({after: "<div class='" + options.klass + "'></div>"}).next("div." + $w(options.klass).join("."));
				dimmer.hide().setOpacity(options.opacity).clonePosition(element, {setWidth: true, setHeight: true});
			}
			options["effect"](dimmer, {duration: options.duration, from: 0, to: options.opacity})
			element.writeAttribute("dimmerid", dimmer.identify());
			element.writeAttribute("dimmercount", 1);
		} else {
			element.writeAttribute("dimmercount", Number(element.readAttribute("dimmercount") + 1));
		}
		return element;
	}, 
	undim: function (element, _options) {
		var options = Object.extend({
			simple: false,
			effect: (Effect ? Effect.Fade : Element.hide), 
			duration: 0.10
		}, _options || {});
		if (options.simple) { options.effect = Element.hide; }
		
		var dimmerid, dimmer, dimmercount;
		if (dimmerid = element.readAttribute("dimmerid")) {
			if (element.tagName.toUpperCase() == "BODY") {
				dimmer = element.down("#" + dimmerid);
			} else {
				dimmer = element.next("#" + dimmerid);
			}
			if (dimmer) {
				if (dimmercount = Number(element.readAttribute("dimmercount")) - 1) {
					element.writeAttribute("dimmercount", dimmercount);
				} else {
					options["effect"](dimmer, {duration: options.duration});
					dimmer.remove();
					element.writeAttribute("dimmercount", null);
					element.writeAttribute("dimmerid", null);
				}
			}
		}
		return element;
	}, 
	centerOn: function (element, anchor) {
		var dimdv = anchor.getDimensions();
		var offdv = anchor.getScrollOffsets();
		var dimel = element.getDimensions();
		element.style.left = (Math.ceil((dimdv.width - dimel.width) / 2) + offdv.left) + "px";
		element.style.top = (Math.ceil((dimdv.height - dimel.height) / 3) + offdv.top) + "px";
		return element
	}
}

LX.Utils = {
	runValidator: function (ev) {
		var src = this;
		var fields = src.select("[validatorsattached]:enabled:not([type='hidden'])");
		
		src.clearValidator();
		
		fields.each( function (field) {
			src.writeAttributeBunch("validatorspending_" + field.identify(), field.readAttributeBunch("validatorsattached"));
		});
		
		fields.each( function (field) {
			field.readAttributeBunch("validatorsattached").each( function (validator) {
				if (ev.memo) {
					field[validator](ev.memo.nextevent);
				} else {
					field[validator]();
				}
			});
		});
		
		/* if (ev.memo) {
			src.fire(ev.memo.nextevent);
		} else {
			src.submit();
		} */
		
		ev.stop(); 
		return false; 
	}, 
	debug: function (e, value, caller) {
		// e.message, e.name
		window.alert(
				"error!" + 
				"\n\rcode: " + (4294967296 + e.number).toString(16) +
				"\n\rdescription: " + e.description +
				(isNot(caller) ? "" : "\n\rcaller: " + caller) +
				"\n\rvalue: " + value
		);
	}, 
	showMessage: function (message, _options) {
		var element, backfix;
		
		var options = Object.extend({
			id: "", 
			klass: "alert"
		}, _options || {});
		
		element = 
			document.body
			.insert({top: "<div id='" + options.id + "' class='" + options.klass + "'></div>"})
			.down("div." + $w(options.klass).join("."))
			.hide()
			.update(message)
			.centerOn(document.viewport)
			.show();
			
		if (Prototype.Browser.IE) {
			backfix = 
				element
				.insert({after: "<iframe class='" + options.klass + "back' style='display: none; position: absolute; filter:progid:DXImageTransform.Microsoft.Alpha(opacity=0);' src='javascript:false;' frameborder='0' scrolling='no'></iframe>"})
				.next("iframe");
			element.writeAttribute("backfixid", backfix.identify());
			backfix.clonePosition(element, { setWidth: true, setHeight: true }).show();
		}

		return element;
	}
}

Ajax.Responders.register({
	onCreate: function (requester, xhr) {
		var options = Object.extend({
			element: document.body
		}, requester.options || {});
		
		requester.options.element.dim({klass: "ajaxwaiting"});
	},
	onComplete: function (requester, xhr) {
		var options = Object.extend({
			element: document.body
		}, requester.options || {});
		
		if (xhr.status == 200) {
			requester.options.element.undim();
		} else {
			LX.Utils.showMessage("http status: " + xhr.status);
		}
	},
	onException: function (requester, exception) {
		LX.Utils.debug(exception, requester.url);
	}
});

String.prototype.toDate = function () {
	value = this.valueOf();
	if (mainLang == "rus") {
		value = value.replace(/(.+)\.(.+)\.(.+)/, "$2\/$1\/$3");
	}
	return new Date(value);
}

Object.extend( Array.prototype, {
	asOptions: function (selected) { 
		var out = new Array;
		out = this.collect( function (e) { return "<option value='" + e.id + "'" + (selected && selected == e.id ? " selected='selected' " : "") + ">" + e.name + "</option>"; });
		return out.join("");
	}
});

Element.addMethods(LX.Elements);
Element.addMethods(["FORM", "FIELDSET"], LX.Forms);
Element.addMethods(["INPUT", "SELECT", "TEXTAREA"], LX.Inputs);
Element.addMethods(["INPUT", "SELECT", "TEXTAREA", "A"], LX.Controls);

function CalendarChange () {
	var src = this;
	var value = src.getValue();
	var dateparent = src.previous("input#" + src.readAttribute("dateparent"));
	if (isEmptyString(dateparent.getValue())) {
		dateparent.setValue("%d%.%m%.%y%");
	}
	if (src.hasClassName("date")) {
		if (isEmptyString(value)) {
			value = "%d%";
		}
		dateparent.setValue(dateparent.getValue().replace(/%d%/, value).replace(/^(.+?)\.(.+?)\.(.+?)$/, value + ".$2.$3"));
	} else if (src.hasClassName("month")) {
		if (isEmptyString(value)) {
			value = "%m%";
		}
		dateparent.setValue(dateparent.getValue().replace(/%m%/, value).replace(/^(.+?)\.(.+?)\.(.+?)$/, "$1." + value + ".$3"));
	} else if (src.hasClassName("year")) {
		if (isEmptyString(value)) {
			value = "%y%";
		}
		dateparent.setValue(dateparent.getValue().replace(/%y%/, value).replace(/^(.+?)\.(.+?)\.(.+?)$/, "$1.$2." + value));
	}
}

function CalendarLoad () {
	
	var listdate, listmonth, listyear;
	
	$$("input.dateselect[type='text']").each( function (e) {
														
		listdate = "<option value=''></option>" + $R(1, 31).collect( function (e) { return "<option value='" + e + "'>" + e + "</option>"; } ).join("");
		listmonth = "<option value=''></option>" + $R(1, 12).collect( function (e) { return "<option value='" + e + "'>" + e + "</option>"; } ).join("");
		if (e.hasClassName("up")) {
			listyear = "<option value=''></option>" + $R((new Date).getYear() - 1, (new Date).getYear() + 70).collect( function (e) { return "<option value='" + e + "'>" + e + "</option>"; } ).join("");
		} else {
			listyear = "<option value=''></option>" + $R((new Date).getYear() - 70, (new Date).getYear() + 1).collect( function (e) { return "<option value='" + e + "'>" + e + "</option>"; } ).join("");
		}
		
		if (!e.readAttribute("datedate")) {
		
			e.hide();
			
			e.insert({after: "<select name='" + e.name + "_Y" + "' class='dateselector year'>" + listyear + "</select>&nbsp;&nbsp;"});
			var year = e.next("select.dateselector.year");
			year.writeAttribute("dateparent", e.identify()).observe("change", CalendarChange);
			e.writeAttribute("dateyear", year.identify());
			
			e.insert({after: "<select name='" + e.name + "_M" + "' class='dateselector month'>" + listmonth + "</select>&nbsp;&nbsp;"});
			var month = e.next("select.dateselector.month");
			month.writeAttribute("dateparent", e.identify()).observe("change", CalendarChange);
			e.writeAttribute("datemonth", month.identify());
			
			e.insert({after: "<select name='" + e.name + "_D" + "' class='dateselector date'>" + listdate + "</select>&nbsp;&nbsp;"});
			var date = e.next("select.dateselector.date");
			date.writeAttribute("dateparent", e.identify()).observe("change", CalendarChange);
			e.writeAttribute("datedate", date.identify());
			
			var parsed;
			if (parsed = e.getValue().match(/(\d{1,2})\.(\d{1,2})\.(\d{4})/)) {
				date.setValue(Number(RegExp.$1));
				month.setValue(Number(RegExp.$2));
				year.setValue(Number(RegExp.$3));
			}
		
		}
		
	});
	
	var options = {updateformat: "dd.mm.yyyy", weekdaystart: 1};
	Object.extend(Date.prototype, {
		monthnames: ["Январь", "Февраль", "Март", "Апрель", "Май", "Июнь", "Июль", "Август", "Сентябрь", "Октябрь", "Ноябрь", "Декабрь"],
		// daynames: ["Понедельник", "Вторник", "Среда", "Четверг", "Пятница", "Суббота", "Воскресенье"]
		daynames: ["Вс", "Пн", "Вт", "Ср", "Чт", "Пт", "Сб"]
	});
	
	$$("input.datecalendar").each( function (input) {
		var div, link, calendar;
		
		if (!input.readAttribute("calendared")) {
			
			input.writeAttribute("calendared");
		
			div = input.insert({after: "<div class='calendar biletbyscal'></div>"}).next("div.calendar").hide();
			
			// !!! VOODOO: &nbsp; is for prototype whitespace handling
			link = input.insert({after: "&nbsp;<a href='javascript://' class='input date'><img src='" + (mainAdm ? "../" : "") + "i/buttoncalendar.gif' alt='' /></a>"}).next("a.date");
			
			link.observe("click", function () {
				var src = this, date = new Date, d = new Number, m = new Number, y = new Number;
				if (src.readAttribute("disabled")) { return; }
				if (input.type != "hidden" && !input.disabled) {
					if (!div.visible()) {
						if (div.empty()) {
							calendar = new scal(div, input, options);
						}
						div.clonePosition(input, { setWidth: false, setHeight: false, offsetLeft: 5, offsetTop: 25 });
						if (input.value.match(/(\d{1,2})\.(\d{1,2})\.(\d{4})/)) {
							d = Number(RegExp.$1);
							m = Number(RegExp.$2) - 1;
							y = Number(RegExp.$3);
							date = new Date(y, m, d);
							if (!isNaN(date)) {
								calendar.setCurrentDate(date, true);
							}
						}
					}
					calendar.toggleCalendar();
				}
			});
		
		}
		
		// calendar = new scal(div, input, options);
	});
}

function setFlippingEdit (id) {
	var list = $(id).getElementsBySelector("div.flipping");
	for (var i = 0; i < list.length; i++) {
		
		var a = list[i].down("a");
		
		list[i].insert({top: (a && a.hasClassName("js") ? "&nbsp;" : "&nbsp;&middot;&nbsp;")});
		
		if (list[i].hasClassName("open")) {
			list[i].insert({top: "<a href='javascript://' class='inlineeditor flipper open js'>-</a>"});
		} else if (list[i].hasClassName("close")) {
			list[i].insert({top: "<a href='javascript://' class='inlineeditor flipper close js'>+</a>"});
		}
		
		list[i].down("a.inlineeditor.flipper").observe("click", function (ev) {
			var src = this;
			var tgt = src.next("div.inlineeditor.flipped");
			var parent = tgt.up("div.envelope");
			var temp;
			if (tgt.hasClassName("close")) {
				temp = parent.getElementsBySelector("a.inlineeditor.flipper.open");
				if (temp.length) {
					// temp.first().addClassName("close").removeClassName("open").innerHTML = "+";
					temp.first().addClassName("close").removeClassName("open").update("+");
				}
				temp = parent.getElementsBySelector("div.inlineeditor.flipped.open");
				if (temp.length) {
					temp.first().addClassName("close").removeClassName("open");
				}
				parent.addClassName("open").removeClassName("close");
				tgt.addClassName("open").removeClassName("close");
				// src.addClassName("open").removeClassName("close").innerHTML = "-";
				src.addClassName("open").removeClassName("close").update("-");
			} else if (tgt.hasClassName("open")) {
				parent.addClassName("close").removeClassName("open");
				tgt.addClassName("close").removeClassName("open");
				// src.addClassName("close").removeClassName("open").innerHTML = "+";
				src.addClassName("close").removeClassName("open").update("+");
			}
		});
	}
}

function setRankingSort (id) {
	var list = $(id).getElementsBySelector("div.ranking");
	for (var i = 0; i < list.length; i++) {
		list[i].insert({top: (list[i].down("a").hasClassName("js") ? "&nbsp;" : "&nbsp;&middot;&nbsp;")});
		list[i].insert({top: "<a href='javascript://' class='rankerdown js' title='Ctrl+click to very bottom'>&#8595;</a>"});
		list[i].insert({top: "&nbsp;"});
		list[i].insert({top: "<a href='javascript://' class='rankerup js' title='Ctrl+click to very top'>&#8593;</a>"});
		list[i].down("a.rankerup").observe("click", RankingSort);
		list[i].down("a.rankerdown").observe("click", RankingSort);
	}
}

function setRankingSortTR (id) {
	var list = $(id).select("tr.ranking td:first-child");
	for (var i = 0; i < list.length; i++) {
		list[i].insert({top: (list[i].down("a") && list[i].down("a").hasClassName("js") ? "&nbsp;" : "&nbsp;&middot;&nbsp;")});
		list[i].insert({top: "<a href='javascript://' class='rankerdown js' title='Ctrl+click to very bottom'>&#8595;</a>"});
		list[i].insert({top: "&nbsp;"});
		list[i].insert({top: "<a href='javascript://' class='rankerup js' title='Ctrl+click to very top'>&#8593;</a>"});
		list[i].down("a.rankerup").observe("click", RankingSortTR);
		list[i].down("a.rankerdown").observe("click", RankingSortTR);
	}
}

function RankingSort (ev) {
	var src = this;
	
	var parent, tgt, sect, flag = false, direction = new String, temp = new String, paramid = new Array, parampos = new Array;

	direction = $w(src.className).without("js").reduce();
	src = $(src).up("div.ranking");
	parent = src.up("div.envelope");
	sect = src.className.match(/sect_(.+?\b)/)[1];
	
	if (direction == "rankerup" && !ev.ctrlKey) {
		tgt = src.previous("div.ranking");
		if (tgt) {
			flag = true;
			parent.insertBefore(src, tgt);
			temp = src.className;
			src.className = tgt.className;
			tgt.className = temp;
			paramid.push(tgt.id.match(/rankid_(\d+)/)[1]); 
			parampos.push(tgt.className.match(/rankpos_(\d+)/)[1]);
			paramid.push(src.id.match(/rankid_(\d+)/)[1]); 
			parampos.push(src.className.match(/rankpos_(\d+)/)[1]);
		}
	} else if (direction == "rankerdown" && !ev.ctrlKey) {
		tgt = src.next("div.ranking");
		if (tgt) {
			flag = true;
			parent.insertBefore(tgt, src);
			temp = src.className;
			src.className = tgt.className;
			tgt.className = temp;
			paramid.push(tgt.id.match(/rankid_(\d+)/)[1]); 
			parampos.push(tgt.className.match(/rankpos_(\d+)/)[1]);
			paramid.push(src.id.match(/rankid_(\d+)/)[1]); 
			parampos.push(src.className.match(/rankpos_(\d+)/)[1]);
		}
	} else if (direction == "rankerup" && ev.ctrlKey) {
		while (tgt = src.previous("div.ranking")) {
			flag = true;
			parent.insertBefore(src, tgt);
			temp = src.className;
			src.className = tgt.className;
			tgt.className = temp;
			paramid.push(tgt.id.match(/rankid_(\d+)/)[1]); 
			parampos.push(tgt.className.match(/rankpos_(\d+)/)[1]);			
		}
		paramid.push(src.id.match(/rankid_(\d+)/)[1]); 
		parampos.push(src.className.match(/rankpos_(\d+)/)[1]);
	} else if (direction == "rankerdown" && ev.ctrlKey) {
		while (tgt = src.next("div.ranking")) {
			flag = true;
			parent.insertBefore(tgt, src);
			temp = src.className;
			src.className = tgt.className;
			tgt.className = temp;
			paramid.push(tgt.id.match(/rankid_(\d+)/)[1]); 
			parampos.push(tgt.className.match(/rankpos_(\d+)/)[1]);			
		}
		paramid.push(src.id.match(/rankid_(\d+)/)[1]); 
		parampos.push(src.className.match(/rankpos_(\d+)/)[1]);
	}
	
	if (flag) {
		new Ajax.Request(siteAjax + "?ajax=ranking&sect=" + sect + "&idlist=" + paramid.join(",") + "&poslist=" + parampos.join(","), {
			element: $("container"),
			method: "get"
		});
	}
}

function RankingSortTR (ev) {
	var src = this;
	
	var parent, tgt, sect, flag = false, direction = new String, temp = new String, paramid = new Array, parampos = new Array;

	direction = $w(src.className).without("js").reduce();
	src = $(src).up("tr.ranking");
	parent = src.up("tbody.envelope");
	sect = src.className.match(/sect_(.+?\b)/)[1];
	
	if (direction == "rankerup" && !ev.ctrlKey) {
		tgt = src.previous("tr.ranking");
		if (tgt) {
			flag = true;
			parent.insertBefore(src, tgt);
			temp = src.className;
			src.className = tgt.className;
			tgt.className = temp;
			paramid.push(tgt.id.match(/rankid_(\d+)/)[1]); 
			parampos.push(tgt.className.match(/rankpos_(\d+)/)[1]);
			paramid.push(src.id.match(/rankid_(\d+)/)[1]); 
			parampos.push(src.className.match(/rankpos_(\d+)/)[1]);
		}
	} else if (direction == "rankerdown" && !ev.ctrlKey) {
		tgt = src.next("tr.ranking");
		if (tgt) {
			flag = true;
			parent.insertBefore(tgt, src);
			temp = src.className;
			src.className = tgt.className;
			tgt.className = temp;
			paramid.push(tgt.id.match(/rankid_(\d+)/)[1]); 
			parampos.push(tgt.className.match(/rankpos_(\d+)/)[1]);
			paramid.push(src.id.match(/rankid_(\d+)/)[1]); 
			parampos.push(src.className.match(/rankpos_(\d+)/)[1]);
		}
	} else if (direction == "rankerup" && ev.ctrlKey) {
		while (tgt = src.previous("tr.ranking")) {
			flag = true;
			parent.insertBefore(src, tgt);
			temp = src.className;
			src.className = tgt.className;
			tgt.className = temp;
			paramid.push(tgt.id.match(/rankid_(\d+)/)[1]); 
			parampos.push(tgt.className.match(/rankpos_(\d+)/)[1]);			
		}
		paramid.push(src.id.match(/rankid_(\d+)/)[1]); 
		parampos.push(src.className.match(/rankpos_(\d+)/)[1]);
	} else if (direction == "rankerdown" && ev.ctrlKey) {
		while (tgt = src.next("tr.ranking")) {
			flag = true;
			parent.insertBefore(tgt, src);
			temp = src.className;
			src.className = tgt.className;
			tgt.className = temp;
			paramid.push(tgt.id.match(/rankid_(\d+)/)[1]); 
			parampos.push(tgt.className.match(/rankpos_(\d+)/)[1]);			
		}
		paramid.push(src.id.match(/rankid_(\d+)/)[1]); 
		parampos.push(src.className.match(/rankpos_(\d+)/)[1]);
	}
	
	if (flag) {
		new Ajax.Request(siteAjax + "?ajax=ranking&sect=" + sect + "&idlist=" + paramid.join(",") + "&poslist=" + parampos.join(","), {
			element: $("container"),
			method: "get"
		});
	}
}

function setDefault (variable, valuedef) {
	if (isNot(variable)) {
		variable = valuedef;
	}
	return variable;
}

function isUndefined (value) {
	return (typeof value == "undefined" || (typeof value == "string" && value == "undefined"));
}

function isNull (value) {
	return (value == null);
}

function isNot (value) {
	return isUndefined(value) || isNull(value);
}

function isEmptyString (value) {
	return isUndefined(value) || isNull(value) || String(value).replace(/\s+/g, " ").replace(/(^\s)|(\s$)/, "") == "";
}

function isEmptyDate (value) {
	return isUndefined(value) || isNull(value) || value == 0;
}
 
function FCKeditor_OnComplete( editorInstance )
{
	editorInstance.Events.AttachEvent( 'OnBlur'	, FCKeditor_OnBlur ) ;
	editorInstance.Events.AttachEvent( 'OnFocus', FCKeditor_OnFocus ) ;
}

function FCKeditor_OnBlur(editorInstance) {
	editorInstance.ToolbarSet.Collapse();
	editorInstance.UpdateLinkedField();
}

function FCKeditor_OnFocus( editorInstance )
{
	editorInstance.ToolbarSet.Expand() ;
}

function setFlippingTag (id) {
/*	var list = $(id).getElementsByTagName("a");
	for (var i = 0; i < list.length; i++) {
		if (list[i].id.indexOf("tag-show-control_") == 0) {
			list[i].observe("click", flipTagShow);
		}
		if (list[i].className.indexOf("tag-click") != -1) {
			list[i].observe("click", flipTag);
		}
	} */
	// !!! 2DO: tag click engine
	var list = $(id).getElementsBySelector("a.tagger.flipper");
	for (var i = 0; i < list.length; i++) {
		list[i].observe("click", function (ev) {
			var src = this;
			var tgt = src.next("div.tagger.flipped");
			if (tgt.hasClassName("close")) {
				tgt.addClassName("open").removeClassName("close");
				src.addClassName("open").removeClassName("close");
			} else if (tgt.hasClassName("open")) {
				tgt.addClassName("close").removeClassName("open");
				src.addClassName("close").removeClassName("open");
			}
		});
	}
}

function setPoppingShow (id) {
	var list = $(id).getElementsBySelector("a.popping");
	for (var i = 0; i < list.length; i++) {
		list[i].observe("click", function (ev) {
			var src = ev.findElement("a");
			ev.stop();
			var winWidth = Math.floor(screen.availWidth * 0.75), winHeight = Math.floor(screen.availHeight * 0.75);
			var winLeft = (screen.availWidth - winWidth) / 2, winTop = (screen.availHeight - winHeight) / 2;
			window.open(src.href,  "_blank", "width=" + winWidth + ", height=" + winHeight + ", left=" + winLeft + ", top=" + winTop + ", location=0, resizable=0, menubar=0, toolbar=0, status=1, scrollbars=1");
		});
	}
}

function flipTag (ev) { 
	var src = this;
	var tgt = $(src.id.replace(/^(tag-click_)(.+)(_\d+)$/, "$2"));
	var oldarray = new Array, newarray = new Array;
	oldarray = tgt.value.split(/\s*,\s*/);
	var length = oldarray.length;
	var flag = false;
	for (var i = 0; i < length; i++) {
		if (oldarray[i].toLowerCase() == src.innerHTML.toLowerCase()) {
			oldarray[i] = undefined;
			flag = true;
			break;
		}
	}
	if (!flag) {
		oldarray.push(src.innerHTML);
		src.className += " selected";
	} else {
		src.className = src.className.replace(/\s*selected\s*/, "");
	}
	
	for (var i = 0; i < oldarray.length; i++) {
		if (!isEmptyString(oldarray[i])) {
			newarray.push(oldarray[i]);
		}
	}
	tgt.value = newarray.join(", ");
}

function flipUploadFrame (ev) {
	var src = this;
	var tgt = $("upload-frame_" + src.id.slice(src.id.indexOf("_") + 1));
	// L8R: rewrite for prototype.js class name switching
	var array = tgt.className.split(/\s/);
	for (var i = 0; i < array.length; i++) {
		if (array[i] == "open") {
			array[i] = "close";
		} else if (array[i] == "close") {
			array[i] = "open";
		}
	}
	tgt.className = array.join(" ");
}

function outPassword (count) {
	var out = new String;
	var arr = new Array("2", "3", "4", "5", "6", "7", "8", "9", "A", "B", "C", "D", "E", "F", "G", "H", "J", "K", "M", "N", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z", "a", "b", "c", "d", "e", "f", "g", "h", "j", "k", "m", "n", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z");
	for (var i = 0; i < count; i++) {
		out += arr[outRandInt(arr.length - 1)];
	}
	return out;
}

function outRandInt (hi) {
	// L8R: math stats 
	// return Math.ceil(Math.random() * hi);
	// return Math.floor(Math.random() * hi);
	return Math.round(Math.random() * hi);
}

document.write("<style type='text/css'>");
document.write("#container { display: block; }");
document.write("#jsblocked { display: none; }");
document.write("</style>");
