$type.element = function(element) {
	return $type(element) == 'element';
}
$type.textnode = function(element) {
	return $type(element) == 'textnode';
}
$type.whitespace = function(element) {
	return $type(element) == 'whitespace';
}
$type.arguments = function(element) {
	return $type(element) == 'arguments';
}
$type.array = function(element) {
	return $type(element) == 'array';
}
$type.object = function(element) {
	return $type(element) == 'object';
}
$type.string = function(element) {
	return $type(element) == 'string';
}
$type.number = function(element) {
	return $type(element) == 'number';
}
$type.date = function(element) {
	return $type(element) == 'date';
}
$type.bool = function(element) {
	return $type(element) == 'boolean';
}
$type.func = function(element) {
	return $type(element) == 'function';
}
$type.regexp = function(element) {
	return $type(element) == 'regexp';
}
$type.Class = function(element) {
	return $type(element) == 'class';
}
$type.collection = function(element) {
	return $type(element) == 'collection';
}

Array.implement({
			/**
			 * Функция, создания диапазона целых чисел
			 * 
			 * @return {Array}
			 */
			range : function() {
				if (this.length < 2)
					return false;
				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 {Array}
			 *            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;
			},
			
			intersect : function(a, m) {
			    var s = this;
			    var l = this.length;
			    var int = [];
			    
			    for (var i = 0; i < l; i++) {
			        if (a.contains(this[i])) {
			            if (m) {
			                int.push(i);
			            } else {
			                int.push(this[i]);
			            }
			        };
			    };
			    
			    return int;
			}
		});

Element.implement({

	/**
	 * Функция переключает класс hide - то есть переключает видимость элемента
	 * Нетрудно понять, что в стиля должен быть прописан стиль .hide { display:
	 * none; }
	 */
	toggle : function() {
		if (this.hasClass('hide')) {
			this.removeClass('hide');
		} else {
			this.addClass('hide');
		}
		return this;
	},

	/**
	 * Открывает элемент
	 */
	show : function(styles) {
	    if (arguments.length) {
	        this.setStyles(arguments[1]);
	    };
	    
		if (this.hasClass('hide')) {
			this.removeClass('hide');
		}
		return this;
	},
	
	showAt : function(x, y) {
	    this.setStyles({
	        top: y,
	        left: x
	    }).show();
	    return this;
	},

	/**
	 * @description Скрывает элемент (добавляет класс hide)
	 */
	hide : function() {
		if (!this.hasClass('hide')) {
			this.addClass('hide');
		}
		return this;
	},

	/**
	 * @description Скрыт элемент или нет
	 */
	isVisible : function() {
		return !this.hasClass('hide');
	},

	/**
	 * Функция переключает элементу значение указанного аттрибута
	 * 
	 * @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') {
				if (control_elements[i].getProperty('multiple')) {
					for (var j = 0; j < control_elements[i].options.length; j++) {
						control_elements[i].options[j].selected = false;
					}
				} else {
					control_elements[i].options[0].selected = true;
				}
			}
			if (control_elements[i].get('tag') == 'textarea') {
				control_elements[i].value = '';
			}
		}
		return this;
	},
	/**
	 * Проверяем, является ли значение поля числом с плавающей запятой
	 * 
	 * @return {Boolean} правда, если значение поля - число в плавающей запятой;
	 *         ложь - в обратном случае
	 */
	isFloat : function() {
		var val = this.get('text');
		if (/^[-+]?[0-9]+(\.[0-9]+)?$/.test(val)) {
			return true;
		}
		return false;
	},
	
	emphasize : function(c, a) {
		if (!a) {
			a = this.retrieve("highlight:original", this
							.getStyle("color"));
			a = (a == "transparent") ? "#fff" : a;
		}
		var b = this.get("tween");
		b.start("color", c || "#ffff88", a).chain(
				function() {
					this.setStyle("color", this
									.retrieve("highlight:original"));
					b.callChain();
				}.bind(this));
		return this;
	},
	
	twitch: function() {
	    var timer = null;
	    var offset = 1;
	    var el = this;
	    var times = 5;
	    var currentIteration = 0;
	    var speed = 50;
	    el.setStyle('position', 'relative');
	    var t = function() {
	        if (currentIteration == times) {
	            $clear(timer);
	            timer = null;
	            el.setStyles({
	                'position': '',
	                'left': ''
	            });
	        } else {
	            if (offset > 0) {
	                offset = -1;
	            } else {
	                offset = 1;
	            }
	            el.setStyle('left', offset);
	            currentIteration++;
	        }
	    }
	    timer = t.periodical(speed);
	},

    hasEvent: function(eventType,fn) {
        //get the element's events
        var myEvents = this.retrieve('events');
        //can we shoot this down?
        return myEvents && myEvents[eventType] && (fn == undefined || myEvents[eventType].keys.contains(fn));
    }
});

Document.implement({
    hasEvent: function(eventType,fn) {
        //get the element's events
        var myEvents = this.retrieve('events');
        //can we shoot this down?
        return myEvents && myEvents[eventType] && (fn == undefined || myEvents[eventType].keys.contains(fn));
    }
});

String.implement({
			/**
			 * Проверяем, является ли значение поля числом с плавающей запятой
			 * 
			 * @return {Boolean} правда, если значение поля - число в плавающей
			 *         запятой; ложь - в обратном случае
			 */
			isFloat : function() {
				var val = this;
				val += '';
				if (/^[-+]?[0-9]+(\.[0-9]+)?$/.test(val)) {
					return true;
				}
				return false;
			},
			/**
			 * Смотрим, четное ли число
			 * 
			 * @return {Boolean} правда, если значение поля - четное; ложь - в
			 *         обратном случае
			 */
			isOdd : function() {
				var num = this.toInt();
				return (num % 2) ? true : false;
			},
			/**
			 * Фукнция-транслитератор
			 * 
			 * @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;
			},
			
			decline : function(original_num, ends_with_zero_five_six_seven_eight_nine, eleven_hundred_eleven, one, twelve_thirteen_fourteen_hundred_twelve_hundred_thirteen_hundred_fourteen, two_three_four) {
			    var prefix = this;
            	var suffix = '';
            	var num = original_num + '';
            	var last = num.substr(num.length - 1, num.length).toInt();
            	var twolast = num.substr(num.length - 2, num.length).toInt();
            	switch (last) {
            		case 0 :
            		case 5 :
            		case 6 :
            		case 7 :
            		case 8 :
            		case 9 :
            		    // все, что заканчивается на 0, 5, 6, 7, 8, 9
            			suffix = ends_with_zero_five_six_seven_eight_nine;
            			break;
            		case 1 :
            			if (twolast > 10 && twolast <= 14) {
            			    // 11, 111...
            				suffix = eleven_hundred_eleven;
            			} else {
            			    // 1
            				suffix = one;
            			}
            			break;
            		case 2 :
            		case 3 :
            		case 4 :
            			if (twolast > 10 && twolast <= 14) {
            			    // 12, 13, 14, 112, 113, 114...
            				suffix = twelve_thirteen_fourteen_hundred_twelve_hundred_thirteen_hundred_fourteen;
            			} else {
            			    // 2, 3, 4
            				suffix = two_three_four;
            			}
            			break;
            	}
            	return prefix + suffix;
			}
		});

Function.implement({
			/**
			 * @description Debounces function
			 * @param {Number}
			 *            threshold
			 * @param {Boolean}
			 *            execAsap
			 * @return {Function}
			 */
			debounce : function(threshold, execAsap) {
				var func = this, timeout;

				return function debounced() {
					var obj = this, args = arguments;
					function delayed() {
						if (!execAsap) {
							func.run(args, obj);
						}
						timeout = null;
					}

					if (timeout) {
						$clear(timeout);
					} else if (execAsap) {
						func.run(args, obj);
					}

					timeout = delayed.delay(threshold || 100);
				}
			}
		});

Number.implement({
	/**
	 * 
	 * @param {String}
	 *            c количество знаком после запятой
	 * @param {String}
	 *            d десятичный разделитель (запятая, например)
	 * @param {String}
	 *            t тысячный разделитель 100 000 000
	 * @return {String} форматированное число
	 */
	formatMoney : function(c, d, t) {
		var n = this, c = isNaN(c = Math.abs(c)) ? 2 : c, d = d == undefined
				? ","
				: d, t = t == undefined ? "." : t, s = n < 0 ? "-" : "", i = parseInt(n = Math
				.abs(+n || 0).toFixed(c))
				+ "", j = (j = i.length) > 3 ? j % 3 : 0;
		return s + (j ? i.substr(0, j) + t : "")
				+ i.substr(j).replace(/(\d{3})(?=\d)/g, "$1" + t)
				+ (c ? d + Math.abs(n - i).toFixed(c).slice(2) : "");
	}
});

function $get(key, url) {
	if (arguments.length < 2)
		url = location.href;
	if (arguments.length > 0 && $type.string(key) && key != "") {
		if (key == "#") {
			var regex = new RegExp("[#]([^$]*)");
		} else if (key == "?") {
			var regex = new RegExp("[?]([^#$]*)");
		} else {
			var regex = new RegExp("[?&]" + key + "=([^&#]*)");
		}
		var results = regex.exec(url);
		return (results == null) ? "" : results[1];
	} else {
		url = url.split("?");
		var results = {};
		if (url.length > 1) {
			url = url[1].split("#");
			if (url.length > 1)
				results["hash"] = url[1];
			url[0].split("&").each(function(item, index) {
						item = item.split("=");
						results[item[0]] = item[1];
					});
		}
		return results;
	}
}

/**
 * @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 Активировать все сабмиты или деактивировать
 */

var disableSubmits = null;

window.addEvent('domready', function() {
			// инициализируем функцию disableSubmits, чтобы она знала про все
			// кнопки-сабмиты на странице
			// находим все сабмиты на странице
			var submits = $$('input[type=submit], button[type=submit], input[type=image]');
			disableSubmits = (function() {
				return function(pos) {
					$each(submits, function(subm) {
								if (pos) {
									subm.disabled = true;
								} else {
									subm.disabled = false;
								}
							});
				}
			})();
			// Находим все формы на странице
			var forms = $$('form');
			// циклимся по всем найденным формам
			$each(forms, function(form) {
						// айдишник формы, либо нулл
						var form_id = form.getProperty('id') || null;

						// если есть установки по проверке и у формы есть
						// айдишник
						if (typeof validation_data != 'undefined'
								&& $chk(form_id)
								&& $chk(validation_data[form_id])
								&& $chk(FormValidator)) {

							// инициализируем валидатор
							FormValidator.set(form, validation_data[form_id],
									true);

							// добавляем проверку формы при сабмите
							form.addEvent('submit', function(e) {
										disableSubmits(true);
										if (!FormValidator.validate(form_id)) {
											new Event(e).stop();
											// освобождаем кнопки-сабмиты
											disableSubmits(false);
										}
									});
						} else {
							// при сабмите блокируем все кнопки-сабмиты, если форме это действительно нужно
							// если форме не нужно, то у нее будет стоять класс always-submit
							if (!form.hasClass('always-submit')) {
							    form.addEvent('submit', function(e) {
										    disableSubmits(true);
									    });
							}
						}
					});
		});

/**
 * При выгрузке страницы енэйблим сабмиты обратно, чтобы нажатие "назад" не
 * приводило к показу страниц с отсутствующими сабмитами
 */
window.addEvent('unload', function() {
			disableSubmits(false);
		});

// заглушка на консоль
if (!window.console) {
    window.console = {
        log: function(){}
    };
}

// объявление данных для проверки
var validation_data = {};

// скроллер окошка
var windowScroller;
if (Fx.Scroll) {
    windowScroller = new Fx.Scroll($(window), {
        duration: 200
    });
} else {
    windowScroller = {
        start: function() {
            alert('Подключи mootools.more!');
        }
    };
};

// класс disabled
window.addEvent('domready',function(){

    $each($$('.disabled'), function(item){
        item.disabled = true;
    });
    

});
