﻿
/**
 * Виджет диалогового окна
 * Sultana_Dialog
 */

(function($) {

	$.widget("ui.Sultana_Dialog", {
	
		/**
		 * Параметры диалогового окна.
		 * Значения по умолчанию.
		 */ 
	
		options: {
			title: "",
			width: "750",
			height: "auto",
			top: "50px",
			left: "30px",
			zIndex: 1000,
			footer: false,
			open: false,
			modal: false,
			containment: ".Sultana_Desktop_Layout_Body",
			position: "next", // next | center показать по центру броузера
			draggable: true,
			resizable: true,
			buttons: {},
			controls: {
				minimize: { label: "Свернуть", enable: true },
				maximize: { label: "Развернуть", enable: true },
				restorize: { label: "Свернуть в окно", enable: true },
				close: { label: "Закрыть", enable: true }
			}
		},
		
		/**
		 * Конструктор диалогового окна
		 */
		
		_create: function() {
			var self = this, options = self.options;
			self.element.addClass("Sultana_Dialog");
			if (!$(options.containment).size()) options.containment = "document";

			// Основные элементы окна, доступные во всех метода виджета
			
			self.dialog = {
				title: null,
				layout: null,
				body: null,
				panels: {},
				controls: {},
				buttons: {},
				modalbg: $()
			};
			
			// Если нужно скроем окно задвигом (чтобы размеры оставались ненулевые) 
			
			if (!self.options.open) {
				self.element.css("left", "-2000");
				
				// TODO Доктайп <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
				// не поддерживает отрицательные значения параметров CSS, в частности параметра left
				
			}
			
			// Создание раскладки окна
			
			self.createDialogLayout();
			self.makeDraggable();
			self.makeResizable();
			
			// Создание меню окна
			
			if (options.menubar) {
				self.appendDialogPanel("Sultana_Dialog_Menubar");
				$(options.menubar)
					.appendTo(self.dialog.panels["Sultana_Dialog_Menubar"])
					.sfMenubar();
			}

			// Двойной клик по заголовку окна максимизирует его.
				
			$(".Sultana_Dialog_Header", self.element).dblclick(function() { 
				self.toggle();
			});
			
			// Клик по окну ставит его поверх всех окон 
			
			self.element.mousedown(function(event) {
				self.moveToTop(false, event);
			});
			
			// Применение всех опций окна
		
			$.each(self.options, function(option, value) {
				self._setOption(option, value);
			});
			
			// После создания окна обязательно пересчитать overflow контентной части.
			
			self.resetDialogContentOverflow();
			
			// Обработка события 'После создания окна'
			
			self._trigger("afterCreate");
						
		},
		
		makeDraggable: function() {
			var self = this, options = self.options;
			self.element
				.draggable({
					handle: ".Sultana_Dialog_Header",
					cancel: ".Sultana_Dialog_Controls", 
					containment: options.containment,
					scroll: false
				})
				// Хак для броузера Google Chrome: благодаря disableSelection курсор cursor:move не пропадает.
				.find(".Sultana_Dialog_Header").disableSelection();
		},
		
		makeResizable: function() {
			var self = this, options = self.options;
			self.element
				.resizable({
					containment: options.containment,
					resize: function() { self.resetDialogContentOverflow(); }
				})
				// Хак для JQuery UI: корректировка уголка изменения размеров.
				.find(".ui-resizable-se").addClass("ui-icon ui-icon-grip-diagonal-se"); 
		},
		
		createDialogLayout: function() {
			var self = this, options = self.options;
			var content = $(">*", self.element);
			// Создание контейнера окна
			self.dialog.layout = $("<table class='Sultana_Dialog_Layout'><tbody></tbody></table>").appendTo(self.element);
			// Основные панели окна
			self.appendDialogPanel("Sultana_Dialog_Header");
			self.appendDialogBody();
			self.appendDialogPanel("Sultana_Dialog_Footer");
			// Добавление заголовка окна в панель Sultana_Dialog_Header
			self.dialog.title = $("<span class='Sultana_Dialog_Title'></span>").html(options.title).appendTo(self.dialog.panels["Sultana_Dialog_Header"]);
			// Добавление управляющих кнопок окна в панель Sultana_Dialog_Header
			self.dialog.controls["set"] = $("<div class='Sultana_Dialog_Controls'></div>").appendTo(self.dialog.panels["Sultana_Dialog_Header"]);
			self.appendDialogControl("Sultana_Dialog_Control_Minimize");
			self.appendDialogControl("Sultana_Dialog_Control_Maximize").click(function() { self.toggle(); return false; });
			self.appendDialogControl("Sultana_Dialog_Control_Restorize").hide().click(function() { self.toggle(); return false; });
			self.appendDialogControl("Sultana_Dialog_Control_Close").click(function() { self.close(); return false; });
			// Перемещение контента на свое место в окне
			content.appendTo(self.dialog.content);
		},
		
		appendDialogControl: function(controlClassName) {
			var self = this, options = self.options;
			self.dialog.controls[controlClassName] = 
				$("<a class='Sultana_Dialog_Control' href='#'>&nbsp;</a>")
					.addClass(controlClassName)
					.appendTo(self.dialog.controls["set"]);
			return self.dialog.controls[controlClassName];
		},
		
		appendDialogPanel: function(panelClassName) {
			var self = this, options = self.options;
			var panel = $("<tr><td class='Sultana_Dialog_Panel'></td></tr>").appendTo(self.dialog.layout);
			self.dialog.panels[panelClassName] = panel.find("td").addClass(panelClassName);
			return self.dialog.panels[panelClassName];
		},
		
		appendDialogBody: function() {
			var self = this, options = self.options;
			var body = $("<tr><td class='Sultana_Dialog_Body'></td></tr>").appendTo(self.dialog.layout);
			self.dialog.body = body.find("td");
			self.dialog.content = $("<div class='Sultana_Dialog_Content'></div>").appendTo(self.dialog.body);
			return self.dialog.body;
		},
		
		insertDialogPanel: function() {
			
		},
		
		/**
		 * Восстановление режима overflow 
		 * 
		 * Восстанавливает высоту для контентной части окна после изменения его размеров.
		 * Эта жуткая конструкция для того, чтобы нормально работала опция overflow: auto; контентной части окна.
		 */
		
		resetDialogContentOverflow: function() {
			var self = this, options = self.options, body = self.dialog.body;
			// Вычисление суммарной высоты всех панелей
			var heightPanels = 0;
			$.each(self.dialog.panels, function(i, panel) {
				heightPanels += panel.outerHeight();
			});
			// Хак для броузера FF (правда зачем это требуется пока не знаю)
			var offset = body.outerHeight() - body.innerHeight();
			// Вычисление высоты контентной части окна
			var bodyHeight = self.element.height() - offset - (body.innerHeight() - body.height()) - heightPanels;
			// Установка высоты контентной части окна
			$(".Sultana_Dialog_Content", body).css("height", bodyHeight);
		},
		
		/**
		 * Максимизация окна
		 */
		
		maximize: function() {
			var self = this, options = self.options;

			// Определяем корректировки размеров максимального окна
			
			self.element.addClass("Sultana_Dialog_Maximize");
			var offset = {
				width: self.element.outerWidth() - self.element.width(),
				height: self.element.outerHeight() - self.element.height()
			};
			
			// Временно удаляем класс максимального окна 
			// Такой реверанс чтобы окно не дергалось при увеличении.
			self.element.removeClass("Sultana_Dialog_Maximize");
			
			// Сохраняем позицию и размеры окна до максимизации (для последующего восстановления)
			
			self.dialog.restore = {
				position: self.element.position(), 
				size: { width: self.element.width(), height: self.element.height() }
			};
			
			// Начинаем максимизацию окна с анимацией
			
			var containment = (options.containment == "document") ? "body" : options.containment;
			
			self.element.effect("transfer", { to: containment, className: 'ui-effects-transfer' }, 500, function() {
				// Ставим новые позицию и размеры окна (максимизируем) 
				self.element.css({
					left: $(containment).position().left, 
					top: $(containment).position().top, 
					width: $(containment).width() - offset.width, 
					height: $(containment).height() - offset.height
				});
				// Новые параметры окна в максимальном режиме
				self.dialog.controls.Sultana_Dialog_Control_Restorize.show();
				self.dialog.controls.Sultana_Dialog_Control_Maximize.hide();
				self._setOption("draggable", false);
				self._setOption("resizable", false);
				self.element.addClass("Sultana_Dialog_Maximize");
				self.resetDialogContentOverflow();
			});
		},
		
		/**
		 * Восстановление окна
		 */
		
		restore: function() {
			var self = this, options = self.options;
			var restore = {
				left: self.dialog.restore.position.left, 
				top: self.dialog.restore.position.top, 
				width: self.dialog.restore.size.width, 
				height: self.dialog.restore.size.height
			};
			var trans = $("<div></div>").css(restore).css("position", "absolute").appendTo("body");
			self.element.effect("transfer", { to: trans, className: 'ui-effects-transfer' }, 500, function() {
				trans.remove();
				// Восстановить позицию и размер окна
				self.element.css(restore);
				// Восстановить параметры окна 
				self.dialog.controls.Sultana_Dialog_Control_Restorize.hide();
				self.dialog.controls.Sultana_Dialog_Control_Maximize.show();
				self._setOption("draggable", true);
				self._setOption("resizable", true);
				self.element.removeClass("Sultana_Dialog_Maximize");
				self.resetDialogContentOverflow();
			});
		},
		
		/**
		 * Переключение размера окна: 
		 * maximize/restore
		 */
		
		toggle: function() {
			var self = this, options = self.options;
			if (self.isMaximize()) self.restore(); else self.maximize();
		},
		
		/**
		 * Проверка на масимальность окна
		 */
		
		isMaximize: function() {
			var self = this, options = self.options;
			return !self.dialog.controls["Sultana_Dialog_Control_Maximize"].is(":visible");
		},
		
		/**
		 * Закрытие окна
		 */
		
		close: function(event) {
			var self = this, options = self.options;
			// Если окно открыто, то закрываем
			if (options.open) {
				if (false === self._trigger('beforeClose', event)) return;
				// Удаляем сначала фон модального окна, если таковой есть
				
				// TODO: наверное включение и отключение этого фона вынести нужно в отдельный метод, чтобы тут не загромождать кучей кода
				
				if (options.modal) {
					if ($.browser.msie)
						// Для MSIE просто скрываем фон, иначе всякие глюки для объектов с position=absolute.
						self.dialog.modalbg.hide();
					else
						self.dialog.modalbg.fadeOut();
				}
				
				// Максимальное окно перед закрытием восстанавливаем в размерах
				if (self.element.hasClass("Sultana_Dialog_Maximize")) self.restore();
				self.element.hide("drop", { direction: "right" }, function() {
					options.open = false;	
					self._trigger('afterClose', event);

					self.element.css("left", "-2000");
					self.element.show();
					
				});
			}
			return self;		
		},
		
		/**
		 * Открытие окна
		 */
		
		open: function(event) {
			var self = this, options = self.options;
			if (false === self._trigger('beforeOpen', event)) return;
			// Если окно модальное, то включаем фон
			if (options.modal) {
				self.dialog.modalbg = $("<div/>")
					.addClass("Sultana_Dialog_Modal_Background")
					.appendTo("body")
					.css({
						width: $(document).width() - (($.browser.msie) ? 21 : 0),
						height: $(document).height(),
						zIndex: ++$.ui.Sultana_Dialog.maxZ
					});
				// Для всех броузеров, кроме MSIE (в нем глюк с полупрозрачностью), делаем плавное затемнение.
				
				// TODO: Нужно сделать возможность вообще отключать любые эффекты для окон и таким образом можно
				// для броузера MSIE отключать эти эффекты одним махом, чтобы не мешались (из-за них слишком много непредсказумевых глюков)
				
				if (!$.browser.msie) {
					self.dialog.modalbg.hide().fadeIn();
				}
			}
			// Перемещаем окно поверх всех окон
			self.moveToTop();
			// Если окно не скрыто, то колеблем его анимацией
			if (self.options.open) {
				self.element.effect("bounce", { distance: 10, times: 3 }, 100); 
				return;
			} else {
				// Иначе если окно не было открыто, то открываем
				if (!options.open) {
					if (self.options.position == "center") self._setOption("position", "center");
					
					self.element.hide();
					self.element.css("left", options.left);
					
					self.element.show("drop", function() {
						self._trigger("afterOpen");
						//self.resetDialogContentOverflow();
						options.open = true;
					});
				}
			}
			return self;
		},
		
		/**
		 * Поставить окно поверх всех других окон
		 */
		
		moveToTop: function(force, event) {
			var self = this, options = self.options, body = self.dialog.body;
			if (options.zIndex > $.ui.Sultana_Dialog.maxZ) {
				$.ui.Sultana_Dialog.maxZ = options.zIndex;
			}
	
			// Save and then restore scroll since Opera 9.5+ resets when parent z-Index is changed.
			// http://ui.jquery.com/bugs/ticket/3193
			// Какой жуткий глюк. Это исправление (saveScroll) компенсирует его лишь частично.
			var saveScroll = { scrollTop: body.attr("scrollTop"), scrollLeft: body.attr("scrollLeft") };
			self.element.css("z-index", ++$.ui.Sultana_Dialog.maxZ);
			body.attr(saveScroll);
			
			self._trigger("focus", event);
			return self;
		},
		
		/**
		 * Установка опций окна на ходу
		 */
		
		_setOption: function(option, value) {
			var self = this, options = self.options;
			$.Widget.prototype._setOption.apply(this, arguments);
			switch (option) {
				case "height":
					self.element.css("height", value);
					break;
				case "width":
					self.element.css("width", value);
					break;
				case "top":
					self.element.css("top", value);
					break;
				case "left":
					if (self.options.open) {
						self.element.css("left", value);
					}
					break;
				case "footer":
					var footer = self.dialog.panels["Sultana_Dialog_Footer"];
					if (false === value) {
						footer.parent().hide();
					} else {
						footer.html(value).parent().show();
					}
					break;
				case "open":
					if (false === value) self.close(); else self.open();
					break;
				case "draggable":
					self.element.draggable("option", "disabled", !value);
					break;
				case "resizable":
					self.element
						.resizable("option", "disabled", !value)
						// Хак для JQuery: пока не ясно почему класс ui-state-disabled ставиться.
						.removeClass("ui-state-disabled");
					// Если изменение размеров разрешаем, то кнопку максимизации тоже разрешаем и наоборот.
					var max = self.dialog.controls["Sultana_Dialog_Control_Maximize"];
					if (value) max.show(); else max.hide();
					break;
				case "controls":
					var c = self.dialog.controls, b = value;
					if (b.minimize) { 
						if (b.minimize.enable) { 
							c["Sultana_Dialog_Control_Minimize"].attr("title", b.minimize.label); 
						} else { 
							c["Sultana_Dialog_Control_Minimize"].hide(); 
						}
					}
					if (b.maximize) c["Sultana_Dialog_Control_Maximize"].attr("title", b.maximize.label);
					if (b.restorize) c["Sultana_Dialog_Control_Restorize"].attr("title", b.restorize.label);
					if (b.close) c["Sultana_Dialog_Control_Close"].attr("title", b.close.label);
					break;
				case "position":
					var browser = getBrowserProperties();
					if (value == "center") {
						
						// TODO Тут нужно предусмотреть вариант, когда окно скрыто
						// и нужно будет его show, затем изменить позицию и снова hide, если было до этого скрыто
						
						var top = browser.winScrollTop + (browser.winHeight - self.element.height()) / 2;
						var left = browser.winScrollLeft + (browser.winWidth - self.element.width()) / 2;
						
						if (self.options.open) {
							self.element.css({
								top: top,
								left: left
							});
							//self.resetDialogContentOverflow();
						} else {
							self.element.css("top", top);
							options.left = left;
						}
					}
					break;
			}
		}
		
	});
	
	$.extend($.ui.Sultana_Dialog, {
		version: "1",
		maxZ: 0
	});
	
	function getBrowserProperties() {
		var w = document.documentElement;
		var d = document.body;
		var tww = document.compatMode == 'CSS1Compat' && !window.opera ? w.clientWidth
				: d.clientWidth;
		var twh = document.compatMode == 'CSS1Compat' && !window.opera ? w.clientHeight
				: d.clientHeight;
		var sl = (window.scrollX) ? window.scrollX
				: (w.scrollLeft) ? w.scrollLeft : d.scrollLeft;
		var st = (window.scrollY) ? window.scrollY
				: (w.scrollTop) ? w.scrollTop : d.scrollTop;
		var wW1 = (window.innerHeight && window.scrollMaxY) ? d.scrollWidth
				: (d.scrollHeight > d.offsetHeight) ? d.scrollWidth
						: (w && w.scrollHeight > w.offsetHeight) ? w.scrollWidth
								: d.offsetWidth;
		var wH1 = (window.innerHeight && window.scrollMaxY) ? d.scrollHeight
				: (d.scrollHeight > d.offsetHeight) ? d.scrollHeight
						: (w && w.scrollHeight > w.offsetHeight) ? w.scrollHeight
								: d.offsetHeight;
		var wW2 = (self.innerHeight) ? self.innerWidth
				: (w && w.clientHeight) ? w.clientWidth : d.clientWidth;
		var pW = (wW1 < wW2) ? wW2 : wW1;
		var wH2 = (self.innerHeight) ? self.innerHeight
				: (w && w.clientHeight) ? w.clientHeight : d.clientHeight;
		var pH = (wH1 < wH2) ? wH2 : wH1;
		pW = ($.browser.msie) ? pW : Math.max(w.scrollWidth, w.clientWidth,
				d.scrollWidth, d.offsetWidth);
		pH = ($.browser.msie) ? pH : Math.max(w.scrollHeight, w.clientHeight,
				d.scrollHeight, d.offsetHeight);
		if (window.opera) {
			tww = (d.scrollWidth == d.clientWidth) ? w.clientWidth : tww;
			twh = (d.scrollHeight == d.clientHeight) ? w.clientHeight : twh;
		}
		return {
			winWidth : tww, // Размеры вьюпорта броузера
			winHeight : twh,
			winScrollLeft : sl, // Смещение вьюпорта относительно начала документа
			winScrollTop : st,
			pageWidth : pW, // Размеры документа
			pageHeight : pH
		};
	}

})(jQuery);
