/**
 * @fileoverview	XLI.com Common Javascript
 * @author			Michael Bester <mbester@schematic.com>
 * @version			1.0
 * @dependencies	jquery.js (version 1.3.x)
 */

/*
 *	For CSS purposes, right off the bat, let's add a 'js' class to the HTML tag.
 *	While not technically valid, it'll prevent flash of non-js styles.
 */

$('html').addClass('js');

/*
 *	Declare the global CP object for namespacing.
 */

var XLI = window.XLI || {};

/**
 * Sets up the functionality common to most areas of the site.
 * @class
 */
XLI.Global = (function() {

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

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

		// Header ID
		HEADER_ID : '#header',

		// Main Nav element ID
		NAV_MAIN_ID : "#navMain",

		// Content ID
		CONTENT_ID : "#content",

		// Main Nav element ID
		NAV_MAIN_HOME_SUBNAV_ID : "#navMainHome",

		// Subnav class name
		SUBNAV_CLASS : "navSub",

		// Active nav item class name
		ACTIVE_CLASS : "active",

		// Main Nav Secondary Class
		SECONDARY_CLASS : "secondary",

		// Hover class for IE6
		HOVER_CLASS : "hover",

		// Visible class for subnav
		VISIBLE_CLASS : "visible",

		// Flyout menu class
		FLYOUT_CLASS : "flyout",

		// Loading indicator class
		LOADING_CLASS : "loading",

		// The class name applied to a flyout's parent element to make it visible
		FLYOUT_ACTIVE_CLASS : "open",

		// Flyout Menu subnavigation class
		FLYOUT_SUBNAV_CLASS : "flyoutSubnav",

		// Flyout Subnav close button HTML
		FLYOUT_CLOSE_HTML : '<p class="close gl"><span></span>close</p>',

		// Flyout Subnav close button HTML
		FLYOUT_SUBNAV_CLOSE_HTML : '<li class="close gl"><span></span>close</li>',

		// Search form class
		SEARCH_FORM_CLASS : "search",

		// Class name for elements with custom scrollbars
		SCROLLABLE_CLASS : "scrollable",

		// Class name for tabbable element containers.
		TABBABLE_CLASS : "tabbedPanel",

		// Image Replacement class
		IR_CLASS : "gl",

		// Special class used for filtering elements
		DEFER_CLASS : "defer",

		// Used for form fieldsets to autohide default text.
		AUTOHIDE_CLASS : 'autohide'

	};

	/**
	 *	Element References as returned by JQuery
	 *	references created as needed
	 */
	var $elements = {

		// The document element
		doc : $(document),

		// The body element
		body : null,

		// The Main nav
		navMain : null,

		// The active nav item
		navActive : null,

		// The main nav items without a class of "secondary"
		primaryNavLis : null,

		// Visible Sub Nav
		visibleSubnav : null,

		// Flyout menus
		flyouts : null,

		// Search Forms
		searchForms : null,

		// Select Elements
		selects : null,

		// Panes with custom scrollbars
		scrollables : null
	};

	/**
	 *	Shorthand references to the CONSTANTS and $elements.
	 */
	var C	= CONSTANTS;
	var $e	= $elements;

	/**
	 *	Are we on Windows?
	 */
	var win = (navigator.platform.toLowerCase().indexOf('win') > -1) ? true : false;

	/**
	 *	Set some browser flags
	 *	If we're dealing with IE6, set a flag and fix background image caching.
	 */
	var ie	= $.browser.msie;
	var ie6 = ($.browser.msie && (parseInt($.browser.version, 10) === 6));
	var ie7 = ($.browser.msie && (parseInt($.browser.version, 10) === 7));
	var ie8 = ($.browser.msie && (parseInt($.browser.version, 10) === 8));
	var moz = $.browser.mozilla;
	try {
		if (ie6) {
			document.execCommand("BackgroundImageCache", false, true);
		}
	} catch(e) {}

	/**
	 * Builds out the navigation menu/submenu functionality and fixes IE6 issues
	 * @private
	 * @returns nothing
	 */
	var initNav = function() {

		var bodyId, activeId;

		// Create a reference to the body and main nav.
		$e.body		= $('body');
		$e.navMain	= $(C.NAV_MAIN_ID);

		// Patch up IE6
		if (ie6) {
			ie6Hover('li', $e.navMain);
			ie6Hover($('#topLinks li.secondary'));
		}

		// get the body element id...
		bodyId = $e.body.attr('id');
		// ...capitalize it...
		if (bodyId.length > 0) {
			bodyId = bodyId.substring(0,1).toUpperCase() + bodyId.substring(1, bodyId.length);
		}
		// ...and append it to the main nav id to find the active nav item
		activeId		= C.NAV_MAIN_ID + bodyId;
		$e.navActive	= $(activeId);

		XLI.Debug.log($e.navActive);

		// Apply the active class name to it.
		$e.navActive.addClass(C.ACTIVE_CLASS);

		/**
		 *	Identify the primary main nav items
		 */
		$e.primaryNavLis = $(C.NAV_MAIN_ID + '>li');

		/**
		 *	Apply a hover behavior to the primary nav items (channels) which hides the active subnav
		 */
		$e.primaryNavLis.each(function(i){

			var $this = $(this);
			this.$siblings	= this.$siblings || $this.siblings('li');
			this.$subnav	= this.$subnav || $this.find('>ul');

			$this.hover(
				function(){
					var $this = $(this);
					$this.addClass(C.ACTIVE_CLASS);
					this.$siblings.removeClass(C.ACTIVE_CLASS);
					if (XLI.Global.ie6 || XLI.Global.ie7) {
						this.$subnav.css('display','block');
					} else if ($e.visibleSubnav !== null) {
						$e.visibleSubnav.removeClass(C.VISIBLE_CLASS);
					}
				},
				function(){
					var $this = $(this);
					$this.removeClass(C.ACTIVE_CLASS);
					$e.navActive.addClass(C.ACTIVE_CLASS);
					if (XLI.Global.ie6 || XLI.Global.ie7) {
						this.$subnav.css('display','none');
						($e.navActive.get(0) || $e.primaryNavLis.eq(0).get(0)).$subnav.css('display','block');
					} else if ($e.visibleSubnav !== null) {
						$e.visibleSubnav.addClass(C.VISIBLE_CLASS);
					}
				}
			);
		});

		/**
		 *	If none of the primary nav items (channels) are active, show the home subnav
		 */
		if ($e.primaryNavLis.index($e.navActive.get(0)) == -1) {
			$e.visibleSubnav = $(C.NAV_MAIN_HOME_SUBNAV_ID + ">ul");
			$e.visibleSubnav.addClass(C.VISIBLE_CLASS);
		}

	};

	/**
	 * Compensates for the lack of :hover support on list elements in IE6 for the main navigation
	 * @param {String} element Element tag name or selector string to apply this fix to
	 * @param {Object} $scope jQuery object representing what to scope this function to.
	 * @public
	 * @returns nothing
	 */
	var ie6Hover = function(element, $scope) {

		if (!ie6) {
			return;
		}

		var $el = (typeof element.jquery === 'string') ? element : $(element, $scope);

		try {
			$el.each(function(i){
				var $this = $(this);
				$this.hover(
					function(){
						$(this).addClass(C.HOVER_CLASS);
					},
					function(){
						$(this).removeClass(C.HOVER_CLASS);
					}
				);
			});
		} catch(e) {}
	};

	/**
	 * Sets up Flyout menus to appear on click rather than hover.
	 * Also adds a close button to a flyout
	 * @private
	 * @returns nothing
	 */
        var openFlyout = null;

	var initFlyouts = function() {
		$e.flyouts = $(C.HEADER_ID + " ." + C.FLYOUT_CLASS);
		$e.flyouts.each(function(){

			var that = this,
				$this = $(this);

			// Create references to related elements
			this.$parent = $this.closest('li');	// The flyout's parent <li> element
			this.$subnav = $this.find('.' + C.FLYOUT_SUBNAV_CLASS); // flyout subnav (appears in the very top - weather, commuting alert flyouts)
			this.$close = (this.$subnav.length > 0) ? $(C.FLYOUT_SUBNAV_CLOSE_HTML) : $(C.FLYOUT_CLOSE_HTML); // generate a close button

                        // prevent the link that opens the overlay from changing page state
                        this.$parent.children('a').bind('click', function(e) {
                                e.preventDefault();
                        });

                        this.$parent.children('div').bind('click', function(e){
                                // stop propagation of div click event to prevent div from closing when clicked
                                if(e != null && typeof e.target !== 'undefined' && e.target.nodeName.toUpperCase() !== "A") {
                                        e.stopPropagation();
                                        return;
                                }

                                // only inside-the-flyout link clicks get here:
                                e.preventDefault();

                                var target = jQuery(e.target);

                                if(typeof target.attr("href") !== 'undefined' && target.attr("href") !== null) {
					XLI.Global.State.resetHashState();
                                        XLI.Global.State.setHashState("fo", this.$parent.attr("id"));
                                        XLI.Global.State.syncHashState();

                                        window.location.href = target.attr("href");
                                }
                        });

			// Open up the flyout on click
			this.$parent.bind('click', function(e){

				var p = this;

				 // Close any open flyouts
				$e.flyouts.each(function(){
					if (typeof this.$parent !== 'undefined' && typeof this.$parent.removeClass === 'function') {
						this.$parent.removeClass(C.FLYOUT_ACTIVE_CLASS);
					}
					// WTF Are you doing hiding the close buttons, IE? Hackery here.
					if (this.$parent.get(0) === p && (ie6 || ie7)) {
						this.$close
								.css('display','inline')
								.css('display','block');
					}
				});

                                // if flyout is open, and we clicked it again, keep it closed
                                if(XLI.Global.openFlyout === p.id) {
                                        XLI.Global.openFlyout = null;
                                        return;
                                }

                                XLI.Global.openFlyout = p.id;

				that.$parent.addClass(C.FLYOUT_ACTIVE_CLASS);

				// in the case of IE6, hide selects.
				if (ie6) {
					$e.selects = $e.selects || $('select');
					$e.selects.css('display','none');
				}

                                if (e.stopPropagation) {
                                        e.stopPropagation();
                                } else {
                                        e.cancelBubble = true;
                                }
			});

			// ...and set up a click action to close the popup
			this.$close.bind('click',function(e){
				e.stopPropagation(); // We need to cancel bubbling to prevent the $parent click event from being triggered.
				that.$parent.removeClass(C.FLYOUT_ACTIVE_CLASS);
				// in the case of IE6, show hidden selects.
				if (ie6) {
					$e.selects = $e.selects || $('select');
					$e.selects.css('display','');
				}

                                XLI.Global.State.resetHashState();
                                XLI.Global.State.syncHashState();

                                XLI.Global.openFlyout = null;
			});

			// This is for IE's benefit
			if (ie6) {
				this.$close.bind('mouseover mouseout', function(){
					$(this).toggleClass(C.HOVER_CLASS);
				});
			}


			// Attach the close button
			if (this.$subnav.length > 0) {
				this.$subnav.append(this.$close);
			} else {
				$this.prepend(this.$close);
			}
		});

                var openFlyoutId = XLI.Global.State.getHashState("fo");

                if(openFlyoutId !== null) {
                        jQuery("li#" + openFlyoutId).click();
                }
	};

	/**
	 * Sets default text in search fields.
	 * @private
	 * @returns nothing
	 */
	var initSearch = function() {
		$e.searchForms = $('form.' + C.SEARCH_FORM_CLASS + ', fieldset.' + C.AUTOHIDE_CLASS);
		$e.searchForms.each(function(){

			var that = this,
				$this = $(this);

			// Find the elements we'll need to work with.
			this.$label = $this.find('label');
			this.$input = $this.find('input[type=text]');

			// Drop out if we don't have what we need
			if (this.$label.length === 0 || this.$input.length === 0) {
				return;
			}

			// Set the form's default text
			this.defaultText = this.$label.text();

			// Set the input's value to the default text if it's blank
			if (this.$input.attr('value') === "") {
				this.$input.attr('value', this.defaultText);
			}

			// Set up focus and blur events which will show/hide the default text
			this.$input.bind('focus', function(){
				if (that.$input.attr('value') === that.defaultText) {
					that.$input.attr('value', "");
				}
			});

			this.$input.bind('blur', function(){
				if (that.$input.attr('value') === "") {
					that.$input.attr('value', that.defaultText);
				}
			});

		});
	};

	var initTabbedSections = function() {

		try {
			$e.tabbables = $('.' + C.TABBABLE_CLASS);

			if (!$e.tabbables.length) {
				return;
			}

			$e.tabbables.tabs();
		} catch(e) {
			XLI.Debug.error("Error initializing tabbable sections: " + e.message);
		}

	};

	/**
	 * Ininialize sections with a custom vertical scrollbar
	 * @private
	 * @returns nothing
	 */
	var initScrollables = function() {

		// Set the default options.
		$.fn.jScrollPane.defaults.scrollbarWidth = 12;
		$.fn.jScrollPane.defaults.scrollbarMargin = 5;
		$.fn.jScrollPane.defaults.dragMinHeight = 30;

		try {
			$e.scrollables = $('.' + C.SCROLLABLE_CLASS + ":not(." + C.DEFER_CLASS + ")");

			if (!$e.scrollables.length) {
				return;
			}

			$e.scrollables.jScrollPane();

		} catch(e) {
			XLI.Debug.error("Error initializing custom scrollbar sections: " + e.message);
		}

	};

	/**
		An array to add namespaced events to
	*/
	var customEventQueue = [];

	/**
	 * Registers a namespaced event to the custom event queue
	 * @public
	 * @param {String} event The namespaced event (i.e. 'load.adManager')
	 * @returns nothing
	 */
	var queueCustomEvent = function(event) {
		if (typeof event !== 'string') {
			return;
		}
		customEventQueue.push(event);
	};

	/**
	 * Queues custom namespaced events to fire after all other domReady events have fired.
	 * 	This method is a bit noisy, but it does ensure our firing order.
	 * @private
	 * @returns nothing
	 */
	var fireCustomEvents = function() {
		// If we still have events in the readyList,
		// try this function again in a few milliseconds.
		while (jQuery.readyList !== null) {
			window.setTimeout(fireCustomEvents, 10);
			return;
		}
		// fire custom events.
		$.each(customEventQueue, function(){
			$e.doc.trigger(this);
		});
	};

	/**
	 * Adds a content clearing div to the bottom of the content container. Necessary for IE6 and 7 only.
	 * @private
	 * @returns nothing
	 */
	var ieContentClear = function() {
		if (!ie6 && !ie7) {
			return;
		}
		$e.content = $(C.CONTENT_ID)
						.append(
							$('<div></div>').css('clear','both')
						);
	};


	/**
	 * This is a workaround for a Firefox/PC redraw bug with ads while animating.
	 * Simply adds a class to the carousel element. Dependent on CSS to hide iframes in the carousel while animating.
	 * @private
	 * @returns nothing
	 */
	var onAnimationStart = function(slide) {
		var $slide = $(slide);
		if (!XLI.Global.win || !XLI.Global.moz || !$slide.length) {
			return;
		}
		$slide.addClass(C.ANIMATING_CLASS);
	};

	/**
	 * This is a workaround for a Firefox/PC redraw bug with ads while animating.
	 * Simply removes a class from the carousel element. Dependent on CSS to hide iframes in the carousel while animating.
	 * @private
	 * @returns nothing
	 */
	var onAnimationEnd = function(slide) {
		var $slide = $(slide);
		if (!XLI.Global.win || !XLI.Global.moz || !$slide.length) {
			return;
		}
		$slide.removeClass(C.ANIMATING_CLASS);
	};

	return {

		/**
		 * Global Javascript initialization
		 * @public
		 */
		initialize : function() {
			if (initialized) {
				return;
			}

			initNav();
			initFlyouts();
			initTabbedSections();
			initScrollables();
			initSearch();
			ieContentClear();
			fireCustomEvents();

			initialized = true;
		},

		// We'll need these elsewhere, so make it public
		ie6Hover : ie6Hover,
		queueCustomEvent : queueCustomEvent,

		// Make some private vars public
		C : CONSTANTS,
		ie : ie,
		ie6 : ie6,
		ie7 : ie7,
		moz : moz,
		win : win

	};

}());


XLI.Global.State = (function() {
        /**
         *      Hash tag states
         */
        var hashStates = {};

        var initialized = false;

        var resetHashState = function() {
                hashStates = {};
        };

        var setHashState = function(key, value) {
                hashStates[key] = value;
        };

        var getHashState = function(key) {
                if(! initialized)
                        loadHashState();

                if(typeof hashStates[key] !== 'undefined') {
                        return hashStates[key];
                } else {
                        return null;
                }
        };

        var syncHashState = function(key) {
                if(! initialized)
                        loadHashState();

                var hash = "";

                for(var k in hashStates) {
                        var v = hashStates[k];

                        if(v === null || typeof v === 'undefined')
                                continue;

                        hash = hash + "/" + k + ":" + v;
                }

                window.location.hash = hash;
        };

        var loadHashState = function() {
                if(initialized)
                        return;

                var states = window.location.hash.split('/');

                for(var i = 0; i < states.length; i++) {
                        if(states[i] === null || typeof states[i] === 'undefined' || states[i] === "" || states[i] === "#")
                                continue;

                        var state = states[i].split(":");

                        if(state[0] === null || typeof state[0] === 'undefined' || state[0] === "")
                                continue;

                        hashStates[state[0]] = state[1];
                }

                initialized = true;
        };

        return {
                resetHashState: resetHashState,
                setHashState: setHashState,
                getHashState : getHashState,
                syncHashState : syncHashState,
                initialize: loadHashState
        }
}());


/**
 * Manages the varous carousels on the site.
 * @class
 */
XLI.CarouselManager = (function() {

	/**
	 * An array of registered carousels
	 * @private
	 */
	var queue = [];

	var domReadyQueueProcessed = false;

	/**
	 * Goes through the carouse queuel and initializes each one. Run on DOMReady.
	 * @private
	 * @param {Boolean} domload Used to set domReady flag
	 * @returns nothing
	 */
	var loadQueue = function(domload) {

		var $target, curr;

		if (domload) {
			domReadyQueueProcessed = true;
		}

		while (queue.length) {
			curr = queue[0];
			try {
				// Create a jQuery object from the selector
				$target = $(curr.target);
				// Initialize the carousel
				if ($target.length > 0) {
					$target.jcarousel(curr.config);
				}
			} catch(e) {
				XLI.Debug.error("Error trying to initialize Carousel: " + e.message + "\nTried converting " + curr.target + " to a Carousel");
				return;
			}
			queue.shift();
		}
	};

	return {

		/**
		 * Registers a carousel. Can be called at any time prior to DOMReady
		 * @public
		 * @param String target A css selector that targets the element(s) where an ad should be rendered (i.e. "ul.carousel li.ad")
		 * @param Object config  An object with carousel configuration options. Object should take the following form:
		 *
		 *		{						// Configuration options available in the carousel engine. See /js/libs/jquery.jcarousel.js
		 *			scroll : 1,
		 *			animation : 'fast'
		 *		}
		 *
		 * @returns nothing
		 */
		register : function(target, config) {

			if (typeof target !== 'string' || (typeof config !== 'undefined' && typeof config !== 'object')) {
				return;
			}

			// Set up an empty config object if need be.
			config = config || {};

			// Add the carousel options to the queue
			queue.push({
				target: target,
				config: config
			});

			// Register the queue to load if needed
			if (queue.length === 1) {
				if (!domReadyQueueProcessed) {
					// If we're pre-domready
					$(document).bind('load.carouselManager', function() {
						loadQueue(true);
					});
				} else {
					// otherwise process the queue as soon as something is added.
					loadQueue();
				}
			}

		}

	};

}());



/**
 * Sets up advertisement rendering
 * @class
 */
XLI.AdManager = (function(){

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

		// Doubleclick URL for iframes
		DBLCLK_IFRAME_URL : 'http://ad.doubleclick.net/adi/cblvsn.',

		// Doubleclick URL for constructing script tags
		DBLCLK_SCRIPT_URL : 'http://ad.doubleclick.net/adj/cblvsn.',

		// Basic iFrame HTML for setting up new ads.
		IFRAME_PROTO : '<iframe src="" marginwidth="0" marginheight="0" hspace="0" vspace="0" frameborder="0" scrolling="no" bordercolor="#000000"></iframe>',

		// Script tag prototype
		SCRIPT_PROTO : '<script type="text/javascript" charset="utf-8"></script>',

		// Iframe ID Prefix
		IFRAME_ID_PREFIX : 'dclk'

	};

	/**
	 *	Shorthand reference to the CONSTANTS
	 */
	var C = CONSTANTS;

	/**
	 * An array of registered ad calls
	 * @private
	 */
	var queue = [];

	/**
	 * A random number to tie together the ads on the page
	 * @private
	 */
	var ord = Math.random() * 10000000000000000;

	/**
	 * The zone used in tag generation
	 * @private
	 */
	var zone = "nwsd/home";

	/**
	 * Goes through the ad queue and processes each one, either rendering an ad
	 * or if it appears in a carousel, registering the ad with the carousel. Runs on DOMReady.
	 * @private
	 * @returns nothing
	 */
	var processQueue = function() {

		if (!XLI.AdManager.enabled) {
			return;
		}

		$.each(queue, function(){
			var that = this,
				$target, $parentCarousel, el;

			$target = $(this.target);
			if ($target.length === 0) {
				return;
			}

			$parentCarousel = $target.parents('.carousel');

			if ($parentCarousel.length > 0) {

				// Apply slide-specific tag parameters
				$target.each(function(){
					var $slide = $(this);
					if (!$slide.data('tagParams')) {
						$slide.data('tagParams', that.tag_params);
					}
				});

				// The carousel element.
				el = $parentCarousel.get(0);

				if (typeof el.jCarousel === 'object' &&
					typeof el.jCarousel.registerAdConfig === 'function')
				{
					el.jCarousel.registerAdConfig(this.config);
				} else {
					if (typeof el.adConfig === 'object' &&
						typeof el.adConfig.width !== 'undefined' &&
						typeof el.adConfig.height !== 'undefined')
					{
						return;
					}
					XLI.Debug.log('Carousel not initialized. Adding Ad Data to Carousel ad Queue');
					el.adConfig = this.config;
				}
			} else {
				try {
					XLI.AdManager.render($target, this.config, this.tag_params);
				} catch(e) {
					XLI.Debug.error("Error rendering ad: " + e.message);
				}
			}
		});

		// Empty the queue
		queue = [];
	};

	return {

		/**
			Flag to turn ad rendering on or off globally.
		*/
		enabled : true,

		/**
		 * Registers an ad to be displayed
		 * @public
		 * @param String target A css selector that targets the element(s) where an ad should be rendered (i.e. "ul.carousel li.ad")
		 * @param Object config An object with the width and height of the ad.
		 *			Should conform to standard ad sizes and take the following form:
		 *
		 *		{
		 *			width : 300,
		 *			height : 250
		 *		}
		 *
		 * @param {Object} tag_params An object containing any additional parameters you want to apply to the ad URLs
		 * @returns nothing
		 */
		register : function(target, config, tag_params) {

			if (typeof target !== 'string' || typeof config !== 'object' || typeof config.width === "undefined" || typeof config.height === "undefined") {
				return;
			}

			tag_params = tag_params || {};

			queue.push({
				target : target,
				config : config,
				tag_params : tag_params
			});

			// Register the queue to load if needed
			if (queue.length === 1) {
				$(document).bind('load.adManager', processQueue);
			}
		},


		/**
		 * Renders an ad
		 * @public
		 * @param {Object} $target A jQuery object
		 * @param {Object} config An object containing the width and height of an ad
		 * @param {Object} tag_params An object containing any additional parameters you want to apply to the ad URLs
		 * @returns nothing
		 */
		render : function($target, config, tag_params) {

			if (typeof $target === 'object' && typeof $target.jquery !== 'string') {
				return;
			}

			$target.each(function(){

				// drop out if we've already rendered an ad in this element
				if (this.adRendered) {
					return;
				}

				var $this = $(this),
					$iframe = $(C.IFRAME_PROTO),
					$script = $(C.SCRIPT_PROTO),
					scriptUrl = C.DBLCLK_SCRIPT_URL + zone +
							";aid=" +
							";sz=" + config.width + "x" + config.height +
							";abr=!ie",
					iframeUrl = C.DBLCLK_IFRAME_URL + zone +
							";aid=" +
							";sz=" + config.width + "x" + config.height;

				if (typeof tag_params === 'object') {
					for(var i in tag_params) {
						var k = i;
						var v = tag_params[k];

						scriptUrl = scriptUrl + ";" + k + "=" + v;
						iframeUrl = iframeUrl + ";" + k + "=" + v;
					}
				}

				// add these back to the end
				scriptUrl += ';ord=' + ord + '?';
				iframeUrl += ';ord=' + ord + '?';

				$iframe
					.attr('id', C.IFRAME_ID_PREFIX + ord)
					.attr('height', config.height)
					.attr('width', config.width)
					.attr('src', iframeUrl);

				if (navigator.userAgent.indexOf("Gecko") === -1) {
					$script.attr('src', scriptUrl);
					$iframe.append($script);
				}

				$this.append($iframe);
				this.adRendered = true;
			});

		},

		/**
		 * Overrides the internally generated ord. Should be called before ads are rendered.
		 * @public
		 * @param {String or Integer} newOrd The ord you want to set. A number is expected, anything else will be ignored.
		 * @returns nothing
		 */
		setOrd : function(newOrd) {
			newOrd = parseInt(newOrd, 10);
			if (isNaN(newOrd)) {
				return;
			}
			ord = newOrd;
		},

		setZone : function(newZone) {
			zone = newZone;
 		}

	};

}());


/**
 * HTML Builder for constructing carousel slides and similar content
 */
XLI.Builder = (function(){

	/**
		Some Constants
	*/
	var CONSTANTS = {

		// Info paragraph class
		INFO_CLASS : "info",

		// Category element class
		CATEGORY_CLASS : "category",

		// Time Element class
		TIME_CLASS : "time",

		// Headline element class
		TITLE_CLASS : "title",

		// Comment element class
		COMMENT_CLASS : "comments",

		// Summary, or blurb, class
		BLURB_CLASS : "summary",

		// Rating Class Prefix
		RATING_CLASS_PREFIX : "stars",

		// Breaking News Class
		BREAKING_CLASS : "breaking",

		// breaking news text
		BREAKING_TEXT : "Breaking News",

		// Media type wrapper class
		MEDIA_WRAPPER_CLASS : "mediaType",

		// Media type class prefix
		MEDIA_CLASS_PREFIX : "type",

		// Button Class Name
		BUTTON_CLASS : "button"

	};

	/**
		HTML Seeds for creating elements
	*/
	var HTML = {
		DIV : "<div></div>",
		SPAN : "<span></span>",
		UL : "<ul></ul>",
		LI : "<li></li>",
		A : "<a></a>",
		IMG : "<img />",
		EM : "<em></em>",
		STRONG : "<strong></strong>",
		H3 : "<h3></h3>",
		H4 : "<h4></h4>",
		P : "<p></p>"
	};

	/**
	 *	Shorthand references to the CONSTANTS and HTML.
	 */
	var C	= CONSTANTS;
	var H	= HTML;

	/**
	 * Truncates the length of a string based on word count.
	 * @public
	 * @param {String} blurb The text to truncate
	 * @param {Number} len The word count to truncate to.
	 * @returns The truncated text
	 * @type String
	 */
	var setWordCount = function(blurb, len) {
		if (typeof blurb !== 'string' || typeof len !== 'number') {
			return;
		}

		var temp = "",
			words = blurb.split(' ');

		if (words.length <= len) {
			return blurb;
		}

		for (var x = 0; x < len; x++) {
			temp += words[x];
			if (x < (len - 1)) {
				temp += " ";
			}
		}
		temp += "\u2026"; // That there's an ellipsis.
		return temp;
	};

	return {

		/**
		 * Creates a jQuery object representation of image element, wrapped in a link if provided
		 * @public
		 * @param {Object} data An object representing the specifics of this image. Should be like so:
		 *
		 *  {
		 *  	url : '/path/to/image',
		 *  	title : 'alt text for image',
		 *  	link : '/url/where/you/want/to/link/to'
		 *  }
		 *
		 * @returns jQuery object representing the image or false
		 * @type Object or false
		 */
		image : function(data) {

			var $link, $img = false;

			if (data.url && data.url !== "") {
				$img = $(H.IMG)
							.attr('src', data.url)
							.attr('alt', data.title);

				if (data.link && data.link !== "") {
					$link = $(H.A)
								.attr('href', data.link)
								.append($img);

					$img = $link;

				}
			}

			return $img;
		},

		/**
		 * Creates a jQuery object representation of an info paragraph with a category and timestamp
		 * @public
		 * @param {Object} data An object representing the specifics of the info paragraph. Should be like so:
		 *
		 *  {
		 *  	category : {
		 *  		title : 'category title'
		 *  		link : '/path/to/category/
		 *  	},
		 *  	time : 'timestamp'
		 *  }
		 *
		 * @returns jQuery object representing the the info paragraph or false
		 * @type Object or false
		 */
		categoryAndTime : function(data) {

			var $category, $time, $info = false;

			if ((data.category && (data.category.title && data.category.title !== "")) || data.time && data.time !== "") {
				$info = $(H.P).addClass(C.INFO_CLASS);
				if (data.category && data.category.title) {

					if (data.category.link && data.category.link !== "") {
						$category = $(H.A).attr('href', data.category.link);
					} else {
						$category = $(H.STRONG);
					}

					$category
						.addClass(C.CATEGORY_CLASS)
						.text(data.category.title);

					$info
						.append($category)
						.append('&nbsp;');
				}

				if (data.time && data.time !== "") {
					$time = $(H.EM)
								.addClass(C.TIME_CLASS)
								.text(data.time)
								.appendTo($info);
				}
			}

			return $info;

		},

		/**
		 * Creates a jQuery object representation of a star rating
		 * @public
		 * @param {String or Number} rating The number of stars in a rating between 0 and 5:
		 * @returns jQuery object representing the the rating HTML
		 * @type Object or false
		 */
		rating : function(rating) {

			var $rating = false,
				rating = parseInt(rating, 10);

			if (!isNaN(rating) && rating <= 5 && rating >= 0) {
				$rating = $(H.SPAN)
							.addClass(C.RATING_CLASS_PREFIX + rating)
							.text(rating + " " + C.RATING_CLASS_PREFIX);
			}

			return $rating;
		},

		/**
		 * Creates a jQuery object representation of a headline, linked if necessary
		 * @public
		 * @param {Object} data An object representing the specifics of the headline element. Should be like so:
		 *
		 *  {
		 *  	title : 'Your Headline Text',
		 *  	link : '/url/where/you/want/to/link/to',
		 *		imageURL : '/an/image/url/',
		 *		classSuffix : 'aSuffixToAddToTheClassNameIfTheresAnImage'
		 *  }
		 *
		 * @param {String} tag If you don't want the result wrapped in an H3 (default), specify an alternate tag with this param (i.e. '<h2></h2>')
		 * @returns jQuery object representing the the headline element or false
		 * @type Object or false
		 */
		title : function(data, tag) {

			var $link,
				tag = tag || H.H3,
				$title = false;

			if (data.title && data.title !== "") {
				$title = $(tag)
							.addClass((data.imageURL && data.imageURL !== "") ? C.TITLE_CLASS : (C.TITLE_CLASS + (data.classSuffix || "")));
				if (data.link && data.link !== "") {
					$link = $(H.A)
								.attr('href', data.link)
								.text(data.title)
								.appendTo($title);
				} else {
					$title.text(data.title);
				}
			}

			return $title;

		},

		/**
		 * Creates a jQuery object representation of a blurb, or summary, paragraph.
		 * You can also truncate it to a specific word count if you'd like.
		 * @public
		 * @param {Object} data An object representing the specifics of the summary paragraph
		 *
		 *  {
		 *  	txt : 'Your blurb text',
		 *  	wordCount : 40
		 *  }
		 *
		 * @returns jQuery object representing the the summary paragraph or false
		 * @type Object or false
		 */
		blurb : function(data) {

			var text, $blurb = false;

			if (data.txt && data.txt !== "") {
				$blurb = $(H.P).text((typeof data.wordCount === 'number') ? setWordCount(data.txt, data.wordCount) : data.txt);
				$blurb.addClass(C.BLURB_CLASS);
			}
			return $blurb;
		},

		/**
		 * Creates a jQuery object representation of a comment paragraph.
		 * @public
		 * @param {Object} data An object representing the specifics of the summary paragraph
		 *
		 *  {
		 *  	commentCount : 40,
		 *  	link : '/url/where/you/want/to/link/to'
		 *  }
		 *
		 * @param {String} tag If you don't want the result wrapped in a paragraph (default), specify an alternate tag with this param (i.e. '<li></li>')
		 * @returns jQuery object representing the the summary paragraph or false
		 * @type Object or false
		 */
		comments : function(data, tag) {

			var text, $link,
				tag = tag || H.P,
				$comments = false;

			if (data.commentCount && data.commentCount !== "") {
				var text = data.commentCount;
				$comments = $(tag).addClass(C.COMMENT_CLASS);
				if (data.link && data.link !== "") {
					$link = $(H.A)
								.attr('href', data.link)
								.text(text)
								.appendTo($comments);
				} else {
					$comments.text(text);
				}
			}

			return $comments;
		},

		/**
		 * Creates a jQuery object representation of the media links.
		 * @public
		 * @param {Object} data An object representing the specifics of the media links
		 *
		 *  {
		 *		'video' : '/url/to/video/'
		 *		'photo' : '/url/to/photos/'
		 *	}
		 *
		 * @param {String} tag If you don't want the result wrapped in a paragraph (default), specify an alternate tag with this param (i.e. '<li></li>').
		 * @returns jQuery object representing the media links or false
		 * @type Object or false
		 */
		mediaLinks : function(data, irClass) {

			var className, $link, $media = false;

			irClass = irClass || XLI.Global.C.IR_CLASS;

			if (data) {
				$.each(data, function(key, value) {
                                        if(typeof value === "undefined")
                                                return;

					className = C.MEDIA_CLASS_PREFIX + (key.substring(0,1).toUpperCase() + key.substring(1,key.length)).replace(/s$/i, '');

					if (!$media) {
						$media = $(H.P).addClass(C.MEDIA_WRAPPER_CLASS);
					}

					$link = $(H.A)
								.attr('href', value)
								.addClass(irClass)
								.addClass(className)
								.text(key)
								.append(H.SPAN)
								.appendTo($media);
				});
			}

			return $media;
		},

		/**
		 * Button builder.
		 * @public
		 * @param {Object} data An object representing the specifics of the button link
		 *
		 *  {
		 *		'txt' : 'Link Text'
		 *		'link' : '/url/to/destination/'
		 *	}
		 *
		 * @param {String or Boolean} tag If you don't want the result wrapped in a paragraph (default), specify an alternate tag with this param (i.e. '<li></li>'). Can also pass false to not create a wrapper.
		 * @returns jQuery object representing the link or false
		 * @type Object or false
		 */
		button : function(data, tag) {

			var tag = tag || H.P,
				$wrap = false,
				$button = false;

			if (typeof tag === 'string' && tag !== "") {
				$wrap = $(tag);
			}

			if (data.link && data.link !== "" && data.txt && data.txt !== "" ) {
				$button = this.link({
					txt : data.txt,
					link : data.link
				}).addClass(C.BUTTON_CLASS);

				if ($wrap.length) {
					$wrap.append($button);
					return $wrap;
				}
			}

			return $button;
		},

		/**
		 * Generic link builder.
		 * @public
		 * @param {Object} data An object representing the specifics of the link
		 *
		 *  {
		 *		'txt' : 'Link Text'
		 *		'link' : '/url/to/destination/'
		 *	}
		 *
		 * @returns jQuery object representing the link or false
		 * @type Object or false
		 */
		link : function(data) {

			var $link = false;

			if (data.link && data.link !== "" && data.txt && data.txt !== "" ){
				$link = $(H.A)
							.attr('href', data.link)
							.text(data.txt);
			}

			return $link;

		},

		/**
			Making a private function public
		 */
		setWordCount : setWordCount
	};

}());


/**
 * Sets up the Debug mode
 * @class
 */
XLI.Debug = (function(){

	/**
	 * Global flag to turn the debug mode on or off.
	 * @private
	 */
	var enabled = false;

	/**
	 * Whether or not we want to throw alerts in IE6 and 7 when debug mode is on
	 * @private
	 */
	var ieAlerts = false;

	return {

		/**
		 * Logs a message to the console if one is available
		 * @public
		 * @param {String|Object|Array|Boolean|Number} thing Any content you want displayed in the console.
		 * @returns nothing
		 */
		log : function(thing) {
			if (enabled) {
				try {
					console.log.apply(console, arguments);
				} catch (e) {
					if ((XLI.Global.ie6 || XLI.Global.ie7) && ieAlerts) {
						alert ('LOG: ' + thing);
					}
				}
			}
		},
		/**
		 * Logs an error message to the console if one is available
		 * @public
		 * @param {String|Object|Array|Boolean|Number} thing Any content you want displayed in the console.
		 * @returns nothing
		 */
		error : function(thing) {
			if (enabled) {
				try {
					console.error.apply(console, arguments);
				} catch (e) {
					if ((XLI.Global.ie6 || XLI.Global.ie7) && ieAlerts) {
						alert ('ERROR: ' + thing);
					}
				}
			}
		},

		/**
		 * Displays all the properties of something in the console if one is available
		 * @public
		 * @param {String|Object|Array|Boolean|Number} thing Any content you want displayed all the properties of in the console.
		 * @returns nothing
		 */
		dir : function(thing) {
			if (enabled) {
				try {
					console.dir(thing);
				} catch (e) {
					if (XLI.Global.ie6 || XLI.Global.ie7 && ieAlerts) {
						alert ('DIR: ' + thing);
					}
				}
			}
		},

		/**
		 * Logs an informational message to the console if one is available
		 * @public
		 * @param {String|Object|Array|Boolean|Number} thing Any content you want displayed all the properties of in the console.
		 * @returns nothing
		 */
		info : function(thing) {
			if (enabled) {
				try {
					console.info(thing);
				} catch (e) {
					if ((XLI.Global.ie6 || XLI.Global.ie7) && ieAlerts) {
						alert ('INFO: ' + thing);
					}
				}
			}
		}
	};

}());


/*
 * Run the Global init when the dom is loaded
 */
$(document).ready(XLI.Global.State.initialize);
$(document).ready(XLI.Global.initialize);

/**
	Push custom events to the queue
*/
XLI.Global.queueCustomEvent('load.adManager');
XLI.Global.queueCustomEvent('load.carouselManager');
