/**
 * @fileoverview	exploreli.com Homepage Feature Module Javascript
 * @author			Michael Bester <mbester@schematic.com>
 * @version			1.0
 * @dependencies	jquery.js (v.1.3.x)
 *					jquery.jcarousel.js
 *					global.js
 */

XLI.FeatureModule = function() {

	/**
		A Flag to keep track of whether or not we've initialized this object.
	*/
	var initialized = false;

	/**
	 *	Set Some Constants
	 */
	var CONSTANTS = {

		// Featured Module ID
		MODULE_ID : "#feature",
		
		// The wrapper element for the whole carousel.
		CAROUSEL_VIEW_ID : "#featureCarousel",
		
		// Calendar Module ID
		CALENDAR_MODULE_ID : "featuredCalendar",
		
		// Carousel Selector
		CAROUSEL_SELECTOR : "ul.carousel",
		
		// Nav section class
		NAV_CLASS : "nav",
		
		// Carousel pager navigation class
		PAGER_CLASS : 'pager',
		
		// Wrapper class
		WRAPPER_CLASS : 'wrapper',
		
		// Content Map ID
		CONTENT_MAP_ID : 'contentMap',
		
		// Quick Read ID / Class Name
		QUICK_READ_ID_CLASS : 'quickRead',
		
		// Scrim class name
		SCRIM_CLASS : 'scrim',
		
		// Quick read content class name
		CONTENT_CLASS : 'content',

		// "next" class name
		NEXT_CLASS : 'next',
		
		// "previous" class name
		PREV_CLASS : 'prev',
		
		// Class for button disabled state
		DISABLED_CLASS : 'disabled',
		
		COUNTER_CLASS : 'counter',
		
		// Content Map drag handle class
		DRAG_HANDLE_CLASS : 'dragHandle',
		
		// Content Map dragging class, to be applied to the HTML element
		DRAGGING_CLASS : 'dragging',
		
		// Class to be applied to carousel slides only while animating. This is to work around a redraw bug on Firefox/PC
		ANIMATING_CLASS : 'animating',
		
		// overlay ajax target class
		CONTENT_TARGET_CLASS : 'contentTarget',
		
		// To indicate when a list nav item has a subnav.
		HAS_SUB_CLASS : 'hasSub',
		
		// Custom scrollbar container
		SCROLL_CONTAINER_CLASS : 'jScrollPaneContainer',
		
		// Section Reveal Scroll in/out Speed in milliseconds
		SCROLL_SPEED : 700,
		
		// animation speed of fading quickread in and out in milliseconds.
		QUICK_READ_SPEED : 200,
		
		// Text for quick read links
		QUICK_READ_TEXT : "Quick Read",
		
		// Quick read ad configuration
		QUICK_READ_AD_CONFIG : {
			width: 300,
			height: 250
		},

		// The number of slides at the end of the carousel which shouldn't be included in the total slide count
		SLIDE_OFFSET : 0
	
		
	};

	/**
	 *	Element References as returned by JQuery
	 *	references created as needed
	 */
	var $elements = {
		
		html : $('html'),

		// The Document
		document : $(document),
		
		// The featured module
		module : null,
		
		// Any scrollable panels in the module.
		scrollables : null,
		
		// The wrapper element for the whole carousel.
		carouselView : null,
		
		// Carousel List
		carousel : null,
		
		// Carousel Navigation
		pager : null,
		
		// Nav section
		nav : null,
		
		// the actual nav list
		navList : null,
		
		// Content Map
		contentMap : null,
		
		// Content Map Slices
		slices : [],
		
		// Content Map Drag handle
		dragHandle : null,
		
		// Quick Read
		quickRead : null,

		// Quick Read Nav 
		quickReadNav : null,

		// Quick Read Nav 
		quickReadNavNext : null,

		// Quick Read Nav 
		quickReadNavPrev : null,

		// Quick Read Content section
		quickReadContent : null,
		
		// Quick Read Close Button
		quickReadClose : null,
		
		// scrim behind quick read
		scrim : null
	};

	/**
	 *	Shorthand references to the CONSTANTS and $elements.
	 */
	var C	= CONSTANTS;
	var $e	= $elements;
	
	/**
		a handle for the carousel, its length and animation speed.
	*/
	var carousel = null,
		carouselLength,
		carouselAnimationSpeed;
	
	/**
		a handle for the carousel
	*/
	var quickReadIndex = 0;
	
	/**
		Intra-section animation flags
	*/
	var interstitialAnimation = false;
	var animateTransition = true;
	
	/**
	 * Sets up the homepage feature navigation, binding actions to each item.
	 * @private
	 * @returns nothing
	 */
	var initNav = function() {	
		
		// Find some elements we'll need.
		$e.scrollables	= $e.module.find('.' + XLI.Global.C.SCROLLABLE_CLASS);
		$e.carouselView = $(C.CAROUSEL_VIEW_ID);
		$e.nav			= $e.module.find('.' + C.NAV_CLASS);
		$e.navList		= $e.nav.find('>ul');
		
		// set a reference to the default item;
		var $def;
		
		// Set up actions on the top level links
		$e.navList.find('>li').each(function(i){
			var rel,
				sn,
				$lis,
				$this = $(this),
				$that = $this;
			this.$siblings = $this.siblings();
			this.$link = $this.find('>a');
			this.$subnav = $this.find('ul.sub');
			rel = this.$link.attr('rel');
			
			// Set up the default item.
			if ($this.hasClass('default')) {
				$def = $this;
			}
			
			// Find the associated content overlay.
			if (rel) {
				rel = $(rel);
				if (rel.length) {
					this.$content = rel;
					// add a close button to each overlay
					$(XLI.Global.C.FLYOUT_CLOSE_HTML)
						.bind('click', function(e){
							e.preventDefault();
							// revert back to the first item
							activateSection($def);
						})
						.prependTo(this.$content);
				}
			}
			
			// And associate the carousel container to its nav element
			if (this.$link.attr('href') === C.CAROUSEL_VIEW_ID) {
				this.carousel = true;
			}
			
			// Subnav specific actions here.
			if (this.$subnav.length) {
				
				//$this.addClass(C.HAS_SUB_CLASS);
				
				sn = this.$subnav.get(0);
				
				// Get the height of the subnav
				sn.h = this.$subnav.height();
				
				// So we can do the subnav slide in and out,
				// let's set up a wrapper which we can use to hide it.
				this.$subnav.css('position','relative');
				this.$subnavWrapper = $('<div></div>')
											.css({
												'position' : 'relative',
												'overflow' : 'hidden',
												'z-index' : '10'
											})
											.insertBefore(this.$subnav)
											.append(this.$subnav)
											.hide();
											
				// get the subnav list elements
				$lis = this.$subnav.find('li');
				
				// Set up actions on any subnav links.
				this.$subnav.find('a[href!=#]').each(function(i){
					
					if (!rel) {
						return;
					}
					var $this = $(this),
						that = this;
					
					// get the subnav list elements
					this.$parentLis = $lis;
					
					// find where we'll be dropping new data	
					this.$contentTarget = rel.find('.' + C.CONTENT_TARGET_CLASS);
					this.$scrollWrapper = rel.find('.' + XLI.Global.C.SCROLLABLE_CLASS);
					this.$contentTargetContainer = rel.find('.' + C.SCROLL_CONTAINER_CLASS);
					
					$this.bind('click', function(e){
						e.preventDefault();
						
				 		// turn off auto scrolling
			   		        carousel.userInteractedWithCarousel();

						// deactivate any other active elements in this subnav
						that.$parentLis.removeClass(XLI.Global.C.ACTIVE_CLASS);
						// and add active to this one
						$this.parent().addClass(XLI.Global.C.ACTIVE_CLASS);
						
						// Go and get new content
						try {
							var url = $this.attr('href');
                                                        var callback = "";
          
                                                        //if we're requesting from entriq, the function name cannot be dynamic--it's always "ws_results".
                                                        if(url.indexOf("entriq.net") >= 0) {
                                                                callback = "ws_results";
                                                        } else {
                                                                url = url + ((url.indexOf("?") >= 0) ? "&" : "?") + "callback=featuredModuleDataCallback";
                                                                callback = "featuredModuleDataCallback";
                                                        }
                
                                                        $.jsonp({
                                                                url: url,
                                                                callback: callback,
								cache: true,
								beforeSend : function() {
									that.$contentTarget.empty();
									that.$contentTargetContainer.addClass(XLI.Global.C.LOADING_CLASS);
									that.$scrollWrapper.jScrollPane();
								},
								success : function(data) {
									// that.$contentTarget.html(data);
									XLI.VideoJsonHandler.parse(data).appendTo(that.$contentTarget);
									that.$contentTargetContainer.removeClass(XLI.Global.C.LOADING_CLASS);

									// Fix for IE6 hover states
									XLI.Global.ie6Hover('li', that.$contentTarget);
									
									// delay this a bit because too many things are happening at once									
									setTimeout(function() {
										that.$scrollWrapper.jScrollPane();
										$e.document.trigger("section:dataLoaded");
									}, 50);
								},
								error : function(xhr, message) {
									XLI.Debug.error("Error getting new content for feature module overlay: " + message);
									that.$contentTargetContainer.removeClass(XLI.Global.C.LOADING_CLASS);
								} 
							});
						} catch(e) {
							XLI.Debug.error("Error getting new content for feature module overlay: " + e.message);
							that.$contentTargetContainer.removeClass(XLI.Global.C.LOADING_CLASS);
						}
					});
					
				});
				
				// Set up actions on any subnav items with sub items
				this.$subnav.children('li').children('a').bind('click',function(e){
					var $this = $(this);
					
					// side close any open subsubs
					$this.parent().siblings().find('.subsub').slideUp();
					// open current subsub
					$(this).siblings('.subsub').slideDown();
					e.preventDefault();
				});
			}
			
			this.$link.bind('click', function(e) {
				e.preventDefault();
				var $this = $(this);
				if (!$that.hasClass(XLI.Global.C.ACTIVE_CLASS)) {
					mediateSectionLoaded($this);
					activateSection($that, animateTransition);
				} else {
					mediateSectionLoaded($def);
					activateSection($def, animateTransition);
				}
			});
		});
		
		// override the default if need be - this is a dummy fallback
		if (!$def) {
			$def = $e.navList.find('>li:first');
		}
	};
	
	/**
	 * Initializes any section-specific code once the subnav
	 * ajax call completes
	 * @private
	 * @params {jquery node} $contentTarget
	 * @returns nothing
	 */
 	var mediateSectionLoaded = function($link) {
		switch ($link.attr("rel")) {
			case "#featuredVideos" :
				XLI.LatestVideos.open();
			break;

			case "#featuredPhotos" :
				XLI.LatestVideos.cleanupPlayer();
			break;

			default:
			
			break;
		}
	};

	/**
	 *	Initial Panel lifted from hash tag in location
	 */
	var initialIndex = window.location.toString().match(/feature(\d+)/);
        
        if(initialIndex !== null) {
        	initialIndex = jQuery.jcarousel.intval(initialIndex[1]);
        } else {
        	initialIndex = 1;
    	}

	/**
	 * Sets up the feature carousel
	 * @private
	 * @returns nothing
	 */
	var initCarousel = function() {
		
		// Find some elements we'll need.
		$e.carousel		= $e.carouselView.find(C.CAROUSEL_SELECTOR);
		
		if (!$e.carousel.length || !$e.nav.length) {
			return;
		}

		$e.carousel.jcarousel({
			pagerTarget : $e.nav,
			pagerHTML : '',
			addJumpLink : true,
			auto: 0,
			autoDetectScroll: true,
			initCallback : primeUnloadedContent,
			itemVisibleInCallback : {
				onBeforeAnimation: null, //activateSlice,
				onAfterAnimation: renderAd
			},
			buttonNextHTML: '<span></span>',
			buttonPrevHTML: '<span></span>',
			/**
			itemVisibleOutCallback : {
				onBeforeAnimation: inactivateSlice,
				onAfterAnimation: null
			},
			*/
			itemFirstInCallback : updateCounter,
			itemLastInCallback : addUnloadedSlide,
			classIR : "ir",
			classSlideTitleSuffix : "",
			firstSlideElement : "category",
			containsAds : false	// This is a misnomer. It's just here to prevent the overriding of the itemVisibleInCallback

		});
		
                $e.carousel.find(".item a").live("click", function(e) {
                    var t = jQuery(this);

		    // turn off auto scrolling
		    carousel.userInteractedWithCarousel();
                    
                    if(t.hasClass("quickRead"))
                        return;
                    
                    e.preventDefault();
                         
                    window.location.hash = "#feature" + carousel.first;
                    window.location.href = $(this).attr("href");
                });
		
                carousel.pager = $("#feature .nav .pager");
                carousel.jumpLink = carousel.pager.find(".jump a");
                carousel.buttonNext = carousel.pager.find("." + C.NEXT_CLASS).parent();
                carousel.buttonPrev = carousel.pager.find("." + C.PREV_CLASS).parent();
		
		carousel.buttonsBound = false;
		carousel.buttons();
	};
	
	/**
	 * Sets up the carousel to load additional content when a user begins to interact with its navigation
	 * Set up as a callback from the carousel initialization.
	 * @private
	 * @param {Object} carousel Describe this parameter
	 * @returns nothing
	 */
	var primeUnloadedContent = function(karousel) {
		
		carousel = karousel;
		
		// Prime preloaded content
		$e.carousel.find('>li').each(function(i){
			var $this = $(this),
				$summary = $this.find('.summary');
				
			// Set up the quick read
			addQuickRead($this);
		});
		
		// Set up the content map
		// buildContentMap();
		
		// Find the pager
		$e.pager = $e.nav.find('.' + C.PAGER_CLASS);
		
		// Set up to load unloaded content when a user moves their mouse over the feature.
		// $e.module.bind('mouseover', loadUnloadedContent);
		
		// Activate the first item when everything else is done
		$e.document.bind('load.activateFeatureOverlay', function(){
			var $li = $e.navList.find('li'),
				$link = $li.eq(0).find('>a:first'),
				otherActive = false;
				
			$li.each(function(){
				if ($(this).hasClass(XLI.Global.C.ACTIVE_CLASS)) {
					otherActive = true;
				}
			});
			if (!otherActive) {
				$link.trigger('click');
			}
		});
		
		/**
			Record the total carousel length.
		*/
		carouselLength = carousel.size();
		if ($.isArray(XLI.FeatureModule.unloadedSlideData)) {
			carouselLength += XLI.FeatureModule.unloadedSlideData.length;
		}
		
		/**
			Add a counter to the pager.
		*/
		$e.counter = $("li.counter");	
		
		// Load all items if the initial item is not at the beginning
		if(initialIndex > 1) {
                        var l = XLI.FeatureModule.unloadedSlideData.length;
                        
                        for(var i=1; i<=l; i++) {
                          addUnloadedSlide(karousel);
                        }
    		
    		// Scroll (without animation) to new index
            setTimeout(function() {
                carousel.scroll(initialIndex, false);
            },100);
        }	
	};
	
	var addUnloadedSlide = function(karousel, item, index, state) {
		if (!$.isArray(XLI.FeatureModule.unloadedSlideData) || XLI.FeatureModule.unloadedSlideData.length === 0 || carousel === null) {
			return;
		};

/*
		var scr = parseInt(carousel.options.scroll, 10),
			size = carousel.size(),
			$slide, data, x;
			
		if ((scr === 1) ?
			(size - scr) === index :
			(size === carousel.last)) {

			for (x = 0; x < scr; x++) {
				data = XLI.FeatureModule.unloadedSlideData.shift();

				if (data.displayAd) {
					carousel.appendAdvertisement();
				} else {
					$slide = carousel.appendSlide(data);
					$slide.data('slideData', data);
					addQuickRead($slide);
				}
			}
		}
*/
		
		var scr = parseInt(carousel.options.scroll, 10),
			$slide, data, x;
			
		for (x = 0; x < scr; x++) {
			data = XLI.FeatureModule.unloadedSlideData.shift();

			if(typeof data === 'undefined')
				return;

			if (data.displayAd) {
				carousel.appendAdvertisement(data);

			} else {
				$slide = carousel.appendSlide(data);
				$slide.data('slideData', data);
				addQuickRead($slide);
			}
		}
	};
	
	var updateCounter = function() {
		$e.counter.text(carousel.first /* + "–" + carousel.last */ + " of " + (carouselLength - parseInt(C.SLIDE_OFFSET, 10)));
	};
	
	/**
	 * Loads the unloaded content into the carousel. Also kills the events that call this function, as it only needs to run once
	 * @private
	 * @returns nothing
	 */
	var loadUnloadedContent = function() {
		if (!$.isArray(XLI.FeatureModule.unloadedSlideData) || carousel === null) {
			return;
		};
		
		// Unbind this behavior, cause we only need it once
		$e.module.unbind('mouseover', loadUnloadedContent);
		// And remove the initCallback from the carousel to prevent recursively calling this function
		carousel.initCallback = null;
		
		$.each(XLI.FeatureModule.unloadedSlideData, function() {
			var $slide;
			if (this.displayAd) {
				carousel.appendAdvertisement();
			} else {
				$slide = carousel.appendSlide(this);
				$slide.data('slideData', this);
				addQuickRead($slide);
			}
		});
	};
	
	/**
	 * Sets up the HTML for the content map, properly adding and adjusting sections for each slide 
	 * @private
	 * @returns nothing
	 */
	var buildContentMap = function() {
		if (!$e.carouselView.length) {
			return;
		}
		
		var HTML = {
				DIV : "<div></div>"
			},
			slides = [],
			wideSlides = [],
			cmWidth,
			availWidth,
			adjustedAvailWidth = 0,
			cols,
			ww, wn,
			handleWidth = 0;
		
		// Create a skeleton structure of all slides, both rendered...
		$e.carousel.find('>li').each(function(i){
			var $this = $(this);
			slides.push({
				'title' : $this.find('.title').text(),
				'width' : (($this.hasClass('wide') || $this.hasClass('ad')) ? "wide" : ""),
				'displayAd' : $this.hasClass('ad')
			});
		});
		// ...and unrendered.
		$.each(XLI.FeatureModule.unloadedSlideData, function(){
			slides.push({
				'title' : this.title,
				'width' : ((this.displayAd ? 'wide' : false) || this.slideWidth || ""),
				'displayAd' : this.displayAd || false
			});
		});
		
		// Create and append the Content Map Wrapper
		$e.contentMap = $(HTML.DIV)
							.attr('id', C.CONTENT_MAP_ID)
							.appendTo($e.carouselView);
		
		// Append slides to the map
		cmWidth = $e.contentMap.width();
		// Available width (subtract slide left and right margins - in this case 2 px total)
		availWidth = cmWidth - (slides.length * 2);
		// find out how many wide slides we have
		wideSlides = $.grep(slides, function(slide){
			return slide.width === "wide";
		});
		// Find out how many columns we have in our underlying grid of wide and narrow slides.
		cols = (wideSlides.length * 2) + (slides.length - wideSlides.length);
		// and set up our widths.
		wn = Math.round(availWidth / cols);
		ww = Math.round((availWidth / cols) * 2);
		
		// Now let's add the sections
		$.each(slides, function(i){
			var $slice = $(HTML.DIV)
							.attr('carouselIndex', i + 1)
							.css('width', (this.width === "wide" || this.displayAd) ? ww : wn)
							.appendTo($e.contentMap);
							
			if (this.displayAd) {
				$slice.attr('title', 'advertisement');
			} else if (this.title) {	
				$slice.attr('title', this.title);
			}

			adjustedAvailWidth += (this.width === "wide" || this.displayAd) ? ww : wn;
			
			// Save a reference to each slice
			$e.slices.push($slice);
		});
		
		// Add the margins back in
		adjustedAvailWidth += (slides.length * 2);
		
		// Adjust available width and bind a click event to the slices
		$e.contentMap
			.css({
				'width' : adjustedAvailWidth,
				'marginLeft' : (parseInt($e.contentMap.css('marginLeft'), 10) - ((adjustedAvailWidth - cmWidth) / 2))
			})	
			.bind('click', function(e){
				var $slice = $(e.target),
					ci = parseInt($slice.attr('carouselIndex'), 10);
					
				if (isNaN(ci)) {
					return;
				}
				
				carousel.scroll(ci);
			});
			
	};
	
	/**
	 * Activates a slice in the content map
	 * Called as a callback from the carousel action
	 * @private
	 * @param {Object} karousel A reference to the carousel object
	 * @param {Object} item The active slide
	 * @param {Number} index The 1-based index of the active slide
	 * @returns nothing
	 */	
	var activateSlice = function(karousel, item, index) {
		var i = index - 1;
		if (typeof $e.slices[i] === 'undefined' || !$e.slices[i].length) {
			return;
		}
		$e.slices[i].addClass(XLI.Global.C.ACTIVE_CLASS);
		if (!XLI.Global.ie6) {
			updateDragHandle();
		}
	};
	
	/**
	 * Deactivates a slice in the content map
	 * Called as a callback from the carousel action
	 * @private
	 * @param {Object} karousel A reference to the carousel object
	 * @param {Object} item The active slide
	 * @param {Number} index The 1-based index of the active slide
	 * @returns nothing
	 */
	var inactivateSlice = function(karousel, item, index) {
		var i = index - 1;
		if (typeof $e.slices[i] === 'undefined' || !$e.slices[i].length) {
			return;
		}
		$e.slices[i].removeClass(XLI.Global.C.ACTIVE_CLASS);
		if (!XLI.Global.ie6) {
			updateDragHandle();
		}
	};
	
	/**
	 * Simply a bridge to call the carousel's native renderAd() method
	 * @private
	 * @param {String|Object|Array|Boolean|Number} paramName Describe this parameter
	 * @returns nothing
	 */
	var renderAd = function(karousel, item, index) {
		if (!karousel || typeof karousel.renderAd !== 'function') {
			return;
		}
		karousel.renderAd(karousel, item, index);
	};
		
	/**
	 * Creates the drag handle for the content map if its not available.
	 * @private
	 * @returns nothing
	 */
	var createDragHandle = function() {
		$e.dragHandle = $('<div></div>')
							.addClass(C.DRAG_HANDLE_CLASS)
							.bind('mousedown', startDrag)
							.appendTo($e.contentMap);
	};
	
	/**
	 * Updates the position and width of the drag handle to wrap around the active slices in the content map
	 * @private
	 * @returns nothing
	 */
	var updateDragHandle = function() {
		if ($e.dragHandle === null) {
			createDragHandle();
		}
		
		var padding = 2,
			width = 0,
			elFirst,
			pos,
			x,
			prevActive = false,
			$curr;
		
		for (x = 0; x < $e.slices.length; x++) {
			$curr = $e.slices[x];
			if ($curr.hasClass(XLI.Global.C.ACTIVE_CLASS)) {
				var el = $curr.get(0);
				el.pos = el.pos || $curr.position();
				el.offset = el.offset || $curr.offset();
				el.ow = el.ow || $curr.outerWidth(true);
				el.iw = el.iw || $curr.outerWidth();
				width += el.ow;
			}
		}
		
		elFirst = $e.slices[carousel.first - 1].get(0);
		
		$e.dragHandle.css({
			width : width + padding,
			left : elFirst.pos.left - (elFirst.ow - elFirst.iw) - padding
		});
		
	};
	
	/**
	 * Initializes dragging actions on the content map.
	 * @private
	 * @returns nothing
	 */
	var startDrag = function() {
		$e.html
			.addClass(C.DRAGGING_CLASS)
			.bind('mouseup', endDrag)
			.bind('mousemove', updateDrag);
		if ($.browser.msie) {
			$e.html
				.bind('dragstart', ignoreNativeDrag)
				.bind('selectstart', ignoreNativeDrag);
		}
		
		// Save the original animation speed.
		carousel.originalAnimation = carousel.originalAnimation || carousel.options.animation;
		// and turn off the animation for dragging.
		carousel.options.animation = 0;
		
		return false;
	};
	
	/**
	 * Cleans up after content map dragging action.
	 * @private
	 * @returns nothing
	 */
	var endDrag = function() {
		$e.html
			.removeClass(C.DRAGGING_CLASS)
			.unbind('mouseup', endDrag)
			.unbind('mousemove', updateDrag);
		if ($.browser.msie) {
			$e.html
				.unbind('dragstart', ignoreNativeDrag)
				.unbind('selectstart', ignoreNativeDrag);
		}
		// Revert the animation to its original speed.
		carousel.options.animation = carousel.originalAnimation;
	};
	
	/**
	 * Updates the carousel while dragging through the content map..
	 * @private
	 * @returns nothing
	 */
	var updateDrag = function(e) {
		
		$.each($e.slices, function(i){
			
			var el = this.get(0);
			el.offset = el.offset || this.offset();
			el.ow = el.ow || this.outerWidth(true);
			el.iw = el.iw || this.outerWidth();
			
			if (e.pageX > el.offset.left && e.pageX < (el.offset.left + el.iw)) {
				carousel.scroll(i + 1);
				return false;
			}
		});
	};
	
	/**
	 * Nothing more than a dummy function for IE compatibility while dragging.
	 * @private
	 * @returns false
	 * @type Boolean
	 */
	var ignoreNativeDrag = function() {
		return false;
	};
	
	/**
	 * Activates an overlay in the homepage feature.
	 * @private
	 * @param {Object} $nav jQuery object representing one of the homepage feature nav items 
	 * @param {Boolean} animate Whether or not to animate the transition. 
	 * @returns nothing
	 */
	var activateSection = function($nav, animate) {
		if (typeof $nav !== 'object' || typeof $nav.jquery !== 'string' || interstitialAnimation) {
			return;
		}
		
		animate = (typeof animate === 'boolean') ? animate : true;
		
		var nav = $nav.get(0);
		
		try {
			// First deactivate others
			nav.$siblings.each(function(){
				var $this = $(this);
				toggleSection($this, false);
				$this.removeClass(XLI.Global.C.ACTIVE_CLASS);
				// TODO : if we're playing a video, stop or kill it.
			});
			// Then open the active one
			$nav.addClass(XLI.Global.C.ACTIVE_CLASS);
			toggleSection($nav, true, animate);
			
			// if we have a subnav without a preselected item, go ahead and preselect the first subnav.
			if (nav.$subnav) {
				if (!nav.$subnav.find('li.active').length) {
					nav.$subnav.find('a[href!=#]:first').trigger('click');
				}
			}
			
		} catch(e) {
			XLI.Debug.error('Homepage feature nav not initialized correctly. Can\'t activate section: ' + e.message);
		}
	};
	
	/**
	 * Animates overlays into and out of view in the homepage feature.
	 * @private
	 * @param {Object} $nav jQuery object representing one of the homepage feature nav items 
	 * @param {Boolean} open true when you're opening a section, false when you're closing it.  
	 * @returns nothing
	 */
	var toggleSection = function($nav, open, animate) {
		
		var height,
			subnavHeight,
			navWrapperEl = $e.nav.get(0),
			navListEl = $e.navList.get(0),
			navEl = $nav.get(0),
			modEl = $e.module.get(0),
			contentEl;
			
		animate = (typeof animate === 'boolean') ? animate : true;
			
		// Make note of the original dimensions of the module and nav elements
		modEl.oHeight	= modEl.oHeight || parseInt(($e.module.css('height') === 'auto') ? ($e.module.outerHeight() - parseInt($e.module.css('padding-top'), 10) - parseInt($e.module.css('padding-bottom'), 10)) : $e.module.css('height'), 10);
		modEl.vPad		= modEl.vPad || $e.module.outerHeight() - modEl.oHeight;
		
		navWrapperEl.top		= navWrapperEl.top || parseInt($e.nav.css('top'), 10);
		navWrapperEl.padTop		= navWrapperEl.padTop || parseInt($e.nav.css('padding-top'), 10);
		navWrapperEl.padBot		= navWrapperEl.padBot || parseInt($e.nav.css('padding-bottom'), 10);
		navWrapperEl.w 			= navWrapperEl.w || $e.nav.outerWidth();
		navWrapperEl.oHeight	= navWrapperEl.oHeight || $e.nav.height();
		
		//navListEl.top			= navListEl.top || parseInt($e.navList.css('top'), 10);
		
		try {
			
			if ((typeof navEl.$content !== 'undefined' && navEl.$content.length) || navEl.carousel) {
			
				// make note of some dimensions.
				if (typeof navEl.$content !== 'undefined' && navEl.$content.length) {
					contentEl = navEl.$content.get(0);
					contentEl.padTop = contentEl.padTop || parseInt(navEl.$content.css('padding-top'), 10);
					contentEl.padBot = contentEl.padBot || parseInt(navEl.$content.css('padding-bottom'), 10);
				}
			
				// tell everyone that we're animating
				interstitialAnimation = true;
			
				// Get the current section height.
				height = (navEl.carousel) ? modEl.oHeight : navEl.$content.outerHeight();
				
				if (open) {
						
						// Get the module height to where it needs to be.
						$e.module
							//.css({
							//	'overflow': 'hidden'
							//})
							.animate({
								'height' : (navEl.carousel) ? height : height - modEl.vPad
							}, C.SCROLL_SPEED, 'easeOutQuad');
					
						// Reveal the section...
						if (navEl.carousel) {
							// the carousel
								$e.carouselView
									.show()
									.css({
										'z-index' : 40
									})
									.animate({
										'opacity' : 1
									}, (C.SCROLL_SPEED / 3), 'linear', function(){
										interstitialAnimation = false;
									});
						} else {
							// An overlay
							navEl.$content
								.show()
								.css({
									'top' : height - (height * 2),
									'visibility' : 'visible',
									'z-index' : 40,
									'opacity' : 1
								});

							// Animate the content into place
							navEl.$content
								.animate({
									'top' : 0
								}, C.SCROLL_SPEED, 'easeOutQuad', function(){
								//	$e.module.css('overflow', 'visible');
									$e.document.trigger("section:openFinished");
									interstitialAnimation = false;
								});
						}
					
						// ...and its subnav...
						if (typeof navEl.$subnav !== 'undefined' &&
							navEl.$subnav.length &&
							typeof navEl.$subnavWrapper !== 'undefined' &&
							navEl.$subnavWrapper.length
						) {

							var snEl = navEl.$subnav.get(0);
							snEl.h = snEl.h || navEl.$subnav.outerHeight(true);

							// Set the top margin of the subnav and animate it
							navEl.$subnav
									.css({
										'top' : snEl.h - (snEl.h * 2)
									})
									.show()
									.animate({
										'top' : 0
									}, C.SCROLL_SPEED, 'easeOutQuad');


							// animate the height of the wrapper
							navEl.$subnavWrapper
									.show()
									.css({
										'height' : 0
									})
									.animate({
										'height' : snEl.h
									}, C.SCROLL_SPEED, 'easeOutQuad', function(){
										if (XLI.Global.ie6 || XLI.Global.ie7) {
											$(this).css("overflow","visible").css("height","auto").css("zoom","1");
										} else {
											$(this).css("overflow","visible").css("height","auto");
										}
									});
						}
						
						// ...as well as the nav elements
						$e.nav.animate({
							'height'	: (navEl.carousel) ? navWrapperEl.oHeight : height - contentEl.padTop - contentEl.padBot - navWrapperEl.padTop - navWrapperEl.padBot,
							'top'		: (navEl.carousel || (navEl.$content && navEl.$content.attr('id') === C.CALENDAR_MODULE_ID)) ? navWrapperEl.top : contentEl.padTop,
							'left'		: (navEl.$content && navEl.$content.attr('id') === C.CALENDAR_MODULE_ID) ? (navWrapperEl.w - (navWrapperEl.w * 2)) : 0
						}, C.SCROLL_SPEED, 'easeOutQuad');
						
//						$e.navList.animate({
//							'top'		: ((navEl.carousel || (navEl.$content && navEl.$content.attr('id') === C.CALENDAR_MODULE_ID)) ? navListEl.top : contentEl.padTop + navWrapperEl.padTop)
//						}, C.SCROLL_SPEED, 'easeOutQuad');
				
				} else {
					// if we're closing a section.
					// Hide the overflow so we can scroll up nicely.
					//$e.module.css('overflow', 'hidden');
					
					// Hide the section...
					if (navEl.carousel) {
						// the carousel
						$e.carouselView
							.css({
								'z-index' : 39
							})
							.animate({
								'opacity' : 0
							}, (C.SCROLL_SPEED / 3), 'linear', function(){
								$e.carouselView.hide();
							});
					} else {
						if (navEl.$content.attr("id") === "featuredVideos") {
							$e.document.trigger("section:close");
						}
						navEl.$content
							.css({
								'z-index' : 39
							})
							.animate({
								'opacity' : 0
							}, (C.SCROLL_SPEED / 3), 'linear', function(){
								navEl.$content.hide();
							});
							//.animate({
							//	'top' : height - (height * 2)
							//}, C.SCROLL_SPEED, 'easeInQuad'	, function(){
							//	navEl.$content.css({
							//		'visibility' : 'hidden'
							//	});
							//});
					}
					
					// and close its subnav
					if (typeof navEl.$subnav !== 'undefined' &&
						navEl.$subnav.length &&
						typeof navEl.$subnavWrapper !== 'undefined' &&
						navEl.$subnavWrapper.length
					) {

						var snEl = navEl.$subnav.get(0);
						snEl.h = snEl.h || navEl.$subnav.outerHeight(true);

						// Set the top margin of the subnav and animate it
						navEl.$subnav
								.animate({
									'top' : snEl.h - (snEl.h * 2)
								}, C.SCROLL_SPEED, 'easeOutQuad');


						// animate the height of the wrapper
						navEl.$subnavWrapper
								.animate({
									'height' : 0
								}, C.SCROLL_SPEED, 'easeOutQuad', function(){
									navEl.$subnavWrapper.hide();
								});
					}
				}
			} else {
				if (open) {
					// We're going back to the carousel (no overlay)
					$e.module.animate({
						'height' : modEl.oHeight
					}, C.SCROLL_SPEED, 'easeInQuad', function(){	
						interstitialAnimation = false;
					});
				}
			}
		
		} catch(e) {
			XLI.Debug.error("Couldn't activate homepage feature overlay: " + e.message);
		}
	};
	
	/**
	 * Adds a quickread link to a carousel slide and attaches the appropriate actions.
	 * @private
	 * @param {Object} $slide A jQuery object representing the slide to which you want to add quick read
	 * @returns nothing
	 */
	var addQuickRead = function($slide) {
		if (typeof $slide !== 'object' || typeof $slide.jquery !== 'string') {
			return;
		}
		
		// Construct slide data object is need be.
		var data = $slide.data('slideData') || {
			title				: $slide.find('.title').text(),
			category : {
				title	: $slide.find('.category').text(),
				link	: $slide.find('.category').attr('href')
			},
			time				: $slide.find('.time').text(),
			quickReadImageURL	: $slide.find('input[name=quickReadImageURL]').attr('value'),
			link				: $slide.find('.title a').attr('href'),
			breakingNews		: (($slide.find('.breaking').length) ? true : false),
			commentCount		: (parseInt($slide.find('.comments').text(), 10)),
			media : {
				photos	: $slide.find('.typePhoto').attr('href'),
				video	: $slide.find('.typeVideo').attr('href')
			},
			blurb				: $slide.find('.summary').text()
		};
		
		// Add actions links if necessary
		if (typeof data.actions === 'undefined') {
			data.actions = {};
			$slide.find('input.action[type=hidden]').each(function(){
				var $this = $(this);
				data.actions[$this.attr('name')] = $this.attr('value');
			});
		}
		
		$slide.data('slideData', data);
		
			
		var $links;
		var $qrLink = $slide.find('.links a');

		if($qrLink.length > 0) {
			$qrLink.bind('click', function(e){
					e.preventDefault();
					showQuickRead();
					quickReadIndex = parseInt($slide.attr('carouselindex'), 10);
					populateQuickRead(data);
					});		
		} else {
			$qrLink = $('<a></a>')
						.attr('href', '#')
						.text(C.QUICK_READ_TEXT)
						.bind('click', function(e){
							e.preventDefault();
							showQuickRead();
							quickReadIndex = parseInt($slide.attr('carouselindex'), 10);
							populateQuickRead(data);
						});
		
			$links = $slide.find('p.links');
			if (!$links.length) {
				$('<p></p>')
					.addClass('links')
					.append($qrLink)
					.insertAfter($slide.find('h3'));
			} else {
				$links.prepend($qrLink);
			}		
		}		
	};
	
	/**
	 * Builds the framework HTML for the Quick Read overlay
	 * @private
	 * @returns nothing
	 */
	var buildQuickRead = function() {
		
		if ($e.quickRead && $e.quickRead.length) {
			return;
		}
		
		var HTML = {
			DIV : "<div></div>",
			SPAN : "<span></span>",
			UL : "<ul></ul>",
			LI : "<li></li>",
			A : "<a></a>"
		};
		
		$e.quickRead = $(HTML.DIV).attr('id', C.QUICK_READ_ID_CLASS);
		$e.quickReadContent = $(HTML.DIV)
								.addClass(C.CONTENT_CLASS)
								.appendTo($e.quickRead);
								
		$e.quickReadClose = $(XLI.Global.C.FLYOUT_CLOSE_HTML)
								.bind('click', hideQuickRead)
								.appendTo($e.quickReadContent);
								
								
		// Create the pager
		$e.quickReadNav = $(HTML.UL)
								.addClass(C.PAGER_CLASS)
								.appendTo($e.quickRead);
								
		$e.quickReadPrev = $(HTML.LI)
								.addClass(C.PREV_CLASS)
								.append(
									$(HTML.A)
										.addClass(XLI.Global.C.IR_CLASS)
										.addClass(C.PREV_CLASS)
										.text('\u2190') // Unicode left arrow
										.append(HTML.SPAN)
										.bind('click', quickReadPrev)
								)
								.appendTo($e.quickReadNav);
								

		$e.quickReadNext = $(HTML.LI)
								.addClass(C.NEXT_CLASS)
								.append(
									$(HTML.A)
										.addClass(XLI.Global.C.IR_CLASS)
										.addClass(C.NEXT_CLASS)
										.text('\u2192') // Unicode Right arrow
										.append(HTML.SPAN)
										.bind('click', quickReadNext)
								)
								.appendTo($e.quickReadNav);
		
		// Fortify IE6						
		XLI.Global.ie6Hover($e.quickReadClose);
		XLI.Global.ie6Hover($e.quickReadPrev.find('span'));
		XLI.Global.ie6Hover($e.quickReadNext.find('span'));
	
		// Create and append the scrim
		$e.scrim = $(HTML.DIV)
						.addClass(C.SCRIM_CLASS)
						.css({
							'opacity' : 0,
							'height' : $e.module.outerHeight()
						})
						.appendTo($e.module);
		
		// Add the quickread element
		$e.quickRead.appendTo($e.module);
	};
	
	/**
	 * Loads the next carousel slide content into the quick read view. Also scrolls carousel to related slide.
	 * @private
	 * @param {Object} e Event
	 * @returns Nothing
	 */
	var quickReadNext = function(e) {
		e.preventDefault();
		if (quickReadIndex === carousel.size()) {
			return;
		}
		
		var nextIndex = (quickReadIndex + 1),
			$slide = carousel.list.find('>li[carouselindex=' + nextIndex + ']');
			
		$slide.hasClass('ad') ? 
			renderQuickReadAd($slide.data('tagParams')) : 
			populateQuickRead($slide.data('slideData'));
			
    	// Hide ads because they cause flickering in the carousel behind quick read
        $e.carousel.find('iframe').hide(); 
			
		carousel.scroll(nextIndex);
		quickReadIndex = nextIndex;
		
		updateQuickReadNav();
	};
	
	/**
	 * Loads the previous carousel slide content into the quick read view. Also scrolls carousel to related slide.
	 * @private
	 * @param {Object} e Event
	 * @returns Nothing
	 */
	var quickReadPrev = function(e) {
		e.preventDefault();
		if (quickReadIndex === 1) {
			return;
		}
		
		var prevIndex = (quickReadIndex - 1),
			$slide = carousel.list.find('>li[carouselindex=' + prevIndex + ']');
			
		$slide.hasClass('ad') ? 
			renderQuickReadAd($slide.data('tagParams')) : 
			populateQuickRead($slide.data('slideData'));
		
		// Hide ads because they cause flickering in the carousel behind quick read
        $e.carousel.find('iframe').hide(); 
        		
		carousel.scroll(prevIndex);
		
		quickReadIndex = prevIndex;
		
		updateQuickReadNav();
	};
	
	/**
	 * Updates the quick read navigation arrows, disabling them if need be, after a user clicks from one quick read slide to the next.
	 * @private
	 * @param {Object} e Event
	 * @returns Nothing
	 */
	var updateQuickReadNav = function() {
		$e.quickReadNext.removeClass(C.DISABLED_CLASS);
		$e.quickReadPrev.removeClass(C.DISABLED_CLASS);

		if (quickReadIndex === 1) {
			$e.quickReadPrev.addClass(C.DISABLED_CLASS);
		} else if (quickReadIndex === carousel.size()) {
			$e.quickReadNext.addClass(C.DISABLED_CLASS);
		} else {
			$e.quickReadNext.removeClass(C.DISABLED_CLASS);
			$e.quickReadPrev.removeClass(C.DISABLED_CLASS);
		}
	};
	
	/**
	 * Displays the quick read overlay with a fade in.
	 * @private
	 * @returns nothing
	 */
	var showQuickRead = function() {
		if (!$e.quickRead || !$e.scrim) {
			buildQuickRead();
		}
	
		// Turn off carousel animation.
		carouselAnimationSpeed = carousel.options.animation;
		carousel.options.animation = 0;
		carousel.options.autoDetectScroll = false;
		carousel.options.scroll = 1;

		$e.scrim
			.show()
			.animate({
				'opacity' : 0.92
			}, C.QUICK_READ_SPEED, 'linear', function(){
				$e.module.addClass(C.QUICK_READ_ID_CLASS);
			});
		
		$e.quickRead
			.show()
			.css({
				'opacity' : 0
			})
			.animate({
				'opacity' : 1
			}, C.QUICK_READ_SPEED, 'linear');
	};
	
	/**
	 * Hides the quick read overlay with a fade out.
	 * @private
	 * @returns nothing
	 */
	var hideQuickRead = function() {
		if (!$e.quickRead || !$e.scrim) {
			return;
		}

		// Turn carousel animation back on.
		carousel.options.animation = carouselAnimationSpeed;
		carousel.options.autoDetectScroll = true;
			
		// Show ads because they were hidden (flickering in the carousel behind quick read)
        $e.carousel.find('iframe').show(); 
        
		$e.scrim
			.animate({
				'opacity' : 0
			}, C.QUICK_READ_SPEED, 'linear', function(){
				$e.scrim.hide();
				$e.module.removeClass(C.QUICK_READ_ID_CLASS);
			});
		
		$e.quickRead
			.animate({
				'opacity' : 0
			}, C.QUICK_READ_SPEED, 'linear', function(){
				$e.quickRead.hide();
			});
	};
	
	/**
	 * Builds the HTML for a single quick read story and populates the quick read content area.
	 * @private
	 * @param {Object} data A data structure describing the quick read information
	 * @returns nothing
	 */
	var populateQuickRead = function(data) {

		if (!data || typeof data !== 'object') {
			return;
		}

		var $els = {};

		// Clear out the previous quick read.
		emptyQuickRead();

		// Update the nav
		updateQuickReadNav();

		// Set up a wrapper for the text to function like a column
		$els.textWrap = $('<div></div>').addClass(C.WRAPPER_CLASS);

		// Image
		$els.image = XLI.Builder.image({
			url : data.quickReadImageURL,
			title : data.title,
			link : data.link
		});
		if ($els.image) {
			
			$els.textWrap.hide();

			var setWrapperWidth = function() {
				var w = parseInt($e.quickReadContent.css('width'), 10) - $els.image.outerWidth(true);
				$els.textWrap.css('width', w);
				if ($els.textWrap.parent().hasClass(C.SCROLL_CONTAINER_CLASS)) {
					$els.textWrap.parent().css('width', w);
				}
				$els.textWrap
					.show()
					.jScrollPane();
			};
			
			var imageLoadError = function() {
				// Hide the broken image
				$els.image.hide();
				// set the width to the content and show
				var w = parseInt($e.quickReadContent.css('width'), 10);
				$els.textWrap.css('width', w);
				if ($els.textWrap.parent().hasClass(C.SCROLL_CONTAINER_CLASS)) {
					$els.textWrap.parent().css('width', w);
				}
				$els.textWrap
						.show()
						.jScrollPane();
			};

			if ($els.image.get(0).tagName.toLowerCase() !== 'img') {
				$els.image = $els.image.find('img');
			}

			$els.image
				.bind('load', setWrapperWidth)
				.bind('error', imageLoadError);

			$e.quickReadContent.append($els.image);
				
			// In case we're looking at a cached image, IE doesn't fire the 'load' event when displaying cached images.
			if (XLI.Global.ie) {
				window.setTimeout(setWrapperWidth, 50);
			}
		}

		// Category
		$els.category = XLI.Builder.categoryAndTime({
			category : {
				title : data.category.title,
				link : data.category.link
			},
			time : data.time
		});
		if ($els.category) {
			$els.textWrap.append($els.category);
		}

		// Breaking News
		if (data.breakingNews) {
			$els.textWrap.append(XLI.Builder.breakingNews());
		}

		// Title
		$els.title = XLI.Builder.title({
		 	title : data.title,
		 	link : data.link,
		 	imageURL : data.imageURL
		});
		if ($els.title) {
			$els.textWrap.append($els.title);
		}

		// Summary / Blurb
		$els.blurb = XLI.Builder.blurb({
			txt : data.blurb
		});
		if ($els.blurb) {
			$els.textWrap.append($els.blurb);
		}

		// Media
		$els.media = XLI.Builder.mediaLinks(data.media, 'ir');
		if ($els.media) {
			$els.textWrap.append($els.media);
		}

		// Actions list
		$els.actions = $('<ul></ul>').addClass('actions');
		/**
			// First comments,
			$els.comments = XLI.Builder.comments({
				commentCount : data.commentCount,
				link : data.link
			} , '<li></li>');
			if ($els.comments) {
				$els.actions.append($els.comments);
			}
		*/
			// then other actions.
			if (data.actions) {
				$.each(data.actions, function(name, value){
					var $lnk = XLI.Builder.link({
						link : value,
						txt : name
					});

					if ($lnk) {
						$els.actions.append(
							$('<li></li>').append($lnk)
						);
					}
				});
			}

		// Some special stlying help for IE6.
		if (XLI.Global.ie6) {
			$els.actions.find('>li:first').addClass('first-child');
		}

		if ($els.actions.children().length) {
			$els.textWrap.append($els.actions);
		}

		$e.quickReadContent.append($els.textWrap);
		try {
			$els.textWrap.jScrollPane();
		} catch(e) {}


	};
	
	/**
	 * Renders an ad in the quick read content area
	 * @private
	 * @returns nothing
	 */
	var renderQuickReadAd = function(tagParams) {
		// Clear out the previous quick read.
		emptyQuickRead();
		
		XLI.AdManager.render($e.quickReadContent, C.QUICK_READ_AD_CONFIG, tagParams);

		// So we can reuse this function...
		$e.quickReadContent.get(0).adRendered = false;
	};
	
	/**
	 * Removes HTML from the quick read content area.
	 * @private
	 * @returns
	 */
	var emptyQuickRead = function() {
		$e.quickReadContent.find('>*:not(p.close)').remove();
	};
	
	return {
		
		/**
		 * Sets up the Feature module
		 * @public
		 * @returns nothing
		 */
		initialize : function() {
			if (initialized) {
				return;
			}
			
			// Create a reference to the featured module.
			$e.module = $(C.MODULE_ID);
			
			// And bail out if we can't find it.
			if (!$e.module.length) {
				return;
			}
			
			initNav();
			initCarousel();
			
			initialized = true;
		}/*,
		
		ud : updateDragHandle
		*/
	};
	
}();

// Queue it up to load.
$(document).bind('load.featureModule', XLI.FeatureModule.initialize);
XLI.Global.queueCustomEvent('load.featureModule');



/**
	Feature Module Calendar functionality
*/

XLI.FeatureModule.Calendar = function() {

	/**
		A Flag to keep track of whether or not we've initialized this object.
	*/
	var initialized = false;
	
	/**
		Options for building the filter checkboxes on the daily calendar
	 */
	var DAILY_FILTERS = {
		'xliPick'			: "ExploreLI Picks",
		'artsEnt'			: "Arts & Entertainment",
		'kidsFamily'		: "Kids & Family",
		'nightlife'			: "Nightlife",
		'sportsOutdoors'	: "Sports & Outdoors"
	};

	/**
	 *	Set Some Constants
	 */
	var CONSTANTS = {

		// The ID of the calendar module overlay
		MODULE_ID : "#featuredCalendar",
		
		// ID of the monthly feature section
		MONTHLY_ID : "#monthlyFeature",
		
		// ID of the daily listing section
		DAILY_ID : "#dailyListing",
		
		// Selector for grabbing the dataUrl inputs
		DATA_URL_SELECTOR : "input[name=dataUrl]",
		
		// Month selector list class
		MONTH_SELECTOR_CLASS : "monthSelector",
		
		// Daily wrapper class
		DAILY_CLASS : "daily",
		
		// Previous button Class
		PREV_CLASS : "prev",
		
		// Next button class
		NEXT_CLASS : "next",
		
		// Disabled class
		DISABLED_CLASS : "disabled",
		
		// Feature class
		FEATURE_CLASS : "feature",
		
		// More class
		MORE_CLASS : "more",
		
		// Other Events
		OTHER_EVENTS_ID : "#otherEvents",
		
		// Filter wrapper class
		FILTER_CLASS : "filters",
		
		// Filter label text
		FILTER_LABEL_TEXT : "Show",
		
		// View All text
		VIEW_ALL_TEXT : "View All",
		
		// Daily Calendar carousel selector
		DAILY_CAROUSEL_SELECTOR : "ul.carousel",
		
		// Date Class
		DATE_CLASS : "date",
		
		// Days, to be displayed in the daily calendar
		DAYS : [
			"Sun",
			"Mon",
			"Tue",
			"Wed",
			"Thu",
			"Fri",
			"Sat"
		]

	};

	/**
	 *	Element References as returned by JQuery
	 *	references created as needed
	 */
	var $elements = {
		
		// The calendar module
		module : null,
	
		// The Monthly section
		monthly : null,
		
		// Monthly data url input
		monthlyDataUrlInput : null,
		
		// Monthly Feature section
		monthlyFeature : null,
		
		// Monthly Other Events List
		monthlyOther : null,
		
		// The Month header
		monthHeader : null,
		
		// The Daily feature section
		daily : null,
		
		// Daily data url input
		dailyDataUrlInput : null,
		
		// Daily calendar wrapper
		dailyWrapper : null,
		
		// Daily calendar
		dailyCalendar : null,
		
		// Daily Filters
		dailyFilters : null
		
	};

	/**
	 *	Shorthand references to the CONSTANTS and $elements.
	 */
	var C	= CONSTANTS;
	var $e	= $elements;
	
	/**
	 * Initializes the monthly feature section and builds the nav to select other months
	 * @private
	 * @returns nothing
	 */
	var initMonthly = function() {
		// Find necessary elements and drop out if we don't have them.
		$e.monthly = $(C.MONTHLY_ID, $e.module);
		if (!$e.monthly.length) {
			return;
		}
		
		// Get some additional elements
		$e.monthlyDataUrlInput	= $e.monthly.find(C.DATA_URL_SELECTOR);
		$e.monthlyFeature		= $e.monthly.find('.' + C.FEATURE_CLASS);
		$e.monthlyOther			= $e.monthly.find(C.OTHER_EVENTS_ID + " ul");
		$e.monthHeader			= $e.monthly.find('h3');
		
		if (!$e.monthlyDataUrlInput.length) {
			return;
		}
		
		// Create the monthly navigation
		var $nav		= $('<ul></ul>')
							.addClass(C.MONTH_SELECTOR_CLASS);
							
		$e.monthPrev	= $('<li></li>')
							.addClass(C.PREV_CLASS)
							.addClass(C.DISABLED_CLASS + C.PREV_CLASS)
							.text(C.PREV_CLASS)
							.bind('click', getNewMonthData)
							.appendTo($nav);
							
		$e.monthNext	= $('<li></li>')
							.addClass(C.NEXT_CLASS)
							.text(C.NEXT_CLASS)
							.bind('click', getNewMonthData)
							.appendTo($nav);
		
		// Append the nav
		$nav.insertAfter($e.monthHeader);
		
	};
	
	/**
		The index of the month data is requested for
	*/
	var requestedMonth;
	
	/**
	 * Triggers the XHR to get additional monthly data.
	 * @private
	 * @returns nothing
	 */
	var getNewMonthData = function() {
		var $this = $(this);
		// deselect the button.
		this.blur();
		if ($this.attr('class').indexOf(C.DISABLED_CLASS) > -1) {
			return;
		}
		
		requestedMonth = parseInt($e.monthHeader.attr('class').replace(/^month_/, ""), 10) + ($this.hasClass('next') ? 1 : -1);
		// wrap around the year change
		if (requestedMonth > 12) {
			requestedMonth = requestedMonth - 12;
		}
		if (requestedMonth < 1) {
			requestedMonth = requestedMonth + 12;
		}
		
		try {
			$.ajax({
				'url'		: $e.monthlyDataUrlInput.val() + requestedMonth,
				'dataType'	: 'json',
				'success'	: updateMonthly,
				'error'		: function(xhr, message) {
					XLI.Debug.error("Request Error requesting new month data : " + message);
				}
			});
		} catch(e) {
			XLI.Debug.error("XHR Error requesting new month data : " + e.message);
		}
	};
	
	/**
	 * Handles a successful XHR from getNewMonthData(). Creates HTML from the JSON result
	 * @private
	 * @param {Object} data JSON describing the requested month data
	 * @returns nothing
	 */
	var updateMonthly = function(data) {
		if (!data.name || !data.featured || !data.other) {
			return;
		}
		
		var $els = {};
		
		// Empty the sections
		$e.monthlyFeature.empty();
		
		// update the header
		$e.monthHeader
			.text(data.name)
			.attr('class', $e.monthHeader.attr('class').replace(/\d+/, requestedMonth));
		
		// Build New HTML for the monthly feature
		$els.image = XLI.Builder.image({
			url : data.featured.imageUrl,
			title : data.featured.title,
			link : data.featured.link
		});
		
		$els.featuredDate = $('<h4></h4>').text(data.featured.date);
		
		$els.title = XLI.Builder.title({
			title : data.featured.title,
			link : data.featured.link
		}, '<h2></h2>');
		
		$els.button = XLI.Builder.button({
			txt : "Read More",
			link : data.featured.link
		});
		
		$.each($els, function(key, el){
			if (el) {
				$e.monthlyFeature.append(el);
			}
		});
		
		// Do the "Other Events" List
		$e.monthlyOther.empty();
		$.each(data.other, function(){
			if (!this.title) {
				return;
			}
			
			var $li	  = $('<li></li>'),
				$link = (this.link) ? $('<a></a>').attr('href', this.link).text(this.title) : false;
				
			($link) ? $li.append($link) : $li.text(this.title);			
			
			$e.monthlyOther.append($li);
		});
		
		// Update the previous / next buttons.
		$e.monthPrev[data.prev ? 'removeClass' : 'addClass'](C.DISABLED_CLASS + C.PREV_CLASS);
		$e.monthNext[data.next ? 'removeClass' : 'addClass'](C.DISABLED_CLASS + C.NEXT_CLASS);
	};
	
	var dailyCarousel = null;

	var initDaily = function() {
		// Find necessary elements and drop out if we don't have them.
		$e.daily = $(C.DAILY_ID, $e.module);
		if (!$e.daily.length) {
			return;
		}
		
		$e.dailyWrapper			= $e.daily.find("." + C.DAILY_CLASS);
		$e.dailyCalendar		= $e.daily.find(C.DAILY_CAROUSEL_SELECTOR);
		$e.dailyDataUrlInput	= $e.daily.find(C.DATA_URL_SELECTOR);
		if (!$e.dailyDataUrlInput.length || !$e.dailyWrapper.length || !$e.dailyCalendar.length) {
			return;
		}
		
		// Build filters
		buildDailyFilters();
		
		// Initalize Carousel
		try {
			$e.dailyCalendar.jcarousel({
				scroll : 5,
				containsAds : false,
				pagerTarget : $e.daily,
				initCallback : function(carousel) {
					// Create a reference to the carousel object
					dailyCarousel = carousel;
					// Kill this init - no longer needed
					carousel.options.initCallback = null;
				},
				additionalContentBaseURL : $e.dailyDataUrlInput.val(),
				additionalContentCallback : addDays
			});
		} catch(e) {
			XLI.Debug.error("Error initializing Daily calendar carousel :" + e.message);
		}
	};
	
	var buildDailyFilters = function() {
		
		var HTML = {
			DIV		: "<div></div>",
			H5		: "<h5></h5>",
			LABEL	: "<label></label>",
			INPUT	: "<input />"
		};
		
		$wrap = $(HTML.DIV)
					.addClass(C.FILTER_CLASS)
					.append(
						$(HTML.H5).text(C.FILTER_LABEL_TEXT + ":")
					);
		
		$.each(DAILY_FILTERS, function(key, val){
			var $label = $(HTML.LABEL),
				$input = $('<input type="checkbox" checked="checked" value="' + key + '"></input>')
							.bind('change', filterDaily);
							
			$label
				.append($input, " " + val)
				.appendTo($wrap);
		});
		
		$e.dailyFilters = $wrap.find('input');
		
		$wrap.insertBefore($e.dailyWrapper);
		
	};
	
	var filterDaily = function() {
		if (!$e.dailyFilters.length) {
			return;
		}
		
		var active = [],
			selectors = {
				'on' : '',
				'off' : ''
			};
		
		$e.dailyFilters.each(function(){
			selectors[(this.checked) ? 'on' : 'off'] += ('dd.' + this.value + ", ");
			if (this.checked) {
				active.push(this.value);
			}
		});
		
		selectors.on	= selectors.on.replace(/,\s+$/, "");
		selectors.off	= selectors.off.replace(/,\s*$/, "");
		
		// Show and hide
		try {
			if (selectors.off !== "") {
				$(selectors.off, $e.dailyWrapper).each(function() {
					var $this 	= $(this);
					// we don't want to hide anything that still has an active class.
					for (var a in active) {
						if($this.hasClass(active[a])) {
							return;
						}
					}
					$this.hide();
				});
			}
			
			if (selectors.on !== "") {
				$(selectors.on, $e.dailyWrapper).show();
			}
		} catch(e) {
			XLI.Debug.error("Error filtering Daily calendar :" + e.message);
		}
		
	};
	
	var addDays = function(json) {
		
		if (typeof json !== 'object' || !$.isArray(json.upcoming)) {
			return;
		};
		
		var $els = {},
			HTML = {
				DIV		: "<div></div>",
				SPAN	: "<span></span>",
				DL		: "<dl></dl>",
				DT		: "<dt></dt>",
				DD		: "<dd></dd>",
				IMG		: "<img />",
				A		: "<a></a>"
			};
		
		$.each(json.upcoming, function(i, day){
			$els.temp	= $(HTML.DIV);
			$els.dl		= $(HTML.DL);
			$els.dt		= $(HTML.DT);
			
			var size	= parseInt(dailyCarousel.size(), 10) + 1,
				date	= new Date(day.date);
			
			$els.dt
				.text(C.DAYS[date.getDay()] + " ")
				.append(
					$(HTML.SPAN)
						.addClass(C.DATE_CLASS)
						.text(date.getDate())
				)
				.appendTo($els.dl);
				
			var buildEntry = function(data) {
				$els.dd = $(HTML.DD);
				// Add Classes
				if ($.isArray(data.categories)) {
					$.each(data.categories, function(){
						$els.dd.addClass(this);
					});
				}
				if (typeof data.imageUrl === "string" && data.imageUrl !== "") {
					// Add a featured class to this DD
					$els.dd.addClass(C.FEATURE_CLASS);
					// Add the image
					$els.image = XLI.Builder.image({
							url : data.imageUrl,
							title : data.title,
							link : data.link
						});
					if ($els.image) {
						$els.image.appendTo($els.dd);
					}
				}
				
				$els.title = XLI.Builder.link({
					txt : data.title,
					link : data.link
				});
				
				if ($els.title) {
					$els.title.appendTo($els.dd);
				}
				
				$els.dl.append($els.dd);
				
			};
			
			if ($.isArray(day.events)) {
				// We'll loop through the events twice,
				// first to get the first featured event (has an image)
				$.each(day.events, function(j, event){
					if (typeof event.imageUrl === "string" && event.imageUrl !== "") {
						buildEntry(event);
						return false;
					}
				});
				// then again to add any without featured events.
				$.each(day.events, function(j, event){
					if (typeof event.imageUrl !== "string" || event.imageUrl === "") {
						buildEntry(event);
					}
				});
			}
			
			if (day.more && day.more.count && day.more.link) {
				$els.dd = $(HTML.DD)
							.addClass(C.MORE_CLASS)
							.text(day.more.count + " more events ");
				
				$els.moreLink = XLI.Builder.link({
					txt : C.VIEW_ALL_TEXT,
					link : day.more.link
				});
				
				if ($els.moreLink) {
					$els.moreLink.appendTo($els.dd);
				}
				
				$els.dl.append($els.dd);
			}
			
			$els.temp.append($els.dl);
			dailyCarousel.add(size, $els.temp.html());
			
		});
		
		// Filter the newly added slides
		filterDaily();
		
	};

	return {

		/**
		 * Sets up the Feature module calendar
		 * @public
		 * @returns nothing
		 */
		initialize : function() {
			if (initialized) {
				return;
			}
			
			// Create a reference to the module.
			$e.module = $(C.MODULE_ID);

			// And bail out if we can't find it.
			if (!$e.module.length) {
				return;
			}
			
			initMonthly();
			initDaily();
			
			initialized = true;
		}

	};

}();

/**
	Fire it up.
*/
$(document).ready(XLI.FeatureModule.Calendar.initialize);

XLI.VideoJsonHandler = function() {
	
	/*
		<li id="videoId_190">
			<div class="videoItem">
				<a href="#"><img src="/fpo/images/101x58.jpg" class="image" alt="" /></a>
				<h4><a href="#" class="category">category</a> <em class="time">5 min</em></h4>
				<p class="summary"><a href="#">title</a></p>
				<p class="meta">
					<span class="summary-long"></span>
					<span class="title"></span>
				</p>
			</div>
		</li>
	
		<li>
			<div class="photoItem">
				<h4><a href="#" class="category">category</a> <em class="time">5 m ago</em></h4>
				<a href="#"><img src="/fpo/images/161x92.png" class="image" alt="" /></a>
				<p class="summary"><a href="#"><?php echo $titles[rand(0, count($titles) - 1)]; ?></a></p>
			</div>
		</li>
	*/
	
	var ID_PREFIX = "videoId_";
	
	var HTML = {
		LI : '<li></li>',
		MAIN : '<div class="videoItem"></div>',
		IMG : '<img />',
		LINK : '<a href="#"></a>',
		CATEGORY : '<p class="info"></p>',
		CATEGORY_LINK : '<a class="category"></a>',
		TIME : '<em class="time"></em>',
		CAPTION : '<h4></h4>',
		META : '<p class="meta"></p>',
		SUMMARY_LONG : '<span class="summary-long"></span>',
		TITLE : '<span class="title"></span>',
		
		IMAGE_MAIN : '<div class="photoItem"></div>'
	};
	
	function videoItem(data) {
		var item = $(HTML.LI).attr("id", ID_PREFIX + data.videoId);
		var main = $(HTML.MAIN);
		var img = $(HTML.LINK).append($(HTML.IMG).attr("src", data.imageURL));
		var categoryLink = $(HTML.CATEGORY_LINK).attr("href", data.category.link).html(data.category.title);
		
		var category = $(HTML.CATEGORY).append(
			categoryLink,
			" ",
			$(HTML.TIME).text(data.time)
		);
		
		var caption = $(HTML.CAPTION).html(data.caption);
		var meta = $(HTML.META);
		var longSummary = $(HTML.SUMMARY_LONG).text(data.summary);
		var title = $(HTML.TITLE).text(data.title);
		
		meta.append(longSummary, title);
		
		return item.append(main.append(img, category, caption, meta));
	};
	
	function imageItem(data) {
		var item = $(HTML.LI);
		var main = $(HTML.IMAGE_MAIN);
		var img = $(HTML.LINK).attr("href", data.link).append($(HTML.IMG).attr("src", data.imageURL));
		var category = $(HTML.CATEGORY).append(
			$(HTML.CATEGORY_LINK).attr("href", data.category.link).html(data.category.title),
			" ",
			$(HTML.TIME).text(data.time)
		);
		var caption = $(HTML.CAPTION).html(data.title);
		
		return item.append(main.append(category, img, caption));
	};
	
	function item(data) {
		if (data.videoId) {
			return videoItem(data);
		} 

                if (data.imageURL) {
                        return imageItem(data);
                }

                return null;
	};
	
	function appendTo($container) {
		$container.append.apply($container, this);
	};
	
	return {
		parse : function(data) {
			var items = [];
			for (var i = 0, l = data.length; i < l; i++) {
                                var e = item(data[i]);

                                if(e == null) {
                                        continue;
				}

                                items.push(e);
			}
			items.appendTo = appendTo;
			return items;
		}
	};
}();

/**********************************************

	Latest Videos Overlay Controller
	
	Call open() and this component will:
	
	* listen for the overlay to finish animating
	* listen for the ajax request for playlist data to finish loading
	* create html elements to display video metadata
	* create the flash player and display video metadata
	* reflect the currently playing video in the playlist
	* listen for further ajax requests for playlist data and update accordingly
	
	Call close() and this component will:
	
	* destroy the flash player
	* reset state variables
	* remove event listeners
	
	The events it listens for are (in addBehavior()):
	
	* click on video playlist thumbnails 
	* section:dataLoaded on the document
	* section:openFinished on the document 
	* section:close on the document

**********************************************/
XLI.LatestVideos = function() {
	
	/* --- CONSTANTS --- */

	var CONSTANTS = {
		
		CONTAINER_SELECTOR : "#mainVideo",
		
		PLAYER_CONTAINER_SELECTOR : ".flash",
		
		VIDEOLIST_SELECTOR : "#videoList li",
		
		TITLE_SELECTOR : ".title",
		
		CATEGORY_SELECTOR : ".category",
		
		DURATION_SELECTOR : ".time",
		
		SUMMARY_SELECTOR : ".summary-long",
		
		COLUMN_ONE_SELECTOR : "#featuredVideos .columnOne",
		
		ACTIVE_CLASS : "active",
		
		CLOSE_BTN_SELECTOR : "#featuredVideos .close",
		
		SHARE_DATA_URL : ""
		
	};

	// shorthand reference
	var C = CONSTANTS;

	/* --- STATIC VARIABLES --- */

	var HTML = {
		META : '<div class="videoMeta"></div>',
		INFO : '<p class="info"></p>',
		CATEGORY : '<a></a>',
		DURATION : '<em class="time"></em>',
		TITLE : '<h2></h2>',
		SUMMARY : '<p class="summary"></p>',
		ACTIONS : '<ul class="actions"></ul>',
		EXPAND : '<p class="expander"></p>'
	};
	
	var flashvars = {
		autoPlay : false,
		showRelatedVideos: false
	};
	
	// dom elements
	var $document = $(document);
	var $container;
	var $columnOne;
	var $playerContainer;
	var $videoList;
	var $closeBtn;
	
	// generated dom elements
	var $category;
	var $duration;
	var $title;
	var $summary;
	var $actions;
	var $expand;
	
	// data
	var playlist = {};
	var player;
	var currentItem;
	var currentVideo;
	
	// state
	var initialized = false;
	var dataLoaded = false;
	var flashReady = false;
	var cubeOpen = false;
	var cubeAnimating = false;

        // Timer
        var playerLoadDelay = null;
        var retryTimer = 0;
        var retryInterval = 50; // in milliseconds
        var retryLimit = 30; // in seconds	

	/* --- PUBLIC CLASS METHODS --- */

	var publicMethods = {
		
		open : function() {
			initialize();
			addBehavior();
			initNewData();
		},

                cleanupPlayer: function() {
                        XLI.Debug.info("XLI.LatestVideos.cleanupPlayer()");

			hideCube();
                        
                        if (playerLoadDelay !== null) {
                                window.clearTimeout(playerLoadDelay);
                                playerLoadDelay = null;
                        }
         
                        if (player) {
                                player.destroy();
                                player = null;
                        }
                },

		close : function() {
			hideCube();
                        
                        if (playerLoadDelay !== null) {
                                window.clearTimeout(playerLoadDelay);
                                playerLoadDelay = null;
                        }
         
                        if (player) {
                                player.destroy();
                                player = null;
                        }
          
			dataLoaded = false;
			flashReady = false;
			
			removeBehavior();
		},

		test : function() { // remove
			showCube();
		},

		test2 : function() { // remove
			hideCube();
		},

		test3 : function(e) { // rename
			showShare(e);
		},
		test4 : function() { // rename
			hideShare();
		},
		test5 : function(e) { // rename
			showEmail(e);
		},
		test6 : function() { // rename
			hideEmail();
		},
		
		test7 : function() { // remove
			var xyz = {};
			xyz.type = XLI.VideoPlayer.START_PREROLL;
			player.delegateEvent(xyz);
		},
		test8 : function() { // remove
			var xyz = {};
			xyz.type = XLI.VideoPlayer.END_PREROLL;
			player.delegateEvent(xyz);
		},
		

		test9 : function() { // remove
			var xyz = {};
			xyz.type = XLI.VideoPlayer.SHOW_HALF_LEADER;
			player.delegateEvent(xyz);
		}
				
	};

	/* --- PRIVATE METHODS --- */
	
	function initialize() {
		if (! initialized) {
			getElements();
			createMetaElements();
			initialized = true;
		}
		$playerContainer = $container.find(C.PLAYER_CONTAINER_SELECTOR);
		$playerContainer.hide();
	};
	
	// we can't create the player unless both the data is loaded
	// and the overlay is fully visible. this event callback manages
	// both the dataLoaded and openFinished events
	function handleAsyncEvents(event) {
		switch(event.type) {
			case "section:dataLoaded":
				initNewData();
			break;
			case "section:openFinished":
				flashReady = true;
			break;
			default:
			break;	
		};
		if (dataLoaded && flashReady && ! player) {
			createPlayer();
		}
	};
	
	// this will only try to play the video. if the player
	// hasn't been created, nothing will happen until it is
	function initNewData() {
		$videoList = $(C.VIDEOLIST_SELECTOR);
		if ($videoList.length) {
			showVideo($videoList.eq(0),false);
			dataLoaded = true;
		}
	};
	
	// elements that already exist in the page
	function getElements() {
		$container = $(C.CONTAINER_SELECTOR);
		$columnOne = $(C.COLUMN_ONE_SELECTOR);
		$videoList = $(C.VIDEOLIST_SELECTOR);
		$playerContainer = $container.find(C.PLAYER_CONTAINER_SELECTOR);
		$closeBtn = $(C.CLOSE_BTN_SELECTOR);
		XLI.Global.ie6Hover($closeBtn);
	};
	
	// video metadata elements need to be created and appended
	function createMetaElements() {
		var meta = $(HTML.META);
		var info = $(HTML.INFO);
		$duration = $(HTML.DURATION);
		$category = $(HTML.CATEGORY);
		$title = $(HTML.TITLE);
		$summary = $(HTML.SUMMARY);
		$actions = $(HTML.ACTIONS);
		
		XLI.Global.ie6Hover($category);
		
		info.append($category, " ", $duration);
		meta.append(info, $title, $summary, $actions);
		$playerContainer.parent().after(meta);
		
		var sponsor = meta.siblings(".sponsor");
		if (sponsor.length) {
			meta.append(sponsor);
		}
	};
	
	// add the event listeners
	function addBehavior() {
		// $videoList.live("click", onVideoClick);
		$document.bind("section:dataLoaded", handleAsyncEvents);
		$document.bind("section:openFinished", handleAsyncEvents);
		$document.bind("section:close", publicMethods.close);
	};
	
	// remove the event listeners (don't want to try to
	// create the player when "photos" ajax data finishes
	// loading, for example)
	function removeBehavior() {
		$videoList.die("click", onVideoClick);
		$document.unbind("section:dataLoaded", handleAsyncEvents);
		$document.unbind("section:openFinished", handleAsyncEvents);
		$document.unbind("section:close", publicMethods.close);
	};
	
	// gets the video data from the html sent
	// over the wire and stores it the playlist object
	function parsePlaylistItem($item) {
		if (! $item.get(0)) {
			return;
		}
		var id = parseInt($item.attr("id").split("_")[1], 10);
		
		if (! playlist[id]) {
			playlist[id] = {
				id : id,
				category : $item.find(C.CATEGORY_SELECTOR),
				duration : $item.find(C.DURATION_SELECTOR),
				title : $item.find(C.TITLE_SELECTOR),
				summary : $item.find(C.SUMMARY_SELECTOR)
			};
		}
			
		return playlist[id];
	};
	
	// creates the player and adds an event listener to 
	// go to the next video when the current one ends
	function createPlayer() {
		$playerContainer = $container.find(C.PLAYER_CONTAINER_SELECTOR);
		
		var vars = $.extend({}, flashvars);
		if (currentVideo) {
			vars.videoId = currentVideo.id;
		}
		
		player = XLI.VideoPlayer.create($playerContainer, vars);
		player.addCallback(XLI.VideoPlayer.INITIALIZE, playerReady);
		player.addCallback(XLI.VideoPlayer.COMPLETE, loadNextVideo);
		player.addCallback(XLI.VideoPlayer.START_PREROLL, showCube);
		player.addCallback(XLI.VideoPlayer.END_PREROLL, endPreroll);
//		player.addCallback(XLI.VideoPlayer.SHOW_HALF_LEADER, changeHalfLeader);
		player.addCallback(XLI.VideoPlayer.RELATED_VIDEO_CLICK, handleRelatedVideo);
		$playerContainer.show();
	};
	
	function playerReady() {
		$videoList.live("click", onVideoClick);
	};
	
	function onVideoClick(e) {
		if (! $(e.target).is(".info a")) {
			e.preventDefault();
			showVideo($(this));	
		}
	};
	
	// can only play a video if the video player
	// has been created. otherwise the video will be the
	// first to play when it is created
	function showVideo($item,auto) {
		auto = (typeof auto === 'boolean') ? auto : true;
		currentItem = $item;
		currentVideo = parsePlaylistItem($item);
		$videoList.removeClass(C.ACTIVE_CLASS);
		$item.addClass(C.ACTIVE_CLASS);
		showMetaData();
		
                var videoLoader = function() {
                        if (player && player.initialized && typeof player.vars === "object") {
                                if (playerLoadDelay !== null) {
                                        window.clearTimeout(playerLoadDelay);
                                        playerLoadDelay = null;
                                }
                        
                                try {
                                        player.setAutoPlay(auto);
                                } catch(e) {}
        
                                if (currentVideo.id != player.getCurrentVideoId()) {
                                        player.loadVideo(currentVideo.id);
                                } else {
                                        if(auto) {
                                                player.play();
                                        }
                                }
                        } else {
                                if (retryTimer >= (retryLimit * 1000)) {
                                        playerLoadDelay = null;
                                        return;
                                } else {
                                        retryTimer += retryInterval;
                                        playerLoadDelay = window.setTimeout(videoLoader, retryInterval);
                                }
                        }       
                };
                                        
                videoLoader();
	};
	
	function loadNextVideo(event) {
		if (currentItem.next("li")) {
			showVideo(currentItem.next("li"));
		}
	};
	
	// injects the metadata into the elements
	// below the video player
	function showMetaData() {
		$duration.html(currentVideo.duration.html());
		$category.html(currentVideo.category.html());
		$title.html(currentVideo.title.html());
		$summary.html(currentVideo.summary.html());

		// clean up any old overlays
		if (cubeOpen) {
			hideCube("now");
		}
		removeShare();
		hideEmail();
		removeHalfLeaderAd();
		
		$("ul.actions .fave").unbind("click").bind("click",function(e) { XLI.LatestVideos.test3(e); e.preventDefault(); });
		$("ul.actions .friend").unbind("click").bind("click",function(e) { XLI.LatestVideos.test5(e); e.preventDefault(); });
	};
	
	/* Resize methods (no longer used) */
	
	var sizes = {
		collapsed : {
			widthOne : 0,
			widthTwo : 798,
			videoHeight : 444
		},
		expanded : {
			widthOne : 173,
			widthTwo : 650,
			videoHeight : 360
		}
	};
	
	var collapsed = false;
	
	function toggleSize(e) {
		if (collapsed) {
			resize(sizes.collapsed, sizes.expanded, 1000);
		} else {
			resize(sizes.expanded, sizes.collapsed, 1000);
		}
		
		collapsed = !collapsed;
		$expand.find("a").html(collapsed ? "View Thumbnails" : "Larger Video").toggleClass("expand").toggleClass("collapse");

		e.preventDefault();
	};
	
	function resize(begin, end, duration) {
		
		var easing = jQuery.easing.easeInOutExpo;
		
		var change = {};
		for (var key in begin) {
			change[key] = end[key] - begin[key];
		}
		
		// run the animation
		var start = new Date().getTime();
		animate();
		
		// the working animation function
		function animate() {
			var t = new Date().getTime() - start;
			if (t < duration) {
				
				$container.css("width", easing(null, t, begin.widthTwo, change.widthTwo, duration));
				$columnOne.css("width", easing(null, t, begin.widthOne, change.widthOne, duration));
				player.$container.css("height", easing(null, t, begin.videoHeight, change.videoHeight, duration));
				
				setTimeout(animate, 0);
			} else {
				
				$container.css("width", end.widthTwo);
				$columnOne.css("width", end.widthOne);
				player.$container.css("height", end.videoHeight);
				
			}
		};
	};
	
	function endPreroll() {
		var url = player.getHalfLeaderAd();
		
		if (url) {
			$("#featuredVideos #mainVideo .halfBanner").html("");
			$("#featuredVideos #mainVideo .halfBanner").append("<iframe class='halfBannerIframe' style='width:234px;height:60px;border:0' height='60' width='234' scrolling='no' border='0' src='"+url+"'><a href='"+url+"'>"+url+"</a></iframe>")
		}
		hideCube();
	};

	function showShare(e) {
		if (0 == $("#featuredVideos .shareThing").length) {
			$.jsonp({
				url: C.SHARE_DATA_URL + "?callback=ws_results&videoId=" + currentVideo.id,
				callback: "ws_results",
				cache: true,
				beforeSend: function() {
					//$e.featuredVideoContainer.addClass(Newsday.Global.C.LOADING_CLASS);
				},
				success: function(data) {
					//$("#featuredVideos .videoPlayer").css("position","relative").css("overflow","hidden");
					$("#featuredVideos .videoPlayer").append("<div class='shareThing'><h4>Share:</h4> <a href='#' class='close gl' onclick='XLI.LatestVideos.test4();return false;'>X<span></span></a><ul></ul></div>");

					for (i=0;i<data.length;i++) {
						$("#featuredVideos .shareThing ul").append("<li class='"+data[i].service+"'><a href='"+data[i].url+"'>"+data[i].service+"</a></li>");
					}
					$("#featuredVideos .shareThing").animate({bottom:"0"},C.SHOW_SPEED);
				},
				error : function(xhr, message) {
					XLI.Debug.error("Error getting new content for share module overlay: " + message);
				},
				complete : function() {
					//$e.featuredVideoContainer.removeClass(Newsday.Global.C.LOADING_CLASS);
				}
			});
		} else {
			$("#featuredVideos .shareThing").animate({bottom:"0"},C.SHOW_SPEED);
		}
	};
	
	function hideShare() {
		$("#featuredVideos .shareThing").animate({bottom:"-90px"},C.SHOW_SPEED);
	};
	
	function removeShare() {
		$("#featuredVideos .shareThing").remove();
	};

	function showEmail(e) {
		if (0 == $("#featuredVideos .sendEmail").length) {
			//$("#featuredVideos #mainVideo").css("position","relative");
			$("#featuredVideos #mainVideo").append("<div class='sendEmail'><form action='#TBD' method='post'><fieldset><h4>Email This Video</h4><a href='#' class='close gl' onclick='XLI.LatestVideos.test6();return false;'>X<span></span></a><label for='mail_email'>email address</label><input type='text' id='mail_email' /><label for='mail_message'>message</label><textarea cols='10' rows='4' id='mail_message'></textarea><a href='' onclick='XLI.LatestVideos.test6();return false;'>send</a> <a href='' class='cancel' onclick='XLI.LatestVideos.test6();return false;'>cancel</a></fieldset></form></div>");
		}
	};
	
	function hideEmail() {
		$("#featuredVideos .sendEmail").remove();
	};
	
	function showCube() {
		var url = player.getCubeAd();
		
		if (url) {
			if (!cubeOpen && !cubeAnimating) {
				cubeOpen = true;
				cubeAnimating = true;
				//$("#feature").css("overflow","hidden");
				$("#featuredVideos .columnOne").css("position","relative");
		
				$("#featuredVideos").append("<div class='adwrapen'style='position:absolute;left:10px;top:0;width:304px;height:400px;overflow:hidden'><iframe class='adboxen' style='width:300px;height:250px;position:absolute;left:310px;top:83px;border:0' height='250' width='300' scrolling='no' border='0' src='"+url+"'><a href='"+url+"'>"+url+"</a></iframe></div");
		
				$("#featuredVideos .columnOne:first, #feature .main:first, #feature .nav:first").animate({left:"-305px"},1400,function() {
					XLI.Debug.log("showCube: nav animation done");
				});
				$("#featuredVideos .adboxen:first").animate({left:"0"},1400,function(){
					XLI.Debug.log("showCube: cube animation done");
					cubeAnimating = false;
				});
				
			} else {
				XLI.Debug.error("XLI.LatestVideos.showCube(): animation failed, cube already open;");
			}
		}
	};
	
	function hideCube(when) {
		XLI.Debug.log("hideCube() called");
		if (cubeOpen && !(cubeAnimating)) {
			cubeOpen = false;
			cubeAnimating = true;
			if ("now" == when) {
				$("#featuredVideos .adboxen:first").parent().remove();
				$("#featuredVideos .columnOne:first, #feature .main:first, #feature .nav:first").css("left","0");
				cubeAnimating = false;
			} else {
				
				$("#featuredVideos .columnOne:first, #feature .main:first, #feature .nav:first").animate({left:"1"},1400,function(){
					XLI.Debug.log("hideCube: description animation done");
				});
				$("#featuredVideos .adboxen:first").animate({left:"305px"},1400,function(){
					XLI.Debug.log("hideCube: cube animation done");
					$(this).parent().remove();
					cubeAnimating = false;
				});
			}
		} else {
			XLI.Debug.error("XLI.LatestVideos.hideCube(): animation failed, cube already hidden;");
		}
	};
	
	function removeHalfLeaderAd() {
		$("#featuredVideos #mainVideo .halfBanner").html("");
	};
	
	function handleRelatedVideo() {
		var id = player.getClickedVideoID();
		
		if (id) {
			XLI.VideoGalleryOverlay.load(null, id);
		}
	};

	return $.extend(publicMethods, CONSTANTS);
}();

XLI.Global.queueCustomEvent('load.activateFeatureOverlay');

