/**
 * jEmersion - универсальная библиотека для создания всевозможных вспылывающих
 * элементов.
 * 
 * @author Mokhov Oleg (oleg.mokhov@gmail.com)
 * @copyright JetStyle (http://www.jetstyle.ru)
 * 
 * @include jquery.js
 * @include events.js
 */

var jEmersion = (function() {
	// Здесь будет храниться оверлей (бэкграунд)
	var overlayElement;

	// Сколько всего элементов всплыло
	var eElements = {};

	return {
		// Счётчик всплывающих элементов
		eElementsCount : 0,

		// Дефолтные настройки
		settings : {
			'overlay' : {
				backgroundColor : '#f00',
				opacity : '0.5'
			}
		},

		/**
		 * Создание бэкграунда под выплывашками
		 * 
		 * @return {jEmersion.overlay}
		 */
		overlay : function() {
			if (typeof overlayElement == "undefined") {
				var overlay = document.createElement('div');

				// Определяем стили для оверлея
				with (overlay.style) {
					backgroundColor = jEmersion.settings.overlay.backgroundColor;
					opacity = jEmersion.settings.overlay.opacity;
					display = 'none';
					position = 'fixed';
					top = '0';
					width = '100%';
					height = '100%';
					zIndex = '1000';
				};

				// Для IE особые правила
				if (jEmersion.helpers.isMSIE) {
					// IE-браузеры до сих пор не имеют нормальной поддержки
					// CSS-свойства opacity
					with (overlay.style) {
						filter = 'alpha(opacity=' + parseFloat(opacity) * 100
								+ ')';
						backgroundAttachment = 'fixed';
					}

					/**
					 * Также в IE-браузерах неправильно работает height: 100%
					 * для абсолютно позионированных элементов
					 */
					window.attachEvent('onresize', function() {
						overlay.style.height = document.documentElement.clientHeight
								+ "px";
					});

					// IE6 не знает что такое position fixed
					if (jEmersion.helpers.browserVersion < 7) {
						overlay.style.position = "absolute";

						window.attachEvent('onscroll', function() {
									overlay.style.top = jEmersion.helpers
											.getBodyScrollTop()
											+ "px";
								});
					}
				}

				// Присоединяем элемент к документу
				document.body.appendChild(overlay);

				// Добавляем событие - по щелчку на оверлее нужно спрятать все
				// всплывшие окна
				addEvent(overlay, 'click', function() {
							jEmersion.closeAll();
						});

				// overlayElement создаётся один раз, а потом мы просто передаём
				// его в return
				overlayElement = overlay;
			}

			return {
				el : overlayElement,
				showOverlay : function() {
					jEmersion.helpers.show(overlayElement);
				}
			}
		},
		
		/**
		 * Показать элемент
		 * @param {DOMElement} elem Элемент который необходимо отобразить
		 * @param {DOMElement} overlay Оверлей
		 */
		showElement: function(elem, overlay) {
			// Запомним порядковый номер в общем списке всплывших элементов
			var i = ++jEmersion.eElementsCount;
			
			jEmersion.helpers.show(elem);
			elem.style.zIndex = (i) + 1000;
			
			if (overlay) {
				overlay.showOverlay();
			}
			
			// Сохраним элемент
			eElements[i] = elem;
			
			return i;
		},
		hideElement: function(i, overlay) {
			jEmersion.helpers.hide(eElements[i]);
			--jEmersion.eElementsCount;
			
			if (overlay) {
				if (jEmersion.eElementsCount == 0) {
					this.overlay.hideOverlay();
				}
			}
			
			delete eElements[i];
		},
		/**
		 * Закрыть все окна и убрать оверлей
		 */
		closeAll : function() {
			for (var i in eElements) {
				jEmersion.helpers.hide(eElements[i]);
			}
			jEmersion.helpers.hide(overlayElement);
		}
	}
})();

/**
 * Класс для popup элементов
 * FIXME Убрать зависимость от jQuery
 * 
 * @param {Array}
 *            settings Настройки popup'а
 */
jEmersion.popup = function(settings) {
	// el.show();
	// Если нужно то включаем оверлей
	if (settings.overlay && settings.overlay == true) {
		this.overlay = jEmersion.overlay();
	};
	
	this.element = (typeof settings.element == "string" ? jEmersion.helpers.id(settings.element) : settings.element)
	
	// Центрируется ли попап по середение окна?
	this.centered = settings.centered || true;
}

jEmersion.popup.prototype = {
	/**
	 * Показать попап
	 */
	show: function(){
		var that = this;
		
		that.locate();
		if (that.centered) {
			addEvent(window, 'resize', function(){that.locate()}); // Просто написать that.locate нельзя, т.к потеряем контекст
		}
		
		this.i = jEmersion.showElement(this.element, this.overlay || null);
	},
	/**
	 * Скрыть попап
	 */
	hide: function(){
		var that = this;
		
		jEmersion.hideElement(that.i, that.overlay || null);
		
		if (that.centered) {
			removeEvent(window, 'resize', function(){that.locate()});
		}
	},
	
	/**
	 * Спозиционировать попап
	 * FIXME Убрать jQuery (нужно разобраться с взятием computedStyles)
	 */
	locate: function(){
		var that = this;
		if (that.centered) {
			//alert($(that.element).widht());
			with (that.element.style) {
				position = "absolute"; // На всякий случай
				left = "50%";
				marginLeft = (- parseInt($(that.element).width())) / 2 + "px"; // FIXME
				marginTop = (- parseInt($(that.element).height())) / 2 + "px"; // FIXME
				top = (jEmersion.helpers.getBodyScrollTop() + document.documentElement.clientHeight / 2) + "px";
			}
		}
	}
}


/**
 * Класс для baloon элементов. Baloon отличается от popup'а возможность прилипать к другим элементам (за это отвечают параметры stickTo и stickAt)
 * 
 * @param {Array}
 *            settings Настройки baloon'a
 *            	stick Инфа о том к чему прилипать
 *            		to Элемент
 *            		at С какой стороны
 *            		mode Режим (centered | normal)
 */
jEmersion.baloon = function(settings) {
	this.element = (typeof settings.element == "string" ? jEmersion.helpers.id(settings.element) : settings.element);
	
	this.visible = (jEmersion.helpers.getStyle(this.element, 'display') == 'none' ? false : true);
	this.stick = {
		'to': settings.stick.to, 
		'at': (settings.stick.at || 'top'),
		'mode': (settings.stick.mode || 'centered')
		};
}

jEmersion.baloon.prototype = {
	/**
	 * Показать baloon
	 */
	show: function(){
		var that = this;
		
		that.locate();
		
		this.i = jEmersion.showElement(this.element);
		this.visible = true;
		addEvent(window, 'resize', function(){that.locate()}); // Просто написать that.locate нельзя, т.к потеряем контекст
	},
	/**
	 * Скрыть попап
	 */
	hide: function(){
		jEmersion.hideElement(this.i);
		this.visible = false;
		removeEvent(window, 'resize', function(){that.locate()}); // Просто написать that.locate нельзя, т.к потеряем контекст
	},
	
	/**
	 * Спозиционировать попап
	 */
	locate: function(){
		var that = this;
		
		// Первичная установка параметров нахождения липучки.
		var stickTo = that.stick.to;
		var elLeft = $(stickTo).offset().left;
		var elTop = $(stickTo).offset().top;
		var c = (that.stick.mode == 'centered');
		
		that.element.style.position = "absolute"; // На всякий случай
		
		// Смотрим к какой стороне прилипнуть
		switch(that.stick.at) {
			case 'right':
				elLeft += jEmersion.helpers.realWidth(stickTo);
				if (c) {
					elTop += (jEmersion.helpers.realHeight(stickTo)/2 - jEmersion.helpers.realHeight(that.element) / 2);
				}
				break;
				
			case 'bottom':
				elTop += jEmersion.helpers.realHeight(stickTo);
				if (c) {
					elLeft += (jEmersion.helpers.realWidth(stickTo)/2 - jEmersion.helpers.realWidth(that.element) / 2);
				}
				break;
			case 'left':
				elLeft -= jEmersion.helpers.realWidth(that.element);
				if (c) {
					elTop += (jEmersion.helpers.realHeight(stickTo)/2 - jEmersion.helpers.realHeight(that.element) / 2);
				}
				break;
				
			case 'top':
			default:
				elTop -= jEmersion.helpers.realHeight(that.element);
				if (c) {
					elLeft += (jEmersion.helpers.realWidth(stickTo)/2 - jEmersion.helpers.realWidth(that.element) / 2);
				}
				break; 
		}
		
		with(that.element.style) {
			left = elLeft + "px";
			top = elTop + "px";
		}
		/*if (that.centered) {
			with (that.element.style) {
				position = "absolute"; // На всякий случай
				left = "50%";
				marginLeft = - (parseInt(jEmersion.helpers.getComputedStyle(that.element, "width"))) / 2 + "px";
				marginTop = - (parseInt(jEmersion.helpers.getComputedStyle(that.element, "height"))) / 2 + "px";
				top = (jEmersion.helpers.getBodyScrollTop() + document.documentElement.clientHeight / 2) + "px";
			}
		}*/
	},
	/**
	 * Проверка того видим ли сейчас элемент
	 */
	isVisible: function() {
		return this.visible;
	},
	
	toggle: function() {
		if (this.isVisible()) {
			this.hide();
		}
		else {
			this.show();
		}
	},
	
	/**
	 * Прилипнуть к указанному элементу
	 * @param {DOMElement} element
	 */
	stickTo: function(element) {
		this.stick.to = element;
	},
	
	/**
	 * Вернуть как DOMElement
	 * @return {DOMElement}
	 */
	get: function() {
		return this.element;
	},
	
	changeStickness: function(newStickness) {
		this.stick.at = newStickness;
	}
}

/**
 * Функции и параметры, которые облегчают написание кроссбраузерного кода
 */
jEmersion.helpers = {
	// Get a style property (name) of a specific element (elem)
	getStyle : function(elem, name) {
		// If the property exists in style[], then it's been set
		// recently (and is current)

		if (elem.style[name])
			return elem.style[name];

		// Otherwise, try to use IE's method
		else if (elem.currentStyle)
			return elem.currentStyle[name];

		// Or the W3C's method, if it exists
		else if (document.defaultView && document.defaultView.getComputedStyle) {
			// It uses the traditional 'text-align' style of rule writing,
			// instead of textAlign
			name = name.replace(/([A-Z])/g, "-$1");
			name = name.toLowerCase();

			// Get the style object and get the value of the property (if it
			// exists)
			var s = document.defaultView.getComputedStyle(elem, "");
			return s && s.getPropertyValue(name);
			
		// Otherwise, we're using some other browser
		} else
			return null;
	},
	
	/**
	 * Взятие реальной ширины элемента
	 * 
	 * FIXME Убрать jQUery
	 * @param {DOMElement} element
	 * @return {Number} Ширина элемента
	 */
	realWidth: function (element) {
		return $(element).width() + 
		(parseInt($(element).css('padding-left')) || 0) +  
		(parseInt($(element).css('padding-right')) || 0) + 
		(parseInt($(element).css('border-left-width')) || 0) +  
		(parseInt($(element).css('border-right-width')) || 0);  
	},
	
	/**
	 * Взятие реальной высоты элемента
	 * 
	 * FIXME Убрать jQUery
	 * @param {DOMElement} element
	 * @return {Number} Высота элемента
	 */
	realHeight: function (element) {
		return $(element).height() + 
		(parseInt($(element).css('padding-top')) || 0) +  
		(parseInt($(element).css('padding-bottom')) || 0) +
		(parseInt($(element).css('border-top-width')) || 0) +  
		(parseInt($(element).css('border-bottom-width')) || 0);  
	},
	
	/**
	 * Взять элемент по id (сокращение от document.getElementById)
	 * @param {String} id
	 * @return {DOMElement}
	 */
	id : function(id){
		return document.getElementById(id);
	},
	/**
	 * Показать элемент
	 * 
	 * @param {DOMElement}
	 *            elem
	 */
	show : function(elem) {
		elem.style.display = "block";
	},
	/**
	 * Скрыть элемент
	 * @param {DOMElement} elem
	 */
	hide : function(elem) {
		elem.style.display = "none";
	},
	// Текущий браузер IE (bool)
	isMSIE : !-[1,],
	browserVersion : function() {
		var userAgent = navigator.userAgent.toLowerCase();
		return (userAgent.match(/.+(?:rv|it|ra|ie)[\/: ]([\d.]+)/) || [0, '0'])[1];
	},

	/**
	 * Узнать насколько прокручена страница
	 * 
	 * @return {Number} Значение верхнего угла активной части страницы
	 *         относительно верхнего угла всей страницы в пикселах
	 */
	getBodyScrollTop : function() {
		return self.pageYOffset
				|| (document.documentElement && document.documentElement.scrollTop)
				|| (document.body && document.body.scrollTop);
	}
}

$(function() {
		//var test = new jEmersion.popup({'element': 'test', 'overlay': true});
		/*var test = new jEmersion.baloon({
			'element': 'test', 
			'stick': {
				'to' : document.getElementById('r1'),
				'at' : 'right'
			}
		});
		//test.element.style.backgroundColor = "red";
		test.show();*/
		});

