/**
 * @author Andrzej Martynowicz
 * @version 0.65
 * 
 * Note: this library is under development. Some features may
 * slightly change in final version.
 * 
 * This library is redistributed under LGPL license.
 * See http://www.gnu.org/copyleft/lesser.html for details.
 * 
 */

  // =========================================== //
 //  Definition of GlassLayerPopupConstants     //
// =========================================== //

/**
 * @class This class holds some constants used later.
 * @private
 */
function GlassLayerPopupConstants() {}

/**
 * Represents the value of name attribute of the main div which defines the popup
 */
GlassLayerPopupConstants.MAIN_DIV_NAME = "glassLayerPopupDiv";

/**
 * Represents the prefix of id attribute of the main div which defines the popup
 */
GlassLayerPopupConstants.MAIN_DIV_ID_PREFIX = "glassLayerPopup_";

/**
 * Represents the prefix of name attribute of the iframe (only for iframe popups)
 */
GlassLayerPopupConstants.IFRAME_NAME_PREFIX = "glassLayerPopupFrame_";


  // ====================================== //
 //  Definition of GlassLayerPopupUtils    //
//======================================= //


/**
 * This class contains various helper and utility methods
 * 
 * @constructor
 * @return
 */
function GlassLayerPopupUtils() {}
	
/**
 * Returns the width of the screen. For internal use only.
 * @private
 */
GlassLayerPopupUtils.getCanvasWidth = function() {
	var width = document.documentElement.clientWidth;
	
	if (width == 0 || width == undefined) {
		width = document.body.clientWidth; // window.innerWidth
	}
	
	if (width == 0 || width == undefined) {
		width = screen.width;
	}
	return width;
};

/**
 * Returns the height of the screen. For internal use only.
 * @private
 */
GlassLayerPopupUtils.getCanvasHeight = function() {
	var documentHeight = document.documentElement.clientHeight;
	var bodyHeight = document.body.clientHeight; 
	var windowHeight = window.innerHeight;
	
	var height = documentHeight;
	
	if (windowHeight != 0 && windowHeight != undefined && bodyHeight != 0 && bodyHeight != undefined) {
		// if we are on Gecko engine (Firefox, Opera) than we have access to 
		// windowHeight and and bodyHeight. If they are equal than it means that the
		// size of the document is shorter than wole visible area (if body is longer than
		// bodyHeight > windowHeight). In this case we can assume that we can draw
		// on whole bodyHeight and we will use this instead of documentHeight 
		// (documentHeight is shorter than body height)
		if (windowHeight == bodyHeight) {
			height = bodyHeight;
		}
	}
	
	if (height == 0 || height == undefined) {
		height = document.body.clientHeight; // window.innerHeight;
	}

	if (height == 0 || height == undefined) {
		height = screen.height;
	}
	
	return height;
};


/**
 * Returns the top window where application has access
 * @private
 */
GlassLayerPopupUtils.getTopAccessibleWindow = function() {
//	if (window == top) {
//		// we are in top window so current one (top) is of course the
//		// top accessible one
//		return window;
//	}
	
	var topAccessible = window;
	var parentWindow = null;
	var canAccessParent = true;
	
	while(canAccessParent) {
		try {
			parentWindow = topAccessible.parent;
			if (parentWindow == topAccessible) {
				// if parent points to current frame than the current frame does
				// not have parent and it's top one
				canAccessParent = false;
			} else {
				// try to modify the frame
				parentWindow.glassLayerPopupAccessTest = function() {};
				topAccessible = parentWindow;
			}
		} catch (e) {
			// there was no parrent window or we could not access the parnet
			// so we should stop looping since current window is top accesible
			canAccessParent = false;
		}
	}
	return topAccessible;
}


/**
 * Singleton instance which is common for all frames since it resides in top 
 * accessible window (see GlassLayerPopupUtils.getPopupsManagerSingleton to 
 * learn how this is achieved). 
 * 
 * Thanks to this we have always one and only one instance of 
 * GlassLayerPopupsManager in the application no matter how many nested popups
 * we have created (each nested popup created using GlassLayerPopup.showUrl()
 * creates an iframe and we don't want new instance of GlassLayerPopupsManager
 * be created in this frame. We want to have one instance no matter how many
 * frames we created)
 * 
 * @private
 */
GlassLayerPopupUtils.popupsManagerInstance = null;

/**
 * Returns an instance of GlassLayerPopupsManager which is common to all
 * windows and frames
 */
GlassLayerPopupUtils.getPopupsManager = function() {
	if (GlassLayerPopupUtils.popupsManagerInstance == null) {
		var topAccessibleWindow = GlassLayerPopupUtils.getTopAccessibleWindow();
		
		var popupsManager = topAccessibleWindow.GlassLayerPopupsManager;
		if (popupsManager == undefined) {
			// we should not get here.... but if so we can just return current 
			// instance (associated with current window).. however this will 
			// not be shared instance at all :(
			popupsManager = window.GlassLayerPopupsManager;
		}
		
		popupsManager.initialize(); // initialize the manager
		GlassLayerPopupUtils.popupsManagerInstance = popupsManager;
	}
	
	return GlassLayerPopupUtils.popupsManagerInstance;
};

/**
 * When popup is shown using showUrl() than we loose the reference to actual instance of GlassLayerPopup
 * and because of this we can not perform many operation on popup which we might want to do. This method
 * comes as a convenience method which returns a reference to the correct instance of GlassLayerPopup inside
 * which GlassLayerPopupUtils.getCurrentPopup() is called. If this method is not called inside any GlassLayerPopup 
 * created by GlassLayerPopup.showUrl() but by showHtml() than it returns null. 
 * 
 * NOTE: from above there is one conculsion do not call this method if you created popup using 
 * GlassLayerPopup.showHtml(). In this case you still have reference to the popup since the call
 * was made inside the same window.
 */
GlassLayerPopupUtils.getCurrentIframePopup = function() {
	var currentWindowName = window.name; // get's the name of the iframe
	
	if (currentWindowName.indexOf(GlassLayerPopupConstants.IFRAME_NAME_PREFIX) !== 0) {
		throw "Trying to call GlassLayerPopupUtils.getCurrentPopup() inside a frame/window which is not a popup frame " +
				" It's possible that you have nested an iframe inside the popup and trying to call this method from " +
				" inside. This is not supported yet.";
	}
	
	var popupId = GlassLayerPopupUtils.parseIdFromPopupIframeName(currentWindowName);
	
	var popupsManager = GlassLayerPopupUtils.getPopupsManager();
	var popup = popupsManager.popups[popupId];
	
	return popup;
	
};

/**
 * Obtains a reference to the html popup inside which element passed as a 
 * parameter is located. Usually should be called using 
 * GlassLayerPopupUtils.getCurrentHtmlPopup(this)
 */
GlassLayerPopupUtils.getHtmlPopupWithElement = function(element) {
	if (element == null || element == undefined) {
		throw 'Trying to get html popup relative to element which is null or undefined';
	}
	
	var popupMainDiv = GlassLayerPopupDOMUtils.findParentWithName(element, GlassLayerPopupConstants.MAIN_DIV_NAME);
	if (parent == null) {
		throw "Trying to call GlassLayerPopupUtils.getCurrentHtmlPopup() for element "
			+ " which is not located on popup. element is: " + currentElement 
			+ " HINT: when using this and calling from <a> element use onclick instead of href='javascript'";
	}
	
	var mainDivId = popupMainDiv.getAttribute("id");
	var popupId = GlassLayerPopupUtils.parseIdFromMainDivId(mainDivId);
	var popup = GlassLayerPopupsManager.getPopup(popupId);
	
	return popup;
}

/**
 *	Checks if current browser is Internet Explorer.
 *	@private 
 */
GlassLayerPopupUtils.isIEBrowser = function() {
	//return /MSIE (\d+\.\d+);/.test(navigator.userAgent);
	return window.ActiveXObject ? true : false;
};

/**
 * This is private helper function and can not be accessed by outside world.
 * It parses the number from string. The parsing begins at possition defined
 * by beginIndex parameter
 * 
 * @access private
 */
GlassLayerPopupUtils._parseIdFromString = function(str, beginIndex) {
	var firstChar = str.charAt(beginIndex);
	if (isNaN(firstChar)) {
		throw "Can not parse id out of " + str + " The char "
			+ "at index " + beginIndex + " is not numeric";
	}
	
	var ret = "" + str.charAt(beginIndex);
	
	for (var i = beginIndex + 1; i < str.length; i++) {
		var curChar = str.charAt(i);
		if (isNaN(curChar)) {
			break;
		}
		ret += curChar;
	}
	
	return ret;
	
};

	
/**
 * Each main div of GlassLayerPopup has assigned DOM identifier which has following
 * outlook: GlassLayerPopupConstants.MAIN_DIV_ID_PREFIX + uniqueId + "_mainDiv".
 * 
 * This method retrives the uniqueId from above string
 * 
 * @private
 * @access package
 * @return the id of the popup as string
 */
GlassLayerPopupUtils.parseIdFromMainDivId = function(glassLayerPopupIdString) {
	// If GlassLayerPopupConstants.MAIN_DIV_ID_PREFIX == "glassLayerPopup_" 
	// than this has 16 characters, so the 17th (glassLayerPopupIdString[16])
	// stands for the first character of id
	var beginIndex = GlassLayerPopupConstants.MAIN_DIV_ID_PREFIX.length;
	return GlassLayerPopupUtils._parseIdFromString(glassLayerPopupIdString, beginIndex);
	
};

/**
 * Each iframe of GlassLayerPopup has assigned DOM name attribute which has following
 * outlook: GlassLayerPopupConstants.IFRAME_NAME_PREFIX + uniqueId 
 * 
 * This method retrives the uniqueId from above string
 * 
 * @private
 * @access package
 * @return the id of the popup as string
 */
GlassLayerPopupUtils.parseIdFromPopupIframeName = function(iframeName) {
	// If GlassLayerPopupConstants.IFRAME_NAME_PREFIX == "glassLayerPopupFrame_" 
	// than this has 21 characters, so the 22nd (glassLayerPopupIdString[21])
	// stands for the first character of id
	var beginIndex = GlassLayerPopupConstants.IFRAME_NAME_PREFIX.length;
	return GlassLayerPopupUtils._parseIdFromString(iframeName, beginIndex);
	
};

/**
 * @private
 * @access package
 */
GlassLayerPopupUtils.getContentDivByMainDiv = function(mainDivRef) {
	return mainDivRef.childNodes[1];
}

/**
 * @private
 * @access package
 */
GlassLayerPopupUtils.getTopbarDivByContentDiv = function(contentDivRef) {
	return contentDivRef.firstChild;
}

/**
 * Returns reference to iframe element or null in case contentDiv does not
 * contain glassLayerPopup iframe
 * 
 * @private
 * @access package
 */
GlassLayerPopupUtils.getPopupIframeByContentDiv = function(contentDivRef) {
	if (contentDivRef.childNodes.length <= 1 || 
		contentDivRef.childNodes[1].nodeName.toLowerCase() != 'iframe' ||
		contentDivRef.childNodes[1].name.indexOf(GlassLayerPopupConstants.IFRAME_NAME_PREFIX) !== 0) {
		return null;
	}
	
	return contentDivRef.childNodes[1];
}



  // ================================= //
 //  Definition of GlassLayerPopup    //
//================================== //


/**
 * Constructs new instance of GlassLayerPopup
 * 
 * The options:
 * <ul>
 * 	<li> id - the id of the popup - if you set this manually you should set
 * 		      it to the number less than 1000 since 1000 is the first auto
 * 			  generated number</li>
 *  <li> width - the initial width of the popup</li>
 *  <li> height - the initial height of the popup</li>
 *  <li> allowClose - set it to true to enable close button</li>
 *  <li> allowMaximize - set it to true to enable maximize button</li>
 *  <li> scrolling - no|yes|auto - set it to enable or disable scrolling if
 *  			     if content does not fit the window</li>
 *  <li> domDocument - the document element to which the popup will be attached.
 *  		By default the current document is used, but you might want
 *  		to pass here another document. For example if you created popup using
 *  		showUrl and you want to display the anotoher popup from inisde this
 *  		by defaut this will result in popup "inside popup". To overcome this
 *  		(if you want the second popup not to be displayed inside the current
 *  		popup) you can pass here top.document (or the document to which the 
 *  		current popup was attached to) 
 *  <li> styleClassPrefix - the prefix of attribute class of all items in popup.
 *  		It can be used used to customize outlook of popup. By default the 
 *  		preffix is 'glassLayerPopup_', so for example the content div will
 *  		have class attribute 'glassLayerPopup_content_div' </li>
 *  <li> closeButtonHtml - the html which will be displayed as a close button. 
 *  		Defaults to [x].</li>
 *  <li> closeButtonImg - if set overrides the closeButtonHtml. The value describes
 *  		the source of the image (so the attribute of src tag) which should be
 *  		displayed as button which closes window</li>
 *  <li> maximizeButtonHtml - the html which will be displayed as a maximize button
 *  		Defaults to [M].</li>
 *  <li> maximizeButtonImg - if set overrides the maximizeButtonHtml. The value 
 *  		describes the source of the image (so the attribute of src tag) which 
 *  		should be displayed as button which maximizes window</li>
 *  <li> topButtonsSpacerHtml - the html which should be used as a spacer which
 *  		separates the buttons on top bar (close button, maximize button).
 *  		Defaults to &nbsp;</li>
 *  <li> closeButtonOnClick - a function which should be called when the close button 
 *  		(on topbar) is clicked. This function is called before the actual close()
 *  		method is called on popup so it is called before any beforeCloseListeners
 *  		or afterCloseListeners are called</li>
 *  <li> closeButtonOnClickParams - if you whish to use closeButtonOnClick option than
 *  		you can use closeButtonOnClickParams option to pass parameters to the
 *  		function defined by closeButtonOnClick. The parameters should be passed
 *  		inside array, example: 
 *  		new GlassLayerPopup({closeButtonOnClick : function(text) { alert(text);}, closeButtonOnClickParams : new Array("Hello world")}) </li>
 *  <li> contentStyle - adds extra style for the content of the popup. There are
 *  		some limitations here: you are not allowed to pass here following style
 *  		attributes: left, top, position, width, height. Note: width and height
 *  		can be passed via width and height option. Defaults to:
 *  		"border-style: solid; border-width: 1px;"</li>
 *  <li> topbarStyle - adds extra style for the topbar of the popup (topbar is the bar
 *  		there maximize and close buttons are placed)
 *  <li> modal - true or false - decides whether current window should be considered
 *  		modal shich means that user can click only on elements of the popup and
 *  		all the other elements on the page are blocked. Defaults to false.
 *  <li> modalBgStyle - adds extra style for the whole page except for the popup. It's
 *  		role is to cover all the visible area not occupied by the popup with layer
 *  		with style defined by this option. Defaults to: 'color: #f1eedf'
 *  <li> modalBgOpacity - then modalBgStyle is not empty than this option defines the
 *  		opacity of the background. Should be set to value between 0 and 1. Defaults
 *  		to 0.5.
 * </ul>
 * 
 * @constructor
 * @param options array of options
 * @return
 */
function GlassLayerPopup(options) {
	
	// =================== constructor ===================== //
	var defaultOptions = {
			id : 0,
			width : 400,	
			height : 300,
			allowClose : true,
			allowMaximize : true,
			scrolling : 'no',
			domDocument : document,
			styleClassPrefix : 'glassLayerPopup_',
			closeButtonHtml : '[x]',
			closeButtonImg : '',
			maximizeButtonHtml : '[M]',
			maximizeButtonImg : '',
			topButtonsSpacerHtml : '&nbsp;',
			closeButtonOnClick : null,
			closeButtonOnClickParams : null,
			contentStyle : '',
			topbarStyle : '',
			modal : false,
			modalBgStyle : 'background-color: #000000', 
			modalBgOpacity : 0.5
	};
	
	var popupsManager = GlassLayerPopupUtils.getPopupsManager();
	defaultOptions.id = popupsManager.getNextId();
	
	if (options != undefined) {
		// options were passed so we will take default values only for those which were 
		// not passed 
		for (var key in options) {
			defaultOptions[key] = options[key];
		}
	}
	
	this.options = defaultOptions;
	
	// validate the options
	if (this.options.topbarStyle.indexOf('"') != -1) {
		throw 'The opttion topbarStyle can not contain " (double quote) character';
	}
	if (this.options.contentStyle.indexOf('"') != -1) {
		throw 'The opttion contentStyle can not contain " (double quote) character';
	}
	if (this.options.modalBgStyle.indexOf('"') != -1) {
		throw 'The opttion modalBgStyle can not contain " (double quote) character';
	}

	/**
	 * The value of following property should be used as a prefix of all identifiers for this popup
	 * @private
	 */
	this.domPrefix = popupsManager.getDomId(this.options.id);
	this.popupDiv = null; // popupDiv this will be created by this this._show
	this.isIframePopup = null; // isIframePopup will be set by call to showUrl or showHtml
	this.listeners = {};

//	/**
//	 * Contains the current status of the popup. Possible statuses are SHOWING, SHOWN, CLOSING
//	 * @private
//	 */
//	this.state = 'SHOWING';

	popupsManager.popupCreated(this.options.id, this);
	// =============== end of constructor =================== //
	
	// =============== methods definition =================== //
	
	/**
	 * Returns the value of option passed as paremeter
	 * @param option a string identifying the option 
	 * @private
	 * @access package
	 */
	this.getOptionValue = function(option) {
		var optionValue = null;
		eval('optionValue = this.options.' + option);
		return optionValue;
	}
	
	/**
	 * Returns the dom document to which this popup is attached
	 * @private
	 * @access package
	 */
	this.getDomDocument = function() {
		return this.options.domDocument;
	}

	/**
	 * Returns the refference to the main div which builds the popup
	 * @private
	 * @access package
	 */
	this.getMainDiv = function() {
		return this.popupDiv;
	}

	/**
	 * Returns the id generated or manually assigned to given popup
	 */
	this.getId = function() {
		return this.options.id;
	}

	/**
	 * Displays the popup. The content of the popup is filled with given html 
	 */
	this.showHtml = function (html) {
		this.isIframePopup = false;
		this._show(html);
//		this.state = 'SHOWN';
	};
	
	/**
	 * In case of iframe popup this method sets correct size of iframe and
	 * should be called in case size of the whole popup changed and we
	 * need to adjust size of the iframe
	 * @private
	 */
	this.adjustIframeSize = function() {
		var mainDiv = this.popupDiv;
		var contentDiv = GlassLayerPopupUtils.getContentDivByMainDiv(mainDiv);
		var topbarDiv = GlassLayerPopupUtils.getTopbarDivByContentDiv(contentDiv);
		var iframe = GlassLayerPopupUtils.getPopupIframeByContentDiv(contentDiv);
		
		var contentDivHeight = contentDiv.clientHeight;
		var topbarHeight = topbarDiv.offsetHeight;
		var height = contentDivHeight - topbarHeight;
		var width = contentDiv.clientWidth;
		
		iframe.style.height = '' + height + 'px';
		iframe.style.width = '' + width + 'px';
	}
	
	/**
	 * Displays the popup. The content of the popup is loaded from given url
	 * using iframe. 
	 */
	this.showUrl = function (url) {
		this.isIframePopup = true;
		
		var iframeName = GlassLayerPopupConstants.IFRAME_NAME_PREFIX + this.options.id;
		
		// Temoraryly calculate the approximate dimensions of the frame.
		// They will be recalculated as soon as the popup is show. See below. 
		var width  = Math.round(0.95 * this.options.width)
		var height = Math.round(0.95 * this.options.height) - 25; // -25 - predicted height of topbar (with close and minmize buttons)
		
		var content = 
			'<iframe scrolling="'+ this.options.scrolling +'" frameborder="0" name="' + iframeName + '" '
			+	'style="'
			+		'width: ' + width + 'px; '
			+		'height: ' + height + 'px; '
			+ 	'"' 
			+ '></iframe>'
			;
		this._show(content);
		window.frames[iframeName].window.location = url; // set the desired url
		
		this.adjustIframeSize();
//		this.state = 'SHOWN';
	};
	
	/**
	 * Shows modal background which is effects in blocking access to all visible
	 * elements on the screen.
	 */
	this._getModalBackgroundDivHtml = function() {
		if (! this.options.modal) {
			// if popup is not modal we don't cover the page with modal background
			// therefore we will show empty div 
			return '<div></div>';
		}
		
		var width = GlassLayerPopupUtils.getCanvasWidth();
		var height = GlassLayerPopupUtils.getCanvasHeight();
		
		var bgStyle = this.options.modalBgStyle;
		if (bgStyle != '') {
			bgStyle += ';'; 
			if (GlassLayerPopupUtils.isIEBrowser()) {
				bgStyle += 'filter: alpha(opacity='+ (this.options.modalBgOpacity * 100) +');';
			} else {
				bgStyle += 'opacity:'+ this.options.modalBgOpacity +';';
			}
		}
		
		var html = '<div style="'
			+ 'position: absolute; ' 
			+ 'top: 0px; '
			+ 'left: 0px; '
			+ 'height: ' + height + 'px; '
			+ 'width: ' + width + 'px; '
			+ 'height: ' + height + 'px; '
			+ bgStyle
			+ '" '
			+ '></div>'

		return html;	
	}
	
	/**
	 * This is private function. Do not call it because the inner implementation
	 * may change. Use showUrl() or showContent() instead
	 */
	this._show = function (content) {
		var domDocument = this.options.domDocument;		
		
		var popupDiv = domDocument.createElement('div');
		this.popupDiv = popupDiv;
		
		popupDiv.setAttribute("name", GlassLayerPopupConstants.MAIN_DIV_NAME);
		popupDiv.setAttribute("id", this.domPrefix + "_mainDiv");
		
		// so some caluclations before used to determine the content of the div
		var left = Math.round(GlassLayerPopupUtils.getCanvasWidth() / 2 - this.options.width / 2); 
		var top = Math.round(GlassLayerPopupUtils.getCanvasHeight() / 2 - this.options.height / 2);
		
		if (left <= 0) left = 10;
		if (top <= 0) top = 10;
		
		var closeButtonHtml = this.options.closeButtonHtml;
		if (this.options.closeButtonImg != '') { // closeButtonImg overrides the closeButtonHtml
			closeButtonHtml = '<img src="'+ this.options.closeButtonImg +'" border="0" class="' + this.options.styleClassPrefix + 'close_button_img" />'
		}
		
		var maximizeButtonHtml = this.options.maximizeButtonHtml;
		if (this.options.maximizeButtonImg != '') { // maximizeButtonImg overrides the maximizeButtonHtml
			maximizeButtonHtml = '<img src="'+ this.options.maximizeButtonImg +'" border="0" class="' + this.options.styleClassPrefix + 'maximize_button_img" />'
		}
		
		var topbarDivStyle = (this.options.topbarStyle == '' ? 'text-align: right' : this.options.topbarStyle);  
		var contentDivExtraStyle = (this.options.contentStyle == '' ? '' : this.options.contentStyle);  
		
		popupDiv.innerHTML =
			this._getModalBackgroundDivHtml()
			+ '<div style="'
			+ contentDivExtraStyle + '; '
			+ 'width: ' + this.options.width + 'px; '
			+ 'height: ' + this.options.height + 'px; '
			+ 'position: absolute; '
			+ 'left: ' + left + 'px; '
			+ 'top: ' + top + 'px; '
			+ '"'
			+ 'class="' + this.options.styleClassPrefix + 'content_div"'
			+ '>'
			+ '<div id="' + this.domPrefix + '_topButtonsDiv" style="'+ topbarDivStyle +'" class="' + this.options.styleClassPrefix + 'topbar_div">'
			+ (this.options.allowMaximize 
				 	? '<a id="' + this.domPrefix + '_topButtonMaximize" +  '
				 		+ 'style="color: black; font-weight: bold; text-decoration: none" '
						+ 'href="javascript:void(0)">'+ maximizeButtonHtml +'</a>'
						+ this.options.topButtonsSpacerHtml
				 	: '')
			+ (this.options.allowClose 
			 	? '<a id="' + this.domPrefix + '_topButtonClose" +  '
			 		+ 'style="color: black; font-weight: bold; text-decoration: none" '
					+ 'href="javascript:void(0)">'+ closeButtonHtml +'</a>'
					+ this.options.topButtonsSpacerHtml
			 	: '')
			+ '</div>'
			+ content
			+ '</div>'
			;
		
		var body = domDocument.getElementsByTagName('body')[0];
		body.appendChild(popupDiv);
		
		// Now we will set the actions for all the buttons in the top bar
		if (this.options.allowMaximize) {
			var contentDiv = GlassLayerPopupUtils.getContentDivByMainDiv(popupDiv);
			var topButtonsDiv = GlassLayerPopupUtils.getTopbarDivByContentDiv(contentDiv);
			
			var maximizeLink = GlassLayerPopupDOMUtils.findChildWithId(topButtonsDiv, this.domPrefix + '_topButtonMaximize');
			
			maximizeLink.onclick = function() {
				// beacause this function will be fired as onclick event of closeLink 
				// "this" will point to the maximizeLink itself (so to the <a href="..."> 
				// element
				var maximizeLinkElem = this; 
				var popupsManager = GlassLayerPopupUtils.getPopupsManager();
				
				// mainDiv is the main div of the popup (it is the actual popup itself) 
				var mainDiv = maximizeLinkElem.parentNode.parentNode.parentNode;
				var mainDivId = mainDiv.id;
				var popupId = GlassLayerPopupUtils.parseIdFromMainDivId(mainDivId);
				
				popupsManager.maximizePopup(popupId);
			};
		};
		
		
		if (this.options.allowClose) {
			var contentDiv = GlassLayerPopupUtils.getContentDivByMainDiv(popupDiv);
			var topButtonsDiv = GlassLayerPopupUtils.getTopbarDivByContentDiv(contentDiv);
			
			var closeLink = GlassLayerPopupDOMUtils.findChildWithId(topButtonsDiv, this.domPrefix + '_topButtonClose');
			
			closeLink.onclick = function() {
				// beacause this function will be fired as onclick event of closeLink 
				// "this" will point to the closeLink itself (so to the <a href="..."> 
				// element
				var closeLinkElem = this;
				
				var popupsManager = GlassLayerPopupUtils.getPopupsManager();
				
				// mainDiv is the main div of the popup (it is the actual popup itself) 
				var mainDiv = closeLinkElem.parentNode.parentNode.parentNode;
				var mainDivId = mainDiv.id;
				var popupId = GlassLayerPopupUtils.parseIdFromMainDivId(mainDivId);
				
				var popup = popupsManager.getPopup(popupId);
				
				var onCloseCallback = popup.getOptionValue('closeButtonOnClick');
				var onCloseCallbackParams = popup.getOptionValue('closeButtonOnClickParams');

				if (onCloseCallback != null) {
					var callbackParams = (onCloseCallbackParams == null ? new Array() : onCloseCallbackParams);
					onCloseCallback.apply(onCloseCallback, callbackParams);
				}

				popup.close();
				
			};
		};
		
		// as a final step we will notify the manager that the popup has been shown
		var popupsManager = GlassLayerPopupUtils.getPopupsManager();
		popupsManager.popupShown(this.options.id, this);
	};
	
	/**
	 * A function passed as the argrument will be called just before the popup is closed
	 */
	this.addBeforeCloseListener = function(callback) {
		if (this.listeners.beforeClose == undefined) {
			this.listeners.beforeClose = new Array();
		}
		this.listeners.beforeClose.push(callback);
	}
	
	/**
	 * A function passed as the argrument will be called right after the popup is closed
	 */
	this.addAfterCloseListener = function(callback) {
		if (this.listeners.afterClose == undefined) {
			this.listeners.afterClose = new Array();
		}
		this.listeners.afterClose.push(callback);
	}
	
	/**
	 * listenerType is the index of associative array this.listeners. So for example
	 * if index is onClose than all this.listerners.onClose will be called
	 * 
	 * @private
	 */
	this.callListeners = function(listenerType) {
		var listeners;
		eval('listeners = this.listeners.'+ listenerType);
		
		if (listeners != undefined) {
			for (var i=0; i<listeners.length; i++) {
				listeners[i].call(listeners[i], this);
			}
		}
	}
	
	/**
	 * Closes this popup
	 * @access public
	 */
	this.close = function() { 
//		if (this.state == 'CLOSING') {
//			throw "Trying to close popup which already is closing. You probably tried to call close() twice";
//		}
//		this.state = 'CLOSING';
		
		this.callListeners('beforeClose');
		
		var mainDiv = this.popupDiv;
		var domDocument = this.options.domDocument;

		var body = domDocument.getElementsByTagName('body')[0];
		body.removeChild(mainDiv);
		
		var popupsManager = GlassLayerPopupUtils.getPopupsManager();
		popupsManager.popupClosed(this.options.id);
		
		this.callListeners('afterClose');
	}
	
	
	/**
	 * Closes this popup
	 * @access public
	 */
	this.maximize = function() {
		var mainDiv = this.popupDiv;
		var contentDiv = GlassLayerPopupUtils.getContentDivByMainDiv(mainDiv);
		
		var borderWidth = contentDiv.offsetWidth - contentDiv.clientWidth;
		var borderHeight = contentDiv.offsetHeight - contentDiv.clientHeight;
		
		var newWidth = GlassLayerPopupUtils.getCanvasWidth() - borderWidth;
		var newHeight = GlassLayerPopupUtils.getCanvasHeight() - borderHeight;
		var newLeft = 0;
		var newTop = 0;
		
		contentDiv.style.width = newWidth + 'px';
		contentDiv.style.height = newHeight + 'px';
		contentDiv.style.left = newLeft + 'px';
		contentDiv.style.top = newTop + 'px';
		
		// now if this is iframe popup (shwn using GlassLayerPopup.showUrl())
		// we need to resize the iframe with content as well
		if(this.isIframePopup) {
			this.adjustIframeSize();
		}
	}
	
	/**
	 * Returns string representation of this popup containing the id of the popup.
	 * 
	 * NOTE: this is toString() method so do not expect that this method will return the same string when
	 * the library will be updated. This is debug method not a business method. 
	 *
	 * @access public
	 */
	this.toString = function() {
		return '[GlassLayerPopup id: '+ this.options.id +']';
	};
	
};


  // ======================================= //
 //  Definition of GlassLayerPopupsManager  //
//======================================== //


/**
 * @class This class contains functions which operates on one or many popups
 * Call the methods in static way and don't instantiate it unless you are not
 * afraid of troubles :)
 *
 */
function GlassLayerPopupsManager() {}

/**
 * @private
 */
GlassLayerPopupsManager.initialized = false;

/**
 * This is kind of static initialization. This is called by
 * GlassLayerPopupUtils.getInstanceOfGlassLayerPopupsManager() to initialize one
 * and only one instance if GlassLayerPopupsManager.
 * 
 * NOTE: do not call this method directly. This can be called only by function 
 * mentioned above.
 * 
 * @private
 * @access package
 */
GlassLayerPopupsManager.initialize = function() {
	if (! GlassLayerPopupsManager.initialized) {
		GlassLayerPopupsManager.popups = GlassLayerPopupsManager._popups; 
		GlassLayerPopupsManager.currentId = GlassLayerPopupsManager._currentId; 
		GlassLayerPopupsManager.getNextId = GlassLayerPopupsManager._getNextId;
		GlassLayerPopupsManager.getPopup = GlassLayerPopupsManager._getPopup;
		GlassLayerPopupsManager.getDomId = GlassLayerPopupsManager._getDomId;
		GlassLayerPopupsManager.popupCreated = GlassLayerPopupsManager._popupCreated;
		GlassLayerPopupsManager.popupShown = GlassLayerPopupsManager._popupShown;
		GlassLayerPopupsManager.popupClosed = GlassLayerPopupsManager._popupClosed;
		GlassLayerPopupsManager.closeAllPopups = GlassLayerPopupsManager._closeAllPopups;
		GlassLayerPopupsManager.closePopup = GlassLayerPopupsManager._closePopup;
		GlassLayerPopupsManager.maximizePopup = GlassLayerPopupsManager._maximizePopup;
		GlassLayerPopupsManager.initialized = true;
	}
}


/**
 * Associative array where key is id of the popup and value is a reference to
 * the popup with the id. Stores all the popups.
 * 
 * WARNING: do not call directly to GlassLayerPopupsManager._popups, call to 
 * GlassLayerPopupsManager.getPopups() instead since this will return correct
 * 
 * @private
 */
GlassLayerPopupsManager.popups  = function() { throw "Not initialized yet!" };
GlassLayerPopupsManager._popups = new Object();
/**
 * Stores the counter which is used to generated ids for popups
 * @private
 */
GlassLayerPopupsManager.currentId  = function() { throw "Not initialized yet!" };
GlassLayerPopupsManager._currentId = 1000; 

/**
 * This method will act as a counter of all instances of GlassLayerPopup. Its
 * role is to create automathic numbering of all popups. The first assigned 
 * number is 1001. 
 * 
 * NOTE: if you need to assign your own id give it the number lesser than 1000
 * and pass as an option to constructor of GlassLayerPopup

 * @private
 * @access package
 */
GlassLayerPopupsManager.getNextId  = function() { throw "Not initialized yet!" };
GlassLayerPopupsManager._getNextId = function() {
	GlassLayerPopupsManager.currentId = GlassLayerPopupsManager.currentId + 1;
	return GlassLayerPopupsManager.currentId;
};

/**
 * Returns reference to popup with given id 
 * 
 * @param popupId
 * @return
 */
GlassLayerPopupsManager.getPopup  = function(popupId) { throw "Not initialized yet!" };
GlassLayerPopupsManager._getPopup = function(popupId) {
	return GlassLayerPopupsManager.popups[popupId];
};

/**
 * Returns DOM unique id for given popupId. The popupId is the numeric unique
 * identifier of popup generated by GlassLayerPopupsManager.getNextId()
 * 
 * @param popupId
 * @private
 * @access package
 */
GlassLayerPopupsManager.getDomId  = function() { throw "Not initialized yet!" };
GlassLayerPopupsManager._getDomId = function(popupId) {
	return GlassLayerPopupConstants.MAIN_DIV_ID_PREFIX + popupId;	
};

/**
 * This is listener method. It is called by constructor of GlassLayerPopup to notify
 * the manager that the popup with given id has been created. The manager  
 * saves the reference to the popup
 * 
 * @private
 * @access package
 */
GlassLayerPopupsManager.popupCreated  = function(popupId, popupInstance) { throw "Not initialized yet!" };
GlassLayerPopupsManager._popupCreated = function(popupId, popupInstance) {
	GlassLayerPopupsManager.popups[popupId] = popupInstance;
}

/**
 * This is listener method. It is called by GlassLayerPopup._show() to notify
 * the manager that the popup with given id has been shown. The manager 
 * saves the reference to the the popup. <br/><br/>
 * 
 * NOTE: This might seem like a duplicate of GlassLayerPopupsManager.popupCreated() method. However this
 * listeners is called every time when the popup is shown, so allows manger to be aware of it even when it
 * is shown again. Consider following code:<br/><br/>
 * 
 * 
 * var popup = new GlassLayerPopup(); <i>// will issue a call to GlassLayerPopupsManager.popupCreated() which adds the reference to popup</i><br/>
 * popup.showHtml("Hello world!"); <i>// will issue a call to GlassLayerPopupsManager.popupShown() which replaces the reference added in previous step</i><br/>
 * popup.close(); <i>// will issue a call to GlassLayerPopupsManager.popupClosed() which removes the reference to popup</i><br/>
 * popup.showHtml("Hello again!"); <i>// will issue another call to GlassLayerPopupsManager.popupShown() and adds again the reference that has been removed while calling close()</i><br/>
 * 
 * 
 * @private
 * @access package
 */
GlassLayerPopupsManager.popupShown  = function(popupId, popupInstance) { throw "Not initialized yet!" };
GlassLayerPopupsManager._popupShown = function(popupId, popupInstance) {
	GlassLayerPopupsManager.popups[popupId] = popupInstance;
}

/**
 * This is listener method. It is  called by GlassLayerPopup.close() method to notify
 * the manager that the popup has been closed. The manager assumes that it can
 * remove reference to this popup from all popups which it manages
 * 
 * @private
 * @access package
 */
GlassLayerPopupsManager.popupClosed  = function(popupId) { throw "Not initialized yet!" };
GlassLayerPopupsManager._popupClosed = function(popupId) {
	delete GlassLayerPopupsManager.popups[popupId];
}

/**
 * Asks a popup with given id to close
 */
GlassLayerPopupsManager.closePopup  = function(popupId) { throw "Not initialized yet!" };
GlassLayerPopupsManager._closePopup = function(popupId) {
	var popup = GlassLayerPopupsManager.popups[popupId];
	popup.close();
}

/**
 * Asks all open popups to close
 */
GlassLayerPopupsManager.closeAllPopups  = function() { throw "Not initialized yet!" };
GlassLayerPopupsManager._closeAllPopups = function() {

	for (var popupId in GlassLayerPopupsManager.popups) {
		GlassLayerPopupsManager.closePopup(popupId);
	}
	
};	

/**
 * Asks a popup with given id to maximize
 */
GlassLayerPopupsManager.maximizePopup = function(popupId) { throw "Not initialized yet!" };
GlassLayerPopupsManager._maximizePopup = function(popupId) {
	var popup = GlassLayerPopupsManager.popups[popupId];
	popup.maximize();
}


  // ======================================== //
 //  Definition of GlassLayerPopupDOMUtils   //
//========================================= //


/**
 * @private
 * 
 * This class contains methods which operates on DOM elements. Was added for
 * convenience in order not to bother external libraries
 * @return
 */
function GlassLayerPopupDOMUtils() {}

/**
 * Inspects all childs of given parent element and returns child element 
 * with given id. If such child is not found null is returned.
 */
GlassLayerPopupDOMUtils.findChildWithId = function(parentElement, childId) {
	var childElement = null;
	for (var i = 0; i < parentElement.childNodes.length; i++) {
		var curChild = parentElement.childNodes[i];
		if (curChild.id == childId) {
			childElement = curChild;
			break;
		}
	}
	
	return childElement;
};

/**
 * Goes up in dom tree to find an element with name given as a parameter. If such
 * element is not found null is returned
 */
GlassLayerPopupDOMUtils.findParentWithName = function(currentElement, parentName) {
	var parentElem = null;
	
	var canAccessParent = true;
	var curParent = null;
	
	while(canAccessParent && parentElem == null) {
		try {
			var curParent = currentElement.parentNode;
			if (curParent == currentElement) {
				// if parent points to the same element than somethig is wrong... 
				canAccessParent = false;
			} else if (currentElement.getAttribute && currentElement.getAttribute("name") == parentName) {
				parentElem = currentElement
			}
			currentElement = curParent;
		} catch (e) {
			// there was no parrent element or we could not access the parnet
			// so we should stop looping since current window is top accesible
			canAccessParent = false;
		}
	}
	return parentElem;

};

