/*
 *    Copyright Baracoda
 *    svn revision: 3486
 *    Latest modification: 
 */
var AbstractPlugin = Class.create();
AbstractPlugin.prototype = {
  initialize: function() {
    this.url = "";
    this.listener = null;
    this.isOK = false;
  },

  setUrl: function(url) {
    this.url = url;
  },

  setListener: function(listener) {
    this.listener = listener;
  },

  play: function() {},

  pause: function() {},

  stop: function() {},

  fastForward: function() {},

  fastReverse: function() {},

  getVolume: function() { return 0; },

  setVolume: function(vol) {},

  getPosition : function() {},

  getPluginInfo: function() { return "Abstract"; },
  
  isPluginOK: function() { return this.isOK;},
  
  getCurrentCapabilities: function() {
    return $H({
      play: false, pause: false, stop: false,
      fastForward: false, fastReverse: false,
      getVolume: false, setVolume: false });
  },

  getPluginName: function() {},

  getPluginObject: function() {}
};
CodecTable = $H({
  'mp3': 'mp3',
  'wma': 'wma',
  'asf': 'wma',
  'rm': 'real',
  'ram': 'real',
  'cook': 'real',
  'unk': 'mp3'
});

var Asset = Class.create();
Asset.prototype = {
	initialize: function(codec, urls, productId, isPodcast) {
    this.codec = null;
    this.productId = productId;
    this.isPodcast = isPodcast ? isPodcast: false;
    if(codec)
      this.codec = CodecTable.get(codec.toLowerCase());
		this.urls = urls;
		this.current = 0;
	},
	
	next_url: function() {
		if(!this.urls || this.urls.length == 0)
			return null;

	  if(this.current == this.urls.length)
      return null;

	  var url = this.urls[this.current];
	  this.current += 1;
	  return url;
	},
	
	reset: function() {
		this.current = 0;
	},
	
	get_codec: function() {
	  return this.codec;
	},

  isBad: function() {
    return !this.codec || !this.urls || this.urls.length == 0;
  },

  getExtension: function(url) {
    try {
      var matches = /^((\w+):\/\/\/?)?((\w+):?(\w+)?@)?([^\/\?:]+):?(\d+)?(\/?[^\?#;\|]+)?([;\|])?([^\?#]+)?\??([^#]+)?#?(\w*)/(url);
      if(matches && matches.length >= 9) {
        var path = matches[8];
        if(path == null)
          return null;

        var dot = path.lastIndexOf('.');
        if(dot >= 0)
          return path.substring(dot);
      }
    } catch(e) {
      // IE doesn't understand regexps...
    }

    return null;
  },
	
  needQtEncapsulation: function() {
    var codec = this.get_codec();
    if(codec && (codec == 'wma' || codec == 'asf'))
      return true;

    if(this.urls == null || this.urls.length == 0)
      return false;

    return this.isPodcast;

    /*

    var currentUrl = null;
    if(this.current >= this.urls.length) 
      currentUrl = this.urls[this.urls.length - 1];
    else
      currentUrl = this.urls[this.current];

    var ext = this.getExtension(currentUrl);
    if(ext && ext == '.mp3')
      return true;

    return false;
    */
  }
};
var AudioLib = {
  AudioPlayer: null,

 getPlayer: function(eventHandler, language) {
   if(!AudioLib.AudioPlayer) {
     AudioLib.AudioPlayer = new Casing(eventHandler);
   }
   AudioLib.AudioPlayer.language = language;
     return AudioLib.AudioPlayer;
 }
}
var Casing = Class.create();
Casing.prototype = Object.extend(new AbstractPlugin(), {
  initialize: function(eventHandler) {
  	// browser and plugin detection
    detect = new Detect();
    this.browser = detect.browser;
    this.browserVersion = detect.version;
    this.OS = detect.OS;
    this.plugins = new Plugins();
    this.eventHandler = eventHandler;
    this.asset = null;
    this.previous_pstatus = null;
    this.timeout = new Array();	
    this.timeout[0] = this.timeout[1] = null;
    new PeriodicalExecuter(
      this.pstatus_poller.bind(this),
      audiolib_latency);
    this.useTimeout = true;  
    this.timeLimit = null;
    this.pluginsAttached = false;
		this.bufferingStart = 0;
		this.isSettingAsset = false;
    this.language = null;
    Event.observe(window, 'unload', this.onUnload.bindAsEventListener(this));
  },

  myHandler: function(evt, name, arg) {
	 try {
		 if(!this.language) {
			 this.eventHandler(evt, name, arg);
			 return;
		 }

		 var map = translations.get(this.language);
		 if(!map) {
			 this.eventHandler(evt, name, arg);
			 return;
		 }

		 var trans = map.get(name);
		 if(trans == null) {
			 this.eventHandler(evt, name, arg);
			 return;
		 }

		 this.eventHandler(evt, trans, arg);
	 } catch(e) {}
  },
  
  attachPlugins: function() {
    if(this.pluginsAttached)
      return this.current_player ? true : false;
    else
      this.pluginsAttached = true;

    switch(this.OS) {
      case 'Mac':
        if(this.browser == "Safari") {
          this.wmqtp = new HTML5Audio(this.myHandler.bind(this));
          break;
        }

        if(this.plugins.VLCDetected) {
          this.wmqtp = new VLC(this.myHandler.bind(this));
          break;
        }

        var qtEnabled = false;
        if(qtEnabled)
          this.wmqtp = new QTP(this.myHandler.bind(this));
        else {
          report_missing_plugin('vlc', this.myHandler.bind(this));
          return false;
        }
        break;

      case 'Linux':
        if(!this.plugins.VLCDetected) {
          report_missing_plugin('vlc', this.myHandler.bind(this));
          return false;
        }

        this.wmqtp = new VLC(this.myHandler.bind(this));
        break;

      case 'Windows':
        if(this.browser == 'Firefox' && !this.plugins.WMPFirefoxDetected) {
          report_missing_plugin('wmp', this.myHandler.bind(this));
          return false;
        }

        this.wmqtp = new WMP(this.myHandler.bind(this));
        break;
    }

    this.rp = this.plugins.RealDetected ? new RPL(this.myHandler.bind(this)) : null;
    this.current_player = this.wmqtp;
    return true;
  },

  setAsset: function(asset) {
    if(!this.attachPlugins())
      return;

    this.stop();

    this.asset = asset;
    if(!asset || asset.isBad()) {
      this.myHandler('pluginStatus', 'BadAsset', '');
      this.clearAsset();
    } else {
      this.asset.reset();
			this.isSettingAsset = true;
      this.useNextUrl();
		}
  },

  clearAsset: function() {
    this.asset = null;
  },

	setTimeLimit: function(limit) {
		if (limit != null)
			this.timeLimit = parseInt(limit);
		else
		this.timeLimit = null;
  },
  
  setUrl: function(url, codec) {
    if(!this.attachPlugins())
      return;

	if ('undefined' != typeof codec) {
		codec = codec.toLowerCase();
	}	
  	switch (codec) {
  		case 'real':
  			if (null != this.rp) {
  				this.current_player = this.rp;
  				this.current_player.setUrl(url);
  			} else
  			  report_missing_plugin('rp', this.myHandler.bind(this));
  		break;	
  		
  		case 'mp3':
  		case 'wma':
  		case 'asf':
  			if (null != this.wmqtp) {
  				this.current_player = this.wmqtp;
  				this.current_player.setUrl(url, this.asset.needQtEncapsulation());
  			} else
  			  report_missing_plugin('wmp', this.myHandler.bind(this));
  		break;
  		
  		default:
  			if (null != this.wmqtp) {
  				this.current_player = this.wmqtp;
  				this.current_player.setUrl(url);
  			} else {
				//alert('Codec: ' + codec);
				if (this.myHandler)
					this.myHandler('pluginStatus', 'UnsupportedCodec', null);
			}
  	}
  },
  
  get_timestamp: function() {
    return (new Date()).getTime();
  },
  
  start_timeout: function(index) {
    this.timeout[index] = this.get_timestamp();
  },
  
  stop_timeout: function(index) {
    this.timeout[index] = null;
  },
  
  play: function() {
    if(!this.attachPlugins())
      return;

    if(!this.asset) {
      this.myHandler('pluginStatus', 'NoAsset', '');
      return;
    }

    this.current_player.play();
    if (this.useTimeout)
      this.start_timeout(0);
  },

  pause: function() {
    if(!this.attachPlugins())
      return;

    this.current_player.pause();
    this.stop_timeout(0);
  },

  stop: function() {
    if(!this.attachPlugins())
      return;

    this.current_player.stop();
    this.stop_timeout(0);
  },

  fastForward: function() {
    if(!this.attachPlugins())
      return;

    this.current_player.fastForward();
  },

  fastReverse: function() {
    if(!this.attachPlugins())
      return;

    this.current_player.fastReverse();
  },

  getVolume: function() {
    if(!this.attachPlugins())
      return 0;

    return this.current_player.getVolume();
  },
  
  setVolume: function(vol) {
    if(!this.attachPlugins())
      return;

    if(this.wmqtp)
      this.wmqtp.setVolume(vol);

    if(this.rp)
      this.rp.setVolume(vol);
  },

  getPosition : function() {
    if(!this.attachPlugins())
      return;

    return this.current_player.getPosition();
  },

  getPluginInfo: function() {
    if(!this.attachPlugins())
      return;

    return this.current_player.getPluginInfo();
  },

  getPluginType: function() {
    return this.current_player.getPluginType();
  },

  getPluginObject: function() {
    return this.current_player.getPluginObject();
  },

  getPluginStatus: function() {
    if(!this.pluginsAttached)
      return 'Idle';

  	pstatus = 'Error';
  	try {
  	  if(!this.current_player)
  	    pstatus = 'NoPlayer';
  	  else if ('' == this.current_player.url)
  		  pstatus = 'Idle';
  		else
  		  pstatus = this.current_player.getPluginStatus();
  	} catch(err) {}

  	return pstatus;
  },
  
  getCurrentCapabilities: function() {
    if(!this.attachPlugins())
      return;

    this.current_player.getCurrentCapabilities();
  },

  onUnload: function() {
    // force the player to stop before closing the page
    this.stop();
  },
  
  pstatus_poller: function() {
    var pstatus = this.getPluginStatus();
    
    // use the event to implement the asset logic.
    // i.e. fallback on the next url when a server doesn't reply
 		this.asset_watchdog(pstatus);
	
		// limit the buffering time to 30s
		if(pstatus == 'Buffering') {
			var now = new Date().getTime();
			if(this.bufferingStart == 0)
				this.bufferingStart = now;
			else if(now - this.bufferingStart > 90000)
				this.stop();
		} else
			this.bufferingStart = 0;
		
		if(this.isSettingAsset) {
			this.isSettingAsset = false;
			
			// force the status to be reported
			this.previous_pstatus = 'Idle';
		}
 		
 		// report pstatus changes to the client's listener
  	if(this.myHandler && pstatus != this.previous_pstatus)
  		this.myHandler('pluginStatus', pstatus, null);
    this.previous_pstatus = pstatus;
  },
  
  useNextUrl: function() {
		if(!this.asset)
			return false;
		
		var url = this.asset.next_url();
		if(!url)
			return false;
			
		this.setUrl(url, this.asset.get_codec());
		return true;
  },
  
  asset_watchdog: function(pstatus) {
    // did we reach timeout ?
    if(this.timeout[0] && this.get_timestamp() - this.timeout[0] > audiolib_timeout*1000) {
      //spy('timeout');
      // a connection timeout occured, switch to the next url
      this.stop_timeout(0);
      if (this.myHandler)
  		  this.myHandler('pluginStatus', 'TimeOut', null);

      if (this.useNextUrl())
     		this.play();
    }
    
    if (this.timeout[1] && this.timeLimit && this.get_timestamp() - this.timeout[1] > this.timeLimit * 1000) {
		this.stop();
		this.stop_timeout(1);
		if (this.myHandler)
			this.myHandler('timeLimit', 'TimeLimit', null);
    }
    

    // interrupt the timeout event
    if (pstatus == 'Playing') {
      this.stop_timeout(0);
      if (null == this.timeout[1] && null != this.timeLimit)
		this.start_timeout(1);
    }
    
  	// if an error occured while connecting to an asset
  	// use the next url if possible
  	if (pstatus == 'Error' && this.asset) {
  	  if (this.useNextUrl())
  	    this.play();
  	}
  	
  }, 
  
  getMediaInfo: function() {
  	this.current_player.getMediaInfo();
  }
});

var audiolib_latency = 0.2;
var audiolib_timeout = 20;

// Based on Peter-Paul Koch browser detection script (http://www.quirksmode.org/js/detect.html)
var Detect = Class.create();
Detect.prototype = {
	initialize: function() {
		// detecting browser, version & OS 
		this.browser = this.searchString(this.dataBrowser) || "An unknown browser";
		this.version = this.searchVersion(navigator.userAgent)
			|| this.searchVersion(navigator.appVersion)
			|| "an unknown version";
		this.OS = this.searchString(this.dataOS) || "an unknown OS";
		this.plugins = new Plugins();
	},
	
	searchString: function (data) {
		for (var i=0;i<data.length;i++)	{
			var dataString = data[i].string;
			var dataProp = data[i].prop;
			this.versionSearchString = data[i].versionSearch || data[i].identity;
			if (dataString) {
				if (dataString.indexOf(data[i].subString) != -1)
					return data[i].identity;
			}
			else if (dataProp)
				return data[i].identity;
		}
	},
	
	searchVersion: function (dataString) {
		var index = dataString.indexOf(this.versionSearchString);
		if (index == -1) return;
		return parseFloat(dataString.substring(index+this.versionSearchString.length+1));
	},
	
	dataBrowser: [
		{ 	string: navigator.userAgent,
			subString: "OmniWeb",
			versionSearch: "OmniWeb/",
			identity: "OmniWeb"
		},
		{
			string: navigator.vendor,
			subString: "Apple",
			identity: "Safari"
		},
		{
			prop: window.opera,
			identity: "Opera"
		},
		{
			string: navigator.vendor,
			subString: "iCab",
			identity: "iCab"
		},
		{
			string: navigator.vendor,
			subString: "KDE",
			identity: "Konqueror"
		},
		{
			string: navigator.userAgent,
			subString: "Firefox",
			identity: "Firefox"
		},
		{
			string: navigator.vendor,
			subString: "Camino",
			identity: "Camino"
		},
		{		// for newer Netscapes (6+)
			string: navigator.userAgent,
			subString: "Netscape",
			identity: "Netscape"
		},
		{
			string: navigator.userAgent,
			subString: "MSIE",
			identity: "Explorer",
			versionSearch: "MSIE"
		},
		{
			string: navigator.userAgent,
			subString: "Gecko",
			identity: "Mozilla",
			versionSearch: "rv"
		},
		{ 		// for older Netscapes (4-)
			string: navigator.userAgent,
			subString: "Mozilla",
			identity: "Netscape",
			versionSearch: "Mozilla"
		}
	],
	
	dataOS : [
		{
			string: navigator.platform,
			subString: "Win",
			identity: "Windows"
		},
		{
			string: navigator.platform,
			subString: "Mac",
			identity: "Mac"
		},
		{
			string: navigator.platform,
			subString: "Linux",
			identity: "Linux"
		}
	]
};

function report_missing_plugin(plugin, listener) {
  var name = 'uknown plugin';
  var url = 'unknown location';
  switch(plugin) {
    case 'wmp':
      if(detect.browser == 'Firefox') {
        name = 'Windows Media Player Firefox';
        url = 'http://port25.technet.com/pages/windows-media-player-firefox-plugin-download.aspx';
      } else {
        name = 'Windows Media Player';
        url = 'http://www.microsoft.com/windows/windowsmedia/download/AllDownloads.aspx?displang=en&qstechnology=';
      }
      break;

    case 'rp':
      name = 'Real Player';
      switch(detect.OS) {
      case "Windows":
        url = "http://france.real.com/download/player/win/v11/fr/?src=fd_fr_rp_select&rsrc=fd_fr_rp_select";
        break;

      case "Mac":
        url = "http://france.real.com/download/player/mac/v11/fr/?src=fd_fr_rp_select&rsrc=fd_fr_rp_select";
        break;

      case "Linux":
        url = "http://france.real.com/download/player/linux/v11/en_eu/?src=fd_fr_rp_select&rsrc=fd_fr_rp_select";
        break;
      }
      break;

    case 'qt':
      name = 'QuickTime';
      url = 'http://www.apple.com/quicktime/download/';
      break;

    case 'vlc':
      name = 'VLC Mozilla Plugin';

      switch(detect.OS) {
        case "Windows":
          url = "http://www.videolan.org/vlc/";
          break;

        case "Mac":
          if(navigator.userAgent.indexOf('Intel') >= 0)
            url = "http://www.videolan.org/mirror-geo.php?file=vlc/1.0.5/macosx/vlc-plugin-1.0.5-intel.dmg";
          else
            url = "http://www.videolan.org/mirror-geo.php?file=vlc/1.0.5/macosx/vlc-plugin-1.0.5-powerpc.dmg";
          break;

        case "Linux":
          url = "http://www.videolan.org/vlc/";
          break;

      }
      break;
  }

  url = "http://phoenix.radiomee.com/radiomee/help/";

  if (listener)
    listener('missingPlugin', name, url);
}

var detectableWithVB = false;
var pluginFound = false;


function canDetectPlugins() {
    if( detectableWithVB || (navigator.plugins && navigator.plugins.length > 0) ) {
	return true;
    } else {
	return false;
    }
}

function detectFlash() {
    var pluginFound = detectPlugin('Shockwave','Flash'); 
    if(!pluginFound && detectableWithVB) {
	var pluginFound = detectActiveXControl('ShockwaveFlash.ShockwaveFlash.1');
    }
    return pluginFound;
}

function detectDirector() { 
    var pluginFound = detectPlugin('Shockwave','Director'); 
    if(!pluginFound && detectableWithVB) {
	var pluginFound = detectActiveXControl('SWCtl.SWCtl.1');
    }
    return pluginFound;
}

function detectQuickTime() {
    var pluginFound = detectPlugin('QuickTime');
    if(!pluginFound && detectableWithVB) {
	var pluginFound = detectQuickTimeActiveXControl();
    }
    return pluginFound;
}

function detectVLC() {
  return detectPlugin('VLC');
}

function detectReal() {
    var pluginFound = detectPlugin('RealPlayer');
    if(!pluginFound && detectableWithVB) {
	var pluginFound = (detectActiveXControl('rmocx.RealPlayer G2 Control') ||
		       detectActiveXControl('RealPlayer.RealPlayer(tm) ActiveX Control (32-bit)') ||
		       detectActiveXControl('RealVideo.RealVideo(tm) ActiveX Control (32-bit)'));
    }	
    return pluginFound;
}

function detectWindowsMedia() {
    var pluginFound = detectPlugin('Windows Media');
    if(!pluginFound && detectableWithVB) {
	var pluginFound = detectActiveXControl('MediaPlayer.MediaPlayer.1');
    }
    return pluginFound;
}

function detectActiveX() {
	var pluginFound = (window.ActiveXObject || window.GeckoActiveXObject);
	return pluginFound;
}

function detectActiveXWM() {
	var pluginFound = false;
	try {
		if (player = new ActiveXObject("WMPlayer.OCX.7")) {
			pluginFound = true;
		} else if (player = new GeckoActiveXObject("WMPlayer.OCX.7")) {
			pluginFound = true;
		}
	} catch (err) {
		
	}
	return pluginFound;
}

function detectWMPlayerFirefoxPlugin() {
	var pluginFound = detectPlugin('Windows Media Player Firefox Plugin');
	return pluginFound;
}

function detectPlugin() {
    var daPlugins = detectPlugin.arguments;
    var pluginFound = false;
    if (navigator.plugins && navigator.plugins.length > 0) {
	var pluginsArrayLength = navigator.plugins.length;
	for (pluginsArrayCounter=0; pluginsArrayCounter < pluginsArrayLength; pluginsArrayCounter++ ) {
	    var numFound = 0;
	    for(namesCounter=0; namesCounter < daPlugins.length; namesCounter++) {
		if( (navigator.plugins[pluginsArrayCounter].name.indexOf(daPlugins[namesCounter]) >= 0) || 
		    (navigator.plugins[pluginsArrayCounter].description.indexOf(daPlugins[namesCounter]) >= 0) ) {
		    numFound++;
		}   
	    }
	    if(numFound == daPlugins.length) {
		pluginFound = true;
		break;
	    }
	}
    }
    return pluginFound;
}

if ((navigator.userAgent.indexOf('MSIE') != -1) && (navigator.userAgent.indexOf('Win') != -1)) {
    document.writeln('<script language="VBscript">');

    document.writeln('\'do a one-time test for a version of VBScript that can handle this code');
    document.writeln('detectableWithVB = False');
    document.writeln('If ScriptEngineMajorVersion >= 2 then');
    document.writeln('  detectableWithVB = True');
    document.writeln('End If');

    document.writeln('\'this next function will detect most plugins');
    document.writeln('Function detectActiveXControl(activeXControlName)');
    document.writeln('  on error resume next');
    document.writeln('  detectActiveXControl = False');
    document.writeln('  If detectableWithVB Then');
    document.writeln('     detectActiveXControl = IsObject(CreateObject(activeXControlName))');
    document.writeln('  End If');
    document.writeln('End Function');

    document.writeln('\'and the following function handles QuickTime');
    document.writeln('Function detectQuickTimeActiveXControl()');
    document.writeln('  on error resume next');
    document.writeln('  detectQuickTimeActiveXControl = False');
    document.writeln('  If detectableWithVB Then');
    document.writeln('    detectQuickTimeActiveXControl = False');
    document.writeln('    hasQuickTimeChecker = false');
    document.writeln('    Set hasQuickTimeChecker = CreateObject("QuickTimeCheckObject.QuickTimeCheck.1")');
    document.writeln('    If IsObject(hasQuickTimeChecker) Then');
    document.writeln('      If hasQuickTimeChecker.IsQuickTimeAvailable(0) Then ');
    document.writeln('        detectQuickTimeActiveXControl = True');
    document.writeln('      End If');
    document.writeln('    End If');
    document.writeln('  End If');
    document.writeln('End Function');

    document.writeln('</scr' + 'ipt>');
}

var Plugins = Class.create();
Plugins.prototype = {
 initialize: function () {	
 	this.canDetectPlugins = canDetectPlugins();
 	this.FlashDetected = (this.canDetectPlugins && detectFlash());
 	this.DirectorDetected = (this.canDetectPlugins && detectDirector());
 	this.QTDetected = (this.canDetectPlugins && detectQuickTime());
 	this.RealDetected = (this.canDetectPlugins && detectReal());
 	this.WMDetected = (this.canDetectPlugins && detectWindowsMedia());
 	this.ActiveXDetected = detectActiveX();
 	this.ActiveXWMDetected = (this.ActiveXDetected && detectActiveXWM());
 	this.WMPFirefoxDetected = (this.canDetectPlugins && detectWMPlayerFirefoxPlugin());
  this.VLCDetected = (this.canDetectPlugins && detectVLC());
 }
};
var HTML5Audio = Class.create();
HTML5Audio.prototype = Object.extend(new AbstractPlugin(), {
  initialize: function() {
    this.resetAudio();
    this.myState = "Idle";
  },

  resetAudio: function() {
    this.audio = new Audio();
    this.audio.addEventListener("pause", this.onStop.bind(this), true);
    this.audio.addEventListener("error", this.onError.bind(this), true);
    this.audio.addEventListener("loadstart", this.onBuffer.bind(this), true);
    this.audio.addEventListener("canplay", this.onPlay.bind(this), true);
    this.audio.addEventListener("canplaythrough", this.onPlay.bind(this), true);
    this.audio.addEventListener("play", this.onPlay.bind(this), true);
    this.audio.addEventListener("progress", this.onBuffer.bind(this), true);
  },

  setUrl: function(url) {
    this.url = url;

    this.stop();
  },

  play: function() {
    if(!this.audio)
      return;

    try {
      this.resetAudio();
      this.audio.src = this.url;
      this.audio.play();
    } catch(e) {
    }
  },

  stop: function() {
    if(!this.audio)
      return;

    this.audio.pause();
    this.onStop();
  },

  pause: function() {
    if(!this.audio)
      return;

    this.audio.pause();
  },

  getVolume: function() {
    if(!this.audio)
      return;

    var vol = this.audio.volume;

    return parseInt(vol*100);
  },

  setVolume: function (vol) {
    if(!this.audio)
      return;

    if (vol < 0)
      vol = 0.0;
    else if (vol > 100)
      vol = 1.0
    else
      vol = parseInt(vol)/100.0;

   this.audio.volume = vol; 
  },

  getPosition: function() {
    if(!this.audio)
      return;

    return this.audio.currentTime;
  },

  fastForward: function () {
    // TODO
  },

  fastReverse: function () {
    // TODO
  },

  getPluginInfo: function() {
    return "HTML5 - Audio";
  },

  getPluginType: function() {
    return "HTML5";
  },

  getPluginObject: function() {
    return this.audio;
  },

  getPluginStatus: function() {
    return this.myState;
  },

  onStop: function() {
    this.myState = 'Stopped';
    this.isPlaying = false;
  },

  onPause: function() {
    this.myState = 'Stopped';
    this.isPlaying = false;
  },

  onOpen: function() {
    this.myState = 'Connecting';
    this.isPlaying = true;
  },

  onBuffer: function() {
    this.myState = 'Buffering';
    this.isPlaying = true;
  },

  onPlay: function() {
    this.myState = 'Playing';
    this.isPlaying = true;
  },

  onError: function() {
    this.myState = 'Error';
    this.isPlaying = false;
  },

  getCurrentCapabilities: function() {
    return $H({
      play: true, pause: true, stop: true,
      fastForward: false, fastReverse: false,
      getVolume: true, setVolume: true });
  }
});
// QuickTime
var QTP = Class.create();
QTP.prototype = Object.extend(new AbstractPlugin(), {
	initialize: function() {
		// if function is call with arguments, url is the first argument; otherwise default url value is set
		var Url = (null != arguments[0]) ? arguments[0] : './init.mov';
		var obj = document.createElement('object');
		obj.id = 'qt_plugin';
		obj.setAttribute('codebase', 'http://www.apple.com/qtactivex/qtplugin.cab');
		obj.setAttribute('width', 1);	
		obj.setAttribute('height', 1);	
		
		var param = document.createElement('param');
		param.name = 'src';
		param.value = Url;
		obj.appendChild(param);
		param = document.createElement('param');
		param = document.createElement('param');
		param.name = 'autoplay';
		param.value = true;
		obj.appendChild(param);
		param = document.createElement('param');
		param.name = 'controller';
		param.value = true;
		param = document.createElement('param');
		param.name = 'type';
		param.value = 'video/quicktime';		
		obj.appendChild(param);
		if (null == $('qt_plugin')) {
			document.body.appendChild(obj);
		} else {
			document.body.replaceChild(obj, $('qt_plugin'));
		}		
		var embed = document.createElement('embed');
		embed.setAttribute('width', 1);
		embed.setAttribute('height', 1);
		embed.setAttribute('src', Url);
		embed.setAttribute('name', 'qt_plugin');
		embed.setAttribute('enablejavascript', true);
		embed.setAttribute('autoplay', true);
		embed.setAttribute('controller', true);
		embed.setAttribute('type', 'video/quicktime');
		try {
			obj.appendChild(embed);
		} catch(err) {
			//alert(err);
		}
		obj.setAttribute('classid', 'clsid:02BF25D5-8C17-4B23-BC80-D3488ABDDC6B');
		this.qtp = ('object' == typeof(document.qt_plugin)) ? $('qt_plugin') : document.qt_plugin;
		this.isOK =  this.qtp ? true : false;
		this.emulatedStatus = 'Idle';
	},

  _keyStr : "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=",

  // public method for encoding
  encode_b64 : function (input) {
    var output = "";
    var chr1, chr2, chr3, enc1, enc2, enc3, enc4;
    var i = 0;

    while (i < input.length) {

      chr1 = input.charCodeAt(i++);
      chr2 = input.charCodeAt(i++);
      chr3 = input.charCodeAt(i++);

      enc1 = chr1 >> 2;
      enc2 = ((chr1 & 3) << 4) | (chr2 >> 4);
      enc3 = ((chr2 & 15) << 2) | (chr3 >> 6);
      enc4 = chr3 & 63;

      if (isNaN(chr2)) {
        enc3 = enc4 = 64;
      } else if (isNaN(chr3)) {
        enc4 = 64;
      }

      output = output +
        this._keyStr.charAt(enc1) + this._keyStr.charAt(enc2) +
        this._keyStr.charAt(enc3) + this._keyStr.charAt(enc4);

    }

    return output;
  },
	
	preparePlaylistUrl: function(Url) {
		return 'http://liveradio.orange.fr/redirectors/playlistredirect/' + this.encode_b64(Url) + '/list.pls';
	},

	QTUrl: function(Url) {
		return 'http://liveradio.orange.fr/redirectors/qtredirect/' + this.encode_b64(Url) + '/trick.mov';
	},

	setUrl: function(url, needEncapsulation) {
    this.url = url;
    this.needEncapsulation = needEncapsulation;
    this.emulatedStatus = 'Switching';
	},

  switchUrl: function(url) {
    var trickUrl = null;
    if(this.needEncapsulation)
      trickUrl = this.QTUrl(url);
    else
      trickUrl = this.preparePlaylistUrl(url);

		try {
			this.qtp.SetURL(trickUrl);
		} catch (err) {
			this.initialize(trickUrl);
      try {
        if ('function' == typeof(this.qtp.Stop))
          this.qtp.Stop(); 
      } catch(err) {
        alert(err);
      }
		}
  },
	
	play: function() {
    this.switchUrl(this.url);
		this.emulatedStatus = 'Playing';
		if ('function' != typeof this.qtp.Play) {
			try {
				this.qtp.Play();
			} catch (err) {
			}
		}
	},
	
	stop: function() {
    try {
      this.emulatedStatus = 'Stopped';
      if ('undefined' != typeof(this.qtp)) {
        this.qtp.Rewind();
        this.qtp.Stop();
      }
    } catch(err) {
    }
	},
	
	pause: function() {
		this.emulatedStatus = 'Paused';
		this.qtp.Stop();
	},

	getVolume: function() {
		return 10*this.qtp.GetVolume()/25;
	},
	
	setVolume: function (vol) {
		if (vol < 0)
			vol = 0;
		else if (vol > 100)
			vol = 100;
		else
			vol = parseInt(vol);
    
    try {
      this.qtp.SetVolume(25*vol/10);
    } catch(e) {}
	},	
	
	fastForward: function () {
		this.emulatedStatus = 'Seeking';
		this.qtp.SetRate(2);
	},
	
	fastReverse: function () {
		this.emulatedStatus = 'Seeking';
		this.qtp.SetRate(-2);
	},	
	
	getPluginInfo: function() { return "QuickTime " + this.qtp.GetQuickTimeVersion(); },

  getPluginType: function() {
    return 'QT';
  },

  getPluginObject: function() {
    return this.qtp;
  },
	
	getPluginStatus: function() {
    if(this.emulatedStatus == 'Idle')
      return 'Idle';
    else if(this.emulatedStatus == 'Switching')
      return 'Stopped';

		try {
      playState = this.qtp.GetPluginStatus();
      if ('Complete' == playState.substr(0, 8) && playState.length > 8) {
        playState = 'Error';
      } else if ('Complete' == playState /*&& this.emulatedStatus != 'Error'*/) {
        if (this.qtp.GetEndTime() == this.qtp.GetTime()) {
          playState = 'EndOfStream';
        } else {
          playState = ('Idle' != this.emulatedStatus) ? this.emulatedStatus : 'Playing';
        }
      } else {
        if (playState.indexOf('Error') != -1) {
          playState = 'Error';
        } else {
          if ('Waiting' == playState) {
            playState = 'Buffering';
          } else {
            playState = 'Connecting';
          }
          }
        }
    } catch(err) {
      return 'Idle';
    }
    //		playState = this.qtp.GetPluginStatus();
    return playState;
  },

getCurrentCapabilities: function() {
                          return $H({
play: true, pause: true, stop: true,
fastForward: true, fastReverse: true,
getVolume: true, setVolume: true });
                        }
});
// Real Player
var RPL = Class.create();
RPL.prototype = Object.extend(new AbstractPlugin(), {
	initialize: function() {
    div = $(document.createElement('div'));
    document.body.appendChild(div);
    div.style.width = div.style.height = 0;
    div.innerHTML = '<EMBED  NAME="real_plugin" TYPE="audio/x-pn-realaudio-plugin" WIDTH=0 HEIGHT=0 CONTROLS=ImageWindow CONSOLE=one BACKGROUNDCOLOR=white CENTER=true>';
    document.real_player = document.real_plugin;
    if (1 == div.getWidth())
      div.hide();
    this.rpl = document.real_plugin;
    this.isOK = this.rpl ? true : false;
	},
	
	setUrl: function(url) {
		this.url = url;
	},
	
	play: function() {
  	if('undefined' != typeof this.rpl)
  		this.rpl.SetSource(this.url);

		this.clearRealInterval();
		if(this.rpl)
		  this.rpl.DoPlay();
	},
	
	stop: function() {
		this.clearRealInterval();
	  if(this.rpl)
  		this.rpl.DoStop();
	},
	
	pause: function() {
	  if(this.rpl)
  		this.rpl.DoPause();
	},

	getVolume: function() {
		return this.rpl.GetVolume();
	},
	
	setVolume: function (vol) {
		if (vol < 0)
			vol = 0;
		else if (vol > 100)
			vol = 100;
		else
			vol = parseInt(vol);
		
    try {
      if('undefined' != typeof this.rpl/* && 'function' == typeof this.rpl.SetVolume*/)
        this.rpl.SetVolume(vol);
    } catch(e) {}
	},	
	       
	getPosition:  function() {
                return parseInt(this.rpl.GetPosition()/1000);
    },
	
	fastForward: function () {
		this.clearRealInterval();
		this.realInterval = setInterval('AudioLib.AudioPlayer.rp.rpl.SetPosition(AudioLib.AudioPlayer.rp.rpl.GetPosition() + 500)', 250);
	},
	
	fastReverse: function () {
		this.clearRealInterval();
		this.realInterval = setInterval('AudioLib.AudioPlayer.rp.rpl.SetPosition(AudioLib.AudioPlayer.rp.rpl.GetPosition() - 500)', 250);
	},

	clearRealInterval: function() {
		if ('undefined' != typeof(this.realInterval))
			clearInterval(this.realInterval);
	},
	
	getPluginInfo: function() {
	  return "Real " + this.rpl.GetVersionInfo();
	},

  getPluginType: function() {
    return 'RM';
  },

  getPluginObject: function() {
    return this.rpl;
  },
	
  getPluginStatus: function() {
  	if ('undefined' == typeof(this.rpl.GetLastErrorRMACode()) ||
  	    'undefined' == typeof(this.rpl.GetPlayState())) {
  		return 'Idle';
  	}
  	
  	error = parseInt(this.rpl.GetLastErrorRMACode());
  	switch (error) {
  		case 0:
  			error = '';
  			break;
  			
  		case 16389:
  			error = 'Operation failed';
  			break;
  			
  		case 262153:
  			error = 'Unexpected call or method is not implemented';
  			break;
  			
  		default:
  			error = 'Error';
  			break;			
  	}
  	
  	playStates = new Array('Stopped', 'Connecting', 'Buffering', 'Playing', 'Paused', 'Seeking');
  	playState = playStates[parseInt(this.rpl.GetPlayState())];
  	
  	endOfStream = (this.rpl.GetLength() == this.rpl.GetPosition() && this.rpl.GetPosition() > 0) ? 'EndOfStream' : '';

  	//error takes precedence over endOfStream and playState
  	if ('' != error) {
  		return error;
  	} else if ('' != endOfStream) {
  		return endOfStream;
  	} else {
  		return playState;
  	}
  },	
	
    getCurrentCapabilities: function() {
      return $H({
        play: true, pause: true, stop: true,
        fastForward: true, fastReverse: true,
        getVolume: true, setVolume: true });
    },
    
    getMediaInfo: function() {
    	//alert(this.rpl.GetEntryAbstract(0));
    }
});
translations = $H({
  fr:	$H({
    Error: 'Erreur',
    NoPlayer: 'Plateforme non supportée',
    NoAsset: 'Stop',
    Idle: '',
    Playing: 'Lecture en cours',
    Stopped: 'Stop',
    Paused: 'Pause',
    Seeking: '',
    Complete: 'Fin de lecture',
    Waiting: 'Veuillez patienter',
    Connecting: 'Connexion en cours',
    Buffering: 'Mise en cache',
    EndOfStream: 'Fin de lecture',
    TimeOut: 'Serveur saturé ou diffusion interrompue',
    UnsupportedCodec: 'Format non supporté'
  }),
  en: $H({
    Error: 'Error',
    NoPlayer: 'Error',
    NoAsset: 'Stop',
    Idle: '',
    Playing: 'Reading',
    Stopped: 'Stop',
    Paused: 'Pause',
    Seeking: '',
    Complete: 'End of reading',
    Waiting: 'Please wait',
    Connecting: 'Connecting...',
    Buffering: 'Buffering...',
    EndOfStream: '',
    TimeOut: 'Sorry, radio server overload or broadcasting interrupted. Please try again later',
    UnsupportedCodec: 'Unknown format'
  }),
  de: $H({
    Error: 'Fehler',
    NoPlayer: 'fehler',
    NoAsset: 'Stop',
    Idle: '',
    Playing: 'Lesung',
    Stopped: 'Stop',
    Paused: 'Pause',
    Seeking: '',
    Complete: 'Ende des Lesens',
    Waiting: 'Bitte warten',
    Connecting: 'Verbindung',
    Buffering: 'Ladung',
    EndOfStream: '',
    TimeOut: 'Server-Überlastung oder Rundfunks unterbrochen. Bitte versuchen Sie es später erneut',
    UnsupportedCodec: 'Unbekannt Format'
  }),
  it: $H({
    Error: 'Errore',
    NoPlayer: 'Errore',
    NoAsset: 'Stop',
    Idle: '',
    Playing: 'Lettura',
    Stopped: 'Stop',
    Paused: 'Pausa',
    Seeking: '',
    Complete: 'Fine della lettura',
    Waiting: 'Attendi',
    Connecting: 'Collegamento in corso...',
    Buffering: 'Buffering...',
    EndOfStream: '',
    TimeOut: 'Siamo spiacenti, la radio o la trasmissione di sovraccarico del server interrotta. Si prega di riprovare più tardi',
    UnsupportedCodec: 'Formato sconosciuto'
  }),
	es: $H({
		Error: 'Errore',
		NoPlayer: 'Errore',
    NoAsset: 'Stop',
		Idle: '',
		Playing: 'Leyendo',
		Stopped: 'Stop',
		Paused: 'Pausa',
		Seeking: '',
		Complete: 'Fine della lettura',
		Waiting: 'Espere, por favor',
		Connecting: 'Conectando...',
		Buffering: 'Almacenando...',
		EndOfStream: 'Fin de lectura',
		TimeOut: 'Lo sentimos, se ha interrumpido la transmisión o existe una sobrecarga del servidor de radio. Por favor, inténtelo más tarde.',
		UnsupportedCodec: 'Formato desconocido'
	})
});
var VLC = Class.create();
VLC.prototype = Object.extend(new AbstractPlugin(), {
  initialize: function() {
    var div = document.createElement('div');
    document.body.appendChild(div);
    div.style.width = div.style.height = 1;
    div.innerHTML = '<embed pluginspage="http://www.videolan.org" type="application/x-vlc-plugin" progid="VideoLAN.VLCPlugin.2" width="1" height="1" name="vlc" />';
    this.vlc = document.vlc;
    this.myState = 'Idle';
    this.prevState = 0;
    this.isPlaying = false;
    this.monitorTimerId = null;

    this.vlc.audio.volume += 0;
  },

  setUrl: function(url) {
    this.url = url;
  },

  play: function() {
    if(!this.vlc)
      return;

    this.vlc.playlist.items.clear();

    while(this.vlc.playlist.items.count > 0) {
    // clear() may return before the playlist has actually been cleared
    // just wait for it to finish its job
    }

    var itemId = this.vlc.playlist.add(this.url, null, null);
    if( itemId != -1 ) {
      // play MRL
      this.vlc.playlist.playItem(itemId);
      if(!this.monitorTimerId)
        this.monitor();
    }
  },

  stop: function() {
    var wasPlaying = this.isPlaying;
    this.onStop();

    if(!this.vlc || !wasPlaying)
      return;

    this.vlc.playlist.stop();
    if(this.monitorTimerId) {
      this.monitorTimerId.stop();
      this.monitorTimerId = null;
    }
  },

  pause: function() {
    var wasPlaying = this.isPlaying;
    this.onStop();

    if(!this.vlc || !wasPlaying)
      return;

    this.vlc.playlist.pause();
    if(this.monitorTimerId) {
      this.monitorTimerId.stop();
      this.monitorTimerId = null;
    }
  },

  getVolume: function() {
    return this.vlc.audio.volume;
  },

  setVolume: function (vol) {
    if(!this.vlc)
      return;

    if (vol < 0)
      vol = 0;
    else if (vol > 100)
      vol = 100;
    else
      vol = parseInt(vol);

    this.vlc.audio.volume = vol;
  },

  getPosition: function() {
    return this.vlc.input.time/1000;
  },

  fastForward: function () {
    // TODO
  },

  fastReverse: function () {
    // TODO
  },

  getPluginInfo: function() {
    return "VLC " + this.vlc.get_str_variable("version", "no version info");
  },

  getPluginType: function() {
    return "VLC";
  },

  getPluginObject: function() {
    return this.vlc;
  },

  getPluginStatus: function() {
    return this.myState;
  },

  monitor: function() {
   var newState = this.vlc.input.state;
   if( this.prevState != newState ) {
     switch(newState) {
       case 0:
         this.onStop();
         break;

       case 1:
         this.onOpen();
         break;

       case 2:
         this.onBuffer();
         break;

       case 3:
         this.onPlay();
         break;

       case 4:
         this.onPause();
         break;
     }
     this.prevState = newState;
   } 

   if(!this.monitorTimerId )
     this.monitorTimerId = new PeriodicalExecuter(this.monitor.bind(this), 1);
  },

  onStop: function() {
    this.myState = 'Stopped';
    this.isPlaying = false;
  },

  onPause: function() {
    this.myState = 'Paused';
    this.isPlaying = false;
  },

  onOpen: function() {
    this.myState = 'Connecting';
    this.isPlaying = true;
  },

  onBuffer: function() {
    this.myState = 'Buffering';
    this.isPlaying = true;
  },

  onPlay: function() {
    this.myState = 'Playing';
    this.isPlaying = true;
  },

  getCurrentCapabilities: function() {
    return $H({
      play: true, pause: true, stop: true,
      fastForward: true, fastReverse: true,
      getVolume: true, setVolume: true });
  }
});
var WMP = Class.create();
WMP.prototype = Object.extend(new AbstractPlugin(), {
  initialize: function(handler) {
    this.handler = handler;
    try {
    	this.wmp = new ActiveXObject("WMPlayer.OCX.7");
    	this.wmp.settings.autoStart = false;
    } catch (err) {
    	div = document.createElement('div');
    	div.setAttribute('style', 'position:absolute; top:0; left:0; width:0; height:0');
    	plugin = document.createElement('object');
    	plugin.setAttribute('type','application/x-mplayer2');
    	plugin.setAttribute('id', 'wmp_plugin');
    	plugin.setAttribute('width', 1);
    	plugin.setAttribute('height', 1);
    	plugin.setAttribute('autostart', 0);
    	plugin.setAttribute('showcontrols', 0);
    	
    	div.appendChild(plugin);
    	document.body.appendChild(div);
	    this.wmp = $('wmp_plugin');
    }
    this.isOK = this.wmp ? true : false;
    this.lastErrorCount = 0;
    this.url = null;
  },
  
  setUrl: function(url) {
  	this.url = url;
    try {
      this.stop();
    } catch(e) {}
    this.handler('pluginStatus', 'Stopped', '');
  },
  
  play: function() {
    try {
  	  this.wmp.URL = this.url;
      this.wmp.controls.play();
  	} catch(err) {}
  },
  
  pause: function() {
    if(this.wmp.controls)
      this.wmp.controls.pause();
  },
  
  stop: function() {
    if(this.wmp.controls)
      this.wmp.controls.stop();
    
    try {
      if(this.wmp.URL)
        this.wmp.URL = null;
    } catch(e) {}
  },
  
  getVolume: function() {
    if(this.wmp.settings)
      return this.wmp.settings.volume;
    else
      return 50;
  },

  setVolume: function(vol) {
    if (vol < 0)
      vol = 0;
	  else if (vol > 100)
		  vol = 100;
	  else
		  vol = parseInt(vol);
	  
    try {
      if(this.wmp.settings)
        this.wmp.settings.volume = vol;
    } catch(e) {}
  },
  
  getPosition: function () {
    if (this.wmp.controls)
            return this.wmp.controls.currentPosition;
  },

  fastForward: function () {
	  if (this.wmp.controls &&
	      this.wmp.controls.isAvailable('fastForward'))
		  this.wmp.controls.fastForward();
  },
	
  fastReverse: function () {
    if (this.wmp.controls &&
        this.wmp.controls.isAvailable('fastReverse'))
		  this.wmp.controls.fastReverse();
  },  
  
  getPluginInfo: function() {
    return "WMP " + this.wmp.versionInfo;
  },

  getPluginType: function() {
    return "WM";
  },

  getPluginObject: function() {
    return this.wmp;
  },
  
  getPluginStatus: function() {
		var error = 'Idle';
  	if ('undefined' == typeof(this.wmp.error) ||
  	    'undefined' == typeof(this.wmp.openState) ||
  	    'undefined' == this.wmp.playState) {
  	  audiolib_timeout = 999999;
  		return 'Idle';
  	}

  	try {
  		error = (this.wmp.error.errorCount > this.lastErrorCount) ? 'Error' : '';
  	} catch (err) {
  		error = '';
  	}

	if ('' != error) {
		try {
			message = '<ol>';
			for (i=0; i< this.wmp.error.errorCount; i++) {
				message += '<li>' + this.wmp.error.item(i).errorCode + ' :: WMP URL: ' + this.wmp.URL + ' :: audiolib URL: ' + this.url + '</li>';
				code = this.wmp.error.item(i).errorCode;
			}
			message += '</ol>';
			//$('errorDiv').innerHTML += message;
			this.wmp.error.clearErrorQueue();
			if (-1072885353 == code) {
				error = 'WMPReplay';
//				audio_player.play();
				//this.play();
			}
		} catch (err) {}
	}

  	openState = (13 != this.wmp.openState) ? 'Stopped' : '';
  	playStates = new Array('', 'Stopped', 'Paused', 'Playing', 'Seeking', 'Seeking', 'Buffering', 'Connecting', 'EndOfStream', 'Connecting', 'Stopped', 'Connecting');
  	playState = playStates[this.wmp.playState];
  	if ('' != error) {
  		this.lastErrorCount = this.wmp.error.errorCount;
  		return error;
  	} else if('' != playState) {
  		return playState;
  	} else {
  		return openState;
  	}
  },
  
  getCurrentCapabilities: function() {
    return $H({
      play: true, pause: true, stop: true,
      fastForward: true, fastReverse: true,
      getVolume: true, setVolume: true });
  }
});
AudioLibWrapper = Class.create();
AudioLibWrapper.prototype = {
  initialize: function(flashCont) {
    this.flashCont = $(flashCont);
    this.audio = AudioLib.getPlayer(this.listener.bind(this));
  },

  listener: function(eventType, attr, url) {
    try {
      if('missingPlugin' == eventType || 'notSupportedProduct' == eventType)
        this.flashCont.onConfigEvent('MissingPlugin', attr, url);
      else if('pluginStatus' == eventType)
        this.flashCont.onAudioEvent(attr);
    } catch(err) {
      console.warn("error while calling listener: " + err);
    }
  }
};

function initAudio(flashCont) {
  audioWrapper = new AudioLibWrapper(flashCont);
}

function audio_setAsset(codec, urls, productId) {
  var asset = new Asset(codec, urls, productId);
  audioWrapper.audio.setAsset(asset);
}

function audio_clearAsset() {
  audioWrapper.audio.clearAsset();
}

function audio_play() {
  audioWrapper.audio.play();
}

function audio_pause() {
  audioWrapper.audio.pause();
}

function audio_stop() {
  audioWrapper.audio.stop();
}

function audio_getVolume() {
  return audioWrapper.audio.getVolume();
}

function audio_setVolume(vol) {
  audioWrapper.audio.setVolume(vol);
}

function audio_getPosition() {
  audioWrapper.audio.getPosition();
}

function audio_getStatus() {
  return audioWrapper.audio.getPluginStatus();
}

function audio_getPluginType() {
  return audioWrapper.audio.getPluginType();
}

function audio_getPluginObject() {
  return audioWrapper.audio.getPluginObject();
}

function audio_setProfile(profile) {
}
Cache = Class.create();
Cache.prototype = {
	initialize: function() {
		this.hash = $H({});
	},
	
	put: function(type, id, obj) {
		var key = this.getKey(type, id);
		this.hash.set(key, obj);

    if(type == "radio" || type == "podcast") {
      var genKey = this.getKey('product', id);
      this.hash.set(genKey, obj);
    }
	},
	
	getById: function(type, id) {
		var key = this.getKey(type, id);
		return this.hash.get(key);
	},
	
	getByKey: function(key) {
		return this.hash.get(key);
	},
	
	getKey: function(type, id) {
		return type + "-" + id;
	}
};

cache = new Cache();
podcastRefresh = 5000;
safetyRefresh = 10000;
errorRefresh = 60000;

var Metadata = Class.create();
Metadata.prototype = {
    initialize: function(listener, myHost){
        this.watchlist = $A();
        this.podcastID = 0;
        this.podcastPlaylist = null;
        this.watchmap = $H();
        this.urlmap = $H();
        this.itemmap = $H();
        this.myHost = myHost;
        this.timeRef = 0;
        this.listener = listener;
        this.platformUrl = '/PhoenixWeb/Access';
        this.metadataRoot = '/phoenix/metadata19/';
        this.isClockSynced = false;
        this.isClockSyncing = false;
    },
    
    setPlatformUrl: function(platformUrl){
        this.platformUrl = platformUrl;
    },
    
    setMetadataRoot: function(metadataRoot){
        this.metadataRoot = metadataRoot;
    },
    
    query: function(url, method, parameters, handler){
        var parser = new Parser(handler);
        var now = new Date().getTime();
        phoenixRequest(url + '?ts=' + now, {
            'method': method,
            'parameters': parameters,
            'onSuccess': parser.phoenixHandler.bind(parser),
            'onFailure': function(){
                handler('boolean', false, -1, 'network error');
            }
        });
    },
    
    subscribe: function(id, metadataUrl){
        if (!id || this.isStarted(id)) 
            return false;
        
        if (this.watchlist.length > 15) 
            return false;
        
        this.watchlist.push(id);
        
        this.setUrl(id, metadataUrl);
        
        // if the clock is not synced, don't start the metadata now.
        // it will be started when the clock syncs
        if (this.isClockSynced) 
            this.refresh(id);
        else 
            this.syncClock();
        
        return true;
    },
    
    unsubscribe: function(id){
        this.watchlist = this.watchlist.without(id);
        this.deleteUrl(id);
        var event = this.watchmap.unset(id);
        if (event) 
            clearTimeout(event);
        this.itemmap.unset(id);
    },
    
    list: function(){
        return this.watchlist;
    },
    
    clear: function(){
        this.watchlist = $A();
        this.watchmap.each(function(p){
            if (p.value) 
                clearTimeout(p.value);
        });
        this.wathmap = $H();
        this.urlmap = $H();
    },
    
    getUrl: function(id){
        return this.urlmap.get(id);
    },
    
    setUrl: function(id, metadataUrl){
        if (metadataUrl) 
            this.urlmap.set(id, metadataUrl);
        else 
            this.urlmap.set(id, this.metadataRoot + id + '.xml');
    },
    
    deleteUrl: function(id){
        this.urlmap.unset(id);
    },
    
    refresh: function(id){
        if (id == this.podcastID && this.podcastPlaylist != null) {
            this.onRefresh(id, 'playlist', this.podcastPlaylist);
            return;
        }
        var metadataUrl = this.getUrl(id);
        this.query(metadataUrl, 'get', null, this.onRefresh.bind(this, id));
    },
    
    schedule: function(id, refreshTime){
        var now = new Date().getTime();
        var nextRefresh = refreshTime - now + this.timeRef;
        
        if ( isNaN(nextRefresh) || nextRefresh <= 0) 
            nextRefresh = safetyRefresh;
        
        var event = setTimeout(this.refresh.bind(this, id), nextRefresh);
        this.watchmap.set(id, event);
    },
    
    syncClock: function(){
        if (this.isClockSyncing) 
            return;
        else 
            this.isClockSyncing = true;
        
        this.query(this.platformUrl, 'post', $H({
            'action': 'getPlatformTime'
        }), this.onClockSync.bind(this));
    },
    
    isStarted: function(id){
        return this.watchlist.indexOf(id) >= 0;
    },
    
    onClockSync: function(type, time){
        var now = new Date().getTime();
        this.timeRef = now - time;
        this.isClockSynced = true;
        this.isClockSyncing = false;
        
        // if the client tries to subscribe to a radio before the clock is synced, it will be put on hold.
        // now that the clock is synced, start those radios.
        this.watchlist.each(function(id){
            this.refresh(id);
        }
.bind(this));
    },
    
    onRefresh: function(id, type, history, episode){
        if (!this.isStarted(id)) 
            return;
        
        // if an error occurs, refresh after a while
        var now = new Date().getTime();
        var errorRefreshTime = now - this.timeRef + errorRefresh;
        
        switch (type) {
        
            case 'playlist':{
                try {
                    if (this.podcastID != 0 && this.podcastID != id) 
                        this.unsubscribe(this.podcastID);
                    this.podcastID = id;
                    this.podcastPlaylist = history;
                    var ps = AudioLib.AudioPlayer.getPosition();
                    var currentInd = 0;
                    history.each(function(md, index){
                        this.setTrack(md.trackId, 'track', md);
                        this.setPeople(md.artistId, 'artist', md);
                        this.setAlbum(md.albumId, 'album', md);
                        this.setEpisode(md.episodeId, 'episode', md);
                        md.index = index;
                        md.productId = id;
                        if (parseInt(md.startOffset) <= ps) 
                            currentInd = index;
                    }
.bind(this));
                    
                    // check if the item has changed since last time
                    if (!this.isSameItem(history[currentInd], id)) 
                        this.listener(history[currentInd], history);
                    
                    if (history.length == 0) {
                        this.schedule(id, errorRefreshTime);
                    }
                    else {
                        var currentMetadata = history[currentInd];
                        this.schedule(id, now - this.timeRef + podcastRefresh);
                    }
                } 
                catch (e) {
                    this.schedule(id, errorRefreshTime);
                }
                break;
                
            }
            
            case 'history':{
                try {
                    history.each(function(md, index){
                        this.setTrack(md.trackId, 'track', md);
                        this.setPeople(md.artistId, 'artist', md);
                        this.setAlbum(md.albumId, 'album', md);
                        this.setEpisode(md.episodeId, 'episode', md);
                        md.index = index;
                        md.productId = id;
                    }
.bind(this));
                    
                    // check if the item has changed since last time
                    if (!this.isSameItem(history[0], id)) 
                        this.listener(history[0], history);
                    
                    if (history.length == 0) {
                        this.schedule(id, errorRefreshTime);
                    }
                    else {
                        var currentMetadata = history[0];
                        this.schedule(id, parseInt(currentMetadata.nextRefreshAt));
                    }
                } 
                catch (e) {
                    this.schedule(id, errorRefreshTime);
                }
                break;
            }
            
            default:
                {
                    this.schedule(id, errorRefreshTime);
                    break;
                }
                
        }
        
    },
    
    setTrack: function(id, prefix, obj){
        if (!id) 
            return;
        
        var track = cache.getById('track', id);
        if (!track) 
            return;
        
        obj[prefix + 'Name'] = track.name;
        
        if (track.duration) 
            obj[prefix + 'Duration'] = parseInt(track.duration);
        
        if (track.startTime) 
            obj[prefix + 'StartTime'] = track.startTime;

          if(track.trackType)
            obj[prefix + 'Type'] = track.trackType;
    },
    
    setPeople: function(id, prefix, obj){
        if (!id) 
            return;
        
        var people = cache.getById('people', id);
        if (!people) 
            return;
        
        obj[prefix + 'Name'] = people.name;
        
        if (people.picture) 
            obj[prefix + 'Cover'] = people.picture;
        
        if (people.bio) 
            obj[prefix + 'Bio'] = people.bio;
        
        if (people.news) 
            obj[prefix + 'News'] = people.news;
    },
    
    setAlbum: function(id, prefix, obj){
        if (!id) 
            return;
        
        var album = cache.getById('album', id);
        if (!album) 
            return;
        
        obj[prefix + 'Name'] = album.name;
        
        if (album.cover) 
            obj[prefix + 'Cover'] = album.cover;
    },
    
    setEpisode: function(id, prefix, obj){
        if (!id) 
            return;
        
        var episode = cache.getById('episode', id);
        if (!episode) 
            return;
        
        obj[prefix + 'Name'] = episode.name;
        
        if (episode.podcastId) 
            obj[prefix + 'PodcastId'] = episode.podcastId;
        
        if (episode.startTime) 
            obj[prefix + 'StartTime'] = episode.startTime;
        
        if (episode.endTime) 
            obj[prefix + 'EndTime'] = episode.endTime;
        
        if (episode.duration) 
            obj[prefix + 'Duration'] = parseInt(episode.duration);
        
        if (episode.topic) 
            obj[prefix + 'Topic'] = episode.topic;
        
        if (episode.topicPicture) 
            obj[prefix + 'TopicPicture'] = episode.topicPicture;
        
        this.setPeople(episode.animatorId, 'animator', obj);
        
        var guests = $A();
        ensureArray(episode.guestId).each(function(id){
            var guest = new Object();
            this.setPeople(id, 'guest', guest);
            guests.push(guest);
        }
.bind(this));
        obj[prefix + 'Guests'] + guests;
    },
    
    
    isSameItem: function(newItem, id){
    
        var previousItem = this.itemmap.get(id);
        this.itemmap.set(id, newItem);
        
        if (!previousItem) 
            return false;
        
        if (previousItem.trackName) {
            if (!(newItem.trackName)) 
                return false;
            if (previousItem.trackName != newItem.trackName) 
                return false;
        }
        else {
            if (newItem.trackName) 
                return false;
        }
        
        if (previousItem.startTime != newItem.startTime) 
            return false;
        
        if (previousItem.episodeStartTime != newItem.episodeStartTime) 
            return false;
        
        return true;
    }
};
MetadataWrapper = Class.create();
MetadataWrapper.prototype = {
  initialize: function(flashCont) {
    this.flashCont = $(flashCont);
    this.playlist = $A();
    this.metadata = new Metadata(this.listener.bind(this));
  },
	
  listener: function(metadata, playlist) {
    try {
      this.flashCont.onMetadata(metadata, playlist);		
    } catch(err) {
      console.warn("error while calling listener: " + err);
    }
  }
};

function initMetadata(flashCont) {
	metadataWrapper = new MetadataWrapper(flashCont);
}

function metadata_subscribe(productId, metadataUrl) {
	return metadataWrapper.metadata.subscribe(productId, metadataUrl);
}

function metadata_unsubscribe(productId) {
	metadataWrapper.metadata.unsubscribe(productId);
}

function metadata_list() {
	return metadataWrapper.metadata.list();
}

function metadata_clear() {
	metadataWrapper.metadata.clear();
}

function metadata_setMetadataRoot(fragment) {
	metadataWrapper.metadata.setMetadataRoot(fragment);
}

function metadata_setPlatformUrl(fragment) {
	metadataWrapper.metadata.setPlatformUrl(fragment);
}
function getCacheObjectAttributes(xml){
    var attributes = $A();
    
    $A(xml.childNodes).each(function(n){
        if (n.nodeType == 1) 
            attributes.push(n.tagName);
    });
    
    return attributes.uniq();
}

function parseCacheObject(xml){
    var obj = new Object();
    obj.type = getTagAttribute(xml, 'type');
    obj.id = getTagAttribute(xml, 'id');
    
    getCacheObjectAttributes(xml).each(function(i){
        obj[i] = getTagAttribute(xml, i);
    });
    
    return obj;
}

function cacheExtensions(xml){
    var ext = getSingleTag(xml, 'Extensions');
    getMultipleTags(ext, 'CacheObject').each(function(o){
        cacheObject = parseCacheObject(o);
        cache.put(cacheObject.type, cacheObject.id, cacheObject);
    });
}

var Parser = Class.create();
Parser.prototype = {
    initialize: function(userHandler){
        this.userHandler = userHandler;
        this.authListener = null;
    },
    
    setAuthListener: function(authListener){
        this.authListener = authListener;
    },
    
    getDocument: function(transport){
        // of course, IE does it its own way...
        if (navigator.userAgent.indexOf('MSIE') >= 0) {
            var xml = new ActiveXObject("Microsoft.XMLDOM");
            xml.loadXML(transport.responseText);
            return xml;
        }
        else {
            var domParser = new DOMParser();
            var doc = domParser.parseFromString(transport.responseText, "text/xml");
            return doc;
        }
    },
    
    phoenixHandler: function(transport){
        var xml = this.getDocument(transport);
        
        var challenge = getSingleTag(xml, 'Challenge');
        if (challenge && this.authListener) {
            var platformChallenge = getTagAttribute(challenge, 'value');
            this.authListener(platformChallenge);
        }
        
        var main = getSingleTag(xml, 'MainResponse');
        var responseType = getTagAttribute(main, 'type');
        
        switch (responseType) {
            case 'profile':
                var xmlProfile = getSingleTag(main, 'Profile');
                var profile = new Object();
                
                profile.profileId = getTagAttribute(xmlProfile, 'profileId');
                profile.userId = getTagAttribute(xmlProfile, 'userId');
                profile.deviceName = getTagAttribute(xmlProfile, 'deviceName');
                profile.info = getTagAttribute(xmlProfile, 'info');
                profile.isLogged = getTagAttribute(xmlProfile, 'isLogged');
                profile.email = getTagAttribute(xmlProfile, 'email');
                
                $A(xmlProfile.getElementsByTagName('CacheObject')).each(function(rootXml){
                    var root = parseCacheObject(rootXml);
                    profile[root.section] = root;
                });
                
                this.userHandler('profile', profile);
                break;
                
            case 'time':
                var xmlTime = getSingleTag(main, 'PlatformTime')
                var time = getTagAttribute(xmlTime, 'value');
                this.userHandler('time', time);
                break;
                
            case 'search':
                cacheExtensions(xml);
                
                var search = $H();
                getMultipleTags(main, 'SearchResult').each(function(s){
                    var filter = getTagAttribute(s, 'type');
                    var products = $A();
                    // retrieve the products from the cache
                    getMultipleTags(s, 'match').each(function(m){
                        var id = getTagAttribute(m, 'id');
                        var type = getTagAttribute(m, 'type');
                        products.push(new Product(id, type, phoenix));
                    });
                    
                    search.set(filter, products);
                });
                
                
                this.userHandler('search', search);
                break;
                
            case 'boolean':
                var xmlBoolean = getSingleTag(main, 'BooleanResponse');
                var value = getTagAttribute(xmlBoolean, 'value');
                var errorCode = getTagAttribute(xmlBoolean, 'errorCode');
                var reason = getTagAttribute(xmlBoolean, 'reason');
                this.userHandler('boolean', value, errorCode, reason);
                break;
                
            case 'objects':
                var response = $A();
                getMultipleTags(main, 'CacheObject').each(function(o){
                    var cacheObject = parseCacheObject(o);
                    response.push(cacheObject);
                    cache.put(cacheObject.type, cacheObject.id, cacheObject);
                });
                
                cacheExtensions(xml);
                
                this.userHandler('objects', response);
                break;
                
            case 'metadata':
                var xmlMetadata = getSingleTag(main, 'Metadata');
                var metadata = new Object();
                
                // add all the attributes
                getCacheObjectAttributes(xmlMetadata).each(function(i){
                    metadata[i] = getTagAttribute(xmlMetadata, i);
                });
                
                cacheExtensions(xml);
                
                this.userHandler('metadata', metadata);
                break;
                
            case 'history':
                var playlist = $A();
                getMultipleTags(main, 'Metadata').each(function(xmlMetadata){
                    var md = new Object();
                    
                    // add all the attributes
                    getCacheObjectAttributes(xmlMetadata).each(function(i){
                        md[i] = getTagAttribute(xmlMetadata, i);
                    });
                    playlist.push(md);
                });
                cacheExtensions(xml);
                
                this.userHandler(responseType, playlist);
                break;
                
            case 'playlist':
                var playlist = $A();
                getMultipleTags(main, 'PlaylistItem').each(function(xmlMetadata){
                    var md = new Object();
                    
                    // add all the attributes
                    getCacheObjectAttributes(xmlMetadata).each(function(i){
                        md[i] = getTagAttribute(xmlMetadata, i);
                    });
                    playlist.push(md);
                });
                cacheExtensions(xml);
                
                this.userHandler(responseType, playlist);
                break;
        }
    }
};
function getTagAttribute(xml, attr) {
	// first, look for an inline attribute
	var inline = xml.getAttribute(attr);
	if(inline)
		return inline;
		
	// then, look for inner tags
	var inner = xml.getElementsByTagName(attr);
	var len = inner.length;
	if(!len)
		return null;
	
	switch(len) {
		case 0:
			return null;
		
		case 1:
			var nodes = inner[0].childNodes;
			return nodes.length > 0 ? nodes[0].nodeValue : null;
		
		default:
			// otherwise, build a list of attributes
			nodeList = $A();
			$A(inner).each(function(i) {
				var nodes = i.childNodes[0];
				if(nodes)
					nodeList.push(nodes.nodeValue);
			});
			return nodeList;		
	}		
}

function getArrayAttribute(xml, attr) {
	var inner = xml.getElementsByTagName(attr);
	var len = inner.length;
	if(!len)
		return $A();
	
  nodeList = $A();
  $A(inner).each(function(i) {
    var nodes = i.childNodes[0];
    if(nodes)
      nodeList.push(nodes.nodeValue);
  });

  return nodeList;		
}

function getSingleTag(xml, tag) {
	var tags = xml.getElementsByTagName(tag);
	if(tags.length == 0)
		return null;
	
	return tags[0];
}

function getMultipleTags(xml, tag) {
	if(!xml)
		return $A();
	else
		return $A(xml.getElementsByTagName(tag));
}

function ensureArray(elem) {
  if(!elem)
    return $A();

  if(typeof(elem) == "string")
    return $A([elem]);

  return elem;
}
function phoenixRequest(fragment, request) {
	document.domain = BARACODA_DOMAIN;
	my_frame.phoenixBridge(fragment, request.method, request.parameters, request.onSuccess, request.onFailure);
}
