Number.implement({

			/**
			 * Смотрим, четное ли число
			 * 
			 * @param {Number}
			 *            num Четное или нечетное число
			 */
			isOdd : function() {
				var num = this.toInt();
				return (num % 2) ? true : false;
			},

			/**
			 * Проверяем, является ли значение поля числом с плавающей запятой
			 * 
			 * @param {String|Element}
			 *            elm ссылка на элемент, либо дентификатор элемента, в
			 *            котором стоит проверить значение
			 * @return {Boolean} правда, если значение поля - число в плавающей
			 *         запятой; ложь - в обратном случае
			 */
			isFloat : function() {
				var val = this;
				val += '';
				if (/^[-+]?[0-9]+(\.[0-9]+)?$/.test(val)) {
					return true;
				}
				return false;
			}
		});

Array.implement({

			/**
			 * Функция, создания диапазона целых чисел
			 * 
			 * @param {Number}
			 *            start наименьшее значение
			 * @param {Number}
			 *            stop наибольшее значение
			 * 
			 * @return {Array}
			 */
			range : function() {
				var start = this[0];
				var stop = this[1];
				var nw = [];
				--start;
				while (++start <= stop) {
					nw.include(start);
				}
				return nw;
			},

			/**
			 * Находит наибольшее значение массива чисел
			 * 
			 * @return {Number} максимальное значение массива натуральных чисел
			 */
			getMax : function() {
				var max_value;
				for (var i = 0; i < this.length; i++) {
					if (!$chk(max_value)) {
						max_value = this[i].toInt();
					}
					if (this[i].toInt() > max_value) {
						max_value = this[i].toInt();
					}
				}
				return max_value;
			},

			/**
			 * Находит наименьшее значение массива чисел
			 * 
			 * @return {Number} минимальное занчение массива натуральных чисел
			 */
			getMin : function() {
				var min_value;
				for (var i = 0; i < this.length; i++) {
					if (!$chk(min_value)) {
						min_value = this[i].toInt();
					}
					if (this[i].toInt() < min_value) {
						min_value = this[i].toInt();
					}
				}
				return min_value;
			},

			/**
			 * @description Находит разницу массивов
			 * 
			 * @param {}
			 *            v вычитаемый массив
			 * @param {Boolean}
			 *            m в случае если тру, то возвращается только индекс
			 *            базового массива
			 * @return {Array} разницу массивов
			 */
			diff : function(v, m) {
				if (v.length) {
					var d = [], e = -1, h, i, j, k;
					for (i = this.length, k = v.length; i--;) {
						for (j = k; j && (h = this[i] !== v[--j]););
						h && (d[++e] = m ? i : this[i]);
					}
					return d;
				}
				return this;
			},

			/**
			 * @description Из массива объектов типа checkbox или radio находит
			 *              выделенные и возвращает в виде массива элементов
			 * 
			 * @return {Array} Выделенные элементы
			 */
			getChecked : function() {
				var chkd = [];
				for (var i = 0, len = this.length; i < len; i++) {
					if (this[i].get('tag') == 'input'
							&& (this[i].getProperty('type') == 'radio' || this[i]
									.getProperty('type') == 'checkbox')) {
						if (this[i].checked) {
							chkd.push(this[i]);
						}
					} else {
						break;
					}
				}
				return chkd;
			}
		});

Element.implement({

	/**
	 * Функция переключает класс hide - то есть переключает видимость элемента
	 * Нетрудно понять, что в стиля должен быть прописан стиль .hide { display:
	 * none; }
	 */
	toggle : function() {
		if (this.hasClass('hide')) {
			this.removeClass('hide');
		} else {
			this.addClass('hide');
		}
		return this;
	},

	/**
	 * Открывает элемент
	 */
	show : function() {
		if (this.hasClass('hide')) {
			this.removeClass('hide');
		}
		return this;
	},

	/**
	 * @description Скрывает элемент (добавляет класс hide)
	 */
	hide : function() {
		if (!this.hasClass('hide')) {
			this.addClass('hide');
		}
		return this;
	},

	/**
	 * Функция переключает элементу значение указанного аттрибута
	 * 
	 * @param {String}
	 *            attr атрибут, с которым работаем
	 * @param {Array}
	 *            values Значения атрибута, которые нужно менять
	 */
	toggleAttributes : function(attr, values) {
		if (attr == 'class') {
			if (this.hasClass(values[0])) {
				this.removeClass(values[0]);
				this.addClass(values[1]);
				return;
			}
			if (this.hasClass(values[1])) {
				this.removeClass(values[1]);
				this.addClass(values[0]);
				return;
			}
		} else {
			if ($(this).get(attr) == values[0]) {
				$(this).set(attr, values[1]);
			} else {
				$(this).set(attr, values[0]);
			}
		}
		return this;
	},

	/**
	 * Функция ресетает форму, в которой жестко прописаны <code>value</code>
	 * 
	 * @param {Element|String}
	 *            frm элемент формы, либо его идентфикатор
	 */
	resetForm : function() {
		if (!this.get('tag') !== 'form') {
			// если это не форма, то получается косяк
			return;
		}
		var control_elements = this.getElements('input, select, textarea');
		for (var i = 0, len = control_elements.length; i < len; i++) {
			if (control_elements[i].get('tag') == 'input'
					&& control_elements[i].getProperty('type') != 'reset'
					&& control_elements[i].getProperty('type') != 'submit') {
				if ((control_elements[i].getProperty('type') == 'radio' || control_elements[i]
						.getProperty('type') == 'checkbox')
						&& control_elements[i].checked) {
					control_elements[i].checked = false;
				}
				if (control_elements[i].getProperty('type') == 'text') {
					control_elements[i].value = '';
				}
			}
			if (control_elements[i].get('tag') == 'select') {
				control_elements[i].options[0].selected = true;
			}
			if (control_elements[i].get('tag') == 'textarea') {
				control_elements[i].value = '';
			}
		}
		return this;
	}
});

String.implement({
			/**
			 * Фукнция-транслитератор
			 * 
			 * @param {String}
			 *            string строка, которую нужно транслитирировать
			 * @return {String}
			 */
			transliterate : function(string) {
				string = this.trim();
				var cyr = ["Щ", "Ш", "Ч", "Ц", "Ю", "Я", "Ж", "А", "Б", "В",
						"Г", "Д", "Е", "Ё", "З", "И", "Й", "К", "Л", "М", "Н",
						"О", "П", "Р", "С", "Т", "У", "Ф", "Х", "Ь", "Ы", "Ъ",
						"Э", "Є", "Ї", "щ", "ш", "ч", "ц", "ю", "я", "ж", "а",
						"б", "в", "г", "д", "е", "ё", "з", "и", "й", "к", "л",
						"м", "н", "о", "п", "р", "с", "т", "у", "ф", "х", "ь",
						"ы", "ъ", "э", "є", "ї"];
				var lat = ["Shh", "Sh", "Ch", "C", "Ju", "Ja", "Zh", "A", "B",
						"V", "G", "D", "Je", "Jo", "Z", "I", "J", "K", "L",
						"M", "N", "O", "P", "R", "S", "T", "U", "F", "Kh", "'",
						"Y", "`", "E", "Je", "Ji", "shh", "sh", "ch", "c",
						"ju", "ja", "zh", "a", "b", "v", "g", "d", "je", "jo",
						"z", "i", "j", "k", "l", "m", "n", "o", "p", "r", "s",
						"t", "u", "f", "kh", "'", "y", "'", "e", "je", "ji"];

				for (var i = 0, len = cyr.length; i < len; i++) {
					string = string.replace(new RegExp(cyr[i], 'g'), lat[i]);
				}
				string = string.replace(
						/([qwrtpsdfghklzxcvbnmQWRTPSDFGHKLZXCVBNM]+)[jJ]e/g,
						"$1e");
				string = string.replace(
						/([qwrtpsdfghklzxcvbnmQWRTPSDFGHKLZXCVBNM]+)[jJ]/g,
						"$1'");
				string = string.replace(/([eyuioaEYUIOA]+)[Kk]h/g, "$1h");
				string = string.replace(/^kh/g, "h");
				string = string.replace(/^Kh/g, "H");
				string = string.replace(/[^a-zA-Z0-9_\.!?&']/g, "_");
				string = string.replace(/\n+/g, "_");

				return string;
			}
		});

/**
 * Функция подключения зависимостей
 */
function require() {
	var libs = [];
	var args = Array.prototype.slice.call(arguments);
	if (args.length) {
		if ($type(args[0]) == 'array') {
			libs = args[0];
		} else if ($type(args[0]) == 'string') {
			libs = args.filter(function(n) {
						return $type(n) == 'string';
					});
		} else {
			return false;
		}
	} else {
		return false;
	}
	var scripts_string = ''
	for (var i = 0, len = libs.length; i < len; i++) {
		if (libs[i].length) {
			scripts_string += '\n<scr' + 'ipt type="text/javascript" src="/js'
					+ libs[i].hyphenate().replace(/\-/g, '/') + '.js"></scr'
					+ 'ipt>';
		}
	}
	document.write(scripts_string);
}

/**
 * @description Обработчик событий вешается на элемент управления формой и
 *              запрещает вводить ничего, кроме цифр
 * 
 * @param {Event}
 *            e Событие
 */
function justNumber(e) {
	var event = new Event(e);
	// коды клавиш, печатающих цифры
	var number_key_codes = [48, 57].range().extend([96, 105].range()).extend([
			8, 9, 13, 17, 18, 37, 39, 46, 116]);

	if (!number_key_codes.contains(event.code) && !event.meta) {
		event.stop();
	}
}

/**
 * @description Активирует и деактивирует все сабмиты
 * 
 * @param {Boolean}
 *            pos Активировать все сабмиты или деактивировать
 */
function disableSubmits(pos) {
	$each($$('input[type=submit]'), function(subm) {
				if (pos) {
					subm.disabled = true;
				} else {
					subm.disabled = false;
				}
			});

}

/**
 * Автоматическая валидация формы С сервера приходит JSON и переменная
 * validate_forms. Если validate_forms == true, то происходит следующее. По
 * загрузке DOM'a перехватываются все кнопки submit. Далее находится тег form,
 * внутри которого стоит каждая из кнопок и считывается айдишник этой формы
 * (если он есть). Затем мы смотрим, есть ли такой айдишник в JSON и делаем
 * проверку.
 */
window.addEvent('domready', function() {
			if (typeof validate_forms != 'undefined' && validate_forms) {
				if (typeof validation_data != "undefined"
						&& typeof validation_data == 'object'
						&& typeof FormValidator != "undefined") {
					for (var frm in validation_data) {
						FormValidator.set(frm, validation_data[frm], true);
					}
				}

				var forms = $$('form');
				$each(forms, function(frm) {
							var frm_id = frm.getProperty('id');
							if (typeof validation_data[frm_id] != 'undefined') {
								frm.addEvent('submit', function(e) {
											disableSubmits(true);
											if (!FormValidator.validate(frm_id)) {
												new Event(e).stop();
												disableSubmits(false);
											}
										});
							}
						});
			}
		});

/**
 * При выгрузке страницы енэйблим сабмиты обратно, чтобы нажатие "назад" не
 * приводило к показу страниц с отсутствующими сабмитами
 */
window.addEvent('unload', function() {
			disableSubmits(false);
		});
