﻿/*=============================================================================
 * Author:   Aaron Hagey
 * Date:     8/21/2006
 * Purpose:  Popup widgets.
 *    Widgets.Popup class is for a div already full of content in the page.
 *    Widgets.Popup.Ajax class is for a div in the page that needs to be populated via AJAX.
/*===========================================================================*/
/* NOTE: to have proper event handling on non-fixed position popup, it may be
 * required that each trigger have a valid(unique) id.
/*/
Widgets.PopupAjax = Class.create();
Widgets.PopupAjax.prototype = {
	initialize: function(url, container, triggers, options) {
		this.url = url;
		this.options = options || {};
		Object.extend(this.options, {
			onBeforeShow: this.load.bind(this)
		});
		this.container = container;
		this.popup = new Widgets.Popup(container, triggers, this.options);
	},
	load: function() {
		if (!this.loaded) {
			var container = this.container;
			var url = this.url;
			var options = this.options;
			Object.extend(options, {
				onSuccess: this._ajaxSuccess.bind(this),
				onFailure: this._ajaxFailure.bind(this),
				onException: this._ajaxException.bind(this)
			});
			var req = new Ajax.Updater(container, url, options);
		}
	},
	_ajaxSuccess: function(poXHR, poJSON) {
		this.loaded = true;
	},
	_ajaxFailure: function(poXHR, poJSON) {
		alert("problem 1");
	},
	_ajaxException: function(poAjaxReq, poException) {
		alert("problem 2 -- " + poException);
	}
}
Widgets.Popup = Class.create();
Widgets.Popup.prototype = {
	initialize: function(popup, triggers, options) {
		this.setOptions(options);

		if (popup = $(popup)) {
			this.popupElement = popup;
			this.popupDim = Element.getDimensions(popup);
			this._attachPopup();
		}
		this.hide();

		this.triggerElements = [];
		this.addTriggers(triggers);
	},
	addTriggers: function() {
		for (var i = 0; i < arguments.length; i++) {
			var arg = arguments[i];
			if (arg) {
				if (typeof arg !== 'string' && arg.length) {
					for (var j = 0; j < arg.length; j++) {
						var elem = $(arg[j]);
						if (elem) {
							this.triggerElements.push(elem);
							this._attachTrigger(elem);
						}
					}
				}
				else {
					var elem = $(arg);
					if (elem) {
						this.triggerElements.push(elem);
						this._attachTrigger(elem);
					}
				}
			}
		}
	},
	setOptions: function(options) {
		this.options = {
			behavior: 'hover',				// values: hover, click, focus
			position: 'fixed',				// values: fixed [default], relative [use positionedOffset(trigger)], relativeFar [use cumulativeOffset(trigger)]
			positionAlign: 'bottom right',	// values: left, center, right, top, middle, bottom
			positionOffset: { x: 0, y: 0 },
			showEffect: Element.show,
			hideEffect: Element.hide,
			onBeforeShow: Prototype.emptyFunction,
			onShow: Prototype.emptyFunction,
			onBeforeHide: Prototype.emptyFunction,
			onHide: Prototype.emptyFunction
		};
		Object.extend(this.options, options || {});

		// parse positionAlign values
		var values = this.options.positionAlign.split(/\s+/);
		this.options.positionX = values.detect(function(value) {
			return ['before','left','center','right','after'].include(value);
		});
		this.options.positionY = values.detect(function(value) {
			return ['over','top','middle','bottom','under'].include(value);
		});
	},
	show: function() {
		var popup;
		if (!(popup = this.popupElement)) { return; }
		if (!this.showing) { this._setPosition(); }
		try {
			this.options.onBeforeShow();
		} catch(err) { }
		this.options.showEffect(popup);	//, { duration: .5});
		//popup.style.display = '';
		this.showing = true;
		try {
			this.options.onShow();
		} catch(err) { }
	},
	hide: function(noEffects) {
		var popup;
		if (!(popup = this.popupElement)) { return; }
		try {
			this.options.onBeforeHide();
		} catch(err) { }
		if (noEffects) { popup.hide(); }
		else { this.options.hideEffect(popup); }
		//popup.style.display = 'none';
		this.showing = false;
		try {
			this.options.onHide();
		} catch(err) { }
	},
	showByElement: function(element) {
		var popup;
		if (!(popup = this.popupElement)) { return; }
		this.currentTrigger = element;
		this._showTimed();
	},
	hideByElement: function(element) {
		this._hideTimed();
	},
	_onmouseover: function(e) {
		this._showTimed();
	},
	_onmouseout: function(e) {
		this._hideTimed();
	},
	_focusTrigger: function() {
		try {
			this.currentTarget.focus();
		} catch(err) { }
	},
	_onmouseoverTrigger: function(e) {
		this.currentTrigger = this._getTrigger(e);
		this._showTimed();
	},
	_onmouseoutTrigger: function(e) {
		this.currentTrigger = this._getTrigger(e);
		this._hideTimed();
	},
	_onclickTrigger: function(e) {
		this.currentTrigger = this._getTrigger(e);
		if (!this.showing) {
			this.show();
		} else {
			this.hide();
		}
		Event.stop(e);
		return false;
	},
	_onfocusTrigger: function(e) {
		this.currentTrigger = this._getTrigger(e);
		this._showTimed();
	},
	_onblurTrigger: function(e) {
		this.currentTrigger = this._getTrigger(e);
		this._hideTimed();
	},
	_getTrigger: function(e) {
		var trigger;
		var element = Event.element(e);
		// go up the tree looking for ids, each id found, look for a match in the Triggers collection
		do {
			if (element.nodeType == 1 && element.id) {
				trigger = this.triggerElements.detect(function(value) {
					if (value.id == element.id) { return true; }
					else { return false; }
				});
			}
		} while (!(trigger) && (element = element.parentNode));
		return trigger;
	},
	_setPosition: function() {
		var popup, trigger, popupDim, triggerDim, coords, x, y;
		if (!(popup = this.popupElement)) { return; }
		if (!(trigger = this.currentTrigger)) { return ; }
		switch (this.options.position) {
			case 'fixed':
				break;
			case 'relative':
				coords = Position.positionedOffset(trigger);
				break;
			case 'relativeFar':
				coords = Position.cumulativeOffset(trigger);
				break;
		}
		if (!coords) { return; }
		popupDim = this.popupDim;
		triggerDim = Element.getDimensions(trigger);
		switch (this.options.positionX) {
			case 'before':
				x = coords[0] - popupDim.width;
				break;
			case 'left':
				x = coords[0];
				break;
			case 'center':
				x = coords[0] + (triggerDim.width / 2) - (popupDim.width / 2);
				break;
			case 'right':
				x = coords[0] + triggerDim.width - popupDim.width;
				break;
			case 'after':
				x = coords[0] + triggerDim.width;
				break;
//			default:
//				x = Position.positionedOffset(popup)[0];
//				break;
		}
		switch (this.options.positionY) {
			case 'over':
				y = coords[1] - popupDim.height;
				break;
			case 'top':
				y = coords[1];
				break;
			case 'middle':
				y = coords[1] + (triggerDim.height / 2) - (popupDim.height / 2);
				break;
			case 'bottom':
				y = coords[1] + triggerDim.height - popupDim.height;
				break;
			case 'under':
				y = coords[1] + triggerDim.height;
				break;
//			default:
//				y = Position.positionedOffset(popup)[1];
//				break;
		}
/*alert(coords.inspect()
	+ "\n{ width: " + triggerDim.width + " height: " + triggerDim.height + " }"
	+ "\n{ width: " + popupDim.width + " height: " + popupDim.height + " }"
	+ "\nx: " + x + " y: " + y);
*/
		if (typeof x == 'number') {
			x += this.options.positionOffset.x;
			popup.style.left = x + 'px';
		}
		if (typeof y == 'number') {
			y += this.options.positionOffset.y;
			popup.style.top = y + 'px';
		}
	},
	_showTimed: function() {
		this._cancelTiming();
		if (!this.showing) {
			this.showTimeout = window.setTimeout(this.show.bind(this), 50);
		}
	},
	_hideTimed: function() {
		this._cancelTiming();
		if (this.showing) {
			this.hideTimeout = window.setTimeout(this.hide.bind(this), 200);
		}
	},
	_cancelTiming: function() {
		if (this.showTimeout) {
			window.clearTimeout(this.showTimeout);
			this.showTimeout = null;
		}
		if (this.hideTimeout) {
			window.clearTimeout(this.hideTimeout);
			this.hideTimeout = null;
		}
	},
	_attachPopup: function() {
		var popup = this.popupElement;
		if (!popup) { return; }
		if (!this.popupObservers) {
			this.popupObservers = {
				mouseover: this._onmouseover.bindAsEventListener(this),
				mouseout: this._onmouseout.bindAsEventListener(this),
				click: this._focusTrigger.bindAsEventListener(this)
			};
		}
		switch (this.options.behavior) {
			case 'hover':
				Event.observe(popup, 'mouseover', this.popupObservers.mouseover);
				Event.observe(popup, 'mouseout', this.popupObservers.mouseout);
				break;
			case 'click':
				break;
			case 'focus':
				Event.observe(popup, 'click', this.popupObservers.click);
				break;
		}
	},
	_detachPopup: function() {
		var popup = this.popupElement;
		if (!popup) { return; }
		if (!this.popupObservers) { return; }
		switch (this.options.behavior) {
			case 'hover':
				Event.stopObserving(popup, 'mouseover', this.popupObservers.mouseover);
				Event.stopObserving(popup, 'mouseout', this.popupObservers.mouseout);
				break;
			case 'click':
				break;
			case 'focus':
				Event.stopObserving(popup, 'click', this.popupObservers.click);
				break;
		}
	},
	_attachTrigger: function(elem) {
		if (!(elem = $(elem))) { return; }
		if (!this.triggerObservers) {
			this.triggerObservers = {
				mouseover: this._onmouseoverTrigger.bindAsEventListener(this),
				mouseout: this._onmouseoutTrigger.bindAsEventListener(this),
				click: this._onclickTrigger.bindAsEventListener(this),
				focus: this._onfocusTrigger.bindAsEventListener(this),
				blur: this._onblurTrigger.bindAsEventListener(this)
			};
		}
		switch (this.options.behavior) {
			case 'hover':
				Event.observe(elem, 'mouseover', this.triggerObservers.mouseover);
				Event.observe(elem, 'mouseout', this.triggerObservers.mouseout);
				break;
			case 'click':
				Event.observe(elem, 'click', this.triggerObservers.click);
				break;
			case 'focus':
//				Event.observe(elem, 'click', this.triggerObservers.click);
				Event.observe(elem, 'focus', this.triggerObservers.focus);
				Event.observe(elem, 'blur', this.triggerObservers.blur);
				break;
		}
	},
	_detachTrigger: function(elem) {
		if (!(elem = $(elem))) { return; }
		if (!this.triggerObservers) { return; }
		switch (this.options.behavior) {
			case 'hover':
				Event.stopObserving(elem, 'mouseover', this.triggerObservers.mouseover);
				Event.stopObserving(elem, 'mouseout', this.triggerObservers.mouseout);
				break;
			case 'click':
				Event.stopObserving(elem, 'click', this.triggerObservers.click);
				break;
			case 'focus':
//				Event.stopObserving(elem, 'click', this.triggerObservers.click);
				Event.stopObserving(elem, 'focus', this.triggerObservers.focus);
				Event.stopObserving(elem, 'blur', this.triggerObservers.blur);
				break;
		}
	}
};
