/*  
           ,---------------------------------------------------------.
          /  tsrkit library functions. All the JavaScript functions   )
         /   and objects are in this file.                           /
        /                                                           /
       (            Ioan Sameli, 04.2006 - 09.2006                 /
        `--\   /--------------------------------------------------´
           |  /
            \|
             `
           (")-''-(").___,.~-´'^`-._
            \O_ O  )   `-.  (     ).`-.__.´) 
            (_Y_.)/  ._   )  `._ `. `´-..-´
          _,.`--´_,.-_/  /~-`_.´ ,´
         (il).-'´  (li).´  ((!.-´

             _________________________________________________
    ________|                                                 |_______
    \       |   For more informations about all this, check   |      /
     \      |   the official documentation !                  |     /
     /      |_________________________________________________|     \
    /__________)                                            (________\

                                    
                                                             

Recent updates                                  
--------------
	22.09.2006 11:12	Created a new separated distribution with all the code in one sngle file
	19.10.2006 09:35	Added crossdomain support to Ajax object, using JSON
	01.11.2006 09:45	Changed the project's name, "tsrvod" to "tsrkit"
	28.02.2007 18:35	Included prototype.js, and simplified a couple of things with it


Code convention used in this project: http://javascript.crockford.com/code.html

TODO: an invalid xml ajax answer seems to give "no result" instead of an error

TODO: replace innerHTML with nodeValue, it's better because it's less worse (and it's valid)

TODO1: Since this won't only be used for videos, replace all the occurences to "video" by "item",
       and "playlist" by "search", or something like this

*/


  /********************************/
 /***   tsrkit configuration   ***/
/********************************/
//alert('hallo');




	var fileExtension = './tsrkit_extension.js?d=21-04-2007-11-10';

	// Disable the drop down menu, so it won't be behind the video if opened
	var enableDropDownMenu = 0;

	// The delay after wich a JSON request is considered as timed out, in milliseconds
	var delayTimeoutJSON = 30000000000000;
	var delayTimeoutAjax = 30000000000000;

	// The default video element's width (the height will be 3/4 of this)
	// Can be overridden with a new value from the webpage.
	// TODO: these are not used anymore i think, and should be properties of the player object anyway
	var defaultVideoWidth = 480;
	var originalVideoWidth = 300;
	var videoAspectRation = 16/9;

	// The minimum flash version we want to detect
	var minFlashVersion = 8;

	// Default HTML template for the playlists (ajax)
	var defaultSearchResultTemplate = '<li class="line$line">'
		 + '<a href="#" name="$id" rel="video" onclick="return $player.playVideo(this.name);">'
		 + '<span class="title"><span>$title <em>$vDuration</em>'
		 + '</span></span></a></li>';

	// The title of a video, basically it's just the title, but the date or other details could eventually be added here
	var titleTemplate = '$title';
	var defaultSearchTarget = null;

	// ID of the default target HTML element for the video Playback
	var defaultVideoTarget = 'playerDynamicContent';

	// The URL template for the search
	var searchTemplateURL = 'index.html?descending=true&siteSect=500202&language = fre&searchNow=true&searchString=$s';

	// Ajax waiting signs, put whatever you want here
	var ajaxWaitingSign = '<div class="ajaxLoading">'+str_searching+'</div>';
	var ajaxLoadingSign = '<h1>NOT USED !</h1>';
	var ajaxErrorSign = '<div class="errMsg">Fehler: nicht valides XML Format</div>';
	var ajaxEmptySign = '<div class="errMsg">'+str_norecordsfound+'</div>';
	var requestTimeoutSign = '<div style="font-weight:bold;">'+str_connectionproblem+'</div>';

	// Used when the playback of a video fails because no compatible file has been found
	var noAvailableFile = '<div class="errMsg">No compatible file :(</div>';

	// Icons, used for the configuration panel
	var okayIcon    = '<img src="http://www.tsr.ch/images/icons/icon_16-okay.gif" alt="" />';
	var troubleIcon = '<img src="http://www.tsr.ch/images/icons/icon_16-trouble.gif" alt="" />';
	var warningIcon = '<img src="http://www.tsr.ch/images/icons/icon_16-warning.gif" alt="" />';

	// Maybe this will be useful, but i don't know yet. It's the files to test bandwidth
	//var staticDataSize1 = 'http://www.tsr.ch/download/static/test-data-64k.html';
	//var staticDataSize2 = 'http://www.tsr.ch/download/static/test-data-128k.html';
	var staticDataSize4 = './download/static/test-data-256k.html';
	//var staticDataSize8 = 'http://www.tsr.ch/download/static/test-data-512k.html';
	//var staticDataSize16 = 'http://www.tsr.ch/download/static/test-data-1024k.html';	// I guess this one shouldn't be used too much since it uses too much ram for the client

	// Cookie names that store detection result
	var cookieWM = 'windowsMedia';      // Windows Media detection result
	var cookieRP = 'realMedia';        // Real Player detection result
	var cookieMF = 'flash';           // Macromedia Flash detection result
	var cookieBW = 'testBandWidth';  // BandWidth detection result

	// Maximum age of the cookies containing the test results
	var resultCookiesLife=30;

	// The style of the dialog panel (stored here so we don't have to add it to all the CSS files)
	var displayPanelStyle = 'opacity: .75;filter:Alpha(opacity=75);position:absolute;width:33%;'
		 + 'left:33%;top:33%;border:1px solid red;font-size:14px;font-weight:bold;'
		 + 'background:url(http://www.tsr.ch/images/interactif/player/searching.gif) no-repeat left #FFFFFF;'
		 + '-moz-border-radius:16px;padding:32px;padding-left:48px;';

	// The HTML code of this display panel
	var displayPanelCode = '<div id="displayPanel" style="' + displayPanelStyle + '">$msg</div>';

	// The HTML code for embed videos, for different codecs: Real, WindowsMedia, Flash, and jpeg
	var playerCodeReal = '<embed id="embedVideo" name="embedVideo" title="realMedia" type="audio/x-pn-realaudio-plugin" '
		+ 'autostart="true" src="$file" controls="ImageWindow" console="radio" '
		+ 'nojava="false" width="$width" height="$height" maintainaspect="true"/>'
		+ '<br/><embed width="$width" height="26" type="audio/x-pn-realaudio-plugin" name="controls" id="realControls" '
		+ 'nojava="true" controls="ControlPanel" console="radio"/>';

	// The HTML code for embed videos, for different codecs: Real, WindowsMedia, Flash, and jpeg
	var playerCodeRealaudio = '<img src="$previewimage" width="480" height="272"><embed id="embedVideo" name="embedVideo" title="realMedia" type="audio/x-pn-realaudio-plugin" '
		+ 'autostart="true" src="$file" controls="ImageWindow" console="radio" '
		+ 'nojava="false" width="$width" height="$height" maintainaspect="true"/>'
		+ '<br/><embed width="$width" height="26" type="audio/x-pn-realaudio-plugin" name="controls" id="realControls" '
		+ 'nojava="true" controls="ControlPanel" console="radio"/>';


	// TODO: file qui work = http://members.aol.com/jrzycrim01/mozilla/wmp/vidtest-HS.wmv
	var playerCodeWindowsMedia = '<div onmouseover="debugVideoWM();" id="videoEmbedWindowsMedia"> <object classid="CLSID:6BF52A52-394A-11D3-B153-00C04F79FAA6" width="$width" height="300" '
		+ 'id="embedVideo" standby="chargement..." title="windowsMedia"><param name="URL" value="$file">\n'
		+ '<param name="AutoStart" value="True"><param name="stretchToFit" value="true">\n'
		+ '<param name="showDisplay" value="True"><param name="showStatusBar" value="True">\n'
		+ '<param name="showControls" value="True"><param name="fullScreen" value="False">\n'
		+ '<param name="uiMode" value="full"><param name="enableContextMenu" value="True">\n'
		+ '<embed filename="$file" id="embedVideo" standby="chargement..." name="player" '
		+ 'width="$width" height="300" type="application/x-mplayer2" title="windowsMedia" '
		+ 'pluginspage="http://www.microsoft.com/windows/mediaplayer/" '
		+ 'autostart="true" stretchToFit="true" showcontrols="true" fullscreen="false" showdisply="true" '
		+ 'uimode="full" enablecontextmenu="true" showstatusbar="true"></object></div>';
		
		var playerCodeWindowsMediaAudio = '<div onmouseover="debugVideoWM();" id="videoEmbedWindowsMedia"><img src="$previewimage" width="480" height="255"><object classid="CLSID:6BF52A52-394A-11D3-B153-00C04F79FAA6" width="$width" height="45" '
		+ 'id="embedVideo" standby="chargement..." title="windowsMedia"><param name="URL" value="$file">\n'
		+ '<param name="AutoStart" value="True"><param name="stretchToFit" value="true">\n'
		+ '<param name="showDisplay" value="True"><param name="showStatusBar" value="True">\n'
		+ '<param name="showControls" value="True"><param name="fullScreen" value="False">\n'
		+ '<param name="uiMode" value="full"><param name="enableContextMenu" value="True">\n'
		+ '<embed filename="$file" id="embedVideo" standby="chargement..." name="player" '
		+ 'width="$width" height="45" type="application/x-mplayer2" title="windowsMedia" '
		+ 'pluginspage="http://www.microsoft.com/windows/mediaplayer/" '
		+ 'autostart="true" stretchToFit="true" showcontrols="true" fullscreen="false" showdisply="true" '
		+ 'uimode="full" enablecontextmenu="true" showstatusbar="true"></object></div>';	
		

	var playerCodeFlashSwf = '<object title="flash" classid="clsid:d27cdb6e-ae6d-11cf-96b8-444553540000" '
		+ 'codebase="http://fpdownload.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=7,0,0,0" '
		+ 'height="$height" width="$width"><param name="allowScriptAccess" value="sameDomain"/>\n'
		+ '<param name="movie" value="$file"/>\n'
		+ '<param name="quality" value="high"/><param name="bgcolor" value="#ffffff"/>\n'
		+ '<param name="wmode" value="transparent"/>\n'
		+ '<embed swLiveConnect="true" src="$file" quality="high" wmode="transparent" title="flash" '
		+ 'bgcolor="#ffffff" height="$height" width="$width" align="middle" '
		+ 'allowScriptAccess="sameDomain" type="application/x-shockwave-flash" '
		+ 'pluginspage="http://www.macromedia.com/go/getflashplayer" /></object>';

	/*
var playerCodeFlashSwf = '<object width="800" height="450" type="application/x-shockwave-flash" name="player" id="player" data="http://www.tsr.ch/flash/player.swf?stream=rtmp://stream.tsr.ch/tj/2010/tj_08132010-501k.flv?start=113&v=1.4.5.1" style="visibility: visible;">'
+ '<param name="wmode" value="transparent">'
+ '<param name="AllowScriptAccess" value="always">'
+ '<param name="AllowFullScreen" value="true">'
+ '<param name="flashvars" value="autoPlay=false&amp;bigPlayColor=16777215&amp;bigPlayAlpha=0.8&amp;version=9.0.28&amp;target=player">'
+ '</object>'*/
	
		
		//	var playerCodeFlashVideo = '<embed name="player" id="player" height="$height" width="$width" '
//+ 'flashvars="displayclick=play&autostart=true&file=$datei&stop=$stoppos&streamer=$streamer&$flashpreviewimage" '
//+ 'wmode="opaque" allowscriptaccess="always" allowfullscreen="true" quality="high"  style="width:$widthpx;height:$heightpx" src="player.swf" type="application/x-shockwave-flash"/>';

/*
 var playerCodeFlashVideo = '  <!--[if IE]>'
+'     <object name="player" width="480" height="299" style=" width:480px;" id="player" classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000" style="visibility: visible;" change="function () {  return eval(instance.CallFunction("<invoke name=\""+name+"\" returntype=\"javascript\">" + __flash__argumentsToXML(arguments,0) + "</invoke>")); }" isVideoPlaying="function () { return eval(instance.CallFunction("<invoke name=\""+name+"\" returntype=\"javascript\">" + __flash__argumentsToXML(arguments,0) + "</invoke>")); }">'
+'       <PARAM NAME="_cx" VALUE="21166"><PARAM NAME="_cy" VALUE="11906"><PARAM NAME="FlashVars" VALUE=""><PARAM NAME="Movie" VALUE="http://www.tsr.ch/flash/player.swf?stream=$file&v=1.4.5.1"><PARAM NAME="Src" VALUE="http://www.tsr.ch/flash/player.swf?stream=$file&v=1.4.5.1"><PARAM NAME="WMode" VALUE="Transparent"><PARAM NAME="Play" VALUE="1"><PARAM NAME="Loop" VALUE="-1"><PARAM NAME="Quality" VALUE="High"><PARAM NAME="SAlign" VALUE="LT"><PARAM NAME="Menu" VALUE="-1"><PARAM NAME="Base" VALUE=""><PARAM NAME="AllowScriptAccess" VALUE="always"><PARAM NAME="Scale" VALUE="NoScale"><PARAM NAME="DeviceFont" VALUE="0"><PARAM NAME="EmbedMovie" VALUE="0"><PARAM NAME="BGColor" VALUE=""><PARAM NAME="SWRemote" VALUE=""><PARAM NAME="MovieData" VALUE=""><PARAM NAME="SeamlessTabbing" VALUE="1"><PARAM NAME="Profile" VALUE="0"><PARAM NAME="ProfileAddress" VALUE=""><PARAM NAME="ProfilePort" VALUE="0"><PARAM NAME="AllowNetworking" VALUE="all"><PARAM NAME="AllowFullScreen" VALUE="true">'

+'      </object>'
+'    <![endif]-->'
+'	<!--[if !IE]>-->'
+'   <object width="480" height="299" style=" width:480px;" type="application/x-shockwave-flash" name="player" id="player" data="http://www.tsr.ch/flash/player.swf?stream=$file&v=1.4.5.1" style="visibility: visible;">'
+ '<param name="wmode" value="transparent">'
+ '<param name="AllowScriptAccess" value="always">'
+ '<param name="AllowFullScreen" value="true">'
+ '<param name="flashvars" value="autoPlay=true&amp;bigPlayColor=16777215&amp;bigPlayAlpha=0.8&amp;version=9.0.28&amp;target=player">'
+'     </object>'
+'    <!--<![endif]-->';


*/



 var playerCodeFlashVideoMsie = ' '
+'     <object name="player" width="480" height="299" style=" width:480px;" id="player" classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000" style="visibility: visible;" change="function () {  return eval(instance.CallFunction("<invoke name=\""+name+"\" returntype=\"javascript\">" + __flash__argumentsToXML(arguments,0) + "</invoke>")); }" isVideoPlaying="function () { return eval(instance.CallFunction("<invoke name=\""+name+"\" returntype=\"javascript\">" + __flash__argumentsToXML(arguments,0) + "</invoke>")); }">'
+'       <PARAM NAME="_cx" VALUE="21166"><PARAM NAME="_cy" VALUE="11906"><PARAM NAME="FlashVars" VALUE=""><PARAM NAME="Movie" VALUE="http://www.tsr.ch/flash/player.swf?stream=$file&v=1.4.5.1"><PARAM NAME="Src" VALUE="http://www.tsr.ch/flash/player.swf?stream=$file&v=1.4.5.1"><PARAM NAME="WMode" VALUE="Transparent"><PARAM NAME="Play" VALUE="1"><PARAM NAME="Autostart" VALUE="1"><PARAM NAME="Loop" VALUE="-1"><PARAM NAME="Quality" VALUE="High"><PARAM NAME="SAlign" VALUE="LT"><PARAM NAME="Menu" VALUE="-1"><PARAM NAME="Base" VALUE=""><PARAM NAME="AllowScriptAccess" VALUE="always"><PARAM NAME="Scale" VALUE="NoScale"><PARAM NAME="DeviceFont" VALUE="0"><PARAM NAME="EmbedMovie" VALUE="0"><PARAM NAME="BGColor" VALUE=""><PARAM NAME="SWRemote" VALUE=""><PARAM NAME="MovieData" VALUE=""><PARAM NAME="SeamlessTabbing" VALUE="1"><PARAM NAME="Profile" VALUE="0"><PARAM NAME="ProfileAddress" VALUE=""><PARAM NAME="ProfilePort" VALUE="0"><PARAM NAME="AllowNetworking" VALUE="all"><PARAM NAME="AllowFullScreen" VALUE="true">'

+'      </object>';


var playerCodeFlashVideo = '<object width="480" height="299" style=" width:480px;" type="application/x-shockwave-flash" name="player" id="player" data="http://www.tsr.ch/flash/player.swf?stream=$file&v=1.4.5.1" style="visibility: visible;">'
+ '<param name="wmode" value="transparent">'
+ '<param name="AllowScriptAccess" value="always">'
+ '<param name="AllowFullScreen" value="true">'
+ '<param name="flashvars" value="autoPlay=true&amp;bigPlayColor=16777215&amp;bigPlayAlpha=0.8&amp;version=9.0.28&amp;target=player">'
+ '</object>'


		var playerCodeFlashVideoSF = '<embed name="player" id="player" height="$height" width="$width" '
+ 'flashvars="displayclick=play&autostart=true&file=$datei&stop=$stoppos&streamer=$streamer&$flashpreviewimage" '
+ 'wmode="opaque" allowscriptaccess="always" allowfullscreen="true" quality="high"  style="width:$widthpx;height:$heightpx" src="player.swf" type="application/x-shockwave-flash"/>';





 var playerCodeFlashRSI = ' <embed width="480" height="300" src="RSIPlayer.swf" allowfullscreen="true" allowscriptaccess="always" quality="high" type="application/x-shockwave-flash" pluginspage="http://www.adobe.com/go/getflashplayer" flashvars="config={configFileName: \'$file\',showFullScreenButton: true,useNativeFullScreen: true,autoRewind: true,autoPlay: true,showMenu: false,initialScale: \'scale\'}"/>'




	var playerCodeJpeg = '<img src="$file" height="$height" width="$width" alt="" />';
	/*
	var playerQuicktimeVideo = '<object CLASSID="clsid:02BF25D5-8C17-4B23-BC80-D3488ABDDC6B" width="$width" height="$height" CODEBASE="http://www.apple.com/qtactivex/qtplugin.cab">
<param name="src" value="sample.mov">
<param name="qtsrc" value="$file">
<param name="autoplay" value="true">
<param name="loop" value="false">
<param name="controller" value="true">
<embed src="sample.mov" qtsrc="$file" width="$width" height="$height" autoplay="true" loop="false" controller="true" pluginspage="http://www.apple.com/quicktime/"></embed>
</object>';*/
	

  /*********************************/
 /***   Core and debug module   ***/
/*********************************/

	window.onload = function() {
		//alert("hallo");
	// To execute several functions on page load
	// Adapted from http://www.ibilab.net/webdev/articles/javascript/evenements-multiples-chargement-page-7.htm
		var fList = window.listStart;
		if (fList) {
			fList.each(function(f) {
				tryCallBack(f);
			});
		}
	}

  /***************************/
 /***   Playback module   ***/
/***************************/

	// The player root object, contains all the videos and some settings
	// varName (optional, string): to inform the tsrkit object of its own name. Useful to use JSON, for callbacks
	// Ioan Sameli, 26.09.2006
	tsrkit = function(varName) {

		// Initialize the object before populating it
		this.videos = {};

		// Same for requests
		this.requests = {};

		if (varName) this.varName = varName;

		// The ID of the HTML element where the data will be injected
		if (defaultVideoTarget) this.videoTarget = defaultVideoTarget;
		if (defaultSearchTarget) this.searchTarget = searchTarget;

		// The video that is currently being played
		this.currentVideoID = 0;

		// Get a user automatically
		// c (function): callback function when everything has been initialized
		this.init = function(c) {
			this.getUser(c);
			this.additionalPlayOps();
		}

		// Inject a new title in the player
		// TODO:deprecated function, or at least not used anymore
		this.setTitle = function(pTitle) {
			var t = $(this.videoTitleTarget);
			if (t) t.innerHTML = pTitle;
		}

		// Pretty useless, but i use if for testing.
		this.getTitle = function() {
			var pTitle = $(this.videoTitleTarget).innerHTML;
			trace(pTitle);
			return pTitle;
		}

		// Get a user for this player, with all the configuration
		this.getUser = function(callBack) {

			// Create a new user object for the current object
			this.user = new tsrkit_user;

			// Add the current player as a user's property, so it can be easily accessed
			this.user.player = this;

			var lPlayer = this;

			// Initialise the user, with a callback function if we need to run some test
			var initCallBack = function() {
				lPlayer.getUser();
				tryCallBack(callBack);
			}

			// launch the user's tests
			var t = this.user.init(initCallBack);

			// User's test will return false if a bandwidth test has been launched, so stop here
			// Otherwise, just execute the callback
			if (t == false) {
				return;
			} else {
				tryCallBack(callBack);
			}

			// Some debug
			trace('The player has a user loaded.<br/>Real ? <em>' + this.user.realMedia + '</em><br/>Windows media ? <em>'
				+ this.user.windowsmedia + '</em>Flash ? <em>' + this.user.flash + '</em><br/><br/>Bandwidth ? <em>' + this.user.bandWidth + ' Kbps</em>', 'info');
		}

		// Create a new search in this object, with Ajax or JSON
		// rSource (string): The url to load the data from
		// rTarget (string, optional): The HTML element to inject the data in
		this.getNewSearch = function(rSource, rTarget) {

			var newPlaylist = new tsrkit_search(rSource);

			// The playlist will need to know where it comes from.
			newPlaylist.linkToParent(this);

			// If rTarget isn't provided, use the player's default playlist
			newPlaylist.rTarget = rTarget || this.searchTarget;

			// Return the object, so the caller can play with it
			return newPlaylist;
		}

		// Add a video to the global video directory of this object, that needs to be filled later
		// idNewVideo (string): the ID of the video to add, will be an integer most of the time.
		this.addvideo = function(nvId) {

						
			if (!nvId) {
				nvId = this.videos.length;
				while ((typeof this.videos[nvId]) != 'undefined') {
					nvId++;
				}
			}

			return this.videos[nvId] = new tsrkit_video(nvId);
		}

		// Play a video
		// First, get the right medium according to the preference, and then inject the HTML code to render it in the browser
		this.playVideo = function(idVideo) {
         
			// This video should now be the default video
			if (!idVideo) idVideo = this.currentVideoID;
			else this.currentVideoID = idVideo;
			//alert(this.videos);
		//	 mainPlayer.addvideo(vid);
         // alert('id:'+idVideo+ ':' +this.videos[idVideo]);
		 //trace(this.videos[idVideo][role],'message');
			if ('object' == typeof this.videos[idVideo]){

				// Display the title of this video
				var t = parseTemplate(titleTemplate,this.videos[idVideo]);
				this.setTitle(t);

				// Render the video and return the result
				return this.injectMedium();

			} else {
				trace('the video '+idVideo+' is not defined.','error');
				return false;
			}
		}

		// Select the correct medium for the given video ID according to the user preferences,
		// or select the best medium if the correct one isn't available
		// Take two string arguments
		// Returns a string with the best format
		// idVideo (optional): the video to select the medium from. If not provided, will look in the default video.
		// favCodec (optional), favBitrate (optional): to force the selection of a particular medium
		// instead of the best one for the current user
		// Ioan Sameli, 27.03.2006, 26.09.2006
		// TODO: this would be more logic if it was a property of the video object ? but then difficulty to access the user's properties
		this.selectFormat = function(idVideo, favCodec, favBitrate) {
			
			if (!idVideo) idVideo = this.currentVideoID;
			if (!favCodec) favCodec = this.user.favCodec;
			if (!favBitrate) favBitrate = this.user.favBitrate;
			
		//	alert(favCodec);

			// Initialize variables for the loop
			var lastCompatibleDelta = 100000;
			var lastAlternativeDelta = 100000;

			var chosenFormat = null;
			var alternativeFormat = null;

			// Check if the video's media have been loaded first
			if ( (typeof this.videos[idVideo]) == 'undefined') {
				trace('selectFormat: The video ' + idVideo + ' is not in cache !', 'error');
				return false;
			}

			// We'll work on the media, so make a local var
		
			var media = this.videos[idVideo].media;

			var p = this;
			media.each(function(currentMedium, i) {

				var currentDelta = Math.abs(currentMedium.mBitrate-favBitrate);
				
//trace('The ' + currentMedium.mBitrate + '-' + favBitrate + 'Kbps is ' + currentDelta + 'Kbps away from what we need, with the right codec !' + currentMedium.mUrl, 'warning');
//trace (currentMedium.mCodec + '-' + favCodec,'warning');				
			
			
				//alert();
				if (currentMedium.mCodec == "mp3" || currentMedium.mCodec == "mp4") { currentMedium.mCodec = favCodec;  }
				if (currentMedium.mCodec == favCodec  ) {

					// If the delta with this medium is lower than before, it's better so choose this one
					if (currentDelta < lastCompatibleDelta) {
						lastCompatibleDelta = currentDelta;
						chosenFormat = i;
					//	trace('The ' + currentMedium.mBitrate + '-' + favCodec + 'Kbps is ' + currentDelta + 'Kbps away from what we need, with the right codec !' + currentMedium.mUrl, 'warning');
					}

				} else if (p.user[currentMedium.mCodec] == 'yes') {

					// Same thing with the alternative medium, that will be used if no compatible medium has been found
					if (currentDelta < lastAlternativeDelta) {
						lastAlternativeDelta = currentDelta;
						alternativeFormat = i;
					//	trace('The ' + currentMedium.mCodec + '-' + currentMedium.mBitrate + 'Kbps is ' + currentDelta + 'Kbps away from what we need, with an alternative codec !', 'warning',2);
					}
				}
			});

			if (chosenFormat != null) {
				trace('So we chose ' + chosenFormat + ' for video ' + idVideo, 'success',2);
				return chosenFormat;
			} else if (alternativeFormat != null) {
				trace('No favourite format found, so we chose the ' + alternativeFormat + ' for video ' + idVideo, 'success',2);
				return alternativeFormat;
			} else {
				return false;
			}
		}

		// If somehow the videos array got too huge, we can flush it.
		// TODO: is this really useful ? Run some benchmarks
		this.resetVideoCache = function() {
			this.videos = {};
		}

		// Parse the dynamic links of a playlist to change them to static links to
		// the physical video file, according to the user preference
		// Should be called each time a user changes his preference, to update the links
		// The links to play a video should have a attribute rel="video", and the ID of
		// the video as ID, for example id="4894574"
		this.parsePlaylistLinks = function() {

			// Loop on all the links of the page that have a "rel" attribute set to "video"
			playlistLinks = document.getElementsByTagName('a');
			for (var i = 0; i < playlistLinks.length; i++) {

				if (playlistLinks[i].rel == 'video') {

					var idVideo = playlistLinks[i].name;

					if (!this.videos[idVideo]) {
						trace('parsePlaylistLinks: The video ' + idVideo + ' is not loaded.', 'error');
						continue;
					}

					// Get the best format for this video
					var chosenFormat = this.selectFormat(idVideo);
					
					if (typeof chosenFormat != 'number') continue;

					// Get the medium
					medium = this.videos[idVideo].media[chosenFormat];

					// Set the link href to the physical file
					if (medium) playlistLinks[i].href=medium['mUrl'];
				}
			}
		}

		// Will inject the HTML code to play the selected video in the element with the id given by idElementPlayer
		// idVideo (optional): the id of the video, it has to exist already in player.videos[x]
		// mFormat (optional): the string with the format of the medium we want to play, for example "realMedia-450".
		// If these arguments are not provided, the function will manage to get them by itself.
		// Ioan Sameli, 27.03.2006
		this.injectMedium = function(idVideo, mFormat) {
			
			
			// If no idVideo is provided, play the default Video of the player
			if (!idVideo)idVideo = this.currentVideoID;

			// If not provided, get the best format according to the player's settings
			if (typeof mFormat != 'number') mFormat = this.selectFormat(idVideo);
		
			// If somehow there's no available media for this video
			if (typeof mFormat != 'number') {
				if ($(this.videoTarget)) $(this.videoTarget).innerHTML = noAvailableFile;
				return true;
			}

			var medium = this.videos[idVideo].media[mFormat];
		//	alert( newMedium.mCodec);
			
			// Create the properties collection that will be used by the templating
			//trace(alert(medium.mUrl));
		/*	var previewimage = "";
			if (this.videos[idVideo].image_url != "" ) 
			{
			previewimage = this.videos[idVideo].image_url;	
			}
			else
			{
			previewimage = "images/radio_images/" + this.videos[idVideo].owner + ".gif";
			}*/
			if (this.videos[idVideo].role == "video") 
			{
				flashpreviewimage	= ""; 
			}
			else
			{
			  flashpreviewimage = "image=http://www.pactemultimedia.ch/images/radio_images/" + this.videos[idVideo].owner + ".gif";
			  
			}
			var strtosplit = medium.mUrl;
			var start = strtosplit.split("&"); 
			var count = start.length-1;
			//alert(start[count]);
			startstr = start[count];
			startarr = startstr.split("="); 
			startstrcount = startarr.length-1;
			//alert(startarr[startstrcount]);
			var starttime = parseFloat(startarr[startstrcount]);
		//	alert(starttime);
			if (isNaN(starttime))
			{
			  var endpoint = parseFloat(this.videos[idVideo].stoppos);
			}
			else
			{
			 var endpoint = parseFloat(this.videos[idVideo].stoppos) + starttime;
			}
			
			//alert(endpoint);
		
			//alert(startarr[0]);
			window.location.hash = endpoint;
			previewimage = "images/radio_images/" + this.videos[idVideo].owner + ".gif";
			var mProps = {
				'owner':	this.videos[idVideo].owner,
				'role':		this.videos[idVideo].role,
				'title':    this.videos[idVideo].title,
				'file':     medium.mUrl,
				'datei':     medium.mFilename,
				'streamer':     medium.mStreamer,
				'codec':    medium.mCodec,
				'bitrate':  medium.mBitrate,
				'width':    medium.mWidth,
				'height':   medium.mHeight,
				'video':    this.videos[idVideo],
				'previewimage':	previewimage,
				'flashpreviewimage':	flashpreviewimage,
				'stoppos' : this.videos[idVideo].stoppos
				
			};
			//alert(medium.mCodec);
	//	if (!medium.mCodec) medium.mCodec = "mp3";
			//var abbruchPosition = 545 ;
//var s = "";
//for (property in mProps){s = s + "\n "+property + ' = ' + mProps[property];}
//alert(s); 
//alert('id:'+idVideo + '\n / Format:'+ mFormat + '/ \n file:'+ medium.mUrl);
			// Return true : if the user wants to use an external player.
			// It will simply make the user to follow the link by not cancelling the clic
		
		
			if (this.user.alternativePlay && (mProps.codec == 'realMedia' || mProps.codec == 'windowsMedia' || mProps.codec == 'flashMedia' || mProps.codec == 'FlowPlayer' || mProps.codec == 'mp4' || mProps.codec == 'm4v' || mProps.codec == 'mp3')) return true;

			// Create the HTML code, according to the codec
			//alert(medium.mCodec);
		//	medium.mCodec = strtolower(medium.mCodec);
			
			
			
			switch (medium.mCodec) {

		/*	case 'realmedia':
			
			
			if (mProps.role == "audio" || mProps.role == "podcast")
					{
						mProps.height = parseInt(mProps.height) - 285;
				var playerCode = playerCodeRealaudio;
					}
					else
					{
						mProps.height = parseInt(mProps.height) - 10;
				var playerCode = playerCodeReal;
					}
		
			
			
				
				break;*/
			case 'realMedia':
			if (mProps.role == "audio" || mProps.role == "podcast")
					{
						//mProps.height = parseInt(mProps.height) - 285;
						mProps.height = 1;
						var playerCode = playerCodeRealaudio;
						document.getElementById('fullscreen').style.visibility = 'hidden';
									
			//	alert(mProps.height);
					}
					else
					{
						mProps.height = parseInt(mProps.height) - 10;
						var playerCode = playerCodeReal;
						document.getElementById('fullscreen').style.visibility = 'visible';
					}
			
				
				break;
	/*		case 'windowsmedia':

				

					if (mProps.role == "audio" || mProps.role == "podcast")
					{
						mProps.height = parseInt(mProps.height) /*+ 10*/;
					/*	var playerCode = playerCodeWindowsMediaAudio;
					}
					else
					{
						mProps.height = parseInt(mProps.height) /*+ 10*/;
				/*		var playerCode = playerCodeWindowsMedia;
					}
				break;*/
				
				case 'windowsMedia':

					if (mProps.role == "audio" || mProps.role == "podcast")
					{
						mProps.height = parseInt(mProps.height) /*+ 10*/;
						var playerCode = playerCodeWindowsMediaAudio;
						document.getElementById('fullscreen').style.visibility = 'hidden';
					}
					else
					{
						mProps.height = parseInt(mProps.height) /*+ 10*/;
						var playerCode = playerCodeWindowsMedia;
						document.getElementById('fullscreen').style.visibility = 'visible';
					}
			
				
				
				break;
				case 'h264':

				// Add 16px, it's the height of the flv player control bar
				mProps.height = parseInt(mProps.height) + 16;
				
				var playerCode = playerCodeFlashVideo;
				break;
				
				
			case 'mp3':

				// Add 45px, it's the height of the windows media player control bar
			//	mProps.height = parseInt(mProps.height) + 45;
				mProps.height = parseInt(mProps.height) - 10
				var playerCode = playerCodeWindowsMediaAudio;
			//	alert(playerCode);
				break;	
				
				
			case 'mpeg':

				// Add 45px, it's the height of the windows media player control bar
				//mProps.height = parseInt(mProps.height) + 45;
				
				var playerCode = playerCodeWindowsMedia;
				break;
					
			
			case 'swf':
				//mProps.height = parseInt(mProps.height) - 10;
				var playerCode = playerCodeFlashSwf;
				break;
				
				


				
			case 'flashMediaRSI':

				// Add 16px, it's the height of the flv player control bar
				mProps.height = parseInt(mProps.height) + 16;
				
				var playerCode = playerCodeFlashRSI;
				break;
				
				case 'FlowPlayer':

				// Add 16px, it's the height of the flv player control bar
				mProps.height = parseInt(mProps.height) + 16;
				
				var playerCode = playerCodeFlashRSI;
				break;
					
					case 'flowPlayer':

				// Add 16px, it's the height of the flv player control bar
				mProps.height = parseInt(mProps.height) + 16;
				
				var playerCode = playerCodeFlashRSI;
				break;
					
			
			case 'mp4':

				// Add 16px, it's the height of the flv player control bar
				mProps.height = parseInt(mProps.height) + 16;
				
				var playerCode = playerCodeFlashVideo;
				break;
				
					case 'm4v':

				// Add 16px, it's the height of the flv player control bar
				mProps.height = parseInt(mProps.height) + 16;
				
				var playerCode = playerCodeFlashVideo;
				break;	

				
			case 'jpeg':

				var playerCode = playerCodeJpeg + noAvailableFile;
				break;
				
				case 'flashMedia':

				// Add 16px, it's the height of the flv player control bar
				mProps.height = parseInt(mProps.height) + 16;
				
				if (this.videos[idVideo].owner == "SF")
				{
				var playerCode = playerCodeFlashVideo;	
				}
				else if (this.videos[idVideo].owner == "RSI")
				{
					var playerCode =	playerCodeWindowsMediaAudio;
				}
				else if (this.videos[idVideo].owner == "DRS")
				{
					var playerCode =	playerCodeWindowsMediaAudio;
				}
				
					else if (this.videos[idVideo].owner == "RSR")
				{
					var playerCode =	playerCodeWindowsMediaAudio;
				}
				
					else if (this.videos[idVideo].owner == "TSR")
				{
				
				if (navigator.appName == "Microsoft Internet Explorer")
				{
				var playerCode =	playerCodeFlashVideoMsie;	
				}
				else
				{
				var playerCode =	playerCodeFlashVideo;
				}
				}
				
				
				
				
				
				else
				{
				var playerCode = playerCodeFlashVideo;
				}
				break;




			default:

				// This should not appear but well, it's better to have it.
				playerCode = '<p><b>Error:</b> Unknown codec: ' + medium.mCodec + '!</p>';
			}

			// Parse the template
			playerCode = parseTemplate(playerCode, mProps);

			// Insert the code in the element
			if ($(this.videoTarget)) $(this.videoTarget).innerHTML = playerCode;

			// Extensions ? Give the medium also in the case it's needed
			this.additionalPlayOps(mFormat);

			return false;
		}
	}

	tsrkit_video = function(vId) {
	// Module to configure and play a video
	// Ioan Sameli, 26.09.2006
	// vId (string, optional): To give the ID of the video to play
	// With no configuration, the video will play according to the personal preference of the user
	// TODO: this should actually be something like a subclass of tsrkit

		if (vId) this.id = vId;

		// method: Add a medium to the current video
		this.addMedium = function() {

			// If there isn't yet a parent media array for this video, just create one
			if (!this.media) this.media = new Array;
				
			// Create a new medium empty object and push it in the media array
			var newMedium = {};
			this.media.push(newMedium);

			return newMedium;
		}
	}


  /*************************************/
 /***   User configuration module   ***/
/*************************************/


	// The current user, with all its settings
	// TODO: when user changes its configuration, re-parse all the video links
	// TODO: add Macromedia Flash detection
	tsrkit_user = function() {

		// Initialize the user object, load all the data from the cookie and save in the user object properties
		// If the cookies don't exist, runs the tests
		// Will return false if it runs the bandwidth test, it means the caller functions will have to stop.
		// fromFunc: function that called this, to be called again after the bandwidth test is done
		// redo (optional): if provided, will force all the tests to re-run.
		this.init = function(fromFunc, redo) {

			// Check the plugins too
			this.windowsMedia = this.checkWindowsMedia(redo);
			this.realMedia = this.checkRealPlayer(redo);
			this.swf = this.flashMedia = this.FlowPlayer = this.checkMacromediaFlash(redo);

			// I guess everyone can display jpeg ;)
			this.jpeg = 'yes';

			// Check the bandwidth.
			// If the function decides to run the test (returns false), stop here.
			this.bandWidth = this.checkBandWidth(fromFunc, redo);
			if (this.bandWidth == false) return false;

			// Alternativeplay, to use an alternative method to play videos
			// (typically, will just open the direct link instad of embed in the page)
			this.alternativePlay = getCookie('alternativePlay') ? true : false;

			this.autoSetPrefs(redo);

			this.additionalInitOps();
		}

		// Load the user's preferences from the cookies
		// If not found, will find the best configuration for the current user
		this.autoSetPrefs = function(redo) {

			if ((redo) || (!getCookie('favCodec'))) {
				if (this.realMedia == 'yes') this.favCodec = 'realMedia';
			else if (this.flashMedia == 'yes') this.favCodec = 'flashMedia';
					else if (this.windowsMedia == 'yes') this.favCodec = 'windowsMedia';
				else this.favCodec = 'jpeg';
				setCookie('favCodec', this.favCodec, resultCookiesLife);


			} else {
				this.favCodec = getCookie('favCodec');
			}

			// TODO1: find a solution with these bandwidth... No hardcoding !
			if ((redo) || (!getCookie('favBitrate'))) {
				if (this.bandWidth >= 450) this.favBitrate = 450;
				else if (this.bandWidth >= 150) this.favBitrate = 160;
				else this.favBitrate = 80;
				setCookie('favBitrate', this.favBitrate, resultCookiesLife);
			} else {
				this.favBitrate = getCookie('favBitrate');
			}
		}

		// Save a preference from the config panel
		this.saveUserPref = function(objValue) {

			// If the object isn't checked, we don't save its value, but an empty string
			var pValue = objValue.checked ? objValue.value : '';

			// The name of the value
			var pName = objValue.name;

			// Save is a cookie and print something in debug
			setCookie(pName, pValue, resultCookiesLife);
			trace('Your ' +pName+ ' has been set to ' + pValue);

			// Update the current user
			this.init();

			// If there are some additional operations to execute, this will be defined
			// Otherwise it's just a numb function
			this.additionalSaveOps();
		}

		// Run a bandwidth test and save the result in the user object
		// funcWhenFinished: function to run when the test is done.
		// Ioan Sameli, 06.10.2006
		this.measureBandWidth = function(funcWhenFinished) {

			var currentUser = this;

			// The function to run when the test is done, will also call the funcWhenFinished
			var callBack = function() {

				// Clear the message
				displayMessage();

				// Save the result in a temporary variable, from the request object
				currentUser.lastBWResult = currentUser.testRequestBW.bandWidth;

				// Call the testRequestBW function
				tryCallBack(funcWhenFinished);
			}

			// Launch the request, with a random number in the URL to kill any caching
			var randomNum = new Date().getTime();
			this.testRequestBW = new tsrkit_ahahLoad(staticDataSize4 + '?' + randomNum, '', callBack);

			// Display the waiting message
			displayMessage('Veuillez patienter quelques secondes avant de visionner la vidéo.<br/>Des tests sont en cours d\'exécution.');
		}

		// Returns the bandwidth from the cookie
		// Runs a bandwidth test if cookie isn't available
		// redo (optional): if present, will force the test to rerun, even if we already have the cookie.
		this.checkBandWidth = function(fromFunc, redo) {

			// If we don't have a cookie yet, or want to rerun the test, then run the test !
			if (!getCookie(cookieBW)||redo) {

				// To have access to the user object in the callBack function
				var currentUser = this;

				// The bandwidth test will call back this function when done, that will call the fromFunc to go back to origin.
				var callBack = function() {

					// First of all, save the freaking cookie !
					// If the setCookie doesn't work, stop here to prevent infinite loop
					if (setCookie(cookieBW, currentUser.lastBWResult, resultCookiesLife) == false)return false;

					// And finally, go back to the original function, that will call this function again and get the result.
					tryCallBack(fromFunc);
				}

				// Call the bandwidth test, with the callBack function
				this.measureBandWidth(callBack);

				// Stop here we're done. callBack did the rest of the job.
				return false;
			}

			// This is nothing but a debug print
			else alreadyTested(cookieBW);

			// Return the result
			return getCookie(cookieBW);
		}

		// Windows media player plugin detection.
		// If the value isn't stored in a cookie, call some functions to run test, and create it
		// redo (optional): if present, will force the test to rerun, even if we already have the cookie.
		this.checkWindowsMedia = function(redo) {

			// If there's no cookie yet, or if we want to rerun the tests, call the functions
			if (!getCookie(cookieWM)||redo) {
				if (testWMActiveX() || checkPlugIn("Windows Media")) {
					setCookie(cookieWM, 'yes', resultCookiesLife);
					trace('Test: The Window Media plugin is available ! Cookie created.', 'success');
				} else {
					setCookie(cookieWM, 'no', resultCookiesLife);
					trace('Test: Error ! You don\'t have the Window Media plugin :(  Cookie created.', 'error');
				}
			}

			// This is nothing but a debug print
			else alreadyTested(cookieWM);


			// Return the result
			return getCookie(cookieWM);
		}

		// Real player plugin detection.
		// If the value isn't stored in a cookie, call some functions to run test, and create it
		// redo (optional): if present, will force the test to rerun, even if we already have the cookie.
		this.checkRealPlayer = function(redo) {

			if (!getCookie(cookieRP)||redo) {
				if (testRealActiveX() || checkPlugIn("real")) {
					setCookie(cookieRP, 'yes', resultCookiesLife);
					trace('Test: The Real Media plugin is available ! Cookie created.', 'success');
				} else {
					setCookie(cookieRP, 'no', resultCookiesLife);
					trace('Test: Error ! You don\'t have the  Real mediaplugin. Cookie created.', 'error');
				}
			}

			// This is nothing but a debug print
			else alreadyTested(cookieRP);

			// Return the result
			return getCookie(cookieRP);
		}

		// Windows media player plugin detection.
		// If the value isn't stored in a cookie, call some functions to run test, and create it
		// redo (optional): if present, will force the test to rerun, even if we already have the cookie.
		this.checkMacromediaFlash = function(redo) {

			// If there's no cookie yet, or if we want to rerun the tests, call the functions
			if (!getCookie(cookieMF) || redo) {
				if (DetectFlashVer(minFlashVersion)) {
					setCookie(cookieMF, 'yes', resultCookiesLife);
					trace('Test: The Flash plugin is available ! Cookie created.', 'success');
				} else {
					setCookie(cookieMF, 'no', resultCookiesLife);
					trace('Test: Error ! You don\'t have the Window Media plugin :(  Cookie created.', 'error');
				}
			}

			// This is nothing but a debug print
			else alreadyTested(cookieMF);

			// Return the result
			return getCookie(cookieMF);
		}
	}


  /***********************/
 /***   Ajax module   ***/
/***********************/

	tsrkit_ajaxRequest = function(rSource) {
	// Ioan Sameli, 10.05.2006
	// Call an URL in ajax, and set the function that will handle the answer
	// rSource (string, optional): the URL to load the XML data from

		// If we want to open the request in async or not
		this.async = true;

		// I don't think POST will be very used, so...
		this.rMethod = 'GET';

		// If the source URL has been given in parameter
		if (rSource) this.rSource = rSource;

		// To inject HTML code into the target,
		// will replace the content by default, or add at the end if add is not null
		// TODO: rewrite using DOM ? But i'm injecting HTML code already
		// rResult (string, optional): Some text or HTML to inject in the target.
		// 			If null, will empty the target.
		// append (boolean, optional): If specified, will append the rResult to the target, otherwise will replace the content
		// sTarget (string, optional): to specify another target than the default rTarget's request property.
		this.injectToTarget = function(rResult, append, sTarget) {

			sTarget = sTarget || this.rTarget;

			if (!sTarget) return;

			if (e = $(sTarget)) {

				// If we want to hide the target and that the script.aculo.us effect library is available
				 if (!append && !rResult && typeof Effect == 'object') e.style.display = 'none';

				// For more readable generated code
				rResult += '\n';

				// Replace or append, according to the "append" param
				if (rResult) {
					if (append) e.innerHTML += rResult;
					else e.innerHTML = rResult;
				}
			}
			else trace('injectToTarget: Element ' + sTarget + ' not found \n(' + this.rSource + ')', 'error');
		}

		this.displayTarget = function(invert) {

			// TODO1: this function, together with injectToTarget, is a dirty mess... It should be rewritten.

			if (!this.rTarget) return;

			if (e = $(this.rTarget)) {
				if (typeof Effect == 'object') new Effect.Appear(e, {duration: 0.5});
				else e.style.display = 'block';
			}

			/*
			if (e = $(this.rTarget)) {

				// Check if script.aculo.us effect library is available
				if (typeof Effect == 'object') {
					if (invert) new Effect.Fade(e, {delay: 0.1, duration: 0.1, queue: {position: 'end', scope: 'switchPanel'}});
					else new Effect.Appear(e, {delay: 0.1, duration: 0.3, queue: {position: 'end', scope: 'switchPanel'}});
				} else {
					if (invert) e.style.display = 'none';
					else e.style.display = 'block';
				}
			}
			*/
		}

		// Link the current request to another object, so we'll be able to address the request
		// directly, useful for example with JSON async callback functions.
		// TODO2: maybe we should destroy the object once it's done, to avoid having many useless
		// expired properties in the parent (causing performance / memory problems ?)
		this.linkToParent = function(objParent) {

			// create a new property in the parent, with a unique ID based on the timestamp
			//this.id = new Date().getTime();
           this.id = 1191911829023;
			// Loop until we find a free property. Will actually be useless most
			// of the time, unless we launch more than one request in the same millisecond.
			//while ((typeof objParent.requests[this.id]) != 'undefined') {
			//	this.id++;
			//}

			// Add the current request object to the parent
			objParent.requests[this.id] = this;

			// And add the parent to the current request object
			this.parent = objParent;

			// Print debug
			var reLaunchLink = '<a href="javascript:' + objParent.varName + '.requests[' + this.id + '].launch();">request' + this.id + '</a>';
			trace('New request added to ' + objParent.varName + ': ' + reLaunchLink, 'success');
		}

		// Launch the request !
		// If the request URL is externam, use JSON, otherwise use classic Ajax
		this.launch = function() {

			// Check if the handleResult function is set before launching the request
			if ('function' == typeof this.handleResult) {

				// If the link is internal, use traditional Ajax method, otherwise JSON method
				// TOM if (testIfLinkIsInternal(this.rSource)) this.launchAjax(); 
                //alert(this.rSource.match(/\bfast_wrapper.php\b/));
              // hack by tom
                if (testIfLinkIsInternal(this.rSource) & !this.rSource.match(/\bfast_wrapper\b/)){
                 this.launchAjax();
                 // this.launchJSON(); 
               //  alert('ajax');
                }
				else 
                {
                this.launchJSON();
                //alert('json');
                }

			}
			else trace('Ajaxrequest: no valid handleResult function ?', 'error');

		}

		// A method that will be called after a timeout if the request doesn't get any answer.
		this.handleRequestTimeout = function() {
			this.injectToTarget(requestTimeoutSign);
			this.displayTarget();
		}

		// This method will set the function that the JSON result will call.
		// It can be specified by the argument or generated automatically.
		// Will return the name of the function (string)
		this.setJSONCallbackFunctionName = function(nameFunction) {

			// If no function name, simply return the path to the handleJSONAnswer
			if (! nameFunction) return this.parent.varName + '.requests[' + this.id + '].handleJSONAnswer';

			// If a name has been specified in the argument, create a new function linked to the handleJSONAnswer
			var r = window[this.parent.varName].requests[this.id];
			window[nameFunction] = function(result){r.handleJSONAnswer(result);};
			return nameFunction;
		}

		// For crossdomain request, include a script that will contain JSON data with a callBack function
		this.launchJSON = function() {

			// TODO2: this is often injected into a <ul> html list, so it won't really be valid html
			if (!this.noLoadingSign) this.injectToTarget(ajaxWaitingSign);

			// "Calling" the JSON by adding a script
			//alert(this.rSource);


            loadScript(this.rSource, 'UTF-8');
			//alert('ok');
            trace('JSON: loading the script <a href="' + this.rSource + '">' + this.rSource + '</a>');

			// Manage the timeout: call the timeout function with a timeout
			var p = this;
			var timeoutFunc = function(){
				p.handleRequestTimeout();
			}
			window.setTimeout(timeoutFunc, delayTimeoutJSON);
		}

		this.handleJSONAnswer = function(result) {

			// First, cancel the timeout warning, this is the answer !
			this.handleRequestTimeout = function(){};

			// And forward the result to the handleResult() function
			this.handleResult(result);
		}

		// To launch the ajax request !
		this.launchAjax = function() {

			// create the HTTP Object
			this.request = tsrkit_createXMLHttp();

			if (this.request) {
               // alert();
				// Debug
				trace('AJAX: open this url by ' + this.rMethod + ' <a href="' + this.rSource + '">' + this.rSource + '</a>', 'info');
                // alert("1");
				// TODO2: sometimes (always ?) this is injected in a <ul> html list and it's not valid code
				if (!this.noLoadingSign) this.injectToTarget(ajaxWaitingSign);

				// Create a new instance of the current object so it will be
				// available in the onreadystatechange subfunction
				var currentAjaxRequest = this;

				// Reset the starTime (that will be used to calculate the bandwidth),
				// if we use the same ajax object more than once
				currentAjaxRequest.starTime = 0;
                         
				// Just for more clear and concise code
				var rObj = currentAjaxRequest.request;
              //  alert( currentAjaxRequest.request);
				// Set the url and method of the request
				rObj.open(this.rMethod, this.rSource, this.async);
                  
				// The callback subfunction
				// This embedded function will handle the answer of the http connection,
				// and call the fonction that will handle the result
				// Ioan Sameli, 10.05.2006
				rObj.onreadystatechange = function() {
                        
					// When the answer is here, check if it's valid, and handle the result with "handleResult" function
					if (rObj.readyState == 4) {
                         
						// First, cancel the timeout warning, this is the answer !
						currentAjaxRequest.handleRequestTimeout = function(){};

						// Calculate at what bandwidth the client loaded the answer
						var now = new Date().getTime();
						var time = now - currentAjaxRequest.starTime;
						if (!time) time = 1;
						var size = rObj.responseText.length;
						var speed = Math.round(8 * size / time)
						currentAjaxRequest.bandWidth = speed;
                          
						// When the answer is valid
						if (rObj.responseText.indexOf('invalid') == -1) {
                                 
							// Print debug to console
							trace('AJAX: we got the answer at ' + currentAjaxRequest.bandWidth + 'Kbps ('
								+time + 'ms for ' + Math.round(size / 1024) + 'KB) !', 'success');
                            
                            //foobar
							// Handle the result, give the request as parameter
                           // trace('neu:'+currentAjaxRequest.result);
							
                               // try {
                                    currentAjaxRequest.handleResult(currentAjaxRequest);      
                               // }
                              //  catch(err)
                              ///  {
                            //       alert ('fehler1:'+err.description);
                           //     }  
                            
                          
                            
                            
                            
                             //  alert("ok"); 
							// If the JavaScript in the answer has to be parsed... thank you to prototype !
							if (currentAjaxRequest.parseJS == true) rObj.responseText.evalScripts();

							// Finally, call the freaking callBack function !
							tryCallBack(currentAjaxRequest.rCallback);

						} else {
							trace('AJAX: invalid answer ! (' + currentAjaxRequest.bandWidth + 'Kbps)', 'error');
							currentAjaxRequest.injectToTarget(ajaxErrorSign);
						}
					}

					// readyState 3: the server answered and the datas are loading
					if (rObj.readyState == 3) {
                           
						// When the answer starts to load, save the time, to calculate the bandwidth later
						if (!currentAjaxRequest.starTime) {
							var now = new Date().getTime();
							currentAjaxRequest.starTime = now;
							trace('AJAX: server answered, loading data', 'info');
						}
					}
				}

				// And finally, send the request
				rObj.send(null);

				// Manage the timeout: call the timeout function with a timeout
				var p = this;
				var timeoutFunc = function(){
					p.handleRequestTimeout();
				}
				window.setTimeout(timeoutFunc, delayTimeoutAjax);

				// return true if everything has been a success !
				return true;

			} else {
				trace('Ajax isn\'t available, it doesn\'t work.', 'error')
				return false;
			}
		}
	}


  /*************************/
 /***   Search module   ***/
/*************************/

	// All the playlist related functions
	// Ioan Sameli, 22.09.2006
	tsrkit_search = function(rSource) {

		// This class will use ajax methods
		this.inheritFrom = tsrkit_ajaxRequest;
		this.inheritFrom(rSource);

		// Default playlist HTML template
		this.rTemplate = defaultSearchResultTemplate;

		// arrayResults: an array that contains all the videos to display
		this.renderSearchResults = function(arrayResults, listHeader) {

			var completePlaylistHTML = '';

			listHeader = listHeader || '';

			// Loop on all the video that are in the array
			var p = this;
			arrayResults.each(function(currentVideo, i) {

				// Add a new property, so the $player in the template will be replaced with the player's object name
				currentVideo.player = p.parent.varName;
                //currentVideo.lang = 'de';
				// Parse the template
              //  alert(currentVideo);
				currentItemHTML = parseTemplate(p.rTemplate, currentVideo, i);

				completePlaylistHTML += currentItemHTML;
			});

			// Somehow, if the playlist is not displayed with a small delay, the display will
			// bug when using script.aculo.us effect library. So use a setTimeout with no delay.
			var currentPlaylist = this;

			window.setTimeout(function(){

				// Finally, render the current item to the html list, with the listHeader
				// Or display something if no video has been found
				currentPlaylist.injectToTarget(completePlaylistHTML ? listHeader + completePlaylistHTML : ajaxEmptySign);

				// Redisplay the playlist once everything is done
				currentPlaylist.displayTarget();

				// Parse the links, to link to the real files
				currentPlaylist.parent.parsePlaylistLinks();

			},0);
		}
	}

  /********************************************/
 /***   Ahah: Loading of external panels   ***/
/********************************************/

	// Load an external file in a given HTML Element
	// rSource (string): the URL to load the XML data from
	// rTarget (string): the ID of the target element to inject the data in
	// initfunction(optional): function to execute once the panel has been loaded
	// Ioan Sameli, 25.09.2006
	// TODO: i'm not sure this function works with external URL (ajax will switch to JSON), to test
	tsrkit_ahahLoad = function(rSource, rTarget, initFunction) {

		// This class will use ajax methods
		this.inheritFrom = tsrkit_ajaxRequest;
		this.inheritFrom(rSource);

		// The target
		this.rTarget = rTarget;

		// If after loading the data we need to execute something
		this.initFunction = initFunction ? initFunction : false;

		var ahahCallback = this.initFunction;

		// Set to true to parse the JavaScript code fragments in the loaded document
		// Warning: If yes, the document loaded needs to be strictly valid XML !
		this.parseJS = true;

		this.handleResult = function(answer) {

			// If a target element is specified, insert the answer into target.
			// If no target has been specified, just append to the document, and as ID give the source URL
			// Strip the scripts if they've been parsed
			var fillTarget = answer.rTarget || answer.rSource;

			fillBlock(fillTarget, answer.request.responseText.stripScripts());

			// Call the callback
			tryCallBack(ahahCallback);

			// Extensions support
			// answer.additionalAhahOps();
		}

		// Launch immediatly
		// TODO: maybe do this a better way
		this.launch();
	}



  /********************************/
 /***   Configuration module   ***/
/********************************/


  /***********************************/
 /***   Random useful functions   ***/
/***********************************/

	function getXmlElement(elmRoot,elmName) {
	// Just a cool little thing to easily extract data from XML
		return elmRoot.getElementsByTagName(elmName).length?(a=elmRoot.getElementsByTagName(elmName).item(0).firstChild) ? a.data : '' : '';
		}

	// To print something in the console, used from nearly everywhere
	// str (string): the string to display in the console
	// msgClass (string, optional): to apply a CSS class on the debug message (typically, "info", "error", "success")
	// seriousness (integer, optional): the seriousness of the debug comment. The ones above 1 are currently ignored.
	function trace(str, msgClass, seriousness) {

		// TODO: make the minimum seriousness configurable
		if (seriousness > 1) return;

		// If the debugZone object is available print the comment in it !
		if ((typeof debugZone) == 'object') {

			// Create a new dom element, and insert it at the top of the zone element
			var newEntry = document.createElement('p');
			newEntry.innerHTML = str;
			if (msgClass)newEntry.className = msgClass;
			debugZone.insertBefore(newEntry, debugZone.firstChild);

		}

		// If there's no loggingZone yet, save all the messages in an array to display later
		// I changed, it will always save them even when the log is displayed.
		// So if the panel is reoladed, will display the whole historic.
		if ('undefined' == typeof traceLog) {
			traceLog = new Array;
		}
		traceLog.push({str:str,msgClass:msgClass});
	}

	// function: Get a value from cookie
	function getCookie(id) {
		return getParam(id, document.cookie, ';');
	}

	function setCookie(id, val, age) {
	// function: Save a value from a cookie
	// age (integer, optional) is the maximum age in day
		if (!age)age = 90;
		var expire = new Date().getTime()+(1000*60*60*24*age); /* Convert age in miliseconds */
		var expDate = new Date(expire);
		document.cookie = id+"="+val+"; expires="+expDate.toGMTString()+"; path=/;";
		if (!document.cookie)return false;
	}

	// Split a string, and return the value of the asked parameter.
	// When only pName is provided, will get a parameter from the URL
	// But can alse be useful to get a parameter's value from cookie, hash, or other random string...
	// Parameters :
	//    pName (string, optional): the parameter's name to get. If not provided, will return an object with all the parameters
	//    pString (string, optional): the string containing all the params, if not given, the current URL querystring will be used
	//    pSeparator (string, optional): the separator charachter, if not given, the "&" will be used
	function getParam(pName, pString, pSeparator) {

		// If there's no string to search in, use the current url, after removing the ' ? ' at the beggining.
		if (typeof pString == 'undefined') pString = window.location.search.substring(1,window.location.search.length);

		// Default separator is '&', for urls
		pSeparator = pSeparator || '&';

		// Initialize the object that will contain all the parameters
		var allParams = $H();
		
		// Split the string with the separator to get separate tokens, and loop trough them
		// To add each parameter and its value to the allParams object
		pList = pString.split(pSeparator);

		pList.each(function(pCurrent) {

			var cParam = pCurrent.split('=');
			allParams[cParam[0].replace(/\s/, '')] = cParam[1];
			
		});
		
		// If an argument has been specified and it is found, return it.
		// Otherwise, return the object containing all the params
		return pName ? (allParams[pName]) : (allParams);
	}

	// Add, replace or remove a parameter in a given string.
	// Parameters :
	//    pName (string): the name of the parameter to add
	//    pValue (string, optional): the value for the param. If not provided and the param is present in the string, it will be removed.
	//    pString (string, optional): the string containing all the params, if not given, the current URL querystring will be used
	//    pSeparator (string, optional): the separator charachter, if not given, the "&" will be used
	function addParam(pName, pValue, pString, pSeparator) {
       // alert(pValue); 
		var allParams = getParam(null, pString, pSeparator);

		// Add or remove the param
		if (pValue != null) allParams[pName] = pValue;
		else delete allParams[pName];
         
		// Build the new string with the new param
		var newPString = '';
		
		allParams.each(function(cParam, cName) {
			if (cParam) {
                 
				// Separator if more than one item
				newPString += newPString ? pSeparator : '';
				newPString += cParam.key + '=' + cParam.value;
			}
		});
         //  alert(newPString);       
		return newPString;
	}

	// Sometimes the callback functions can't be just a function object (mostly because of the object hierarchy)
	// so, we have to check if it is a string or a function.
	// NOTE: String support disabled, will display an error if a string is given as param! String is evil!
	//    f (function or string): the function that has to be executed
	//    a (anything, optional): the parameter for the function, if needed. Can only be used when f is a function
	function tryCallBack(f, a) {
		if (typeof f == 'function') {
			try {f(a);}
			catch(er) {trace('tryCallBack error'.bold() + ': ' + er + '<hr/>' + f + '<hr/>', 'error');}
		} else if (typeof f == 'string') alert(f);
	}

	// Display a message, or hide it if msg is null or so
	function displayMessage(msg) {

		// TODO2: function disabled because it causes a bug on MSIE
		return;

		if (msg){
			fillBlock('msgZone',msg);
			new Effect.Appear('msgZone', {queue: {position: 'end', scope: 'msgZone'}});
		} else {
			new Effect.Fade('msgZone', {queue: {position: 'end', scope: 'msgZone'}});
		}
	}

	// Test if the given URL is to another domain using DOM
	// Used for Ajax request, to switch to crossdomain support
	function testIfLinkIsInternal(strLink) {

		// Create a ghost DOM link, and assign the URL
		var ghostLink = document.createElement('a');
		ghostLink.href = strLink;

		// This is to help MSIE
		if (!ghostLink.hostname)return true;

		// Return the result of a test between current domain and the link's domain
		return (ghostLink.hostname == self.location.hostname);
	}

	// Will fill the given block with the fillWith. If the block doesn't exist,
	// will create a block with the given ID and append it to the document.
	//    blockId (string): the ID of the HTML element
	//    fillWith (string): the content to add in the element
	function fillBlock(blockId,fillWith) {
		if (!blockId)return false;

		var block = $(blockId);

		// If the block already exists, just fill it with the content
		if (block) {
			block.innerHTML = fillWith;
			block.display = 'block';

		// Otherwise, create a block with the given ID, and fill it
		} else {
			var block = document.createElement('div');
			block.innerHTML = fillWith;
			block.id = blockId;
			document.body.appendChild(block);
		}
	}

	// Alias for tsrkit_templateParser
	// This could be done a lot simplier,
	// but i need the object model to be able to add extension to its prototype.
	parseTemplate = function(template, properties, count) {
		var t = new tsrkit_templateParser(template, properties, count);
		return t.template;
	}

	// Will replace the $thing in the template string by the properties.thing value
	// Parameters
	//    template (string): the template, with keywords to search and replace such as $title or $id
	//    properties (object): has to conatin all the data, with keys corresponding to the keywords of template
	//    count (integer, optional): the index in the list, actually the line (1, 2, 3, ...)
	tsrkit_templateParser = function(template, properties, count) {

		if (!template) return;

		if (!properties) {
			var err = '<em>parseTemplate</em>: Sorry, no properties given for template';
			trace(err, 'error');
			return err;
		}

		this.template = template;
		this.properties = properties;
		this.count = count;

		this.additionalParsingOps();

		// Execute it twice, because each keyword may be present more than once.
		// TODO: this could be done better i guess...
		for (var i = 0; i < 2; i++) {

			// Special case with the line. Add 1 to the count since most usually the index will start at 0.
			if ('undefined' != typeof this.count) {
				this.count += 1;
				this.template = this.template.replace('$line',((parseInt(this.count) % 2) + 1));
				this.template = this.template.replace('$index',this.count);
			}

			// Replace all the properties
			for (var prop in this.properties) {
				this.template = this.template.replace('$' + prop,this.properties[prop]);
			}
		}
	}


	// Create and return a XMLHttpRequest object, that we'll use for ajax operations
	// copy pasted from http://fettig.net/playground/ajax-subdomain/xmlhttp.js
	// adapted from http://jibbering.com/2002/4/httprequest.html
	tsrkit_createXMLHttp = function() {
		var xmlhttp;
		try {
			xmlhttp = new ActiveXObject("Msxml2.XMLHTTP");
		} catch (e) {
			try {
				xmlhttp = new ActiveXObject("Microsoft.XMLHTTP");
			} catch (E) {
				xmlhttp = false;
			}
		}
		if (!xmlhttp && typeof XMLHttpRequest != 'undefined') {
			xmlhttp = new XMLHttpRequest();
		}
		return xmlhttp;
	}

	// Add a function that will be executed at page loading
	// fnc: can be either a function or a string that will be executed
	function addToStart(fnc) {
		if (!window.listStart) window.listStart = new Array();
		window.listStart.push(fnc)
	}

	// Load a javascript file by appending a <script> tag to the head.
	// Parameters:
	//    jsFile (string): the path to the javascript file
	//    charset (strinf, optional): If a charset has to be specified (UTF-8 or so)
	function loadScript(jsFile, charset){
		var head = document.getElementsByTagName('head')[0];
		var script = document.createElement('script');
		script.type = 'text/javascript';
		if (charset) script.charset = charset;
		script.src = jsFile;
		head.appendChild(script);
	}















      /***************************************************/
     /***************************************************/
    /***                                             ***/
   /***         OLD AND STILL USED FUNCTIONS        ***/
  /***                                             ***/
 /***************************************************/
/***************************************************/

// TODO: clean these messy functions !











  /**********************************/
 /***   Window media detection   ***/
/**********************************/

	function alreadyTested(cookieName) {
		return;
		trace('You already have a cookie called <b>' + cookieName + '</b>, with the value "<b>' + getCookie(cookieName)
			+ '</b>" [<a href="javascript:setCookie(\'' + cookieName + '\',\'\',0);trace(\'The cookie ' + cookieName
			+ ' has been deleted.\');">delete this cookie</a>]');
	}

	
	
	
	
	
	
	
	
	// Fill a field in the configuration panel
	function setUserDetect(elmId, val) {
		var elmObj = $('usrResult' + elmId);
		var imgObj = $('usrResult' + elmId + 'img');
		if (val == 'yes') {
			var elmCaption = str_present;
			var elmColor = 'green';
			var imgFile = okayIcon;
		} else {

			var elmCaption = str_notdetected;
			var elmColor = 'red';
			var imgFile = troubleIcon;
		}
		if (elmObj) {
			elmObj.innerHTML = elmCaption;
			elmObj.style.color = elmColor;
		}
		if (imgObj)imgObj.innerHTML = imgFile;
	}

	function testWMActiveX() {
	// MSIE only !
	// Essaie de créer un ActiveX Windows Media. Uniquement valable pour MSIE
		try {
			testWMObject = new ActiveXObject("MediaPlayer.MediaPlayer.1");
			return true;
		}
		catch(f) {
			return false;
		}
	}

	function checkPlugIn() {
	// Mozilla compatible browsers only !
	// recherche dans la collection de plugins avec les strings passés en paramètres
	// Par exemple : "Real","Flash", etc...
	// Ioan Sameli, 2003

		// si le navigateur (IE notamment) ne supporte pas cette méthode, on se casse
		if (! navigator.plugins.length)return false;

		// initialisation des variables
		allFound = false;
	    plugInsCollection = navigator.plugins;

		// Pour chaque plugin trouvé, on regarde si il contient tous les arguments passés à la fonction
		for (var i = 0; i < plugInsCollection.length; i++) {

			// On passe tout en majuscule pour ne pas être embêté par des bêtises
	        plugInDescription = " " + (plugInsCollection[i].description.toUpperCase());
			plugInName = " " + (plugInsCollection[i].name.toUpperCase());

			// on cherche chaque arguments
			for (j=0;j<arguments.length;j++) {
				var argUp = arguments[j].toUpperCase();
				if (plugInDescription.indexOf(" " + argUp)!=-1 || plugInName.indexOf(" " + argUp)!=-1) {
					allFound = true;
				} else {
					if (allFound != true)allFound = false;
					break;
				}
			}
			if (allFound)return true;
	   	 }
		return false;
	}

  /********************************/
 /***   Real media detection   ***/
/********************************/

	/*function testRealActiveX() {
		if (navigator.appVersion.indexOf('MSIE')>0)return testThisActiveXVB('rmocx.RealPlayer G2 Control.1');
	}*/

	function testRealActiveX() {
	// Essaie de créer un ActiveX Real et d'en récuperer la version, ne marche que sous IE
	// Désactivé, parce que Real Enterprise ne peut pas être détecté sous MSIE via JS. Voir la fonction en VBscript.
	// Ioan Sameli, 2003

		try {
			testObject = new ActiveXObject("rmocx.RealPlayer G2 Control.1");

			// Since G2 (Real 6), all Real player version numbers begin by 6
			embedVersion = testObject.GetVersionInfo();

			// First version supported : 6.0.6.131 = G2 Gold
			versionArray = embedVersion.split(".");
			conditionA = versionArray[0]>=6;
			conditionB = versionArray[1]>=0;
			conditionC = versionArray[2]>=6;
			conditionD = versionArray[3]>=131;

			if (conditionA && conditionB && conditionC && conditionD)return true;
			return false;
			}
		catch(e) {
			return false;
			}
	}

  /**************************************/
 /***   Macromedia flash detection   ***/
/**************************************/

	// Return TRUE if the flash version installed is at least the one given in parameter, otherwise returns FALSE
	// TODO: this function works, but could be better
	function DetectFlashVer(fVersion) {
	 	fVersion = parseFloat(fVersion);

		if (!navigator.plugins.length) {
			var currentVersion = JSGetSwfVerIE();

			if (currentVersion >= fVersion)var regtrebvgf = true;
			else var regtrebvgf = false;

			if (currentVersion >= fVersion)return true;
			else return false;
		}

		for (var i = 15; i > 0; i--) {
			versionStr = JSGetSwfVer(i);

			if (versionStr == -1 ) {
				return false;
			} else if (versionStr != 0) {
				if (typeof versionStr == 'int' || typeof versionStr == 'number') versionMajor = versionStr;

				else {
					versionArray = versionStr.split(".");
					versionMajor = versionArray[0];
				}

				if (versionMajor >= fVersion) {
					return true;
				} else {
					return false;
				}
			}
		}
	}
	function JSGetSwfVer(i) {
	// Détection flash / navigateur non IE

		if (navigator.plugins != null && navigator.plugins.length > 0) {
			if (navigator.plugins["Shockwave Flash 2.0"] || navigator.plugins["Shockwave Flash"]) {
				var swVer2 = navigator.plugins["Shockwave Flash 2.0"] ? " 2.0" : "";
		      	var flashDescription = navigator.plugins["Shockwave Flash" + swVer2].description;
				descArray = flashDescription.split(" ");
				tempArrayMajor = descArray[2].split(".");
				versionMajor = tempArrayMajor[0];
				versionMinor = tempArrayMajor[1];
				if (descArray[3] != "") {
					tempArrayMinor = descArray[3].split("r");
				} else {
					tempArrayMinor = descArray[4].split("r");
				}
				versionRevision = tempArrayMinor[1] > 0 ? tempArrayMinor[1] : 0;
				flashVer = versionMajor + "." + versionMinor + "." + versionRevision;
	      		} else {
				flashVer = -1;
			}
		} else {
			// Flash n'as pas été trouvé
			flashVer = -1;
		}
		return flashVer;
	}
	function JSGetSwfVerIE() {
	// Détection flash / seulement MSIE
		for (var i = 15; i > 0; i--) {
			try{
				var flash = new ActiveXObject("ShockwaveFlash.ShockwaveFlash." + i);
				return i;
			}
			catch(e) {
			}
		}
		return -1;
	}



  /*****************************/
 /***   CONSOLE FUNCTIONS   ***/
/*****************************/

	// TODO: this is not yet done !

	tsrkit_console = function() {

		this.loadPanel = function() {
			// AHAH loading
			// Insert the saved items from the array
			// rewrite the trace function
		}


		this.display = function() {
			// if not loaded, load it
		}

		this.hide = function() {
			// just CSS
		}
	}

  /**********************/
 /***   EXTENSIONS   ***/
/**********************/

	/*
		Numb methods that may be redefined by user to add extensions and new functionalities
		To add an extension, just redefine one of these methods in a file defined
		by the fileExtension variable (see top of this file)
	*/

	// Numb method, if needed it has to be set later by the user
	tsrkit_user.prototype.additionalSaveOps = function() {}

	// Called when the user is initialisated
	tsrkit_user.prototype.additionalInitOps = function() {}

	// Called when a video is played, and also on player initialisation
	tsrkit.prototype.additionalPlayOps = function() {}

	// Called when a template is parsed
	tsrkit_templateParser.prototype.additionalParsingOps = function() {}

	// Called when some data is loaded with Ahah
	// tsrkit_ahahLoad.prototype.additionalAhahOps = function() {}

	/* Load the file containing extensions */
	loadScript(fileExtension);