/** Class for creating floating windows, tabs and applying common events to them. */
var Wndw = {

	// Width of the minimized windows
	minimizedWidth: 260,
	// Height of the minimized windows
	minimizedHeight: 60,
	// Inner content's distance from the bottom edge of a resizable window 
	bottomDistance: 36,
	// Default prefix of window areas' ids
	prefix: {
		top: 'WindowTop_',
		fixed: 'WindowFixed_',
		content: 'WindowContent_',
		title: 'WindowTitle_',
		min: 'WindowMin_',
		max: 'WindowMax_',
		close: 'WindowClose_',
		resize: 'WindowResize_',
		iframe: 'WindowIFrame_'
	},
	// Background-color of an active window's title
	activeColor: '#aabbcc',
	// Index for storing the primary element in the openers array
	primaryIndex: 'primary',
	// Additional window classes
	classes: {'window': true},
	// Arrays to store window sizes when minimized
	wwidth: [],
	wheight: [],
	// Indicates whether currently dragged window left the top of the body area or not
	out: false,
	// Which window has been already created, it's id can be found among the indexes of this array with a value of true
	windows: [],
	// Which element has already applyed window events, can be found among the indexes of this array with a value of true
	elements: [],
	// Which window has already got an opener that activates it, can be found among the indexes of this array with a value of its opener id
	openers: [],
	// Default opener
	defaultOpenerId: null,
	// Arrays to store the maximum sizes of the windows
	maximizedWidth: [],
	maximizedHeight: [],
	// Which tab content has been already applyed, it's id can be found among the indexes of this array with a value of true
	tabs: [],
	// List of the currently active tabs
	primaryTabs: [],
	// List of applet container element ids
	appletContainers: [],
	// Stores the colors of the currently activated window
	storedColors: null,

	/**
	 * Create draggable, resizable window from a 'window' classed div.
	 *
	 * @param id          Id of the original 'div' element.
	 * @param title       Title of the window, it can be null in this case the title will generated from the id.
	 * @param windowStr   A comma separated list of window elements. Future events will be applied to the window 
	 *                    elements contained in this list. If it is null, every element will be displayed.
	 */
	create: function(id, title, windowStr) {
		if (Wndw.windows[id] == null && document.getElementById(id) != null && Wndw.classes[document.getElementById(id).className] != null) {
			if (title == null) {
				title = id.charAt(0).toUpperCase()+id.substr(1, id.length-1);
			}
			var draggable = (windowStr == null || (windowStr != null && windowStr.match(/drag/)));
			// IFrame hack to eliminate drop-down list and applet z-index problems
			var windowHtml = '<iframe id="'+Wndw.prefix.iframe+id+'" class="windowFrame" src="" frameborder="0"></iframe>';
			windowHtml += '<div class="'+(draggable ? 'windowTop' : 'fixedWindowTop')+'" id="'+(draggable ? Wndw.prefix.top : 
				Wndw.prefix.fixed)+id+'"><p class="windowTitle" id="'+Wndw.prefix.title+id+'">'+title+'</p></div><div class="windowButtons">';
			if (windowStr == null || (windowStr != null && windowStr.match(/close/))) {
				windowHtml += '<img class="windowClose" id="'+Wndw.prefix.close+id+'" src="images/window_close.jpg" />';
			}
			if (windowStr == null || (windowStr != null && windowStr.match(/min/))) {
				windowHtml += '<img class="windowMin" id="'+Wndw.prefix.min+id+'" src="images/window_min.jpg" />';
			}
			if (windowStr == null || (windowStr != null && windowStr.match(/max/))) {
				windowHtml += '<img class="windowMax" id="'+Wndw.prefix.max+id+'" src="images/window_max.jpg" />';
			}
			windowHtml += '</div>';
			windowHtml += '<div class="windowContent" id="'+Wndw.prefix.content+id+'">'+document.getElementById(id).innerHTML+'</div>';
			if (windowStr == null || (windowStr != null && windowStr.match(/resize/))) {
				windowHtml += '<img class="windowResize" id="'+Wndw.prefix.resize+id+'" src="images/window_resize.gif" />';
			}
			windowHtml += '</div>';
			document.getElementById(id).innerHTML = windowHtml;
			var windowFrame = document.getElementById(Wndw.prefix.iframe+id);
			var doc = windowFrame.contentDocument;
			if (doc == null) {
				doc = windowFrame.contentWindow.document;
			}
			if (doc != null) {
				var backgroundColor = '#ffffff';
				if (document.getElementById(id).currentStyle != null) {
					backgroundColor = document.getElementById(id).currentStyle['backgroundColor']; 
				} else if (window.getComputedStyle != null) {
					backgroundColor = window.getComputedStyle(document.getElementById(id), null).backgroundColor;
				}
				doc.open();
				doc.write('<html><body style="background-color: '+backgroundColor+';"></body></html>');
				doc.close();
			}
			var winWidth = $('#'+id).css('width');
			var winHeight = $('#'+id).css('height');
			$('#'+Wndw.prefix.resize+id).css('left', winWidth);
			$('#'+Wndw.prefix.resize+id).css('top', winHeight);
			$('#'+Wndw.prefix.iframe+id).css('width', winWidth);
			$('#'+Wndw.prefix.iframe+id).css('height', winHeight);
			$('#'+Wndw.prefix.iframe+id).css('visibility', 'visible');
			$('#'+Wndw.prefix.content+id).css('width', '100%');
			$('#'+Wndw.prefix.content+id).css('height', '100%');
			Wndw.windows[id] = true;
		}
	},

	/**
	 * Minimize a window with a particular id.
	 *
	 * @param windowId   Id of the window element.
	 */
	minimize: function(windowId) {
		Wndw.wwidth[windowId] = $('#'+windowId).css('width');
		Wndw.wheight[windowId] = $('#'+windowId).css('height');
		$('#'+Wndw.prefix.content+windowId).hide();
		$('#'+Wndw.prefix.min+windowId).hide();
		$('#'+Wndw.prefix.resize+windowId).hide();
		$('#'+windowId).animate({width: Wndw.minimizedWidth, height: Wndw.minimizedHeight}, 300, function() {
			$('#'+Wndw.prefix.iframe+windowId).css('width', Wndw.minimizedWidth+'px');
			$('#'+Wndw.prefix.iframe+windowId).css('height', Wndw.minimizedHeight+'px');	
		});
		$('#'+Wndw.prefix.max+windowId).show();
	},

	/**
	 * Maximize a window with a particular id.
	 *
	 * @param windowId   Id of the window element.
	 */
	maximize: function(windowId) {
		$('#'+Wndw.prefix.min+windowId).show();
		$('#'+windowId).animate({width: Wndw.wwidth[windowId], height: Wndw.wheight[windowId]}, 300, function() {
			$('#'+Wndw.prefix.iframe+windowId).css('width', Wndw.wwidth[windowId]);
			$('#'+Wndw.prefix.iframe+windowId).css('height', Wndw.wheight[windowId]);
			$('#'+Wndw.prefix.content+windowId).css('width', Wndw.wwidth[windowId]);
			$('#'+Wndw.prefix.content+windowId).css('height', (parseInt(Wndw.wheight[windowId]) - Wndw.bottomDistance)+'px');
			$('#'+Wndw.prefix.content+windowId).show();
			$('#'+Wndw.prefix.resize+windowId).show();
			$('#'+windowId).css('opacity', '0.99');
			setTimeout("$('#"+windowId+"').css('opacity', '1')", 0);
		});
		$('#'+windowId+' .windowMax').hide();
	},

	/**
	 * Resize a window with a particular id.
	 *
	 * @param windowId   Id of the window element.
	 * @param wWidth     New width of the window to set.
	 * @param wHeight    New height of the window to set.
	 */
	resize: function(windowId, wWidth, wHeight) {
		var cHeight = wHeight - Wndw.bottomDistance;
		wWidth += 'px';
		wHeight += 'px';
		cHeight += 'px';
		$('#'+windowId).css('width', wWidth);
		$('#'+windowId).css('height', wHeight);
		$('#'+Wndw.prefix.iframe+windowId).css('width', wWidth);
		$('#'+Wndw.prefix.iframe+windowId).css('height', wHeight);
		$('#'+Wndw.prefix.content+windowId).css('width', wWidth);
		$('#'+Wndw.prefix.content+windowId).css('height', cHeight);
		$('#'+Wndw.prefix.resize+windowId).css('left', wWidth);
		$('#'+Wndw.prefix.resize+windowId).css('top', wHeight);
	},

	/**
	 * Return if a window given by its id is open or closed.
	 *
	 * @param windowId   Id of the window element.
	 * @return boolean.
	 */
	isOpen: function(windowId) {
		return ($('#'+windowId).css('display') == 'block');
	},

	/**
 	 * Open a window with a particular id.
 	 *
 	 * @param windowId     Id of the window element.
 	 * @param openerObj    Window opener DOM object.
 	 * @param afterEvent   Callback function which is executed after the window is opened.
 	 */
	open: function(windowId, openerObj, afterEvent) {
		if ($('#'+windowId).css('display') != 'block') {
			try {
				$(openerObj).TransferTo({
					to: windowId,
					className: 'transferer', 
					duration: 400,
					complete: function() {
						var winHeight = $('#'+windowId).css('height');
						$('#'+Wndw.prefix.content+windowId).css('height', (parseInt(winHeight) - Wndw.bottomDistance)+'px');
						$('#'+windowId).show();
						if (afterEvent != null) {
							afterEvent();
						}
					}
				});
			} catch (e) {}
		}
		Wndw.activate(windowId);
	},

	/**
	 * Close a window with a particular id.
	 *
	 * @param windowId   Id of the window element.
	 */
	close: function(windowId) {
		var toId = Wndw.defaultOpenerId;
		if (Wndw.openers[windowId] != null && Wndw.openers[windowId][Wndw.primaryIndex] != null) {
			toId = Wndw.openers[windowId][Wndw.primaryIndex];
		}
		if (toId == null) {
			$('#'+windowId).hide();
		} else {
			$('#'+windowId).TransferTo({
				to: toId,
				className: 'transferer', 
				duration: 400
			}).hide();
		}
	},

	/**
	 * Attach window opening event to an element.
	 *
	 * @param windowId     Id of the window element.
	 * @param openerId     Id of the window opener element.
	 * @param afterEvent   Callback function that is executed after the window is opened.
	 */
	attachToOpener: function(windowId, openerId, afterEvent) {
		if (openerId != Wndw.primaryIndex && (Wndw.openers[windowId] == null || Wndw.openers[windowId][openerId] == null)) {
			$('#'+openerId).click(function() {
				Wndw.open(windowId, $(this), afterEvent);
			});
			if (Wndw.openers[windowId] == null) {
				Wndw.openers[windowId] = [];
			}
			Wndw.openers[windowId][openerId] = true;
			Wndw.openers[windowId][Wndw.primaryIndex] = openerId;
		}
	},

	/**
	 * Change the primary opener (the element where the window "returns" when closed).
	 *
	 * @param windowId     Id of the window element.
	 * @param openerId     Id of the element which becomes the new primary opener.
	 */
	changePrimaryOpener: function(windowId, openerId) {
		Wndw.openers[windowId][Wndw.primaryIndex] = openerId;
	},

	/**
	 * Add a CSS class which can be handled as a window from this moment.
	 *
	 * @param className   Name of the new window CSS class.
	 */
	addClass: function(className) {
		Wndw.classes[className] = true;
	},

	/**
	 * Put the given window to the top of the others.
	 *
	 * @param windowId   The id of the window to be activated.
	 */
	activate: function(windowId) {
		for (clazz in Wndw.classes) {
			$('.'+clazz).css('z-index', '0');
		}
		if (Wndw.storedColors != null) {
			var lastWindowId = Wndw.storedColors.windowId;
			$('#'+lastWindowId+' .windowTop').css('background-color', Wndw.storedColors.draggableTop);
			$('#'+lastWindowId+' .fixedWindowTop').css('background-color', Wndw.storedColors.fixedTop);
			$('#'+lastWindowId+' .windowButtons').css('background-color', Wndw.storedColors.buttons);
		}
		Wndw.storedColors = {
			windowId: windowId,
			draggableTop: $('#'+windowId+' .windowTop').css('background-color'),
			fixedTop: $('#'+windowId+' .fixedWindowTop').css('background-color'),
			buttons: $('#'+windowId+' .windowButtons').css('background-color')
		};
		$('#'+windowId).css('z-index', '10');
		$('#'+windowId+' .windowTop').css('background-color', Wndw.activeColor);
		$('#'+windowId+' .fixedWindowTop').css('background-color', Wndw.activeColor);
		$('#'+windowId+' .windowButtons').css('background-color', Wndw.activeColor);
	},

	/**
	 * Generic window event creator function (using fixed CSS labels).
	 *
	 * @param elems   JQuery-like selector of elements on which events will be applied.
	 */
	applyEvents: function(elems) {
		$(elems).each(function() {
			var wid = $(this).attr('id');
			if (Wndw.elements[wid] == null) {
				$('#'+Wndw.prefix.top+wid).mousedown(function() {
					Wndw.activate($(this).parent().attr('id'));
				});
				$('#'+wid+' .windowMin, #'+wid+' .windowMax, #'+wid+' .windowClose').unbind('click');
				$('#'+wid+' .windowMin').click(function() {Wndw.minimize($(this).parent().parent().attr('id'))});
				$('#'+wid+' .windowMax').click(function() {Wndw.maximize($(this).parent().parent().attr('id'))});
				$('#'+wid+' .windowClose').click(function() {Wndw.close($(this).parent().parent().attr('id'))});
				if (Wndw.maximizedWidth[wid] == null) {
					Wndw.maximizedWidth[wid] = parseInt($('#'+wid).css('width'));
				}
				if (Wndw.maximizedHeight[wid] == null) {
					Wndw.maximizedHeight[wid] = parseInt($('#'+wid).css('height'));
				}
				$('#'+wid).Resizable({	
					minWidth: Wndw.minimizedWidth,
					minHeight: Wndw.minimizedHeight,
					maxWidth: Wndw.maximizedWidth[wid],
					maxHeight: Wndw.maximizedHeight[wid],
					dragHandle: '#'+Wndw.prefix.top+wid,
					handlers: { se: '#'+Wndw.prefix.resize+wid },
					onDragStart: function() {
						$('#'+wid).css('opacity', '0.7');
						for (var i = 0; i < Wndw.appletContainers.length; i++) {
							if (wid == Wndw.appletContainers[i]) {
								return;
							}
						}
					},
					onDrag: function(x, y) {
						Wndw.out = (y < 0);
					},
					onDragStop: function() {
						if (Wndw.out == true) {
							$('#'+wid).css('top', '0px');
							Wndw.out = false;
						}
						$('#'+wid).css('opacity', '1');
					},
					onResize: function(size, position) {
						$('#'+Wndw.prefix.iframe+wid).css('width', size.width+'px');
						$('#'+Wndw.prefix.iframe+wid).css('height', size.height+'px');
						$('#'+Wndw.prefix.resize+wid).css('left', size.width+'px');
						$('#'+Wndw.prefix.resize+wid).css('top', size.height+'px');
						$('#'+Wndw.prefix.content+wid).css('width', size.width+'px');
						$('#'+Wndw.prefix.content+wid).css('height', (size.height - Wndw.bottomDistance)+'px');
					}
				});
				Wndw.elements[wid] = true;
			}
		});
	},

	/**
	 * Generic tab creator function (using fixed CSS labels).
	 *
	 * @param elems      JQuery-like selector of elements on which tabs will be applied.
	 * @param tabNames   Associative array for tab titles where the page container ids are the keys and the tab titles 
	 *                   are the values. It can be null, in this case the title will be generated from the container id.
	 */
	applyTabs: function(elem, tabNames) {
		var first;
		var tabTitle;
		var tabPrefix = 'tab'+Wndw.tabs.length;
		var tabHtml = '<div class="tabs"><ul>';
		if (Wndw.tabs[elem] == null) {
			Wndw.tabs[elem] = true;
			$('#'+elem+'>div.tabContent').each(function() {
				id = $(this).attr('id');
				tabTitle = (tabNames != null && tabNames[id] != null) ? tabNames[id] : id.replace(/_/, ' ');
				if (first == null) {
					tabHtml += '<li id="'+tabPrefix+id+'"><span>'+tabTitle+'</span></li>';
					first = id;
				} else {
					tabHtml += '<li id="'+tabPrefix+id+'"><a href="javascript:void(0);">'+tabTitle+'</a></li>';
				}
			});
			if (first == null) {
				elem = Wndw.prefix.content+elem;
				$('#'+elem+'>div.tabContent').each(function() {
					id = $(this).attr('id');
					tabTitle = (tabNames != null && tabNames[id] != null) ? tabNames[id] : id.replace(/_/, ' ').charAt(0).toUpperCase()+id.substr(1, id.length-1);
					if (first == null) {
						tabHtml += '<li id="'+tabPrefix+id+'"><span>'+tabTitle+'</span></li>';
						first = id;
					} else {
						tabHtml += '<li id="'+tabPrefix+id+'"><a href="javascript:void(0);">'+tabTitle+'</a></li>';
					}
				});
			}
			if (first == null) {
				return;
			}
			tabHtml += '</ul></div>';
			$('#'+elem).html(tabHtml+$('#'+elem).html());
			if (Wndw.primaryTabs[elem] == null) {
				$('#'+first+'.tabContent').show();
				Wndw.primaryTabs[elem] = first;
			}
			$('#'+elem+' div.tabs li').unbind('click');
			$('#'+elem+' div.tabs li').click(function() {
				id = $(this).attr('id').substr(tabPrefix.length);
				if (id == Wndw.primaryTabs[elem]) {
					return;
				}
				$('#'+Wndw.primaryTabs[elem]+'.tabContent').hide();
				$('#'+id+'.tabContent').show();
				$(this).html('<span>'+$(this).children('a').html()+'</span>');
				$('#'+tabPrefix+Wndw.primaryTabs[elem]).html('<a href="javascript:void(0);">'+$('#'+tabPrefix+Wndw.primaryTabs[elem]).children('span').html()+'</a>');
				Wndw.primaryTabs[elem] = id;
			});
		}
	}
};
