
/** SOURCE:  _ui/js/activeuser.js  **/

/**
 * @fileoverview Handles behaviour for the "logged-in" user
 * @author Josh Johnston josh@xhtmlized.com
 * @version 0.1
 * @requires prototype.1.6.0.2
 * @requires FloatingPanels
 * @requires GhostValues
 * @requires NewWindow
 */

/**
 * ActiveUser: Handles behaviour for the "logged-in" user
 * @namespace
 */
var ActiveUser = function(){
  /*global $, $$, FloatingPanels, GhostValues, NewWindow */
	var _signInForm;
	var _signUpForm;
  var _baseElm;
  var self = /** @scope ActiveUser */{
    /**
     * Initialize the ActiveUser component
     * @param {DOMelement} Base element for user data
     */
    initialize: function(baseElm) {
      _baseElm = baseElm;

      // bind event callbacks
      // clicking sign in triggers the floating panel
		_baseElm.select('a.signin')[0].observe('click', self.handleSignInClick);
		_baseElm.select('a.signup')[0].observe('click', self.handleSignUpClick);
		_baseElm.select('li.signout a')[0].observe('click', self.handleSignOutClick);

		if(pluckgbl.cCheck('HD')) {
			_baseElm.select('a.signin')[0].innerHTML = "Welcome " + pluckgbl.getName("HD");
			_baseElm.select('a.signup')[0].hide();
		}

		// check for a logged-in user
		var name = FoxUtils.getCookie('COOKIE_FIRSTNAME');
		if (name) {
			self.loginUser(name);
		} else {
			self.logoutUser();
		}
    },

    /**
     * Log in a user
     * @param {string} username
     */
    loginUser: function(username) {
      // set the user's name in the welcome field
      if(pluckgbl.cCheck('HD')) {
		document.getElementById("signin_link").innerHTML = "Welcome " + pluckgbl.getName('HD');
		
		// TEMPORARY FIX FOR SYTYCD PLUCK INTEGRATION 
		// if has callback
		if (pluckgbl.hasCallback != null) {
			pluckgbl.hasCallback();
			// reset variable
			pluckgbl.hasCallback = null;
		}
		
		$('user-nav').firstDescendant().hide();
		
		gSLAuthProxy.Show();
		if(Subscriptions != 'undefined') {
			Subscriptions.getSubscriptions();
		}
      }
      else {
         gSLAuthProxy.ShowLogin();
      }
    },

	/**
     * Log out the current user
     */
    logoutUser: function(username) {
      if (FoxUtils.getCookie('COOKIE_USERGUID') != '') {
    	  FoxUtils.clearCookie('COOKIE_USERGUID');
  	  }
  	  if (FoxUtils.getCookie('COOKIE_USERNAME') != '') {
		  FoxUtils.clearCookie('COOKIE_USERNAME');
  	  }
	  if (FoxUtils.getCookie('COOKIE_FIRSTNAME') != '') {
		  FoxUtils.clearCookie('COOKIE_FIRSTNAME');
  	  }

	  // change the state of the component
      self.exitState('loggedin');
    },

    /**
     * Enter a new state
     * @param {string} State name
     */
    enterState: function(state) {
      _baseElm.addClassName(state);
    },

    /**
     * Exit a state
     * @param {string} State name
     */
    exitState: function(state) {
      _baseElm.removeClassName(state);
    },

	checkSession: function() {
		var cookieId = FoxUtils.getCookie('COOKIE_USERGUID');
		if (cookieId == '') {
			return false;
		}

		var postData = {
			module: 'profiles',
			action: 'check_session_exists',
			callmethod: 'GetProfileInfo',
			snap_request: 'post'
		};

		var request = new Ajax.Request('/controller.php',
									   {method:'post',
										parameters: postData,
										onSuccess: self.handleCheckSessionResponse,
										onFailure: self.handleResponseError
									   });

	},

	handleCheckSessionResponse: function(transport) {
		var response = transport.responseText;
	},

	/**
     * Handle a click on the 'forgot password' link
     * @param {DOMevent} event
     */
    handleForgotPasswordClick: function(event) {
      event.stop();

		var form = _signInForm;
		var formValues = self.getSignInFormValues();

		$('password_signin').removeClassName('invalid');
		$('ghostvalues_placeholder_password_signin').removeClassName('invalid');

		if (!formValues.username) {
			form.addClassName('invalid');
			$('username_signin').addClassName('invalid');
			form.select('p.validation_error')[0].innerHTML = 'Please enter your username and click the link again. Your password will be emailed to you.';
			return;
		}

		// send the password request
		form.removeClassName('invalid');
		$('username_signin').removeClassName('invalid');

		var postData = {
			module: 'profiles',
			action: 'call_snap',
			callmethod: 'SendPassword',
			snap_request: 'post',
			return_function: 'callback_forgot_password_ajax',
			UsernameOrEmail: formValues.username
		};

		var request = new Ajax.Request('/controller.php',
									   {method:'post',
										parameters: postData,
										onSuccess: self.handleForgotPasswordResponse,
										onFailure: self.handleResponseError
									   });
    },

	handleForgotPasswordResponse: function(transport) {
		var response = transport.responseText;

		var parts = response.split(';');
		if (parts.length > 1 && parts[0] == 'Success') {
			alert(parts[1]);
			FloatingPanels.close($('signin_panel'));
		}
		else {
			var form = _signInForm;
			form.addClassName('invalid');
			$('username_signin').addClassName('invalid');
			form.select('p.validation_error')[0].innerHTML = response;
			return;
		}
	},

	/**
     * Handle a click on the sign out link
     * @param {DOMevent} event
     */
    handleSignOutClick: function(event) {
      event.stop();
	  self.doSignOut();
    },

	/**
	 * Sign out the current user
	 */
	doSignOut: function() {
		var postData = {
			module: 'profiles',
			action: 'signout'
		};

		var request = new Ajax.Request('/controller.php',
									   {method:'post',
										parameters: postData,
										onSuccess: self.handleSignOutResponse,
										onFailure: self.handleResponseError
									   });
	},

	handleSignOutResponse: function(transport) {
		var response = transport.responseText;

		if (response.indexOf('success') != -1) {
			// exit logged-in state
			self.logoutUser();
		}
		else {
			alert(response);
		}
	},

	handleResponseError: function(transport) {
		alert("There is a problem connecting to the server.\nPlease try again later");
	},

    /**
     * Handle a click on the sign in link
     * @param {DOMevent} event
     */
    handleSignInClick: function(event) {
		event.stop();
		Dimmer.locked = true;
		GlobalNav.close();
		self.doSignInClick();
		Dimmer.locked = false;
    },

	/**
	 * Action for clicking Sign In link
	 */
	doSignInClick: function() {
		FloatingPanels.close($('signup_panel'));
		FloatingPanels.open($('signin_panel'),
			  {
/*
			  oncreate: function(baseElm){
			     baseElm.select('input').each(GhostValues.initialize);
				 baseElm.select('a[href$=#forgotpassword]')[0].observe('click', self.handleForgotPasswordClick);
				   _signInForm = $('signinform');
				   _signInForm.observe('submit', function(event) {
										 event.stop();

										 if(!self.validateSignIn()) {
											 return;
										 }
										 self.doSignIn();
					   });
			   },
*/
			   onopen: function() {
				   if (typeof hidePlayer=="function") {
					hidePlayer();
				   }
				   if ($('carousel')) {
					   Carousel.pause();
				   }
				   Dimmer.show();
				   Dimmer.onClick = function(event) {
					   FloatingPanels.close($('signin_panel'));
				   };
			   },
			   onclose: function() {
				  if (typeof showPlayer=="function") {
					showPlayer();
				   }
				   if ($('carousel')) {
						Carousel.resume();
					}
				   Dimmer.onClick = function() { };
			   }});
	},

    /**
     * Handle a click on the sign up link
     * @param {DOMevent} event
     */
    handleSignUpClick: function(event) {
		event.stop();
		self.doSignUpClick();
    },

	doSignUpClick: function() {
		Dimmer.locked = true;
		GlobalNav.close();
		Dimmer.locked = false;
	  FloatingPanels.close($('signin_panel'));
      FloatingPanels.open($('signup_panel'),
			  {
			  oncreate: function(baseElm){
				 baseElm.select('a.popuplink').each(NewWindow.initialize);
				 baseElm.select('input').each(GhostValues.initialize);
				 baseElm.select('.already_member a')[0]
					   .observe('click', function(event) {
						   event.stop();
						   Dimmer.locked = true;
						   self.doSignInClick();
						   Dimmer.locked = false;
					   });
				 baseElm.select('.actions a')[0]
					   .observe('click', function(event) {
						   event.stop();
						   FloatingPanels.close($('signup_panel'));
					   });

				 _signUpForm = $('signupform');
				 _signUpForm.observe('submit', function(event) {
										 event.stop();
										 if(!self.validateSignUp()) {
											 return;
										 }
										 self.doSignUp();
									 });

				 },
			   onopen: function() {
				   if (typeof hidePlayer=="function") {
					hidePlayer();
				   }
				   if ($('carousel')) {
				   	Carousel.pause();
				   }
				   Dimmer.show();
				   Dimmer.onClick = function(event) {
					   FloatingPanels.close($('signup_panel'));
				   };
			   },
			   onclose: function() {
				   if (typeof showPlayer=="function") {
					showPlayer();
				   }
				   if ($('carousel')) {
				  	 Carousel.resume();
				   }
				   Dimmer.onClick = function() { };
			   }});
	},

	/**
	 * Send the form to sign up
	 */
	doSignUp: function() {
		var formValues = self.getSignUpFormValues();
		var postData = {
			module: 'profiles',
//			action: 'call_snap',
			action: 'slauthsignup',
			callmethod: 'Signup',
			snap_request: 'post',
			return_function: 'callback_signup_ajax',
//			FirstName: formValues.firstname,
//			LastName: formValues.lastname,
			Username: formValues.username,
			Password: formValues.regpassword,
			Email: formValues.regemail,
			DOB: [formValues.dobmonth, formValues.dobday, formValues.dobyear].join('/'),
			Gender: formValues.gender,
//			ZipCode: formValues.zipcode,
			Country: 'USA'
		};

		_signUpForm.select('a.cancel-button')[0].hide();
		_signUpForm.select('.actions input')[0].hide();

		var errMsg = new Element('p',{'class':'loadingMsg'})
								.setStyle({'color': '#900'})
								.update('Submitting your details...');

		_signUpForm.select('.actions')[0].appendChild(errMsg);

		var request = new Ajax.Request('/controller.php',
									   {method:'post',
										parameters: postData,
										onSuccess: self.handleSignUpResponse,
										onFailure: self.handleResponseError,
										onComplete: function() {
											_signUpForm.select('a.cancel-button')[0].show();
											_signUpForm.select('.actions input')[0].show();
											_signUpForm.select('.loadingMsg')[0].remove();
}});
	},

	handleSignUpResponse: function(transport) {
		var response = transport.responseText;

		if (response.indexOf('Success') != -1) {
			self.doSignUpSuccess(response);
		}
		else {
			self.doSignUpFailure(response);
		}
	},

	doSignUpSuccess: function(responseText) {
		var response = responseText.split(';');
		var username = response[1] || self.getSignUpFormValues().username;

		// enter logged-in state
		self.loginUser(username);

		// close the panel
		FloatingPanels.close($('signup_panel'));

		// reset the form
		_signUpForm.reset();

		// clear the form error state and messages
		$('signup_panel').select('.invalid').each(function(elm) {
			elm.removeClassName('invalid');
		});

		// show the player (??)
		if (typeof showPlayer == 'function') {
			showPlayer();
		}
	},

	  doSignUpFailure: function(responseText) {
		  var form = _signUpForm;

		  // highlight invalid fields
		  if (responseText.indexOf('Username') != -1) {
			  form.select('input[name="username"]')[0].addClassName('invalid');
		  }
		  else if (responseText.indexOf('Email Address') != -1) {
			  form.select('input[name="regemail"]')[0].addClassName('invalid');
		  }

		  // enter the invalid state
		  form.select('p.validation_error')[0].innerHTML = responseText;
		  form.addClassName('invalid');
	  },

	/**
	 * Get the values of the signup form
	 * @return {hash}
	 */
	getSignUpFormValues: function() {
		var form = _signUpForm;
		var formValues = form.serialize(true);
		var elm;
		for (var key in formValues) {
			elm = form.select('input[name="'+key+'"]')[0];

			// clear ghosted fields
			if (elm && elm.hasClassName('ghosted')) {
				formValues[key] = '';
			}
		}

		// finally, grab the value for any radio buttons
		formValues.gender = form.select('input:checked[name="gender"]').pluck('value')[0];

		return formValues;
	},

    /**
     * Validate Sign Up forms
	 * @return boolean
     */
    validateSignUp: function() {
		var formValues = self.getSignUpFormValues();
		var validateState = true;
		var errorMessages = [];
		var form = _signUpForm;
		var requiredTextField = $w("username regpassword regpasswordconfirm");
		var currentDate = new Date();

		// not sure what this does, or where the cookie is set, but it was on v2.0
		// Looks like the presence of the cookie prevents you from registering
		var block_registration = FoxUtils.getCookie('COOKIE_BLOCK_REG');
		if(block_registration == "true") {
			form.select('p.validation_error')[0].innerHTML = 'Sorry. We are unable to process your registration at this time.';
			form.addClassName('invalid');
			return false;
		}

		var fieldName;
		var elm;
	  for(i=0;i<requiredTextField.length;i++){
		  fieldName = requiredTextField[i];
		  elm = $(form[fieldName]);

	    // reset invalid state
	    elm.removeClassName('invalid');

		// Check the empty fields for required field
	    if(formValues[fieldName] == ''){
		   if(elm.readAttribute('type') == 'text'){
		      elm.addClassName('invalid');
		   }
		   else if(elm.readAttribute('type') == 'regpassword'){
		      // set invalid state for ghostedfield
			 // $("ghostvalues_placeholder_" + $(form[requiredTextField[i]]).readAttribute('id')).addClassName('invalid');
			   elm.addClassName('invalid');
		   }

			errorMessages.push(fieldName+' cannot be empty');
			validateState = false;
		}
	    // reset invalid state when user changes the textfield
	    elm.observe('change', function(event){
						var elm = event.element();
						if (elm.value != '') {
							elm.removeClassName('invalid');
						}
					});
	  }

	  // Check Password (if its not empty)
	  if(!$(form['regpassword']).hasClassName('invalid')){
		  if($F(form['regpassword']).length < 6){
			$(form['regpassword']).addClassName('invalid');
			$(form['regpasswordconfirm']).addClassName('invalid');
			  errorMessages.push('Password must be more than 6 characters');
			  validateState = false;
		  }
		  else if($F(form['regpassword']) != $F(form['regpasswordconfirm'])){
			$(form['regpasswordconfirm']).addClassName('invalid');
			errorMessages.push('Password and Confirm Password must match exactly');
			validateState = false;
		  }
	  }

	  // check email validity e.g a@b.com
	  var isValidEmail = /^([a-zA-Z0-9_\.\-])+\@(([a-zA-Z0-9\-])+\.)+([a-zA-Z0-9]{2,4})+$/;
	  if(!isValidEmail.test($F(form['regemail']))){
	  	 $(form['regemail']).addClassName('invalid');
		  errorMessages.push('Email Address is invalid');
		 validateState = false;
	  }

	  // check date only accept number
	  var isNumber = /^[0-9]+$/;
	  if(!isNumber.test(formValues.dobday)){
	  	 $(form['dobday']).addClassName('invalid');
		  errorMessages.push('Day of birth must be a number');
		 validateState = false;
	  }
	  if(!isNumber.test(formValues.dobmonth)) {
	     $(form['dobmonth']).addClassName('invalid');
		  errorMessages.push('Month of birth must be a number');
		 validateState = false;
	  }
	  if(!isNumber.test(formValues.dobyear)) {
		$(form['dobyear']).addClassName('invalid');
		errorMessages.push('Year of birth must be a number');
		validateState = false;
	  } 
          if (validateState && (formValues.dobyear < 1900 || formValues.dobyear > currentDate.getFullYear())) {
		$(form['dobyear']).addClassName('invalid');
		errorMessages.push('Please enter a valid year');
		validateState = false;
          }
	  if (validateState && (formValues.dobmonth < 1 || formValues.dobmonth > 12)) {
		$(form['dobmonth']).addClassName('invalid');
		errorMessages.push('Please enter a valid month');
		validateState = false;
	  }
          if (validateState && (formValues.dobday < 1 || formValues.dobday > [31,(formValues.dobyear%4==0?29:28),31,30,31,30,31,31,30,31,30,31][formValues.dobmonth-1])) {
		$(form['dobday']).addClassName('invalid');
		errorMessages.push('Please enter a valid day');
		validateState = false;
	  }
	  if(validateState == false) {
		  form.select('p.validation_error')[0].innerHTML = 'Some fields are invalid, please correct them and click Submit again: <br /><br />' + errorMessages.join('<br />');
		  form.addClassName('invalid');
	  }
	  else {
		form.removeClassName('invalid');
	  }

	  return validateState;
    },

	/**
	 * Send the form to sign in
	 */
	//doSignIn: function() {
	doSignIn: function(email,password) {
		//var formValues = self.getSignInFormValues();
		var postData = {
			module: 'profiles',
//			action: 'call_snap',
			action: 'slauthsignin',
			callmethod: 'Signin',
			snap_request: 'post',
			return_function: 'callback_signin_ajax',
//			Username: formValues.username,
//			Password: formValues.password
			Username: email,
			Password: password
		};

//		_signInForm.addClassName('submitting');

		var request = new Ajax.Request('/controller.php',
									   {method:'post',
										parameters: postData,
										onSuccess: self.handleSignInResponse,
										onFailure: self.handleResponseError,
										onComplete: function() {
//											_signInForm.removeClassName('submitting');
										}});
	},

	handleSignInResponse: function(transport) {
		var response = transport.responseText;

		if (response.indexOf('Success') != -1) {
			self.doSignInSuccess(response);
		}
		else {
			self.doSignInFailure(response);
		}
	},

	doSignInSuccess: function(responseText) {
		var response = responseText.split(';');
		var username = response[1];

		// enter logged-in state
		self.loginUser(username);

		// close the panel
		FloatingPanels.close($('signin_panel'));
/*
		// reset the form
		_signInForm.reset();

		// clear the form error state and messages
		$('signup_panel').select('.invalid').each(function(elm) {
													  elm.removeClassName('invalid');
												  });
*/
		// show the player (??)
		if (typeof showPlayer == 'function') {
			showPlayer();
		}
	},

	  doSignInFailure: function(responseText) {
/*
		  var form = _signInForm;

		  // highlight invalid fields
		  if (responseText.indexOf('Username') != -1) {
			  form.select('input[name="username"]')[0].addClassName('invalid');
			  form.select('p.validation_error')[0].innerHTML = responseText;
		  }
		  else {
			  form.select('input[name="username"]')[0].addClassName('invalid');
			  form.select('input[name="password"]')[0].addClassName('invalid');
		  }

		  // enter the invalid state
		  form.addClassName('invalid');
*/
		eval(responseText); // execute javascript slauth returns
	  },

	/**
	 * Get the values of the signup form
	 * @return {hash}
	 */
	getSignInFormValues: function() {
		var form = _signInForm;
		var formValues = form.serialize(true);
		var elm;
		for (var key in formValues) {
			elm = form.select('input[name="'+key+'"]')[0];

			// clear ghosted fields
			if (elm.hasClassName('ghosted')) {
				formValues[key] = '';
			}
		}

		return formValues;
	},

    /**
     * Validate Sign In forms
	 * @return boolean
     */
    validateSignIn: function() {
      var validateState = true;
		var formValues = self.getSignInFormValues();
		var form = _signInForm;

	  if(formValues.username == ''){
		 $('username_signin').addClassName('invalid');
     	 validateState = false;
	  }
	  // reset invalid state when user focus on the textfield
	  $('username_signin')
	   .observe('focus', function(event){
			event.stop();
			$(this).removeClassName('invalid');
		});

	  if(formValues.password == ''){
		  if ($("ghostvalues_placeholder_password_signin")) {
			  $("ghostvalues_placeholder_password_signin").addClassName('invalid');
		  }
		  else {
			  $("password_signin").addClassName('invalid');
		  }
     	 validateState = false;
	  }

		// vaild, so remove all error messages and markers
	  if(validateState) {
		  form.select('.invalid').each(function(elm) {
										   elm.removeClassName('invalid');
									   });
		  form.removeClassName('invalid');
	  }
		// invalid
	  else {
		  form.select('p.validation_error')[0].innerHTML = 'Invalid ID or Password. Please Try Again.';
		  form.addClassName('invalid');
	  }

	  return validateState;
	}
  };

  return self;
}();

/* Pluck */
Event.observe(window,'load',function() {
/*
	$$('td.Table_FloatLoginLeft a').each(function() {
		Event.observe(this,'click',function(e) {
			if(Event.element(e).innerHTML == "Register") {
				Event.stop(e);
				ActiveUser.doSignUpClick();
			}
		});
	});
*/
	Event.observe(document, 'keypress', function(e){
		var elem = Event.element(e).identify()
		if(elem == "plckEmail" || elem == "password") {
			var code;
			if (!e) var e = window.event;
			if (e.keyCode) code = e.keyCode;
			else if (e.which) code = e.which;
			if(code == 13) {
				Event.stop(e);
				gSLAuthProxy.SubmitLoginForm(document.getElementById('slaLoginForm'));
			}
		}
	});
});

/** SOURCE:  _ui/js/carousel.js  **/

/**
 * @carousel Handles behaviour for the Flash Carousel
 * @author Dave Rosen X-Team.com dave@xhtmlized.com
 * @version 0.1
 * @requires prototype.1.6.0.2
 * @requires SWFObject
 * @requires Clock
 */

/**
 * Carousel: Handles behaviour for the Flash carousel
 * @namespace
 */
var Carousel = function(){
	/* global document, window, Clock, Options */

	var _holderID = "carousel";
	var _movieID = "carousel_embed";
	var _movie = null;
	var _options = {};
	var _isPaused = false;
	var _defaultOptions = {
		home: {
			xmlPath: '_xml/carousel/home/',
			swfPath: '_ui/flash/carousel/CarouselPreloader.swf',
			imagePath: ''
		},
		fod: {
			xmlPath: '/_xml/carousel/fod/',
			swfPath: '/_ui/flash/carousel/CarouselPreloader.swf',
			imagePath: ''
		}
	};

	var self = /** @scope Carousel */{
		/**
	     * Initialize the Carousel component
	     * @param {DOMid} Id of the flash holder element
		 * @param {string} page Name of the page
	     * @param {hash} options
		 * @config {path} xmlPath Path to xml files
		 * @config {path} imagePath Path to images
		 * @config {path} swfPath Path to swf files
	     */
	    initialize: function(holderID, page, options) {
			_holderID = holderID;
			_movieID = holderID+"_embed";
			var defaultOptions = _defaultOptions[page] || _defaultOptions.home;
			_options = Options.merge(defaultOptions, options);
	  		self.embedFlashCarousel();
		},

		/**
		 * Get the path of the xml file
		 * @return {path}
		 */
		getXmlPath: function() {
			return _options.xmlPath + 'carousel_'+Clock.format('Y-m-d')+'.xml'+'?t='+(new Date()).getTime();
		},

		/**
		 * Embed Carousel
		 */
		embedFlashCarousel: function() {
			// return if flash player already embedded
			var so = new SWFObject(_options.swfPath, _movieID, '967', '252', '9', '#656565');
			so.addParam('allowScriptAccess', 'always');	// (REQUIRED)
			so.addParam('wmode', 'transparent');
			so.addParam('scale', 'noscale');
			so.addParam('align', 'TOP');

			so.addVariable('xmlPath', self.getXmlPath());	// Path to the XML
			so.addVariable('imagePath', _options.imagePath); // Path to the images

			so.write(_holderID);
		},

		/**
		 * Embed Carousel
		 */
		getMovie: function() {
			// lazy-load the flash object reference
			if (!_movie) {
				// resolve the string _movieId to a Flash object reference based on browser type.
				_movie = (!!document[_movieID])
					? document[_movieID]
					: window[_movieID];
			}
			return _movie;
		},

		/**
		 * Enable Carousel
		*/
		pause: function() {
			var movie = self.getMovie();

			if (movie && movie.sendToFlash && !_isPaused) {
				_isPaused = true;
				try {
					movie.sendToFlash('disable');
				}
				catch(e) {

				}
			}
		},

		/**
		 * Disable Carousel
		*/
		resume: function() {
			var movie = self.getMovie();

			if (movie && movie.sendToFlash && _isPaused) {
				_isPaused = false;
				try {
					movie.sendToFlash('enable');
				}
				catch(e) {

				}
			}
		},

		/**
		 * Resize the carousel
		 * @param {int} height, in pixels
		 */
		setCarouselHeight: function(height) {
			self.getMovie().height = height;
			$('carousel').setStyle({height: height+'px'});
		}

	};
	return self;
}();

/** SOURCE:  _ui/js/episodeinfo.js  **/

/**
 * @fileoverview Handles behaviour for the "episode info" panels on the video page
 * @author Josh Johnston josh@xhtmlized.com
 * @version 0.1
 * @requires prototype.1.6.0.2
 * @requires PagedSets
 */

/**
 * EpisodeInfo: Handles behaviour for the "episode" panels
 * @namespace
 */
var EpisodeInfo = function(){
	/*global $, PagedSets */

	var _instance;
	var self = /** @scope EpisodeInfo */{
		/**
		 * Initialize the ShowPromos component
		 * @param {DOMelement} Base element
		 */
		initialize: function(baseElm) {
			_instance = PagedSets.create(baseElm, {
											 selectors: {set: '.show-listings'}
										 });
		}
	};

	return self;
}();

/** SOURCE:  _ui/js/floatingpanels.js  **/

/**
 * @fileoverview Graphics and behaviour for floating panels
 * @author Josh Johnston josh@xhtmlized.com
 * @version 0.1
 * @requires prototype.1.6.0.2
 * @requires Overlays
 */

/**
 * When you apply FloatingPanels behaviour to a div, it is given
 * the "floatingpanel" class, and it's innerHTML changes to the
 * following:
 <div class="floatingpanel">
 <a href="#" class="close">close</a>
 <span class="a"></span>
 <span class="r"></span>
 <span class="b"></span>
 <span class="rb"></span>
 original content goes here...
 </div>
 */

/**
 * FloatingPanels: Graphics and behaviour for floating panels
 * @namespace
 */
var FloatingPanels = function(){
	/*global $, $$, Overlays, Dimmer */
	var _markerClass = 'dropdown';

	var _instanceOptions = {};

	var self = /** @scope FloatingPanels */{
		onOpen: function() { },
		onClose: function() { },

		isOpen: function(baseElm) {
			return baseElm.hasClassName('open');
		},

		/**
		 * Open a floatingpanel
		 * @param {DOMelement} baseElm
		 * @param {hash} options
		 */
		open: function(baseElm, options) {
			var id = baseElm.identify();
			_instanceOptions[id] = options;

			// create a new floating panel if necessary
			if (!baseElm.hasClassName(_markerClass)) {
				self.create(baseElm, options);
			}

			if (options && options.bringToFront !== false) {
				Overlays.bringToFront(baseElm);
			}
			baseElm.addClassName('open');

			var onOpen = _instanceOptions[id].onopen;
			if (onOpen) {
				onOpen(baseElm);
			}
		},

		/**
		 * Close a floatingpanel
		 * @param {DOMelement} baseElm
		 */
		close: function(baseElm) {
			baseElm.removeClassName('open');
			// clear styles
			baseElm.setAttribute('style', '');

			var onClose = self.getInstanceOption(baseElm, 'onclose');
			if (onClose) {
				onClose(baseElm);
			}

			if(!Dimmer.locked) {
				Dimmer.hide();
			}
		},

		/**
		 * Create a FloatingPanel component
		 * @param {DOMelement} baseElm Base element for the floating panel content
		 * @param {hash} options
		 * @config {function} oncreate Callback for post-create
		 */
		create: function(baseElm, options) {
			self.applyWrapperHtml(baseElm);

			// bind event behaviour
			// close button
			baseElm.select('.close')[0].observe('click', self.handleCloseClick);

			// post-create callback
			if (options && typeof options.oncreate == 'function') {
				options.oncreate(baseElm);
			}
		},

		/**
		 * Wrap the base element in the floatingpanel html structure
		 * @param {DOMelement} Base element
		 */
		applyWrapperHtml: function(baseElm) {
			baseElm.addClassName(_markerClass);
			baseElm.innerHTML = '<a href="#" class="close">close</a><span class="a"></span><span class="r"></span><span class="b"></span><span class="rb"></span>'+baseElm.innerHTML;
		},

		/**
		 * Handle a click on the close button
		 * @param {DOMevent} event
		 */
		handleCloseClick: function(event) {
			event.stop();
			self.close(event.element().parentNode);
		},

		getInstanceOption: function(baseElm, key) {
			var id = baseElm.identify();
			return _instanceOptions[id] ? _instanceOptions[id][key] : null;
		}
	};

	return self;
}();

/** SOURCE:  _ui/js/fod.js  **/

/**
 * @fileoverview Behaviour for the entire fod page
 * @author Josh Johnston josh@xhtmlized.com
 * @version 0.1
 */

/**
 * Fod: Behaviour for the entire fod page
 * @namespace
 */
var Fod = function(){
	/*global $, FodPlayer, UserReviews, HashHelper */

	var _content = {
		episodeSummary: null,
		officialUpdate: null,
		episodeReviews: null
	};

	var self = /** @scope Fod */{
		/**
		 * Initialize the Fod module
		 */
		initialize: function() {
			// startup sub-modules
			//FodPlayer.initialize($('show-player'));
			/*
			if ($('show-episode-reviews')) {
				UserReviews.initialize($('show-episode-reviews'), 'rate-review-panel', 'report-panel');
			}


			// startup dynamic content sections
			_content.episodeSummary = LiveContent.create($('livecontent-episode-summary'),
													  {getCacheKey: self.getEpisodeCacheKey});

			_content.officialUpdate = LiveContent.create($('livecontent-show-official-update'),
													  {getCacheKey: self.getShowCacheKey});

			_content.episodeReviews = LiveContent.create($('livecontent-episode-reviews'),
													  {getCacheKey: self.getEpisodeCacheKey});

			// refresh livecontent regions
			LiveContent.refresh(_content.episodeSummary);
			LiveContent.refresh(_content.officialUpdate);
			LiveContent.refresh(_content.episodeReviews);
			 */
		},

		/**
		 * Get a cache key for the current show
		 */
		getShowCacheKey: function() {
			return {sh: HashHelper.getValue('sh')};
		},

		/**
		 * Get a cache key for the current episode
		 */
		getEpisodeCacheKey: function() {
			return {sh: HashHelper.getValue('sh'), ep: HashHelper.getValue('ep')};
		}
	};

	return self;
}();

/** SOURCE:  _ui/js/fodplayer.js  **/

/**
 * @fileoverview Behaviour for the player on the fod page
 * @author Josh Johnston josh@xhtmlized.com
 * @version 0.1
 */

/**
 * FodPlayer: Behaviour for the player on the fod page
 * @namespace
 */
var FodPlayer = function(){
	/*global $, Dimmer, FloatingPanels */

	var _baseId;
	var _panelIds = {
		player: 'video-player',
		share: 'share',
		info: 'info',
		rate: 'rate',
		commercial: 'commercial',
		installationguide: 'installation-guide'
	};

	var self = /** @scope FodPlayer */{
		/**
		 * Initialize the FodPlayer component
		 * @param {DOMelement} baseElm Base element
		 */
		initialize: function(baseElm) {
			_baseId = baseElm.identify();

			// handle a click on the 'install' button
			$(_panelIds.player).select('.install-player')[0]
				.observe('click', function(event) {
							 event.stop();
							 self.showInstallationGuide();
						 });

			// handle a click on the 'rate & review' button
			$('show-summary').observe('click', function(event) {
										  var target = event.element();
										  var targetPath = target.ancestors();

										  // handle clicks on the rate & review link
										  var rateLinkElm = $('show-summary').select('.rate-review')[0];
										  if (target == rateLinkElm || targetPath.indexOf(rateLinkElm) != -1) {
											  event.stop();
											  self.showRateReviewPanel();
										  }
									  });
		},

		/**
		 * Show the installation guide popup
		 */
		showInstallationGuide: function() {
			FloatingPanels.open($(_panelIds.installationguide));
			Dimmer.show();

			// handle a click on the installation guide 'close' button
			$(_panelIds.installationguide).select('.close')[0]
				.observe('click', function(event) {
							 event.stop();
							 self.hideInstallationGuide();
						 });
		},

		/**
		 * Hide the installation guide popup
		 */
		hideInstallationGuide: function() {
			FloatingPanels.close($(_panelIds.installationguide));
			Dimmer.hide();
		},

		/**
		 * Show the Rate & Review panel
		 */
		showRateReviewPanel: function() {
			FloatingPanels.open($(_panelIds.rate));
			$(_panelIds.rate).select('.cancel-button')[0]
				.observe('click', function(event) {
							 event.stop();
							 FloatingPanels.close($(_panelIds.rate));
						 });
		},

		/**
		 * Hide the Rate & Review panel
		 */
		hideRateReviewPanel: function() {
			FloatingPanels.close($(_panelIds.rate));
		}
	};

	return self;
}();

/** SOURCE:  _ui/js/foxmenus.js  **/

/**
 * @fileoverview Handles a generic fox dropdown menu
 * @author Josh Johnston josh@xhtmlized.com
 * @version 0.1
 * @requires prototype.1.6.0.2
 */

/**
 * FoxMenus: handles a generic fox menu
 * @namespace
 */
var FoxMenus = {
	classNames: {
		base: 'foxmenu',
		menuopen: 'foxmenu-open',
		selected: 'selected'
	},

	create: function(baseElm) {
		return FoxMenuInstance(baseElm);
	},

	/**
	 * Extract the value from an element
	 * @param {DOMelement} elm
	 * @return {string}
	 */
	extractValue: function(elm) {
		var matches = elm.readAttribute('href').match(/\#\w+?\:([\w\-]+)/);
		return (matches && matches.length == 2)
			? matches[1]:
			'';
	}
};

/**
 * FoxMenus: handles a generic fox menu
 * @namespace
 */
var FoxMenuInstance = function(_baseElm){
	var _static = FoxMenus;
	var self = /** @scope FoxMenuInstance */{
		onChange: null,

		initNav: function() {
			// click behaviour dropdown menus
			_baseElm.observe('click', self.handleClick);

			// initialize dropdown rollovers
			_baseElm.observe('mouseover', self.handleMouseOver)
				.observe('mouseout', self.handleMouseOut);
		},

		/**
		 * Handle a click on the menu
		 * @param {DOMevent} event
		 */
		handleClick: function(event) {
			var elm = event.element();
			var value;

			event.stop();

			// only act on link clicks
			if (!elm.match('.foxmenu li a')) {
				return;
			}

			value = _static.extractValue(elm);

			// set the selection
			self.setSelectedValue(value);

			// update the text
			self.setTitleValue(value);

			if (typeof self.onChange == 'function') {
				self.onChange(value);
			}

			// close the menu
			self.close();
		},

		/**
		 * Handle a mouseover on the menu
		 * @param {DOMevent} event
		 */
		handleMouseOver: function(event) {
			_baseElm.addClassName(_static.classNames.menuopen);
		},

		/**
		 * Handle a mouseout on the menu
		 * @param {DOMevent} event
		 */
		handleMouseOut: function(event) {
			var target = event.relatedTarget || event.toElement;
			if (target && target.descendantOf(_baseElm)) {
				return false;
			}
			else {
				self.close();
			}
		},

		/**
		 * Close the menu
		 */
		close: function() {
			_baseElm.removeClassName(_static.classNames.menuopen);
		},

		/**
		 * Set the title value
		 * @param {string} value
		 */
		setTitleValue: function(value) {
			_baseElm.firstDescendant().select('.value')[0].innerHTML = value;
		},

		/**
		 * Get the currently selected value
		 */
		getSelectedValue: function() {
			var elm = _baseElm.select('ul a.'+_static.classNames.selected)[0];
			return elm ? _static.extractValue(elm) : '';
		},

		/**
		 * Set the selection marker
		 * @param {string} value
		 */
		setSelectedValue: function(value) {
			// de-select the old item
			_baseElm.select('ul a')
				.each(function(elm){
						  if (_static.extractValue(elm) == value) {
							  elm.addClassName(_static.classNames.selected);
						  }
						  else {
							  elm.removeClassName(_static.classNames.selected);
						  }
					  });
		}
	};

	self.initNav();
	return self;
};


/** SOURCE:  _ui/js/globalnav.js  **/

/**
 * @fileoverview Handles behaviour for the global navigation
 * @author Josh Johnston josh@xhtmlized.com
 * @version 0.1
 * @requires prototype.1.6.0.2
 * @requires FloatingPanels
 */

/**
 * GlobalNav: Handles behaviour for the global navigation
 * @namespace
 */
var GlobalNav = function(){
	/*global $, FloatingPanels */

	var _baseElm;
	var _watchFullEpisodesPanel;

	var self = /** @scope GlobalNav */{
		/**
		 * Initialize the GlobalNav component
		 * @param {DOMelement} Base element for the global nav
		 */
		initialize: function(baseElm) {
			_baseElm = baseElm;
			_watchFullEpisodesPanel = $('full-episodes-nav');

			// bind event callbacks
			_baseElm.select('a.full-episodes')[0]
				.observe('click', function(event) {
							 event.stop();

							 // close the panel, if already open
							 if (FloatingPanels.isOpen(_watchFullEpisodesPanel)) {
								 FloatingPanels.close(_watchFullEpisodesPanel);
							 }
							 // otherwise open it
							 else {
								 self.doWatchFullEpisodesOpen();
							 }
						 });
		},

		/**
		 * Action for opening the nav popup
		 */
		doWatchFullEpisodesOpen: function(){
			if (typeof hidePlayer=="function") {
				hidePlayer();
			}
			if ($('carousel')) {
				Carousel.pause();
			}
			FloatingPanels.open(_watchFullEpisodesPanel,
								{bringToFront: true,
								 onclose: function(panelElm) {
									 var activeElm = _baseElm.select('li.active');
									 if (typeof showPlayer=="function") {
										showPlayer();
									 }
									 if ($('carousel')) {
									 	Carousel.resume();
									 }
									 if (activeElm[0]) {
											 activeElm[0].removeClassName('active');
									 }
									 self.stopWatchingBodyClicks();
								 }});

			// add the 'active' class to the li
			self.startWatchingBodyClicks();
			_baseElm.select('li')[0].addClassName('active');
		},

		close: function() {
			FloatingPanels.close(_watchFullEpisodesPanel);
			$$('body')[0].stopObserving('click', self.handleBodyClick);
		},

		/**
		 * Handle a click on the body
		 */
		handleBodyClick: function(event) {
			var target = event.element();
			if (target != _watchFullEpisodesPanel && !target.up('#full-episodes-nav')) {
				self.close();
			}
		},

		handleCarouselClick: function(event) {
			FloatingPanels.close(_watchFullEpisodesPanel);
		},

		startWatchingBodyClicks: function() {
			$$('html')[0].observe('click', self.handleBodyClick);

			var carousel = $('carousel-embed');
			if (carousel) {
				$('carousel-embed').observe('click', self.handleCarouselClick);
			}
		},

		stopWatchingBodyClicks: function() {
			$$('html')[0].stopObserving('click', self.handleBodyClick);

			var carousel = $('carousel-embed');
			if (carousel) {
				carousel.stopObserving('click', self.handleCarouselClick);
			}
		}
	};

	return self;
}();

/** SOURCE:  _ui/js/livecontent.js  **/

/**
 * @fileoverview Turn links into ajax-updated regions
 * @author Josh Johnston josh@xhtmlized.com
 * @version 0.1
 */

/**
 * LiveContent: Turn links into ajax-updated regions
 * @namespace
 */
var LiveContent = function(){
	/*global alert, $, Element, CacheLoader */

	var self = /** @scope LiveContent */{
		/**
		 * Create a LiveContent instance
		 * @param {DOMelement} baseElm
		 * @param {hash} options
		 * @return {object} instance
		 */
		create: function(baseElm, options) {
			options = options || {};
			var instance = {
				baseId: baseElm.identify(),
				holderId: null,
				cacheId: null,
				callbacks: {
					getCacheKey: null
				}
			};

			// marker class
			baseElm.addClassName('livecontent');

			// extract the cache id
			instance.cacheId = options.cacheId || instance.baseId.replace(/^livecontent\-/, '');

			// set the holder id
			instance.holderId = options.holderId || instance.baseId + "-holder";

			// assign callbacks
			instance.callbacks.getCacheKey = options.getCacheKey || null;

			return instance;
		},

		/**
		 * Refresh the content
		 * @param {object} instance
		 */
		refresh: function(instance) {
			CacheLoader.load(instance.cacheId, self.getCacheKey(instance),
							 {onSuccess: function(responseText) {
								  self.handleLoadSuccess(responseText, instance);
								  },
							  onFailure: function() {
								  self.handleLoadError(instance);
							  }});
		},

		/**
		 * Get the cache key for an instance
		 * @param {object} instance
		 * @return {hash} key
		 */
		getCacheKey: function(instance) {
			return (instance.callbacks.getCacheKey)
				? instance.callbacks.getCacheKey()
				: {};
		},

		/**
		 * Handle a loading error
		 * @param {object} instance
		 */
		handleLoadError: function(instance) {
			alert('error: '+instance.inspect());
		},

		/**
		 * Handle a successful load
		 * @param {JSONstring} responseText
		 * @param {object} instance
		 */
		handleLoadSuccess: function(responseText, instance) {
			var response = responseText.evalJSON();
			self.getContentHolder(instance).innerHTML = response.content;
		},

		/**
		 * Get the content holder elm
		 * @param {object} instance
		 * @return {DOMelement}
		 */
		getContentHolder: function(instance) {
			var holderElm = $(instance.holderId);
			return (holderElm) ? holderElm : self.createContentHolder(instance);
		},

		/**
		 * Create the content holder elm
		 * @param {object} instance
		 * @return {DOMelement}
		 */
		createContentHolder: function(instance) {
			var holderElm = new Element('div', {id: instance.holderId});
			$(instance.baseId).insert({before: holderElm});
			return holderElm;
		}
	};

	return self;
}();

/** SOURCE:  _ui/js/loader.js  **/

/**
 * @fileoverview Loading process for the page
 * @author Josh Johnston josh@xhtmlized.com
 * @version 0.1
 * @requires prototype.1.6.0.2
 */

/**
 * Loader: get the page ready for action
 * @namespace
 */
var Loader = function(){
	/*global $, $$, ActiveUser, CacheLoader, Carousel, Clock, EpisodeInfo, FloatingPanels, Fod, FoxMenus, FoxMenuInstance, GhostValues, GlobalNav, HashHelper, Overlays, Schedule, ScheduleTonight, ShowList, ShowPromos, Videos */
	var self = /** @scope Loader */{
		/**
		 * Initializes the page
		 */
		initialize: function(){
			self.initGlobalComponents();
			self.initComponents();
			self.initHeaderDate();

			// let every body know
			$$('body')[0].addClassName('jsloaded');
		},

		initializePlayPage: function() {
			self.initGlobalComponents();
			self.initHeaderDate();

			// make all elements classed 'dropdown' into a floating panel
			$$('.dropdown').each(FloatingPanels.create);

			// let every body know
			$$('body')[0].addClassName('jsloaded');
		},

		/**
		 * Work out what page we're on by looking at the body class
		 * @return {string}
		 */
		getPageName: function() {
			var bodyClasses = ['fod', 'home', 'shows', 'schedule', 'video'];
			var body = $$('body')[0];
			for (var i=0, len=bodyClasses.length; i<len; ++i) {
				if (body.hasClassName(bodyClasses[i])) {
					return bodyClasses[i];
				}
			}
			return false;
		},

		/**
		 * Sets the date on the header to the user's local time
		 */
		initHeaderDate: function(){
			// set the date on the header
			var headerElm = $('header');
			var valueElm = headerElm ? headerElm.select('.date .value')[0] : null;
			if (valueElm) {
				valueElm.innerHTML = Clock.format('l F jS');
			}
		},

		/**
		 * Hook up components with their base elements
		 */
		initGlobalComponents: function(){
			// global components
			ActiveUser.initialize($('user-nav'));
			Clock.initialize();
			GlobalNav.initialize($('nav'));
			HashHelper.initialize();
			Overlays.initialize();
		},


		initComponents: function() {
			// page-specific components
			if (typeof CacheLoader == 'object') {
				var cacheMode = 'offline';
				var cacheDir = (self.getPageName() == 'video' || self.getPageName() == 'fod')
					? '../_cache' : '_cache';
				CacheLoader.initialize({mode: cacheMode, cacheDir: cacheDir});
			}

			if (typeof Videos == 'object' && $('videos')) {
				Videos.initialize($('videos'));
			}

			if (typeof ShowPromos == 'object' && $('promo-mods')) {
				ShowPromos.initialize($('promo-mods'));
			}

			if (typeof EpisodeInfo == 'object') {
				$$('.pagescroller').each(EpisodeInfo.initialize);
			}

			if (typeof Fod == 'object' && self.getPageName() == 'video') {
				Fod.initialize();
			}

			if (typeof FoxMenus == 'object') {
				$$('.foxmenu').each(FoxMenus.create);

				var viewOptions = $$('.view-options')[0];
				if (typeof ShowList == 'object' && self.getPageName() == 'shows' && viewOptions) {
					var menus = viewOptions.childElements();
					ShowList.initialize($('shows-listing'),
										{viewmode: menus[0],
										 filter: menus[1]});
				}

				if (typeof Schedule == 'object' && self.getPageName() == 'schedule') {
					var menus = $$('.view-options')[0].childElements();
					Schedule.initialize($('schedule'), {
											week: $('week-options'),
											filter: menus[0],
											timezone: menus[1]
										});
				}
			}

			if (typeof ScheduleTonight == 'object' && $('schedule-tonight')) {
				ScheduleTonight.initialize($('schedule-tonight'));
			}

			if (typeof Carousel == 'object' && $('carousel')) {
				Carousel.initialize('carousel', self.getPageName());

				// watch for FloatingPanels events
				FloatingPanels.onOpen = Carousel.pause;
				FloatingPanels.onClose = Carousel.resume;
			}

			// swap ad placeholder with an iframe
			if (HashHelper.getValue('offline') != 1) {
				var page = self.getPageName();
				
				// ad zone override params
				var fullUrl = window.location.href;
				
				// default values
				var adZone = "fbc.fox/"+page;
				var tile = "1";
				var sz = "300x250";
				
				// only apply for non-production servers
				var hostName = window.location.hostname;
				if (hostName.match("www") == null && fullUrl.match("adoverride")) {
					var queryString = fullUrl.substring(fullUrl.indexOf('[')+1, fullUrl.indexOf("]"));
				
					AdOverride = new Array();
					var splitCommas = queryString.split(",");
					for(i=0;i<splitCommas.length;i++)
					{	
						splitColons = splitCommas[i].split(":");
						AdOverride[splitColons[0]] = splitColons[1];
					}	
				
					var adZone = AdOverride["zone"] != null ? AdOverride["zone"] : adZone;
					var tile = AdOverride["tile"] != null ? AdOverride["tile"] : tile;
					var sz = AdOverride["size"] != null ? AdOverride["size"] : sz;
				}
				
				var ord = Math.random()*10000000000000000;
				var adSource = "http://ad.doubleclick.net/adi/"+adZone+";tile="+tile+";sz="+sz+";ord="+ord;

				var placeholder = $$('.ad .placeholder')[0];
				if (placeholder) {
					placeholder.replace('<iframe src="' + adSource + '?" width="300" height="250" marginwidth="0" marginheight=" 0" hspace="0" vspace="0" frameborder="0" scrolling="no"></iframe>');
				}
			}

			// make all elements classed 'dropdown' into a floating panel
			$$('.dropdown').each(FloatingPanels.create);

			// make all popup links open a new window
			$$('a.popuplink').each(NewWindow.initialize);
		}

	};

	return self;
}();

/** SOURCE:  _ui/js/player.js  **/

/**
 * @fileoverview Flash video player
 * @author Josh Johnston josh@xhtmlized.com
 * @version 0.1
 * @requires prototype.1.6.0.2
 */

/**
 * Player: Flash video player
 * @namespace
 */
var Player = function(){
	/*global window, setShowTitle, $, SWFObject */
	var _instances = {};
	var _adsUrl = null; //"http://ad.doubleclick.net/adx/fbc.video/home;dcmt=xml/text;sz=320x240;";

	var _private;
	var self = {
		/**
		 * Create a new player instance
		 * @param {DOMid} baseId
		 * @return {hash} instance
		 */
		create: function(baseId, swfLocation) {
			// Set the player .SWF location
			swfLocation = swfLocation ?
				swfLocation :
				'_ui/flash/player/HybridPlayer.swf';

			var instance = {
				baseId: baseId,
				holderId: baseId + '-object',
				movieId: baseId + '-embed',
				swfLocation: swfLocation
			};

			_instances[baseId] = instance;
			_private.initEvents(instance);

			return instance;
		},

		play: function(instance, videoUrl, options, custom) {
			options = options || {};

			// tracking
			if (options.showTitle) {
				setShowTitle(options.showTitle);
			}

			// make sure the player is embedded
			_private.embedFlashPlayerOnce(instance, options.forceRefresh, custom);

			// play the video
			var streamingFlag = options.streaming ? 1 : 0;
			window.setTimeout(function() {
								  _private.playWhenReady($(instance.movieId), videoUrl, streamingFlag);
							  }, 1000);
		},

		replay: function(instance) {
			$(instance.movieId).replayEpisode();
		},

		clear: function(instance) {
			var movieElm = $(instance.movieId);
			if (movieElm.PlayURL) {
				movieElm.PlayURL('', 0);
			}
		},

		pauseAll: function() {
			for (var i in _instances) {
				self.pause(_instances[i]);
			}
		},

		pause: function(instance) {
			var baseElm = $(instance.baseId);
			baseElm.addClassName('paused');
			_private.initEvents(instance);
		},

		unpauseAll: function() {
			for (var i in _instances) {
				self.unpause(_instances[i]);
			}
		},

		unpause: function(instance) {
			var baseElm = $(instance.baseId);
			baseElm.removeClassName('paused');
		},

		/**
		 * Minimize the controls over the flash movie
		 */
		setMinimalControls: function(instance) {
			var movieElm = $(instance.movieId);
			if (movieElm.setMinimalControls) {
				movieElm.setMinimalControls();
			}
		},

		/**
		 * Display the controls over the flash movie
		 */
		setDefaultControls: function(instance) {
			var movieElm = $(instance.movieId);
			if (movieElm.setDefaultControls) {
				movieElm.setDefaultControls();
			}
		}
	};

	/**
	 * Private functions
	 */
	_private = {
		/**
		 * Initialize events
		 */
		initEvents: function(instance) {
			var baseElm = $(instance.baseId);

			// "replay video" link
			baseElm.select('.replay')
				.each(function(elm) {
						  elm.observe('click', function(event) {
										  event.stop();
										  Player.unpause(instance);
										  Player.replay(instance);
									  });
					  });
		},

		/**
		 * Poll the movie until it's ready, then play the video
		 * @param {DOMelement} movieElm
		 * @param {string} videoUrl
		 * @param {int} streamingFlag
		 */
		playWhenReady: function(movieElm, videoUrl, streamingFlag) {
			if (movieElm.PlayURL) {
				movieElm.PlayURL(videoUrl, streamingFlag);
			}
			else {
				window.setTimeout(function() {
									  _private.playWhenReady(movieElm, videoUrl, streamingFlag);
								  }, 100);
			}
		},

		/**
		 * Embed the flash player
		 * @param {hash} instance
		 * @param {boolean} forceRefresh
		 */
		embedFlashPlayerOnce: function(instance, forceRefresh, custom) {
			// force a reload of the embedded player
			if (forceRefresh && $(instance.movieId)) {
				$(instance.movieId).remove();
			}

			// return if flash player already embedded
			if ($(instance.movieId)) {
				return;
			}

			var so = new SWFObject(instance.swfLocation, instance.movieId, '100%', '100%', '8', '#ffffff');
			so.addParam('allowScriptAccess', 'always');	// (REQUIRED)
			so.addParam('wmode', 'transparent'); // allows move install process to be shown
			so.addParam('scale', 'noscale');
			so.addParam('align', 'l');
			so.addParam('salign', 'tl');

			so.addVariable('movieID', instance.movieId);	// Makes the flash aware of its own ID	(REQUIRED)
			so.addVariable('playerParentID', 'moveplayer'); // ID of the div that gets the client inside of it (REQUIRED)
			so.addVariable('movieParentID', instance.holderId); // ID of the div that holds the flash object
			so.addVariable('movieFormat', 'home');

			//Downloaded FLV
			//so.addVariable("video", "http://foxbc.edgeboss.net/download/foxbc/homepage/simpsons/clip_02_non-tagged.flv");
			//so.addVariable("streaming", "0");
			//Akamai StreamOS

			//so.addVariable("video", options.videoUrl);
			so.addVariable("streaming", "1");

			if (_adsUrl) {
				so.addVariable("streamflashads", _adsUrl);
			}

			// write the flash object
			so.write(instance.holderId);

			// initialize events
			var movieElm = $(instance.movieId);
			movieElm.observe('mouseover', function(event) {
								 self.setDefaultControls(instance);
							 });
			movieElm.observe('mouseout', function(event) {
								 self.setMinimalControls(instance);
							 });

			// Set custom width and height
			if(custom && custom.w && custom.h) {
				setTimeout(function(){movieElm.setCustomSize(custom.w, custom.h);},200);
			}
		}
	};

	return self;
}();




//Tracking
function sendPixelHit(url, comp) // comp -> It is from a companion display ad
{
	log('sendPixelHit', url)
	if(comp)
		url += '?count=%s'.format(compIndex++); // This will help make sure that the pixel requests dont get cached.
	var image = new Image(1,1);
	setTimeout(function() { image.src = url; }, 1);
}


/** global **/
var mn_showTitle = "";

function setShowTitle(title) {
	mn_showTitle = title;
}

function getShowTitle(){
	return mn_showTitle;
}

function sendNielsenCall(url)
{
	//log('sendNielsenCall: ', url);
	var image = new Image(1,1);
	setTimeout(function() { image.src = url; }, 1);
}

function getSrnd(){
	if(typeof adid != 'undefined'){
		return adid;
	}
	return 0;
}

function showEndEpisode(show){
	if (show) {
		Player.pauseAll();
	} else {
		Player.unpauseAll();
	}
}

/** SOURCE:  _ui/js/schedule.js  **/

/**
 * @fileoverview Handles behaviour on the schedule page
 * @author Josh Johnston josh@xhtmlized.com
 * @version 0.1
 * @requires prototype.1.6.0.2
 * @requires HashHelper
 */

/**
 * Schedule: Handles behaviour on the schedule page
 * @namespace
 */
var Schedule = function(){
	/*global location, $, $$, Element, Clock, CacheLoader, Dimmer, HashHelper, FoxMenus, FloatingPanels, Player */
	var _cacheId = 'schedule-grid';
	var _classNames = {
		filtered: 'filtered',
		rowfirst: 'even',
		menuopen: 'open',
		selected: 'selected',
		selected_filtered: 'selected_filtered'
	};
	var _hashKeys = {
		week: 'week',
		filter: 'showtype',
		timezone: 'timezone',
		time: 'time'
	};
	var _currentTimeBracket = 2000;
	var _bracketSize = 200;
	var _minTime = 1200;
	var _maxTime = 2400;
	var _menus;
	var _baseElm;
	var _lastCacheKey;
	var _playerInst;

	var self = /** @scope Schedule */{
		/**
		 * Initialize the Schedule component
		 * @param {DOMelement} baseElm
		 */
		initialize: function(baseElm, menus) {
			_baseElm = baseElm;

			// setup the week menu
			self.initWeekMenu();

			// setup the time navigation
			self.initTimeNavigation();

			// setup the grid
			self.initScheduleGrid();

			// setup the menus
			menus.week.addClassName('foxmenu');
			menus.filter.addClassName('foxmenu');
			menus.timezone.addClassName('foxmenu');
			_menus = {week: FoxMenus.create(menus.week),
					  filter: FoxMenus.create(menus.filter),
					  timezone: FoxMenus.create(menus.timezone)};

			// When the menu changes, it triggers a change in the location hash
			_menus.week.onChange = function(value) {
				HashHelper.setValue(_hashKeys.week, value);
				HashHelper.updateLocation();
			};
			_menus.filter.onChange = function(value) {
				HashHelper.setValue(_hashKeys.filter, value);
				HashHelper.updateLocation();
			};
			_menus.timezone.onChange  = function(value) {
				HashHelper.setValue(_hashKeys.timezone, value);
				HashHelper.updateLocation();
			};

			// set up the HashHelper
			HashHelper.addObserver({key: _hashKeys.week,
									callback: self.setWeek});
			HashHelper.addObserver({key: _hashKeys.filter,
									callback: self.setFilter});
			HashHelper.addObserver({key: _hashKeys.timezone,
									callback: self.setTimezone});
			//HashHelper.addObserver({key: _hashKeys.time,
			//						callback: self.setTimeBracket});
			HashHelper.startWatching();

			// load defaults
			self.loadDefaultSettings();

			// load the grid
			self.loadFromCache();

			// set up the video player
			_playerInst = Player.create('schedule-player');
		},

		initCalendarAdd: function(linkElm){
			linkElm.observe('click',function(event){
								event.stop();
								var strong = new Element('strong',{className:'added'}).update('Added To Calendar');
								$(this.parentNode).appendChild(strong);
								$(this).remove();
			});
		},

		/**
		 * Setup week menu
		 */
		initWeekMenu: function() {
			// get the starting day: the previous sunday (or today, if today is sunday)
			var today = Clock.getDate();
			var startDay = (Clock.format('D', today) == 'Sun')
				? today
				: self.getPreviousSunday(today);
			var oneWeekInMs = 1000 * 60 * 60 * 24 * 7;

			$('week-options').select('li a').each(function(elm, index) {
													var date = new Date(startDay.getTime() + (oneWeekInMs * index));
													self.initWeekMenuItem(elm, date);
												});
		},

		/**
		 * Setup a week menu item
		 * @param {DOMelement} elm
		 * @param {date} date
		 */
		initWeekMenuItem: function(elm, date) {
			// temporary hack for new line up
			var str = elm+"";
			if (str.match("newlineup") == null) {
				//elm.innerHTML = self.buildWeekText(date);
				elm.writeAttribute('href', '#week:'+Clock.format('Y-m-d', date));
			} else {
				elm.writeAttribute('href', '#week:newlineup');
			}
		},

		/**
		 * Get the Sunday prior to the date given
		 * @param {date} date
		 * @return {date}
		 */
		getPreviousSunday: function(date) {
			var dayOfWeek = date.getDay();
			var oneDayInMs = 1000 * 60 * 60 * 24;
			var offsetMs = (dayOfWeek) * oneDayInMs;
			return new Date(date.getTime() - offsetMs);
		},

		/**
		 * Build text to go in a week menu item
		 * @param {date} date
		 * @return {string}
		 */
		buildWeekText: function(startDate) {
			var sixDaysInMs = 1000 * 60 * 60 * 24 * 6;
			var endDate = new Date(startDate.getTime() + sixDaysInMs);
			var start = Clock.format('M-jS', startDate).split('-');
			var end = Clock.format('M-jS', endDate).split('-');

			// if the months are the same, remove the month part from the end date
			if (start[0] == end[0]) {
				end.shift();
			}

			// construct the string
			return start.join(' ') + ' to ' + end.join(' ');
		},

		/**
		 * Initialize time navigation
		 */
		initTimeNavigation: function() {
			$('time-nav')
				.observe('click', self.handleTimeNavClick);

            // ----
            // duplicate the time navigation

            var time_2 = document.createElement('ul');
            time_2.id = 'time-nav-2';
            time_2.innerHTML = $('time-nav').innerHTML;
            $('schedule').appendChild(time_2);

            $(time_2).observe('click', self.handleTimeNavClick);

            // ----
            // duplicate the hour markers

            var hours_2 = document.createElement('ul');
            hours_2.id = 'hours-2';
            hours_2.className = 'hours';
            hours_2.innerHTML = $('hours').innerHTML;
            $('schedule').appendChild(hours_2);
		},

        showTimeNav: function() {
            $('time-nav').show();
            $('time-nav-2').show();

            $('hours').show();
            $('hours-2').show();
        },

        hideTimeNav: function() {
            $('time-nav').hide();
            $('time-nav-2').hide();

            $('hours').hide();
            $('hours-2').hide();
        },

        setTimeNavText: function(text) {
            $('time-nav').select('.current')[0].innerHTML = text;
            $('time-nav-2').select('.current')[0].innerHTML = text;
        },

		/**
		 * Initialize the schedule grid
		 */
		initScheduleGrid: function() {
			//_baseElm.observe('click', self.handleGridClick)
				_baseElm.observe('mouseover', self.handleGridMouseOver)
				.observe('mouseout', self.handleGridMouseOut);
		},

		/**
		 * Load the default settings
		 */
		loadDefaultSettings: function() {
			// load settings from the hash
			var week = HashHelper.getValue(_hashKeys.week);
			var filter = HashHelper.getValue(_hashKeys.filter);
			var timezone = HashHelper.getValue(_hashKeys.timezone);
			var time = HashHelper.getValue(_hashKeys.time);

			// if settings aren't in the hash, look in the markup
			if (!week) {
				week = _menus.week.getSelectedValue();
			}
			if (!filter) {
				filter = _menus.filter.getSelectedValue();
			}
			if (!timezone) {
				timezone = _menus.timezone.getSelectedValue();
			}
			if (!time) {
				time = _currentTimeBracket;
			}

			// set the values
			self.setWeek(week);
			self.setFilter(filter);
			self.setTimezone(timezone);
			self.setTimeBracket(time);
		},

		selectCurrentDay: function() {
			var currentDay = Clock.format('D').toLowerCase();
			_baseElm.select('.week li.selected')
				.each(function(elm) {
						  elm.removeClassName('selected');
						  elm.removeClassName('selected_filtered');
					  });

			_baseElm.select('.week li.'+currentDay)
				.each(function(elm) {
						  elm.addClassName('selected');
						  if(elm.hasClassName('filtered')) {
							  elm.addClassName('selected_filtered');
						  }
				});
		},

		/**
		 * Builds a cache key
		 * @return {hash}
		 */
		buildCacheKey: function() {
			var key = {
				date: _menus.week.getSelectedValue()
			};
			return key;
		},

		/**
		 * Load data from the cache
		 */
		loadFromCache: function() {
			var key = self.buildCacheKey();
			if (!_lastCacheKey
				|| CacheLoader.joinKey(key) != CacheLoader.joinKey(_lastCacheKey)) {
				CacheLoader.load(_cacheId, key,
							 {onSuccess: self.handleLoadSuccess,
							  onFailure: self.handleLoadError});
				_lastCacheKey = key;
			}
		},

		/**
		 * Handle a loading error
		 */
		handleLoadError: function() {
			self.populate('<ol class="week"><li>no data for this day :(</li></ol>');
		},

		/**
		 * Handle a successful load
		 * @param {JSONstring} responseText
		 */
		handleLoadSuccess: function(responseText) {
			var response = responseText.evalJSON();
			var content = (response.result == 'success')
				? response.content
				: '<ol class="week"><li>An error occurred :(</li></ol>';

			self.populate(content);
		},

		/**
		 * Populate with new content
		 * @param {html} content
		 */
		populate: function(content) {

			// remove 'xmlns' attributes
			content = content.replace(/xmlns\:\w+\=[\"\'].*?[\"\']/g, '');
			_baseElm.select('ol.week').each(function(elm) {
												$(elm).remove();
											});
			_baseElm.insert({bottom: content});

			// give a slight delay, to allow IE to get with the program
			setTimeout(function() {
						   self.initNewContent();
						   self.refreshFilter();

						   // select the current day
						   self.selectCurrentDay();

						   // show the current time window
						   self.showCurrentTimeWindow();
					   }, 100);
		},

		initNewContent: function() {
			_baseElm.select('ol.week').each(self.initNewWeekContent);
		},

		initNewWeekContent: function(weekElm) {
			// add the 'day1 ... day7' classes
			weekElm.childElements().each(function(elm, index) {
											 elm.addClassName('day'+(index+1));
										 });
		},

		/**
		 * Set the week
		 * @param {string} week
		 */
		setWeek: function(week) {
			var items = $$('#week-options ul li a');
			var title = '';
			for (var i=0, length=items.length; i<length; i++) {
				if (items[i].readAttribute('href').indexOf('#'+_hashKeys.week+':'+week) === 0) {
					title = items[i].innerHTML;
					break;
				}
			}

			// change the menu text
			_menus.week.setTitleValue(title);
			_menus.week.setSelectedValue(week);

			if (title == "The New 09 Line-Up") { // temporary for new line up
				$('new09lineup').show();
				self.hideTimeNav();
			}

			// reload the grid
			self.loadFromCache();
		},

		/**
		 * Refresh the current filter
		 */
		refreshFilter: function() {
			self.filterItems(_menus.filter.getSelectedValue());
		},

		/**
		 * Set the filter
		 * @param {string} filter
		 */
		setFilter: function(filter) {
			// apply filters
			self.filterItems(filter);

			var title = (filter == 'all') ? 'view all' : filter;

			// change the menu text
			_menus.filter.setTitleValue(title);
			_menus.filter.setSelectedValue(filter);
		},

		/**
		 * Set the timezone
		 * @param {string} timezone
		 */
		setTimezone: function(timezone) {
			var title = timezone;

			// change the menu text
			_menus.timezone.setTitleValue(title);
			_menus.timezone.setSelectedValue(timezone);
		},

		/**
		 * Filter items in the list
		 */
		filterItems: function(filter) {
			// filter the individual shows
			_baseElm.select('.week ol li')
				.each(function(elm){
						  // empty items are always filtered
						  if (elm.hasClassName('empty')) {
							  elm.addClassName(_classNames.filtered);
						  }
						  // reveal items matching the filter
						  else if (filter == 'all' || elm.hasClassName('type-'+filter)) {
							  elm.removeClassName(_classNames.filtered);
						  }
						  // hide items excluded by the filter
						  else {
							  elm.addClassName(_classNames.filtered);
						  }
					  });

			// filter the days:
			// if the last item in a day is filtered, mark the day as filtered
			_baseElm.select('.week > li')
				.each(function(elm) {
						  // check the last item
						  var lastChild = elm.select('ol li:last-child')[0];
						  var listShow = elm.select('ol li');
						  if ((lastChild && lastChild.hasClassName(_classNames.filtered)) || !listShow.length ) {
							  elm.addClassName(_classNames.filtered);
						  }
						  else {
							  elm.removeClassName(_classNames.filtered);
						  }
					  });
		},

		/**
		 * Handle a click on the time navigation
		 * @param {DOMevent} event
		 */
		handleTimeNavClick: function(event) {
			var elm = event.element();

			// only act on link clicks
			if (elm.nodeName != 'A') {
				return;
			}

			// prev / next buttons
			var rel = elm.readAttribute('rel');
			var fun;
			if (rel == 'prev' || rel == 'next') {
				fun = (rel == 'prev')
					? self.moveTimePrevious
					: self.moveTimeNext;

				fun();
				HashHelper.setValue(_hashKeys.time, _currentTimeBracket);
				HashHelper.updateLocation();
				event.stop();
			}
		},

		/**
		 * Handle a click on the schedule grid
		 * @param {DOMevent} event
		 */
		handleGridClick: function(event) {
			var source = event.element();
			var sourcePath = source.ancestors();

			// get the grid cell that the event occurred in
			var cellElm = source.up('.vevent');
			if (!cellElm) {
				return;
			}

			var titleElm = cellElm.select('h4')[0];
			var infoElm = cellElm.select('.info')[0];

			// handle click on title or info
			if (source == titleElm || sourcePath.indexOf(titleElm) != -1
			   || source == infoElm || sourcePath.indexOf(infoElm) != -1) {
				event.stop();
				self.doGridItemClick(cellElm);
				return;
			}

			// handle click on 'add to calendar'
			var addElm = cellElm.select('.add')[0];
			if (source == addElm || sourcePath.indexOf(addElm) != -1) {
				self.doAddToCalendarClick(addElm);
				event.stop();
			}
		},

		/**
		 * Do a click on the 'add to calendar' button
		 * @param {DOMelement} linkElm
		 */
		doAddToCalendarClick: function(linkElm) {
			linkElm.addClassName('added')
				.removeClassName('add')
				.childElements()[0].innerHTML = 'Added To Calendar';
		},

		/**
		 * Handle a mouseover on the schedule grid
		 * @param {DOMevent} event
		 */
		handleGridMouseOver: function(event) {
			var source = event.element();
			var sourcePath = source.ancestors();

			// get the grid cell that the event occurred in
			var cellElm = source.up('.vevent');
			if (!cellElm) {
				if (source.hasClassName('vevent')) {
					_baseElm.select('.vevent .hover')
						.each(function(elm) {
								  elm.removeClassName('hover');
							  });
				}
				return;
			}

			var titleElm = cellElm.select('h4')[0];
			var infoElm = cellElm.select('.info')[0];

			// ignore if the title is aleady hovered
			if (titleElm.hasClassName('hover')) {
				return;
			}

			// tell if the title or the info is hovered
			if (source == titleElm || sourcePath.indexOf(titleElm) != -1
			   || source == infoElm || sourcePath.indexOf(infoElm) != -1) {
				titleElm.addClassName('hover');
				infoElm.addClassName('hover');
			}
		},

		/**
		 * Handle a mouseout on the schedule grid
		 * @param {DOMevent} event
		 */
		handleGridMouseOut: function(event) {
			var source = event.element();

			// get the grid cell that the event occurred in
			var cellElm = source.up('.vevent');
			if (!cellElm) {
				return;
			}

			// get the element the mouse ended up at
			var target = event.relatedTarget || event.toElement;

			// work out if the mouseout was FROM title/info TO something else
			var titleElm = cellElm.select('h4')[0];
			var infoElm = cellElm.select('.info')[0];

			if (target) {
				if (target == titleElm || target.descendantOf(titleElm)
					|| target == infoElm || target.descendantOf(infoElm)) {
					return;
				}
				else {
					titleElm.removeClassName('hover');
					infoElm.removeClassName('hover');
				}
			}
		},

		doGridItemClick: function(itemElm) {
			var showLinkElm = itemElm.select('h4 a')[0];
			var watchPreviewElm = itemElm.select('a.watch')[0];
			var href = watchPreviewElm
				? watchPreviewElm.readAttribute('href')
				: showLinkElm.readAttribute('href');

			// play an flv
			if (href.match(/\.flv$/)) {
				self.openProgramInfo(itemElm);
			}
			// normal hyperlink behaviour
			else {
				//location.href = href;
			}
		},

		/**
		 * Open the program info popup
		 * @param {DOMelement} showElm
		 */
		openProgramInfo: function(showElm) {
			var gridElm = _baseElm.select('.week.current')[0];
			var gridSize = gridElm.getDimensions();
			var gridPos = gridElm.positionedOffset();

			var playerElm = $(_playerInst.baseId);
			var playerSize = playerElm.getDimensions();

			// center the player in the schedule
			var left = Math.floor(gridPos.left + (gridSize.width - playerSize.width)*0.5);
			var top = Math.floor(gridPos.top + (gridSize.height - playerSize.height)*0.5);
			playerElm.setStyle({'left': left+'px', 'top': top+'px'});

			// populate the player
			var options = self.extractPlayerOptions(showElm);
			self.populatePlayer(options);
			Player.play(_playerInst, options.videoUrl, {streaming: true, showTitle: options.showName});
			FloatingPanels.open(playerElm, {bringToFront: false,
											onopen: function() {
												Dimmer.show();
											},
											onclose: function() {
												Dimmer.hide();
												Player.clear(_playerInst);
												}});
		},

		/**
		 * Extract player options from a show element
		 * @param {DOMelement} showElm
		 * @return {hash}
		 */
		extractPlayerOptions: function(showElm) {
			var showLinkElm = showElm.select('h4 a')[0];
			var tuneinElm = showElm.select('.tunein')[0];
			var options = {
				showName: showLinkElm.select('.summary')[0].innerHTML,
				showUrl: showLinkElm.readAttribute('href'),
				episodeName: showElm.select('h4 .episode')[0].innerHTML,
				dayOfWeek: showElm.up('li').select('h3')[0].innerHTML,
				screeningTime: tuneinElm.select('.eastern-time abbr')[0].innerHTML + ' \\ ' + tuneinElm.select('.central-time abbr')[0].innerHTML,
				description: showElm.select('.description')[0].innerHTML,
				videoUrl: showLinkElm.readAttribute('href')
			};
			return options;
		},

		/**
		 * Populate the player
		 * @param {hash} options
		 */
		populatePlayer: function(options) {
			var playerElm = $(_playerInst.baseId);

			playerElm.select('dt')[0].innerHTML = options.showName;
			playerElm.select('dd')[0].innerHTML = options.episodeName + ' - ' + options.dayOfWeek + ' ' + options.screeningTime;
			playerElm.select('dd')[1].innerHTML = options.description;
			var siteLink = playerElm.select('.program-website a')[0];
			var linkTextPrefix = (options.showName.toLowerCase().indexOf('the') === 0)
				? 'Visit '
				: 'Visit the ';
			siteLink.innerHTML = linkTextPrefix + options.showName + ' Site';
			siteLink.writeAttribute('href', options.showUrl);
		},

		/**
		 * Move to the previous time bracket
		 */
		moveTimePrevious: function() {
			var newTimeBracket = _currentTimeBracket - _bracketSize;
			if (newTimeBracket < _minTime) {
				// ? ignore
				return;
			}
			self.setTimeBracket(newTimeBracket);
		},

		/**
		 * Move to the next time bracket
		 */
		moveTimeNext: function() {
			var newTimeBracket = _currentTimeBracket + _bracketSize;
			if (newTimeBracket > _maxTime) {
				// ? ignore
				return;
			}
			self.setTimeBracket(newTimeBracket);
		},

		/**
		 * Set the time bracket
		 * @param {int} timeBracket
		 */
		setTimeBracket: function(timeBracket) {
			_currentTimeBracket = parseInt(timeBracket, 10);

			// make the new text
			var startDate = self.getStartDate();
			var endDate = self.getEndDate();

			var text = startDate.format('ga') + ' to ' + endDate.format('ga');
			self.setTimeNavText(text);

			// update the hour markers
			self.updateHourMarkers();

			// update the time window
			self.showCurrentTimeWindow();

			// reload the grid
			self.loadFromCache();
		},

		/**
		 * Show the current time window
		 */
		showCurrentTimeWindow: function() {
			var windowIndex = (_currentTimeBracket - _minTime) / _bracketSize;

			// hide the old window
			_baseElm.select('ol.current').each(function(elm) {
														elm.removeClassName('current');
													});

			// show the new window
			var elm = _baseElm.select('ol.week')[windowIndex];
			if (elm) {
				elm.addClassName('current');
			}
		},

		/**
		 * Get a date object representing the start of the time window
		 * @return {Date}
		 */
		getStartDate: function() {
			var date = new Date();
			var startTime = _currentTimeBracket+"";
			date.setHours(startTime.substr(0,2), startTime.substr(2,2));
			return date;
		},

		/**
		 * Get a date object representing the end of the time window
		 * @return {Date}
		 */
		getEndDate: function() {
			var date = new Date();
			var endTime = _currentTimeBracket + _bracketSize + "";
			date.setHours(endTime.substr(0,2), endTime.substr(2,2));
			return date;
		},

		/**
		 * Update the hour markers above the grid
		 */
		updateHourMarkers: function() {
			var startDate = self.getStartDate();
			var msStart = startDate.getTime();
			var msBetween = 1000 * 60 * 30;

            var updateFunc = function(elm, index) {
				var date = new Date(msStart + (msBetween * index));
				var timeString = date.format('g:i a');
				if (timeString.match(/^\d+:00/)) {
					timeString = date.format('g a');
				}
				elm.innerHTML = timeString;
			};

			$('hours').select('li').each(updateFunc);
            $('hours-2').select('li').each(updateFunc);
		}
	};

	return self;
}();

/** SOURCE:  _ui/js/scheduletonight.js  **/

/**
 * @fileoverview Handles the "What's on Tonight" box on the homepage
 * @author Josh Johnston josh@xhtmlized.com
 * @version 0.1
 * @requires prototype.1.6.0.2
 * @requires CacheLoader
 * @requires Clock
 */

/**
 * ScheduleTonight: handles the "What's on Tonight" box on the homepage
 * @namespace
 */
var ScheduleTonight = function(){
	/*global $, CacheLoader, Clock */
	var _baseId;
	var _cacheId = 'schedule-tonight';
	var _dateString;

	var self = /** @scope ScheduleTonight */{
		/**
		 * Initialize the ScheduleTonight component
		 * @param {DOMelement} baseElm
		 */
		initialize: function(baseElm) {
			_baseId = baseElm.identify();

			// watch the clock
			self.setDate(Clock.getDate());
			Clock.addObserver(self.setDate);

			// observe clicks
			baseElm.observe('click', self.handleClick);
		},

		/**
		 * Set the date
		 * @param {date} date
		 */
		setDate: function(date) {
			var newDate = Clock.format('Y-m-d', date);
			if (newDate != _dateString) {
				_dateString = newDate;

				// populate from the html cache
				self.loadFromCache();
			}
		},

		/**
		 * Load data from the cache
		 */
		loadFromCache: function() {
			CacheLoader.load(_cacheId, {date: _dateString},
							 {onSuccess: self.handleLoadSuccess,
							  onFailure: self.handleLoadError});
		},

		/**
		 * Handle a loading error
		 */
		handleLoadError: function() {
			self.populate('<ol><li>no data for this day :(</li></ol>');
		},

		/**
		 * Handle a successful load
		 * @param {JSONstring} responseText
		 */
		handleLoadSuccess: function(responseText) {
			var response = responseText.evalJSON();
			var content = (response.result == 'success')
				? response.content
				: '<ol><li>An error occurred :(</li></ol>';

			self.populate(content);
		},

		/**
		 * Populate with new content
		 * @param {html} content
		 */
		populate: function(content) {
			var baseElm = $(_baseId);
			baseElm.select('ol')[0].replace(content);

			// count how many shows are now listed
			var numShows = baseElm.select('ol')[0].childElements().length;

			// change the 'view' class
			baseElm.writeAttribute('class',
								   baseElm.readAttribute('class').replace(/\bview\-\d+\b/, 'view-'+numShows));
		},

		/**
		 * Handle a click on the schedule
		 * @param {DOMevent} event
		 */
		handleClick: function(event) {
			var target = event.element();

			// only act on "watch" links
			if (!target.hasClassName('watch')) {
				return;
			}

			var href = target.readAttribute('href');
			if (href.match(/\.flv$/)) {
				event.stop();
				// activate the video player
				var itemElm = target.parentNode;
				var fullEpisodeElm = Element.select(itemElm, '.full-episode')[0];
				var fullEpisodeUrl = fullEpisodeElm
					? fullEpisodeElm.readAttribute('href')
					: false;

				var options = {
					showName: itemElm.select('h3 a')[0].innerHTML.replace(/\-\s*<em>.*?<\/em>/, ''),
					episodeName: '',
					description: itemElm.select('.description')[0].innerHTML,
					runningTime: '',
					fullEpisodeUrl: fullEpisodeUrl,
					videoUrl: target.readAttribute('href')
				};
				Videos.openPlayer(options);
			}
			else {
				// normal hyperlink behaviour
			}
		}
	};

	return self;
}();

/** SOURCE:  _ui/js/showlist.js  **/

/**
 * @fileoverview Handles the list of shows
 * @author Josh Johnston josh@xhtmlized.com
 * @version 0.1
 * @requires prototype.1.6.0.2
 */

/**
 * ShowList: handles the list of shows
 * @namespace
 */
var ShowList = function(){
	/*global location */
	var _classNames = {
		filtered: 'filtered',
		rowfirst: 'even',
		listmode: 'view-list',
		menuopen: 'open',
		selected: 'selected'
	};
	var _hashKeys = {
		viewmode: 'showview',
		filter: 'showtype'
	};
	var _listElm;
	var _menus;

	var self = /** @scope ShowList */{
		/**
		 * Initialize the ShowList component
		 * @param {DOMelement} listElm
		 */
		initialize: function(listElm, menus) {
			_listElm = listElm;

			menus.viewmode.addClassName('foxmenu');
			menus.filter.addClassName('foxmenu');
			_menus = {viewmode: FoxMenuInstance(menus.viewmode),
					  filter: FoxMenuInstance(menus.filter)};

			_menus.viewmode.onChange = self.setViewMode;
			_menus.filter.onChange = self.setFilter;

			self.loadDefaultSettings();
		},

		/**
		 * Load the default settings
		 */
		loadDefaultSettings: function() {
			// load settings from the hash
			var mode = self.extractSettingFromHash(_hashKeys.viewmode);
			var filter = self.extractSettingFromHash(_hashKeys.filter);

			// if settings aren't in the hash, look in the markup
			if (!mode) {
				mode = _menus.viewmode.getSelectedValue();
			}
			if (!filter) {
				filter = _menus.filter.getSelectedValue();
			}

			// set the values
			_menus.filter.setSelectedValue(filter);
			self.setFilter(filter);

			_menus.viewmode.setSelectedValue(mode);
			self.setViewMode(mode);
		},

		/**
		 * Extract a setting from the location hash
		 * @param {string} key
		 * @return {string}
		 */
		extractSettingFromHash: function(settingKey) {
			// get the location hash
			var hash = location.hash;
			var pattern = new RegExp('\\b'+settingKey+'\\:([\\w\\d]+?)\\b');
			var matches = pattern.exec(hash);

			// found a state id
			return (matches && matches.length === 2)
				? matches[1]
				: '';
		},

		/**
		 * Set the view mode
		 * @param {string} mode
		 */
		setViewMode: function(mode) {
			var modeText;

			// change the class on the list
			if (mode == 'detail') {
				modeText = 'Detailed';
				_listElm.removeClassName(_classNames.listmode);
			}
			else {
				modeText = 'List';
				_listElm.addClassName(_classNames.listmode);
			}

			// re-apply row markers
			self.applyRowMarkers(self.getRowSize());

			// change the menu text
			_menus.viewmode.setTitleValue(modeText);
		},

		/**
		 * Toggle between list view and detailed view
		 */
		toggleViewMode: function() {
			var newMode = (_listElm.hasClassName(_classNames.listmode))
				? 'detail'
				: 'list';
			self.setViewMode(newMode);
		},

		/**
		 * Set the filter
		 * @param {string} filter
		 */
		setFilter: function(filter) {
			// apply filters, and rewrite the row markers
			self.filterItems(filter);
			self.applyRowMarkers(self.getRowSize());

			var title = (filter == 'all') ? 'view all' : filter;

			// change the menu text
			_menus.filter.setTitleValue(title);
		},

		/**
		 * Get the row size for the current view mode
		 * @return {int}
		 */
		getRowSize: function() {
			// 2 items per row in list view, 4 items per row in detailed view
			return (_listElm.hasClassName(_classNames.listmode)) ? 2 : 4;
		},

		/**
		 * Filter items in the list
		 */
		filterItems: function(filter) {
			_listElm.childElements()
				.each(function(elm){
						  // reveal items matching the filter
						  if (filter == 'all' || elm.hasClassName('type-'+filter)) {
							  elm.removeClassName(_classNames.filtered);
						  }
						  // hide items excluded by the filter
						  else {
							  elm.addClassName(_classNames.filtered);
						  }
					  });
		},

		/**
		 * Mark items at the start of each row
		 */
		applyRowMarkers: function(rowSize) {
			var count = 0;
			_listElm.childElements()
				.each(function(elm){
						  // ignore filtered items
						  if (elm.hasClassName(_classNames.filtered)) {
							  return;
						  }

						  // add a class to the first item in every row
						  if (count % rowSize === 0) {
							  elm.addClassName(_classNames.rowfirst);
						  }
						  else {
							  elm.removeClassName(_classNames.rowfirst);
						  }

						  count++;
					  });
		}
	};

	return self;
}();

/** SOURCE:  _ui/js/showpromos.js  **/

/**
 * @fileoverview Handles behaviour for the "promos" panel on the home page
 * @author Josh Johnston josh@xhtmlized.com
 * @version 0.1
 * @requires prototype.1.6.0.2
 * @requires PagedSets
 */

/**
 * Promos: Handles behaviour for the "promos" panel
 * @namespace
 */
var ShowPromos = function(){
	/*global $, PagedSets */

	var _instance;
	var self = /** @scope ShowPromos */{
		/**
		 * Initialize the ShowPromos component
		 * @param {DOMelement} Base element
		 */
		initialize: function(baseElm) {
			_instance = PagedSets.create(baseElm, {
											 selectors: {set: 'ol'},
											 settings: {clipDimensions: baseElm.select('.inner')[0].getDimensions()}
										 });
		}
	};

	return self;
}();

/** SOURCE:  _ui/js/userreviews.js  **/

/**
 * @fileoverview Handles behaviour for the "User Reviews" section
 * @author Josh Johnston josh@xhtmlized.com
 * @version 0.1
 * @requires prototype.1.6.0.2
 * @requires FloatingPanels
 * @requires GhostValues
 */

/**
 * UserReviews: Handles behaviour for the "User Reviews" section
 * @namespace
 */
var UserReviews = function(){
	/*global $, $$, FloatingPanels, GhostValues */

	var _baseElm;
	var _rateReviewPanelId;
	var _reportPanelId;

	var self = /** @scope UserReviews */{
		/**
		 * Initialize the UserReviews component
		 * @param {DOMelement} Base element for user data
		 * @param {DOMelement} Rate & Review panel
		 * @param {DOMelement} Report Violation panel
		 */
		initialize: function(baseElm, rateReviewPanelId, reportPanelId) {
			_baseElm = baseElm;
			_rateReviewPanelId = rateReviewPanelId;
			_reportPanelId = reportPanelId;

			// bind event callbacks
			_baseElm.observe('click', self.handleClick);

			// load reviews
			self.loadReviews(1);
		},

		/**
		 * Handle a click somewhere on the component
		 * @param {DOMevent} event
		 */
		handleClick: function(event) {
			var target = event.element();
			// rate & review link
			if (target.nodeName == 'A' && target.up('.rate-review')) {
				self.openRateReviewPanel();
				event.stop();
				return;
			}

			// report link
			if (target.nodeName == 'A' && target.up('.report')) {
				self.openReportViolationPanel(target);
				event.stop();
				return;
			}

			// pagination
			var pageLinks = $('review-paging').select('li a');
			if (target.nodeName != 'A') {
				target = target.up('a');
			}
			if (pageLinks.indexOf(target) != -1) {
				self.doPaginationClick(target);
				event.stop();
			}
		},

		/**
		 * Action for a click on a pagination link
		 * @param {DOMelement} elm
		 */
		doPaginationClick: function(elm) {
			var currentPage = self.getCurrentPageNum();
			var href = elm.readAttribute('href');
			var pageNum = href.replace('#page:', '');
			var maxPage = self.getNumPages();

			// handle relative actions
			if (pageNum == 'first') {
				pageNum = 1;
			}
			else if (pageNum == 'previous') {
				pageNum = Math.max(1, currentPage-1);
			}
			else if (pageNum == 'next') {
				pageNum = Math.min(maxPage, currentPage+1);
			}
			else if (pageNum == 'last') {
				pageNum = maxPage;
			}

			if (pageNum != currentPage && pageNum >= 1 && pageNum <= maxPage) {
				self.loadReviews(pageNum);
			}
		},

		loadReviews: function(pageNum) {
			var topicId = self.getTopicId();
			var reverseFlag = self.getReverseFlag();

			self.setCurrentPageNum(pageNum);
			self.showLoadingReviews();

			// call function from ajax.js
			show_page('', '', topicId, pageNum, reverseFlag);
		},

		/**
		 * Give visual indication that reviews are loading
		 */
		showLoadingReviews: function() {
			var loaderElm = $('commentsList').select('.loader')[0];
			if (!loaderElm) {
				loaderElm = new Element('div').addClassName('loader');
				loaderElm.innerHTML = 'loading reviews...';
				$('commentsList').insert({bottom: loaderElm});
			}

			$('commentsList').addClassName('loading');
		},

		/**
		 * Get the current reverse flag
		 * @return {string}
		 */
		getReverseFlag: function() {
			return $('reverseFlag') ? $F('reverseFlag') : '';
		},

		/**
		 * Get the current topic id
		 * @return {int}
		 */
		getTopicId: function() {
			return $('TopicID') ? $F('TopicID') : '';
		},

		/**
		 * Get the currently selected page
		 * @return {int}
		 */
		getCurrentPageNum: function() {
			var baseElm = $('review-paging');
			if (!baseElm) {
				return 1;
			}

			var selectedElm = baseElm.select('li.selected-review-page')[0];
			return selectedElm
				? (baseElm.select('li').indexOf(selectedElm)-1)
				: 1;
		},

		/**
		 * Set the currently selected page
		 * @param {int}
		 */
		setCurrentPageNum: function(pageNum) {
			var baseElm = $('review-paging');
			if (!baseElm) {
				return;
			}

			baseElm.select('li')
				.each(function(elm, index) {
						  if (index-1 == pageNum) {
							  elm.addClassName('selected-review-page');
						  }
						  else {
							  elm.removeClassName('selected-review-page');
						  }
					});
		},

		/**
		 * Get the number of pages
		 * @return {int}
		 */
		getNumPages: function() {
			var baseElm = $('review-paging');
			return baseElm.select('li').length - 4;
		},

		/**
		 * Open the Rate & Review panel
		 */
		openRateReviewPanel: function() {
			// fix z-indexes for IE
			_baseElm.select('li.review').each(function(elm) {
												  elm.setStyle({zIndex: ''});
											  });

			FloatingPanels.open($(_rateReviewPanelId), {bringToFront: false});
			FloatingPanels.close($(_reportPanelId));

			$(_rateReviewPanelId).select('.cancel-button')[0]
				.observe('click', function(event) {
							 event.stop();
							 FloatingPanels.close($(_rateReviewPanelId));
						 });
		},

		/**
		 * Open the Report Violation panel
		 * @param {DOMelement} linkElm
		 */
		openReportViolationPanel: function(linkElm) {
			// fix z-indexes for IE
			_baseElm.select('li.review').each(function(elm) {
												  elm.setStyle({zIndex: 999});
											  });
			linkElm.up('li').setStyle({zIndex: 1000});

			linkElm.insert({before: $(_reportPanelId)});
			FloatingPanels.open($(_reportPanelId), {bringToFront: false});
			FloatingPanels.close($(_rateReviewPanelId));

			$(_reportPanelId).select('.cancel-button')[0]
				.observe('click', function(event) {
							 event.stop();
							 FloatingPanels.close($(_reportPanelId));
						 });
		}
	};

	return self;
}();

/** SOURCE:  _ui/js/videos.js  **/

/**
 * @fileoverview Handles behaviour for the "videos" panel on the home page (and other places too)
 * @author Josh Johnston josh@xhtmlized.com
 * @version 0.1
 * @requires prototype.1.6.0.2
 * @requires TabbedPanel
 * @requires SWFObject
 */

/**
 * Videos: Handles behaviour for the "videos" panel
 * @namespace
 */
var Videos = function(){
	/*global $, $F, Element, TabbedPanel */
	var _panelIdPrefix = 'videos-';
	var _hashKey = 'videos';
	var _defaultPanel;
	var _baseElm;
	var _playerInst;

	var self = /** @scope Videos */{
		/**
		 * Initialize the Videos component
		 * @param {DOMelement} Base element
		 */
		initialize: function(baseElm) {
			_baseElm = baseElm;

			self.initTabs();
			self.initPanels();
			self.initPlayer();
		},

		/**
		 * Setup tab links to switch panels
		 */
		initTabs: function() {
			_baseElm.select('ul li').each(self.associatePanelWithTab)
				.each(TabbedPanel.initTabs);
		},

		/**
		 * Setup panels
		 */
		initPanels: function() {
			var panels = [$(_panelIdPrefix + 'clips'),
						  $(_panelIdPrefix + 'fullepisodes')].compact();

			// observe click events
			_baseElm.observe('mouseover', self.handleMouseOver);
			_baseElm.observe('mouseout', self.handleMouseOut);
			_baseElm.observe('click', self.handleClick);

			// set the default panel to either (i) the active panel, or
			// (ii) the first existing panel
			for (var i=0, length=panels.length; i<length; i++) {
				// set the default
				if (panels[i].hasClassName('active')
					|| !_defaultPanel) {
					_defaultPanel = panels[i];
				}
			}
		},

		/**
		 * Associate a panel with a tab, based on the link's href
		 * @param {DOMelement} tabElm
		 */
		associatePanelWithTab: function(tabElm) {
			var href = tabElm.select('a')[0].readAttribute('href');
			var pattern = new RegExp('\\b'+_hashKey+'\\:([\\w\\d]+?)\\b');
			var matches = pattern.exec(href);
			if (matches && matches.length == 2) {
				tabElm.setAttribute('data-panelid', _panelIdPrefix + matches[1]);
			}
		},

		/**
		 * Setup player
		 */
		initPlayer: function() {
			_playerInst = Player.create('videos-player');

			var closeElm = $(_playerInst.baseId).select('a.close')[0];
			closeElm.observe('click', function(event){
								 event.stop();
								 self.closePlayer();
							 });
		},

		/**
		 * Handle a click on a "watch" link in the clips panel
		 * @param {DOMelement} linkElm
		 */
		doWatchClipClick: function(linkElm) {
			var href = linkElm.readAttribute('href');

			// play an flv
			if (href.match(/\.flv$/)) {
				var itemElm = linkElm.parentNode;
				var fullEpisodeElm = Element.select(itemElm, '.full-episode')[0];
				var fullEpisodeUrl = fullEpisodeElm
					? fullEpisodeElm.readAttribute('href')
					: false;

				var options = {
					showName: Element.select(itemElm, 'h3')[0].innerHTML.stripTags(),
					episodeName: Element.select(itemElm, '.video-title')[0].innerHTML.stripTags(),
					description: Element.select(itemElm, '.video-description')[0].innerHTML.stripTags(),
					runningTime: Element.select(itemElm, '.video-running-time')[0].innerHTML.stripTags(),
					fullEpisodeUrl: fullEpisodeUrl,
					videoUrl: href
				};
				self.openPlayer(options);
			}
			// normal hyperlink behaviour
			else {
				location.href = href;
			}
		},

		/**
		 * Open the player
		 * @param {hash} options
		 */
		openPlayer: function(options) {
			TabbedPanel.activatePanel($(_playerInst.baseId));
			self.populatePlayer(options);
			Player.play(_playerInst, options.videoUrl, {streaming: true,
														showTitle: options.showName});
		},

		/**
		 * Populate the player with clip details
		 * @param {hash} options
		 * @config {string} showName
		 * @config {string} episodeName
		 * @config {string} description
		 * @config {string} runningTime
		 * @config {string} videoUrl
		 */
		populatePlayer: function(options) {
			var playerElm = $(_playerInst.baseId);
			var title = options.showName;
			var episodeDetails = options.episodeName;

			if (options.runningTime) {
				episodeDetails += ' (' + options.runningTime + ')';
			}

			if (episodeDetails) {
				title += '<em>' + episodeDetails + '</em>';
			}

			var fullEpisodeUrl = options.fullEpisodeUrl;
			var fullEpisodeElm = playerElm.select('.watch')[0];
			if (fullEpisodeElm) {
				if (fullEpisodeUrl) {
					fullEpisodeElm.writeAttribute('href', fullEpisodeUrl);
					fullEpisodeElm.setStyle({display: 'inline'});
				}
				else {
					fullEpisodeElm.setStyle({display: 'none'});
				}
			}

			playerElm.select('h3')[0].innerHTML = title;
			playerElm.select('p')[0].innerHTML = options.description;
		},

		/**
		 * Close the player
		 */
		closePlayer: function() {
			// get the active tab
			var tabElm = _baseElm.select('ul li.active')[0];
			var panelElm = (tabElm)
				? TabbedPanel.getPanelForTab(tabElm)
				: _defaultPanel;

			TabbedPanel.activatePanel(panelElm);

			// clear the video from the player
			Player.clear(_playerInst);
		},

		/**
		 * Indicate the component has started loading
		 */
		enterLoadingState: function() {
			_baseElm.addClassName('loading');
		},

		/**
		 * Indicate the component has finished loading
		 */
		exitLoadingState: function() {
			_baseElm.removeClassName('loading');
		},

		/**
		 * Handle a mouseover on one of the panels
		 * @param {DOMevent} event
		 */
		handleMouseOver: function(event) {
			var source = event.element();
			var sourcePath = source.ancestors();

			// get the item that the event occurred in
			var itemElm = source.up('.playlist li');
			if (!itemElm) {
				if (source.match('.playlist li')) {
					_baseElm.select('.playlist .hover')
						.each(function(elm) {
								  elm.removeClassName('hover');
							  });
				}
				return;
			}

			// ignore if the item is aleady hovered
			if (itemElm.hasClassName('hover')) {
				return;
			}

			var thumbElm = itemElm.select('img')[0].parentNode;
			var watchElm = itemElm.select('.watch')[0];

			// tell if the title or the info is hovered
			if (source == thumbElm || sourcePath.indexOf(thumbElm) != -1
			   || source == watchElm || sourcePath.indexOf(watchElm) != -1) {
				itemElm.addClassName('hover');
			}
		},

		/**
		 * Handle a mouseout on one of the panels
		 * @param {DOMevent} event
		 */
		handleMouseOut: function(event) {
			var source = event.element();

			// get the grid cell that the event occurred in
			var itemElm = source.up('.playlist li');
			if (!itemElm) {
				return;
			}

			// get the element the mouse ended up at
			var target = event.relatedTarget || event.toElement;

			// work out if the mouseout was FROM title/info TO something else
			var thumbElm = itemElm.select('img')[0].parentNode;
			var watchElm = itemElm.select('.watch')[0];

			if (target) {
				if (target == thumbElm || target.descendantOf(thumbElm)
					|| target == watchElm || target.descendantOf(watchElm)) {
					return;
				}
				else {
					itemElm.removeClassName('hover');
				}
			}
		},

		/**
		 * Handle a click on the clips panel
		 * @param {DOMevent} event
		 */
		handleClick: function(event) {
			var target = event.element();

			var itemElm = target.match('.playlist li')
				? target
				: target.up('.playlist li');

			// click somewhere on a playlist item
			if (itemElm) {
				var thumbElm = itemElm.select('img')[0].parentNode;
				var watchElm = itemElm.select('.watch')[0];

				// clicking on any of these elements is the same as
				// clicking "watch"
				if (target == thumbElm || target.descendantOf(thumbElm)
					|| target == watchElm || target.descendantOf(watchElm)) {

					event.stop();
					self.doWatchClipClick(watchElm);
				}
			}
		}
	};

	return self;
}();

/** SOURCE:  _ui/js/utils.js  **/

/**
 * From fox v2.0
 */

var FoxUtils = {
	/**
	 * function to get cookie values
	 **/
	getCookie: function(cookieName)
	{
		if (document.cookie.length>0)
		{
			cookieStart=document.cookie.indexOf(cookieName + "=");
			if (cookieStart!=-1)
			{
				cookieStart=cookieStart + cookieName.length+1;
				cookieEnd=document.cookie.indexOf(";",cookieStart);
				if (cookieEnd==-1) cookieEnd=document.cookie.length;

				return unescape(document.cookie.substring(cookieStart,cookieEnd));
			}
		}
		return "";
	},

	clearCookie: function(cookieName) {
		FoxUtils.setCookie(cookieName, '');
	},

	setCookie: function(cookieName, value) {
		document.cookie = cookieName+'='+value;
	}
};


