/**
 * @author a.novikov
 * @version 1.0
 */

(function(window){
	var Library = {};
	window.NLib = window.Library = window.Lib = Library;
})(window);

(function(Lib) {
	Lib.Scope = function(callback) {
		return callback();
	};
})(NLib);

(function(Lib, $) {
	var Numeric = function(element, codes) {
		this.initCodes();
		this.extendCodes(codes);
		this.bind(element);
	};
	
	Numeric.prototype = {
		bind: function(element) {
			this.element = element;
			var object = this;

			element.onkeypress = function(event) {
				object.onKeyPress(event);
			}
		},
		extendCodes: function(codes) {
			this.initCodes();

			for (var code in codes) {
				if (codes[code]) {
					this.validCodes[code] = true;
				}
				else {
					delete this.validCodes[code];
				}
			}

		},
		initCodes: function() {
			if ( !('validCodes' in this) ) {
				this.validCodes = Object.clone(this.defaultValidCodes);
			}
		},
		onKeyPress: function(event) {
			var code = event.charCode || event.keyCode;

			if (!(event.keyCode in this.validCodes) && isNaN( parseInt( String.fromCharCode(code) ) ) ) {
				event.preventDefault();
				if (event.keyCode == 13) {
					$(this).change();
				}
			}
		},
		defaultValidCodes: {
			8:	true,	// backspace
			9:	true,	// tab
			13: true,	// enter
			35:	true,	// end
			36:	true,	// home
			37:	true,	// left
			39:	true,	// right
			46:	true	// del
		}
	};
	
	Lib.numeric = function(selector, codes) {
		$(selector).each(function() {
			new Numeric(this, codes);
		});
	};
})(NLib, jQuery);

(function(Lib){

	var ErrorHandlers = {
		Ajax: {
			handlerArray: function(errors) {
				var text = errors.join('\n');

				if (errors.length == 1) {
					alert('Произошла ошибка: '+text);
				}
				else if (errors.length > 1) {
					alert('Произошли ошибки:\n'+text);
				}
			}
		}
	};

	Lib.ErrorHandlers = ErrorHandlers;

})(NLib);


(function(Lib, $, window){
	var Ajax = {
		wrap: function(url, callback, errorHandler) {
			this.wrapResponce( this.loadJson(url) , callback, errorHandler);
		},
		wrapText: function(text, callback, errorHandler) {
			this.wrapResponce( eval('('+text+')') , callback, errorHandler);
		},
		wrapResponce: function(responce, callback, errorHandler) {
			if(responce.status === 'REDIRECT') {
				window.location.href = responce.body;
			}
			else if(responce.status != 'OK') {
				if (errorHandler === undefined) {
					errorHandler = Lib.ErrorHandlers.Ajax.handlerArray;
				}
				errorHandler( responce.messages );
			}
			else {
				callback( responce.body, responce.messages );
			}
		},
		loadText: function(url) {
			return $.ajax({url: url, async: false}).responseText;
		},
		loadJson: function(url) {
			var text = this.loadText(url);
			return eval('('+text+')');
		},
		loadInto: function(element, url, errorHandler) {
			var callback = function(data) {
				$(element).html(data);
			}
			this.wrap(url, callback, errorHandler);
		}
	};

	Lib.Ajax = Ajax;

})(NLib, jQuery, window);

(function(Lib) {
	Lib.Number = {
		format: function( number, decimals, dec_point, thousands_sep ) {	// Format a number with grouped thousands
			//
			// +   original by: Jonas Raoni Soares Silva (http://www.jsfromhell.com)
			// +   improved by: Kevin van Zonneveld (http://kevin.vanzonneveld.net)
			// +	 bugfix by: Michael White (http://crestidg.com)

			var i, j, kw, kd, km;

			// input sanitation & defaults
			if( isNaN(decimals = Math.abs(decimals)) ){
				decimals = 2;
			}
			if( dec_point == undefined ){
				dec_point = ",";
			}
			if( thousands_sep == undefined ){
				thousands_sep = ".";
			}

			i = parseInt(number = (+number || 0).toFixed(decimals)) + "";

			if( (j = i.length) > 3 ){
				j = j % 3;
			} else{
				j = 0;
			}

			km = (j ? i.substr(0, j) + thousands_sep : "");
			kw = i.substr(j).replace(/(\d{3})(?=\d)/g, "$1" + thousands_sep);
			//kd = (decimals ? dec_point + Math.abs(number - i).toFixed(decimals).slice(2) : "");
			kd = (decimals ? dec_point + Math.abs(number - i).toFixed(decimals).replace(/-/, 0).slice(2) : "");


			return km + kw + kd;
		}
	};
})(NLib);

(function(Lib) {
	Lib.String = {
		getCodesByExpression: function(expressionString) {
			var code;
			var nextCode;
			var codes = [];
			
			for (var i = 0; i < expressionString.length; ++i) {
				if ( (expressionString[i] == '-') && (i != 0) && (i != expressionString.length-1) ) {
					nextCode = expressionString.charCodeAt(++i);
					if (nextCode < code) {
						$.error('invalid input string');
					}
					for (code++; code <= nextCode; code++) {
						codes.push(code);
					}
				}
				else {
					code = expressionString.charCodeAt(i);
					codes.push(code);
				}
			}
			
			return codes;
		}
	};
})(NLib);


(function(Lib, $){
	
	var Binder = function(Class, key, AdapterClass) {
		this.key = key;
		this.Class = Class;
		this.AdapterClass = AdapterClass;
		
		var binder = this;

		$.fn[key] = function() {
			return binder.call(this, arguments);
		};
	};
	
	Binder.prototype = {
		getAdapter: function(element) {
			var data = $(element).data( this.key );
			
			if (!data) {
				throw "There is no object associated with this element";
			}
			
			return data;
		},
		bindNewAdapter: function(element) {
			var object = new this.Class(element);
			var adapter = new this.AdapterClass(object);

			$(element).data( this.key, adapter );

			return adapter;
		},
		getOrCreateAdapter: function(element) {
			var adapter;
			
			try {
				adapter = this.getAdapter(element);
			}
			catch (message) {
				adapter = this.bindNewAdapter(element);
			}
			
			return adapter;
		},
		unbind: function(element) {
			$(element).removeData( this.key );
		},
		call: function(jElements, jArgs) {
			var binder = this;

			var method;
			var args;

			var options = jArgs[0];
			
			if ( options instanceof Object || !options ) {
				method = 'init';
				args = jArgs;
			}
			else {
				method = options;
				args = Array.prototype.slice.call(jArgs, 1);
			}

			var ret;

			jElements.each(function() {
				var adapter = binder.getOrCreateAdapter(this);

				if (method in adapter && adapter[method] instanceof Function) {
					ret = adapter[method].apply( adapter, args );
				}
				else {
					$.error('Invalid arguments');
				}

				if (method === 'destroy') {
					binder.unbind(this);
				}
			});

			return ret;
		}
	};

	Lib.bindClassToJQuery = function(Class, key, AdapterClass) {
		new Binder(Class, key, AdapterClass);
	};

})(NLib, jQuery);
