/*******************************************************************************

$Id: VacancySys.js,v 1.2 2009-08-03 06:11:08 ozgur Exp $

$Source: /cvs/web/vacancy/html/js/VacancySys.js,v $

Popup management object:
	- Allows creation of modal 'panels' instead of popups. These panels can
	'detached' and turned back into popups if needed.
	- Detached popups can use the same mechanism.
	- Tracks detached popups so that:
		- minimized popups that have their contents are brought back
		into focus.
		- if a panel is opened when a popup for that functionality is
		hidden away, the popup is closed.
		- when the page using VacancySys is refreshed, tracking data is
		passed onto the new one.
	- Handles refreshing of openers/iframes based on popup/panel structure.
	Accomodates cascading behaviour to refresh a hierarchy of popups/panels
	where popups and top level panels are immediately refreshed whereas
	pages containing active panels are marked for refresh when their panels
	are detached or closed.

USAGE:
	- This object should be inserted into a page through the cgi creating
	the page using site_perl/Utils/PageBuilder which will also add
	dependencies.

	- In the case of popup pages creating popups, VacancySysSupport.js
	should also be included on the page

$Log: VacancySys.js,v $
Revision 1.2  2009-08-03 06:11:08  ozgur
_displayPage():Set display frame height to 0 to prevent panels using the height of old document when auto sizing._createDetached():create a popup size based on panel size if popup size is not specified. RT#142630

Revision 1.1  2009-06-30 06:21:12  ozgur
Initial release


*******************************************************************************/

VacancySys = function () {

	var
		Y = YAHOO,
		YW = Y.widget,
		YL = Y.lang,
		YU = Y.util,
		YUD = YU.Dom,
		YUE = YU.Event,
		YLJ = YL.JSON,
		isIE = YAHOO.env.ua.ie,

		// IFrame Handling
		_iframeDisplayIdName = "popup_replacer",
		_iframeControlIdName = "popup_controller",
		_iframeMaskIdName = "popup_mask",

		// Element Refs
		_iframeDisplay = null,
		_iframeControl = null,
		_iframeMask = null,
		_iframeBtnClose = null,
		_iframeBtnDetach = null,

		// Nested iframes refs, used for z-indexing
		_ifLayer = 0

		// Control iFrame minimum sizes
		_controlIframeSizesSet = 0,
		_controlIframeMinWidth = 0,
		_controlIframeMinHeight = 0,

		// Document display iFrame settings.
		_ifReqConfWidth = null,
		_ifReqConfHeight = null,

		// Stores popup arguments passed so we can use them
		// when detaching our panels
		_ifReqConfPopArgs = null,

		// We can't access iFrame's document properties if its an
		// external url
		_iframeDocExternal = false,

		// Popup windows' onload event is unreliable so we'll let
		// the detached windows call the necessary functions if
		// we're expecting it to
		_amWaitingDetachedLoad = false,


		// Refresh control. If the current page can't be immediately
		// refreshed (has child iframes) this stores the refresh
		// request for later processing.
		_refreshOrders = {active: false, cascade: false, excludeMe: false},

		// Detached pop-up tracking and handling
		// NOTE: Only pop-ups with a name can be tracked! This
		// restriction is applied so we can pass on tracking data
		// when our page is refreshed and gets a new popup manager.
		// Ensure that this argument is always provided or put up
		// with screen flicker.
		_detached_Popups = [],
		_defaultWinName = 'VacancySysPopup'
	;

	YUE.onDOMReady(function() {_initPage();});

	/****************************** VacancySys INITIALIZATION FUNCTIONS *****************************/
	
	function _initPage() {
		//
		// setup our iframe refs
		//
		_iframeControl = YUD.get(_iframeControlIdName);
		_iframeDisplay = YUD.get(_iframeDisplayIdName);
		_iframeMask = YUD.get(_iframeMaskIdName);

		//
		// Make sure we didn't forget to include the iframes on the page!
		// NOTE: Dev tool
		//
		if (!(YL.isValue(_iframeControl) && YL.isValue(_iframeDisplay) && YL.isValue(_iframeMask))) {
			console.log("where are my iframes?!");
			return false;
		}

		//
		// We need to know our layer for z-indexes.
		// window.parent.VacancySys will point back at us if there is
		// no parent manager so we always start with layer 1
		//
		_ifLayer = window.parent.VacancySys.getLayer() + 1;

		//
		// redraw our iframes when they are visible and there is resizing
		// or scrolling happening.
		//
		var redrawOnDOMEvent = function() {
			if (YUD.getStyle(_iframeDisplay, 'visibility') == 'visible') {
				_redraw();
			}
		};
		YUE.on(window.parent, "resize", redrawOnDOMEvent);
		YUE.on(window, "scroll", redrawOnDOMEvent);

		//
		// There might have been a manager handling this page and the
		// page got refreshed on it. Go get any data it sent to us.
		//
		_importDetached();

		return true;
	};

	function _importDetached() {
		if (YL.isValue(window.existing_popups)) {
			_detached_Popups = window.existing_popups;
		}
	};

	/******************************** IFRAME INITIALIZATION FUNCTIONS *******************************/

	/*
	* Build our control iframe - or the panel's control bar if you like.
	* Called by our controllerOnLoad public interface which in turn is
	* triggered by the iFrame element's hardcoded onload event.
	*/
	function _initControlIframe() {
		if (_iframeControl) {
			var 
				cfDoc = _iFrameDoc(_iframeControl),
				btnCloseEl = cfDoc.getElementById("ifc_btn_close"),
				btnDetachEl = cfDoc.getElementById("ifc_btn_detach")
			;
	
			if (YL.isValue(btnCloseEl) && YL.isValue(btnDetachEl)) {
				_iframeBtnClose = new YW.Button(btnCloseEl);
				_iframeBtnDetach = new YW.Button(btnDetachEl);
			} else {
				console.log("Couldn't find control buttons!");
				return false;
			}

			_setControlIframeSize();
		}
	};


	/******************************* IFRAME DISPLAY MANAGEMENT FUNCTIONS ****************************/

	/*
	* Set minimum width and height values for the control iFrame for use when
	* redrawing the iframe collection.
	*/	
	function _setControlIframeSize() {
		var
			cfDoc = _iFrameDoc(_iframeControl),
			dfTable = cfDoc.getElementById("ctrl_table")
		;
	
		_controlIframeMinWidth = _getDocumentWidth(cfDoc);
		_controlIframeMinHeight = dfTable.offsetHeight;

		if (_controlIframeMinWidth > 0 && _controlIframeMinHeight > 0) { _controlIframeSizesSet = 1; }
	};

	/*
	* position/resize control, content and mask iframes based on viewport
	* and scroll position. 
	*/
	function _redraw() {
		var
			pgWidth = YUD.getViewportWidth(),
			pgHeight = YUD.getViewportHeight(),
			docWidth = YUD.getDocumentWidth(),
			docHeight = YUD.getDocumentHeight()
			pDoc = document,
			dfDoc = null,
			mfDoc = _iFrameDoc(_iframeMask),
			pDocScrollX = YUD.getDocumentScrollLeft(pDoc),
			pDocScrollY = YUD.getDocumentScrollTop(pDoc),
			dfDocW = 0,
			dfDocH = 0,
			dfReqW = _ifReqConfWidth,
			dfReqH = _ifReqConfHeight,
			maxW = 0,
			totalH = 0,
			xMargin = 0,
			yMargin = 10 
		;

		if (!_controlIframeSizesSet) {_setControlIframeSize();}

		// Collect Information
		if (_iframeDocExternal) {
			dfDocW = pgWidth;
			dfDocH = pgHeight;
		} else {
			dfDoc = _iFrameDoc(_iframeDisplay);
			dfDocW = _getDocumentWidth(dfDoc);
			dfDocH = _getDocumentHeight(dfDoc);
		}

		if (YL.isValue(dfReqW) && dfReqW > 0) {
			maxW = dfReqW;
		} else {
			maxW =  Math.max(_controlIframeMinWidth, dfDocW);
		}

		if (YL.isValue(dfReqH) && dfReqH > 0) {
			dfDocH = dfReqH;
		}

		totalH = dfDocH + _controlIframeMinHeight;

		// Set z-indexes
		YUD.setStyle(_iframeControl, 'z-index', (_ifLayer*10)+1);
		YUD.setStyle(_iframeDisplay, 'z-index', (_ifLayer*10)+1);
		YUD.setStyle(_iframeMask, 'z-index', _ifLayer*10);

		// Set heights
		if (totalH > (pgHeight-(yMargin * 2))) {
			dfDocH = pgHeight - (yMargin * 2) - _controlIframeMinHeight;
		} else if (totalH < pgHeight-(yMargin * 2)) {
			yMargin = (pgHeight - totalH) / 2;
		}
		yMargin += pDocScrollY;
		YUD.setStyle(_iframeControl, 'height', _controlIframeMinHeight);
		YUD.setStyle(_iframeDisplay, 'height', dfDocH);
		YUD.setStyle(_iframeMask, 'height', docHeight);

		// Set widths
		if (maxW > (pgWidth-20)) { maxW = (pgWidth-20); }
		xMargin = (pgWidth - maxW) / 2; 
		YUD.setStyle(_iframeControl, 'width', maxW);
		YUD.setStyle(_iframeDisplay, 'width', maxW);
		YUD.setStyle(_iframeMask, 'width', docWidth);

		// Set positions
		YUD.setStyle(_iframeControl, 'left', xMargin);
		YUD.setStyle(_iframeControl, 'top', yMargin);
		YUD.setStyle(_iframeDisplay, 'left', xMargin);
		YUD.setStyle(_iframeDisplay, 'top', yMargin+_controlIframeMinHeight);
		YUD.setStyle(_iframeMask, 'left', 0);
		YUD.setStyle(_iframeMask, 'top', 0);
		YUD.setStyle(_iframeMask, 'right', 0);
		YUD.setStyle(_iframeMask, 'bottom', 0);
	};

	function _showIFrames() {
		if (
			YUD.getStyle(_iframeDisplay, 'visibility') == 'hidden'
			&& YUD.getStyle(_iframeControl, 'visibility') == 'hidden'
			&& YUD.getStyle(_iframeMask, 'visibility') == 'hidden'
		) {
			_redraw();
			YUD.setStyle(_iframeDisplay, 'visibility', 'visible');
			YUD.setStyle(_iframeControl, 'visibility', 'visible');
			YUD.setStyle(_iframeMask, 'visibility', 'visible');
			_disableParentControlFrame(true);
			return true;
		}
		return false;
	};

	function _hideIFrames() {
		if (
			YUD.getStyle(_iframeDisplay, 'visibility') == 'visible'
			&& YUD.getStyle(_iframeControl, 'visibility') == 'visible'
			&& YUD.getStyle(_iframeMask, 'visibility') == 'visible'
		) {
			YUD.setStyle(_iframeControl, 'visibility', 'hidden');
			YUD.setStyle(_iframeDisplay, 'visibility', 'hidden');
			YUD.setStyle(_iframeMask, 'visibility', 'hidden');
			
			_disableParentControlFrame(false);
			return true;
		}
		return false;
	};


	/******************************** IFRAME DOCUMENT HANDLING FUNCTIONS ****************************/
	
	/*
	* Get a reference to the iframe document based on what function works on
	* the user's browser
	*/
	function _iFrameDoc(iFrame) {
		if (iFrame) {
			if (isIE) {
				if (YL.isValue(iFrame.contentWindow)) {
					return iFrame.contentWindow.document;
				} else {
					return null;
				}
			} else {
				return iFrame.contentDocument;
			}
		}
	};

	/*
	* Used in place of location.reload() so we can remove messages sent to us
	* and add messages to pass to the new popup manager that will turn up.
	* forceGet arg mimicks the same arg for location.reload() by appending
	* a random number to the url if there are no outgoing no messages.
	*/
	function _reloadLocation(forceGet) {
		var
			additionalArgs = false,
			locTo = window.location.href
		;

		// 
		// Remove existing messages / random numbers
		//
		locTo = locTo.replace(/(&existing_popups=.*|&random=.*)/g, '');

		//
		// Pass our array of tracked popup names to the new manager
		//
		if (YL.isArray(_detached_Popups) && _detached_Popups.length > 0) {
			var detachedJSON = YLJ.stringify(_detached_Popups);
			additionalArgs = true;
			locTo = locTo+'&existing_popups='+encodeURIComponent(detachedJSON);
		}

		if (forceGet && !additionalArgs) {
			locTo = locTo+'&random='+Math.random();
		}

		window.location.href = locTo;
	};

	/************************************ IFRAME USAGE FUNCTIONS ************************************/

	/*
	* Enable/disable the control iframe (detach and close buttons).
	* called throught our public interface by a child iframe in our content
	* iframe using the next function.
	*/
	function _disableControlFrame(disable) {
		if (YL.isValue(_iframeControl) && YL.isValue(_iframeBtnClose) && YL.isValue(_iframeBtnDetach)) {
			_iframeBtnDetach.set('disabled', disable);
			_iframeBtnClose.set('disabled', disable);
		}
	};

	/*
	* Disable/enable the parent iFrame's parent popup controller's control
	* iframe! say that 3 times fast!
	*/
	function _disableParentControlFrame(disable) {
		if (
			YL.isValue(parent.document)
			&& YL.isValue(document)
			&& parent.document != document
			&& YL.isValue(parent.VacancySys)
		) {
			parent.VacancySys.disableControlFrame(disable);
		}
	};

	function _closeIFrames() {
		_hideIFrames();
		_iframeDisplay.src = '';
		//
		// free of children, we can now refresh if we need to.
		//
		_applyRefreshOrders();
	};

	/*
	* Move the current content iFrame into a pop-up.
	*/
	function _detachContent() {
		var
			currUrl = _iframeDisplay.src,
			args = _ifReqConfPopArgs
		;

		// IE is a pain here! we can't read the location if its an
		// external one so we have to skip and let the detached window
		// show the original url the iframe was created with.
		if (isIE && !_iframeDocExternal) {
			currUrl = window.frames[_iframeDisplayIdName].document.location.href;
		}

		var win = _createDetached(currUrl, args);

		if (_iframeDocExternal || _iFrameDoc(_iframeDisplay).forms.length == 0) {
			_closeIFrames();
		} else {
			_amWaitingDetachedLoad = true;
		}
	};

	/*
	* Load the given url into the content iFrame and display it.
	* FIXME: Needs more documentation
	*/
	function _displayPage(page, conf) {
		//
		// Figure out if the provided page is an external url so we
		// don't try to access the document anywhere
		//
		if (page.substr(0,4) == 'http') {
			var locPrefix = window.document.location.protocol+'//'+window.document.location.hostname;
			if (page.substr(0,locPrefix.length) == locPrefix) {
				_iframeDocExternal = false;
			} else {
				_iframeDocExternal = true;
			}
		} else {
			_iframeDocExternal = false;
		}

		if (YL.isObject(conf)) {
			if (conf.width) {
				_ifReqConfWidth = conf.width;
			} else {
				_ifReqConfWidth = null;
			}
			if (conf.height) {
				_ifReqConfHeight = conf.height;
			} else {
				_ifReqConfHeight = null;
			}
			if (conf.popargs) {
				_ifReqConfPopArgs = conf.popargs;
			} else {
				_ifReqConfPopArgs = [_defaultWinName];
			}
		} else {
			_ifReqConfWidth = null;
			_ifReqConfHeight = null;
			_ifReqConfPopArgs = [_defaultWinName];
		}

		if (YL.isArray(_ifReqConfPopArgs) && _ifReqConfPopArgs[0]) {
			_removeDetached(_ifReqConfPopArgs[0]);
		}

		YUD.setStyle(_iframeDisplay, 'height', '0px');
		_iframeDisplay.src = page;
		
		YUE.addListener(_iframeDisplay, "load", function() {
			YUE.removeListener(_iframeDisplay, "load", arguments.callee);
			_showIFrames();
		});
	};


	/********************************** DOCUMENT REFRESH FUNCTIONS **********************************/
	
	/*
	* For delayed refreshes when we have an active display iframe. This
	* function will set the necessary data to perform the refresh when we
	* are free of that iframe
	*/
	function _setRefreshOrders(cascading, excludeSelf) {
		if (!cascading && excludeSelf) { console.log("What do you want me to refresh!"); }
		_refreshOrders = {
			active: true,
			cascade: cascading,
			excludeMe: excludeSelf
		};
	};

	/*
	* Refresh away if we need to. Called when the display iframe is closed
	* or detached.
	*/	
	function _applyRefreshOrders() {
		if (_refreshOrders.active) {
			_refreshDisplay(_refreshOrders.cascade , _refreshOrders.excludeMe);

		}
	};

	/*
	* Base refresh function called through our public interface. Will figure
	* out how to deal with the refresh request, refresh where it can and apply
	* refresh orders where it can't.
	*/
	function _refreshDisplay(cascading, excludeSelf) {
		if (cascading) {
			if (YL.isValue(opener)) {
				if (YL.isValue(opener.VacancySys)) {
					opener.VacancySys.refreshDisplay(cascading, false);
				} else if (YL.isValue(opener.refreshOpener)) {
					opener.refreshOpener(cascading, false)
				} else {
					opener.location.reload(true);
				}
			} else if (parent != self) {
				if (YL.isValue(parent.VacancySys)) {
					parent.VacancySys.refreshDisplay(cascading, false);
				} else {
					parent.location.reload(true);
				}
			}
		}

		if (YUD.getStyle(_iframeDisplay, 'visibility') == 'visible') {
			_setRefreshOrders(cascading, excludeSelf);
		} else {
			// REFRESH SELF
			if (!excludeSelf) {_reloadLocation(true);}
		}
	};


	/************************************* MODIFIED YUI FUNCTIONS ***********************************/

	/*
	* Originals in yui dom package - modified as they will not accept document as an argument.
	* FIXME The reason they don't seems to bes safari and opera quirks so this code might not work
	* on those browsers. Might be able to get away with it. After further investigation and a final
	* solution is discovered/available, move to ixa libs
	*/

	function _getDocumentWidth(docRef) {
		var scrollWidth = (docRef.compatMode != 'CSS1Compat') ? docRef.body.scrollWidth : docRef.documentElement.scrollWidth;
		var w = Math.max(scrollWidth, _getViewportWidth(docRef));

		return w;
	};

	function _getViewportWidth(docRef) {
		var w = self.innerWidth;  // Safari
		var mode = docRef.compatMode;
		if (mode || isIE) { // IE, Gecko, Opera
			w = (mode == 'CSS1Compat') ?
				docRef.documentElement.clientWidth : // Standards
				docRef.body.clientWidth; // Quirks
		}

		return w;
        };

	function _getDocumentHeight(docRef) {
		var scrollHeight = (docRef.compatMode != 'CSS1Compat') ? docRef.body.scrollHeight : docRef.documentElement.scrollHeight;
		var h = Math.max(scrollHeight, _getViewportHeight(docRef));
		return h;
	};

	function _getViewportHeight(docRef) {
		var h = self.innerHeight; // Safari, Opera
		var mode = docRef.compatMode;
		if ((mode || isIE)) { // IE, Gecko
			h = (mode == 'CSS1Compat') ?
				docRef.documentElement.clientHeight : // Standards
				docRef.body.clientHeight; // Quirks
		}
		return h;
	};


	/********************************** DETACHED CONTENT MANAGEMENT *********************************/

	function _createDetached(url, args) {
		var
			win = null,
			hasName = (YL.isArray(args) && args.length),
			hasConf = hasName && YL.isValue(args[1])
		;
	
		if (hasName && !hasConf) {
			newConf = 'scrollbars=yes,resizable=yes,statusbar=0';
			if (_ifReqConfWidth && _ifReqConfWidth > 0) {
				newConf = 'width='+_ifReqConfWidth+','+newConf;
			} else {
				newConf = 'width='+YUD.getStyle(_iframeDisplay, 'width')+','+newConf;
			}
			if (_ifReqConfHeight && _ifReqConfHeight > 0) {
				newConf = 'height='+_ifReqConfHeight+','+newConf;
			} else {
				newConf = 'height='+YUD.getStyle(_iframeDisplay, 'height')+','+newConf;
			}

			args[1] = newConf;
		}
		if (hasName) {
			var wName = args[0];
			win = window.open(url, wName, args[1]);
			_trackDetached(win, wName);
		} else {
			win = window.open(url);
		}
		win.focus();

		_applyRefreshOrders();

		return win;
	};

	function _detachedLoaded(doc) {
		_amWaitingDetachedLoad = false;
		_transferIframeFormsToDetached(doc);
		_closeIFrames();
	};

	function _removeDetached(winName) {
		if (_isTracked(winName)) {
			window.open('', winName).close();
			_endTrackDetached(winName);
		}
	};

	function _trackDetached(win, winName) {
		for(var i=0;i<_detached_Popups.length;i++) {
			if (_detached_Popups[i] == winName) { return; }
		}

		_detached_Popups.push(winName);
	};

	function _endTrackDetached(winName) {
		for(var i=0;i<_detached_Popups.length;i++) {
			if (_detached_Popups[i] == winName) {
				_detached_Popups.splice(i,1);
			}
		}
	};

	function _isTracked(winName) {
		for(var i=0;i<_detached_Popups.length;i++) {
			if (_detached_Popups[i] == winName) {
				return true;
			}
		}
		return false;
	};


	/********************************* FORM COPY ON DETACH FUNCTIONS ********************************/

	/*
	* Copy form fields when detaching iFrames. Prevents loss of data already
	* entered by the user.
	*
	* @name getFieldContent
	* @memberOf VacancySys
	* @function
	* @param {HTMLDocument} doc The document object in the popup where the form
	* data will be copied to.
	* @return {Boolean} Indicates whether any copying took place.
	*/
	function _transferIframeFormsToDetached(doc) {
		var
			docToRead = _iFrameDoc(_iframeDisplay),
			docToWrite = doc,
			readFormData = [],
			writeFormData = []
		;

		// Do we have something to do?
		if (docToRead.forms.length == 0) { return false; }
		// Is the document we're transferring to the same as the original?
		if(docToRead.forms.length != docToWrite.forms.length) {
			console.log('document form numbers mismatch');
			return false;
		}

		// Get the element refs for all forms
		for (var i=0;i<docToRead.forms.length;i++) {
			readFormData[i] = IX.Form.getFormFields(docToRead.forms[i]);
			writeFormData[i] = IX.Form.getFormFields(docToWrite.forms[i]);
			// Any difference in the forms?
			if(readFormData[i].length != writeFormData[i].length) {
				console.log('form '+i+'elements mismatch');
				return false;
			}
		}

		// Copy across attributes of all forms' all elements
		for (var i=0;i<readFormData.length;i++) {
			for (var j=0;j<readFormData[i].length;j++) {
				IX.Form.setFieldContent(writeFormData[i][j], IX.Form.getFieldContent(readFormData[i][j]));
			}
		}

		return true;
	};


	/*************************************** PUBLIC FUNCTIONS ***************************************/
	
	return {
		/*
		* IFrame management public functions.
		*/
		
		controllerOnLoad: function() {
			_initControlIframe();
		},

		closeIframe: function() {
			_closeIFrames();
		},

		detachIframe: function() {
			_detachContent();
		},

		detachedLoaded: function(doc) {
			if (_amWaitingDetachedLoad) {
				_detachedLoaded(doc);
			}
		},

		getLayer: function() {
			return _ifLayer;
		},

		displayPage: function(page, conf) {
			if (YL.isValue(page)) {
				_displayPage(page, conf);
			}
		},

		disableControlFrame: function(disable) {
			_disableControlFrame(disable);
		},

		reloadLocation: function(forceGet) {
			_reloadLocation(forceGet);
		},

		endTrackDetached: function(winName) {
			_endTrackDetached(winName);
		},

		refreshDisplay: function(cascading,excludeSelf) {
			_refreshDisplay(cascading,excludeSelf);
		}
	};
}();