/******************************************************************************

  rawflow.js - Common functions for all Rawflow-enabled web broadcasts. Includes
               simple utilities as well as play and client generation.

  Copyright (C) 2002-2007 Rawflow Limited

  $Id: rawflow.js 11189 2007-05-03 11:15:24Z tobyb $

 ******************************************************************************/

////////////////////////////// Global Variables ///////////////////////////////
var DEBUG                 = false;
var PLUGINID              = "@rawflow.com/ICDClient,version="

var gClientMandatory      = false;
var gBrowserUnsupported   = false;
var gFallbackUrl          = "";                                             // The URL to fall back to when something goes wrong.
var gFallbackFlashStream  = "";                                             // For Flash, the stream at the above URL to fall back to when something goes wrong.
var gShowCloseWindow      = false;                                           // Should we show a warning message when closing the window
var gCloseWindowMessage   = "Closing this window will end the broadcast.";  // The message to display on close.
var gLoadingElement       = "";                                             // An element to show while the control is loading.
var gNotLoadingElement    = "";                                             // An element to show when the control has finished loading.
var gLoadingMovie         = "";                                             // A Flash movie to play while the control is loading.
var gNotLoadingMovie      = "";                                             // A Flash movie to play when the control has finished loading.
var gLoadingTimeout;                                                        // Used to delay a bit before showing the loading div.
var gClient;
var gIsInstalling         = false;                                          // Set during plugin installation
var gAllowClientCreation  = true;                                           // Should we create the client when asked (allows cookie based confirmation)
var gAllowPlayerCreation  = true;                                           // Should we create the player when asked (allows cookie based confirmation)

////////////////////////////// Property Setters //////////////////////////////

function setFallbackUrl(url) {
  gFallbackUrl = url;
}

function setFallbackFlashStream(stream) {
  gFallbackFlashStream = stream;
}

function setShowCloseWindow(show) {
  gShowCloseWindow = show;
}

function setCloseWindowMessage(message) {
  gCloseWindowMessage = message;
}

function setLoadingElement(element) {
  gLoadingElement = element;
}

function setNotLoadingElement(element) {
  gNotLoadingElement = element;
}

function setLoadingMovie(movie) {
  gLoadingMovie = movie;
}

function setNotLoadingMovie(movie) {
  gNotLoadingMovie = movie;
}

function setClientMandatory() {
  gClientMandatory = true;
}

function setAllowClientCreation(allow) {
  gAllowClientCreation = allow;
}

function setAllowPlayerCreation(allow) {
  gAllowPlayerCreation = allow;
}

////////////////////////////// Utility Functions //////////////////////////////

function debug(message) {
  if (DEBUG) {
    //alert(message);
  }
}

function hasPlugin(plugin) {
  var result = false;
  if (navigator.plugins && navigator.plugins.length) {
    for(i = 0; i < navigator.plugins.length; i++) {
      if(navigator.plugins[i].name.indexOf(plugin) != -1) {
        result = true;
        break;
      }
    }
  }
  return result;
}

function hasActiveX() {
  // window.ActiveXObject is defined on MacOS,
  // even though that OS can't load ActiveX objects
  return (window.ActiveXObject && isWindows());
}

function tryExpression(expression) {
  var result = null;
  if(document.getElementById) {
    // Only later-generation browsers support try..catch
    eval('try { result = ' + expression + '; } catch (e) { }');
  } else {
    eval('result = ' + expression + ';');
  }
  return result;
}

function tryStatement(statement) {
  var result = false;
  if(document.getElementById) {
    // Only later-generation browsers support try..catch
    eval('try { ' + statement + '; result = true; } catch (e) { }');
  } else {
    eval('' + statement + '; result = true;');
  }
  return result;
}

function createActiveXObject(id) {
  var control = null;
  // Annoyingly, window.ActiveXObject is defined on MacOS,
  // even though that OS can't load ActiveX objects!
  if(hasActiveX()) {
    control = tryExpression('new ActiveXObject("' + id + '")');
  }
  return control;
}

function getObject(objectName)
{
  if(document.getElementById)
  {
    return document.getElementById(objectName);
  } else if(document.all) {
    return document.all[objectName];
  } else if(document.layers) {
    return document.layers[objectName];
  }
  return null;
}

function getFrame(frame) {
  var win = null;
  win = getObject(frame);
  if (win==null) {
    win = document.frames[frame];
  }
  return (win != null && win.contentWindow) ? win.contentWindow : win;
}

function frameNavigate(frame, url) {
  if(frame.location) {
    frame.location.href = url;
  } else if(frame.src) {
    frame.src = url;
  }
}

function show(layer) {
  if (document.layers) {
    document.layers[layer].visibility = "show";
  } else {
    getObject(layer).style.visibility = "visible";
  }
}

function hide(layer) {
  if (document.layers) {
    document.layers[layer].visibility = "hide";
  } else {
    getObject(layer).style.visibility = "hidden";
  }
}

function isWindows() {
  return (navigator.userAgent.indexOf('Windows') >= 0);
}

function isMac() {
  return (navigator.userAgent.indexOf('Mac') >= 0);
}

function isLinux() {
  return (navigator.userAgent.indexOf('Linux') >= 0);
}

function isIE() {
  return (navigator.appName == 'Microsoft Internet Explorer');
}

function hasRawflowActiveX()
{
  return createActiveXObject("RFACTIVEX.RawflowCtrl.1") != null;
}

function hasWindowsMediaActiveX()
{
  return createActiveXObject("MediaPlayer.MediaPlayer.1") != null;
}

function hasRealMediaActiveX()
{
  return (createActiveXObject("rmocx.RealPlayer G2 Control") != null || createActiveXObject("RealPlayer.RealPlayer(tm) ActiveX Control (32-bit)") != null || createActiveXObject("RealVideo.RealVideo(tm) ActiveX Control (32-bit)") != null);
}

////////////////////////////// Private Functions //////////////////////////////

function _startLoading() {
  if(gLoadingElement != "") {
    show(gLoadingElement);
  }
  if(gNotLoadingElement != "") {
    hide(gNotLoadingElement);
  }
  if(gLoadingMovie != "" && document[gLoadingMovie]) {
    document[gLoadingMovie].Play();
  }
  if(gNotLoadingMovie != "" && document[gNotLoadingMovie]) {
    document[gNotLoadingMovie].Stop();
  }
}

function _stopLoading() {
  if(gLoadingElement != "") {
    hide(gLoadingElement);
  }
  if(gNotLoadingElement != "") {
    show(gNotLoadingElement);
  }
  if(gLoadingMovie != "" && document[gLoadingMovie]) {
    document[gLoadingMovie].Stop();
  }
  if(gNotLoadingMovie != "" && document[gNotLoadingMovie]) {
    document[gNotLoadingMovie].Play();
  }
}

//
// Wait (using setTimeout) for client to be ready before trying to call play()
// Allows users to not think that their browser has 'crashed' while waiting for
// client to talk to server.
//
function _waitAndPlay() {
  if(gClient.canPlay()) {
    gClient.play();
    stopLoading();
  } else {
    // timeout in ms - as fine grained as necessary.
    setTimeout("_waitAndPlay()", 200);
  }
}

////////////////////////////// Public Functions ///////////////////////////////

function fallback(player) {
  if(gFallbackUrl != "") {
    if(player == null) {
      debug('no player installed');
      //location.href = gFallbackUrl;
    } else {
      debug("falling back");
      player.playURL(gFallbackUrl, gFallbackFlashStream);
    }
  }
  stopLoading();
}

function start(client, player) {
  if(client == null) {
    if(gBrowserUnsupported || !gClientMandatory) {
      //window.onbeforeunload = checkCloseWindow;
      fallback(player);
    } else {
      stopLoading();
    }
  } else {
    client.setPlayer(player);
    debug('calling client.start()');
    client.start();
    if(client.isRunning()) {
      debug('waiting for control to be ready');
      gClient = client;
      window.onbeforeunload = checkCloseWindow;
      window.onunload = closeWindow;
      _waitAndPlay();
    } else if(!gClientMandatory) {
      window.onbeforeunload = checkCloseWindow;
      fallback(player);
    } else {
      debug('stop Loading');
      stopLoading();
    }
  }
}

function checkCloseWindow() {
  if(gShowCloseWindow && !gIsInstalling) {
    // Display a message if the user tries to close the window.
    return gCloseWindowMessage;
  }
}

function closeWindow() {
  if(gClient != null)
  {
    // explicitely stop the player and the client
    gClient.stop();
  }
}

function startLoading() {
  gLoadingTimeout = setTimeout(_startLoading, 500);
}

function stopLoading() {
  clearTimeout(gLoadingTimeout);
  _stopLoading();
}

//////////////////////////////// Media Players ////////////////////////////////

function ActiveXRealMediaPlayer(playerName, width, height, showControls) {
  this.playerName = playerName;
  this.width = width;
  this.height = height;

  this.player = null;
  this.playing = false;
  
  this.showControls = false;
  if ((showControls == null) || showControls)
  {
    this.showControls = true;
  }

  this.output = function() {
    // image window
    var statusBarHeight = 27;
    var controlPanelHeight = 26;
    
    var imageHeight = this.height;
    if (this.showControls)
    {
      imageHeight = this.height - statusBarHeight - controlPanelHeight;
    }
    
    document.writeln('<div id="playerContainer" style="width:' + this.width + 'px; height:' + imageHeight + ';">');
    document.writeln('<object id="' + this.playerName + '" name="' + this.playerName + '" classid="CLSID:CFCDAA03-8BE4-11CF-B84B-0020AFBBCCFA" width="' + this.width + '" height="' + imageHeight + '" border="0">');
    document.writeln('  <param name="autostart" value="true">');
    document.writeln('  <param name="controls" value="ImageWindow">');
    document.writeln('  <param name="console" value="RawFlowReal">');
    document.writeln('</object><br>');
    
    if (this.showControls)
    {
      // status bar
      document.writeln('<object id="realStatusBar" name="realStatusBar" classid="CLSID:CFCDAA03-8BE4-11CF-B84B-0020AFBBCCFA" width="' + this.width + '" height="' + statusBarHeight + '" border="0">');
      document.writeln('  <param name="autostart" value="true">');
      document.writeln('  <param name="controls" value="StatusBar">');
      document.writeln('  <param name="console" value="RawFlowReal">');
      document.writeln('</object><br>');

      // control panel
      document.writeln('<object id="realControlPanel" name="realControlPanel" classid="CLSID:CFCDAA03-8BE4-11CF-B84B-0020AFBBCCFA" width="' + this.width + '" height="' + controlPanelHeight + '" border="0">');
      document.writeln('  <param name="autostart" value="true">');
      document.writeln('  <param name="controls" value="ControlPanel">');
      document.writeln('  <param name="console" value="RawFlowReal">');
      document.writeln('</object>');      
    }
    
    this.attachPlayer();
    return this.player != null;
  }

  this.playURL = function(url, stream) {
    this.attachPlayer();
    if(this.player != null) {
      this.player.Source = url;
      this.play();
    }
  }
  
  this.play = function(url) {
    this.attachPlayer();
    if(this.player != null) {
      this.player.DoPlay();
      this.playing = true;
    }
  }

  this.stop = function(url) {
    this.attachPlayer();
    if(this.player != null) {
      this.player.DoStop();
      this.playing = false;
    }
  }
  
  this.mute = function(url) {
    this.attachPlayer();
    if(this.player != null) {
      if (this.player.GetMute()) {
        this.player.SetMute(false)
      } else {
        this.player.SetMute(true)
      }
    }
  } 
  
  this.setvolume = function(percentOfMax) {
    // note that real player takes a percentage direct
    var prevVolume = this.player.GetVolume();
    // we do that because if the volume=0, the sound will be max.
    if (percentOfMax > 1)
      this.player.SetVolume(percentOfMax);
    else  this.player.SetVolume(1);
    
    var currentVolume = this.player.GetVolume();
  }
  
  this.fullscreen = function(url) {
    this.attachPlayer();
    if(this.player != null) {
      this.player.SetFullScreen();
    }
  }
  
  this.attachPlayer = function() {
    if(this.player == null) {
      // Try to attach to an existing media player
      this.player = getObject(this.playerName);
    }  
    
  }

  this.isPlaying = function() {
    return this.playing;
  }
}

function FramedWindowsRealPlayer(playerName, width, height, showControls) {
  this.playerName = playerName;
  this.width = width;
  this.height = height;

  this.player = null;
  this.playing = false;
  this.playerUrl = "realplayer.html";
  
  this.showControls = 0;
  if ((showControls == null) || showControls)
  {
    this.showControls = 1;
  }  
  
  this.output = function() {
    var src = this.playerUrl + "?width="  + this.width
                                          + "&height=" + this.height
                                          + "&showControls=" + this.showControls;
    
    document.writeln('<div id="playerContainer">');
    document.writeln('<iframe id="' + this.playerName + '" name="' + this.playerName + '" ');
    document.writeln('        style="width: ' + this.width + 'px; height: ' + this.height + 'px; border: none" ');
    document.writeln('        frameborder="0" ');
    document.writeln('        src="' + src + '">');
    document.writeln('</iframe>');
    document.writeln('</div>');

    this.player = getFrame(this.playerName);
    return this.player != null;
  }
  
  this.playURL = function(url, stream) {
    if(this.player == null) {
      // Try to attach to an existing media player
      this.player = getFrame(this.playerName);
    }
    if(this.player != null) {
      var src = this.playerUrl + "?url=" + escape(url)
                                         + "&width=" + this.width
                                         + "&height=" + this.height
                                         + "&showControls=" + this.showControls;

      frameNavigate(this.player, src);
      this.playing = true;
    }
  }
  
  this.play = function() {
    // not implemented
  }  
  
  this.stop = function() {
    // not implemented
  }
  
  this.mute = function() {
    // not implemented
  } 
  
  this.setvolume = function(percentOfTotal) {
    // not implemented
  }
  
  this.fullscreen = function() {
    // not implemented
  }
  
  this.isPlaying = function() {
    return this.playing;
  }
}

function ActiveXWindowsMediaPlayer(playerName, width, height, showControls) 
{
  this.playerName = playerName;
  this.width = width;
  this.height = height;

  this.player = null;
  this.playing = false;
  this.soundOn = true;
  
  this.uiMode = "none";
  if ((showControls == null) || showControls)
  {
    this.uiMode = "mini";
  }
  
  this.output = function() {
    document.writeln('<div id="playerContainer">');
    document.writeln('  <object id="' + this.playerName + '" ');
    document.writeln('      CLASSID="CLSID:6BF52A52-394A-11d3-B153-00C04F79FAA6" ');
    document.writeln('      standby="Loading Microsoft Windows Media Player components..." ');
    document.writeln('      type="application/x-oleobject" ');
    document.writeln('      width="' + this.width + '" ');
    document.writeln('      height="' + this.height + '">');
    document.writeln('    <param name="uiMode" value="' + this.uiMode + '"/>');
    document.writeln('  </object>');
    document.writeln('</div>');

    this.attachPlayer();
    return this.player != null;
  }

  this.playURL = function(url, stream) {
    this.attachPlayer();
    if(this.player != null) {
      this.player.URL = url;
      this.play();
    }
  }
  
  this.play = function() {
    this.attachPlayer();
    if(this.player != null) {
      this.player.Controls.play();
      this.playing = true;
    }
  }  
  
  this.stop = function() {
    this.attachPlayer();
    if(this.player != null) {
      this.player.Controls.stop();
      this.playing = false;
    }
  }
  
  this.mute = function() {
    this.attachPlayer();
    if(this.player != null) {
      if (this.soundOn == true) {
        this.player.Settings.mute=true;
        this.soundOn=false;
      } else {
        this.player.Settings.mute=false;
        this.soundOn=true;
      }
    }
  } 
  
  this.setvolume = function(percentOfMax) {
    // now using sensible WMP 7 API
    this.attachPlayer();
    if(this.player != null) {
      this.player.Settings.volume = percentOfMax;
    }
  }
  
  this.fullscreen = function(url) {
    this.attachPlayer();
    if(this.player != null) {
      this.player.fullScreen=true;
    }
  }
  
  this.attachPlayer = function() {
    if(this.player == null) {
      // Try to attach to an existing media player
      this.player = getObject(this.playerName);
    }  
  }
  
  this.isPlaying = function() {
    return this.playing;
  }
}

function FramedWindowsMediaPlayer(playerName, width, height, showControls)
{
  this.playerName = playerName;
  this.width = width;
  this.height = height;

  this.player = null;
  this.playing = false;
  this.playerUrl = "mediaplayer.html";
  
  this.uiMode = "none";
  if ((showControls == null) || showControls)
  {
    this.uiMode = "mini";
  }

  this.output = function() {
    document.writeln('<div id="playerContainer">');
    document.writeln('<iframe id="' + this.playerName + '" name="' + this.playerName + '" ');
    document.writeln('        style="width: ' + this.width + 'px; height: ' + this.height + 'px; border: none" ');
    document.writeln('        frameborder="0" ');
    document.writeln('        src="' + this.playerUrl + '?width=' + this.width + '&height=' + this.height + '&uiMode=' + this.uiMode +'">');
    document.writeln('</iframe>');
    document.writeln('</div>');

    this.player = getFrame(this.playerName);
    return this.player != null;
  }

  this.playURL = function(url, stream) {
    if(this.player == null) {
      // Try to attach to an existing media player
      this.player = getFrame(this.playerName);
    }
    if(this.player != null) {
      var src = this.playerUrl + "?url=" + escape(url)
                                         + "&width=" + this.width
                                         + "&height=" + this.height;
      frameNavigate(this.player, src);
      this.playing = true;
    }
  }
  
  this.play = function() {
    // not implemented
  }  
  
  this.stop = function() {
    // not implemented
  }
  
  this.mute = function() {
    // not implemented
  } 
  
  this.setvolume = function(percentOfTotal) {
    // not implemented
  }
  
  this.fullscreen = function() {
    // not implemented
  }

  this.isPlaying = function() {
    return this.playing;
  }
}

function ActiveXSWFPlayer(playerName, width, height, showControls) {
  this.playerName = playerName;
  this.width = width;
  this.height = height;

  this.player = null;
  this.playing = false;
  this.soundOn = true;

  this.output = function() {
    document.writeln('<div id="playerContainer">');
    document.writeln('  <object id="' + this.playerName + '" ');
    document.writeln('      CLASSID="CLSID:D27CDB6E-AE6D-11cf-96B8-444553540000" ');
    document.writeln('      standby="Loading Adobe Shockwave Flash Player components..." ');
    document.writeln('      width="' + this.width + '" ');
    document.writeln('      height="' + this.height + '">');
    document.writeln('  <param name="movie" value="./SWFPlayer.swf"> ');
    document.writeln('  <param name="allowFullScreen" value="true" /> ');
    document.writeln('  </object>');
    document.writeln('</div>');

    this.attachPlayer();
    return this.player != null;
  }
  
  this.playURL = function(url, stream) {
    this.attachPlayer();
    if(this.player != null) {
      this.player.playURL(url, stream);
    }
  }
  
  this.play = function() {
    this.attachPlayer();
    if(this.player != null) {
      this.player.playStream();
      this.playing = true;
    }
  }  
  
  this.stop = function() {
    this.attachPlayer();
    if(this.player != null) {
      this.player.stopStream();
      this.playing = false;
    }
  }
  
  this.mute = function() {
    this.attachPlayer();
    if(this.player != null) {
      if (this.soundOn == true) {
        this.player.mute(true);
        this.soundOn=false;
      } else {
        this.player.mute(false);
        this.soundOn=true;
      }
    }
  } 
  
  this.setvolume = function(percentOfMax) {
    this.attachPlayer();
    if(this.player != null) {
      this.player.setVolume(percentOfMax);
    }
  }
  
  this.fullscreen = function(url) {
    this.attachPlayer();
    if(this.player != null) {
      this.player.fullScreen(true);
    }
  }
  
  this.attachPlayer = function() {
    if(this.player == null) {
      // Try to attach to an existing media player
      this.player = getObject(this.playerName);
    }  
  }
  
  this.isPlaying = function() {
    return this.playing;
  }
}

function FramedSWFPlayer(playerName, width, height, showControls) {

  this.playerName = playerName;
  this.width = width;
  this.height = height;

  this.player = null;
  this.playing = false;
  this.playerUrl = "mediaplayer.html";

  this.output = function() {

    document.writeln('<div id="playerContainer">');
    document.writeln('  <object id="' + this.playerName + '" data="./SWFPlayer.swf"');
    document.writeln('  width="' + this.width + '" height="' + this.height + '" type="application/x-shockwave-flash">');
    document.writeln('    <param name="quality" value="high">');
    document.writeln('    <param name="bgcolor" value="#FFFFFF">');
    document.writeln('    <param name="pluginurl" value="http://www.adobe.com/go/getflashplayer">');
    document.writeln('  </object>');
    document.writeln('</div>');

    this.player = getFrame(this.playerName);
    return this.player != null;
  }

  // player controls
  this.playURL = function(url, stream) {
    this.attachPlayer();
    if(this.player != null) {
      this.player.playURL(url, stream);
    }
  }
  
  this.play = function() {
    this.attachPlayer();
    if(this.player != null) {
      this.player.playStream();
      this.playing = true;
    }
  }  
  
  this.stop = function() {
    this.attachPlayer();
    if(this.player != null) {
      this.player.stopStream();
      this.playing = false;
    }
  }
  
  this.mute = function() {
    this.attachPlayer();
    if(this.player != null) {
      if (this.soundOn == true) {
        this.player.mute(true);
        this.soundOn=false;
      } else {
        this.player.mute(false);
        this.soundOn=true;
      }
    }
  } 
  
  this.setvolume = function(percentOfMax) {
    this.attachPlayer();
    if(this.player != null) {
      this.player.setVolume(percentOfMax);
    }
  }
  
  this.fullscreen = function(url) {
    this.attachPlayer();
    if(this.player != null) {
      this.player.fullScreen(true);
    }
  }
  
  this.attachPlayer = function() {
    if(this.player == null) {
      // Try to attach to an existing media player
      this.player = getObject(this.playerName);
    }  
  }
  
  this.isPlaying = function() {
    return this.playing;
  }
}


/////////////////////////////// Rawflow Clients ///////////////////////////////

function ActiveXRawflowClient(clientName, version, path) {
  this.clientName = clientName;
  versionArray = version.split(".");
  this.version = versionArray.join(",");
  this.path = path;

  this.rawflow = null;
  this.player = null;
  this.modified = true;
  this.worked = false;
  this.properties = new Object();
  this.stunservers = new Array();

  this.requiresInstall = function() {
    return false;
  }

  this.install = function() {
  }

  this.isValid = function() {
    return this.rawflow != null;
  }

  this.output = function() {
    document.writeln('<object id="' + this.clientName + '" width="100" height="100" '); 
    document.writeln('        classid="CLSID:029FDBA6-3547-11D7-AA4C-0050BF051A00" ');
    document.writeln('        codebase="' + this.path + 'Rawflow.cab#Version=' + this.version + '">');
    document.writeln('</object>');

    this.rawflow = document[this.clientName];
    return this.isValid();
  }

  this.start = function() {
    if(this.rawflow == null || !hasRawflowActiveX())
    {
      return false;
    }

    this.setProperty("Embedded", (this.player != null));

    for(var property in this.properties) {
      this.rawflow[property] = this.properties[property];
    }

    if(this.modified) {
      this.modified = false;
      this.worked = true;
      for (var serverindex = 0; serverindex < this.stunservers.length; ++serverindex) {
        stunServerName = this.stunservers[serverindex];
        this.worked = this.worked && tryStatement("this.rawflow.addStunServer('" + stunServerName + "', 3478)");
      }
      this.worked = this.worked && tryStatement("this.rawflow.start()");
    } else {
      this.worked = tryStatement("this.rawflow.restart()");
    }
  }

  this.stop = function() {
    // stop the player
    if(this.player != null)
    {
      this.player.stop();
    }
    
    // stop the client
    if(this.isValid())
    {
      this.rawflow.stop();
    }
  }

  this.isRunning = function() {
    return this.worked;
  }

  this.canPlay = function() {
    return this.rawflow.URLToPlayReady;
  }

  this.play = function() {
    if(this.player != null) {
      this.player.playURL(this.rawflow.URLToPlay, this.rawflow.FlashStream);
    }
  }

  this.setConfig = function(config) {
  }

  this.setProperty = function(name, value) {
    this.modified = (this.properties[name] != value);
    this.properties[name] = value;
  }

  this.setPlayer = function(player) {
    this.player = player;
  }

  this.addStunServer = function(server) {
    this.stunservers[this.stunservers.length] = server;
    this.modified = true;
  }

  // Add a trailing slash to the path if one does not exist, but retain empty paths
  if(this.path != "" && this.path.charAt(this.path.length - 1) != "/") {
    this.path += "/";
  }
}

function PluginRawflowClient(clientName, version, path) {
  this.clientName = clientName;
  this.version = version;
  this.path = path;
  
  this.xpiPath = "";
  this.config = "";
  this.rawflow = null;
  this.player = null;
  this.started = false;
  this.worked = false;
  
  this.properties = new Object();
  this.stunservers = new Array();

  // called by InstallTrigger.
  this.callBack = function(url, status) {
    if (status == 0)
    {
      debug("XPInstall succeeded");
      // replicate ActiveX behaviour by reloading this page on success
      // this will get the plugin up and running.
      navigator.plugins.refresh(true);
      window.location.reload();
    }
    else
    {
      debug("XPInstall failed");
      gIsInstalling = false;
    }
  }

  this.requiresInstall = function() {
    // make sure we have an up-to-date idea of our installed plugins
    navigator.plugins.refresh(false);
  
    // can only install if InstallTrigger is recognised
    var triggerDefined = ("undefined" != typeof(InstallTrigger));
    var installEnabled = InstallTrigger.enabled();
    
    var alreadyInstalled = hasPlugin("Rawflow");
    var versionCompare = InstallTrigger.compareVersion(PLUGINID + this.version, this.version);

    debug(" triggerDefined = " + triggerDefined + " installEnabled = " + installEnabled + " alreadyInstalled = " + alreadyInstalled + " versionCompare = " + versionCompare);
    
    // returns true if InstallTrigger can be used and either the plugin is not installed or a newer version
    // is available
    return triggerDefined && installEnabled && (!alreadyInstalled || versionCompare < 0);
  }

  this.install = function() {
    // XPInstall
    var xpi = { "Rawflow ICD Client" : this.xpiPath };
    InstallTrigger.install( xpi, this.callBack );
    gIsInstalling = true;
  }

  this.isValid = function() {
    return hasPlugin("Rawflow") && this.rawflow != null;
  }

  this.output = function() {
    // check if we need to install the plugin
    if(this.xpiPath != "" && this.requiresInstall()) 
    {
      this.install(); // calls back when complete
    }
    else
    {
      document.writeln('<object id="' + this.clientName + '" ');
      if (isLinux())
      {
        // only linux client requires client.rfw file
        document.writeln('        data="' + this.config + '" ');
      }
      document.writeln('        codebase="' + this.client + '" ');
      document.writeln('        type="application/x-rawflow-stream" ');
      document.writeln('        width="0" ');
      document.writeln('        height="0">');
      document.writeln('</object>');

      this.rawflow = document[this.clientName];
    }
    return this.isValid();
  }

  this.start = function() 
  {
    if(!this.isValid())
    {
      this.worked = false;
      return false;
    }
    if (!isLinux())
    {
      this.setProperty("Embedded", (this.player != null));

      for(var property in this.properties) 
      {
				var value = this.properties[property];

        if((isNaN(value) || value == "") && typeof value != 'boolean')
        {
          eval("this.rawflow." + property + " = '" + value + "'");
        }
        else
        {
          eval("this.rawflow." + property + " = " + value);   
        }
      }

      if(this.modified) 
      {
        this.modified = false;
        for (var serverindex = 0; serverindex < this.stunservers.length; ++serverindex) {
          this.rawflow.AddStunServer(this.stunservers[serverindex], 3478);
        }
        this.rawflow.Start();
      } else {
        this.rawflow.Restart();
      }
    }
    this.worked = true;
    
  }

  this.stop = function() {
    // stop the player
    if(this.player != null)
    {
      this.player.stop();
    }
    
    // stop the client
    if(this.isValid())
    {
      this.rawflow.Stop();
    }
  }

  this.isRunning = function() {
    return this.worked;
  }

  this.canPlay = function() {
    return isLinux() || this.rawflow.URLToPlayReady;
  }

  this.play = function() {
    if(!isLinux() && this.player != null) {
      this.player.playURL(this.rawflow.URLToPlay, this.rawflow.FlashStream);
    }
  }

  this.setConfig = function(config) {
    this.config = config;
  }

  this.setProperty = function(name, value) {
    this.modified = (this.properties[name] != value);
    this.properties[name] = value;
  }
 
	this.getProperty = function(name) {
		return this.properties[name];
	}
 
  this.setPlayer = function(player) {
    this.player = player;
  }
  
  this.addStunServer = function(server) {
    this.stunservers[this.stunservers.length] = server;
    this.modified = true;
  }

  // get path to xpi
  if(this.path == "") {
    // If the path is empty, use the current page's path.
    this.path = location.href.substring(0, location.href.lastIndexOf('/') + 1);
  } else if(this.path.charAt(this.path.length - 1) != "/") {
    // Add a trailing slash to the path if one does not exist, but retain empty paths
    this.path += "/";
  }
  if(isWindows()) {
    this.xpiPath = this.path + "rfwin.xpi";
  } else if(isMac()) {
    this.xpiPath = this.path + "rfmac.xpi";
  } else if(isLinux()) {
    this.xpiPath = this.path + "rflinux.xpi";
  }
}

function JavaWebStartRawflowClient(clientName, version, path) {
  this.clientName = clientName;
  this.version = version;
  this.path = path;

  this.worked = false;
  this.player = null;

  this.isValid = function() {
    return true;
  }

  this.output = function() {
    return true;
  }

  this.start = function() {
    this.worked = true;
  }
  
  this.stop = function() {
  }

  this.isRunning = function() {
    return this.worked;
  }

  this.canPlay = function() {
    return true;
  }

  this.play = function() {
  }

  this.setProperty = function(name, value) {
  }

  this.setPlayer = function(player) {
    this.player = player;
  }
}

////////////////////////////////// Factories //////////////////////////////////

function createWindowsMediaPlayer(width, height, showControls) 
{
  var showControls = (showControls == null) ? true : showControls;

  var player = null;
  if (gAllowPlayerCreation)
  {
    if(hasWindowsMediaActiveX()) 
    {
      player = new ActiveXWindowsMediaPlayer("actualPlayer", width, height, showControls);
    } 
    else if(hasPlugin("Windows Media")) 
    {
      player = new FramedWindowsMediaPlayer("actualPlayer", width, height, showControls);
    }
    if(player != null && !player.output()) 
    {
      player = null;
    }
  }
  return player;
}

function createRealPlayer(width, height, showControls) 
{
  var showControls = (showControls == null) ? true : showControls;

  var player = null;
  if (gAllowPlayerCreation)
  {
    if(hasRealMediaActiveX()) 
    {
      player = new ActiveXRealMediaPlayer("actualPlayer", width, height, showControls);
    } 
    else if(hasPlugin("Real")) 
    {
      player = new FramedWindowsRealPlayer("actualPlayer", width, height, showControls);
    }
    if(player != null && !player.output()) 
    {
      player = null;
    }
  }
  return player;
}

function createMp3MediaPlayer(width, height, showControls) 
{
  var showControls = (showControls == null) ? true : showControls;

  var player = null;
  if (gAllowPlayerCreation)
  {
    if (!isMac()) {
      player = createWindowsMediaPlayer(width, height, showControls);
    } else {
      debug('Mac being used');
    }
  }
  return player;
}

function createSWFPlayer(width, height, showControls) 
{
  var showControls = (showControls == null) ? true : showControls;

  var player = null;
  if (gAllowPlayerCreation)
  {
    if(createActiveXObject("ShockwaveFlash.ShockwaveFlash.1") != null) {
      player = new ActiveXSWFPlayer("actualPlayer", width, height, showControls);
    } else if(hasPlugin("Shockwave Flash")) {
      player = new FramedSWFPlayer("actualPlayer", width, height, showControls);
    }
    if(player != null && !player.output()) {
      player = null;
    }
  }
  return player;
}

// Function for when an embedded player isn't possible
function createNullEmbeddedPlayer(width, height, showControls) 
{
  return null;
}

function createRawflowClient(version, path, config) 
{
  var rawflow = null;
  if (gAllowClientCreation) 
  {
    if(hasActiveX()) {
      rawflow = new ActiveXRawflowClient("rawflow", version, path);
    } else if(isMac()) {
//    rawflow = new JavaWebStartClient("rawflow", version, path);
    } else if(navigator.plugins) {
      rawflow = new PluginRawflowClient("rawflow", version, path);
    }
    if(rawflow == null) {
      gBrowserUnsupported = true;
    } else {
      if (config != null)
      {
        rawflow.setConfig(config);
      }
      rawflow.output();
    }
  }
  return rawflow;
}

