/**
 *		Version:	1.0.0
 */

/*#	Check if a TVListings object and some if its properties already
	exists and if not initialise them	*/
if (!TVListings) {	var TVListings = {};}

if (!TVListings.properties) {	
	TVListings.properties = {};
	TVListings.properties.params = {};
	TVListings.properties.cookie_prefix = "dnitvl_";
	TVListings.properties.defaults = {};
} else {	
	if (!TVListings.properties.cookie_prefix) {
		TVListings.properties.cookie_prefix = "dnitvl_";
	}	
	if (!TVListings.properties.params) {
		TVListings.properties.params = {};
	}
}


/* Service Location & Info Defaults */
	TVListings.appname = "TV Schedule";
	TVListings.servlet_address = "/dni-tvlistings/";
	TVListings.listings_address = "/tv-schedule/";
	TVListings.image_path = "/tier0/images/common/dnitvl";
	TVListings.style_suffix = "";
	TVListings.style_prefix = "";
	TVListings.type = "";
	TVListings.loader_style = "";
	TVListings.show_datepicker = true;
	TVListings.enable_flip = false;
	TVListings.carousel = false;


/**
 * Adds a parameter to be used and retreived from various sources
 * @param {String} name 	Name to use for the TVListings.properties.param key
 * @param {Object} regex	Optional. Regular expression to override the default
 * @param {Integer} pos		Optional. Regex group from the match method to use
 */
TVListings.properties.add_parameter = function (name, pos, regex) {
	//create an object to hold the parameter's settings
	var param = {'name': name.toLowerCase()};
	
	//if custom regex for the get_url_parameter method then assign that
	if (regex) { param.regex = regex; param.pos = pos;}
	
	//add it
	this.params[name] = param;
}

/**
 * Uses the information in the TVListings.properties object's params property to retrieve information from the current URL
 * @param {Object} name		Name of the TVListings.properties.params key
 * @param {String} url		Optional. Specify a URL to get the information frmo instead of the current URL
 */
TVListings.properties.get_url_parameter = function (name, url) {
	var param = this.params[name];
	if (!url) var url = document.location.href;
	
	//if custom regex has been supplied then use that, otherwise use default regex
	if (param.regex) {
		var url_regex = RegExp(param.regex);
		var pos = param.pos;
	} else {
		var url_regex = RegExp('(.*)(\\?|&)' + param.name + '=([^&]*)($|&)(.*)');
		var pos = 3;
	}
	
	//try and get the information from the URL, otherwise return null
	var value = null;	
	try { value = url.match(url_regex)[pos];}
	catch(e) { value = null;}
	
	return value;
}

/**
 * Uses the information in the TVListings.properties object to retrieve cookie information from the browser
 * @param {Object} name		Name of the TVListings.properties.params key
 */
TVListings.properties.get_cookie_parameter = function (name, ovr) {
	if (!ovr) {
		var param = this.params[name];
		
		//if a prefix for settings cookies has been set, use it
		if (this.cookie_prefix) name = this.cookie_prefix + name;
	}

	var cookie_regex = RegExp('(^|.*; )' + name + '=([^;]*).*');

	//try and get the information from document.cookie, otherwise return null
	var value = null;
	try { value = document.cookie.match(cookie_regex)[2];} 
	catch(e) { value = null;}
	
	return value;
}

/**
 * Creates TVListings property param object from a string (kind of like a miroformat)
 * Class Name syntax is: name__value
 * @param {String} str		String to extract information from
 * @param {String} delimit	A delimiter used to seperate properties
 */
TVListings.properties.get_info_from_string = function (str, delimit) {
	if (!delimit) var delimit = " ";
	var isparam = RegExp('(.*)__(.*)');
	var arr = {}
	if (str&& str.length > 0)
	{	
		//Create an array of the element's classes
		var str = str.split(delimit);
		//loop through classes and add them to arr if they are parameters
		for (var i=str.length-1,k;k=str[i];i--) {
			var tmp = k.match(isparam);
			if (tmp) {
				arr[tmp[1]] = tmp[2];
			}
		}
	}
	//return the array object of parameters
	return arr;
}

/**
 * Gathers information for a TVListings.properties.params object and returns an array
 */
TVListings.properties.process_parameters = function (params) {
	var value = {};
	var chnl_fix = 0;
	if (!params) var params = this.params;
	if (this.defaults) var ovr = true;
	for (var i in params) { //loop through params
		var j, k = params[i];
		var name = k.name;
		if ((ovr) && (this.defaults[name]) && (this.defaults[name].value !== "") && (this.defaults[name].override)) { //look for a master override
				value[name] = this.defaults[name].value;
		} else if ((j = this.get_url_parameter(name)) && (j.value !== "")) { //next try looking in the url
			value[name] = j;
		} else if ((j = this.get_cookie_parameter(name)) && (j.value !== "")) { //lastly try cookies
			value[name] = j;
		} else if ((ovr) && (this.defaults[name]) && (this.defaults[name].value !== "")) {
			//having failed, try to return a suggested value
			value[name] = this.defaults[name].value;
			if (name == "channel_code") chnl_fix++;
			if (name == "country_code") chnl_fix--;
		}
	}
	
	if ((value.country_code)&&(chnl_fix == 1)) {
		delete value.channel_code;
	}
	
	return value;	
}

/**
 * Sets a cookie for a TVListings.properties.params object
 * @param {Object} arr		Array of key-value pairs, keys corresponding to TVListings.properties.params keys
 * @param {Object} exp		Optional. The expiration date of the cookie, formatted by toGMTString()
 */
TVListings.properties.set_cookie_parameters = function (arr, exp) {
	if (!exp) var exp = new Date(new Date().getFullYear()+20, 1, 1).toUTCString();
	for (i in arr) {
		var k = arr[i];
		var name = this.params[i].name;
		if (this.cookie_prefix) { name = this.cookie_prefix + name;}
		
		//assemble the cookie string
		var value = name + "=" + k + "; ";
		value += "expires=" + exp + "; "; //if an expiry date has been specified add that
		value += "path=/; ";
		
		//set cookie
		document.cookie = value;
	}
}

/**
 * Deletes a cookie for a TVListings.properties.params object
 * @param {Object} arr		Array of key-value pairs, keys corresponding to TVListings.properties.params keys
 */
TVListings.properties.remove_cookie_parameters = function (arr) {
	for (i in arr) {
		var k = arr[i];
		var name = this.params[i].name;
		if (this.cookie_prefix) { name = this.cookie_prefix + name;}
		
		//assemble the cookie string
		var exp = new Date().toUTCString();
		var value = name + "=" + k + ";";
		value += "expires=" + exp + ";; "; //if an expiry date has been specified add that

		//set cookie
		document.cookie = value;
	}
}
/**
 * Adds a TVListings.properties.params object and a value to the current url
 * @param {Object} name		Name of the TVListings.properties.params key
 * @param {Object} val		Value to set the parameter to
 * @param {String} url		Optional. Specify a URL to change instead of the current URL
 */
TVListings.properties.add_url_parameter = function (name, val, url) {
	var param = this.params[name];
	var val = val;
	
	//if no URL was specified use the current URL
	if (!url) url = document.location.href;
	
	var tmp_str = "";
	//check if the current url already has a query string
	if (url.match(RegExp('\\?'))) {
		tmp_str = "&"
	} else { tmp_str = "?"}
	
	//prepare the parameter
	tmp_str += param.name + "=" + val;
		
	//add the parameter
	var value = null;
	value = url + tmp_str;

	return value;
}

/**
 * Changes a TVListings.properties.params object's value in the current URL and returns the new URL
 * @param {Object} name		Name of the TVListings.properties.params key
 * @param {Object} val		Value to change the parameter to
 * @param {String} url		Optional. Specify a URL to change instead of the current URL
 */
TVListings.properties.remove_url_parameter = function (name, url) {
	var param = this.params[name];
	
	//if no URL was specified use the current URL
	if (!url) url = document.location.href;
	
	//if custom regex has been supplied then use that, otherwise use default regex
	if (param.regex) {
		var url_regex = RegExp(param.regex);
		var pos = param.pos;
	} else {
		var url_regex = RegExp('(.*)(\\?|&)' + param.name + '=([^&]*)($|&)(.*)');
		var pos = 3;
	}
	
	var regex_fix = RegExp('(.*)&$');
	
	var count = null, value = null;
	//get the amount of matches the regex returns
	try { count = url.match(url_regex).length-1;}
	catch(e) { count = null;}
	
	//if there were matches found in the URL replace the match at position param.pos
	if ((count !== null) && (count >= 0)) {
		var tmp_str = "";
		for (var i = 1; i <= count; i++) {
			if ((i !== pos) && (i !== pos+1)) { tmp_str += "$" + i;}
		}
		value = url.replace(url_regex, tmp_str).replace(regex_fix, "$1");
	} else {
		value = url;
	}
	
	return value;
}

/**
 * Changes a TVListings.properties.params object's value in the current URL and returns the new URL
 * @param {Object} name		Name of the TVListings.properties.params key
 * @param {Object} val		Value to change the parameter to
 * @param {String} url		Optional. Specify a URL to change instead of the current URL
 */
TVListings.properties.change_url_parameter = function (name, val, url, del) {
	var param = this.params[name];

	var val = val;
	
	//if no URL was specified use the current URL
	if (!url) url = document.location.href;
	
	//if custom regex has been supplied then use that, otherwise use default regex
	if (param.regex) {
		var url_regex = RegExp(param.regex);
		var pos = param.pos;
	} else {
		var url_regex = RegExp('(.*)(\\?|&)' + param.name + '=([^&]*)($|&)(.*)');
		var pos = 3;
	}
	
	var count = null, value = null;
	//get the amount of matches the regex returns
	try { count = url.match(url_regex).length-1;}
	catch(e) { count = null;}
	
	//if there were matches found in the URL replace the match at position param.pos
	if ((count !== null) && (count >= 0)) {
		var tmp_str = "";
		for (var i = 1; i <= count; i++) {
			if (i == pos) { tmp_str += param.name + "=" + val;}
			else { tmp_str += "$" + i;}
		}
		value = url.replace(url_regex, tmp_str);
	} else { //else add the parameter to the URL
		value = this.add_url_parameter(name, val, url);
	}
	
	return value;
}

/**
 * Creates a URL from an Object Array of paramater key-value pairs like those produced by process_parameters()
 * @param {Array} arr		An array of key-value pairs to use as the query strings
 * @param {Object} url		Optional. A URL to use as a base, instead of the current one
 */
TVListings.properties.get_url = function (arr, url) {
	//if no URL was specified use the current URL
	if (!url) var url = document.location.href;
	
	//remove any query strings
	if ((url.match(RegExp('\\?')))) {
		url = url.match(RegExp('(.*)\\?(.*)'))[1];
	}
	
	//create query string
	var tmp_str = "?";
	for (var i in arr) { tmp_str += i + "=" + arr[i] + "&";}
	tmp_str = tmp_str.replace(RegExp('&$'), "");
	
	//arrange the new url
	var value = url + tmp_str;
		
	return value;
}

/* Listings Controls */

TVListings.controls = {} //most things here require JQuery because I'm lazy

	
	/* Reminders Functionality */
	TVListings.controls.reminders = {}
	TVListings.controls.reminders.messages = {}
	TVListings.controls.reminders.when = 1;
	TVListings.controls.reminders.animate = true;
	
	TVListings.controls.reminders.init = function () {
		var form = $(document.getElementById("listings-reminder"));
		var submit = $(document.getElementById("listings-reminder-submit"));
		var days = document.getElementById("listings-reminder-days");
		
		if ($(".listings-reminder-days select") > 0)
		{
			days = $(".listings-reminder-days select").val();
		}
		
		
		var msgs = TVListings.properties.get_info_from_string(submit.attr("alt"), "%%");
		this.messages.success = msgs.success;
		this.messages.failed = msgs.failed;
		this.messages.invalid_email = msgs.invalid_email;
		this.messages.help = submit.parent().children(".hint").html();
		
		form.submit(function(){
			TVListings.controls.reminders.submit();
			return false;
		});
		
		if (days) {
			this.dropdown(days);
		}
		//remove remniders for programmes which start within 24 hours
		this.remove_old_reminders();
		
		//CLICK TO ACTIVATE REMINDER LINK
		this.addClickToActivate();
		//GENERATE UA LOGIN AND REGISTER LINKS
        this.addUALinks();
	}
	
	/**
	 * Function that activates the email input for reminders when the user is logged in
	 */
	TVListings.controls.reminders.addClickToActivate = function() {
        $("#click-to-activate").click(function(){
			$("#listings-reminder-email").removeClass().removeAttr("readonly");
			return false;
		});
    }
	
	/**
     * Function that generate the links to UA
     */
    TVListings.controls.reminders.addUALinks = function() {
		var loginURL = "/ua-fe/login.shtml?returnUrl=" + escape(location);
		var registerURL = "/ua-fe/register-step-1.shtml?returnUrl=" + escape(location);
        $("#ua-login").attr("href",loginURL);
		$("#ua-register").attr("href",registerURL);
    }
	
	/**
	 * Validates email addresses (from ap3 send-to-friend, except accepts 4 character TLDs)
	 * @param {Object} address		Email address to validate
	 */
	TVListings.controls.reminders.validate_email = function(address) {
		var invalid = /(@.*@)|(\.\.)|(@\.)|(\.@)|(^\.)/;
		var valid = /^.+\@(\[?)[a-zA-Z0-9\-\.]+\.([a-zA-Z]{2,4}|[0-9]{1,3})(\]?)$/;
		if (!invalid.test(address) && valid.test(address)) {
			return true;
		} else {
			return false;
		}
	}
	
	/**
	 * Takes an array of elements and returns an array of their values.
	 * @param {Object} arr		Array of checkbox DOM Elements
	 */
	TVListings.controls.reminders.get_checkbox_values = function (arr) {
		var value = [];
		for (var i=arr.length-1;i>=0;i--) {
			if (arr[i].checked) {
				value.push(arr[i].value);
			}
		}
		return value;
	}
	
	/**
	 * Requires JQuery. Hides status messages.
	 */
	TVListings.controls.reminders.hide_messages = function () {
		$("#listings-reminder-msg").fadeOut("slow").html("");
	}
	
	/**
	 * Requires JQuery. Displays success message.
	 */
	TVListings.controls.reminders.success = function (msg) {
		this.hide_messages();
		if (!msg) var msg = this.messages.success;
		$("#listings-reminder-msg").css("opacity", "0").html(msg).fadeIn("slow");
		$("#reminders").addClass("submitted");

	}
	
	/**
	 * Requires JQuery. Displays error message.
	 */
	TVListings.controls.reminders.failed = function (msg) {
		this.hide_messages();
		if (!msg) var msg = this.messages.failed;
		$("#listings-reminder-msg").css("opacity", "0").html(msg).fadeIn("slow");
	}
	
	/**
	 * Gather reminder information from the page and return a properties object
	 * @param {Object} name		Name attribute of the reminder checkboxes which is also used as 'Name' -email and -when
	 * 							for when-to-send and email address inputs
	 */
	TVListings.controls.reminders.get_reminder_properties = function (name) {
		try {
			var sids;
			if ($("#listings-reminder-id").length > 0)
			{
				var rid = $("#listings-reminder-id").attr("value");
				if(rid != ""){
					sids = [rid];
				} else {
					return false;
				}
			} else {
				//Get checkboxes and check if they are checked before extracting schedule ids
				var checkboxes = document.getElementsByName(name);
				sids = this.get_checkbox_values(checkboxes);
				
				//If no checkboxes are checked do nothing
				if (sids.length < 1) {
					this.failed(this.messages.help);
					return false;
				}
			}
			
			//Get channel code from the listings properties
			var chnl_code = TVListings.properties.get_info_from_string(
				$("#listings-channel-controls li.current").attr("class")
			).channel_code;

			if (!chnl_code)
			{
				chnl_code =  TVListings.properties.get_info_from_string($("#listings-header").attr("class")).channel_code;
			}
	
			
			//Get the when to send and email address values
			var when = TVListings.controls.reminders.when;
			var email = document.getElementsByName(name + "-email")[0].value;
			
			//validate email and return properties object if successfull
			if ((!this.validate_email(email))) {
				this.failed(this.messages.invalid_email);
				return false;
			} else {
				var value = {}
				value.sids = sids;
				value.chnl_code = chnl_code
				value.when = when;
				value.email = email;
				return value;
			}
		} catch (e) {
			this.failed();
			return false;
		}
	}
	
	/**
	 * Generates and returns xml as a string for programme reminder information arrays
	 * @param {Object} arr		Array of program reminder information generated by the get_reminder_properties method
	 */
	TVListings.controls.reminders.get_reminders_xml = function (arr) {
		try {
			if ((!arr.email)||(arr.sids.length<1)||(!arr.when)) {
				this.failed(this.messages.help);
				return false;
			}
			//Create top-level reminders element
			var xml = document.createElement("reminders");
			
			//Create channel-code element
			var chnl_code = document.createElement("channel-code");
			chnl_code.innerHTML = arr.chnl_code;
			xml.appendChild(chnl_code);
			
			if(TVListings.domain_code) {
				var domainElement = document.createElement("domain-code");
				domainElement.innerHTML = TVListings.domain_code;
				xml.appendChild(domainElement);
			}
			
			//Create email element and add to reminders element
			var email = document.createElement("email");
			email.innerHTML = arr.email;
			xml.appendChild(email);
	
			//Create schedule-item-id elements for checked programmes and add to reminders element
			for (var i=arr.sids.length-1,k;k=arr.sids[i];i--) {
				var tmp_elem = document.createElement("scheduled-item");
				tmp_elem.innerHTML = k;
				xml.appendChild(tmp_elem);
			}
			
			//Create when-to-send element
			var when = document.createElement("reminder-offset");
			when.innerHTML = arr.when;
			xml.appendChild(when);
			
			//Create a dummy parent element so that innerHTML contains the reminders root element as well
			var dummy = xml;
			var xml = document.createElement("dummy");
			xml.appendChild(dummy);
			
			//Add xml element and return the complete reminders xml as a string
			return '<?xml version="1.0" encoding="UTF-8"?>' + xml.innerHTML;
		} catch(e) {
			this.failed();
			return false;
		}
	}
	
	TVListings.controls.reminders.remove_old_reminders = function () {
		var date = new Date();
		var threshold = date.getTime() + 86400000;
		var hidden = 0;
		
		var checkboxes = document.getElementsByName("listings-reminder");
		var checkboxes_len = checkboxes.length;

		for (var i=checkboxes.length-1;((i !== -1) && (checkboxes[i].alt) && (i>=0));i--) {
			var checkbox = checkboxes[i];
			var this_datetime = TVListings.properties.get_info_from_string(checkbox.alt).datetime;
			var this_date_obj = TVListings.misc.date.get_date_object(
				this_datetime.substring(0,8),
				this_datetime.substring(8)
			);
			if ((this_date_obj.getTime() < threshold)) {
				var jq_Checkbox = $(checkbox);
				jq_Checkbox.parent().parent().addClass("no-reminders");
				jq_Checkbox.parent().children().remove();
				hidden++;
			}
		}
		if(navigator.appName == "Microsoft Internet Explorer"){
			checkboxes_len--;
		}
		if (hidden == checkboxes_len) {
			//$(".listings-page-controls.reminders-form").remove();
			//$(document.getElementById("listings-reminder-heading")).remove();
		}
	}
	
	/**
	 * Requires JQuery. Sets reminders.
	 */
	TVListings.controls.reminders.submit = function () {
		var properties = this.get_reminder_properties("listings-reminder");
		if ((properties)&&(typeof(properties) == "object")) {
			var xml = this.get_reminders_xml(properties);
			if (xml) {
				var url = TVListings.servlet_address + "reminder";
				$.ajax({
					'url': url, 'data': {'message':xml},
					'type': "POST",
					'success': function () { TVListings.controls.reminders.success();},
					'error': function () { TVListings.controls.reminders.failed();}
				})
			}
		}
		return false;
	}
	
	TVListings.controls.reminders.dropdown = function (elem) {
		if (typeof(elem.height) !== "function") elem = $(elem);
		
		var toggle = document.createElement("span");
		toggle.className = "controls_toggle_button";
		$(toggle).insertAfter(elem);
		
		$(document).click(function (event) {
				if ((($(event.target).parents("#listings-reminder-days").length == 0)
				  && (elem.parent().parent().hasClass("expanded")))
				    || (event.target.className == "controls_toggle_button")) {
						elem.parent().parent().toggleClass("expanded");
						elem.parent().toggleClass("expanded");
				}
		})
		
		elem.children("li:not(.default-menu-item)").click(
			function () {
				TVListings.controls.reminders.when = this.getElementsByTagName("strong")[0].innerHTML;
				$(this).parent().children().filter(".current").removeClass("current");
				$(this).addClass("current");
				$(this).parent().parent().removeClass("expanded");
				$(this).parent().parent().parent().removeClass("expanded");
				$(this).siblings(".default-menu-item").children("strong").html($(this).text());
			}
		);
		
		elem.children("li").hover(
			function () { $(this).addClass("visible");},
			function () { $(this).removeClass("visible");}
		);
	}
	
	/* Other Controls */

	/**
	 * Saves the value of a checkbox in a cookie as the default country code. Removes on second click.
	 * @param {Object} elem		Control's main ul element
	 */
	TVListings.controls.remember_country = function (elem) {
		if (elem) {
			if (typeof(elem.height) !== "function") elem = $(elem);
			elem.change(
				function () {
					if (this.checked !== false) {
						TVListings.properties.set_cookie_parameters({
							'country_code': this.value
						});
					} else {
						TVListings.properties.remove_cookie_parameters({
							'country_code': ""
						});
					}
				}
			)
		}
	}
	
	/**
	 * Turns a listings single-level list into functional controls
	 * @param {Object} elem		Control's main ul element
	 */
	TVListings.controls.apply_single_level = function (elem, del_) {
		if (del_) var del = del_;
		if (elem)
		{
		
			var list_items = $(elem.getElementsByTagName("li"));
			
			//loop through li elements
			list_items.each (function () {
						
				//extract parameter information from class names
				this.info = TVListings.properties.get_info_from_string(this.className);
				
				//on click update the current page with the new parameter information
				$(this).click( function () {
					if (del) {
						for (var i in del) {
							TVListings.main.stage.url = TVListings.properties.remove_url_parameter(i,TVListings.main.stage.url);
						}
					}
					
					//update the stage URL with new info
					for (var i in this.info) {
						if (i !== "ss") {
							TVListings.main.stage.url = TVListings.properties.change_url_parameter(
								i, this.info[i], TVListings.main.stage.url, del
							);
						}
					}
					//go to new URL
					document.location.href = TVListings.main.stage.url;
					return false;
				});
			});
		}
	}
	
	/**
	 * Turns a listings multi-level list into functional controls
	 * @param {Object} elem		Control's main ul element
	 */
	TVListings.controls.apply_multi_level = function (elem, del, max_height) {
		if(elem){
			//get all descendant li elements
			var list_items = $(elem.getElementsByTagName("li")).filter(":not(.menu-item-spacer)").filter(":not(.first-item-spacer)");
			
			//On hover show sub menus
			if ($.fn.hoverIntent){
				$(elem).hoverIntent({
					'sensitivity': 8,
					'interval': 100,
					'timeout': 250,
					'over': function () {
						TVListings.controls.datepicker.close();
						$(this).parent().css("z-index", "3");
						TVListings.controls.show_submenu(this, max_height);
					},
					'out': function () {
						TVListings.controls.hide_submenu(this, max_height);
						$(this).parent().css("z-index", "1");
					}
				});
			}
			function menuHandler(e) {
				if ($("#listings-series-controls>li.default-menu-item").has(e.target).size() == 0){
					$('#series_wrapper').removeClass('menu-open');
				}									
			}
			if (elem.id == "listings-series-controls") {
				
				
				$("#listings-series-controls>li.default-menu-item").click(function(){
					var seriesWrapper = $('#series_wrapper');
					if (!seriesWrapper.hasClass('menu-open')){
						seriesWrapper.addClass('menu-open');
						$('body').bind('click' , menuHandler );	
					} else {
						seriesWrapper.removeClass('menu-open');
						$('body').unbind('click' , menuHandler);
					}
				});
			}
					
			if ($.fn.hoverIntent){
				$(elem.parentNode).hoverIntent({
					'sensitivity': 5,
					'interval': 50,
					'over': function () { TVListings.controls.show_submenu(this);},
					'out': function () { TVListings.controls.hide_submenu(this);}
				});
			}
			
			list_items.hover(
				function () { TVListings.controls.show_submenu(this);},
				function () { TVListings.controls.hide_submenu(this);}
			);
						
			list_items.filter(".current")
			list_items = list_items.filter(":not(.current)").filter(":not(.default-menu-item)");
			

			//loop through li elements
			list_items.each (function () {
						
				//extract parameter information from class names
				this.info = TVListings.properties.get_info_from_string(this.className);
				
				//Check if the object has properties
				var has_properties = false;
				
				for (var f in this.info) {
					has_properties = true;
				}
				
				if (has_properties == true) {
					
					//on click update the current page with the new parameter information
					$(this).click( function () {
						if (del) {
							for (var i in del) {
								TVListings.main.stage.url = TVListings.properties.remove_url_parameter(i,TVListings.main.stage.url);
							}
						}
						
						//if we have only a country being changed and not a channel then make sure channel_code is not
						//in the URL so that it does not override it
						if ((this.info.country_code)&&(!this.info.channel_code)) {
							TVListings.main.stage.url = TVListings.properties.remove_url_parameter(
								'channel_code',TVListings.main.stage.url
							);
						}
						
						//update the stage URL with new info
						for (var i in this.info) {
							if (i !== "ss") {
								TVListings.main.stage.url = TVListings.properties.change_url_parameter(
									i, this.info[i], TVListings.main.stage.url, del
								);
							}
						}
						//go to new URL
						document.location.href = TVListings.main.stage.url;
					})
				}
			});
		}
	}
		
	
	TVListings.controls.show_submenu = function (elem, max_height) {
		//if elem is not a JQuery object create one
		if (typeof(elem.height) !== "function") elem = $(elem);
		
		if (max_height) {
			elem.css('height', max_height + "px").css('overflow', "auto");			
		}

		elem.addClass("visible");
	}
	
	TVListings.controls.hide_submenu = function (elem, max_height) {
		//if elem is not a JQuery object create one
		if (typeof(elem.height) !== "function") elem = $(elem);
		
		elem.removeClass("visible");
		
		if (max_height) {
			elem.css('overflow', "visible").css('height', "auto");
		}
	}
	
	TVListings.controls.widget = function () {
		$("#listings-widget-region-control").change(function () {

			//Get selected option
			var opt = $("#listings-widget-region-control option:selected")[0];

			//Get the current page settings
			var settings = TVListings.properties.process_parameters();
			
			//Change the country code
			settings.country_code = opt.value;
			delete settings.channel_code;
			
			TVListings.main.get_listings(settings);
		})
		
		$("#listings-widget-link").click(function () {
	
			if (TVListings.listings_address !== "") {
				//Get the current page settings
				var settings = TVListings.properties.get_info_from_string(this.className);
				
				//Create URL using these settings
				var url = TVListings.properties.get_url(settings, TVListings.listings_address);
				
				document.location.href = url;
			}
		})
	}
	
	TVListings.controls.datepicker = {}
	TVListings.controls.datepicker.event_match = ".dpTable";
	TVListings.controls.datepicker.picker = null;
	TVListings.controls.datepicker.input = null;
	
	TVListings.controls.datepicker.init = function () {
		this.input = document.getElementById('dnitvl_datepicker_input');
		
		var trans = {}
		$("#datepicker-translations *").each(function () {
			var arr = TVListings.properties.get_info_from_string(this.className);
			if (arr.name) {
				trans[arr.name] = [];		
				for (var i=1,k=0;k=arr[i];i++) {
					trans[arr.name].push(k);
				}
			}
		});
		
		for (var i=trans.months.length-1,k;k=trans.months[i];i--) {
			DatePicker.monthArrayLong[i] = k;
			DatePicker.monthArrayMed[i] = k.slice(3);
			DatePicker.monthArrayShort[i] = k.slice(3);
		}
		
		if ((trans.days.length >= 6)&&(trans.days_short.length >= 6)) {
			DatePicker.dayArrayLong[0] = trans.days[6];
			DatePicker.dayArrayMed[0] = trans.days.pop(6);
			DatePicker.dayArrayShort[0] = trans.days_short.pop(6);
		}
		
		for (var i=0,k;k=trans.days[i];i++) {
			DatePicker.dayArrayLong[i+1] = k;
			DatePicker.dayArrayMed[i+1] = k;
			DatePicker.dayArrayShort[i+1] = trans.days_short[i];
		}
		
		
		var date_arr = TVListings.properties.get_info_from_string(this.input.className);
		var sarr = DatePicker.splitDateString(date_arr.date_range_start);
		var earr = DatePicker.splitDateString(date_arr.date_range_end);
		DatePicker.startDate = {
			'd': sarr[0],
			'm': sarr[1],
			'y': sarr[2]
		}
		
		DatePicker.endDate = {
			'd': earr[0],
			'm': earr[1],
			'y': earr[2]
		}
		
		DatePicker.displayDatePicker(TVListings.controls.datepicker.input.id,this);
		DatePicker.displayDatePicker(TVListings.controls.datepicker.input.id,this);
		
		this.picker = document.getElementById('dnitvl_datepicker');
		
		var dp_unfocus;
		
		var toggle_dp = function () {
			DatePicker.displayDatePicker(TVListings.controls.datepicker.input.id,this);
		}
		
		$(this.input).click(toggle_dp);
		
		$(document).click(function (event) {
			if (($(event.target).parents(TVListings.controls.datepicker.event_match).length == 0)
					&& (event.target.id !== TVListings.controls.datepicker.input.id)) {
				if ((TVListings.controls.datepicker.picker)
						  &&(TVListings.controls.datepicker.picker.style.display !== "none")) {
					toggle_dp();
				}
			}
		})
		

	}
	
	TVListings.controls.datepicker.open = function () {
		if ((this.picker)&&(this.picker.style.display == "none")) {
			DatePicker.displayDatePicker(TVListings.controls.datepicker.input.id,this.input)
		}
	}
	TVListings.controls.datepicker.close = function () {
		if ((this.picker)&&(this.picker.style.display !== "none")) {
			DatePicker.displayDatePicker(TVListings.controls.datepicker.input.id,this.input)
		}
	}
	
	/*TV Listings Search */
	TVListings.controls.search = {};
	
	TVListings.controls.search.autoCompleteOptions = null;
	TVListings.controls.search.autoCompleteCurrent = null;
	TVListings.controls.search.autoCompletePartial = null;
	TVListings.controls.search.asyncLoaded = false;
	TVListings.controls.search.maxAutoCompleteItems = 8;
	TVListings.controls.search.enteredText = null;
	
	TVListings.controls.search.init = function() {
		//Fix tab sizing issues if one i 1-line, other is 2-line
		if ($("li.type__day > a").css('height') > $("li.type__week > a").css('height')) {
			$("li.type__week > a").height($("li.type__day > a").css('height'));
		}
		else {
			$("li.type__day > a").height($("li.type__week > a").css('height'));
		}
		
		TVListings.controls.search.autoCompleteOptions = new Array();
		TVListings.controls.search.initButton();
		TVListings.controls.search.initTextInput();
	}
	
	TVListings.controls.search.initButton = function() {		
		var searchbox_wrapper = $('#listings-search-input');
		var searchbox = $('#dnitvl_search_input');
		var searchboxDefaultText = "Find a TV show";
		searchbox.val(searchboxDefaultText)
		searchbox.focus( function(){ 
			searchbox_wrapper.addClass('focus');
			if (this.value == searchboxDefaultText) { this.value = ''; }
		});
		searchbox.blur( function(){ 
			if (!$('#dnitvl_search_autocomplete').hasClass('show')) { searchbox_wrapper.removeClass('focus'); }
			if (this.value == '') { this.value = searchboxDefaultText; }
		});
		$('#dnitvl_search_button').click(function() {
			if (searchbox.val() != searchboxDefaultText) { 
				TVListings.controls.search.makeRequest($('#dnitvl_search_input').val());
			}			
		});
	}
	
	TVListings.controls.search.initTextInput = function() {
		var list = document.createElement('ul');
		list.id = 'dnitvl_search_autocomplete';
		
		/*$('#dnitvl_search_input').blur(function() {
			$('#dnitvl_search_autocomplete').removeClass('show');
		});
		
		$('#dnitvl_search_input').focus(function() {
			if ($('#dnitvl_search_autocomplete').text().length > 0){
				TVListings.controls.search.enteredText = $('#dnitvl_search_autocomplete').text();
				$('#dnitvl_search_autocomplete').addClass('show');
			}
		});*/
		var listingsSearchInputText = document.getElementById('listings-search-input-text');
		if (listingsSearchInputText)
		{
			document.getElementById('listings-search-input-text').appendChild(list);
		}
		
		$('#dnitvl_search_input').keyup(function(e) {
			if (e.keyCode == 13) { //Enter
				var searchstring;
				var selected = $('#dnitvl_search_autocomplete').children('.selected')[0];
				if (selected == null)
					searchstring = $('#dnitvl_search_input').val();
				else
					searchstring = $(selected).text();
				TVListings.controls.search.makeRequest(searchstring);
				return;
			}
			
			if (e.keyCode != 38 && e.keyCode != 40){ //Up/down
				var searchstring = $('#dnitvl_search_input').val();
				if (searchstring.length == 1  && e.keyCode != 8) {
					TVListings.controls.search.fetchAutoComplete(searchstring);
				}
				TVListings.controls.search.showAutoComplete();
				return;
			}
			
			if (e.keyCode == 40) {//down arrow
				var selected = $('#dnitvl_search_autocomplete').children('.selected')[0];
				if (selected == null) {
					var curr = $('#dnitvl_search_autocomplete li:nth-child(1)');
					$(curr).addClass('selected');
					TVListings.controls.search.enteredText = $('#dnitvl_search_input').val();
					$('#dnitvl_search_input').val($(curr).text());
				}
				else {
					if ($(selected).nextAll('li').eq(0).length == 0){
						$('#dnitvl_search_input').val(TVListings.controls.search.enteredText);
					}
					else {
						$(selected).nextAll('li').eq(0).addClass('selected');
						$('#dnitvl_search_input').val($(selected).next().text());
					}
					$(selected).removeClass('selected');
				}
				return;
			}
			
			if (e.keyCode == 38) { //up arrow
				var selected = $('#dnitvl_search_autocomplete').children('.selected')[0];
				if (selected == null) {
					$('#dnitvl_search_autocomplete li:last-child').addClass('selected');
					TVListings.controls.search.enteredText = $('#dnitvl_search_input').val();
					$('#dnitvl_search_input').val($('#dnitvl_search_autocomplete li:last-child').text());
				}
				else {
					if ($(selected).prev().length == 0){
						$('#dnitvl_search_input').val(TVListings.controls.search.enteredText);
					}
					else {
						$(selected).prev().addClass('selected');
						$('#dnitvl_search_input').val($(selected).prev().text());
					}
					$(selected).removeClass('selected');
				}
				return;
			}
			
		});
	}
	
	TVListings.controls.search.makeRequest = function(searchstring) {	
		if (searchstring.length > 0) {
			var channelcode = TVListings.properties.get_info_from_string(
					$("#listings-channel-controls li.current").attr("class")
				).channel_code;

			if (!channelcode)
			{
				channelcode =  TVListings.properties.get_info_from_string($("#listings-header").attr("class")).channel_code;
			}
			TVListings.main.stage.url = TVListings.properties.change_url_parameter('type', 'search');
			TVListings.main.stage.url = TVListings.properties.change_url_parameter('channel_code', channelcode, TVListings.main.stage.url);
			TVListings.main.stage.url = TVListings.properties.change_url_parameter('search_string', searchstring, TVListings.main.stage.url);
			TVListings.main.stage.url = TVListings.properties.remove_url_parameter('page', TVListings.main.stage.url);
			document.location.href = TVListings.main.stage.url;
		}
	} 
	
	TVListings.controls.search.fetchAutoComplete = function(searchstring) {	
		if (searchstring.length > 0) {
			
			var channelcode = TVListings.properties.get_info_from_string(
					$("#listings-channel-controls li.current").attr("class")
				).channel_code;

			if (!channelcode)
			{
				channelcode =  TVListings.properties.get_info_from_string($("#listings-header").attr("class")).channel_code;
			}
			
			$.ajax({
					type: 'GET',
					dataType: 'xml',
					url: TVListings.servlet_address + 'GetSeriesTitleBySearchString',
					data: 'search_string=' + searchstring + '&channel_code=' + channelcode + '&match_type=matchAll',
					success: function(xml){
						TVListings.controls.search.processAutoComplete(xml);
					}
			});
			return TVListings.controls.search.autoCompleteOptions;
		}
		return null;
	}
	
	
	TVListings.controls.search.processAutoComplete = function(xml) {
		TVListings.controls.search.autoCompleteOptions = new Array();
		$(xml).find('series-title').each(function(index, element)
		{
			var entry = $.trim($(element).text().toLowerCase());
			if ($.inArray(entry, TVListings.controls.search.autoCompleteOptions) == -1)
				TVListings.controls.search.autoCompleteOptions[TVListings.controls.search.autoCompleteOptions.length] = entry;
		});
		TVListings.controls.search.autoCompleteOptions.sort();
		TVListings.controls.search.showAutoComplete();
	}
	
	TVListings.controls.search.showAutoComplete = function() {
		
		var searchstring = $('#dnitvl_search_input').val();
		$('#dnitvl_search_autocomplete').empty();
		if (searchstring.length != 0) {
			TVListings.controls.search.autoCompleteCurrent = jQuery.grep(TVListings.controls.search.autoCompleteOptions, function(n, i) {
				return (n.substring(0, searchstring.length).toLowerCase() == searchstring.toLowerCase());
				});
			TVListings.controls.search.autoCompletePartial = jQuery.grep(TVListings.controls.search.autoCompleteOptions, function(n, i) {
				return (n.indexOf(searchstring) != -1 && n.substring(0, searchstring.length).toLowerCase() != searchstring.toLowerCase());
				});
		}
		else {
			TVListings.controls.search.autoCompleteCurrent = new Array();
			TVListings.controls.search.autoCompleteCPartial = new Array();
		}
		if (searchstring.length ==0 || (TVListings.controls.search.autoCompleteCurrent.length == 0 && TVListings.controls.search.autoCompletePartial.length == 0)){
			$('#dnitvl_search_autocomplete').removeClass('show');
		}
		else {
			$('#dnitvl_search_autocomplete').addClass('show');
			$.each(TVListings.controls.search.autoCompleteCurrent, function(index, value) {
				var item = document.createElement('li');
				item.innerHTML = value.replace(searchstring, "<span>" + searchstring + "</span>");
				document.getElementById('dnitvl_search_autocomplete').appendChild(item);
				$(item).click(function() {
					$('#dnitvl_search_autocomplete').removeClass('show');					
					TVListings.controls.search.makeRequest(value);
				});
				$(item).mouseover(function() {
					$(this).siblings().removeClass('selected');
					$(this).addClass('selected');
					item.innerHTML = value;
				});
				$(item).mouseout(function() {
					$(this).removeClass('selected');
					item.innerHTML = value.replace(searchstring, "<span>" + searchstring + "</span>");
				});
				if (index >= TVListings.controls.search.maxAutoCompleteItems - 1)
					return false;
			});
			if (searchstring.length !=0 && (searchstring.length >=3 || TVListings.controls.search.autoCompleteCurrent.length == 0) && TVListings.controls.search.autoCompletePartial.length > 0) {
				$.each(TVListings.controls.search.autoCompletePartial, function(index, value) {
					var item = document.createElement('li');
					if (TVListings.controls.search.autoCompleteCurrent.length > 0 && index == 0)
						item.className = "partial-top";
					item.innerHTML = value.replace(searchstring, "<span>" + searchstring + "</span>");
					document.getElementById('dnitvl_search_autocomplete').appendChild(item);
					$(item).click(function() {
						$('#dnitvl_search_autocomplete').removeClass('show');
						
						TVListings.controls.search.makeRequest(value);
					});
					$(item).mouseover(function() {
						$(this).siblings().removeClass('selected');
						$(this).addClass('selected');
						item.innerHTML = value;
					});
					$(item).mouseout(function() {
						$(this).removeClass('selected');
						item.innerHTML = value.replace(searchstring, "<span>" + searchstring + "</span>");
					});
					if (index >= TVListings.controls.search.maxAutoCompleteItems - 1)
						return false;
				});
			}
		}
	}

	
/* Retrieve Listings */

//Check if TVListings object already exists in the page
//if (!TVListings) var TVListings = {};
TVListings.request = {};

/**
 * Executes actions just before a request is made. To be used by the beforeSend option of $.ajax (JQuery)
 * @param {Object} data		Array of key-value pairs like those produced by TVListings.parameters.process_parameters()
 * @param {Object} request	XMLHttpRequest Object which is about to be sent	
 */
TVListings.request.sent = function(url, arr, request) {  //can be used for displaying a loading message etc.
	if (arr.type !== "widget") {
		var message = document.createElement("div");
		message.innerHTML = ""
		
		var image = new Image();
		image.src = TVListings.image_path + "/loading.gif";
		image.alt = "loading";
		image.width = "24";
		image.height = "24";
		if (TVListings.loader_style == "bar"){
			image.src = "/tv-guide/images/global/ajax-loader-bar.gif";
			image.width = "220";
			image.height = "19";
		}

		var overlay = document.createElement("div");
		overlay.className = "overlay";
		
		var image_container = document.createElement("p");
		image_container.className = "listings-loading-image";
		if (TVListings.loader_style == "bar"){
			image_container.innerHTML = "Loading...";
		}
		image_container.appendChild(image);
		
		var title = document.createElement("h2");
		title.id = "listings-title";
		title.innerHTML = TVListings.appname;
		message.appendChild(title);
		
		message.appendChild(image_container);
		if (TVListings.loader_style == "bar"){
			message.appendChild(overlay);
			message.className = "listings-loading double " + arr.type + "-view";
		}
		
		if (TVListings.results_element)
		{
			TVListings.header_element.html("");
			TVListings.results_element.html(message.innerHTML);
			TVListings.results_element.attr("class","listings-loading double column");
		} else {
			message.id = "listings";
			message.className = "listings-loading " + arr.type + "-view";
			var body = document.getElementById("dni-listings");
			body.innerHTML = "";
			body.appendChild(message);
		}
	}
}


/**
 * If a request fails, this function can be called to handle it.
 * @param {Object} request		XMLHTTPRequest Object of the request which failed
 * @param {Object} status		Status Object
 * @param {Object} exception	Exception object if one was thrown
 * @param {Object} arr			Array of key-value pairs from the request like those produced by TVListings.parameters.process_parameters()
 */
TVListings.request.failed = function (response, status, exception, arr) {
	if (arr.type !== "widget") {
		var resp;
		if (response.responseXML)
		{
			resp = response.responseXML.documentElement;
		}
		
		var error_msg = document.createElement("p");
		if (resp && (resp.textContent||resp.innerText))
		{
			error_msg.innerHTML = resp.textContent||resp.innerText;
		} else {
			error_msg.innerHTML = "Sorry we are unable to show the TV schedule at this time, please try again later";
		}
		
		var error = document.createElement("div");
		
		var title = document.createElement("h2");
		title.id = "listings-title";
		title.innerHTML = TVListings.appname;
		
		error.appendChild(title);
		error.appendChild(error_msg);
		
		error.className = "listings-error-fatal " + arr.type + "-view";
		error.id = "listings";
		if (TVListings.results_element)
		{
			var response_header_elem = $(response).find("#listings-header");
			var response_results_elem = $(response).find("#listings-results");
			TVListings.header_element.html("");
			TVListings.results_element.html(error);
			TVListings.results_element.attr("id", "listings-error");
			TVListings.results_element.removeClass("listings-loading");
		} else {
			var body = document.getElementById("dni-listings");
			body.innerHTML = "";
			body.appendChild(error);
		}
		
	}
}

/**
 * Requires JQuery. Makes a GET request to the specified url
 * containing data based on the TVListings.parameters.params objects
 * @param {String} url		URL to make GET request to
 * @param {Object} arr		Array of key-value pairs like those produced by TVListings.parameters.process_parameters()
 */
TVListings.request.get_listings = function (url, arr) {
	if (TVListings.style_suffix) arr.style += TVListings.style_suffix;
	if (TVListings.style_prefix) arr.style = TVListings.style_prefix+arr.style;
	var CASTGC;
	if (CASTGC = TVListings.properties
				.get_cookie_parameter("CASTGC", true)) {
		arr.CASTGC = CASTGC;
	}
	$.ajax({
		url: url, data: arr,
		dataType: "html",
		type: "GET",
		contentType : 'true',
		beforeSend: function(request){
			TVListings.request.sent(url, arr, request);
		},
		success: function(data, status){
			TVListings.request.process_listings(data, status, arr);
		},
		error: function(request, status, exception){
			TVListings.request.failed(request, status, exception, arr);
		}
	})
		

}

/**
 * After a successful request, this function can be called to handle the returned listings data.
 * @param {Object} response		Reponse object from requst
 * @param {Object} status		Status Object
 * @param {Object} arr			Array of key-value pairs used in the request
 */
TVListings.request.process_listings = function (response, status, arr) {
	if (arr.type !== "widget") { //Main tv listings application things
		var old = document.getElementById("listings-reminder");
		
		if (old) delete old; //remove old listings content
		var body = document.getElementById("dni-listings");
		
		if (TVListings.results_element)
		{
			var response_header_elem = $(response).find("#listings-header");
			var response_results_elem = $(response).find("#listings-results");

			TVListings.header_element.html(response_header_elem.html());
			TVListings.results_element.html(response_results_elem.html());

			TVListings.header_element.attr("class",response_header_elem.attr("class"));
			TVListings.results_element.attr("class", response_results_elem.attr("class"));

			if ($(response).find("#listings.listings-error").length > 0)
			{
				TVListings.results_element.addClass("listings-error");
			} else {
				TVListings.results_element.removeClass("listings-error");
			}

		} else {
			body.innerHTML = response; //insert new listings content
		}
		$("#listings").attr("class", arr.type + "-view");

		//enable reminder functionality
		TVListings.controls.reminders.init();
		
		TVListings.reporting.initReportingParams(arr);
		
		//enable IE fixes
		TVListings.misc.ui.ie_fixes();
		
		//enable "rememebr my country"
		var remember = document.getElementById("listings-controls-remember");
		TVListings.controls.remember_country(remember);
		
		TVListings.reporting.exec_queue();
		//FACEBOOK INTEGRATION
		TVListings.facebook.init();
		TVListings.ui.flip.scrollTo = true;
		TVListings.ui.flip.activate();
		view_specific_calls();
				
	} else {
		document.getElementById("dni-listings").innerHTML = response;
		$("#listings")[0].className = arr.type + "-view";
		view_specific_calls();
	}

	function view_specific_calls ()	 {
		switch(arr.type) { //things which need to be called specifically for each view
			case "search":
			case "day":	
				TVListings.controls.search.init();
				
				//enable listings tabs
				TVListings.controls.apply_single_level(
					document.getElementById("listings-controls-tabs")
				);
			
				//enable shorter descriptions
				TVListings.misc.ui.process_descriptions({
					max_height: "0px", no_elipsis: true,
					toggle_text: "View More", duration: 100
				});
				
				//enable controls
				var region_control = document.getElementById("listings-region-controls");
				var channel_control = document.getElementById("listings-channel-controls");
				
				if (region_control) {
					TVListings.controls.apply_multi_level(
						region_control
					);
				}

				TVListings.controls.apply_multi_level(
					document.getElementById("listings-series-controls"),
					{'page': true}
				);

				if(TVListings.carousel)
					$('#listings-channel-list').carousel({xsize: 8, autosize:true});
				if (channel_control) {
					TVListings.controls.apply_multi_level(
							channel_control
					);
				}
				if (TVListings.show_datepicker)
					TVListings.controls.datepicker.init();
				
				break;
			case "series":
				TVListings.controls.search.init();

				//enable listings tabs
				TVListings.controls.apply_single_level(
					document.getElementById("listings-controls-tabs"),
					{'page': true, 'series_id': true}
				);
				
				//enable shorter descriptions
				TVListings.misc.ui.process_descriptions({
					max_height: "0px", no_elipsis: true,
					toggle_text: "View More", duration: 100
				});
				
				//enable controls
				var region_control = document.getElementById("listings-region-controls");
				var channel_control = document.getElementById("listings-channel-controls");
				
				if (region_control) {
					TVListings.controls.apply_multi_level(
							region_control,
						{'series_id': true, 'page': true}
					);
				}
				
				if (channel_control) {
					$('#listings-channel-list').carousel({xsize: 8, autosize:true});
					TVListings.controls.apply_multi_level(
							channel_control,
						{'series_id': true, 'page': true}
					);
				}
				
				TVListings.controls.apply_multi_level(
					document.getElementById("listings-series-controls"),
					{'page': true}, "400"
				);
				
				break;
			case "week":
				
				TVListings.controls.search.init();
				//enable listings tabs
				TVListings.controls.apply_single_level(
					document.getElementById("listings-controls-tabs"),
					{'time_slot': true}
				);
				
				//enable tooltips
				//if (($.tooltip) && ($(window).height)) TVListings.misc.ui.week_tooltips();
				TVListings.misc.ui.process_descriptions({
					max_height: "0px", no_elipsis: true,
					toggle_text: "View More", duration: 100
				});
				
				//enable controls
				var region_control = document.getElementById("listings-region-controls");
				var channel_control = document.getElementById("listings-channel-controls");
				
				if (region_control) {
					TVListings.controls.apply_multi_level(
						region_control,
						{'date': true, 'time_slot': true}
					);
				}
				
				if (channel_control) {
					TVListings.controls.apply_multi_level(
						channel_control
					);
				}
				TVListings.controls.apply_multi_level(
					document.getElementById("listings-time-controls")
				);
				
				TVListings.controls.datepicker.init();
				
				break;
			case "widget":
				//enable region control
				TVListings.controls.widget();
			
				//enable tooltips
				$("#tooltip .body dl").remove();
				if (($.tooltip) && ($(window).height)) TVListings.misc.ui.widget_tooltips();
				
				//enable shorter descriptions
				TVListings.misc.ui.elipsis_descriptions("2.8em", true);
				
				break;
		}
	}
}

/* Main listings-specific request creation functions */
TVListings.main = {}

/**
 * Object to stage a parameter key-value object and urls before making a request. Completely optional to use.
 */
if (!TVListings.main.stage) {
	TVListings.main.stage ={};
	TVListings.main.stage.url = document.location.href;
	TVListings.main.stage.params = TVListings.properties.process_parameters();
}

/**
 * Specific implementation of TVListings which makes a request for a listings view if the type key exists in the Array
 * @param {Object} arr		Array of key-value pairs used in the request. Must include 'type' for the request to go through
 */
TVListings.main.get_listings = function (arr) {

	if (!arr.type) {
		arr.type = "day";
		arr.filter = "1800";
	}
	
	//TVListings.reporting.initReportingParams(arr); - useless call as the page is not populated yet
	
	switch(arr.type) {
		case "day": this.get_day_listings(arr);
			break;
		case "primetime": this.get_primetime_listings(arr);
			break;
		case "week": this.get_week_listings(arr);
			break;
		case "series": this.get_series_listings(arr);
			break;
		case "widget": this.get_widget_listings(arr);
			break;
		case "search": this.get_search_listings(arr);
		break;
	}
}

TVListings.main.get_week_listings = function (arr) {
	
	var url = TVListings.servlet_address + "GetScheduleByWeek";
	arr.style = "week";

	if (!arr.date) {
		var time = new Date();
		var date = TVListings.misc.date.pad_number(time.getDate().toString(), 2);
		var month = time.getMonth()+1;
		month = TVListings.misc.date.pad_number(month.toString(), 2)
		var year = time.getFullYear().toString();
		var f_date =  date + month + year;
		
		arr.date = f_date;	
		TVListings.main.stage.params.date = {'value': f_date};
		TVListings.main.stage.url = TVListings.properties.change_url_parameter(
			'date',
			date + month + year,
			TVListings.main.stage.url
		)
	}
	if (!arr.time_slot) {
		var time_slot = TVListings.misc.date.generate_offset_time();
		
		arr.time_slot = time_slot;
		TVListings.main.stage.params.time_slot = {'value': f_date};
		TVListings.main.stage.url = TVListings.properties.change_url_parameter(
			'time_slot',
			time_slot,
			TVListings.main.stage.url
		)
	}
	
	//work out current time slot here if not specified
	TVListings.request.get_listings(url, arr);
	
}

TVListings.main.get_day_listings = function (arr) {
	
	var url = TVListings.servlet_address + "GetScheduleByBroadcastDate";
	arr.style = "day";
	
	if (!arr.date) {
		
		var time = TVListings.misc.date.generate_request_date(
			TVListings.main.stage.params.ss
		);
		
		var date = time.getDate();

		var month = time.getMonth()+1;
		var year = time.getFullYear().toString();

		date = TVListings.misc.date.pad_number(date.toString(), 2);
		month = TVListings.misc.date.pad_number(month.toString(), 2);
		
		arr.date =  date + month + year;
	}
	
	TVListings.request.get_listings(url, arr);
	
}

TVListings.main.get_primetime_listings = function (arr) {
	
	var url = TVListings.servlet_address + "GetScheduleByTime";
	arr.style = "prime";
	
	if (!arr.date_time) {
		
		//Get Date and Time information
		var time = new Date();
		var date = time.getDate();
		var month = time.getMonth()+1;
		var year = time.getFullYear();
		var hours = time.getHours();
		var minutes = time.getMinutes();
		
		//Add zero padding to numbers
		date = TVListings.misc.date.pad_number(date.toString(), 2)
		month = TVListings.misc.date.pad_number(month.toString(), 2)
		hours = TVListings.misc.date.pad_number(hours.toString(), 2)
		minutes = TVListings.misc.date.pad_number(minutes.toString(), 2)
		
		//Update properties
		arr.date_time =  date + month + year + hours + minutes;
	}
	
	TVListings.request.get_listings(url, arr);
	
}

TVListings.main.get_series_listings = function (arr) {
	
	var url = TVListings.servlet_address + "GetSeriesSchedule";
	arr.style = "series";
	
	if (arr.series_id) {
		//work out current broadcast day and time here if not specified
		TVListings.request.get_listings(url, arr);
	}
	
}

TVListings.main.get_widget_listings = function (arr) {
	
	var url = TVListings.servlet_address + "GetScheduleByTime";
	arr.style = "widget";
	if (!arr.date_time) {
		
		//Get Date and Time information
		var time = new Date();
		var date = time.getDate();
		var month = time.getMonth()+1;
		var year = time.getFullYear();
		var hours = time.getHours();
		var minutes = time.getMinutes();
		
		//Add zero padding to numbers
		date = TVListings.misc.date.pad_number(date.toString(), 2)
		month = TVListings.misc.date.pad_number(month.toString(), 2)
		hours = TVListings.misc.date.pad_number(hours.toString(), 2)
		minutes = TVListings.misc.date.pad_number(minutes.toString(), 2)
		
		//Update properties
		arr.date_time =  date + month + year + hours + minutes;
	}
	
	TVListings.request.get_listings(url, arr);
	
}

TVListings.main.get_search_listings = function (arr) {
	
	var url = TVListings.servlet_address + "GetTransformedScheduleBySearchString";
	arr.style = "search";
	
	if (!arr.search_string) {
		arr.search_string = document.getElementById("dnitvl_search_input").value;
		
	}
	arr.search_string = decodeURI(arr.search_string); //To stop double-encoding of spaces
	TVListings.request.get_listings(url, arr);
	
}

/**
 * TV Listings configurations. Call these to activate listings application modes.
 */
TVListings.init = {};

TVListings.init.main = function () { //main tv listings application page
	
	var geocode = TVListings.misc.get_geocode();
	if (geocode) {
		TVListings.properties.defaults['geocode'] = {'value': geocode};
	}
	
	$(document).ready(function(){
		
		TVListings.properties.add_parameter('type');
		TVListings.properties.add_parameter('geocode');
		TVListings.properties.add_parameter('country_code');
		TVListings.properties.add_parameter('channel_code');
		TVListings.properties.add_parameter('series_id');
		TVListings.properties.add_parameter('time_slot');
		TVListings.properties.add_parameter('page');
		TVListings.properties.add_parameter('date');
		TVListings.properties.add_parameter('search_string');
		TVListings.properties.add_parameter('fliptime');
		TVListings.properties.add_parameter('filter');
		
		var settings = TVListings.properties.process_parameters();
		TVListings.type = settings.type;
		TVListings.ui.flip.init();
		TVListings.main.get_listings(settings);
		
	});
	
}



TVListings.init.widget = function () { //"what's on now" widget

		TVListings.properties.add_parameter('type');
		TVListings.properties.add_parameter('geocode');
		TVListings.properties.add_parameter('country_code');
		TVListings.properties.add_parameter('channel_code');
		TVListings.properties.add_parameter('date_time');
			
		TVListings.properties.defaults['type'] = {'value': "widget", "override": true};
		
		var settings = TVListings.properties.process_parameters();
		TVListings.main.get_listings(settings);

}

/***/


/* Other Functionality */
TVListings.misc = {};

	/* Reporting Functionality */
	TVListings.reporting = { params:{} }
	TVListings.reporting.func_queue = []
	
		/**
		 * Returns a Dictionary List of information about the current listings page
		 * 
		 */
		
		/* Example
	
			var listings_info = TVListings.misc.get_page_info();
			
			var info = {
				country: listings_info.country,
				channel: listings_info.channel,
				page: listings_info.page,
				series: listings_info.series
			}
	
		 */
		TVListings.reporting.get_page_info = function () {
			
			return this.params;
		}
		
		TVListings.reporting.initReportingParams = function(arr) {
			
			var params = this.params = {}
			params.pageType = arr.type;
			params.pageTitle = $("#listings-controls-tabs .current").text();
			params.country = $("#listings-region-controls .current").text();
			params.channel = $("#listings-channel-controls .current").text();	
			params.channelCode = arr.channel_code;
			params.series = $("#listings-series-controls .current").text();
			params.series_id = arr.series_id || null;
			params.date=arr.date;
		}
		
		TVListings.reporting.exec_queue = function () {
			for (var i=this.func_queue.length-1,k;k=this.func_queue[i];i--) {
				k();
			}
		}
	
		
		
	/* Akamai Geo-targeting */
		
		TVListings.misc.get_geocode = function () {
			/* <esi:vars> */ var geocode = "$(GEO{'country_code'})"; //</esi:vars>
			if (geocode.indexOf("$") == -1) {
				TVListings.misc.get_geocode = function() {
					return geocode;
				}
			} else {
				TVListings.misc.get_geocode = function () { return false }
			}
		}

		
		
		
	/* General Date and time functions */
	TVListings.misc.date = {}
	
		/**
		Convert a string to a JavaScript Date object.
		*/
		TVListings.misc.date.get_date_object = function (dateString, timeString, dateFormat) {
			var dateVal, dArray, d, m, y;	 
			if (!dateFormat) var dateFormat = " ";
			try {
				dArray = this.split_date_string(dateString);				
				if (dArray) {
				  switch (dateFormat) {
					case "dmy" :
					  d = parseInt(dArray[0], 10);
					  m = parseInt(dArray[1], 10) - 1;
					  y = parseInt(dArray[2], 10);
					  break;
					case "ymd" :
					  d = parseInt(dArray[2], 10);
					  m = parseInt(dArray[1], 10) - 1;
					  y = parseInt(dArray[0], 10);
					  break;
					case "mdy" :
					default :
					  d = parseInt(dArray[0], 10);
					  m = parseInt(dArray[1], 10) - 1;
					  y = parseInt(dArray[2], 10);
					  break;
				  }
				  
				  if (timeString) {
				  	var tArray = [timeString.substring(0, 2), timeString.substring(2, 4)];
				  	dateVal = new Date(y, m, d, tArray[0], tArray[1]);
				  } else {
				  	dateVal = new Date(y, m, d);
				  }
				} else if (dateString) {
				  dateVal = new Date(dateString);
				} else {
				  dateVal = new Date();
				}
			} catch(e) {
				dateVal = new Date();
			}
			return dateVal;
		}
		
		/**
		Try to split a date string into an array of elements, using common date separators.
		If the date is split, an array is returned; otherwise, we just return false.
		*/
		TVListings.misc.date.split_date_string = function (dateString) {
			var dArray;
			if (dateString.indexOf(" / ") >= 0)
				dArray = dateString.split(" / ");
			else if (dateString.indexOf(".") >= 0)
				dArray = dateString.split(".");
			else if (dateString.indexOf("-") >= 0)
				dArray = dateString.split("-");
			else if (dateString.indexOf("\\") >= 0)
				dArray = dateString.split("\\");
			else
				dArray = [dateString.substring(0,2), dateString.substring(2,4), dateString.substring(4,8)];
				
			return dArray;
		}


		/**
		 * Takes an offset in seconds (found in the XML now)
		 * and returns a JS date object set to the correct schedule
		 * date.
		*/
		TVListings.misc.date.generate_request_date = function (offset) {
		    var now = new Date();
		
		    if (this.get_day_in_seconds(now) < offset) {
		        return (new Date(now.getTime() - 86000000));
		    } else {
		        return now;
		    }
		
		}
		
		TVListings.misc.date.get_today = function () {
		    var time = new Date();
		
			var date = TVListings.misc.date.pad_number(time.getDate().toString(), 2);
			var month = time.getMonth()+1;
			month = TVListings.misc.date.pad_number(month.toString(), 2)
			var year = time.getFullYear().toString();
			return   date + month + year;
			
		}
		
		/**
		 * Takes a date and returns the number of seconds that
		 * have expired on that dates current day
		 */
		TVListings.misc.date.get_day_in_seconds = function (date) {
		    return date.getTime() % 86000;
		}
	
		/**
		 * Generates an offset time period from the current local time
		 */
		TVListings.misc.date.generate_offset_time = function () {
		    var now = new Date().getHours();
		    return now - (now % 2);
		}
	
		/**
		 * Adds zero-padding to a number.
		 * @param {String}  num		Number to pad as a string
		 * @param {Integer} pad		Amount of characters to pad until
		 */
		TVListings.misc.date.pad_number = function(num, pad){
			var padding = "";
			for (var i=0;i<pad-num.length;i++) { padding += "0";}
			num = (padding + num).slice("-" + pad);
			return num;
		}
	
	/* Random UI functions */
	TVListings.misc.ui = {}
	
		
		/* IE Fixes */
		/**
		 * Requires JQuery
		 */
		TVListings.misc.ui.ie_fixes = function () {
			var pctrl = $("ul.listings-page-controls>li.pagination>ol")
			pctrl.children("li:first-child").addClass("first-child");
			pctrl.children("li:first-child").addClass("last-child");
			
			$(".week-view table.listings-results tbody tr").each(function () {
				$(this).children("td:first").addClass("first-child");
				$(this).children("td:last").addClass("last-child");
			});
		}
	
		/* Tooltips */
		/**
		 * Implementation Specific. Initialise Weekly Listings tooltips.
		 */
		TVListings.misc.ui.week_tooltips = function () {
			var programme = $(".week-view table.listings-results tbody td.listings-programme:not(.listings-filler)");
			programme.each( function () {
				var self = $(this);
				var tooltip = TVListings.misc.ui.create_tooltip(
					self.children("dl").children("dt").text(),
					self.children("dl").children("dd.start-time").html(),
					self.children("dl").children("dd.description").text(),
					"week"
				)
				TVListings.misc.ui.attach_tooltip(this, tooltip, "week");

				self.hover(
					function(){ self.addClass("hover")},
					function(){ self.removeClass("hover")}
				)
			});
		}
		
		/**
		 * Implementation Specific. Initialise Widget Listings tooltips.
		 */
		TVListings.misc.ui.widget_tooltips = function () {
			var programme = $(".widget-view table.listings-widget-results tbody td.listings-programme").not(":has(.listings-error-nodata)");
			programme.each(function(){
				var self = $(this);
				var tooltip = TVListings.misc.ui.create_tooltip(
					self.children("dl").children("dt").text(),
					self.children("dl").children("dd.start-time").html(),
					self.children("dl").children("dd.description").text(),
					"widget"
				)
				TVListings.misc.ui.attach_tooltip(this, tooltip, "widget");
			});
		}
		
		/**
		 * Requires JQuery. Creates a basic TV Listings style tooltip element.
		 * @param {Object} title	DOM Element or text node to use as title
		 * @param {Object} stitle	DOM Element or text node to use as sub-title
		 * @param {Object} content	DOM Element or text node to use as content
		 */
		TVListings.misc.ui.create_tooltip = function(title, stitle, content, type){
			var elem = $(document.createElement("dl"));
			var i1 = $(document.createElement("dt"));
			var i2 = $(document.createElement("dd")).addClass("sub-title");
			var title = i1.append($(document.createElement("strong")).append(title));
			var stitle = i2.append(stitle);
			elem.append(title).append(stitle);
			
			var blank = RegExp('^( |)$');
			
			if ((!content.match(blank))&&(content !== null)) {
				var i3 = $(document.createElement("dd")).addClass("content");
				var content = i3.append(content);
				elem.append(content);
			}
			elem.addClass("js_tooltip");
			if (type) elem.addClass(type);
			return elem;
		}
		
		/**
		 * Requires JQuery. Requires Jquery Tooltip plugin. Creates a semi-opaque tooltip from 'tooltip' attached to 'elem'.
		 * @param {Object} elem		DOM Element to attach tooltip to
		 * @param {Object} tooltip	DOM Element to use as tooltip
		 */	
		TVListings.misc.ui.attach_tooltip = function (elem, tooltip, type) {
			if (!type) var type = " generic";
			
			var elem = $(elem);
			elem.tooltip({
				track: true,
				delay: 400,
				extraClass: "js_tooltip " + type,
				bodyHandler: function(){
				return tooltip;
				}
			});
			elem.mouseover(function () { $("#tooltip").css("opacity", ".95")})
		}
		
		/* Programme Descriptions */

		/**
		 * Requires JQuery. Implementation specific.
		 */
		TVListings.misc.ui.process_descriptions = function (config) {

			if (window.jQuery) {
				var no_elipsis = config.no_elipsis || false;
				var no_toggle = config.no_toggle || false;
				var toggle_text = config.toggle_text || "";
				var max_height = config.max_height || "2.4em";
				var no_button = config.no_button || false;
				var target = config.target;
				var dur = config.duration;

				var descriptions = $(".listings-programme .description");
				descriptions.each(function () { 
						$(this).wrapInner(document.createElement("span"));
				});

				descriptions.each(function(){

					var self = $(this), parent = $(this).parent();
					var desc_inner = self.children("span:first");
					this.innerWrapper = desc_inner;

					var link = document.createElement('a');
					link.className = "description-toggle";
					link.appendChild(document.createTextNode(toggle_text));
					var button = no_button ? parent.parent() : link;

					self.removeClass("disabled");
					if (self.height()) {
						self.css("height", max_height); //set the description dd
						desc_inner.fullHeight = desc_inner.offsetHeight;
						if (self.height() < desc_inner.fullHeight) {
							TVListings.misc.ui.toggle_elipsis(desc_inner, no_elipsis);
							$(button).click(function(){
								
								TVListings.misc.ui.toggle_button(desc_inner,{
									duration: dur, height: max_height
								});
							});
							if (!no_button)
								(target ? parent.children(target) : parent).append(link);
						} else {
							self.css("height", "auto");
						}
					}

				});		
			}
		}
		
		/**
		 * Figures out how many characters can fit on one line and removes the excess
		 * @param {Object} elem		Element directly containing text to cut down
		 * @param {Object} cut		Amount of characters to remove at a time
		 */
		TVListings.misc.ui.cut_description = function (elem, trailStr) {

			var trailStr = trailStr || "...",
					len = Math.ceil(elem.offsetWidth / ($.jqem.current()/2.2));
			elem.innerHTML = elem.innerHTML.substring(0, len-trailStr.length);
			var trail = document.createElement("span");
			trail.className = "trailing-text";
			trail.innerHTML = trailStr;
			elem.appendChild(trail);
		}
		
		/**
		 * Implementation specific.
		 */
		TVListings.misc.ui.reset_description = function (elem) { 
			if (elem.fullText) { elem.innerHTML = elem.fullText; elem.fullText = null;}
		}	
		
		/**
		 * Implementation specific.
		 */
		TVListings.misc.ui.toggle_elipsis = function (elem, no_elipsis) {
			if (elem) {
				if (elem.fullText == null) {
					elem.fullText = elem.innerHTML;
					this.cut_description(elem, (no_elipsis ? "..." : ""));
				} else { this.reset_description(elem)}
			}
		}
		
		/**
		 * Requires JQuery. Implementation specific.
		 */
		TVListings.misc.ui.toggle_button = function (elem, config) {

			var dur = config.duration || 500;
			var height = config.height || "1.25em";
			var toggle_func = config.toggle || function(type, elem, desc) {
				elem = elem[0];
				if (type == 1) {
					desc.animate({height:height}, dur, "linear",
						function () { TVListings.misc.ui.toggle_elipsis(elem)}
					);
				} else if (type == 0) {
					var full_height = elem.fullHeight;
					TVListings.misc.ui.toggle_elipsis(elem);
					desc.animate({height: full_height}, dur, "linear");
				}
			};

			//Get relevant jQuery elements
			elem = $(elem);
			var parent = elem.parent().parent().parent().parent()[0];
			var desc = elem.parents("dl").children(".description");
			
			//Check for show/hide classes
			var regex = RegExp('(^|.* )(expanded)($| .*)');
			var match = desc[0].className.match(regex);
			desc.toggleClass("expanded");	

			if ((match !== null)) {
				// if expanded, hide description and animate
				parent.className = parent.className.replace("expanded", "");
				toggle_func(1, elem, desc);
			} else {
				// if hidden, show description and animate
				parent.className += " expanded";
				toggle_func(0, elem, desc);				
			}
		}

TVListings.ui = {}
TVListings.ui.flip = {}

TVListings.ui.flip.init = function(){
	if (TVListings.enable_flip)
	{
		$("#dni-listings").delegate(".module:not(.flip) .module-title a", "click", function(e){
			e.preventDefault();
		});

		$("#dni-listings").delegate(".listings-programme", "click", function(e){
			$('#series_wrapper').removeClass('menu-open');
			if ($(".flipped").length <= 0){
				e.stopPropagation();
				TVListings.ui.flip.flip_listing(this);
			}
		});

		$("#dni-listings").delegate(".close-link", "click", function(e){
			e.preventDefault();
			e.stopPropagation();
			TVListings.ui.flip.deactivate();
			$("#listings-reminder-msg").html("");
		});

		$("html").delegate("body", "click", function(){
			TVListings.ui.flip.deactivate();
		});

		$("#dni-listings").delegate(".reminder .mail-link", "click", function(e){
			e.stopPropagation();
			e.preventDefault();
			TVListings.ui.flip.deactivate();
			var id = $(this).attr("id").substring("reminder-".length);
			$("#listings-reminder-id").attr("value",id);

			$(".submitted").removeClass("submitted");
			$("#listings #reminders").fadeIn();

		});

		$("#dni-listings").delegate(".module-social", "click", function(e){
			e.stopPropagation();
			e.preventDefault();
			sharingLinkClicked(e);
		});
		
		$("#dni-listings").delegate("#listings-reminder-email", "click", function(e){
			if(($(this).hasClass("placeholder") && $(this).val() == $(this).attr("placeholder"))){
				$(this).removeClass("placeholder");
				$(this).attr("value","");
			}
		});

		$("#dni-listings").delegate("#listings-reminder-submit", "mousedown", function(e){
			$(this).addClass("active");
		});

		$("#dni-listings").delegate("#listings-reminder-submit", "mouseup", function(e){
			$(this).removeClass("active");
		});
		

		$("#dni-listings").delegate("#reminders-modal-close", "click", function(e){
			e.stopPropagation();
			$("#listings #reminders").fadeOut();
		});
		
	}
}

TVListings.ui.flip.activate = function(obj){
	if (TVListings.enable_flip)
	{
		$(".flipped .listings-programme").each(function() {
			TVListings.ui.flip.flip_listing(this);
		});
		if (TVListings.ui.flip.scrollTo)
		{
			if ($(".flipped").length > 0 && $(".flipped").offset().top)
			{
				$(document).scrollTop($(".flipped .flip").offset().top)
			}
			
		}
		TVListings.ui.flip.scrollTo = false;
		
		
		//pad the titles as per rest of site
		if (window.padLineFix)
			padLineFix();
		
		
		//ensure flipcard is centred in the timeslot
		$('#dni-listings .module-container').each(function() {
			$(this).height($(this).parent().height());
		});
		
	}
}

TVListings.ui.flip.flip_listing = function(obj){
	if (TVListings.enable_flip)
	{
		var flip = $(obj).find(".flip");
		
		if(flip){

			if (flip.find("span.datetime").length > 0)
			{
				var dateTime = flip.find("span.datetime").attr("id").substring("datetime-".length);
				var date = new Date();
				var threshold = date.getTime();
				var itemDateTime = TVListings.misc.date.get_date_object(dateTime.substring(0,8),dateTime.substring(8));
				if ((itemDateTime.getTime() < threshold)) {
					flip.find(".module-social").remove();
					flip.find(".reminder").remove();
					flip.find("span.datetime").remove();
					flip.addClass("no-reminder");
				}

			}

			$("#listings-results>.overlay").fadeIn(function() {if(this.style.filter) this.style.removeAttribute('filter');});
			$(obj).find(".flip").fadeIn();
			$(obj).parent().addClass("flipped");
			$(obj).find(".module-container").css("z-index", 1000);
			$("#listings-results table").addClass("overlayed");
			
			if(flip.find(".close-link").length <= 0){
				flip.append($('<a class="close-link close-button" href="#"></a>'))
			}
			
			//reapply padding to flipcard
			flip.find("h3 .line").each(function(){$(this).replaceWith($(this).html())});
			flip.find("h3").removeClass('padded-out-js');
			padLineFix();
			
		}
	}
}

TVListings.ui.flip.deactivate = function(){
	if (TVListings.enable_flip)
	{
		$("#listings-results>.overlay").fadeOut();
		$("#listings-results .flip").fadeOut(function() {$("#listings-results .module-container").css("z-index", 0);});
		$("#listings-results tr.flipped").removeClass("flipped");
		$("#listings-results table.overlayed").removeClass("overlayed");
	}
}

/**
 * FACEBOOK FEED
 */
TVListings.facebook = {};
TVListings.facebook.init = function(){
	
	if ((!window.$TORA)||($TORA("USER").getAuthInfo && !$TORA("USER").getAuthInfo("LOGGED_IN_ON_FB"))) {
		return;
	}
	
	var facebookFeedConfig = $("#facebook-feed-configuration *");
	if(facebookFeedConfig.length == 0){
		//NO FACEBOOK INFO - DO NOTHING
		return;
	}
	var facebookInfo = {};
    facebookFeedConfig.each(function () {
		var info = TVListings.properties.get_info_from_string(this.value,"%%");
		facebookInfo[this.className] = info;		
    });
	
	TVListings.facebook.facebookInfo = facebookInfo;
	TVListings.facebook.feed_link();
}

TVListings.facebook.feed_link = function () {		
	function createLinkElement(HAS_AIRED, programmeElement, programmeDate){
		var elem = document.createElement("dd"),
			type = TVListings.type;
		elem.className = "facebook-link-item";
		
		var img = document.createElement("img");
	    img.setAttribute("src", "/resources/ua/images/ua_fb_login_logo.gif");
		
		var facebookInfo = TVListings.facebook.facebookInfo;
		
		var feedText = "";

		if(type == "week"){
			feedText = "Facebook"
		} else {
			feedText = facebookInfo.feedLink.textBeforePresent;
			if(HAS_AIRED){
				feedText = facebookInfo.feedLink.textBeforePast;
			}
		}

		var text = document.createTextNode(" " + feedText + " ");
						
		var link = document.createElement("a");
		link.setAttribute("href", "#");
		link.onclick = function(){
			var _HAS_AIRED = HAS_AIRED;
			var _programmeElement = programmeElement;
			var _programmeDate = programmeDate;
			return TVListings.facebook.feed_link_onClick(_HAS_AIRED, _programmeElement, _programmeDate, type);
		    };
		
		if(type != "week"){
			link.innerHTML = facebookInfo.feedLink.linkText;
			elem.appendChild(img);
			elem.appendChild(text);
		} else {
			link.appendChild(img);
			link.appendChild(text);
		}


		elem.appendChild(link);
		
		return elem;
	}
	
	function getTime(className){
       var itemDatetime = TVListings.properties.get_info_from_string(className).datetime;
	   var itemDatetimeObj = TVListings.misc.date.get_date_object(
	                            itemDatetime.substring(0,8),
	                            itemDatetime.substring(8)
	                            );
	   return itemDatetimeObj;
	}
	
	var now = new Date();
	
	//FOR EVERY ROW WITH PROGRAMMES
	$(".listings-results tbody tr").each(function(){
		//FIND THE PROGRAMME TIME
		$(".listings-programme", this).each(function(){
			var className = this.className;
			if(className.indexOf("datetime") == -1){
				//NOT A VALID ROW
				return;
			}
			var itemDatetime = getTime(className);
			var HAS_AIRED = true;
			if(itemDatetime > now){
				HAS_AIRED = false;
			}

			var dl = $("dl", this);
			//APPEND FACEBOOK FEED LINK
			if($(".duration", dl).length > 0){
				$(".duration", dl).after(createLinkElement(HAS_AIRED, this, itemDatetime));
			//createLinkElement(HAS_AIRED, this, itemDatetime)
				}
			else{
				if ($('#listings').hasClass("search-view")){
					$(".description", dl).after(createLinkElement(HAS_AIRED, this, itemDatetime));
				// createLinkElement(HAS_AIRED, this, itemDatetime)
				}
				else {
					$(".description", dl).before(createLinkElement(HAS_AIRED, this, itemDatetime));
				}
			}
	    });
	});
}
TVListings.facebook.feed_link_onClick = function (HAS_AIRED, programmeElement, programmeDate, type) {
	var facebookInfo = TVListings.facebook.facebookInfo;
	var pad = TVListings.misc.date.pad_number
	
	function toString(element){
		if(!element){
			return "";
		}
		var dummy = document.createElement("div");
		dummy.appendChild(element);
		return dummy.innerHTML;
	}
	
	function formatDate(date){
		return [pad(date.getDate()+"",2),pad((date.getMonth() + 1)+"",2),1900 + date.getYear()].join("/");
	}
	
	function formatTime(date){
		return [pad(date.getHours()+"", 2),pad(date.getMinutes()+"",2)].join(":");
	}
	
	function getChannelPart(){
		return facebookInfo.site.description;
	}
	
	function getShowPart(programmeElement, seriesViewLink){
		if(type != "week"){
			programmeElement = $(programmeElement).parent();
		}
		
		//GET SHOW NAME FROM HTML
		var showPart = $("dl dt",programmeElement).text();
		//SHOW LINK DEFAULTS TO SERIES VIEW
		var showLink = seriesViewLink;
		
		if(!showPart || showPart.length == 0){
			//GET EPISODE TITLE NAME
			showPart = $("dl dd.episode-title",programmeElement).text();
		}
		//GET PROMO LINK IF ANY
		$("ul.programme_promo_links li a:first",programmeElement).each(function(){
			//IF THERE IS A PROMO => LINK TO IT
			showLink = this.href;
		});
		
		var link = document.createElement("a");
		link.setAttribute("href",showLink);
		link.innerHTML =  showPart;
		return {value: showPart,
		        link:  showLink};
	}
	
	function getSeriesViewLink(programmeElement){
		//DEFAULTS TO CURRENT PAGE (IF SERIES VIEW)
		var href = window.location.href;
		if(type != "series"){
			//GET LINK TO SERIES VIEW FROM HTML
			href = $("dl dt a:first",programmeElement)[0].href;
		}
		return href;
	}
	
	function createBody(programmeElement, programmeDate, lineText){
		
		return lineText
				.replace("${date}",formatDate(programmeDate))
				.replace("${time}",formatTime(programmeDate));
	}
	
	function createImage(programmeElement, imageLink){
		var imageSrc = facebookInfo.site.logo;
		if(type != "series"){
			//ANY OTHER VIEW
			$("dd.promo-image img",programmeElement).each(function(){
				//THERE IS ONLY ONE
				imageSrc = this.src;	
			});
		}else{
			//FOR SERIES VIEW
			$("table thead th.title img").each(function(){
				//THERE IS ONLY ONE
				imageSrc = this.src;
			})
		}
		
		return {url: imageSrc,
		        target: imageLink};
	}

	function process(HAS_AIRED, programmeElement, programmeDate){
		var seriesViewLink = getSeriesViewLink(programmeElement);
		var showPart = getShowPart(programmeElement, seriesViewLink);
		var channelPart = getChannelPart();
		
		var caption = facebookInfo.feedContent.headingPresent;
		var line1Text = facebookInfo.feedContent.line1PresentText;
		var line1LinkText = facebookInfo.feedContent.line1PresentLink;
		if(HAS_AIRED){
			caption = facebookInfo.feedContent.headingPast;
			line1Text = facebookInfo.feedContent.line1PastText;
			line1LinkText = facebookInfo.feedContent.line1PastLink;
		}
		caption = caption
				.replace("${show}",showPart.value)
				.replace("${channel}",channelPart);
		
		var bodyString = createBody(programmeElement, programmeDate, line1Text);
		
		var feedContent = {
			title: showPart.value,
			url: showPart.link,
			caption: "%%USER%% " + caption + ".",
			body: bodyString + ".",
			images: [createImage(programmeElement,showPart.link)],
			actions: [{type: line1LinkText,
					text: showPart.value, url: seriesViewLink}]
		};
		$TORA("USER").publishStory(feedContent);
		return false;
	}
	
	TVListings.facebook.feed_link_onClick =  process;
	
	return process(HAS_AIRED, programmeElement, programmeDate);
}

	
/* ***** DATEPICKER FROM HERE ****** */
	
		
// $Id: this.js 5899 2007-03-19 17:38:37Z bashford $

var DatePicker = {
	name: "DatePicker",
	datePickerDivID: "dnitvl_datepicker",
	iFrameDivID: "dnitvl_datepicker_iframe",
	
	dayArrayShort: ['Su', 'Mo', 'Tu', 'We', 'Th', 'Fr', 'Sa'],
	dayArrayMed: ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'],
	dayArrayLong: ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'],
	monthArrayShort: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'],
	monthArrayMed: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'June', 'July', 'Aug', 'Sept', 'Oct', 'Nov', 'Dec'],
	monthArrayLong: ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'],
	defaultDateSeparator: " / ",        // common values would be "/" or "."
	defaultDateFormat: "dmy",        // valid values are "mdy", "dmy", and "ymd"
		
	displayDatePicker: function (dateFieldName, displayBelowThisObject, dtFormat, dtSep) {
		this.targetDateField = document.getElementsByName(dateFieldName).item(0);
		if (this.targetDateField == null) {
			this.targetDateField = document.getElementById(dateFieldName);
		}
		
		// if we weren't told what node to display the datepicker beneath, just display it
		// beneath the date field we're updating
		if (!displayBelowThisObject)
			this.displayBelowThisObject = this.targetDateField;
		else
			this.displayBelowThisObject = displayBelowThisObject;
		
		
		
		// if a date separator character was given, update the dateSeparator variable
		if (dtSep)
			this.dateSeparator = dtSep;
		else
			this.dateSeparator = this.defaultDateSeparator;
		
		// if a date format was given, update the dateFormat variable
		if (dtFormat)
			this.dateFormat = dtFormat;
		else
			this.dateFormat = this.defaultDateFormat;
		
		this.drawDatePicker(this.targetDateField);
	},
	
	drawDatePicker: function (targetDateField, x, y) {
	  var dt = this.getFieldDate(targetDateField.value );
	 
		if (!document.getElementById(this.datePickerDivID)) {
			var newNode = document.createElement("div");
			newNode.setAttribute("id", this.datePickerDivID);
			newNode.setAttribute("class", "dpDiv");
			newNode.setAttribute("style", "visibility: hidden;");
			document.body.appendChild(newNode);
		}
	 
	  // move the datepicker div to the proper x,y coordinate and toggle the visiblity
	  var pickerDiv = document.getElementById(this.datePickerDivID);
	  pickerDiv.style.position = "absolute";
	  pickerDiv.style.visibility = (pickerDiv.style.visibility == "visible" ? "hidden" : "visible");
	  pickerDiv.style.display = (pickerDiv.style.display == "block" ? "none" : "block");
	  
	  if(TVListings.properties.process_parameters().type == "day")
	  	pickerDiv.style.left = ($(this.targetDateField).offset().left-2) + 'px';
	  else
	  	pickerDiv.style.left = ($(this.targetDateField).offset().left-30) + 'px';
		
	  pickerDiv.style.top = ($(this.targetDateField).offset().top + 20) + 'px';
	  pickerDiv.style.zIndex = 10000;
	 
	  // draw the datepicker table
	  this.refreshDatePicker(targetDateField.name, dt.getFullYear(), dt.getMonth(), dt.getDate());
	},
	
	/**
	This is the function that actually draws the datepicker calendar.
	*/
	refreshDatePicker: function (dateFieldName, year, month, day) {
	  // if no arguments are passed, use today's date; otherwise, month and year
	  // are required (if a day is passed, it will be highlighted later)
		var thisDay = new Date();
		
		//Some TV Listings Alterations 
		var hideForwardButton = false;
		var hideBackButton = false;
 
		if ((year >= this.startDate["y"])) {
			if ((year == this.startDate["y"]) && (month == this.startDate["m"]-1)) 
				var startDay = this.startDate["d"];
			else 
				var startDay = -100;
		} else {
				var startDay = this.startDate["d"];
				day = this.startDate["d"];
				month = this.startDate["m"]-1;
				year = this.startDate["y"];
				document.getElementsByName(dateFieldName)[0].value = this.getDateString(new Date(year, month, day));
		}
		
		if ((year <= this.endDate["y"])) {
			if ((year == this.endDate["y"]) && (month == this.endDate["m"]-1)) 
				var endDay = this.endDate["d"];
			else 
				var endDay = 100;
		} else {
				var endDay = this.endDate["d"];
				day = this.endDate["d"];
				month = this.endDate["m"]-1;
				year = this.endDate["y"];
				document.getElementsByName(dateFieldName)[0].value = this.getDateString(new Date(year, month, day));
		}
		
		if ((month >= 0) && (year > 0)) {
			thisDay = new Date(year, month, 1);
		} else {
			day = thisDay.getDate();
			thisDay.setDate(1);
		}
		
		if ((year==this.startDate["y"]) && (month)==this.startDate["m"]-1) { hideBackButton = true;}
		if ((year==this.endDate["y"]) && (month)==this.endDate["m"]-1) { hideForwardButton = true;}
	
		
		// the calendar will be drawn as a table
		// you can customize the table elements with a global CSS style sheet,
		// or by hardcoding style and formatting elements below
		var crlf = "\r\n";
		var TABLE = "<table cols=7 class='dpTable'>" + crlf;
		var xTABLE = "</table>" + crlf;
		var TR = "<tr class='dpTR'>";
		var TR_title = "<tr class='dpTitleTR'>";
		var TR_days = "<tr class='dpDayTR'>";
		var TR_todaybutton = "<tr class='dpTodayButtonTR'>";
		var xTR = "</tr>" + crlf;
		var TD = "<td class='dpTD' onMouseOut='this.className=\"dpTD\";' onMouseOver=' this.className=\"dpTD dpTDHover\";' ";
		var TD_title = "<div class='dpTitleTD'>";
		var TD_buttons = "<span class='dpButtonTD'>";
		var TD_todaybutton = "<td colspan=7 class='dpTodayButtonTD'>";
		var TD_days = "<td class='dpDayTD'>";
		var TD_selected = "<td class='dpTD dpDayHighlightTD' onMouseOut='this.className=\"dpTD dpDayHighlightTD\";' onMouseOver='this.className=\"dpTD dpDayHighlightTD dpTDHover\";' ";    // leave this tag open, because we'll be adding an onClick event
		var xTD = "</td>" + crlf;
		var DIV_title = "<div class='dpTitleText'>";
		var DIV_selected = "<div class='dpDayHighlight'>";
		var xDIV = "</div>";
		
		var TR_title = TR_title + "<td class='dpTitle' colspan='7'>";
		var TD_active = "<td class='dpTD active' onMouseOut='this.className=\"dpTD active\";' onMouseOver=' this.className=\"dpTD dpTDHover active\";' ";
		var TD_blank = "<td>"
		var TD_selected_active = "<td class='dpTD active dpDayHighlightTD' onMouseOut='this.className=\"dpTD active dpDayHighlightTD\";' onMouseOver='this.className=\"dpTD active dpTDHover\";' ";
		var TD_buttons_bck = "<span class='dpButtonTD back'>";
		var TD_buttons_fwd = "<span class='dpButtonTD forward'>";
		var SPAN = "<span>";
		var xSPAN = "</span>" + crlf;
		
		// start generating the code for the calendar table
		var html = TABLE;
	 
		// this is the title bar, which displays the month and the buttons to
		// go back to a previous month or forward to the next month
		html += TR_title;
		if (!hideBackButton) html += TD_buttons_bck + this.getButtonCode(dateFieldName, thisDay, -1, "&lt;") + xSPAN;
		html += TD_title + DIV_title + this.monthArrayLong[ thisDay.getMonth()] + " " + thisDay.getFullYear() + xDIV + xDIV;
		if (!hideForwardButton) html += TD_buttons_fwd + SPAN + this.getButtonCode(dateFieldName, thisDay, 1, "&gt;") + xSPAN;
		html += xTR;
	 
		// this is the row that indicates which day of the week we're on
		html += TR_days;
		for (i=0;i<this.dayArrayShort.length;i++)
			html += TD_days + SPAN + this.dayArrayShort[i] + xSPAN + xTD;
			html += xTR;
	 
		// now we'll start populating the table with days of the month
		html += TR;
	 
		// first, the leading blanks
		for (i = 0; i < thisDay.getDay(); i++)
			html += TD_blank + SPAN + "&nbsp;" + xSPAN + xTD;
	 
		// now, the days of the month
		do {
			var dayNum = thisDay.getDate();
			
			var TD_onclick_active = " onclick=\"" + this.name + ".updateDateField('" + dateFieldName + "', '" + this.getDateString(thisDay) + "');\">";			
			var TD_onclick = " onclick=\"return false;\">";			
			
			if ((startDay<=dayNum)&&(dayNum<=endDay)) {
				var TD_ = TD_active;
				var TD_selected_ = TD_selected_active;
				var TD_onclick_ = TD_onclick_active;
			} else {
				var TD_ = TD;
				var TD_selected_ = TD_selected;
				var TD_onclick_ = TD_onclick;
			}
			
			if (dayNum == day)
			  html += TD_selected_ + TD_onclick_ + SPAN + DIV_selected + dayNum + xDIV + xSPAN + xTD;
			else
			  html += TD_ + TD_onclick_ + SPAN + dayNum + xSPAN + xTD;
			// if this is a Saturday, start a new row
			if (thisDay.getDay() == 6)
			  html += xTR + TR;
			
			// increment the day
			thisDay.setDate(thisDay.getDate() + 1);
		} while (thisDay.getDate() > 1)
	 
		// fill in any trailing blanks
		if (thisDay.getDay() > 0) {
			for (i = 6; i >= thisDay.getDay(); i--)
			  html += TD_blank + SPAN + "&nbsp;" + xSPAN + xTD;
		}
		html += xTR;
	 
		// add a button to allow the user to easily return to today, or close the calendar
		var today = new Date();
		var todayString = "Today is " + this.dayArrayMed[today.getDay()] + ", " + this.monthArrayMed[ today.getMonth()] + " " + today.getDate();
		html += TR_todaybutton + TD_todaybutton;
		html += "<button class='dpTodayButton' onClick='" + this.name + ".refreshDatePicker(\"" + dateFieldName + "\");'>this month</button> ";
		html += "<button class='dpTodayButton' onClick='" + this.name + ".updateDateField(\"" + dateFieldName + "\");'>close</button>";
		html += xTD + xTR;
	 
		// and finally, close the table
		html += xTABLE;
	 
		document.getElementById(this.datePickerDivID).innerHTML = html;
		// add an "iFrame shim" to allow the datepicker to display above selection lists
	},

	getButtonCode: function (dateFieldName, dateVal, adjust, label) {
	  var newMonth = (dateVal.getMonth () + adjust) % 12;
	  var newYear = dateVal.getFullYear() + parseInt((dateVal.getMonth() + adjust) / 12);
	  if (newMonth < 0) {
		newMonth += 12;
		newYear += -1;
	  }
	  return "<button class='dpButton' onClick='" + this.name + ".refreshDatePicker(\"" + dateFieldName + "\", " + newYear + ", " + newMonth + ");'>" + label + "</button>";
	},
	
	getDateString: function (dateVal) {
		var dayString = "00" + dateVal.getDate();
		var monthString = "00" + (dateVal.getMonth()+1);
		dayString = dayString.substring(dayString.length - 2);
		monthString = monthString.substring(monthString.length - 2);
	 
		switch (this.dateFormat) {
			case "dmy" :
			  return dayString + this.dateSeparator + monthString + this.dateSeparator + dateVal.getFullYear();
			case "ymd" :
			  return dateVal.getFullYear() + this.dateSeparator + monthString + this.dateSeparator + dayString;
			case "mdy" :
			default :
			  return monthString + this.dateSeparator + dayString + this.dateSeparator + dateVal.getFullYear();
		}
	},
	
	/**
	Convert a string to a JavaScript Date object.
	*/
	getFieldDate: function (dateString) {
		var dateVal, dArray, d, m, y;	 
		try {
			dArray = this.splitDateString(dateString);
			if (dArray) {
			  switch (this.dateFormat) {
				case "dmy" :
				  d = parseInt(dArray[0], 10);
				  m = parseInt(dArray[1], 10) - 1;
				  y = parseInt(dArray[2], 10);
				  break;
				case "ymd" :
				  d = parseInt(dArray[2], 10);
				  m = parseInt(dArray[1], 10) - 1;
				  y = parseInt(dArray[0], 10);
				  break;
				case "mdy" :
				default :
				  d = parseInt(dArray[1], 10);
				  m = parseInt(dArray[0], 10) - 1;
				  y = parseInt(dArray[2], 10);
				  break;
			  }
			  dateVal = new Date(y, m, d);
			} else if (dateString) {
			  dateVal = new Date(dateString);
			} else {
			  dateVal = new Date();
			}
		} catch(e) {
			dateVal = new Date();
		}
		return dateVal;
	},
		
	/**
	Try to split a date string into an array of elements, using common date separators.
	If the date is split, an array is returned; otherwise, we just return false.
	*/
	splitDateString: function (dateString) {
		var dArray;
		if (dateString.indexOf(" / ") >= 0)
			dArray = dateString.split(" / ");
		else if (dateString.indexOf(".") >= 0)
			dArray = dateString.split(".");
		else if (dateString.indexOf("-") >= 0)
			dArray = dateString.split("-");
		else if (dateString.indexOf("\\") >= 0)
			dArray = dateString.split("\\");
		else
			dArray = [dateString.substring(0,2), dateString.substring(2,4), dateString.substring(4,8)];
			
		return dArray;
	},
	
	updateDateField: function (dateFieldName, dateString) {
		var targetDateField = document.getElementsByName(dateFieldName).item(0);
		if (targetDateField == null) {
			targetDateField = document.getElementById(dateFieldName);
		}
		if (dateString) targetDateField.value = dateString;
	 	
		
		var pickerDiv = document.getElementById(this.datePickerDivID);
		pickerDiv.style.visibility = "hidden";
		pickerDiv.style.display = "none";
	 
		this.targetDateField.focus();
	 
		var date_arr = this.splitDateString(dateString);
		var listings_date = date_arr[0] + '' + date_arr[1] + '' + date_arr[2];
		document.location.href = TVListings.properties.change_url_parameter("date", listings_date);
	}

}

