function GSvideoBar(barRoot, opt_playerRoot, options) {

  this.processArguments(barRoot, opt_playerRoot, options);

  this.setGlobals();
  this.buildSuperStructure();
  this.buildSearchControl();

  // if we have an auto execute list, then start it up
  if (this.autoExecuteMode) {
    this.cycleTimeClosure = this.methodClosure(this, GSvideoBar.prototype.cycleTimeout, [null]);

    // if there is only a single item in the execute list, then
    // disable autoExecuteMode...
    if ( this.executeList.length == 1) {
      this.autoExecuteMode = false;
      this.execute(this.executeList[0]);
    } else {
      this.cycleTimeout();
    }
  }
}

// cycle time for selecting a video set
GSvideoBar.CYCLE_TIME_EXTRA_SHORT = 3000;
GSvideoBar.CYCLE_TIME_SHORT = 10000;
GSvideoBar.CYCLE_TIME_MEDIUM = 15000;
GSvideoBar.CYCLE_TIME_LONG = 30000;

// cycle mode
GSvideoBar.CYCLE_MODE_RANDOM = 1;
GSvideoBar.CYCLE_MODE_LINEAR = 2;

GSvideoBar.THUMBNAILS_SMALL = 1;
GSvideoBar.THUMBNAILS_MEDIUM = 2;
GSvideoBar.prototype.processArguments = function(barRoot, opt_playerRoot,
                                                 opt_options) {
  this.barRoot = barRoot;
  this.playerRoot = opt_playerRoot;
  this.externalMaster = null;
  this.verticalMode = true;
  this.thumbSize = GSvideoBar.THUMBNAILS_MEDIUM;
  this.autoExecuteMode = false;
  this.executeList = new Array();
  this.cycleTime = GSvideoBar.CYCLE_TIME_MEDIUM;
  this.cycleMode = GSvideoBar.CYCLE_MODE_RANDOM;
  this.cycleNext = 0;
  this.cycleTimer = null;

  // set defaults that are changable via options
  this.resultSetSize = GSearch.SMALL_RESULTSET;
  this.ST_ALL_DONE = "i'm done watching this";

  if (opt_options) {
    // option.largetResultSet
    if (opt_options.largeResultSet && opt_options.largeResultSet == true ) {
      this.resultSetSize = GSearch.LARGE_RESULTSET;
    } else {
      this.resultSetSize = GSearch.SMALL_RESULTSET;
    }

    if ( opt_options.master ) {
      this.externalMaster = opt_options.master;
    }

    if (opt_options.horizontal && opt_options.horizontal == true ) {
      this.verticalMode = false;
    } else {
      this.verticalMode = true;
    }

    if (opt_options.thumbnailSize) {
      if (opt_options.thumbnailSize == GSvideoBar.THUMBNAILS_MEDIUM ) {
        this.thumbSize = GSvideoBar.THUMBNAILS_MEDIUM;
      } else if ( opt_options.thumbnailSize == GSvideoBar.THUMBNAILS_SMALL ) {
        this.thumbSize = GSvideoBar.THUMBNAILS_SMALL;
      } else {
        this.thumbSize = GSvideoBar.THUMBNAILS_MEDIUM;
      }
    }

    if (opt_options.string_allDone) {
      this.ST_ALL_DONE = opt_options.string_allDone;
    }

    // the auto execute list contains
    // a cycleTime value, a cycleMode value, and an array
    // of searchExpressions
    if (opt_options.autoExecuteList) {

      // if specified and valid, then use it, otherwise
      // use default set above
      if (opt_options.autoExecuteList.cycleTime) {
        var cycleTime = opt_options.autoExecuteList.cycleTime;
        if (cycleTime == GSvideoBar.CYCLE_TIME_EXTRA_SHORT ||
            cycleTime == GSvideoBar.CYCLE_TIME_SHORT ||
            cycleTime == GSvideoBar.CYCLE_TIME_MEDIUM ||
            cycleTime == GSvideoBar.CYCLE_TIME_LONG ) {
          this.cycleTime = cycleTime;
        }
      }

      if (opt_options.autoExecuteList.cycleMode) {
        var cycleMode = opt_options.autoExecuteList.cycleMode;
        if (cycleMode == GSvideoBar.CYCLE_MODE_RANDOM ||
            cycleMode == GSvideoBar.CYCLE_MODE_LINEAR) {
          this.cycleMode = cycleMode;
        }
      }

      // now grab the list...
      if (opt_options.autoExecuteList.executeList &&
          opt_options.autoExecuteList.executeList.length > 0 ) {
        // grab from the list
        for (var i=0; i < opt_options.autoExecuteList.executeList.length; i++) {
          this.executeList.push(opt_options.autoExecuteList.executeList[i]);
        }
        this.autoExecuteMode = true;
      }
    }

  }

}

GSvideoBar.prototype.setGlobals = function() {
  this.br_AgentContains_cache_ = {};

  // subserstructure boxes
  this.CL_PLAYERBOX = "playerBox_gsvb";
  this.CL_PLAYING = "playerBox_gsvb playing_gsvb";
  this.CL_IDLE = "playerBox_gsvb idle_gsvb";
  this.CL_PLAYERINNERBOX = "playerInnerBox_gsvb";
  this.CL_VIDEOBARBOX = "videoBarBox_gsvb";
  this.CL_VIDEOBARBOXFULL = "videoBarBox_gsvb full_gsvb";
  this.CL_VIDEOBARBOXEMPTY = "videoBarBox_gsvb empty_gsvb";

  // major app states
  this.CL_ACTIVE = "active_gsvb";

  // player
  this.CL_PLAYER = "player_gsvb";
  this.CL_ALLDONE = "alldone_gsvb";
  this.CL_TITLE = "title_gsvb";

  // results
  this.CL_RESULTSBOX = "resultsBox_gsvb";
  this.CL_BRANDINGBOX = "brandingBox_gsvb";
  this.CL_RESULTTABLE_VERTICAL = "resultTable_gsvb vertical_gsvb";
  this.CL_RESULTTABLE_HORIZONTAL = "resultTable_gsvb horizontal_gsvb";
  this.CL_RESULTCELL = "resultCell_gsvb";
  this.CL_RESULTDIV = "resultDiv_gsvb";
  this.CL_RESULTDIV_SMALL = "resultDiv_gsvb smallResultDiv_gsvb";

  this.smallResultBoxHeight = 39;
  this.resultBoxHeight = 77;
}

GSvideoBar.prototype.buildSuperStructure = function() {

  // build the player box if we are a master, if not
  // link up to the master's player
  if (this.externalMaster == null) {
    this.removeChildren(this.playerRoot);
    this.playerBox = this.createDiv(null, this.CL_PLAYERBOX);
    this.playerAllDone = this.createDiv(this.ST_ALL_DONE, this.CL_ALLDONE);
    this.playerAllDone.onclick = this.methodClosure(this, this.stopVideo, []);
    this.playerInnerBox = this.createDiv(null, this.CL_PLAYERINNERBOX);

    this.playerBox.appendChild(this.playerAllDone);
    this.playerBox.appendChild(this.playerInnerBox);
    this.playerRoot.appendChild(this.playerBox);
    this.cssSetClass(this.playerBox, this.CL_IDLE);
  }
  this.player = null;

  // create the videoBar box
  this.removeChildren(this.barRoot);
  this.barBox = this.createDiv(null, this.CL_VIDEOBARBOX);
  this.barRoot.appendChild(this.barBox);

  // add results box and branding box
  this.resultsBox = this.createDiv(null, this.CL_RESULTSBOX);
  this.barBox.appendChild(this.resultsBox);
  this.cssSetClass(this.barBox, this.CL_VIDEOBARBOXEMPTY);

}

GSvideoBar.prototype.buildSearchControl = function() {
  this.vs = new GvideoSearch();
  this.vs.setResultSetSize(this.resultSetSize);
  this.vs.setSearchCompleteCallback(this, GSvideoBar.prototype.searchComplete, [null]);
}

GSvideoBar.prototype.execute = function(query) {
  this.vs.execute(query);
}

GSvideoBar.prototype.clearAllResults = function() {
  this.cssSetClass(this.barBox, this.CL_VIDEOBARBOXEMPTY);
}

GSvideoBar.prototype.searchComplete = function() {
  if ( this.vs.results && this.vs.results.length > 0) {
    this.cssSetClass(this.barBox, this.CL_VIDEOBARBOXFULL);
    this.removeChildren(this.resultsBox);

    var cell;
    var table;
    var row = null;
    if (this.verticalMode) {
      table = this.createTable(this.CL_RESULTTABLE_VERTICAL);
    } else {
      table = this.createTable(this.CL_RESULTTABLE_HORIZONTAL);
    }
    table.setAttribute("align", "center");

    for (var i = 0; i < this.vs.results.length; i++) {

      var res = this.vs.results[i];

      var imageScaler;
      var resultBoxHeight;
      var resultClass = null;
      if (this.thumbSize == GSvideoBar.THUMBNAILS_MEDIUM ) {
        // full size image
        imageScaler = {width:100,height:75};
        resultBoxHeight = this.resultBoxHeight;
        resultClass = this.CL_RESULTDIV;
      } else {
        // small size image
        imageScaler = {width:50,height:37};
        resultBoxHeight = this.smallResultBoxHeight;
        resultClass = this.CL_RESULTDIV_SMALL;
      }
      var scaled = GSearch.scaleImage(res.tbWidth, res.tbHeight, imageScaler);
      var img = this.createImage(res.tbUrl, scaled.width, scaled.height, null);

      if (this.externalMaster) {
        img.onclick = this.methodClosure(this.externalMaster, this.externalMaster.playVideo, [res]);
      } else {
        img.onclick = this.methodClosure(this, this.playVideo, [res]);
      }

      // manually set the top padding
      if ((resultBoxHeight - scaled.height) > 0) {
        var padTop = Math.round((resultBoxHeight - scaled.height)/2);
        img.setAttribute("vspace", padTop);
      }

      // compute duration
      var seconds = res.duration;
      var minutes = parseInt(seconds/60);
      var durationString;
      if (minutes > 0) {
        durationString = minutes + "m";
        var remainder = seconds%60;
        if (remainder > 20) {
          durationString += " " + remainder + "s";
        }
      } else {
        durationString = seconds + "s";
      }

      var toolTip = res.titleNoFormatting + " ( " + durationString + " )";
      var div = this.createDiv(null, resultClass);
      div.title = toolTip;
      div.appendChild(img);

      // create a new row for each result when in vertical mode
      // otherwise, jam everything into a single row.
      if (this.verticalMode) {
        row = this.createTableRow(table);
      } else {
        if (row == null) {
          row = this.createTableRow(table);
        }
      }
      cell = this.createTableCell(row, this.CL_RESULTCELL);
      cell.setAttribute("align", "center");
      cell.appendChild(div);
    }

    // now add in the branding...
    row = this.createTableRow(table);
    var brandingOrientation;
    if (this.verticalMode) {
      cell = this.createTableCell(row, this.CL_RESULTCELL);
      brandingOrientation = GSearch.VERTICAL_BRANDING;
    } else {
      cell = this.createTableCell(row, this.CL_RESULTCELL);
      if (this.br_IsIE()) {
        cell.setAttribute("colSpan", this.vs.results.length);
      } else {
        cell.setAttribute("colspan", this.vs.results.length);
      }
      brandingOrientation = GSearch.HORIZONTAL_BRANDING;
    }
    GSearch.getBranding(cell, brandingOrientation);

    this.resultsBox.appendChild(table);
  } else {
    this.cssSetClass(this.barBox, this.CL_VIDEOBARBOXEMPTY);
  }
}

GSvideoBar.prototype.playVideo = function(result) {
  this.stopVideo();
  if (this.autoExecuteMode && this.cycleTimer) {
    clearTimeout(this.cycleTimer);
    this.cycleTimer = null;
  }
  if (result.playUrl && result.playUrl != "") {
    //var playUrl = result.playUrl + "&autoPlay=true";
    var playUrl = result.playUrl + "&autoPlay=true&playerMode=simple";


    this.cssSetClass(this.playerBox, this.CL_PLAYING);

    if (this.br_IsOpera()) {
      this.player = document.createElement("object");
      this.player.className = this.CL_PLAYER;
      this.player.setAttribute("type", "application/x-shockwave-flash");
      this.player.setAttribute("data", playUrl);
    } else {
      this.player = document.createElement("embed");
      this.player.className = this.CL_PLAYER;
      this.player.setAttribute("type", "application/x-shockwave-flash");
      this.player.setAttribute("src", playUrl);
      this.player.setAttribute("bgcolor", "#000000");
    }
    this.playerInnerBox.appendChild(this.player);

    // the title
    var title = this.createDivLink(result.url, result.title, null, this.CL_TITLE);

    this.playerInnerBox.appendChild(title);
  }
}

GSvideoBar.prototype.stopVideo = function() {
  this.cssSetClass(this.playerBox, this.CL_IDLE);
  this.removeChildren(this.playerInnerBox);
  if (this.player) {
    delete(this.player);
    this.player = null;
  }
  if (this.autoExecuteMode) {
    this.cycleTimer = setTimeout(this.cycleTimeClosure, this.cycleTime);
  }
}

GSvideoBar.prototype.cycleTimeout = function() {
  // select a new video
  // execute a search
  // restart the timer
  if ( this.player == null ) {
    // if there is only a single item in the execute list, then
    // disable autoExecuteMode...
    if ( this.executeList.length == 1) {
      this.autoExecuteMode = false;
      this.execute(this.executeList[0]);
    } else {
      var index = 0;
      if (this.cycleMode == GSvideoBar.CYCLE_MODE_RANDOM) {
        var max = this.executeList.length - 1;
        index = Math.round(max * Math.random());
      } else if (this.cycleMode == GSvideoBar.CYCLE_MODE_LINEAR){
        index = this.cycleNext;
        this.cycleNext++;
        if (this.cycleNext >= this.executeList.length) {
          this.cycleNext = 0;
        }
      }
      this.execute(this.executeList[index]);
      this.cycleTimer = setTimeout(this.cycleTimeClosure, this.cycleTime);
    }
  }
}

/**
 * Static Helper Method
*/
GSvideoBar.methodCallback = function(object, method) {
  return function() {
    return method.apply(object, arguments);
  }
}

/**
 * Class methods
*/
GSvideoBar.prototype.methodClosure = function(object, method, opt_argArray) {
  return function() {
    return method.apply(object, opt_argArray);
  }
}

GSvideoBar.prototype.createDiv = function(opt_text, opt_className) {
  var el = document.createElement("div");
  if (opt_text) {
    el.innerHTML = opt_text;
  }
  if (opt_className) { el.className = opt_className; }
  return el;
}

GSvideoBar.prototype.removeChildren = function(parent) {
  while (parent.firstChild) {
    parent.removeChild(parent.firstChild);
  }
}

GSvideoBar.prototype.removeChild = function(parent, child) {
  parent.removeChild(child);
}

GSvideoBar.prototype.cssSetClass = function(el, className) {
  el.className = className;
}

GSvideoBar.prototype.createTable = function(opt_className) {
  var el = document.createElement("table");
  if (opt_className) { el.className = opt_className; }
  return el;
}

GSvideoBar.prototype.createTableRow = function(table, opt_className) {
  var tr = table.insertRow(-1);
  if (opt_className) { tr.className = opt_className; }
  return tr;
}

GSvideoBar.prototype.createTableCell = function(tr, opt_className) {
  var td = tr.insertCell(-1);
  if (opt_className) { td.className = opt_className; }
  return td;
}

GSvideoBar.prototype.createDivLink = function(href, text, opt_target, opt_className) {
  var div = this.createDiv(null, opt_className);
  var el = document.createElement("a");
  el.href = href;
  el.appendChild(document.createTextNode(text));
  if (opt_className) {
    el.className = opt_className;
  }
  if (opt_target) {
    el.target = opt_target;
  }
  div.appendChild(el);
  return div;
}

GSvideoBar.prototype.createImage = function(src, opt_w, opt_h, opt_className) {
  var el = document.createElement("img");
  el.src = src;
  if (opt_w) { el.width = opt_w; }
  if (opt_h) { el.height = opt_h; }
  if (opt_className) { el.className = opt_className; }
  return el;
}

GSvideoBar.prototype.getNodeWidth = function(node) {
  return node.offsetWidth;
}

GSvideoBar.prototype.br_AgentContains_ = function(str) {
  if (str in this.br_AgentContains_cache_) {
    return this.br_AgentContains_cache_[str];
  }

  return this.br_AgentContains_cache_[str] =
    (navigator.userAgent.toLowerCase().indexOf(str) != -1);
}

GSvideoBar.prototype.br_IsIE = function() {
  return this.br_AgentContains_('msie');
}

GSvideoBar.prototype.br_IsKonqueror = function() {
  return this.br_AgentContains_('konqueror');
}

GSvideoBar.prototype.br_IsOpera = function() {
  return this.br_AgentContains_('opera');
}

GSvideoBar.prototype.br_IsSafari = function() {
  return this.br_AgentContains_('safari') || this.br_IsKonqueror();
}

GSvideoBar.prototype.br_IsNav = function() {
  return !this.br_IsIE() &&
         !this.br_IsSafari() &&
         this.br_AgentContains_('mozilla');
}

GSvideoBar.prototype.br_IsWin = function() {
  return this.br_AgentContains_('win');
}

