/**
* jquery.gadget.js v1.0
*
* @param	title 	Mixed 	string title menu or any DOM Object
* @param	width	String	width menu (example 130px, 30%, 50em)
* @param	action  Array[Object]
* @param	Object{title, callback}
* @param	title  		Mixed 	string title item or any DOM Object ('separator' - reserved word)
* @param	callback  	Object{key:val} 	action function for this item menu
* @param	key  	Event		Event js, all list event look documentation js
* @param	value  	Function	any option permitted in js (anonymous, named method class, etc.)
*
*/

;(function($) {

	$.fn.gadget = function(options, arg) {

		var TYPE = {
			HORIZONTAL: 1,
			VERTICAL: 2
		};

		var STATUS = {
			LOADING:	1,
			LOAD:		2,
			FINISH:		3
		};

		var FOCUS = {
			ON:			1,
			OFF:		2
		};

		var KEY = {
			UP:			38,
			DOWN:		40,
			LEFT:		37,
			RIGHT:		39,
			PAGEUP:		33,
			PAGEDOWN:	34,
			ENTER:		13
		};

		var defalut = {
			type: 1,
			fixed: true,
			width: 400,
			height: 80,

			data: [],
			slide_count_in_request: 10,
			do_request_when: 10,
			mouse_over_highlight: false,

			arrow: 'single',
			keyboard: 'single',
			mouse: 'none', //'single', 'slide'

			focus_get_type: 'click',
			ajax: {
				type: 'GET',
				error_count: 3
			},
			prev: {
				text: '',
				className: 'prev'
			},
			next: {
				text: '',
				className: 'next'
			},
			load: {
				text: '<img src="images/loader.gif">',
				className: 'load'
			},
			move: {
				time: 40,
				step: 100
			}

		};

		if ( typeof options === 'object' || ! options ) {
			var options = $.extend(defalut, options);
			var css = {
				width: 		options.width + 'px',
				height: 	options.height + 'px',
				overflow:	'hidden',
				position:	'relative',
				float: 		'left',
				display:	'block',
				margin:		0,
				padding: 	0
			};
		}

		var $this = $(this);

		var Gadget = function() {
				var self = this;

				this.setDefalutValue = function() {
					this.gid = $this.attr('id');
					this.content = $this.find('ul');
					this.li = this.content.find('li');
					this.section = options.section || this.li.length;
					this.maxSection = this.section;
					this.current = 0;
					this.user_data = "";
					this.template = "";
					this.status = STATUS.LOAD;
					this.focus = FOCUS.OFF;
					this.type = options.type;
					this.ajax_error = 0;

					this.data = options.data;
					this.url = options.url;
					this.pos_section = 0;

					this.arrow = options.arrow;
					this.keyboard = options.keyboard;
					this.mouse = options.mouse;
				};

				this.setWraper = function() {
					this.count = (this.data.length == 0) ? this.section : this.data.length;
					this.width = options.width;
					this.height = options.height;

					if (options.type == TYPE.HORIZONTAL) {
						var div = $('<div>')
							.css({
								overflow:	'hidden',
								position:	'relative',
								height: 	options.height + 'px'
							});
					}

					if (options.type == TYPE.VERTICAL) {
						var div = $('<div>')
							.css({
								overflow:	'hidden',
								position:	'relative',
								width: 	options.width + 'px'
							});
					}

					this.content.css({
							position : 'relative',
							'list-style': 'none',
							padding: 0,
							margin:	0
						})
						.wrap(div);
					this.resizeGadget();

					this.reload();
				};

				this.reload = function() {
					if (this.li.length < this.section) {
						$(this.content).empty(); //грязный хак для гаджета поиска, скорее всего потенциальный источник багов

						for (var i=0; i<this.section; i++) {
							var li = $('<li>').css(css).width(this.width).html(this.template);
							$(this.content).append(li);
							this.setContentHiddenBox(i, li);
						}
					}
				};

				this.getContent = function() {
					if ((this.status == STATUS.FINISH) || (this.status == STATUS.LOADING)) {return;}

					if (! this.url) {
						this.status == STATUS.FINISH;
						return;
					}

					this.status = STATUS.LOADING;
					$.ajax({
						type: options.ajax.type,
						url: this.url,
						data: this.serialize({
							id: this.data[this.count - 1].id,
							gid: this.gid,
							max: this.count,
							userData: this.user_data,
							limit:  options.slide_count_in_request * this.section
						}),
						dataType: "json",
						error: function() { self.ajaxError(self); },
						complete: function(data) { self.ajaxComplete(self, data.responseText); }
					});

				};

				this.serialize = function(data) {
					if (data == undefined) {return "";}

					var str = "";
					for (key in data) {
						str += key + "=" + data[key] + "&";
					}
					return str;
				};

				this.ajaxError = function(self) {
					if (self.ajax_error++ <= options.ajax.error_count) {
						self.is_loading = true;
						self.getContent();
					} else {
						this.status = STATUS.FINISH;
						this.loadButton.css('visibility', 'hidden');
					}
				};

				this.ajaxComplete = function(self, data) {
					data = $.parseJSON(data);
					self.setData(data);
					self.is_loading = false;
					this.loadButton.css('visibility', 'hidden');

					if (data.length == options.slide_count_in_request * this.section) {
						this.nextButton.css('visibility', 'visible');
						this.status = STATUS.LOAD;
					} else {
						if ((this.current + 1) < this.count) {
							this.nextButton.css('visibility', 'visible');
						}
						this.status = STATUS.FINISH;
					}
				};

				this.setData = function(data) {
					this.data = $.merge(this.data, data);
					this.count = parseInt(this.data.length);
				};

				this.next = function(n) {
					if (n == undefined) {
						n = (this.arrow == 'single') ? 1 : this.section;
					}

					if (n == 1) {
						if ((this.current + 1) == this.count) {
							if (this.status == STATUS.LOAD) {
								this.requestContext(n);
							} else {
								return false;
							}
						}

						this.current++;
						this.pos_section++;
						if (this.keyboardHighlight('next')) {return;}
						this.current--;
						this.pos_section--;

					} else {
						this.pos_section = 0; //hak
						this.current -= this.current % this.section;
					}

					if (this.nextButton.css('visibility') == 'hidden') {return;}

					if ((this.current + n) < this.count) {
						this.createHiddenBox('next', n);

						for(var i=0; i<n; i++) {
							this.current++;
							this.delta = 0;
							this.nextMove(this);
						}

					}

					this.requestContext(n);

					this.viewNavArraw(n);
					return false; //for tag a
				};

				this.prev = function(n) {
					if (n == undefined) {
						n = (this.arrow == 'single') ? 1 : this.section;
					}

					if (n == 1) {
						if (this.current == 0) {
							this.outList();
							return false;
						}

						this.current--;
						this.pos_section--;
						if (this.keyboardHighlight('prev')) {return;}
						this.current++;
						this.pos_section++;
					} else {
						this.pos_section = 0; //hak
						this.current -= this.current % this.section;
					}

					if (this.prevButton.css('visibility') == 'hidden') {return;}
					this.createHiddenBox('prev', n);

					for(var i=0; i<n; i++) {
						if (options.type == TYPE.HORIZONTAL) {
							this.delta = - this.width;
						}
						if (options.type == TYPE.VERTICAL) {
							this.delta = - this.height;
						}

						this.current--;
						this.prevMove(this);
					}

					this.viewNavArraw(n);
					return false; //for tag a
				};

				this.createHiddenBox = function(direction, n) {
					for(var i=0; i<n; i++) {
						var li = $('<li>').css(css).width(this.width).height(this.height).html(this.template);
						if (direction == 'next') {
							var id = this.current + n + i;
							$(this.content).append(li);
						} else {
							var id = this.current - i - 1;
							$(this.content).prepend(li);

							if (options.type == TYPE.HORIZONTAL) {
								this.content.find('li').css('left', -1*this.width + 'px');
							}
							if (options.type == TYPE.VERTICAL) {
								this.content.find('li').css('top', -1*this.height + 'px');
							}

						}

						this.setContentHiddenBox(id, li);
					}
				};

				this.setContentHiddenBox = function(id, content) {
					var data = this.data[id];

					if (data == undefined) {
						return $(content).empty();
					}

					for(var tag in data) {
						if (tag == 'html') {
							$(content).html(data[tag]);
						} else {
							var el = $(content).find('[tag='+tag+']');
							for (var attr in data[tag]) {
								if (attr == 'content') {
									el.html(data[tag][attr]);
								} else {
									el.attr(attr, data[tag][attr]);
								}
							}
						}
					}
				};

				this.keyboardHighlight = function(direction) {
					if (this.keyboard != 'single') {return false;}

					if (direction == 'next') {
						if (this.pos_section < this.section) {
							this.selctKeyboardSection();
							this.requestContext(1);
							this.viewNavArraw(1);
							return true;
						} else {
							return false;
						}
					} else {
						if ((this.pos_section) >= 0) {
							this.selctKeyboardSection();
							this.viewNavArraw(1);
							return true;
						} else {
							return false;
						}

					}
				};

				this.selctKeyboardSection = function() {
					this.content.find('li').removeClass('highlight');
					this.content.find('li:eq('+ this.pos_section +')').addClass('highlight');
					this.select(this.data[this.current]);
				};

				this.viewNavArraw = function(n) {
					if (this.prevButton == undefined) {return;}

					if (this.current == 0) {
						this.prevButton.css('visibility', 'hidden');
					} else {
						this.prevButton.css('visibility', 'visible');
					}

					if ((this.current + n) >= this.count) {
						this.nextButton.css('visibility', 'hidden');
						if (this.status == STATUS.LOADING) {
							this.loadButton.css('visibility', 'visible');
						}
					} else {
						if (this.status == STATUS.FINISH) {
							if ((this.current + n) >= this.count) {
								this.nextButton.css('visibility', 'hidden');
							} else {
								this.nextButton.css('visibility', 'visible');
							}
						} else {
							this.loadButton.css('visibility', 'hidden');
							this.nextButton.css('visibility', 'visible');
						}
					}
				};

				this.nextMove = function(self) {
					setTimeout(function(){
						if (options.type == TYPE.HORIZONTAL) {
							var attr = 'left';
							var confines = self.width;
						}
						if (options.type == TYPE.VERTICAL) {
							var attr = 'top';
							var confines = self.height;
						}

						self.delta = self.delta + options.move.step;
						if(self.delta < confines) {
							self.content.find('li').css(attr, - self.delta + 'px');
							setTimeout(function(){self.nextMove(self);}, options.move.time);
						} else {
							self.content.find('li:first').remove();
							self.content.find('li').css(attr, '0px');
							self.keyboardHighlight('next');
						}
					}, options.move.time);
				};

				this.prevMove = function(self) {
					setTimeout(function(){
						if (options.type == TYPE.HORIZONTAL) {
							var attr = 'left';
						}
						if (options.type == TYPE.VERTICAL) {
							var attr = 'top';
						}

						self.delta = self.delta + options.move.step;
						if(self.delta < 0) {
							self.content.find('li').css(attr, self.delta + 'px');
							setTimeout(function(){self.prevMove(self);}, options.move.time);
						} else {
							self.content.find('li:last').remove();
							self.content.find('li').css(attr, '0px');
							self.keyboardHighlight('prev');
						}
					}, options.move.time);
				};

				this.createPanelNav = function() {
					this.prevButton = $('<a>')
						.click(function(){self.onFocus(); self.prev(); return false;})
						.addClass(options.prev.className)
						.attr({
							href: '#'
						})
						.css('visibility', 'hidden')
						.html(options.prev.text);
					this.nextButton = $('<a>')
						.click(function(){self.onFocus(); self.next(); return false;})
						.addClass(options.next.className)
						.attr({
							href: '#'
						})
						.html(options.next.text);
					this.loadButton = $('<span>')
						.addClass(options.load.className)
						.css('visibility', 'hidden')
						.html(options.load.text);

					var div = $('<div>').addClass('nav').append(this.prevButton).append(this.nextButton).append(this.loadButton);
					$this.append(div);
				};

				this.requestContext = function(n) {
					if ((this.count - this.current - n) < options.do_request_when) {
						this.getContent();
					}
				};

				this.getTemplate = function() {
					var tpl = this.content.find('li:first');
					var img = tpl.find('img[tag]').attr('src');

					this.template = tpl.html().replace(img, '');
				};

				this.getFirstData = function() {
					var li = this.content.find('li');
					for (var i=0; i < li.length; i++) {
						this.data.push({
							html: $(li[i]).html(),
							id: $(li[i]).attr('id')
						});
					}
				};

				this.fixedGadget = function() {
					if (options.fixed) {return;}

					$(window).bind('resize', function () {
						self.resizeGadget();
					});
				};

				this.resizeGadget = function() {

					if (options.type == TYPE.HORIZONTAL) {
						if (! options.fixed) {
							var section = Math.floor($this.width() / options.width);
							section = (this.maxSection < section) ? this.maxSection : section;
							this.width = Math.floor($this.width() / section);
							css.width = this.width + 'px';

							if (this.section > section) {
								this.content.find('li:gt(' + (section - 1) + ')').remove();
							}

							if (this.section < section) {
								for(var i = this.section; i<section; i++) {
									var id = i + this.current;
									var li = $('<li>').css(css).width(this.width).html(this.template);
									$(this.content).append(li);
									this.setContentHiddenBox(id, li);
								}
							}

							this.section = section;
							this.viewNavArraw(1);
						}

						var n = options.scrolling_all ? this.section : 1;
						this.content.width(this.width * (this.section + n));
						this.content.parent().width(this.width * this.section);
						this.content.find('li').css(css).width(this.width);
					}

					if (options.type == TYPE.VERTICAL) {
						if (! options.fixed) {
							var section = Math.floor($this.height() / options.height);
							section = (this.maxSection < section) ? this.maxSection : section;
							this.height = Math.floor($this.height() / section);
							css.height = this.height + 'px';

							if (this.section > section) {
								this.content.find('li:gt(' + (section - 1) + ')').remove();
							}

							if (this.section < section) {
								for(var i = this.section; i<section; i++) {
									var id = i + this.current;
									var li = $('<li>').css(css).height(this.height).html(this.template);
									$(this.content).append(li);
									this.setContentHiddenBox(id, li);
								}
							}

							this.section = section;
							this.viewNavArraw(1);
						}

						var n = options.scrolling_all ? this.section : 1;
						this.content.height(this.height * (this.section + n));
						this.content.parent().height(this.height * this.section);
						this.content.find('li').css(css).height(this.height);
					}

					if (this.keyboard == 'single') {
						if (this.section <= this.pos_section) {
							if (this.pos_section != 0) {this.selctKeyboardSection();}
							this.pos_section = 0;
							this.current -= section;
							this.viewNavArraw(1);
						}
					}
				};

				this.mouseOverHighlight = function() {
					if (! options.mouse_over_highlight) {return;}

					var li = this.content.find('li');

					li.live('mouseenter', function() {
						$this.addClass('highlight');
					});

					li.live('mouseleave', function() {
						$this.removeClass('highlight');
					});
				};

				this.setFocusGadget = function() {
					if(options.focus_get_type == 'click') {
						$(document).bind("click", function(event) {
							var obj = $.merge($(event.target).parents(), $(event.target)).filter($this);
							if (obj.length != 0) {
								var obj = $.merge($(event.target).parents(), $(event.target)).filter(self.content.children('li'));
								if (obj.length != 0) {
									var i = 0;
									do {
										obj = obj.prev();
										i++;
									} while (obj.length);
									i--;
								}

								var pos = (i == undefined) ? self.pos_section : i;
								self.onFocus(pos);
							} else {
								self.offFocus();
							}
						});

					} else {

						$this.hover(
							function() {
								self.onFocus();
							},
							function() {
								self.offFocus();
							}
						);

					}
				};

				this.onFocus = function(i) {
					this.focus = FOCUS.ON;

					if (this.keyboard != 'single') {return;}

					var pos = (i != undefined) ? i : this.pos_section;
					if (pos != this.pos_section) {
						this.current = this.current + pos;
					};
					this.pos_section = pos;
					this.selctKeyboardSection();
				};

				this.offFocus = function() {
					this.focus = FOCUS.OFF;

					if (this.keyboard != 'single') {return;}
					this.content.find('li').removeClass('highlight');
				};

				this.useKeyboard = function() {
					if (this.keyboard == 'none') {return;}

					$(document).bind($.browser.opera ? "keypress" : "keydown", function (event) {

						if (self.focus == FOCUS.OFF) {return;}

						var n = (self.keyboard == 'single') ? 1 : self.section;

						if (self.type == TYPE.HORIZONTAL) {
							switch(event.keyCode) {
								case KEY.LEFT:
									event.preventDefault();
									self.prev(n);
									break;
								case KEY.RIGHT:
									event.preventDefault();
									self.next(n);
									break;
								case KEY.ENTER:
									self.activite(self.data[self.current]);
									break;
							}
						}

						if (self.type == TYPE.VERTICAL) {
							switch(event.keyCode) {
								case KEY.UP:
									event.preventDefault();
									self.prev(n);
									break;
								case KEY.DOWN:
									event.preventDefault();
									self.next(n);
									break;
								case KEY.ENTER:
									self.activite(self.data[self.current]);
									break;
							}
						}

					});
				};

				this.wheel = function(event) {
					var delta = 0;
					if (!event) event = window.event; // Событие IE.
					// Установим кроссбраузерную delta
					if (event.wheelDelta) {
							// IE, Opera, safari, chrome - кратность дельта равна 120
							delta = event.wheelDelta/120;
					} else if (event.detail) {
							// Mozilla, кратность дельта равна 3
							delta = -event.detail/3;
					}
					// Вспомогательня функция обработки mousewheel
					if (delta) {
							if (self.focus == FOCUS.OFF) {return;}
							self.mouseWheel(delta);

							// Отменим текущее событие - событие поумолчанию (скролинг окна).
							if (event.preventDefault) {
								event.preventDefault();
							}
							event.returnValue = false; // для IE
					}
				};

				this.mouseWheel = function(delta) {
					if (self.focus == FOCUS.OFF) {return;}

					var n = (self.mouse == 'single') ? 1 : self.section;
					if (delta > 0) {
						self.prev(n);
					} else {
						self.next(n);
					}
					return false;
				};

				this.useMouseScroll = function() {
					if (options.mouse == 'none') {return;}

					// Инициализация события mousewheel
					// mozilla, safari, chrome
					if (window.addEventListener) {window.addEventListener('DOMMouseScroll', this.wheel, false);}
					// IE, Opera.
					window.onmousewheel = document.onmousewheel = this.wheel;
				};


				this.activite = function() {};

				this.select = function() {};

				this.outList = function() {};

				this.init = function() {
					this.setDefalutValue();
					this.getTemplate();
					this.getFirstData();
					this.setWraper();
					this.createPanelNav();
					this.fixedGadget();
					this.mouseOverHighlight();
					this.setFocusGadget();
					this.useKeyboard();
					this.useMouseScroll();
				};
		};

		Gadget.getInstance = function () {
			if (this._instance === undefined) {
				this._instance = new Gadget();
				this._instance.init();
			}
			return this._instance;
		};




		return this.each(function() {

			switch (options) {
				case 'focus':
					if (arg == 'on') {
						g.current = -1;
						g.pos_section = -1;
						g.focus = FOCUS.ON;
					} else {
						g.offFocus();
					}
					return;
				case 'clear':
					$this.hide();
					$(g.content).find('li:last').remove();
					return;
				case 'reload':
					g.current = 0;
					g.pos_section = 0;
					g.count = (g.data.length == 0) ? g.section : g.data.length;
					g.status = STATUS.LOAD;
					g.reload();
					$this.show();
					return;
				case 'options':
					for(var key in arg) {
						g[key] = arg[key];
					}
					return;
				case 'activite':
					g.activite = arg;
					return;
				case 'select':
					g.select = arg;
					return;
				case 'outList':
					g.outList = arg;
					return;
				case 'next':
					g.next(arg);
					return;
				case 'prev':
					g.prev(arg);
					return;

				default: g = Gadget.getInstance();
			}

		});

	};

})(jQuery);

