// define a temporary shortcut for the jQuery function
var $J = jQuery;

// detect IE, and add an `ie` and `ie6` classes to the <html> element if found;
// this is used for various CSS & JS workarounds.
$J(document).ready(function () {
	if ( !! /MSIE/.test(navigator.userAgent) ) $J('html').addClass('ie');
	if ( !! /MSIE 6/.test(navigator.userAgent) ) $J('html').addClass('ie6');
});

//*** TMUtility Class *********************************************************/

var TMUtility = new function () {
	return {
		debug: false,
		start: new Date().getTime(),
		timestamp: function () {
			return (new Date().getTime() - this.start)+'ms';
		},
		trace: function () {
			// abort if not debugging
			if ( ! this.debug) return;
			
			// this is supposed to turn the `arguments` object into a proper
			// array, but doesn't appear to work..
			// var args = [].splice.call(arguments, 0);
			
			// collect the arguments as an array
			var args = [];
			var i = 0;
			while (i < arguments.length) args[i] = arguments[i++];
			
			// attempt to trace to the console
			try { console.log(args) } catch ($x) {}
			
			// create an element to trace into visually
			if ( ! $J('body #tm-util-trace').length )
				$J('<ul/>').attr({ id:'tm-util-trace' }).appendTo($J('body'));
			
			// add an item into the list
			$J('#tm-util-trace').prepend(
				$J('<li/>')
					.append( $J('<div/>').html( this.timestamp() ) )
					.append( $J('<span/>').html( args.join(' ') ) )
			);
		}
	};
}();

/*** TMMasthead Class *********************************************************/

var TMMasthead = function ($element, $data, $options) {
	this.initialize.apply(this, arguments);
}
$J.extend(TMMasthead, {
	PREFIX: 'tm-masthead-',
	DEFAULT_FADE_MS: 'slow' // 'fast'|'slow'|integer_ms
});
$J.extend(TMMasthead.prototype, {
	element: null,
	content: null,
	menu: null,
	imageMap: null,
	tabs: [],
	data: null,
	timer: null,
	index: 0,
	hover: null,
	initialize: function ($element, $data, $options) {
		TMUtility.trace('TMMasthead instance initialized');
		
		// store the data
		this.data = $data;
		
		// get the options
		$options = $options || {};
		if ( $options.cufon !== false ) $options.cufon = true;
		
		// set the `use-cufon` class, if applicable
		if ( ! $J('.ie').length && $options.cufon ) {
			$J('html').addClass('use-cufon');
		}
		
		// reference elements
		this.element = $J($element).bind('mouseenter mouseleave', { scope:this }, this.onMouseEvent);
		this.content = $J('.'+TMMasthead.PREFIX+'content', this.element);
		this.menu = $J('.'+TMMasthead.PREFIX+'menu', this.element);
		this.imageMap = $J('map', this.element);
		
		// set the `hover` flag
		this.hover = false;
		
		// create the tabs
		var tab, d;
		var tray = $J('.'+TMMasthead.PREFIX+'tray', this.element);
		for (var i=0; i<this.data.length; i++) {
			// reference the data
			d = this.data[i];
			
			// create the tab
			tab = new TMMastheadTab(tray, i, d.label, (d.byline || ''));
			tab.getElement().bind('tab-click', { scope:this }, this.onTabEvent);
			
			// select the first tab
			if ( ! i) tab.select();
			
			// mark the last tab
			if ( i == this.data.length-1 ) tab.getElement().addClass(TMMasthead.PREFIX+'last');
			
			// store the tab
			this.tabs[i] = tab;
		}
	
		// add the first promo
		this.showPromo(0);
	},
	showPromo: function ($index, $invocation) {
		// self-reference
		var o = this;
		
		// store the new index
		this.index = $index;
		
		// reference data
		var d = this.data[$index];
		
		TMUtility.trace([
			'showing promo of type `'+d.content.type+'`',
			'fade speed: '+(d.fade ? d.fade+'ms' : '(default)'),
			'duration: '+(d.duration ? d.duration+'ms' : '(default)')
		].join('; '));
		
		// set the invocation, if not specified
		$invocation = $invocation || 'rotation';
		
		// track the invocation
		this.track(d.label, $invocation);
		
		// clear the existing content & imagemap
		this.content.empty();
		this.clearImageMap();
		
		// destroy any timers
		if ( this.timer ) {
			this.timer.stop();
			this.timer.unbind('timer-update timer-complete', this.onTimerEvent);
			this.timer = null;
		}
		
		// re-reference the data
		d = d.content;
		
		// determine the content type
		switch (d.type) {
			case 'image':
				var a, img, area, css;
				
				// create the image
				img = $J('<img/>')
					.css({ opacity:0 })
					.bind('load', { scope:this }, this.onImageEvent)
					.attr({ src:d.src });
				
				// determine if this is anchor-linked, or image-mapped
				if ( d.map ) {
					// image-mapped; determine if <map>/<area> method will work
					// or if we need to create absolutely-positioned <a> tags
					if ( parseFloat($J().jquery) < 1.3) {
						// attach the image
						img.appendTo(this.content);
						
						// create <a> tags
						for (var i=0; i<d.map.length; i++) {
							// reference the data
							area = d.map[i];
							
							// this only works for shape=='rect'!
							if ( area.shape && area.shape != 'rect' ) continue;
							if ( area.coords.length != 4 ) continue;
							
							// create the CSS rules
							css = {
								left: area.coords[0],
								top: area.coords[1],
								width: area.coords[2] - area.coords[0],
								height: area.coords[3] - area.coords[1]
							}
							$J('<a/>')
								.addClass(TMMasthead.PREFIX+'mapped')
								.attr({ href:area.href, title:area.title||'' })
								.html( area.title||'&nbsp;')
								.css(css)
								.appendTo(this.content);
						}
					} else {
						// create <area> tags
						for (var i=0; i<d.map.length; i++) {
							// reference the data
							area = d.map[i];
							
							// loop, creating <area>s, starting by cloning the 
							// first `blank` <area> node.
							$J( $J('area', this.imageMap)[0] )
								.clone()
								.attr({ shape:area.shape||'rect', coords:area.coords.join(','), href:area.href, title:area.title||'' })
								.appendTo(this.imageMap)
						}
						
						// attach the image, assigning the map name
						img.attr({ usemap:'#'+TMMasthead.PREFIX+'content-map' }).appendTo(this.content);
					}
				} else {
					// anchor-linked; wrap the image in a link
					a = $J('<a/>')
						.addClass('image')
						.attr({ href:d.href })
						.append(img)
						.appendTo(this.content);
				}
				break;
			case 'flash':
				break;
			case 'html':
				break;
		}
	},
	clearImageMap: function () {
		$J('area', this.imageMap).each(function ($index) {
			if ( ! $index) return;
			$J(this).remove();
		});
	},
	track: function ($label, $event) {
		// update the tracking properties
		if (s_gi) {
			var s = s_gi('telusDev');
			s.linkTrackVars = 'eVar36';
			s.eVar36 = [$label, $event].join(' - ');

			// execute the tracking
			var sCode = s.tl(true, 'o', s.eVar36);
			if (sCode) document.write(sCode);
			
			// debugging
			// TMUtility.trace(['Omniture:', s.eVar36].join(' '));
		}
	},
	onTimerEvent: function ($e, $timer) {
		var o = $e.data.scope;
		switch ($e.type) {
			case 'timer-complete':
				// increment the index
				if (++o.index >= o.data.length) o.index = 0;
				
				// show the promo
				o.showPromo.apply(o, [o.index, 'rotation']);
				
				// update the tab selections
				$J.each(o.tabs, function ($index, $tab) {
					try {
						if ( this.index == o.index ) this.select();
						else this.deselect();
					} catch ($x) {}
				});
				break;
			case 'timer-update':
				// update the progress bars in the tab(s)
				$J.each(o.tabs, function ($index, $tab) {
					$tab.update($timer.ratio());
				});
				break;
		}
	},
	onImageEvent: function ($e) {
		var o = $e.data.scope;
		setTimeout(function () {
			$J($e.target)
				.css({ visibility: 'visible' })
				.animate(
					{ opacity:1 },
					o.data[o.index].fade||TMMasthead.DEFAULT_FADE_MS,
					function () { o.onFadeEvent.apply(o, [o.index]); }
				);
		}, 100);
		
		// debugging
		TMUtility.trace('&nbsp;&nbsp;image loaded; starting fade animation');
	},
	onFadeEvent: function ($index, $paused) {
		// create a new timer
		if ( this.timer ) {
			this.timer.stop();
			this.timer.unbind('timer-update timer-complete', this.onTimerEvent);
			this.timer = null;
		}
		this.timer = new TMMastheadTimer( this.data[$index].duration||null, this.hover );
		this.timer.bind('timer-update timer-complete', { scope:this }, this.onTimerEvent);
		
		// debugging
		TMUtility.trace('&nbsp;&nbsp;image faded up; starting timer');
	},
	onMouseEvent: function ($e) { 
		var o = $e.data.scope;
		switch ($e.type) {
			case 'mouseenter':
				TMUtility.trace('mouse entered masthead; pausing timer');
				o.hover = true;
				if (o.timer) o.timer.pause();
				break;
			case 'mouseleave':
				TMUtility.trace('mouse left masthead; resuming timer');
				o.hover = false;
				if (o.timer) o.timer.resume();
				break;
		}
	},
	onTabEvent: function ($e, $tab) {
		// reference the scope
		var o = $e.data.scope;
		
		switch ($e.type) {
			case 'tab-click':
				// update the tab selections
				$tab.select();
				$J.each(o.tabs, function ($index) {
					if ( this.index == $tab.index ) return;
					this.deselect();
				});
				
				// show the promo
				o.showPromo.apply(o, [$tab.index, 'click']);
				break;
		}
	}
});

/*** TMMastheadTab Class ******************************************************/

var TMMastheadTab = function ($container, $index, $label, $byline, $selected, $last) {
	this.initialize.apply(this, arguments);
}
$J.extend(TMMastheadTab, { NEXT_ID: 0 });
$J.extend(TMMastheadTab.prototype, {
	index: null,
	element: null,
	progress: null,
	classHover: TMMasthead.PREFIX+'tab-hover',
	classSelected: TMMasthead.PREFIX+'tab-selected',
	initialize: function ($container, $index, $label, $byline, $selected, $last) {
		// set the `selected` and `last` flags
		$selected = $selected || false;
		$last = $last || false;
		
		// store the index
		this.index = $index;
		
		// create the element
		this.element = $J('<div/>')
						.addClass(TMMasthead.PREFIX+'tab')
						.attr({ id:TMMasthead.PREFIX+'tab-'+(TMMastheadTab.NEXT_ID++) })
						.append( $J('<div/>')
							.addClass(TMMasthead.PREFIX+'inner')
							.append( $J('<h3/>').html($label) )
							.append( $J('<span/>').html($byline) )
						)
						.append( $J('<div/>').addClass(TMMasthead.PREFIX+'progress') )
						.appendTo($container)
						.bind('mouseenter mouseleave click', { scope:this }, this.onMouseEvent);
						
		// set special-case classes
		if ($selected) this.select();
		if ($last) this.element.addClass(TMMasthead.PREFIX+'tab-last');
				
		// Cufon replacement
		if ( $J('.use-cufon').length ) {
			// get really specific here to speed it up
			Cufon.replace('.'+TMMasthead.PREFIX+'menu .'+TMMasthead.PREFIX+'tray #'+this.element.attr('id')+' h3');
		}
	},
	getElement: function () {
		return this.element;
	},
	hover: function () {
		if ( this.element.hasClass(this.classHover) ) return;
		if ( this.element.hasClass(this.classSelected) ) return;
		this.element.addClass(this.classHover);
		this.cufon();
	},
	dehover: function () {
		if ( ! this.element.hasClass(this.classHover) ) return;
		this.element.removeClass(this.classHover);
		this.cufon();
	},
	select: function () {
		if ( this.element.hasClass(this.classSelected) ) return;
		this.dehover();
		$J('.'+TMMasthead.PREFIX+'progress', this.element).css({ width:221 });
		this.element.addClass(this.classSelected);
		if ( $J('.ie6').length ) {
			$J('<div/>')
				.addClass(TMMasthead.PREFIX+'arrow')
				.appendTo(this.element);
		}
		this.cufon();
	},
	deselect: function () {
		if ( ! this.element.hasClass(this.classSelected) ) return;
		this.element.removeClass(this.classSelected);
		$J('.'+TMMasthead.PREFIX+'arrow', this.element).remove();
		this.cufon();
	},
	update: function ($ratio) {
		if ( ! this.element.hasClass(this.classSelected)) return;
		$J('.'+TMMasthead.PREFIX+'progress', this.element).css({ width:((1 - $ratio) * 221) });
	},
	cufon: function () {
		if ( ! $J('.use-cufon').length ) return;
		Cufon.refresh('.'+TMMasthead.PREFIX+'menu .'+TMMasthead.PREFIX+'tray #'+this.element.attr('id')+' h3');
	},
	onMouseEvent: function ($e) {
		// reference scope
		var o = $e.data.scope;
		
		// debugging
		TMUtility.trace(['tab mouse event:', $e.type].join(' '));
		
		// act upon the event type
		switch ($e.type) {
			case 'mouseenter':
				if ( o.element.hasClass(this.classSelected) ) break;
				o.hover();
				o.element.trigger.apply(o.element, ['tab-over', o]);
				break;
			case 'mouseleave':
				o.dehover();
				o.element.trigger.apply(o.element, ['tab-out', o]);
				break;
			case 'click':
				if ( o.element.hasClass(this.classSelected) ) break;
				o.element.trigger.apply(o.element, ['tab-click', o]);
				break;
		}
	}
});

/*** TMMastheadTimer Class ****************************************************/

var TMMastheadTimer = function ($duration, $paused) {
	this.initialize.apply(this, arguments);
}
$J.extend(TMMastheadTimer, {
	NEXT_INDEX: 0,
	DEFAULT_INTERVAL_MS: 33,
	DEFAULT_DURATION_MS: 5000
});
$J.extend(TMMastheadTimer.prototype, {
	element: null,
	index: null,
	id: null,
	startAt: null,
	elapsed: 0,
	duration: null,
	paused: false,
	initialize: function ($duration, $paused) {
		var o = this;
		this.element = $J(document);
		this.index = TMMastheadTimer.NEXT_INDEX++;
		if ( ! $paused) this.id = setInterval(function () { o.onTimerEvent.apply(o); }, TMMastheadTimer.DEFAULT_INTERVAL_MS);
		this.duration = $duration || TMMastheadTimer.DEFAULT_DURATION_MS;
		this.paused = $paused || false;
		this.elapsed = 0;
		this.startAt = this.time();
	},
	bind: function () {
		this.element.bind.apply(this.element, arguments);
	},
	unbind: function () {
		this.element.unbind.apply(this.element, arguments);
	},
	time: function () {
		return new Date().getTime();
	},
	pause: function () {
		if (this.paused) return;
		this.paused = true;
		this.stop();
	},
	resume: function () {
		var o = this;
		this.paused = false;
		this.stop();
		this.startAt = this.time();
		this.id = setInterval(function () { o.onTimerEvent.apply(o); }, TMMastheadTimer.DEFAULT_INTERVAL_MS);
	},
	stop: function () {
		clearInterval(this.id);
		this.id = null;
	},
	ratio: function () {
		return Math.max(0, Math.min(1, (this.elapsed / this.duration)));
	},
	onTimerEvent: function ($scope) {
		// set the elapsed time
		// this.elapsed = (this.time() - this.startAt);
		this.elapsed += TMMastheadTimer.DEFAULT_INTERVAL_MS;
		
		// trigger an event
		this.element.trigger('timer-update', this);
		
		// check for completion
		if ( this.elapsed >= this.duration ) {
			this.element.trigger('timer-complete', this);
			this.stop();
		}
	}
});

/*** TMMastheadImage Class ****************************************************/

var TMMastheadImage = function ($data) {
	this.initialize.apply(this, arguments);
}
$J.extend(TMMastheadImage.prototype, {
	initialize: function () {
		
	}
});

/******************************************************************************/

// remove the shortcut
delete $J;
