// 
//  mts.tooltips.js
//  MTS ToolTips
//  
//  Created by Victor Nguyen on 2009-09-07.
//  Copyright 2009 mcm technial services. All rights reserved.
// 

/**
	@ Document settings and how to setup span[class][id]'s for tooltips
*/

if (typeof MTS == "undefined") { var MTS = {}; }

MTS.ToolTips = {
	
	Settings: {
		
		Triggers: {
			CLASS: 'mts-tooltip',
			ELEMENT: 'a',
			ID_PREFIX: 'mts-artistid-', // prefix before artist id, e.g. for id="mts-artistid-999", prefix is 'mts-artistid-'
			URL_PREFIX: 'http://www.thehothits.com/artists/', // artist id gets appended to this
			open_in_new_window: false,
			styles: {
				color: '#cc3539',
				font_weight: 'bold',
				border_bottom: '1px dotted red',
				cursor: 'pointer',
				Icon: {
					padding: '0 2px 0 4px',
					border: '0',
					display: 'inline'
				}
			}
		},
		
		Popup: {
			delay_disp: 200, // ms
			delay_hide: 800, // ms
			width_min: 211, // px
			width_max: 383, // px
			height: 120, // px
			padding: 1, // px
			offset_x: -50, // px
			offset_y: -118 // px
		},
		
		Flash: {
			url: '/flash/movideo_mediagallery.swf',
			vars: {
				mgt: '',
				mgs: 'hh',
				artistId: ''
			},
			width_min: 221, // px
			width_max: 383, // px
			height: 90, // px
			object_id: 'mtsgalleryobject',
			embed_id: 'mtsgalleryembed'
		},

		ImageAssets: {
			bg_left: '/images/mts-tooltips/mts-tooltips-bg-left.png',
			bg_right: '/images/mts-tooltips/mts-tooltips-bg-right.png',
			bg_right_ie6_297: '/images/mts-tooltips/mts-tooltips-bg-right-ie6-297px.png',
			bg_right_ie6_211: '/images/mts-tooltips/mts-tooltips-bg-right-ie6-211px.png',
			ico_play: '/images/mts-tooltips/mts-tooltips-play.gif'
		}
		
	},
	
	Objects: {
		TriggerActive: null, // active trigger link
		Popup: null, // the popup element if it exisits
		Timer: null // application timer
	},
	
	
	
	/**
		@ SETUP MTS.TOOLTIPS
		@ param classname [String (optional)] CSS class to setup mts.tooltips on
	*/
	setup: function (classname, options) {
		/**
			ADD OPTIONS PARSER ?
			Are there key options?
		*/				
		// console.debug(options);
		// for (var key in options) {
		// 	this.Settings.Popup[key] = options[key];
		// }
		
		if (classname) { this.Settings.Triggers.CLASS = classname; }
		
		var triggers = this.Utils.getElementsByClass(this.Settings.Triggers.CLASS, null, this.Settings.Triggers.ELEMENT);
		if (triggers.length < 1) return;
		
		// assign trigger events
		for (var i=0; i < triggers.length; i++) {
			
			this.styleTriggers(triggers[i]);
			
			triggers[i].onmouseover = function (e) {
				if (!e) var e = window.event;
				MTS.ToolTips.handleMouseOver(this, e);
			};

			triggers[i].onmouseout = function (e) {
				if (!e) var e = window.event;
				MTS.ToolTips.handleMouseOut(this, e);
			};
			
			triggers[i].onclick = function () {
				MTS.ToolTips.goToArtistPage(MTS.ToolTips.getArtistId(this), MTS.ToolTips.Settings.Triggers.open_in_new_window);
				return false;
			}; 

		};
		
		// assign document listener for cleanup
		// use mts_addEvent to ensure existing document.onmouseover events aren't overwritten
		mts_addEvent(document,"mouseover", function (e) {
			if (!e) var e = window.event;
			MTS.ToolTips.handleDocumentOver(e);
		});
		
		// preload bg images
		this.preloadImages(this.Settings.ImageAssets);
	},
	
	
	
	/**
		@ TRIGGER MOUSEOVER: CREATE, POSITION, DISPLAY POPUP
		@ param trigger [HTML Element] HTML Element of activated trigger link
	*/
	handleMouseOver: function (trigger) {
		this.stopTimer();
		this.Objects.Timer = setTimeout(function(){ _displayPopup(trigger); trigger=null; }, MTS.ToolTips.Settings.Popup.delay_disp);
		
		function _displayPopup (trigger) {
			// reset timer
			MTS.ToolTips.stopTimer();

			// if trigger is active, its tooltip is already there, do nothing
			if (MTS.ToolTips.Utils.hasClass(trigger, 'active')) { return false; }

			// if another tooltip exists, kill it
			if (MTS.ToolTips.Objects.Popup) { MTS.ToolTips.removePopup(); }

			// set active trigger and artist id
			MTS.ToolTips.Utils.addClass(trigger, 'active');
			MTS.ToolTips.Objects.TriggerActive = trigger;
			MTS.ToolTips.Settings.Flash.vars.artistId = MTS.ToolTips.getArtistId(trigger);

			// create, position and append popup
			_createPopup(trigger);
			MTS.ToolTips.positionPopup(trigger);
			document.body.appendChild(MTS.ToolTips.Objects.Popup);

			function _createPopup (trigger) {
				var popup = document.createElement('div');
			  popup.style.width = MTS.ToolTips.Settings.Popup.width_min + 'px';
				popup.style.height = MTS.ToolTips.Settings.Popup.height + 'px';
				popup.style.padding = MTS.ToolTips.Settings.Popup.padding + 'px';
			  popup.style.overflow = 'hidden';
			  popup.style.zIndex = '9999';
			  popup.style.backgroundColor = 'transparent';
			  popup.style.backgroundRepeat = 'no-repeat';
				popup.style.position = 'absolute';
				popup.setAttribute('rel', trigger.getAttribute('id')); // set id of tooltip to span[rel] of it's trigger
				popup.innerHTML = '<object id="' + MTS.ToolTips.Settings.Flash.object_id + '" width="' + MTS.ToolTips.Settings.Flash.width_min + '" height="' + 0 + '" classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000" codebase="http://download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=9,0,28,0"><param name="allowScriptAccess" value="always" /><param name="flashvars" value="' + _toFlashVarsString(MTS.ToolTips.Settings.Flash.vars) + '" /><param name="movie" value="' + MTS.ToolTips.Settings.Flash.url + '" /><param name="bgcolor" value="#bb2c2f" /><embed id="' + MTS.ToolTips.Settings.Flash.embed_id + '" src="' + MTS.ToolTips.Settings.Flash.url + '?' + _toFlashVarsString(MTS.ToolTips.Settings.Flash.vars) + '" quality="high" bgcolor="#bb2c2f" width="' + MTS.ToolTips.Settings.Flash.width_min + '" height="' + 0 + '" name="myMovieName" align="" type="application/x-shockwave-flash" pluginspage="http://www.macromedia.com/go/getflashplayer" allowscriptaccess="always" allowfullscreen="true" /></object>';

				popup.onmouseover = function (e) {
					MTS.ToolTips.stopTimer();
				};

				popup.onmouseout = function (e) {
					if (!e) var e = window.event;
					MTS.ToolTips.handleMouseOut(popup, e);
				};

				MTS.ToolTips.Objects.Popup = popup;

				function _toFlashVarsString (hash) {
					var pairs = [], string;
					for (key in hash) {
						string = key + '=' + hash[key];
						pairs[pairs.length] = string;
					}
					return pairs.join('&');
				}	
			}
		}
		
	},
	
	
	
	/**
		@ POSITION POPUP
		@ param trigger [HTML Element] HTML Element of activated trigger link
		@ param popup_width [Integer (optional)] Popup width, otherwise it is MTS.ToolTips.Settings.Popup.width_min
		Called after creation of popup by the SWF via External Interface.
		SWF width is passed to it on Flash callback to re-position popup once swf width is known (the popup and swf
		widths are the same in this implementation)
	*/
	positionPopup: function (trigger, popup_width) {
		var popup = this.Objects.Popup;
		var viewport_width = _getViewportSize()[0];
		var trigger_x = _getPosition(trigger)[0];
		var trigger_y = _getPosition(trigger)[1];
		var gap = viewport_width - trigger_x; // width of gap from link position to right edge of viewport 
		if (!popup_width) { popup_width = this.Settings.Popup.width_min; }
		
	  popup.style.top = (trigger_y + this.Settings.Popup.offset_y) + 'px';
		
		// if the RHS gap is smaller than the max width of the popup, right-align the popup
		if (gap < this.Settings.Popup.width_max) {
			popup.style.left = trigger_x - (popup_width + this.Settings.Popup.offset_x) + 'px';
			popup.style.backgroundPosition = 'right top';
			if (this.Utils.isIE6()) {
				bg_ie6 = _getBackgroundImageForIE6(popup_width);
				popup.style.filter = 'progid:DXImageTransform.Microsoft.AlphaImageLoader(src="' + bg_ie6 + '", sizingMethod="crop")';
			} else {
				popup.style.backgroundImage = 'url("' + this.Settings.ImageAssets.bg_right + '")';
			}
		// otherwise, there's enough room, left-align popup
		} else {
		  popup.style.left = trigger_x + 'px';
			popup.style.backgroundPosition = 'left top';
			if (this.Utils.isIE6()) {
				popup.style.filter = 'progid:DXImageTransform.Microsoft.AlphaImageLoader(src="' + this.Settings.ImageAssets.bg_left + '", sizingMethod="crop")';
			} else {
				popup.style.backgroundImage = 'url("' + this.Settings.ImageAssets.bg_left + '")';
			}
		}
		
		// In IE6, we cannot apply background-position background images applied with AlphaImageLoader
		// So we have to create background images sized to match all the possible tooltip widths
		// This function returns the appropriate image for the popup_width
		function _getBackgroundImageForIE6 (popup_width) {
			if (popup_width == MTS.ToolTips.Settings.Popup.width_max) {
				return MTS.ToolTips.Settings.ImageAssets.bg_right;
			} else if (popup_width == 297) {
				return MTS.ToolTips.Settings.ImageAssets.bg_right_ie6_297;
			}	else if (popup_width == 211) {
				return MTS.ToolTips.Settings.ImageAssets.bg_right_ie6_211;
			}
		}
		
		// returns position of element relative to document 
		function _getPosition (el) {
			var x = y = 0;
			do {
				x += el.offsetLeft;
				y += el.offsetTop;
			} while (el = el.offsetParent);
		  return [x, y];
		}
		
		// returns dimensions of document
		function _getViewportSize () {
			var x = y = 0;
			var de = document.documentElement;
			x = self.innerWidth || (de && de.clientWidth) || document.body.clientWidth;
			y = self.innerHeight || (de && de.clientHeight) || document.body.clientHeight;
			return [x, y];
		}
	},
	
	
	
	/**
		@ FLASH CALLBACK: SET SWF SIZE
		@ param swf_width [Interger] Calculated swf width
		It resizes the swf accordingly, aswell as resizing and repositioning the popup.
		Called when Flash has loaded artist tracks and is able to determine the swf width.
		swf height is initially set to 0 in _createPopup to hide flash until this callback.
	*/
	setSize: function (swf_width) {
		var swf_obj = document.getElementById(this.Settings.Flash.object_id);
		if (swf_obj) swf_obj.setAttribute('width', swf_width);
		if (swf_obj) swf_obj.setAttribute('height', this.Settings.Flash.height);
		var swf_emb = document.getElementById(this.Settings.Flash.embed_id);
		if (swf_emb) swf_emb.setAttribute('width', swf_width);
		if (swf_emb) swf_emb.setAttribute('height', this.Settings.Flash.height);
		
		this.Objects.Popup.style.width = swf_width + 'px';
		this.positionPopup(this.Objects.TriggerActive, swf_width);
	},
	
		
	
	/**
		@ TRIGGER/POPUP MOUSEOUT: REMOVE POPUP
		@ param el [HTML Element] HTML Element of mouseout listener (trigger or popup)
		@ param e [Object] Event object
		Called by the trigger and popup. Determines if mouse is has really left the tooltip
		area, then sets timeout to destroy popup.
	*/
	handleMouseOut: function (el, e) {	
		// if mouse has left tooltip, set a timer to destroy popup
		if (_hasMouseLeftToolTip(el, e)) {
			this.stopTimer();
			this.Objects.Timer = setTimeout(function(){ MTS.ToolTips.removePopup(); }, MTS.ToolTips.Settings.Popup.delay_hide);
		}
		
		function _hasMouseLeftToolTip (el, e) {
			// get element mouseout occured on
			var tg = e.srcElement || e.target;
			
			// if it wasn't the trigger/popout, return false
			if (tg != el) { return false; }

			// get related target (element moused-out to)
			var reltg = (e.relatedTarget) ? e.relatedTarget : e.toElement;
			
			// if it's null, something's gone wrong, kill popup immediate and return false
			if (reltg == null) {
				this.removePopup();
				return false;
			}
			
			// starting at related target, scale up DOM till it hits the trigger/popout, <body> or <html>
			while (reltg != tg && reltg.nodeName != 'BODY' && reltg.nodeName != 'HTML') {
				reltg = reltg.parentNode;
			};
			
			// if the moused-out-to element is a child of the the trigger/popout,
			// it's still on toolip territory, so won't remove the popup yet, return false
			if (reltg == tg) { return false; }
			
			// otherwise, the mouse has really left tooltip territory, return true
			return true;
		}
	},
	
	
	
	/**
		@ DOCUMENT MOUSEOVER: CLEAN UP ORPHAN TOOLTIPS
		@ param e [Object] Event object
			Destroys any orphan tooltips not destroyed by the mouseout events on triggers/tooltip
			(this happens sometimes when the cursor mouses out from the tooltip very fast, not firing
			the tooltip mouseout event in the process ... grrr)
	*/ 
	handleDocumentOver: function (e) {
		if (_isMouseAwayFromToolTip(e)) { _cleanUp(); }

		function _cleanUp () {
			// if tooltip is not active AND the timer is counting, do nothing
			if ((!MTS.ToolTips.Objects.Popup) && (MTS.ToolTips.Objects.Timer)) { return; }

			// if tooltip is on screen AND the timer is not counting, set timer to destroy tooltip
			if ((MTS.ToolTips.Objects.Popup) && (MTS.ToolTips.Objects.Timer == null)) {
				MTS.ToolTips.stopTimer();
				MTS.ToolTips.Objects.Timer = setTimeout(function(){ MTS.ToolTips.removePopup(); }, MTS.ToolTips.Settings.Popup.delay_hide);
			}
		}
		
		// checks if mouse is still over popup/link		
		function _isMouseAwayFromToolTip (e) {
			// get hovered el from event
			var tg = e.srcElement || e.target;
			
			// if the hovered el class matches trigger class, return false
			if (MTS.ToolTips.Utils.hasClass(tg, MTS.ToolTips.Settings.Triggers.CLASS)) { return false; }
			
			// take hovered el and step up thru DOM, stop if it's the tooltip, <body> or <html>
			while (tg != MTS.ToolTips.Objects.Popup && tg.nodeName != 'BODY' && tg.nodeName != 'HTML') {
				tg = tg.parentNode;
			};
			
			// if the hovered el is a child of the ToolTip, return false
			if (tg == MTS.ToolTips.Objects.Popup) return false;
			
			// it passes all checks! the mouse has moved away from the tooltip
			return true;
		}
	},

	
	
	/**
		@ MISC APPLICATION FUNCTIONS
	*/
	removePopup: function () {
		this.stopTimer();
		if (!(this.Objects.Popup)) return;
		
		this.Utils.removeClass(this.Objects.TriggerActive, 'active');
		this.Objects.TriggerActive = null;
		
		this.Objects.Popup.parentNode.removeChild(this.Objects.Popup);
		this.Objects.Popup = null;
	},
	
	stopTimer: function () {
		clearTimeout(this.Objects.Timer);
		this.Objects.Timer = null;
	},

	getArtistId: function (trigger) {
 		return trigger.getAttribute('id').split(this.Settings.Triggers.ID_PREFIX)[1];
	},
		
	preloadImages: function (hash) {
		for (key in hash) {
			var img_obj = new Image;
			img_obj.src = hash[key];
		}
	},
	
	goToArtistPage: function (artist_id, new_window) {
		var url = this.Settings.Triggers.URL_PREFIX + artist_id;
		if (new_window) {
			window.open(url);
		} else {
			window.location = url;
		}
	},
	
	styleTriggers: function (trigger) {
		trigger.style.color = this.Settings.Triggers.styles.color;
		trigger.style.fontWeight = this.Settings.Triggers.styles.font_weight;
		trigger.style.borderBottom = this.Settings.Triggers.styles.border_bottom;
		trigger.style.cursor = this.Settings.Triggers.styles.cursor;
		
		var img = document.createElement('img');
		img.setAttribute('src', this.Settings.ImageAssets.ico_play);		
		img.style.padding = this.Settings.Triggers.styles.Icon.padding;
		img.style.display = this.Settings.Triggers.styles.Icon.display;
		trigger.appendChild(img);
	},
	
	
	
	/**
		@ UTILITY FUNCTIONS
	*/
	Utils: {
	
		hasClass: function (ele,cls) {
			return ele.className.match(new RegExp('(\\s|^)'+cls+'(\\s|$)'));
		},

		addClass: function (ele,cls) {
			if (!this.hasClass(ele,cls)) ele.className += " "+cls;
		},

		removeClass: function (ele,cls) {
			if (this.hasClass(ele,cls)) {
				var reg = new RegExp('(\\s|^)'+cls+'(\\s|$)');
				ele.className=ele.className.replace(reg,' ');
			}
		},

		// Dustin Diaz's getElementsByClass
		// http://www.dustindiaz.com/getelementsbyclass
		getElementsByClass: function (searchClass, node, tag) {
			var classElements = new Array();
			if (node == null) node = document;
			if (tag == null) tag = '*';
			var els = node.getElementsByTagName(tag);
			var elsLen = els.length;
			var pattern = new RegExp("(^|\\s)"+searchClass+"(\\s|$)");
			for (i = 0, j = 0; i < elsLen; i++) {
				if ( pattern.test(els[i].className) ) {
					classElements[j] = els[i];
					j++;
				}
			}
			return classElements;
		},

	  isIE6: function () {
			return ( document.all && (/msie 6./i).test(navigator.appVersion) && window.ActiveXObject ) ? true : false;
	  }
	
	}
	
};

// mts_addEvent(element, type, handler)
// written by Dean Edwards, 2005
// with input from Tino Zijdel - crisp@xs4all.nl
// http://therealcrisp.xs4all.nl/upload/addEvent_dean.html
// MTS.ToolTip changes:
// - added conditional wrapper
// - modified method names with 'mts_' prefix to avoid conflicts
if (!window.mts_addEvent || !mts_addEvent.guid) {
	
	function mts_addEvent (element, type, handler) {
		if (element.addEventListener) {
			element.addEventListener(type, handler, false);
		} else {
			if (!handler.$$guid) { handler.$$guid = mts_addEvent.guid++; }
			if (!element.events) { element.events = {}; }
			var handlers = element.events[type];
			if (!handlers) {
				handlers = element.events[type] = {};
				if (element['on' + type]) { handlers[0] = element['on' + type]; }
				element['on' + type] = mts_handleEvent;
			}
			handlers[handler.$$guid] = handler;
		}
	}
	
	mts_addEvent.guid = 1;

	function mts_removeEvent (element, type, handler) {
		if (element.removeEventListener) {
			element.removeEventListener(type, handler, false);
		} else if (element.events && element.events[type] && handler.$$guid) {
			delete element.events[type][handler.$$guid];
		}
	}

	function mts_handleEvent (event) {
		event = event || mts_fixEvent(window.event);
		var returnValue = true;
		var handlers = this.events[event.type];

		for (var i in handlers) {
			if (!Object.prototype[i]) {
				this.$$handler = handlers[i];
				if (this.$$handler(event) === false) { returnValue = false; }
			}
		}

		if (this.$$handler) { this.$$handler = null; }

		return returnValue;
	}

	function mts_fixEvent (event) {
		event.preventDefault = mts_fixEvent.preventDefault;
		event.stopPropagation = mts_fixEvent.stopPropagation;
		return event;
	}
	
	mts_fixEvent.preventDefault = function() {
		this.returnValue = false;
	};
	
	mts_fixEvent.stopPropagation = function() {
		this.cancelBubble = true;
	};
	
}


// This little snippet fixes the problem that the onload attribute on the body-element will overwrite
// previous attached events on the window object for the onload event
if (!window.addEventListener) {
	document.onreadystatechange = function() {
		if (window.onload && window.onload != mts_handleEvent) {
			mts_addEvent(window, 'load', window.onload);
			window.onload = mts_handleEvent;
		}
	};
}