/*
	dialog.class.js
	v1.0 - 27/jun/2006 - Bob Kersten - Initial version.
	(C) Copyright 2006 Fellownet.
	All rights reserved.

	Usefull urls:
	- http://www.sergiopereira.com/articles/prototype.js.html
	- http://wiki.script.aculo.us/scriptaculous/show/CoreEffects
	- http://blogs.ebusiness-apps.com/jordan/pages/Prototype%20Library%20Info.htm
	- http://www.gotapi.com/index.html
*/

var dialog = Class.create();

var __dialogs = new Array();
var __zIndex = 100;

dialog.prototype = {
	initialize: function(options_) {
		/**
		 * This is the constructor function for the dialog class. It accepts exactly one parameter which override the
		 * set of default parameters.
		 */
		this.options = {
			width:300,
			title:false,
			content:'Content',
			buttons:'Close',
			background:'#CACDCE',
			duration:0.3,
			effect:'blind',
			position_fixed:! navigator.appName.match(/Microsoft/i), /* this can be done a lot nicer :-) */
			source:'',
			method:'get',
			parameters:'',
			modal:false,
			shadow:1,
			shadowcolor:'#B8B8B8',
			callback:function(button_, dialog_) { dialog_.close(); }
		}
		Object.extend(this.options, options_ || {} );

		var self = this;

		// Let's create a new unique id for the current dialog. This is necessary to identify it's various dom objects.
		if (__dialogs.length > 0) {
			if (this.options['modal']) {
				// This dialog is modal which means it can't be opened unless it's the only one visible at this moment.
				__dialogs.invoke('flash');
				this.show = this.hide = this.enable = this.disable = function() { /* disable these dialog functions */ }
				return;
			} else this.id = __dialogs.last().id + 10;
		} else this.id = 100;

		// Let's create the DOM objects that are used to create a visible appearance for the dialog. These objects are
		// created in memory at first and added to the DOM tree later on.
		this.div_container = document.createElement('div');
		this.div_container.setAttribute('id', 'div_container_' + this.id);
		this.div_container.className = 'dialogContainer';
		this.div_container.style.visibility = 'hidden';

		this.div_shadow = document.createElement('div');
		this.div_shadow.className = 'dialogShadow';
		this.div_container.appendChild(this.div_shadow);

		this.div_outer = document.createElement('div');
		this.div_outer.className = 'dialogOuter';
		this.div_outer.style.backgroundColor = this.options['background'];
		this.div_container.appendChild(this.div_outer);

		this.div_inner = document.createElement('div');
		this.div_inner.className = 'dialogInner';
		this.div_outer.appendChild(this.div_inner);

		// The following i-frames are necessary to prevent windowed controls from appearing over the dialog in crappy
		// internet explorer browsers.
		this.iframe_content = document.createElement('iframe');
		this.iframe_content.className = 'iframebug';
		this.iframe_content.style.position = 'absolute';
		this.div_container.insertBefore(this.iframe_content, this.div_outer);

		this.iframe_shadow = document.createElement('iframe');
		this.iframe_shadow.className = 'iframebug';
		this.iframe_shadow.style.position = 'absolute';
		this.div_container.insertBefore(this.iframe_shadow, this.div_outer);

		// If there's a title present for the current dialog box we're going to create a DOM element for it below.
		if (this.options['title']) {
			this.div_header = document.createElement('div');
			this.div_header.className = 'dialogHeader';
			this.div_header.innerHTML = this.options['title'];
			this.div_inner.appendChild(this.div_header);
		}

		this.div_content = document.createElement('div');
		this.div_content.className = 'content';
		this.div_inner.appendChild(this.div_content);

		// If there are buttons defined for the current dialog we're going to create a buttonbar for it and add the
		// buttons to the buttonbar.
		if (! /^\s*$/i.test(this.options['buttons'])) {
			this.div_buttons = document.createElement('div');
			this.div_buttons.className = 'dialogButtons';

			this.buttons = new Array();
			var buttondefs = this.options['buttons'].split(/\s*[,|]\s*/i);
			buttondefs.each( function(buttondef_) {
				var input_button = document.createElement('input');
				input_button.setAttribute('type', 'button');
				input_button.setAttribute('value', buttondef_);
				input_button.onclick = function() {
					self.options.callback(buttondef_, self);
				};
				if (self.buttons.length == 0) Element.addClassName(input_button, 'right');
				self.div_buttons.appendChild(input_button);
				self.buttons.push(input_button);
			} );
			this.div_inner.appendChild(this.div_buttons);
		}

		// Finally let's add this new dialog to the end of the list of known dialogs.
		 __dialogs.push(this);
	},

	flash: function() {
		// This function flashes the outermost border for half a second to get the attention of the user.
		this.div_outer.style.border = '1px solid #FF0000';
		var self = this;
		setTimeout(function() {
			self.div_outer.style.border = '1px solid #6E7172';
		}, 500);
	},

	hide: function(onComplete_) {
		/**
		 * This function hides the current dialog. It does not remove it's elements from the DOM tree, use the
		 * destroy function for that.
		 */
		if (Element.visible('div_container_' + this.id)) {
			this.__toggle(onComplete_);
		}
	},

	show: function(onComplete_) {
		/**
		 * This function results in a visible dialog box. If the content of the dialog box has been laoded before
		 * we can immediately show the dialog box, otherwise lot's of extra work is necessary.
		 */
		if (/^\s*$/i.test(this.div_content.innerHTML)) {
			// There doesn't appear to be any content loaded into the current dialog. Let's load the content and
			// set the boundaries of the dialog before showing it.
			var self = this;
			var setBoundaries = function() {
				// First we're going to set the dimensions of the dialog. These are dependend on the options set
				// for the dialog.
				self.options['width'] += (22 + self.options['shadow']);
				self.div_container.style.width = self.options['width'] + 'px';
				self.div_outer.style.width = (self.options['width'] - (self.options['shadow'] + 2)) + 'px';
				self.div_inner.style.width = (self.options['width'] - (self.options['shadow'] + 22)) + 'px';
				self.div_content.style.width = (self.options['width'] - (self.options['shadow'] + 22)) + 'px';
				if (self.div_buttons) self.div_buttons.style.width = (self.options['width'] - (self.options['shadow'] + 22)) + 'px';
				if (self.div_header) self.div_header.style.width = (self.options['width'] - (self.options['shadow'] + 32)) + 'px';

				self.options['height'] = Element.getHeight(self.div_container) + self.options['shadow'];
				self.div_container.style.height = self.options['height'] + 'px';
				self.div_shadow.style.width = (self.options['width'] - self.options['shadow']) + 'px';
				self.div_shadow.style.height = (self.options['height'] - self.options['shadow']) + 'px';
				self.div_shadow.style.left = self.div_shadow.style.top = self.options['shadow'] + 'px';
				self.div_shadow.style.backgroundColor = self.options['shadowcolor'];
		
				self.iframe_content.style.width = (self.options['width'] - self.options['shadow']) + 'px';
				self.iframe_content.style.height = (self.options['height'] - self.options['shadow']) + 'px';
				self.iframe_content.style.left = self.iframe_content.style.top = '0px';
		
				self.iframe_shadow.style.width = (self.options['width'] - self.options['shadow']) + 'px';
				self.iframe_shadow.style.height = (self.options['height'] - self.options['shadow']) + 'px';
				self.iframe_shadow.style.left = self.iframe_shadow.style.top = self.options['shadow'] + 'px';

				// Then we're going to create the mechanisms to keep the dialog in the center of the screen. These
				// mechanisms are different for browsers that support fixed positioning.
				if (self.options['position_fixed']) {
					self.div_container.style.left = '50%';
					self.div_container.style.top = '48%';
					self.div_container.style.position = 'fixed';
					self.div_container.style.marginLeft = '-' + Math.ceil(self.options['width'] / 2) + 'px';
					self.div_container.style.marginTop = '-' + Math.ceil(self.options['height'] / 2) + 'px';
				} else {
					var onScroll = window.onscroll;
					window.onscroll = function() {
						if (self && self.center) self.center();
						if (onScroll) onScroll();
					}
					var onResize = window.onresize;
					window.onresize = function() {
						if (self && self.center) self.center();
						if (onResize) onResize();
					}
					self.center();
				}

				// Finally we're going to set the visibilities of the dialog and make it visible by calling the toggle
				// function.
				self.div_container.style.display = 'none';
				self.div_container.style.visibility = 'visible';
				self.__toggle(onComplete_);
			}

			document.getElementsByTagName('body').item(0).appendChild(this.div_container);

			if (! /^\s*$/i.test(this.options['source'])) {
				new Ajax.Updater( {
					success:this.div_content
				}, this.options['source'], {
					method:this.options['method'],
					parameters:this.options['parameters'],
					onComplete:setBoundaries
				} );
			} else {
				this.div_content.innerHTML = this.options['content'];
				setBoundaries();
			}
		} else {
			// The dialog content has already been laoded so we don't need to create the dialog again. Let's just
			// show the dialog.
			if (! Element.visible('div_container_' + this.id)) {
				this.__toggle(onComplete_);
			}
		}
	},

	close: function(onComplete_) {
		/**
		 * This function can be used to close and destroy a dialog at once.
		 */
		this.disable();
		var self = this;
		this.hide( function() {
			self.destroy();
			if (onComplete_) onComplete_();
		} );
	},

	destroy: function() {
		__dialogs = __dialogs.without(this);
		document.getElementsByTagName('body').item(0).removeChild(this.div_container);
		this.show = this.hide = this.enable = this.disable = function() { /* disable these dialog functions */ }
		this.center = undefined;
	},

	refresh: function(onComplete_) {
		/**
		 * This function reloads the content of the dialog by fetching new content from the server side script.
		 * This is only necessary of the source of the dialog has been set.
		 */
		if (! /^\s*$/i.test(this.options['source'])) {
			new Ajax.Updater( {
				success:this.div_content
			}, this.options['source'], {
				method:this.options['method'],
				parameters:this.options['parameters'],
				onComplete:function() {
					if (onComplete_) onComplete_();
				}
			} );
		}
	},

	disable: function() { this.enable(false); },
	enable: function(enable_) {
		/**
		 * This function iterates through all the buttons on the dialog and disables or enables them all, which makes the
		 * dialog appear to be disabled or enabled at whole.
		 */
		if (enable_ == undefined) enable_ = true;
		if (enable_) {
			if (this.div_overlay) this.div_overlay.style.display = 'none';
		} else {
			if (! this.div_overlay) {
				this.div_overlay = document.createElement('iframe');
				this.div_overlay.className = 'dialogOverlay';
				this.div_overlay.style.width = this.div_container.style.width;
				this.div_overlay.style.height = this.div_container.style.height;
				this.div_overlay.style.zIndex = __zIndex++;
				this.div_container.appendChild(this.div_overlay);
			} else this.div_overlay.style.display = 'block';
		}


		this.buttons.each( function(button_) {
			button_.style.backgroundColor = (enable_ ? '#FFF' : '#EEE');
			button_.disabled = (! enable_);
		} );
	},

	center: function() {
		/**
		 * This function moves the current dialog to the center of the screen. It can be usefull to have the
		 * dialog stay in the center when the window or scrollposition changes.
		 */
		var my_width  = 0;
		var my_height = 0;
		if (typeof(window.innerWidth) == 'number') {
			my_width = window.innerWidth;
			my_height = window.innerHeight;
		} else if (document.documentElement && (document.documentElement.clientWidth || document.documentElement.clientHeight)) {
			my_width  = document.documentElement.clientWidth;
			my_height = document.documentElement.clientHeight;
		} else if (document.body && (document.body.clientWidth || document.body.clientHeight)) {
			my_width  = document.body.clientWidth;
			my_height = document.body.clientHeight;
		}
//	    this.div_container.style.position = 'absolute';
//	    this.div_container.style.display  = 'block';
//	    this.div_container.style.zIndex   = 99;

	    var scrollY = 0;
		if (document.documentElement && document.documentElement.scrollTop) {
			scrollY = document.documentElement.scrollTop;
		} else if (document.body && document.body.scrollTop) {
			scrollY = document.body.scrollTop;
		} else if (window.pageYOffset) {
			scrollY = window.pageYOffset;
		} else if (window.scrollY) {
			scrollY = window.scrollY;
		}
		var dimensions = Element.getDimensions(this.div_container);
		var setX = (my_width - dimensions.width) / 2;
		var setY = (my_height - dimensions.height) / 2 + scrollY;
		setX = (setX < 0) ? 0 : setX;
		setY = (setY < 0 ) ? 0 : setY;

		this.div_container.style.left = setX + "px";
		this.div_container.style.top  = setY + "px";
	},

	__moveToTop:function() {
		/**
		 * This private function is used to move a dialog to the top of the stack which makes it appear above all
		 * other elements on the page.
		 */
		this.div_container.style.zIndex = __zIndex++;
		this.div_shadow.style.zIndex = __zIndex++;
		this.iframe_shadow.style.zIndex = __zIndex++;
		this.iframe_content.style.zIndex = __zIndex++;
		this.div_outer.style.zIndex = __zIndex++;
	},

	__toggle:function(onComplete_) {
		/**
		 * This function shows or hides the actual dialog using either an effect or display it right away if the
		 * duration is set to zero.
		 */
		this.__moveToTop();
		if (parseFloat(this.options['duration']) > 0) {
			Effect.toggle('div_container_' + this.id, this.options['effect'], {
				duration:this.options['duration'],
				afterFinish:function() {
					if (onComplete_) onComplete_();
				}
			} );
		} else {
			Element.toggle('div_container_' + this.id);
			if (onComplete_) onComplete_();
		}
	}
}

dialog.open = function(options_) {
	/**
	 * This 'static' dialog class member can be used to quickly open a dialogbox without having to call the show member
	 * manually.
	 */
	var dlg = new dialog(options_);
	dlg.show();
}

dialog.onTop = function() {
	/**
	 * This function returns the dialog that is currently on top and visible. It can be used to quickly retrieve
	 * a reference to the topmost dialog.
	 */
	return __dialogs.last();
}
