/**
 * @fileoverview	newsday.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 Newsday = window.Newsday || {};

/**
 * Sets up the functionality common to most areas of the site.
 * @class
 */
Newsday.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",
		
		// 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 = {
		
		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);
		}
		
		// 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);
		
		// Apply the active class name to it.
		$e.navActive.addClass(C.ACTIVE_CLASS);
		
		/**
		 *	Identify the primary main nav items without a class of "secondary"
		 */
		$e.primaryNavLis = $(C.NAV_MAIN_ID + ' > li:not(.' + C.SECONDARY_CLASS + ')');
		
		/**
		 *	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:not(.secondary)');
			
			$this.bind('mouseover', function(){
				
				var $this = $(this);
				
				$this.addClass(C.ACTIVE_CLASS);
				this.$siblings.removeClass(C.ACTIVE_CLASS);
				if ($e.visibleSubnav !== null) {
					$e.visibleSubnav.removeClass(C.VISIBLE_CLASS);
				}
			});	
			$this.bind('mouseout', function(){
				
				var $this = $(this);
				
				$this.removeClass(C.ACTIVE_CLASS);
				$e.navActive.addClass(C.ACTIVE_CLASS);
				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 + " ." + C.SUBNAV_CLASS);
			$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.bind('mouseover mouseout', function(){
					$(this).toggleClass(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 default on any links adjacent to the flyout
			this.$parent.children('a').bind('click', function(e){
				e.preventDefault();
			});

			this.$parent.children('div').bind('click', function(e){
				if(e != null && typeof e.target !== 'undefined' && e.target.nodeName.toUpperCase() !== "A") {
					if (e.stopPropagation) {
						e.stopPropagation();
					} else {
						e.cancelBubble = true;
					} 
				}
			});
			
			// 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(Newsday.Global.openFlyout === p.id) {
					Newsday.Global.openFlyout = null;
					return;
				}

				Newsday.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','');
				}

				Newsday.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);
			}
		});
	};
	
	/**
	 * 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) {
			Newsday.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 = 13;
		$.fn.jScrollPane.defaults.scrollbarMargin = 10;
		$.fn.jScrollPane.defaults.showArrows = true;
		$.fn.jScrollPane.defaults.arrowSize = 16;
		$.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) {
			Newsday.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);
		});
	};
	
	return {
		
		/**
		 * Global Javascript initialization
		 * @public
		 */
		initialize : function() {
			if (initialized) {
				return;
			}
			
			initNav();
			initFlyouts();
			initTabbedSections();
			initScrollables();
			initSearch();
			fireCustomEvents();
			
			initialized = true;
		},
		
		// We'll need this 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
		
	};
	
}());

/**
 * Manages the varous carousels on the site.
 * @class
 */
Newsday.CarouselManager = (function() {
	
	/**
	 * An array of registered carousels
	 * @private
	 */
	var queue = [];
	
	var domReadyQueueProcessed = false;
	
	var DOC = $(document);
	
	/**
	 * 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) {
		
		Newsday.Debug.info("Loading Carousel Queue");
		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) {
				Newsday.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/exploreli/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
					DOC.bind('load.carouselManager', function() {
						loadQueue(true);
					});
				} else {
					// otherwise process the queue as soon as something is added.
					loadQueue();
				}
			}
			
		}
		
	};
	
}());



/**
 * Sets up advertisement rendering
 * @class
 */
Newsday.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 (!Newsday.AdManager.enabled) {
			return;
		}
		
		Newsday.Debug.info("Processing Ad Queue");
		
		$.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;

					}
					Newsday.Debug.log('Carousel not initialized. Adding Ad Data to Carousel ad Queue');
					el.adConfig = this.config;
				}
			} else {
				try {
					Newsday.AdManager.render($target, this.config, this.tag_params);
				} catch(e) {
					Newsday.Debug.log("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 + 
							";sz=" + config.width + "x" + config.height +
							";abr=!ie",
					iframeUrl = C.DBLCLK_IFRAME_URL + zone + 
							";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
				iframeUrl = iframeUrl + ";ord=" + ord + "?";
				scriptUrl = scriptUrl + ";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
 */
Newsday.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",
		
		// 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"
		
	};
	
	/**
		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 an "Breaking News" headline
		 * @public
		 * @returns jQuery object representing the the "Breaking News" headline or false
		 * @type Object or false
		 */
		breakingNews : function() {
			return $(H.H4)
						.addClass(C.BREAKING_CLASS)
						.text(C.BREAKING_TEXT);
		},
		
		/**
		 * 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'
		 *  }
		 *
		 * @returns jQuery object representing the the headline element or false
		 * @type Object or false
		 */
		title : function(data) {
			
			var $link, $title = false;
			
			if (data.title && data.title !== "") {
				$title = $(H.H3)
							.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);
			}
			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)
								.attr('title', 'Comments')
								.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/'
		 *	}
		 *
		 * @returns jQuery object representing the media links or false
		 * @type Object or false
		 */
		mediaLinks : function(data) {
			var className, $link, $media = false;
			
			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(Newsday.Global.C.IR_CLASS)
								.addClass(className)
								.text(key)
								.append(H.SPAN)
								.appendTo($media);
				});
			}
			
			return $media;
		},
		
		/**
		 * 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
 */
Newsday.Debug = (function(){
	
	/**
	 * Global flag to turn the debug mode on or off.
	 * @private
	 */
	var enabled = 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() {
			if (enabled) {
				try {
					console.log.apply(console, arguments);
				} catch (e) {}
			}
		},		
		/**
		 * 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() {
			if (enabled) {
				try {
					console.error.apply(console, arguments);
				} catch (e) {}
			}
		},
				
		/**
		 * 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() {
			if (enabled) {
				try {
					console.dir.apply(console, arguments);
				} catch (e) {}
			}
		},
		
		/**
		 * 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() {
			if (enabled) {
				try {
					console.info.apply(console, arguments);
				} catch (e) {}
			}
		}
	};
	
}());


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

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