/*
 * Copyright (c) 2007, Harley Davidson Inc. All rights reserved.
 */
/**
 * The HD.ui.Dialog object literal provides access to a common set of functions and
 * variables for Harley Davidson websites.
 *
 * @class HD.ui.Dialog
 * @static
 * @namespace HD.ui
 *
 */
HD.ui.Dialog = {
	/**
	 * This field is a constant containing all the different types a dialog can be.
	 * @property {Object} TYPES
	 */
	TYPES : {
		STANDARD : "standard",
		ERROR : "error",
		CONFIRM : "confirm",
		ALERT : "alert"
	},

	/**
	 * This field tracks the current type of the dialog.  Defaults to standard
	 * @property {String} type
	 */
	dialogType : "standard",

	/**
	 * The configurable options that may be set externally:
	 * - {String} wrapperElId   :  the id that will be set on the wrapper for the dialog
	 * - {String} contentElId   :  the id that will be set on the wrapper for the dialog's content
	 * - {String} headerElId    :  the id that will be set on the wrapper for the dialog's header
	 * - {int}    defaultWidth  :  the default width
	 * - {int}    defaultHeight :  the default height
	 * @property {Object} options
	 */
	options : {
		wrapperElId : "hdDialogWrapper",
		contentElId : "hdDialogContent",
		headerElId : "hdDialogHeader",
		defaultWidth : 670,
		defaultHeight : 425
	},

	/**
	 * The default failure text
	 * @property {String} failureText
	 */
	failureText : HD.content.Text.defaultErrorMessage,
	
	/**
	 * Handle to the YAHOO dialog object
	 * @property {YAHOO.widget.Dialog} hdDialog
	 * @private
	 */
	hdDialog : null,

	/**
	 * Handle to the dom wrapper for the dialog
	 * @property {HTMLElement} wrapperEl
	 */
	wrapperEl : null,
	

	/**
	 * Handle to the dom wrapper for the dialog's content
	 * @property {HTMLElement} contentEl
	 */
	contentEl : null,
	

	/**
	 * Handle to the dom wrapper for the dialog's header
	 * @property {HTMLElement} headerEl
	 */
	headerEl : null,
	

	/**
	 * The current initialization state of the dialog
	 * @property {boolean} isInit
	 * @private
	 */
	isInit : false,
	
	/**
	 * When a server call is made the variables for height, width, and title need to 
	 * be cached until we actually display the dialog.  This variable is used to cache
	 * that data, until display.
	 * @property {Object} callbackCache
	 * @private
	 */
	callbackCache : {
		height : null,
		width  : null,
		title  : null
	},


	/**
	 * Property indicates if a dialog is currently showing.
	 * @property {boolean} isShown 
	 */
	isShown : false,

	/**
	 * Events that are fired within the dialog:
	 * - beforeInit
	 * - afterInit
	 * - beforeHide
	 * - afterHide
	 * - beforeShow
	 * - afterHide
	 * - beforeConfirm
	 * - beforeNotice
	 * - beforeError
	 * @property {Object} events
	 */
	events : {
		beforeInit    : {name:"beforeInit"   ,event:null},
		afterInit     : {name:"afterInit"    ,event:null},
		beforeHide    : {name:"beforeHide"   ,event:null},
		afterHide     : {name:"afterHide"    ,event:null},
		beforeShow    : {name:"beforeShow"   ,event:null},
		afterShow     : {name:"afterShow"    ,event:null},
		beforeConfirm : {name:"beforeConfirm",event:null},
		beforeNotice  : {name:"beforeNotice" ,event:null},
		beforeError   : {name:"beforeError"  ,event:null}
	},

	/**
	 * CustomEvent declarations.
	 * @method initEvents
	 */
	initEvents : function() {
		// loop through the events and initialize them
		for (var i in this.events) {
			this.events[i].event = new YAHOO.util.CustomEvent(this.events[i].name,this);
		}
	},


	/**
	 * This function will initialize the dialog elements
	 * @method init
	 */
	init : function() {
		// event before
		this.events.beforeInit.event.fire();

		// create and append the doalog content wrapper
		this.wrapperEl = document.createElement("div");
		this.wrapperEl.id = this.options.wrapperElId;
		this.wrapperEl.style.height = "auto";
		this.wrapperEl.style.width = "auto";
		document.body.appendChild(this.wrapperEl);

		// create and append the header in the format that yahoo expects
		this.headerEl = document.createElement("div");
		this.headerEl.id = this.options.headerElId;
		// classname yahoo uses to identify the header
		this.headerEl.className = "hd";
		this.wrapperEl.appendChild(this.headerEl);

		// create and append the body in the format yahoo expects
		this.contentEl = document.createElement("div");
		// classname yahoo uses to identify the body
		this.contentEl.className = "bd";
		this.contentEl.id = this.options.contentElId;		
		this.wrapperEl.appendChild(this.contentEl);

		// set the config for yahoo
		var config = { 
		  fixedcenter : false, // handle manually
		  visible : false,
		  modal : true, 
		  close : true, 
		  constraintoviewport : true
		};

		// initialize the yahoo dialog
		this.hdDialog = new YAHOO.widget.Dialog(this.options.wrapperElId,config);
		// make sure that the dialog is on top
		this.hdDialog.element.style.zIndex = 999999999;

		// because some of the popups use iframes the content is not hiding
		// when hide is called.  use the before hide event to remove all 
		// the content from the inner html to ensure it really hides.
		// this method is only really necessary when YAHOO lib internally 
		// calls the close method (ex. the yahoo created x icon is clicked 
		// within the popup) as we compensate for this in HD.ui.Dialog.hide().
		this.hdDialog.beforeHideEvent.subscribe(function() {
			HD.ui.Dialog.events.beforeHide.event.fire();
			HD.ui.Dialog.contentEl.innerHTML = "";
			HD.ui.Dialog.isShown = false;
			HD.ui.Dialog.events.afterHide.event.fire();
		});

		// yup, we are initialized
		this.isInit = true;
		
		// event after
		this.events.afterInit.event.fire();
	},	


	/**
	 * This function will load a dialog box with the content returned from the provided URL
	 * @method load
	 * @param {String} 			url			The url that we will call to load the dialog
	 * @param {String} 			title		The title of the new dialog
	 * @param {String} 			formId		the id of the form that we submit to the url
	 * @param {Function Ref} 	callback	optional callback method if special processing is required
	 * @param {int} 			width		the new width of the dialog
	 * @param {int} 			height		the new height of the dialog
	 * @param {Object} 			tab			whether or not there is a tab in the dialog
	 */
	load : function(url,title,formId,cb,width,height) {
		var method = (formId) ? "POST" : "GET";

		// set the parameters used in the callback
		callback = cb || HD.ui.Dialog.loadCallback;
		this.callbackCache.width = width || this.options.defaultWidth;
		this.callbackCache.height = height || this.options.defaultHeight;
		this.callbackCache.title = title || "";

		// make request
		this.connectionRequest = YAHOO.util.Connect.asyncRequest(method,url,callback,null);
		return;
	},
	

	/**
	 * Singleton object that has the default callback if there was no callback provided
	 */
	loadCallback : {
		success :    function(o) { HD.ui.Dialog.show(HD.ui.Dialog.callbackCache.title,HD.ui.Dialog.callbackCache.width,HD.ui.Dialog.callbackCache.height,o.responseText);},
		failure :    function() { HD.ui.Dialog.showError(); },
		argument :   null
	},
	
	/**
	 * This function will show the dialog box with the passed in parameters
	 * @method show
	 * @param {String|DomEl}            title    the title of the dialog
	 * @param {int}                     width    the width we want the dialog to be
	 * @param {int}                     height   the height we want the dialog to be
	 * @param {String|DomEl}            content  the content that we want shown
	 * @param {from HD.ui.Dialog.TYPES} type     (optional) the type of the dialog
	 * @param {object}                  offsets  (optional) offsets for the dialog (left/top take priority)
	 *                                           may also include an element property to position in relation to (element object or id)
	 *                                           { top: 10, left: 10, right: 10, bottom: 10, element: $('someElement') }
	 * @param {boolean}                 modal    (optional) whether or not to display the dialog as modal (default true)
	 * @param {String}                  overflowx (optional) whether or not the horizontal scrollbar should be "hidden" on the content (default "auto")
	 */
	show : function (title,width,height,content,type,offsets,modal,overflowx) {
		if (!content) {return;}

		if (this.isInit) {HD.ui.Dialog.hide();}

		// event before
		this.events.beforeShow.event.fire();

		// init
		if (!this.isInit) {this.init();}		
		
		// set the width and height to defaults if they are not passed in
		width = (width) ? this.convertWidthHeight(width) : this.convertWidthHeight(this.options.defaultWidth) ;
		height = (height) ? this.convertWidthHeight(height) : this.convertWidthHeight(this.options.defaultHeight) ;
		title = title || this.callbackTitle;

		// get the dialog's content element that is currently in the dom, as the one 
		// we have cached may not be the element that yahoo has inserted.
		this.contentEl = $(this.options.contentElId);
		this.contentEl.innerHTML = "";
		this.contentEl.style.width = HD.ui.Dialog.convertWidthHeight(width);
		this.contentEl.style.height = HD.ui.Dialog.convertWidthHeight(height);
	
		// render before setting content, as sometimes the yahoo dialog renderer 
		// does not update with the new content we have set in the html.  seems
		// to use some sort of caching from previous dialogs.
		this.hdDialog.render();

		// draw the dialog off the screen so that it can be centered
		this.hdDialog.element.marginTop = '-10000px';
		
		// set the content of the header		
		this.headerEl.innerHTML = unescape(title);

		// retrieve the newly rendered contentEl
		this.contentEl = $(this.options.contentElId);
		// keep it hidden while we modify it.  toggling the display type will
		// also force ie6 to redraw the panel and put in scrollbars if needed.
		this.contentEl.style.display = "none";
		
		if(content instanceof YAHOO.widget.TabView) {
			content.appendTo(this.contentEl);
		// based on the content type set the html
		} else if (typeof content == "string") {
			this.contentEl.innerHTML = content;
		} else {
			this.contentEl.appendChild(content);
		}
		
		// toggle the display on
		this.contentEl.style.display = "block";
		
		// set the stype based on the type to the dialog if necessary
		var w = $('hdDialogWrapper');
		this.dialogType = type || this.TYPES.STANDARD;
		switch (this.dialogType) {
			case this.TYPES.ERROR :
				// add the error wrapper class
				YAHOO.util.Dom.addClass(w,'errorDialog');
				break;
			case this.TYPES.STANDARD :
			case this.TYPES.CONFIRM :
			case this.TYPES.ALERT :
				// remove error wrapper class
				YAHOO.util.Dom.removeClass(w,'errorDialog');
				break;
			default :
				// remove error wrapper class
				YAHOO.util.Dom.removeClass(w,'errorDialog');
				break;		
		}
		
		// Set the modal property of the dialog.
		modal = (modal == false) ? modal : true;
		this.hdDialog.cfg.setProperty('modal', modal);

		this.hdDialog.show();
		this.isShown = true;
		
		// set so that content will scroll 
		this.contentEl.style.height =  HD.ui.Dialog.convertWidthHeight(height);
		this.wrapperEl.style.width =  HD.ui.Dialog.convertWidthHeight(width);
		this.contentEl.style.overflowx = overflowx || "auto";
		this.contentEl.style.overflowy = "auto";
		
		// center dialog
		if (!!offsets) {
			this.position(offsets);
		} else {
			this.center();
		}
		
		// event after
		this.events.afterShow.event.fire();
	},
	
	/**
	 * Hides the currently showing dialog
	 * @method hide
	 */
	hide : function () {
		// event before
		this.events.beforeHide.event.fire();

		// clear the inner html of the content in case it has iframes
		// ... they don't seem to hide fully with yahoo hide alone.
		this.contentEl.innerHTML = "";
		this.hdDialog.hide();
		this.isShown = false;

		// event after
		this.events.afterHide.event.fire();

	},
	

	/**
	 * This function will show the dialog box with the passed in parameters
	 * @method show
	 * @param {String|DomEl}  title    the title of the dialog
	 * @param {int}           width    the width we want the dialog to be
	 * @param {int}           height   the height we want the dialog to be
	 * @param {String|DomEl}  content  the content that we want shown
	 */
	showLoading : function(text,width,height,classname) {
		// init
		if (!this.isInit) {this.init();}

		// if there is a dialog showing just show the loading message within it
		if (this.isShown) {
			// append the animation
			this.loading = new HD.ui.LoadingIndicator(this.options.contentElId,text);
			return;
		}

		// set the width and height to defaults if they are not passed in
		width = (width) ? this.convertWidthHeight(width) : this.convertWidthHeight(this.options.defaultWidth) ;
		height = (height) ? this.convertWidthHeight(height) : this.convertWidthHeight(this.options.defaultHeight) ;
		
		// get the dialog's content element that is currently in the dom, as the one 
		// we have cached may not be the element that yahoo has inserted.
		this.contentEl = $(this.options.contentElId);
		this.contentEl.innerHTML = "";
		this.contentEl.style.width = HD.ui.Dialog.convertWidthHeight(width);
		this.contentEl.style.height = HD.ui.Dialog.convertWidthHeight(height);
	
		// render before setting content, as sometimes the yahoo dialog renderer 
		// does not update with the new content we have set in the html.  seems
		// to use some sort of caching from previous dialogs.
		this.hdDialog.render();
		this.hdDialog.element.marginTop = '-10000px';
		this.hdDialog.show();
		
		this.isShown = true;

		// set the content of the header
		this.headerEl.innerHTML = unescape(text);

		// retrieve the newly rendered contentEl
		this.contentEl = $(this.options.contentElId);
		this.contentEl.style.display = "block";
		
		// center dialog
		this.center();

		// init the loading indicator
		var l = new HD.ui.LoadingIndicator(this.contentEl,text,classname);
	},
	
	
	/**
	 * Function will show a dialog with the provided error message.
	 * @method showAlert
	 * @param {String} alertText   the text to show in the alert
	 * @param {int}    width       the width of the desired popup
	 * @param {int}    height      the height of the desired popup
	 */
	showError : function(alertText,width,height) {
		// event before
		this.events.beforeError.event.fire();

		// set the vars to defaults if they do not exist		
		alertText = alertText || this.failureText;
		width = width || 400;
		height = height || 200;
		
		// set the errror content
		var h = HD.util.template(HD.content.HTML.errorDialog, { _ERROR_TEXT: alertText });
		
		// load the popup with the error message
		HD.ui.Dialog.show('Error',width,height,h,this.TYPES.ERROR);
	},
	
	/**
	 * This function will call a generic confirm popup
	 * @method showConfirm
	 * @param {Object} confirmText       the text to show in the popup
	 * @param {Object} confirmCallback   the function that we will be running on successful confirm
	 * @param {Object} width             the width of the confirm popup
	 * @param {Object} height            the height of the confirm popup
	 */
	showConfirm : function(confirmTitle,confirmText,confirmCallback,width,height) {
		// event before
		this.events.beforeConfirm.event.fire();

		// set the vars to defaults if they do not exist		
		if (!confirmText) {return;}
		confirmTitle = confirmTitle || '';
		width = width || 400;
		height = height || 300;
		this.confirmCallback = confirmCallback || function(){HD.ui.Dialog.hide();};
		
		// set the var
		// set the errror content
		var h = HD.util.template(HD.content.HTML.confirmDialog, { _CONFIRM_TEXT: confirmText });
		
		// load the popup with the error message
		HD.ui.Dialog.show(confirmTitle,width,height,h);
	},
	
	confirmCallback : null,
	
	/**
	 * This function will call a generic notice popup
	 * @method showNotice
	 * @param {Object} noticeText       the text to show in the popup
	 * @param {Object} noticeCallback   the function that we will be running on successful notice
	 * @param {Object} width            the width of the notice popup
	 * @param {Object} height           the height of the notice popup
	 */
	showNotice : function(noticeTitle,noticeText,noticeCallback,width,height) {
		// event before
		this.events.beforeNotice.event.fire();

		// set the vars to defaults if they do not exist		
		if (!noticeText) {return;}
		noticeTitle = noticeTitle || '';
		width = width || 400;
		height = height || 300;
		this.noticeCallback = noticeCallback || function(){HD.ui.Dialog.hide();};
		
		// set the var
		// set the errror content
		var h = HD.util.template(HD.content.HTML.noticeDialog, { _NOTICE_TEXT: noticeText });
		
		// load the popup with the error message
		HD.ui.Dialog.show(noticeTitle,width,height,h);
	},

	/**
	 * This function is what is run on successful confirmation from the Notice Dialog
	 * @property {Object|function} noticeCallback
	 * @private
	 */	
	noticeCallback : null,

	/**
	 * Function used to add "px" to the end of a width height attribute ifthe passed in 
	 * element is a number
	 * @param {int|String} n  the item we want to add a "px" to for style processing
	 * @private
	 */
	convertWidthHeight : function(n){
		if(!isNaN(n)){n += "px";}
		return n;
	},
	
	/**
	 * The function will centere the dialog
	 * @param {int|String} width   (optional) the width of the dialog
	 * @param {int|String} height  (optional) the height of the dialog
	 */
	center : function(width, height) {
		// grab element
		var el = this.hdDialog.element;
		el.style.top = HD.util.getPageScrollTop() + Math.round(HD.util.getPageHeight() / 2) + "px";
		el.style.left = HD.util.getPageScrollLeft() + Math.round(HD.util.getPageWidth() / 2) + "px";
		el.style.bottom = '';
		el.style.right = '';
		el.style.marginTop = '-' + Math.floor((height || el.offsetHeight) / 2) + 'px';
		el.style.marginLeft = '-' + Math.floor((width || el.offsetWidth)  / 2) + 'px';
	},
	
	/**
	 * The function will position the dialog
	 * @param {object} offsets  offsets for the dialog (left/top take priority)
	 *                          { top: 10, left: 10, right: 10, bottom: 10 }
	 */
	position : function(offsets) {
		// grab element
		var el = this.hdDialog.element;
		var posEl = typeof offsets.element != 'undefined' ? $(offsets.element) : null;
		if (!!posEl) {
			var pos = YAHOO.util.Dom.getXY(posEl);
			posEl = {
				x1: pos[0],
				y1: pos[1],
				x2: pos[0] + posEl.offsetWidth,
				y2: pos[1] + posEl.offsetHeight
			};
		}
		el.style.top = '';
		el.style.bottom = '';
		el.style.left = '';
		el.style.right = '';
		if (typeof offsets.top != 'undefined') {
			if (!!posEl) {
				el.style.top = (posEl.y1 - offsets.top - el.offsetHeight) + 'px';
			} else {
				el.style.top = offsets.top + 'px';
			}
		} else if (typeof offsets.bottom != 'undefined') {
			if (!!posEl) {
				el.style.top = (posEl.y2 + offsets.bottom) + 'px';
			} else {
				el.style.bottom = offsets.bottom + 'px';
			}
		} else {
			if (!!posEl) {
				el.style.top = posEl.y1 + 'px';
			} else {
				el.style.top = '0px';
			}
		}
		if (typeof offsets.left != 'undefined') {
			if (!!posEl) {
				el.style.left = (posEl.x1 - offsets.left - el.offsetWidth) + 'px';
			} else {
				el.style.left = offsets.left + 'px';
			}
		} else if (typeof offsets.right != 'undefined') {
			if (!!posEl) {
				el.style.left = (posEl.x2 + offsets.right) + 'px';
			} else {
				el.style.right = offsets.right + 'px';
			}
		} else {
			if (!!posEl) {
				el.style.left = posEl.x1 + 'px';
			} else {
				el.style.left = '0px';
			}
		}
		el.style.marginTop = '0px';
		el.style.marginBottom = '0px';
		el.style.marginLeft = '0px';
		el.style.marginRight = '0px';
	},
	
	resize : function(width, height) {
		this.center(width, height);
		width = HD.ui.Dialog.convertWidthHeight(width);
		height = HD.ui.Dialog.convertWidthHeight(width);
		this.contentEl.style.width = width;
		this.contentEl.style.height = height;
		this.wrapperEl.style.width =  width;
	}
};

// call the initialization of events for the dialog
HD.ui.Dialog.initEvents();

