/******************************************************************************
 * DIALOG Suite!
 *
 * The Dialog suite gives you the ability to display different types of
 * "message boxes" during site operations. Typically, these would be used to
 * notify the user of an ongoing AJAX operation (one that requires them to
 * wait), or to let them know that something succeeded or failed.
 *
 * There are two types of dialogs, the "Loading" dialog, which, just as it would
 * seem, displays a "loading" message. This message obscures the page so other
 * controls can't be clicked and forces the user to wait.
 *
 * The other type is the "Message" dialog, which gives the user a simple message
 * and allows them to clear it by clicking on the page or clears itself after
 * a preset delay.
 *
 * To create a loading object on your page, wait until the DOM is fully loaded.
 * Then, call the custom constructor class with a dialog type argument:
 *
 * var loading = Dialog.create(DIALOG_LOADING);
 *
 * or:
 *
 * var message = Dialog.create(DIALOG_MESSAGE);
 *
 * These type constants are defined for you.
 *
 * An optional second argument allows you to specify the internal identifier
 * of the dialog instance yourself:
 *
 * var loading = Dialog.create(DIALOG_MESSAGE, 'my_id');
 *
 * It is then possible to later snag that object by its ID:
 *
 * var another_reference = Loading.selectInstance('my_id');
 *
 * To actually USE the newly created loading instance, you will probably like
 * to set a message (it sets innerHTML, so HTML is allowed; remember to escape
 * your quotes!):
 *
 * loading.setMessage('My message goes here.');
 *
 * Then, show the object.
 *
 * loading.show();
 *
 * When your process is complete, hide it.
 *
 * loading.hide();
 *
 * The Message dialog type also has an additional method, setDelay():
 *
 * message = Dialog.create(DIALOG_MESSAGE);
 * message.setDelay(1500);
 * message.setMessage('The operation was a success.');
 * message.show();
 *
 * There are a couple of static functions within the Dialog class that can
 * be used also:
 *
 * a_dialog = Dialog.firstVisible();
 *
 * That does what it sounds like: it returns a reference to the first visible
 * dialog that has been created on the page. You probably don't want more than
 * one dialog showing at once because their overlays will stack up, so you
 * may use this to help manage it.
 *
 * You can find out if a single instance is visible by checking the 'visible'
 * property:
 *
 * loading = Dialog.create(DIALOG_LOADING);
 * loading.visible // <= false
 * loading.show();
 * loading.visible // <= true
 *
 * If you create multiple Dialog instances of any types, and you decide to
 * show a second one before you've hidden the first one, the class will
 * automagically hide the currently visible message, show the new message,
 * and transition the objects into their new states so that everything is
 * seamless. If you show a Message while a Loading is shown, it will also
 * apply the click event to the overlay so that the user can clear it, and
 * the new overlay will disappear along with the Message dialog when its
 * delay is reached.
 *
 *****************************************************************************/

/* Here are some handy CONSTANTS. */
var DIALOG_MESSAGE = 'dialog';
var DIALOG_LOADING = 'loading';

/* GLOBAL variables are going to be required for handling instances. */
var DialogArray = new Array();
var DialogIndex = new Array();
var DialogCurrentInstance = '';

var Dialog = {
	create: function() {
		var args = {};
		Object.extend(args, {
			type: arguments[0] || DIALOG_MESSAGE,
			id: arguments[1] || Dialog.generateId()
		});

		DialogCurrentInstance = args.id;

		switch(args.type) {
			case DIALOG_MESSAGE:
				var newInstance = new MessagePrivate(args.id);
				DialogArray[args.id] = newInstance;
				DialogIndex[DialogIndex.length] = args.id;
				break;

			case DIALOG_LOADING:
				var newInstance = new LoadingPrivate(args.id);
				DialogArray[args.id] = newInstance;
				DialogIndex[DialogIndex.length] = args.id;
				break;
		}

		//var newId = (id) ? id : Loading.generateId();
		return newInstance;
	},

	generateId: function() {
		var chars = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXTZabcdefghiklmnopqrstuvwxyz";
		var randomstring = '';
		for (var i=0; i<4; i++) {
			var rnum = Math.floor(Math.random() * chars.length);
			randomstring += chars.substring(rnum,rnum+1);
		}
		return randomstring;
	},

	selectInstance: function(id) {
		if(DialogArray[id]) {
			DialogCurrentInstance = id;
			return DialogArray[id];
		} else {
			return false;
		}
	},

	currentInstance: function() {
		return DialogArray[DialogCurrentInstance];
	},

	firstVisible: function() {
		var fv = DialogIndex.find(function(d) { return DialogArray[d].visible; });
		if(fv) {
			return DialogArray[fv];
		}
		return false;
	},

	swapOverlayIds: function(one, two) {
		if('object' != (typeof(one)).toLowerCase()) {
			one = DialogArray[one];
			if(!one) return false;
		}
		if('object' != (typeof(two)).toLowerCase()) {
			two = DialogArray[two];
			if(!two) return false;
		}

		var tmp;
		var tmp2;
		/* I only use the ID to get the ID to be sure that the element
		 * actually exists. I know it seems redundant. I guess it is. */
		tmp = $('jt_loading_overlay_'+one.instanceId).id;
		tmp2 = $('jt_loading_overlay_'+two.instanceId).id;
		$('jt_loading_overlay_'+one.instanceId).id = 'jt_temp_overlay_swap_id';
		$('jt_loading_overlay_'+two.instanceId).id = tmp;
		$('jt_temp_overlay_swap_id').id = tmp2;
	}
};

var DialogBase = Class.create();
Object.extend(DialogBase, {
	getPageScroll: function() {
		var xScroll, yScroll;

		if (self.pageYOffset) {
			yScroll = self.pageYOffset;
			xScroll = self.pageXOffset;
		} else if (document.documentElement && document.documentElement.scrollTop){	 // Explorer 6 Strict
			yScroll = document.documentElement.scrollTop;
			xScroll = document.documentElement.scrollLeft;
		} else if (document.body) {// all other Explorers
			yScroll = document.body.scrollTop;
			xScroll = document.body.scrollLeft;	
		}

		arrayPageScroll = {
			left: xScroll,
			top: yScroll
		};
		//new Array(xScroll,yScroll) 
		return arrayPageScroll;
	},

	getPageSize: function() {
		var xScroll, yScroll;
		
		if (window.innerHeight && window.scrollMaxY) {	
			xScroll = window.innerWidth + window.scrollMaxX;
			yScroll = window.innerHeight + window.scrollMaxY;
		} else if (document.body.scrollHeight > document.body.offsetHeight){ // all but Explorer Mac
			xScroll = document.body.scrollWidth;
			yScroll = document.body.scrollHeight;
		} else { // Explorer Mac...would also work in Explorer 6 Strict, Mozilla and Safari
			xScroll = document.body.offsetWidth;
			yScroll = document.body.offsetHeight;
		}
		
		var windowWidth, windowHeight;
		
		//	console.log(self.innerWidth);
		//	console.log(document.documentElement.clientWidth);

		if (self.innerHeight) {	// all except Explorer
			if(document.documentElement.clientWidth){
				windowWidth = document.documentElement.clientWidth; 
			} else {
				windowWidth = self.innerWidth;
			}
			windowHeight = self.innerHeight;
		} else if (document.documentElement && document.documentElement.clientHeight) { // Explorer 6 Strict Mode
			windowWidth = document.documentElement.clientWidth;
			windowHeight = document.documentElement.clientHeight;
		} else if (document.body) { // other Explorers
			windowWidth = document.body.clientWidth;
			windowHeight = document.body.clientHeight;
		}	
		
		// for small pages with total height less then height of the viewport
		if(yScroll < windowHeight){
			pageHeight = windowHeight;
		} else { 
			pageHeight = yScroll;
		}

		//	console.log("xScroll " + xScroll)
		//	console.log("windowWidth " + windowWidth)

		// for small pages with total width less then width of the viewport
		if(xScroll < windowWidth){	
			pageWidth = xScroll;		
		} else {
			pageWidth = windowWidth;
		}

		//	console.log("pageWidth " + pageWidth)

		arrayPageSize = {
			page: {
				width: pageWidth,
				height: pageHeight
			},
			window: {
				width: windowWidth,
				height: windowHeight
			}
		}

		//arrayPageSize = new Array(pageWidth,pageHeight,windowWidth,windowHeight) 
		return arrayPageSize;
	},

	place: function(id) {
		var id = (id) ? id : DialogCurrentInstance;
		/* Place the box. This will be called on scroll, also. */
		var size = DialogBase.getPageSize();
		var scroll = DialogBase.getPageScroll();
		$('jt_loading_box_'+id).style.left = (scroll.left + ((size.window.width - $('jt_loading_box_'+id).getWidth()) / 2)) + 'px';
		$('jt_loading_box_'+id).style.top = (scroll.top + ((size.window.height - $('jt_loading_box_'+id).getHeight()) / 2)) + 'px';
	}
});

var MessagePrivate = Class.create();
Object.extend(MessagePrivate, DialogBase);

MessagePrivate.prototype = {
	instanceId: '',
	delay: 2000,
	visible: false,
	initialize: function(id) {
		this.instanceId = id;

		/* Make sure the page is set up for the Loading message. */
		var objBody = document.getElementsByTagName('body').item(0);

		var objOverlay = document.createElement("div");
		objOverlay.setAttribute('id','jt_loading_overlay_'+id);
		objOverlay.className = 'jt_loading_overlay';
		objOverlay.style.display = 'none';
		objBody.appendChild(objOverlay);

		var loadingBox = document.createElement('div');
		loadingBox.setAttribute('id', 'jt_loading_box_'+id);
		loadingBox.className = 'jt_loading_box';
		loadingBox.style.display = 'none';

		var loadingBoxMsg = document.createElement('div');
		loadingBoxMsg.setAttribute('id', 'jt_loading_box_msg_'+id);
		loadingBoxMsg.className = 'jt_loading_box_msg';
		loadingBox.appendChild(loadingBoxMsg);

		var clearMsg = document.createElement('div');
		clearMsg.setAttribute('id', 'jt_loading_box_clear_msg_'+id);
		clearMsg.className = 'jt_loading_box_clear_msg';
		clearMsg.innerHTML = 'Click anywhere to clear this message.';
		loadingBox.appendChild(clearMsg);

		objBody.appendChild(loadingBox);
	},

	setDelay: function(ms) {
		// Just make sure it's a number.
		if((typeof(ms)).toLowerCase() == 'number') {
			this.delay = ms;
		}
	},

	setMessage: function(msg) {
		$('jt_loading_box_msg_'+this.instanceId).innerHTML = msg;
	},

	show: function() {
		var dialogShowing = Dialog.firstVisible();
		/* If they're trying to show the same dialog twice, don't do anything. */
		if(dialogShowing && dialogShowing.instanceId == this.instanceId) return;

		/* Attach some needed events. */
		Event.observe(window, 'scroll', function() { var id = this.instanceId; MessagePrivate.place(this.instanceId); } );
		MessagePrivate.place(this.instanceId);

		/* We now have a cool thing going where different dialog instances can share
		 * the same overlay if one is shown before another is hidden. This prevents the
		 * potentially goofy fading out and in of the overlay between messages of
		 * different types. For example, a loading dialog followed by a message dialog. */
		if(dialogShowing) {
			/* Just hide the message box of the currently showing dialog. 
			 * This also sets the 'visible' property of that dialog to false. */
			dialogShowing.hideMessage();
			/* Swap the IDs of the two overlays so that the status of the
			 * elements remain in sync with what's going on. This way, when
			 * the newly shown dialog hides itself, it can fade out the overlay
			 * that is actually showing (which formerly was not its overlay, but
			 * the overlay of the previous message). */
			Dialog.swapOverlayIds(dialogShowing, this);

			/* In this case, attach the click event observer to clear it when clicked. */
			Event.observe('jt_loading_overlay_'+this.instanceId, 'click', function() {
				var d = DialogArray[DialogCurrentInstance];
				d.hide();
			} );
		} else {
			/* Place the overlay, which only needs to be done now because
			 * it will be the size of the entire page. */
			var size = LoadingPrivate.getPageSize();
			var scroll = LoadingPrivate.getPageScroll();
			$('jt_loading_overlay_'+this.instanceId).style.width = size.page.width + 'px';
			$('jt_loading_overlay_'+this.instanceId).style.height = size.page.height + 'px';

			Event.observe('jt_loading_overlay_'+this.instanceId, 'click', function() {
				var d = DialogArray[DialogCurrentInstance];
				d.hide();
			} );

			new Effect.Appear('jt_loading_overlay_'+this.instanceId, { duration: 0.2, from: 0.0, to: 0.8 } );
		}

		$('jt_loading_box_'+this.instanceId).show();

		if(this.delay) {
			window.setTimeout(function() {
				var d = DialogArray[DialogCurrentInstance];
				d.hide();
			}, this.delay);
		}

		this.visible = true;
	},

	hide: function() {
		Event.stopObserving(window, 'scroll', function() { var id = this.instanceId; LoadingPrivate.place(this.instanceId); } );
		$('jt_loading_box_'+this.instanceId).hide();
		new Effect.Fade('jt_loading_overlay_'+this.instanceId, { duration: 0.2 } );
		this.visible = false;
	},

	hideMessage: function() {
		Event.stopObserving(window, 'scroll', function() { var id = this.instanceId; LoadingPrivate.place(this.instanceId); } );
		$('jt_loading_box_'+this.instanceId).hide();
		this.visible = false;
	}
}

/* The Prototype Class object gives us a constructor and destructor
 * and other cool, mature language features. */
var LoadingPrivate = Class.create();
Object.extend(LoadingPrivate, DialogBase);

/* Instance functions live within the prototype.
 * initialize is called when each instance is constructed. */
LoadingPrivate.prototype = {
	instanceId: '',
	visible: false,
	initialize: function(id) {
		this.instanceId = id;

		/* Make sure the page is set up for the Loading message. */
		var objBody = document.getElementsByTagName('body').item(0);

		var objOverlay = document.createElement("div");
		objOverlay.setAttribute('id','jt_loading_overlay_'+id);
		objOverlay.className = 'jt_loading_overlay';
		objOverlay.style.display = 'none';
		objBody.appendChild(objOverlay);

		var loadingBox = document.createElement('div');
		loadingBox.setAttribute('id', 'jt_loading_box_'+id);
		loadingBox.className = 'jt_loading_box';
		loadingBox.style.display = 'none';

		var loadingBoxMsg = document.createElement('div');
		loadingBoxMsg.setAttribute('id', 'jt_loading_box_msg_'+id);
		loadingBoxMsg.className = 'jt_loading_box_msg';
		loadingBox.appendChild(loadingBoxMsg);

		var loadingImg = document.createElement('img');
		loadingImg.src = '/images/loading.gif';
		loadingImg.className = 'jt_loading_box_img';
		loadingBox.appendChild(loadingImg);

		objBody.appendChild(loadingBox);
	},

	setMessage: function(msg) {
		$('jt_loading_box_msg_'+this.instanceId).innerHTML = msg;
	},

	show: function() {
		var dialogShowing = Dialog.firstVisible();
		/* If they're trying to show the same dialog twice, don't do anything. */
		if(dialogShowing && dialogShowing.instanceId == this.instanceId) return;

		/* Attach some needed events. */
		Event.observe(window, 'scroll', function() { var id = this.instanceId; LoadingPrivate.place(this.instanceId); } );
		LoadingPrivate.place(this.instanceId);

		/* We now have a cool thing going where different dialog instances can share
		 * the same overlay if one is shown before another is hidden. This prevents the
		 * potentially goofy fading out and in of the overlay between messages of
		 * different types. For example, a loading dialog followed by a message dialog. */
		if(dialogShowing) {
			/* Just hide the message box of the currently showing dialog. 
			 * This also sets the 'visible' property of that dialog to false. */
			dialogShowing.hideMessage();
			/* Swap the IDs of the two overlays so that the status of the
			 * elements remain in sync with what's going on. */
			Dialog.swapOverlayIds(dialogShowing, this);
		} else {
			/* Place the overlay, which only needs to be done now because
			 * it will be the size of the entire page. */
			var size = LoadingPrivate.getPageSize();
			var scroll = LoadingPrivate.getPageScroll();
			$('jt_loading_overlay_'+this.instanceId).style.width = size.page.width + 'px';
			$('jt_loading_overlay_'+this.instanceId).style.height = size.page.height + 'px';

			new Effect.Appear('jt_loading_overlay_'+this.instanceId, { duration: 0.2, from: 0.0, to: 0.8 } );
		}

		$('jt_loading_box_'+this.instanceId).show();
		this.visible = true;
	},

	hide: function() {
		Event.stopObserving(window, 'scroll', function() { var id = this.instanceId; LoadingPrivate.place(this.instanceId); } );
		$('jt_loading_box_'+this.instanceId).hide();
		new Effect.Fade('jt_loading_overlay_'+this.instanceId, { duration: 0.2 } );
		this.visible = false;
	},

	hideMessage: function() {
		Event.stopObserving(window, 'scroll', function() { var id = this.instanceId; LoadingPrivate.place(this.instanceId); } );
		$('jt_loading_box_'+this.instanceId).hide();
		this.visible = false;
	}
};
