/**
 * Creates a fully featured video player with controls and monitoring in DHTML, connected to a specific HTML node set
 * @class Represents a video player with complete UI/controls. Behaviour is adopted to the TV 2 Sumo requirements.
 * @constructor
 * @param {int} index A number matching the HTML nodes' ID endings. For example, if the Media Player object has 'video123' as ID, index should be 123
 * @param {boolean} miniOnly Optional parameter. When set to true, the player behaviour will be modified for use as thumbnail/secondary player only
 * @param {boolean} delayedAttach Will not attach to HTML DOM element before "use", i.e. video start
 * @param {object} callbacks An optional object whose properties are pointers to callback functions. Zero or more of these callbacks can be attached: onSwitchClick, onSelectorClick, onMouseOver, onMouseOut, onVideoEnd, onHide
 * @author Tor Erik Alræk
 * @version 3.0
 */

function HtmlPlayer(index,miniOnly,delayedAttach,callbacks) {
	// callbacks = {
	// 		onSwitchClick, - when user presses the switch button (if any)
	// 		onSelectorClick, - when user presses the select button (if any)
	// 		onMouseOver, - when user mouses over the video area
	// 		onMouseOut,
	// 		onVideoEnd, - when video stops by itself (program finished or live stream ended)
	// 		onHide, - when the hide method is called
	// 		onItemChange, - when video position is moving 
	// 		onError, - when user clicks for error details
	//		onProgramError, - when ASX reports a problem with showing the program
	// 		onQualityClick, - when user clicks for quality details settings
	// 		onReplay, - when users presses play again after having stopped the stream (live only)
	// 		playerContainer, - html target element for reloading the player when onReplay event fires
	// 		onMuteChange, - when user presses the mute button
	// 		onPlayerClick - when user clicks in the video area
	//    getStartAd
	//    getEndAd
	//    getAds
	
	
	//		*** flyttet til start() onShowExternalAd - when an ad outside the player is going to be shown
	//  }
	// CONSTRUCTOR
  if (index == null) index = 0;
  var ie = navigator.userAgent.indexOf("MSIE")>=0 && navigator.userAgent.indexOf("Opera")<0 && navigator.userAgent.indexOf("Mac")<0;
  // Gecko also includes Safari
  var geckoWin = navigator.userAgent.indexOf("Gecko")>=0 && navigator.userAgent.indexOf("Opera")<0 && navigator.userAgent.indexOf("Win")>0;
	this.ie = ie;
  var bufferingStarted; // first time buffering
  var buffering = false; // buffering in general
  var videoStarted;
  var sliderRemapped;
  var slider;
  var progressBar;
  var lastDur;
  var lastPos=0;
  var sliderEvtsEnabled;
  var dragging;
  var live;
  var items;
  var started = false;
  var hFallbackTimer;
  var delayedProps;
  var waitForAd = false;
  var endAdPos = '';
  var endAdCode = '';
  var adCategory = '';
  var programTitle = '';
  var currentItem;
  var nedPlayer;
  var springStreamsStream;
  var currentStreamItem; // SpringStreams
  var cancelled = false;
  var wmpErrorMsg = '';
  var wmpErrorCode = '';
  var prevVolume = '90';
  var mutedFromButton = false;
  var startAsMuted = false;
  var hideAd;
	var fullyFeaturedCorePlayer = false;
	var upgradeablePlayer = false;


  var me = this;
  
  var vp = null;
  this.vp = vp;  
  
  var qualityStatusShowing = false;
  var prevStatusMsg;
  
  var fallbackDelay1 = 5000;  // max loading time for start ad
  var fallbackDelay2 = 16000; // max time for showing start ad before video starts buffering
  var fallbackDelay3 = 16000; // max time for showing start ad during buffering
  var fallbackDelay4 = 60000; // max time for showing end ad
  
  var adSession = Math.round(Math.random()*10000);
  
  var bufferFlashEmbed = '<OBJECT id="bufferAnimation" codebase="http://active.macromedia.com/flash/cabs/swflash.cab#version=8,0,0,0" type="application/x-shockwave-flash" data="{flashPath}"><PARAM name="movie" value="{flashPath}"><PARAM name="quality" value="high"><PARAM name="wmode" value="transparent"></OBJECT><div id="bufferAd">{startAd}</div><div id="emptyBufferAd"></div>';

  var titleTimerId;
  var currentProgId;
  
  var skipForwardLong = 60; // seconds
  var skipBackLong = -30; // seconds

  var skipForwardShort = 15; // seconds
  var skipBackShort = -7.5; // seconds

	var log = new Object();
	try {
		log.debug = logDebug;
		log.error = logError;
	} catch(e) {
		log.error = function(source,e) {
			if (location.href.indexOf('debug=true')>0) alert(source + ': ' + e.message);	
		}	
		log.debug = function(source,message) {
			if (location.href.indexOf('debug=true')>0 && $('debugList')) {
				$('debugList').innerHTML = $('debugList').innerHTML + '<li>' + source + ': ' + message + '</li>';	
			}
		}
	}

	// Method required in constructor
	this.attach = function() {
	  try {
 			var wmpGeckoPluginAvailable = false;

			// Decide which player implementation to use
 			if (geckoWin && navigator.mimeTypes) {
 				var x = '';
 				for (var i=0; i<navigator.mimeTypes.length; i++ ) {
					var n = navigator.mimeTypes[i].type;
					if (n != null && n.indexOf("x-ms-wmp")>=0) {
						wmpGeckoPluginAvailable = true;
					}
				}
			}
	  	if (ie || wmpGeckoPluginAvailable) {
	  		fullyFeaturedCorePlayer = true;
				if (geckoWin)	vp = new WmvGeckoCorePlayer('video' + index,log)
				else vp = new WmvIECorePlayer('video' + index,log);					
			} else {
				fullyFeaturedCorePlayer = false;
				vp = new WmvSimpleCorePlayer('video' + index,log);
				if (geckoWin) {
					upgradeablePlayer = true;
				}
			}
			

			if (fullyFeaturedCorePlayer) {
		 		// vp buttons				
				Event.observe('playButton' + index,'click',function(e) {if (!(Element.visible('videoAdEnd' + index) || Element.visible('videoAdStart' + index))){if (videoStarted && callbacks && callbacks.onReplay && !me.isPlaying()){ callbacks.onReplay(index,callbacks.playerContainer,currentProgId);} else {vp.togglePlay();}}	Event.stop(e);}, false);
				Event.observe('volDownButton' + index,'click',function(e) {vp.volumeDown(10); Element.addClassName($('muteButton' + index),'soundButton'); Element.removeClassName($('muteButton' + index),'muteButton'); Event.stop(e);}, false);
				Event.observe('volUpButton' + index,'click',function(e) {vp.volumeUp(10); Element.addClassName($('muteButton' + index),'soundButton'); Element.removeClassName($('muteButton' + index),'muteButton'); Event.stop(e);}, false);	
				Event.observe('muteButton' + index,'click',function(e) {muteChange(); Event.stop(e);}, false);
				Event.observe('prevButton' + index,'click',function(e) {skipBack(); Event.stop(e);}, false);
				Event.observe('nextButton' + index,'click',function(e) {skipForward(); Event.stop(e);}, false);
				Event.observe('toolsButton' + index,'click',function(e) {toggleQualityStatus(); Event.stop(e);}, false);
				Event.observe('fullscreenButton' + index,'click',function(e) {vp.gotoFullscreen();	Event.stop(e);}, false);
				Event.observe('sliderButton' + index,'mousedown',function(){startDrag();}, true);
				Event.observe('sliderButton' + index,'mouseup',function(){endDrag();}, true);		
				//Event.observe('slider' + index,'mouseout',function(){endDrag();}, true);
		
				// vp events
				vp.onStatusChange = statusChange;
				vp.onBufferingStart = bufferingStart;
				vp.onPosChange = posChange;
				vp.onVideoStart = videoStart;
				vp.onVideoEnd = videoEnd;
				vp.onVideoChange = videoChange;
				vp.onPlayStateChange = playStateChange;
				vp.onCeased = me.detach;
				if (callbacks && callbacks.onPlayerClick)
					vp.onPlayerClick = callbacks.onPlayerClick
				else if (!miniOnly) {
					vp.onPlayerClick = playerClicked;
				}
				Event.observe(window,'unload',function(){vp.stop(true);},false);
			} else {
				Element.addClassName('videoPanel' + index,'controlsBuiltIn');
				
				Event.observe('playButton' + index,'click',function(e) {Event.stop(e);}, false);
				Event.observe('volDownButton' + index,'click',function(e) {Event.stop(e);}, false);
				Event.observe('volUpButton' + index,'click',function(e) {Event.stop(e);}, false);	
				Event.observe('muteButton' + index,'click',function(e) {Event.stop(e);}, false);
				Event.observe('fullscreenButton' + index,'click',function(e) {Event.stop(e);}, false);
			}


			// the following buttons are not dependant on IE-WMP:
			if ($('miniMaxiButton' + index)) Event.observe('miniMaxiButton' + index,'click',toggleMiniMaxi,false);
			Event.observe('stopButton' + index,'click',function(e) {me.hide(); Event.stop(e);}, false);

			if ($('switchToButton' + index)) {
			  if (callbacks && callbacks.onSwitchClick) {
					Event.observe('switchToButton' + index,'click',function(e) {callbacks.onSwitchClick(index,getPlayerContainer(),currentProgId,me.getPos()); Event.stop(e);}, false);
				} else {
					Event.observe('switchToButton' + index,'click',function(e) {Event.stop(e);}, false);
				}
			}

			if ($('selectorButton' + index)) {
				if (callbacks && callbacks.onSelectorClick) {
					Event.observe('selectorButton' + index,'click',function(e) {toggleSelectorButton(callbacks.onSelectorClick(index)) ; Event.stop(e);}, false);
				} else {
					Event.observe('selectorButton' + index,'click',function(e) {Event.stop(e);}, false);
				}
			}
		
			if (callbacks && callbacks.onMouseOver) {
				Event.observe('bg' + index,'mouseover',function(e) {callbacks.onMouseOver(index); Event.stop(e);}, false);
				Event.observe('video' + index,'mouseover',function(e) {callbacks.onMouseOver(index); Event.stop(e);}, false);
			}

			if (callbacks && callbacks.onMouseOut) {
				Event.observe('bg' + index,'mouseout',function(e) {callbacks.onMouseOut(index); Event.stop(e);}, false);
				Event.observe('video' + index,'mouseout',function(e) {callbacks.onMouseOut(index); Event.stop(e);}, false);
			}
	  } catch (e) {
	  	log.error('HtmlPlayer.attach',e);
	  }
	}

	// Constructor continues  
  if (!delayedAttach) {
  	me.attach();
  }

	// PUBLIC METHODS

/**
 * Starts video with ads before/after, if exists
 * @param {object} props details of the media being started, and for ads. The startVideo parameters are equivalent to the members of props, but there are three additions
 * @param {string} adCategory props.adCategory is the category used as parameter when getting ads from the ad system
 * @param {string} startAdPos props.startAdPos is the identifier for the ad to be showed before video starts
 * @param {string} endAdPos props.endAdPos is the identifier for the ad to be showed after video ends
 */

/* ny reklameløsning

					if (props.startAdPos && props.startAdPos.length > 0 && (callbacks.getStartAd || callbacks.getAds)) { // has start ad
			  		var bufferWaitCleared = false;
			  		delayedProps = {'metaUrl': props.metaUrl, 'progId': props.progId, 'hostProgId': props.hostProgId, 'live': props.live, 'timeBegin': props.timeBegin, 'startPos': props.startPos};
						
						if (callbacks.getStartAd) {
							callbacks.getStartAd(props.adCategory,[props.startAdPos,props.bannerAdPos], function(ads,waitForAd) {
									if (ads) {
			              log.debug('HtmlPlayer.start','getAds finished');
			              if (!started) {
			              
			              
			              }
									
									}
							
								});
						}
						
*/

	this.start = function(props) {
		if (upgradeablePlayer && !miniOnly) {
			var upgradeMessage = document.createElement('p');
			upgradeMessage.className = 'upgradeMessage';
			upgradeMessage.innerHTML = $MR('sumo.web.video.upgrademessage');
			$('videoPanel'+index).appendChild(upgradeMessage);
			Element.addClassName('videoPanel' + index,'upgradeable');
		}
		try {
			// metaUrl,progId,hostProgId,live,timeBegin,startPos,adCategory,startAdPos,endAdPos,bannerAdPos,bannerAdElement,programTitle,items,bufferFlash,bufferContent
			me.hideStartAd();
			me.hideEndAd(false);
			if (props.live == null) props.live = false;
			changeControls({'live': props.live});
			this.reset(true);
			if (!cancelled) {
				started = false;
				live = props.live;
				currentProgId = props.progId;
				programTitle = props.programTitle == null ? '' : props.programTitle;
				items = props.items;
				if (props.adCategory && props.adCategory.length > 0 && fullyFeaturedCorePlayer) { // has ads
					if (props.endAdPos && props.endAdPos.length > 0) { // has end ad
						endAdPos = props.endAdPos;
						adCategory = props.adCategory;
					}
					if (false && (props.startAdPos && props.startAdPos.length > 0)) { // has start ad
			  		var bufferWaitCleared = false;
			  		delayedProps = {'metaUrl': props.metaUrl, 'progId': props.progId, 'hostProgId': props.hostProgId, 'live': props.live, 'timeBegin': props.timeBegin, 'startPos': props.startPos};
						log.debug('HtmlPlayer.start','Retrieving start ad');
						
						getAds(props.adCategory,[props.startAdPos,props.endAdPos,props.bannerAdPos],null, function(ads) {
								if (ads) {
									endAdCode = ads[props.endAdPos];
		              log.debug('HtmlPlayer.start','getAds finished');
		              if (!started) {
										if (ads[props.startAdPos].toLowerCase().indexOf('enablewaitforad')>0 && getFlashVersion() >=8) {
									  	showStartAd(ads[props.startAdPos]);
											log.debug('HtmlPlayer.start','Start ad retrieved, enableWaitForAd found - waiting for startVideo() from ad');
											waitForAd = true;
									  	try {
		                    window.clearTimeout(hFallbackTimer);
		                    bufferWaitCleared = true;
									  	} catch(e) {}
								  		hFallbackTimer = window.setTimeout(me.startVideo,fallbackDelay2);
										} else {
											log.debug('HtmlPlayer.start','Start ad retrieved (or failed), enableWaitForAd not found');
											if (props.bufferFlash) {
												var bufferAd = bufferFlashEmbed.replace(/{flashPath}/ig,props.bufferFlash).replace(/{startAd}/ig,ads[props.startAdPos]);
											
												showStartAd(bufferAd);
											}
		                  me.startVideo();
										}
										if (props.bannerAdPos) {
											var bannerAdCode = ads[props.bannerAdPos];
											if (bannerAdCode && bannerAdCode.length>0) { 
												if (props.onShowExternalAd) {
													props.onShowExternalAd(bannerAdCode);
												} else if ($(props.bannerAdElement)) {
													$(props.bannerAdElement).innerHTML = bannerAdCode;
												}
											}
										}
									}
								} else {
									me.startVideo(props.metaUrl,props.progId,props.hostProgId,props.live,props.timeBegin,props.startPos,props.programTitle,props.paused,props.muted);
								}
	            });
			  		if (!bufferWaitCleared) hFallbackTimer = window.setTimeout(me.startVideo,fallbackDelay1);
					} else { // start video immediately
						log.debug('HtmlPlayer.start','No start ad, starting immediately.');	
						if (props.bufferFlash && props.bufferFlash.length > 0  && fullyFeaturedCorePlayer) {
							showStartAd(bufferFlashEmbed.replace(/{flashPath}/ig,props.bufferFlash).replace(/{startAd}/ig,''));
						} else if (props.bufferContent && props.bufferContent.length>0 && fullyFeaturedCorePlayer) {
							showStartAd(props.bufferContent);
						}
						me.startVideo(props.metaUrl,props.progId,props.hostProgId,props.live,props.timeBegin,props.startPos,props.programTitle,props.paused,props.muted);
					}
				} else {
					log.debug('HtmlPlayer.start','No ads, starting immediately.');
	
					if (props.bufferFlash && props.bufferFlash.length > 0  && fullyFeaturedCorePlayer) {
						showStartAd(bufferFlashEmbed.replace(/{flashPath}/ig,props.bufferFlash).replace(/{startAd}/ig,''));
					} else if (props.bufferContent && props.bufferContent.length>0 && fullyFeaturedCorePlayer) {
						showStartAd(props.bufferContent);
					}
					this.startVideo(props.metaUrl,props.progId,props.hostProgId,props.live,props.timeBegin,props.startPos,props.programTitle,props.paused,props.muted);
				}
			}
		} catch(e) {
			log.error('HtmlPlayer.start',e);
		}
	}

/**
 * Stops playback of video or ads, and resets all variables related to the program/video being played
 */

	this.reset = function() {
		try {
	  	if (vp) {
	  		vp.stop();
		  	if ($('video' + index)) $('video' + index).style.visibility = 'hidden';
	  	}
	  	bufferingStarted = false;
	  	buffering = false;
	  	videoStarted = false;
	  	sliderRemapped = false;
	  	lastDur = 0;
	  	lastPos = 0;
	  	sliderEvtsEnabled = true;
	  	dragging = false;
	  	live = false;
	  	started = false;
	  	waitForAd = false;
	  	endAdPos = '';
			endAdCode = '';
	  	adCategory = '';
	  	adSession = Math.round(Math.random()*10000000);
	  	currentProgId = 0;
	  	programTitle = '';
	  	delayedProps = null;
	  	items = null;
	  	currentItem = null;
	  	currentStreamItem = null; //SpringStreams
	  	cancelled = false;
  		wmpErrorMsg = '';
  		wmpErrorCode = '';
  		qualityStatusShowing = false;
	  	prevStatusMsg = null;
	  	mutedFromButton = false;
	  	startAsMuted = false;
	  	hideAd = null;

	  	if (slider) {
	  		slider.setValue(0);  		
	  	}
	  	if (progressBar) {
	  		progressBar.setValue(0);
	  	}
	  	if ($('playerStatus' + index))
		  	$('playerStatus' + index).innerHTML = '';
	  	if ($('timeElapsed' + index))
		  	$('timeElapsed' + index).innerHTML = '';
	  	if ($('duration' + index))
		  	$('duration' + index).innerHTML = '';  	

			if ($('muteButton' + index)) {
				Element.addClassName($('muteButton' + index),'soundButton');
				Element.removeClassName($('muteButton' + index),'muteButton');
			}
	  	
	  	try {
	  		window.clearTimeout(hFallbackTimer);
	  	} catch(e) {}
	  	try {
	  		window.clearTimeout(titleTimerId);
	  	} catch(e) {}
		} catch(e) {log.error('HtmlPlayer.reset',e)}	  	
	}

	this.detach = function() {
		try {
			log.debug('HtmlPlayer','Player ' + index + ' going to sleep.');
			if (vp && vp.reset) vp.reset();
			me.reset();
			vp = null;
	  } catch(e) {log.error('HtmlPlayer.detach',e)}
	}

/**
 * Starts video playback immediately, without ads
 * @param {string} metaUrl The url pointing to the media file, to be used by Windows Media Player. If omitted, parameters from start method are used instead.
 * @param {int} progId the video program's id, used for identifying current stream, or to get correct stream in live programs
 * @param {int} hostProgId optional, as props.progId, but used for sub programs having a real host program
 * @param {boolean} live indicates that stream is live if included, and set to true
 * @param {int} timeBegin not used
 * @param {int} startPos when starting playback, jumps playing position to the number of seconds from the start. startPos is optional.
 * @param {string} programTitle the title of the program, to be showed in the status area.
 */


	this.startVideo = function(metaUrl,progId,hostProgId,pLive,timeBegin,startPos,pProgramTitle,paused,muted) {
		try {
  		window.clearTimeout(hFallbackTimer);
  	} catch(e) {}
  	startAsMuted = muted;
  	if (metaUrl && metaUrl.length>16) {
  		//this.reset();
	  	live = pLive;
	  	programTitle = pProgramTitle == null ? '' : pProgramTitle;
	  	currentProgId = progId; 
	  	if(live == null) live = false;

	  	if (waitForAd) {
	  		delayedProps = {'metaUrl': metaUrl, 'progId': progId, 'hostProgId': hostProgId, 'live': live, 'timeBegin': timeBegin, 'startPos': startPos};
	  		hFallbackTimer = window.setTimeout(me.startVideo,fallbackDelay2);
	  	} else {
				vp.start(metaUrl,progId,hostProgId,live,timeBegin,startPos,paused);
				if (miniOnly || muted)
					vp.mute();
				hFallbackTimer = window.setTimeout(me.hideStartAd,fallbackDelay3);
				started = true;
				waitForAd = false;
				delayedProps = null;
	  	}

	  } else { // delayed start, using delayedProps
			if (delayedProps && !started) {
	  		vp.start(delayedProps.metaUrl, delayedProps.progId, delayedProps.hostProgId, delayedProps.live, delayedProps.timeBegin, delayedProps.startPos, delayedProps.paused);
			if (miniOnly || muted)
				vp.mute();
			hFallbackTimer = window.setTimeout(me.hideStartAd,fallbackDelay3);
	  		started=true;
				waitForAd = false;
	  		delayedProps = null;
	  	}
	  }
	}

/**
 * Shows the player in the large design. 
 */

  this.showMaxi = function() {
 	try { 
 		Element.removeClassName($('videoPanel' + index),'mini');
 		Element.removeClassName($('videoPanel' + index),'maxi');
 	} catch (e) {}
 	Element.addClassName($('videoPanel' + index),'maxi');
  	$('videoPanel' + index).style.display = 'block';  
  }

/**
 * Shows the player in the small design.
 */
  this.showMini = function() {
	 	//if (ie) {
	 		try { 
		 		Element.removeClassName($('videoPanel' + index),'mini');
		 		Element.removeClassName($('videoPanel' + index),'maxi');
		 	} catch (e) {}
	 		Element.addClassName($('videoPanel' + index),'mini');	
	  	$('videoPanel' + index).style.display = 'block';
	  //} else {
		//	stopNonIE();
	  //}
  }

/**
 * Toggles the player's design between small and large version
 */
	this.toggleSize = function () {
		toggleMiniMaxi();
	}

/**
 * Reveals a hidden video player
 * @param {boolean} videoOnly if the optional parameter is set to true, only the video area will be revealed, assuming the controls and panels is not hidden.
 * @see #hide
 */
  this.show = function(videoOnly) {
	  	if(videoOnly) {
		  	$('video' + index).style.display = 'block';
		} else {
		  	$('videoPanel' + index).style.display = 'block';
		}
  }

/**
 * Hides the video player
 * @param {boolean} videoOnly if the optional parameter is set to true, only the video area will be hidden, leaving the controls and panels visible.
 * @see #show
 */
  this.hide = function(videoOnly) {
  	if ($('video' + index)) {
	  	if (true || ie) {
	  		try {
			  	if(videoOnly) {
				  	$('video' + index).style.display = 'none';
					} else {
				  	me.hideEndAd(false);
				  	me.hideStartAd();
					  try {vp.stop(true);} catch(e) {};
						$('videoPanel' + index).style.display = 'none';
				  	try {
				  		window.clearTimeout(hFallbackTimer);
				  	} catch(e) {}
				  	try {
				  		window.clearTimeout(titleTimerId);
				  	} catch(e) {}
						if (callbacks && callbacks.onHide) {
							callbacks.onHide();
				  	}
					}
				} catch(e) {
					log.error('HtmlPlayer.hide',e);
				}
	  	} else {
				stopNonIE();
		  	$('videoPanel' + index).style.display = 'none';
		  	try {
		  		window.clearTimeout(hFallbackTimer);
		  	} catch(e) {}
		  	try {
		  		window.clearTimeout(titleTimerId);
		  	} catch(e) {}
				if (callbacks && callbacks.onHide) {
					callbacks.onHide();				  		
				}
			}
  	}
  }

/**
 * Gets the playing state of the video player.
 * @return state, set to true when video is playing, otherwise false 
 * @type boolean
 */
  this.isPlaying = function() {
  	try {
  		if (vp)
  			return vp.isPlaying();
  		else
  			return false;
  	} catch(e) {
  		return false;
  	}
  }

/**
 * Gets the playing state of the video player.
 * @return state, set to true when video is playing, otherwise false 
 * @type boolean
 */
  this.isLive = function() {
  	return live;
  }


/**
 * Gets the mute state of the video player.
 * @return mute setting, set to true when video is muted, otherwise false 
 * @type boolean
 */
  this.isMuted = function(fromButton) {
  	try {
  		if (fromButton) {
 				return mutedFromButton; 				
  		} else
	  		return vp.isMuted();
  	} catch(e) {
  		return false;
  	}
  }

/**
 * Sets the sound volume of the video player.
 * @param {int} level a number between 0 and 100
 */
  this.setVolume = function(level) {
	vp.setVolume(level);
  }

/**
 * Gets the sound volume of the video player.
 * @return a number between 0 and 100
 * @type int
 */
  this.getVolume = function() {
	return vp.getVolume();
  }

/**
 * Mutes the video player.
 */
  this.mute = function() {
		Element.addClassName($('muteButton' + index),'muteButton');
		Element.removeClassName($('muteButton' + index),'soundButton');
		return vp.mute();
	 }

/**
 * Unmutes the video player.
 */
  this.unmute = function() {
		vp.unmute();
		Element.addClassName($('muteButton' + index),'soundButton');
		Element.removeClassName($('muteButton' + index),'muteButton');
  }

/**
 * Go to fullscreen mode
 */
  this.gotoFullscreen = function() {
		vp.gotoFullscreen();
  }



/**
 * Moves playback to the specified position
 * @param {int} pos number of seconds from the beginning of the program
 *  */
  this.gotoPos = function(pos) {
		if (!live) {
			vp.gotoPos(pos);
		}
  }

/**
 * Gets the playback position (time)
 * @return pos number of seconds from the beginning of the program
 * @type int
 **/
  this.getPos = function(pos) {
		if (vp) return vp.getPos()
		else return 0;
  }

/**
 * Gets the ID of the currently opened program
 * @return progId
 * @type int
 */
  this.getProgId = function() {
  	return currentProgId;
  }


/**
 * Gets the ID of the currently playing item
 * @return itemId
 * @type int
 */
  this.getCurrentItemId = function() {
  	if (currentItem && currentItem.id !=0) {
  		return currentItem.id;  		
  	}
  }

/*
	this.enableAdWait = function() {
		waitForAd = true;
	}
	
	this.setStartAd = function(htmlCode) {
		startAdCode = htmlCode;
	}

	this.setEndAd = function(htmlCode) {
		endAdCode = htmlCode;
	}
*/

/**
 * Hides the ad shown before the video starts, if any
 */
	this.hideStartAd = function() {
		$('video' + index).style.visibility = 'visible';
		//log.debug('HtmlPlayer.hideStartAd','started');
		$('videoAdStart' + index).style.display = 'none';

		if ($('videoAdStart' + index).innerHTML.length >0) 
			window.setTimeout(function(){$('videoAdStart' + index).innerHTML = '';},10); // avoid extra load from flash object when playing video(s)
		//log.debug('HtmlPlayer.hideStartAd',$('videoAdStart' + index).innerHTML);
		try {
			window.clearTimeout(hFallbackTimer);
		} catch(e) {}

	}
	
/**
 * Hides the ad shown after the video ends
 */
	this.hideEndAd = function(hideAll) {
		//log.debug('HtmlPlayer.hideEndAd','started');

		if (hideAll==true || hideAll==null) {
			me.hide();
		}
		$('videoAdEnd' + index).innerHTML = ''; // avoid extra load from flash object when playing video(s)
		$('videoAdEnd' + index).style.display = 'none';
		//log.debug('HtmlPlayer.hideEndAd',$('videoAdEnd' + index).innerHTML);
	}

/**
 * @ignore
 */
	this.hideToMini = function() {
		me.hideEndAd();
		try {
			window.clearTimeout(hFallbackTimer);
		} catch(e) {}
		me.showMini();
	}

/**
 * To be called when ad is finished
 */
	this.adEnded = function() { 
		if (hideAd) {
      hideAd();
    }
    hideAd = null;
  }

	// PRIVATE METHODS, INCLUDING EVENT HANDLERS

	function bufferingStart() {
		bufferingStarted = true;
	}
	
	function playStateChange(newState, oldState) {
		try {
			if (newState < 3 || newState == 8 || newState == 10 ) {
				Element.addClassName($('playButton' + index),'playButton'); 
				Element.removeClassName($('playButton' + index),'pauseButton');
				Element.removeClassName($('playButton' + index),'stopButton'); 
			} else {
				if (false && live) {
					Element.addClassName($('playButton' + index),'stopButton');
					Element.removeClassName($('playButton' + index),'pauseButton'); 
					Element.removeClassName($('playButton' + index),'playButton');	
				} else {
					Element.addClassName($('playButton' + index),'pauseButton');
					Element.removeClassName($('playButton' + index),'stopButton'); 
					Element.removeClassName($('playButton' + index),'playButton');
				}
			}
			if (newState == 6) {
				buffering = true;
				changeControls({'buffering': true});
			} else {
				buffering = false;
				changeControls({'buffering': false});
			}
	

		} catch(e) {
			log.debug('HtmlPlayer.playStateChange',e);
		}
	}

	function playerClicked(button) {
		vp.gotoFullscreen();
	}
	
	function videoStart() {
		me.hideStartAd();
		videoStarted = true;
		if (miniOnly || startAsMuted) {
			me.setVolume(prevVolume);
			me.mute();
		}
		//showStatus($MR('sumo.general.video.status.startingvideo') + programTitle);
	}
	
	function videoEnd() {
		if (endAdCode.length > 0 && endAdCode.toLowerCase().indexOf('enablewaitforad')>0) {
			log.debug('HtmlPlayer.start','Retrieving end ad');
			showEndAd();
			//getAd(adCategory,endAdPos,'videoAdEnd' + index, showEndAd);
		}	else {
			if (!miniOnly)
			  me.hide();
		}
		if (callbacks && callbacks.onVideoEnd) {
			var itemId;
			if (currentItem != null) itemId = currentItem.id;
			callbacks.onVideoEnd(index,currentProgId,itemId);
		}
	}
	
	function videoChange(title,entryType,media,url) {
		var item;
		if (media.getItemInfo('DartAdId')) {
			if (!currentStreamItem) {
				item = {'ct': 'ads/preroll',
						'stream': media.sourceURL };
						
			} else {
				item = {'ct': 'ads/postroll',
						'stream': media.sourceURL };
				
			}
		} else if (media.getItemInfo('progId')) {
			item = {'ct': 'content',
					'stream': '/TV2stream/Sumo_OnDemand' + formatAnalyticsString(media.name) };
			switch (media.getItemInfo('progId') + '') {
				case '82015':
					item['stream'] = '/TV2stream/Sumo_Live/TV_2';
				break;
				case '82016':
					item['stream'] = '/TV2stream/Sumo_Live/Zebra';
				break;
				case '82017':
					item['stream'] = '/TV2stream/Sumo_Live/Filmkanalen';
				break;
				case '82018':
					item['stream'] = '/TV2stream/Sumo_Live/Nyhetskanalen';
				break;
				case '303655':
					item['stream'] = '/TV2stream/Sumo_Live/Science_Fiction';
				break;
				case '324298':
					item['stream'] = '/TV2stream/Sumo_Live/TV_2_HD_beta';
				break;
				case '115210':
					item['stream'] = '/TV2stream/Sumo_Live/TV_2_Sport';
				break;
			}
		} else {
			//warn('Neither progId or DartAdId present after stream change');
		}
		currentStreamItem = item;
		
		if (window.springSensors && item) {
			if (springStreamsStream) {
				springStreamsStream.stop();
			}
			springStreamsStream = window.springSensors.track($('video' + index),currentStreamItem);
		}
		
		if (item)
			log.debug(item.ct + ': ' + item.stream);
	}
	
	
	function formatAnalyticsString(s) {
		var sep = "/";
		if (s.length > 0)
			return sep + s.replace(/[^a-zA-Z0-9]+/g,'_').replace(/^_|_$/,'');
		else
			return "";
	}
	
	function changeControls(options) {
		if (options.live!=null) {
			if (options.live) {
		    //log.debug('HtmlPlayer.changeControls','Setting controls to live mode');
		  	Element.addClassName('videoControls' + index,'live');
		  } else {
		    //log.debug('HtmlPlayer.changeControls','Setting controls to on demand mode');
		  	Element.removeClassName('videoControls' + index,'live');
		  }
		}
		if (options.buffering!=null) {
			if (options.buffering) {
		    //log.debug('HtmlPlayer.changeControls','Setting controls to buffering mode');
		  	Element.addClassName('videoControls' + index,'buffering');
		  } else {
		  	Element.removeClassName('videoControls' + index,'buffering');
		  }
		}
		if (options.statusMsg !=null) {
			if (options.statusMsg) {
		  	Element.addClassName('videoControls' + index,'statusMsg');
		  } else {
		  	Element.removeClassName('videoControls' + index,'statusMsg');
		  }
		}
	}

	function showStatus(message) {
		$('playerStatus' + index).innerHTML = message;
		$('playerStatus' + index).title = message;
		changeControls({'statusMsg': true});
	}

	function hideStatus() {
		if (Element.hasClassName('videoControls' + index,'statusMsg')) {
			changeControls({'statusMsg': false});
		}
	}

	function toggleQualityStatus() {
		if (qualityStatusShowing) {
			if (prevStatusMsg) {
				showStatus(prevStatusMsg);
			} else {
				changeControls({'statusMsg': false});
			}
			prevStatusMsg = null;
			window.clearTimeout(titleTimerId);
			qualityStatusShowing = false;
			Event.stopObserving('playerStatus'+index,'click',qualityClick,false);
		} else {
			var bitrate = vp.getBitrate();
			if (bitrate!=null) {
				qualityStatusShowing = true;
				if ($('videoControls' + index).className.indexOf('statusMsg')>=0) prevStatusMsg = $('playerStatus' + index).innerHTML;		
				if (callbacks && callbacks.onQualityClick)
					showStatus($MR('sumo.web.video.status.quality1') + Math.round(bitrate/10000)*10 + $MR('sumo.web.video.status.quality2'));
				else
					showStatus($MR('sumo.web.video.status.quality1') + Math.round(bitrate/10000)*10 + $MR('sumo.web.video.status.quality2.noDetails'));
				Event.observe($('playerStatus'+index),'click',qualityClick,false);
				$('playerStatus'+index).style.cursor = 'pointer';
				titleTimerId = window.setTimeout(toggleQualityStatus,10000);
			}
		}
	}

	function qualityClick() {
		if (callbacks && callbacks.onQualityClick) {
			callbacks.onQualityClick();
		}
		Event.stopObserving('playerStatus'+index,'click',qualityClick,false);
		$('playerStatus'+index).style.cursor = 'default';
	}



	function muteChange() {
		if(vp.toggleMute()) {
			Element.addClassName($('muteButton' + index),'muteButton');
			Element.removeClassName($('muteButton' + index),'soundButton');
			mutedFromButton = true;
			if (callbacks && callbacks.onMuteChange) {
				callbacks.onMuteChange(index,true);
			}
		} else {
			Element.addClassName($('muteButton' + index),'soundButton');
			Element.removeClassName($('muteButton' + index),'muteButton');
			if (callbacks && callbacks.onMuteChange) {
				callbacks.onMuteChange(index,false);
			}
			mutedFromButton = false;
		}
		
	}

	function statusChange(type, message, code) {
	  	try {
	  		window.clearTimeout(titleTimerId);
	  	} catch(e) {}
		switch (type) {
			case vp.MSG_WMP_ERROR:
				me.hideStartAd();
				var errmsg = $MR('sumo.web.video.status.wmperror1');
				wmpErrorMsg = message;
				var u = 0xFFFFFFFF + code + 1;
				wmpErrorCode = u.toString(16).toUpperCase();
				
				if (callbacks && callbacks.onError) {
					errmsg += $MR('sumo.web.video.status.wmperror2');
					Event.observe($('playerStatus'+index),'click',errorClick,false);
					$('playerStatus'+index).style.cursor = 'pointer';
				}
				showStatus(errmsg);
				break;
			case vp.MSG_OPENING:
				showStatus($MR('sumo.general.video.status.startingvideo') + programTitle);
				break;
			case vp.MSG_CHECKS_IF_STARTED:
				showStatus($MR('sumo.general.video.status.checkstarted'));
				break;
			case vp.MSG_TRIES_AGAIN:
				showStatus($MR('sumo.general.video.status.notstarted'));
				break;
			case vp.MSG_PROGRAM_ERROR:
				log.debug(message);
				if (callbacks && callbacks.onProgramError) {
					callbacks.onProgramError(message,code,currentProgId);
				}
				break;
		}
		//titleTimerId = window.setTimeout(function() {$('playerStatus' + index).innerText = programTitle; $('playerStatus' + index).title=programTitle},6000);
	}
	
	function errorClick() {
		if (callbacks && callbacks.onError) {
			callbacks.onError(wmpErrorMsg,wmpErrorCode,currentProgId);
		}
		Event.stopObserving('playerStatus'+index,'click',errorClick,false);
		$('playerStatus'+index).style.cursor = 'default';
	}

	function posChange(pos,dur,posStr,durStr,bufferingProgress) {
		try {
			if (buffering && bufferingProgress < 100) {
				$('timeElapsed' + index).innerHTML = $MR('sumo.general.video.status.buffering');
				$('duration' + index).innerHTML = bufferingProgress + ' %';
				if (pos==0) showStatus('Henter video (' + bufferingProgress + ' % fullført)');
			} else {
				if (!qualityStatusShowing) {
					if (live) showStatus(programTitle)
					else changeControls({'statusMsg': false});
				}
				if (!(dragging)) {
					if (posStr && posStr.length > 0 ) {
						if (posStr.length >7)
							$('timeElapsed' + index).innerHTML = posStr.substring(1);
						else 
							$('timeElapsed' + index).innerHTML = posStr;
					} else {
						$('timeElapsed' + index).innerHTML = '00:00';
					}
				}
				if (!live) {
					if (durStr.length > 7)
						durStr = durStr.substring(1);
					$('duration' + index).innerHTML = durStr;
					//$('timeSeparator' + index).innerHTML = '';
					var pd = pos / dur;
					if (items && vp.getActualProgId()==currentProgId) {
						var newItem = items.checkForItemChange(pos,currentItem);
						if (newItem) {
							currentItem = newItem;
							var prevItem = items.getPrevItem(pos);
							var nextItem = items.getNextItem(pos);
							if (prevItem) {
								$('prevButton' + index).title = $MR('sumo.general.video.control.skipto') + ' «' + prevItem.title + '»';
							} else {
								$('prevButton' + index).title = $MR('sumo.general.video.control.skipback');
							}
							if (nextItem) {
								$('nextButton' + index).title = $MR('sumo.general.video.control.skipto') + ' «' + nextItem.title + '»';
							} else {
								$('nextButton' + index).title = $MR('sumo.general.video.control.skipforward');
							}
	
							if (callbacks.onItemChange) {
								callbacks.onItemChange(currentProgId,newItem.id);
							}
						}
					}

					if (slider && slider.setValue && !dragging) {
						sliderEvtsEnabled = false;
						slider.setValue(pd,0);
						sliderEvtsEnabled = true;
					}
					if (progressBar) {
						progressBar.setValue(pd);
					}
					if (dur != lastDur && $('sliderButton' + index)) {
						if (slider) {
							slider.dispose();
						}

						progressBar = new ProgressBar('progressBar'+index, 'slider' + index, 'sliderButton' + index);
						slider = new Control.Slider('sliderButton' + index,'slider' + index, {
							onChange: function(v){
								if(sliderEvtsEnabled) {
									if (vp) vp.gotoPos(v*lastDur);
									dragging = false;
									log.debug('slider.onChange',v);
								}
							},
							onSlide: function(v) {
								t = v*lastDur;
								var hh = Math.floor(t/3600);
								(hh==0)?hh='':hh=hh+':';
								var rest = t % 3600;
								var mm = Math.floor(rest/60);
								(mm<10)?mm='0'+mm+':':mm=mm+':';
								var ss = Math.floor(rest % 60);
								if (ss<10) ss='0'+ss;
		
								$('timeElapsed' + index).innerHTML = hh + mm + ss;						
							}
						});
						dragging = false;
						lastDur = dur;
					}			
				}
			}
		} catch(e) {
			log.debug('posChange',e);
		}
	}

	function skipBack() {
		var pos = vp.getPos();
		if (items && !live) {
			var prevItem = items.getPrevItem(pos);
			if (prevItem) {
				vp.gotoPos(prevItem.timeBegin);
			} else {
				if (vp.getDuration() < 360)	vp.shiftPos(skipBackShort)
				else vp.shiftPos(skipBackLong);
			} 
		} else {
			if (vp.getDuration() < 360)	vp.shiftPos(skipBackShort)
			else vp.shiftPos(skipBackLong);
		}
	}

	function skipForward() {
		var pos = vp.getPos();
		if (items && !live) {
			var nextItem = items.getNextItem(pos);
			if (nextItem) {
				vp.gotoPos(nextItem.timeBegin);
			} else {
				if (vp.getDuration() < 360)	vp.shiftPos(skipForwardShort)
				else vp.shiftPos(skipForwardLong);
			} 
		} else {
			if (vp.getDuration() < 360)	vp.shiftPos(skipForwardShort)
			else vp.shiftPos(skipForwardLong);
		}
	}

	function startDrag() {
		dragging = true;
		log.debug('slider','start dragging/' + dragging);
	}
	
	function endDrag() {
		dragging = false;
		//log.debug('slider','end dragging/' + dragging);
	}

	// General player functions
  function toggleMiniMaxi(e) {
  	if ($('miniMaxiButton' + index)) {
	  	if (Element.hasClassName($('videoPanel' + index),'mini')) {
	  		Element.removeClassName($('videoPanel' + index),'mini');
	  		Element.addClassName($('videoPanel' + index),'maxi');
	  	} else {
	  		Element.removeClassName($('videoPanel' + index),'maxi');
	  		Element.addClassName($('videoPanel' + index),'mini');
	  	}
	  }
	  Event.stop(e);
  	return false;
  }

  this.toggleSelectorButton = toggleSelectorButton;
  function toggleSelectorButton(show) {
  	if ($('selectorButton' + index)) {
	  	if (show) {
	  		Element.removeClassName($('selectorButton' + index),'hideSelectorButton');
	  		Element.addClassName($('selectorButton' + index),'showSelectorButton');
	  	} else {
	  		Element.removeClassName($('selectorButton' + index),'showSelectorButton');
	  		Element.addClassName($('selectorButton' + index),'hideSelectorButton');
	  	}
  	}
  	return false;
  }
 
	function ProgressBar(pb,sl,slb) {
		try {
			var progressBar = $(pb);
			if ($(sl).currentStyle) {
				var tw = parseInt($(sl).currentStyle.width);
				var bw = parseInt($(slb).currentStyle.width);
			} else {
				var tw = parseInt($(sl).offsetWidth);
				var bw = parseInt($(slb).offsetWidth);
			}
		} catch(e) {
			log.error('HtmlPlayer.ProgressBar constructor',e);
		}
		
		this.setValue = function(v) {
			if (progressBar && tw && bw)
				progressBar.style.width = Math.round(((tw-bw)*v)+(bw/2)) + 'px';			
		}
		
	}
 
	function getAd(sitePage,position,targetElmId,onComplete) {
		/*
		try {
			if (adsup) {
				sitePage = '';
			}
		} catch(e) {}		
		adPath = '/RealMedia/ads/adstream_sx.ads/' + sitePage + '/1--' + adSession + '--@' + position;

		// polluting the global scope in order to get callback from ad
		if (!window.tv2video) {
			window.tv2video = new Array();	
		}
		window.tv2video[index] = me;

		log.debug('HtmlPlayer.getAd','Retrieving ad ' + position + ' to element ' + targetElmId + ' for ' + sitePage);

		var adAjax = new Ajax.Updater(
		targetElmId, 
		adPath, 
		{
			'method': 'get', 
			'onComplete': onComplete,
			'onError': function(request) {log.error('HtmlPlayer.getAd',request.status + ': ' + request.statusText);},
			'onException': function(request) {log.error('HtmlPlayer.getAd',request.status + ': ' + request.statusText);},
			'evalScripts': true,
			'onSuccess': function(){log.debug('HtmlPlayer.getAd','Ad retrieved.');},
			'onFailure': function(request) {log.error('HtmlPlayer.getAd',request.status + ': ' + request.statusText);}
		});
		return adAjax;
		*/
	}
/*
	function getAd2(sitePage,position,targetElmId,onComplete) {
		adPath = 'http://webtv.tv2.no/RealMedia/ads/adstream_sx.ads/' + sitePage + '/1--' + adSession + '--@' + position;

		log.debug('HtmlPlayer.getAd','Retrieving ad ' + position + ' to element ' + targetElmId + ' for ' + sitePage);
		
		var adAjax = new Ajax.Updater(
		null, 
		adPath, 
		{
			'method': 'get', 
			'onComplete': onComplete,
			'onError': function(request) {log.error('HtmlPlayer.getAd',request.status + ': ' + request.statusText);},
			'onException': function(request) {log.error('HtmlPlayer.getAd',request.status + ': ' + request.statusText);},
			'evalScripts': true,
			'onSuccess': function(request){
				
				var doc = request.responseXML.documentElement;
				if (doc.indexOf())
				log.debug('HtmlPlayer.getAd','Ad retrieved.');
				
			},
			'onFailure': function(request) {log.error('HtmlPlayer.getAd',request.status + ': ' + request.statusText);}
		});
		return adAjax;
	}
*/

  function getAds(sitePage,positions,resultObj,onComplete) {
		/*
		function addAd(pos,adCode) {
      if (!resultObj) {
        resultObj = new Object();
      }
			if ((adCode.indexOf('empty.gif')>0 || adCode.indexOf('document.write')>0)) { 
				resultObj[pos] = '';
			} else {
				resultObj[pos] = adCode;
			}
		
		}
		
		
		if (!window.tv2video) {
			window.tv2video = new Array();	
		}
		window.tv2video[index] = me;
		adPath = '/RealMedia/ads/adstream_sx.ads/' + sitePage + '/1--' + adSession + '--@';
		for (var i=0;i<positions.length;i++) {
      if (positions[i]) {
				adPath = adPath + positions[i] + ',';
			} 
    }
		adPath = adPath.substring(0,adPath.length-1) + '?_RM_HTML_PLAYERID_=' + index;

		var adAjax = new Ajax.Request(
  		adPath, 
  		{
  			'method': 'get', 
  			'onError': function(request) {log.debug('HtmlPlayer.getAds.onError',request.status + ': ' + request.statusText);},
  			'onException': function(request) {log.debug('HtmlPlayer.getAds.onException',request.status + ': ' + request.statusText);},
  			'evalScripts': true,
  			'onSuccess': function(transport){
            var adCode = transport.responseText;
            // get starting point for second ad
            var splitPos1 = adCode.indexOf('<!--OAS AD="' + positions[1] + '"');

						if (splitPos1==-1) {
							addAd(positions[0],adCode);
							addAd(positions[1],'');
						} else {
            	addAd(positions[0],adCode.substring(0,splitPos1));
							var remainder = adCode.substring(splitPos1,adCode.length-1);
							if (positions[2]) {
								var splitPos2 = remainder.indexOf('<!--OAS AD="' + positions[2] + '"');
								if (splitPos2==-1) {
            			addAd(positions[1],remainder);				
								} else {
									addAd(positions[1],remainder.substring(0,splitPos2));
									addAd(positions[2],remainder.substring(splitPos2,adCode.length-1));
								}
							} else {
            		resultObj[positions[1]] = remainder;
							}
						}
						
            if (onComplete) onComplete(resultObj);
          },
  			'onFailure': function(request) {
  				log.debug('HtmlPlayer.getAds.onFailure',request.status + ': ' + request.statusText);
  				if (onComplete) onComplete();
  			}
		});
		return adAjax;
		*/
  }


	function showStartAd(adCode) {
		try {
			if (adCode) {
        $('videoAdStart' + index).innerHTML = adCode;
      }
			if ($('videoAdStart' + index).innerHTML.length > 0) {
				$('videoAdStart' + index).style.display = 'block';
				hideAd = me.startVideo;
			}
		} catch (e) {
			log.error('HtmlPlayer.showStartAd',e);
		}
	}
	
	function showEndAd() {
		try {
			if (endAdCode.length>0) {
        $('videoAdEnd' + index).innerHTML = endAdCode;
				$('videoAdEnd' + index).style.display = 'block';
				hideAd = me.hideEndAd;
		  	hFallbackTimer = window.setTimeout(me.adEnded,fallbackDelay4);
			} else {
				me.hide();
			}
		} catch (e) {
			log.error('HtmlPlayer.showEndAd',e);
		}
	}
	
	function getPlayerContainer() {
		if (callbacks && callbacks.playerContainer) {
			return callbacks.playerContainer;
		} else {
			return $('videoPanel'+index).parentNode.id;
		}
	}

	function writeCookie(name, value, expires, path, domain, secure) {
	  logDebug('Util.writeCookie',name + '/' + value + '/' + expires + '/' + domain + '/' + secure);
	  var curCookie = name + "=" + escape(value) +
	      ((expires) ? "; expires=" + expires.toGMTString() : "") +
	      ((path) ? "; path=" + path : "") +
	      ((domain) ? "; domain=" + domain : "") +
	      ((secure) ? "; secure" : "");
	  document.cookie = curCookie;
	}

	function readCookie(name) {
	  var dc = document.cookie;
	  var prefix = name + "=";
	  var begin = dc.indexOf("; " + prefix);
	  if (begin == -1) {
	    begin = dc.indexOf(prefix);
	    if (begin != 0) return null;
	  } else
	    begin += 2;
	  var end = document.cookie.indexOf(";", begin);
	  if (end == -1)
	    end = dc.length;
	  return unescape(dc.substring(begin + prefix.length, end));
	}

}





