(function($){

	// приватные члены dao
	
	// публичные члены dao
	window.dao = {
		
		key: {
			BACKSPACE:				8,
			TAB:							9,
			ENTER:						13,
			SHIFT: 						16,
			CONTROL: 				17,
			ALT: 							18,
			PAUSE: 						19,
			CAPS_LOCK: 				20,
			ESCAPE: 					27,
			SPACE: 						32,
			PAGE_UP: 					33,
			PAGE_DOWN: 			34,
			END: 						35,
			HOME: 						36,
			LEFT: 						37,
			UP: 							38,
			RIGHT: 						39,
			DOWN: 						40,
			INSERT: 					45,
			DELETE: 					46,
			NUMPAD_MULTIPLY: 	106,
			NUMPAD_ADD: 			107,
			NUMPAD_ENTER: 		108,
			NUMPAD_SUBTRACT: 109,
			NUMPAD_DECIMAL: 	110,
			NUMPAD_DIVIDE: 		111,
			F1:							112,
			F2:							113,
			F3:							114,
			F4:							115,
			F5:							116,
			F6:							117,
			F7:							118,
			F9:							119,
			F9:							120,
			F10:							121,
			F11:							122,
			F12:							123,
			NUMLOCK:					144,
			SCROLLLOCK:			145,
			COMMA: 					188,
			PERIOD: 					190
		},

		is_loaded: {},

		load_script: function (name, callback) {
			var instance = this;
			
			if(!$.isArray(name)) {
				name = [name];
			}
			
			var names = [];
			for (var i in name) {
				var v = name[i];
				if(!this.is_loaded[v]) {
					names.push(v);
				}
			}
			
			function checkLoad(name) {
				if(name) {
					instance.is_loaded[name] = true;
//					for(var i in names) { var v = names[i];
//						if(v == name) {
//							names.splice(i, 1);
//							break;
//						}
//					}
				}
				if(names.length) {
					name = names.shift();
					load(name);
				} else if(callback) {
					var cb = callback;
					delete callback;
					cb.call();
				}
			}

			function load(name) {
				$.ajax({
					  url: name,
					  dataType: 'script',
					  cache: true,
					  success: function() { 
							checkLoad(name);
						}
					});
			}
			
//			for(var i in names) { var name = names[i];
//				load(name);
//			}
			
			checkLoad();
		},
		
		re_float_prepare: /,/,
		prepareNumber: function(value) {
			return String(value).replace(dao.re_float_prepare, '.');
		},
		/** Базовые валидаторы типов данных */
		validate: {
				'string': function(value, strict) {
					if(!value) return false;
					return true;
				},
				'int': function(value, strict) {
					// Если введено целое число - оно проходит валидацию
					// TODO убедиться, что не происходит округление
					var val1 = dao.prepareNumber(value);
					var val2 = parseInt(parseFloat(val1));
					if(strict) {
						if(val2 != val1) return false;
					}
					return !isNaN(val2);
				},
				'float': function(value, strict) {
					var val1 = dao.prepareNumber(value);
					var val2 = parseFloat(val1);
					if(strict) {
						if(val2 != val1) return false;
					}
					return !isNaN(val2);
				}
		},
		
		
	
		/** OOP.js
		 *	Create proper-derivable "class".
		 *	Version: 1.2 
		 */
		newClass: function (parent, prop) {
		  // Dynamically create class constructor.
		  var clazz = function() {
		    // Stupid JS need exactly one "operator new" calling for parent
		    // constructor just after class definition.
		    if (clazz.preparing) return delete(clazz.preparing);
		    // Call custom constructor.
		    if (clazz.constr) {
		      this.constructor = clazz; // we need it!
		      clazz.constr.apply(this, arguments);
		    }
		  };
		  clazz.prototype = {}; // no prototype by default
		  if (parent) {
		    parent.preparing = true;
		    clazz.prototype = new parent;
		    clazz.prototype.constructor = parent;
		    clazz.constr = parent; // BY DEFAULT - parent constructor
		  }
		  if (prop) {
		    var cname = "constructor";
		    for (var k in prop) {
		      if (k != cname) clazz.prototype[k] = prop[k];
		    }
		    if (prop[cname] && prop[cname] != Object)
		      clazz.constr = prop[cname];
		  }
		  return clazz;
		}
	};
	
	dao.vd = dao.var_dump = function($obj) {
		alert(dao.svar_dump($obj));
	};
	
	dao.svar_dump = function($obj) {
		return JSON.stringify($obj, null, '\t');
	};
	
	$.fn.dao_validator = function(options) {
		$(this).each(function(){
			var param = $.extend({
				'type': 'string',
				'nullable': false
			}, options);
			$(this).data('dao.validator', param);
			$(this).keyup(function(e){
				var data = $(this).data('dao.validator');
				var v_type = data.type;
				if(v_type && validate[v_type]) {
					var val = $(this).val();
					if ((data.nullable && val.length == 0) || validate[v_type](val)) {
						$(this).removeClass('ui-state-error');
						$(this).data('dao.value.status', 'valid');
					} else {
						$(this).addClass('ui-state-error');
						$(this).data('dao.value.status', 'invalid');
					}
				}
			});
		});
	};
	
	var links = {};
	
	dao.save_link = function (name, value) {
		if(links[name]) throw name + ' is already used';
		links[name] = value;
	};
	dao.get_link = function (name) {
		if(links[name] === undefined) throw name + ' is not saved';
		return links[name];
	};
	
	var wheel_handle = [];
	
	// Основная Функция mousewheel
	function wheel(event){
        var delta = 0;
        if (!event) event = window.event; // Событие IE.
        // Установим кроссбраузерную delta
        if (event.wheelDelta) {
            // IE, Opera, safari, chrome - кратность дельта равна 120
        	//delta = event.wheelDelta/120;
            delta = event.wheelDelta/40;
        } else if (event.detail) {
            // Mozilla, кратность дельта равна 3
            //delta = -event.detail/3;
            delta = -event.detail/1;
        }
        // Вспомогательня функция обработки mousewheel
        if (delta) {
        	for(var i in wheel_handle) { var handle = wheel_handle[i];
                if(false === handle(delta, event)) break;
        	}
        	/*
        	// Отменим текущее событие - событие по-умолчанию (скролинг окна).
        	if (event.preventDefault)
            	event.preventDefault();
            event.returnValue = false; // для IE
            */
        }
	}

	// Инициализация события mousewheel
	if (window.addEventListener) // mozilla, safari, chrome
	    window.addEventListener('DOMMouseScroll', wheel, false);
	// IE, Opera.
	window.onmousewheel = document.onmousewheel = wheel;
	
	dao.unbind_mousewheel = function(cb) {
		for(var i in wheel_handle) {
			if(wheel_handle[i] == cb) {
				wheel_handle.splice(i, 1);
//				break;
			}
		}
	};
	
	dao.bind_mousewheel = function(cb) {
		dao.unbind_mousewheel(cb);
		wheel_handle.push(cb);
	};
	
	dao.catchWheel = function(parent, cb) {
//		dao.vd('cw '+cb);
		if(!parent || !cb) return;
		$(parent).mouseenter(function(){
			dao.bind_mousewheel(cb);
		}).mouseleave(function(){
			dao.unbind_mousewheel(cb);
		});
	};
	
	$.fn.mousewheel = function(cb) {
		dao.catchWheel($(this), cb);
	};
	
	
	dao.uuid = function() {
		'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
		    var r = Math.random()*16|0, v = c == 'x' ? r : (r&0x3|0x8);
		    return v.toString(16);
		});
	};
	
	dao.console = {
		element: null,
		messages: $('<div />'),
		toggle: function() {
			if(this.element) this.element.toggle();
		},
		show: function() {
			if(this.element) this.element.show();
			this.update();
		},
		hide: function() {
			if(this.element) this.element.hide();
			this.update();
		},
		update: function() {
			if(this.element) {
				$('#middle').css('bottom', (this.element.is(':visible') ? this.element.height() : 0) + 5);
			}
		},
		init: function() {
			if(this.element) return;
			this.element = $('#console');
			var m = this.messages.contents();
			this.messages = this.element.find('#console-messages');
			this.messages.append(m);
			this.scroll();
		},
		message: function (message, type) {
			if(!message) return;
//			if(this.empty){ // TODO уведомление о новых сообщениях
//				this.empty = false;
////				this.show();
//			}
			if(type == null) {
				type = 0;
			}
			var a_class = 'ui-widget-content';
			switch (type) {
				case 'alert':
					a_class += ' ui-state-alert';
					break;
				case 'error':
					a_class += ' ui-state-error';
					break;
				case 0:
				case 1:
				case 2:
				case 3:
				case 4:
				case 5:
					a_class += ' debug-level'+type;
					break;
			}
			$('<li class="'+a_class+'">'+message+'</li>').appendTo(this.messages);
			this.scroll();
		},
		scroll: function() {
			this.messages.stop().scrollTo({top:'100%', left:'0%'}, 3000);
		}
	};
	
	$(document).ready(function() {
		dao.console.init.call(dao.console);
	});
	
})(jQuery);

