if(typeof(console) == 'undefined') {
  console = {log: function(txt) {
    var o = $("#debug");
    o.html(o.html()+txt+'<br />');
  }};
}

$(document).ready(function(){
// $(".change_settings").tooltip({position:-50,width:280,title:'Customise your TV guide',timescookie:15,text:'Click here to change and order the channels included in your TV guide.',up:true,top:150});
}); 

function doSearch(cat) {
  if(typeof(tvg) == 'object') {
    tvg.doSearch(cat);
  }
}

function viewCat(cat) {
  doSearch(cat);
}

function closeSearch() {
  $("#searchresults").slideUp("slow");
}

function loadSettings() {
  if(typeof(tvg) == 'object') {
    tvg.loadSettings();
  }
}

function viewProgInfo(progId) {
  if(typeof(tvg) == 'object') {
    tvg.viewProgDetails(progId);
  }
}

function closeSettings() {
  $("#settings").slideUp("slow");
}

function updatelist(id,name,b) {
  var fm = document.getElementById("editform");
  var obj;
  var i;
  var pos = -1;

  obj = fm.channellist;
  var len = obj.options.length;
  for(i = 0;i < obj.options.length;i++) {
    if(obj.options[i].value == id) {
      pos = i;
      break;
    }
  }

  if(b && (pos == -1)) {
    // Add item.
    obj.options[obj.options.length] = new Option(name,id);
  } else if(!b && (pos != -1)) {
    // Remove item.
    for(i = pos;i < len-1;i++) {
      obj.options[i].value = obj.options[i+1].value;
      obj.options[i].text = obj.options[i+1].text;
    }
    obj.options.length--;
  }

}

function selectChannelCat(catId,select) {
  var catClass = '.chancat'+catId;
  var sel = select ? 'checked' : '';
  $(catClass).find("input").each(function(i) {
    this.checked = select;
    var id = this.value;
    var name = $(this).parent().find("label").html();
    updatelist(id,name,select);
  });
}

function selectChannelService() {
  var service = $("#tvservice").val();
  $(".settingschannel input").each(function() {
     this.checked = 'checked';
     var id = this.value;
     var name = $(this).parent().find("label").html();
     updatelist(id,name,true);
  });
  $(".settingschannel").not(".tsp_"+service).find("input").each(function() {
    this.checked = '';
     var id = this.value;
     var name = $(this).parent().find("label").html();
     updatelist(id,name,false);
  });
}

function moveitem(dir) {
  var obj = document.getElementById('channellist');
  var len = obj.options.length;
  var newpos = -1;
  var si = obj.selectedIndex;
  if(si < 0) {
    return;
  }

  if(dir == "up") {
    newpos = si-1;
  } else if(dir == "down") {
    newpos = si+1;
  }
  if((newpos >= 0) && (newpos < len)) {
    var tmpv = obj.options[si].value;
    var tmpt = obj.options[si].text;
    obj.options[si].value = obj.options[newpos].value;
    obj.options[si].text = obj.options[newpos].text;
    obj.options[newpos].value = tmpv;
    obj.options[newpos].text = tmpt;
    obj.selectedIndex = newpos;
  }

}

function checkForm() {
  var fm = document.getElementById('editform');
  var obj = fm.channellist;
  var i;
  var res = "";
  for(i = 0;i < obj.options.length;i++) {
    if(i > 0) {
      res += ",";
    }
    res += obj.options[i].value;
  }
  fm.channelids.value = res;
}

function saveSettings() {
  checkForm();
  var url = '/tv/tv-guide/settings.php';
  var channelids = $("#channelids").val();
  var service = $("#tvservice").val();
  var params = {
    'channelids': channelids,
    'tvservice': service,
    'mode':'save'
  };
  for(var i = 1;i <= 3;i++) {
    params['rg'+i] = $("#rg"+i).val();
  }
  $.post(url,params,function(data,status) {
    // Reset the tv data we have.
    tvg.reset();

    // close settings window.
    closeSettings();
  });
}

function updateRegion(ob,rg) {
  var elem = ob.options[ob.selectedIndex];
  var name = elem.text;
  var val = elem.value;
  var oldVal = $("#rg"+rg+"channelid").val();
  var cl = document.getElementById('channellist');
  var i;
  for(i = 0;i <cl.options.length;i++) {
    if(cl.options[i].value == oldVal) {
      cl.options[i].value = val;
      cl.options[i].text = name;
      break;
    }
  }
  $("#rg"+rg+"channelid").val(val);
  $("#rg"+rg+"text").html(name);
}


// Class TVGuide.
// Constructor.
function TVGuide() {
  this.config = {
    elemContainer: 'tiscalitvguide',
    elemSettings: 'settings',
    elemSettingsInner: 'settingscontent',
    elemChannelNames: 'channelnames',
    elemChannelNamesInner: 'channelnamesinner',
    elemGrid: 'grid',
    elemGridInner: 'gridinner',
    elemTimes: 'times',
    elemStatus: 'status',
    elemBusy: 'busy',
    elemClock: 'clock',
    elemOptions: 'options',
    elemDays: 'days',
    elemInfoBox: 'infobox',
    elemInfoBoxShad: 'infoboxshad',
    elemCurrentTimeLine: 'currenttimeline',

    imagePath: '/tv/images/',
    scriptPath: '/tv/tv-guide/',
    height: 420,
    channelWidth: 120,
    channelHeight: 39,
    minuteScale: 4,
    planner: 0,
    showOptions: 1,
    programmeLink: '',
    type: 'standard',
    channelsToCache: 10,

    dummy: ''
  };
  this.drag = {status: ''};
  this.days = [];
  this.daysByDate = [];
  this.dayIndex = 1;
  this.services = [];
  this.progs = {};
  this.progsById = {};
  this.tempProgsById = {};
  this.instanceName = '';
  this.channels = [];
  this.channelsById = [];
  this.castInfo = {};
  this.scrollInfo = {};
  this.selectedProg = '';
  this.idToView = '';
  this.busyCount = 0;
  this.viewTypes = {
    'standard': 'Standard',
    'vertical': 'Vertical',
    'list': 'List'
  };

  // functions.

  this.getViewTypesHTML = function(type) {
    if(typeof(type) != 'string') {
      type = this.config.type;
    }
    var res = '<div class="viewtypes tool">';
    res += '<div style="font-size: 10px;">View types:</div>';
    for(var k in this.viewTypes) {
      var sel = (k == type) ? ' active' : '';
      res += '<a href="'+this.config.scriptPath+'change-view.php?view='+escape(k)+'" class="tooltip-js'+sel+'" title="Change to '+this.viewTypes[k]+' view">';
      res += '<img src="'+this.config.imagePath+'icons/gallery/view-'+k+'.png" width="21" height="18" alt="" title="" style="margin-top: 6px;" />';
      res += '</a>';
    }
      res += '<div class="md_clear"></div>';
    res += '</div>';
    return res;
  };

  this.getCatIcons = function(categories,showText) {
    var catstr = categories;
    var cats = catstr.split(/\s*[,\/]\s*/);
    var res = '';
    var i;
    var cat = '';
    var catName = '';
    var seen = [];
    for(i = 0;i < cats.length;i++) {
      catName = cats[i];
      if(seen[catName]) {
        continue;
      }
      seen[catName] = 1;
      cat = catName.toLowerCase().replace(/[^\w]/g,'');
      if(showText) {
        if(i > 0) {
          res += ', ';
        }
        res += catName;
      }
      res += '<img src="'+this.config.imagePath+'t.gif" width="6" height="6" class="cat_'+cat+'" alt="'+cat+'" style="margin-left: 2px;" />';
    }
    return res;
  };

  this.updateCastData = function(cast,dayIndex) {
    var k;
    var display = false;
    if(!this.castInfo[dayIndex]) {
      this.castInfo[dayIndex] = {};
      display = true;
    }

    for(k in cast) {
      var i;
      if(!this.castInfo[dayIndex][k]) {
        this.castInfo[dayIndex][k] = [];
      }
      var a = this.castInfo[dayIndex][k];
      for(i = 0;i < cast[k].length;i++) {
        a[a.length] = cast[k][i];
      }
    }
    if(display) {
      this.displayCastInfo();
    }
  };

  this.displayCastInfo = function() {
    var dt = new Date();
    var html = '';
    var cast = this.castInfo[this.dayIndex];
    if(cast) {
      var keys = [];
      var max = 3;
      var imgW = 76;
      var imgH = 76;
      for(var k in cast) {
        keys[keys.length] = k;
      }
      keys.sort(function() {return 0.5 - Math.random();});
      var num = (keys.length > max) ? max : keys.length;
      for(i = 0;i < num;i++) {
        var rnd = Math.floor(Math.random() * cast[keys[i]].length);
        var p = cast[keys[i]][rnd];
        var prog = this.progsById[p.id];
        html += '<div class="castmember">';
        html += '<a href="#" onmouseup="'+this.instanceName+'.viewProgDetails(\''+p.id+'\');return false;" title="'+p.name+'">';
        html += '<img src="'+p.image+'" width="'+imgW+'" height="'+imgH+'" alt="'+p.name+'" class="castimg fleft" />';
        html += '<strong>' + p.name + '</strong>' + '<br />';
        html += prog.title + '<br />';
        html += this.getTimeFromTimeCode(prog.start);
        html += '</a>';
        html += '</div>';
      }
    }

    $("#cast").fadeOut("slow",function() {
      $(this).html(html).fadeIn("slow");
    });

  };

  this.isVertical = function() {
    return this.config.type == 'vertical';
  };
 
  this.getSlotFromNum = function(num) {
    var slot = parseInt(num / 60 * this.config.minuteScale,10);
    return slot;
  };

  this.getSID = function(title) {
    var res = title.toLowerCase();
    res = res.replace(/[^a-z0-9\-\s]/g,'');
    res = res.replace(/^\s*/,'');
    res = res.replace(/\s*$/,'');
    res = res.replace(/\s+/g,'-');
    return res;
  };

  this.doSearch = function(cat) {
    var q = $("#searchquery").val();
    var url = this.config.scriptPath+'search.php';
    var dt = this.days[this.dayIndex].date;
    var epgFormat = 'standard';
    var params = {bd: dt,epgformat: epgFormat};
    if(cat) {
      params.category = cat;
    } else {
      params.query = q;
    }
    $.get(url,params,function(data,status) {
      $("#searchresultsinner").html(data);
      $("#searchresults").slideDown("slow");
    });
  };

  this.getDateFromTimeCode = function(s) {
    var res = '';
    if(s.match(/^(\d\d\d\d)(\d\d)(\d\d)/)) {
      res = RegExp.$1+'-'+RegExp.$2+'-'+RegExp.$3;
    }
    return res;
  };

  this.getTimeFromTimeCode = function(s) {
    var res = '';
    if(s.match(/(\d\d)(\d\d)\d\d$/)) {
      var hr = RegExp.$1;
      var min = RegExp.$2;
      hr = parseInt(hr.replace(/^0/,''),10);
      var suffix = 'am';
      if(hr >= 12) {
        suffix = 'pm';
        if(hr > 12) {
          hr -= 12;
        }
      } else if(hr === 0) {
        hr = 12;
      }
      res = hr+':'+min+suffix;
    }
    return res;
  };

  this.goToNow = function() {
    var dt = new Date();
    var hr = dt.getHours();
    var min = dt.getMinutes();
    var slot = hr + ((min >= 30) ? 0.5 : 0);
    this.setDay(1);
    this.setSlot(slot);
  };

  this.updateClock = function() {
    var dt = new Date();
    var suffix = 'am';
    var hours = dt.getHours();
    var hr = hours;
    if(hr >= 12) {
      suffix = 'pm';
      if(hr > 12) {
        hr -= 12;
      }
    } else if(hr === 0) {
      hr = 12;
    }
    var minutes = dt.getMinutes();
    var min = minutes;
    if(min < 10) {
      min = '0'+min;
    }
    var tm = hr+':'+min+suffix;
    var html = '<a href="#" onclick="'+this.instanceName+'.goToNow();return false;">Now: '+tm+'</a>';
    $("#"+this.config.elemClock).html(html);

    var timeLineSize = 6;
    var timeLeft = ((hours * 60) + minutes) * this.config.minuteScale;
    timeLeft -= (timeLineSize / 2);
    var attrName = this.isVertical() ? 'top' : 'left';
    var attr = {};
    attr[attrName] = timeLeft + 'px';
    $("#currenttimeline").animate(attr,1000);
  };

  this.setDay = function(i) {
console.log('set day func');
    this.closeInfoBox(true);
    if(this.days[i]) {
      $(".daylinks").removeClass("clink");
      $("#daylink_"+i).addClass("clink");
      this.dayIndex = i;
      this.updateVisible();
    }
  };

  this.setSlot = function(slot,func) {
    $(".slotnum").removeClass("clink");
    $("#slot_"+parseInt(slot,10)).addClass("clink");
    var offset = slot * 60 * this.config.minuteScale;
    var attr = this.isVertical() ? 'scrollTop' : 'scrollLeft';
    var animOb = {};
    animOb[attr] = offset;
    $("#"+this.config.elemGrid).animate(animOb,1000,func);
  };

  this.getSlot = function() {
    var dt = new Date();
    var hr = dt.getHours();
    var min = dt.getMinutes();
    var slot = hr + ((min >= 30) ? 0.5 : 0);
    return slot;
  };

  this.loadSettings = function() {
    var url = this.config.scriptPath+'settings.php';
    $("#settings").find("#settingscontent").load(url).end().slideDown("slow");
  };

  this.loadChannels = function(ids) {
    var thisOb = this;
    var progs = this.progs;
    var idList = '';
    for(var i = 0;i < ids.length;i++) {
      idList += escape(ids[i]);
      if(i < ids.length-1) {
        idList += '+';
      }
    }
    var url = this.config.scriptPath+'tv-data-json.php?callback=?&channelids='+idList+'&bd='+this.days[this.dayIndex].date;
    if(this.planner) {
      url += '&planner=1';
    }
    this.busy(true);
    $.getJSON(url,function(data) {
      thisOb.busy(false);
      var i,j,k,x;
      for(i = 0;i < data.channels.length;i++) {
        var chan = data.channels[i];
        var channelId = chan.id;
        var dayIndex = -1;
        for(j = 0;j < thisOb.days.length;j++) {
          if(thisOb.days[j].date == data.date) {
            dayIndex = j;
            break;
          }
        }
        thisOb.channelsById[channelId].status[dayIndex] = 1;
        var od = (chan.od == '1');
        var dayIndices = [];
        if(od) {
          // If a on demand channel, set up same data for all days.
          for(x in thisOb.days) { 
            dayIndices[dayIndices.length] = x;
          }
        } else {
          dayIndices[dayIndices.length] = thisOb.dayIndex;
        }

        for(k = 0;k < dayIndices.length;k++) {
          dayIndex = dayIndices[k];
          if(!progs[dayIndex]) {
            progs[dayIndex] = {};
          }
          if(!progs[dayIndex][channelId]) {
            progs[dayIndex][channelId] = {};
          }
          for(j = 0;j < chan.programmes.length;j++) {
            var p = chan.programmes[j];
            p.channel_id = chan.id;
            if(!progs[dayIndex][channelId][p.evid]) {
              progs[dayIndex][channelId][p.evid] = p;
            } else {
              for(x in p) {
                progs[dayIndex][channelId][p.evid][x] = p[x];
              }
            }
            thisOb.progsById[p.evid] = progs[dayIndex][channelId][p.evid];
          }
        }
        thisOb.addChannelNameInBg(thisOb.channelsById[chan.id]);
        thisOb.addChannelInBg(thisOb.channelsById[chan.id]);
      }
      thisOb.updateCastData(data.cast,thisOb.dayIndex);
    });
  };

  this.loadProgDetails = function(evId,func,tempStore) {
    var thisOb = this;
    var url = this.config.scriptPath+'tv-prog-details-json.php?id='+escape(evId)+'&callback=?';
    $.getJSON(url,function(data) {
      var channelId = data.channel_id;
      var od = (data.od === '1');
      var ob;
      if(tempStore) {
        ob = data;
        ob.left = -1000;
        ob.top = -1000;
        ob.width = 1;
        ob.height = 1;
        ob.niceTime = 'ANY TIME';
        thisOb.tempProgsById[evId] = ob;
      } else {
        for(i = 0;i < thisOb.days.length;i++) {
          if(od || (data.build_date == thisOb.days[i].date)) {
            if(!thisOb.progs[i][channelId]) {
              thisOb.progs[i][channelId] = {};
            }
            if(!thisOb.progs[i][channelId][evId]) {
              thisOb.progs[i][channelId][evId] = {};
            }
            ob = thisOb.progs[i][channelId][evId];
            for(k in data) {
              ob[k] = data[k];
            }
            thisOb.progsById[evId] = ob;
          }
        }
      }
      if(typeof(func) == 'function') {
        func();
      }
      if(thisOb.selectedProg == evId) {
        thisOb.showProgDetails(ob,evId);
      }
      setupTooltip();
    });
  };

  this.showProgDetails = function(ob,idPrefix) {
    var detailsId = idPrefix+'_details';
    var iBox = $("#"+this.config.elemInfoBox);
    var chanNumsOb = iBox.find(".channelnumbers");
    
    iBox.find(".chanlogo, .channelnumbers").show();
    iBox.find(".progtitle").removeClass('nowrap');
    
    var iBoxH = iBox.height();
    var iTopH = iBox.find('.infotop').height();
    var paddingH = 10;
    var chanNumsH = 25;
    var detailsH = iBoxH - iTopH - paddingH - chanNumsH;
    var i;
    var html = '';
    if(ob) {
      html += '<div class="fright" style="width: 200px; margin-left: 5px;">';
      // Image.
      if(ob.image) {
        html += '<div style="margin-bottom: 4px;">';
        html += '<img src="'+ob.image+'" width="200" height="112" alt="'+ob.title+'" />';
        html += '</div>';
      }
      var showURL = '/tv/shows/details/'+this.getSID(ob.title)+'/';
      var tools = [];
      tools[tools.length] = {img:'/tv/images/icons/gallery/more-info.png',margin:'4px 0px 0px 0px',title:'More information',url:showURL};
	  favStatus = 'add';
	  if(ob.fav == 1)	favStatus = 'remove';
      tools[tools.length] = {img:'/tv/images/icons/gallery/fav-'+favStatus+'.png',margin:'6px 0px 0px 1px',title:''+favStatus+' as Favourite',click:'return '+favStatus+'Fav(\'show\',\''+ob.show_id+'\',this,\''+ob.evid+'\')'};
      tools[tools.length] = {img:'/tv/images/icons/gallery/reminder.png',margin:'6px 0px 0px 0px',title:'Remind me',click:'remindMe(\''+ob.evid+'\')'};
	  planStatus = 'add'
	  if(ob.plan == 1)	planStatus = 'remove';  
      tools[tools.length] = {img:'/tv/images/icons/gallery/planner-'+planStatus+'.png',margin:'7px 0px 0px 0px',title:''+planStatus+' Planner',click:'return '+planStatus+'Planner(\''+ob.show_id+'\',this,\''+ob.evid+'\')'};
      var toolsHTML = '';
      var chan = this.channelsById[ob.channel_id];
      var od = chan && (chan.od === '1');
      if(ob.evid.indexOf('placeholder') !== 0) {
        toolsHTML += '<div class="fright tool">';
        for(i in tools) {
          var t = tools[i];
          if(!od || !t.img.match(/remind/)) {
            var url = t.url ? t.url : '#';
            toolsHTML += '<a title="'+t.title+'" href="'+url+'" class="tooltip-js" onclick="'+t.click+'"><img src="'+t.img+'" style="margin:'+t.margin+';"/></a>';
          }
        }
        toolsHTML += '</div>';
        toolsHTML += '<div style="clear: right;"></div>';
      }
      html += toolsHTML;

      // Show info about promo if there is one.
      var promoId = parseInt(ob.promo.id,10);
      if(promoId > 0) {
        this.showVideo(ob.promo);
      }
      if(typeof(curVid) == 'object') {
        if(promoId > 0) {
          var txt = '';
          if(curVid.filename == ob.promo.url) {
            txt = ' currently playing above.';
          } else {
            var pl = wimpy_getPlaylist();
            for(i = 0;i < pl.length;i++) {
              if(ob.promo.url == pl[i].filename) {
                txt = ' is available to <a href="#" onclick="wimpy_gotoTrack('+(i+1)+'); return false;">watch</a> above.';
                break;
              }
            }
          }
          if(txt !== '') {
            html += '<div>';
            html += '<strong>'+ob.promo.title+'</strong> '+txt;
            html += '</div>';
          }
        }
      }

      html += '</div>';

      html += '<div style="height: 100%; overflow: auto;">';
      html += '<div style="margin-bottom: 4px;">';
      if(ob.ep) {
        html += '<strong>'+ob.ep+'</strong><br />';
      }
      html += (ob.desc === '') ? 'No synopsis' : ob.desc;
      html += '</div>';
      if(ob.cert) {
        html += '<img src="'+this.config.imagePath+'certificates/cm-'+ob.cert+'.gif" width="50" height="50" alt="'+ob.cert+'" class="fright" style="margin-left: 5px;" />';
      }

      if(parseInt(ob.dur,10) > 0) {
        html += '<div><strong>Duration:</strong> '+ob.dur+' mins</div>';
      }
      if(parseInt(ob.year,10) > 0) {
        html += '<div><strong>Year:</strong> '+ob.year+'</div>';
      }
      if(ob.cat) {
        html += '<div><strong>Category:</strong> '+this.getCatIcons(ob.cat,1)+'</div>';
      }
      if(ob.cast) {
        html += '<div><strong>Cast:</strong> '+ob.cast+'</div>';
      }

      if(ob.other_airings.length) {
        var also = '';
        for(i = 0;i < ob.other_airings.length;i++) {
          var p = ob.other_airings[i];
          var dt = this.getDateFromTimeCode(p.start);
          var tm = this.getTimeFromTimeCode(p.start);
          var dtOb = this.daysByDate[dt];
          if(dtOb) {
            also += '<li><a href="#" onclick="'+this.instanceName+'.viewProgDetails(\''+p.evid+'\');return false;">'+dtOb.rel_name+' '+tm+' on '+p.channel_name+'</a></li>';
          }
        }
        if(also) {
          html += '<div style="margin: 10px 0px;"><strong>'+ob.title+'</strong> is also showing at these times: ';
          html += '<ul>';
          html += also;
          html += '</ul>';
          html += '</div>';
        }
      }
      if(ob.attributes) {
        html += '<div><strong>Attributes:</strong> '+ob.attributes+'</div>';
      }
      html += '</div>';

      // Channel numbers.
      var cn = '';
      var chan = this.channelsById[ob.channel_id];
      if(chan) {
        for(i = 0;i < this.services.length;i++) {
          var service = this.services[i];
          var chan_num = chan['cn_'+service.id];
          if(chan.rg !== '0') {
            chan_num = service.rp+chan.rg;
          }
          if(chan_num !== '') {
            cn += '<div class="chan_num chan_num_'+service.id+'"><div class="txt">channel '+chan_num+'</div></div>';
          }
        }
        if(cn !== '') {
          cn = '<div style="margin-top: 4px;">'+cn+'</div>';
        }
      }
      chanNumsOb.html(cn).show();

    } else {
      html = '<img src="'+this.config.imagePath+'busy.gif" width="18" height="18" alt="busy" />';
    }
    if(this.selectedProg == idPrefix) {
      $("#"+this.config.elemInfoBox).find(".details").html(html).css("height",detailsH+'px').show();
    }

  };
  
  this.busy = function(b) {
    this.busyCount += b ? 1 : -1;
    var val = (this.busyCount === 0) ? 'none' : 'block';
    $("#"+this.config.elemBusy).css("display",val);
  };

  this.addChannelNameInBg = function(channel) {
    var thisOb = this;
    var f = function() {
      channel = thisOb.addChannelName(channel);
    };
    setTimeout(f,0);
  };

  this.addChannelName = function(channel) {
    if(channel.nameContainer) {
      return channel;
    }
    var p = document.getElementById(this.config.elemChannelNamesInner);
    var ob = document.createElement('div');
    var posAttr = 'top';
    var sizeAttr = ['height','channelHeight'];
    if(this.isVertical()) {
      posAttr = 'left';
      sizeAttr = ['width','channelWidth'];
    }
    ob.className = 'channelname';
    ob.style[posAttr] = channel[posAttr]+'px';
    ob.style[sizeAttr[0]] = this.config[sizeAttr[1]]+'px';
    ob.innerHTML = this.formatChannelName(channel);
    p.appendChild(ob);
    channel.nameContainer = ob;
    var defOpac = '0.7';
    $(ob).css("opacity",defOpac);
    return channel;
  };

  this.addChannelInBg = function(channel) {
    var thisOb = this;
    var f = function() {
      channel = thisOb.addChannel(channel);
    };
    setTimeout(f,0);
  };

  this.clearProgView = function(channel) {
    if(this.idToView !== '') {
      var fields = this.idToView.split(/_/);
      if(fields.length == 2) {
        var progId = this.idToView;
        var channelId = fields[0];
        if(channel.id == channelId) {
          var p = this.progsById[progId];
console.log('clear:'+this.selectedProg+","+this.idToView)
          //if($("#"+this.idToView).length) {
            if(this.selectedProg == this.idToView) {
              this.zoom(progId);
            }
            this.zoom(progId);
          //}
          this.idToView = '';
        }
      }
    }
  };

  this.addChannel = function(channel) {
    channel.lastAdded = (new Date()).getTime();
    this.busy(true);
    var p1 = document.getElementById(this.config.elemGridInner);
    var dayIndex = this.dayIndex;
    channel.dayIndex = dayIndex;
    if(channel.container[dayIndex]) {
      var attached = channel.container[dayIndex].attached;
      if(attached != 1) {
        p1.appendChild(channel.container[dayIndex]);
        channel.container[dayIndex].attached = 1;
      }
      this.clearProgView(channel);
      this.busy(false);
      return channel;
    }
    var thisOb = this;

    var ob = document.createElement('div');
    ob.className = 'channel';
    if(this.isVertical()) {
      ob.style.left = channel.left+'px';
      ob.style.height = (24 * 60 * this.config.minuteScale)+'px';
      ob.style.width = this.config.channelWidth+'px';
    } else {
      ob.style.top = channel.top+'px';
      ob.style.width = (24 * 60 * this.config.minuteScale)+'px';
      ob.style.height = this.config.channelHeight+'px';
    }
    ob.innerHTML = '';
    ob = p1.appendChild(ob);
    channel.container[dayIndex] = ob;
    var defOpac = '0.7';

    // Add content to channel.
    //this.showChannel(channel,channel.container[dayIndex]);
    this.showChannelInBg(channel,channel.container[dayIndex]);

    this.busy(false);
    return channel;
  };

  this.remChannelName = function(channel) {
    var p = document.getElementById(this.config.elemChannelNamesInner);
    if(channel.nameContainer) {
      p.removeChild(channel.nameContainer);
      channel.nameContainer = null;
    }
    return channel;
  };

  this.remChannel = function(channel) {
    var dayIndex = channel.dayIndex;
    var node = channel.container[dayIndex];
    if(node) {
      var p = node.parentNode;
      if(p) {
        node = p.removeChild(node);
        node.attached = 0;
      }
      channel.container[dayIndex] = node;
    }
    return channel;
  };

  this.remUncachedChannels = function() {
    var i;
    var remList = [];
    for(i = 0;i < this.channels.length;i++) {
      var chan = this.channels[i];
      if(chan.visible) {
        chan.lastAdded = (new Date()).getTime();
      } else {
        remList[remList.length] = chan;
      } 
    }

    // Order remove list by those most recently viewed and skip removal of cached channels
    // i.e. those between 0 and this.config.channelsToCache-1.

    remList.sort(function(a,b) {
      var numA = a.lastAdded ? a.lastAdded : 0;
      var numB = b.lastAdded ? b.lastAdded : 0;
      if(numA == numB) {
        return 0;
      }
      return (numA > numB) ? -1 : 1;
    });

    for(i = this.config.channelsToCache;i < remList.length;i++) {
      var chan = remList[i];
      chan = this.remChannel(chan);
    }

  }

  this.showChannelInBg = function(channel,domOb) {
    var thisOb = this;
    var f = function() {
      thisOb.showChannel(channel,domOb);
    };
    setTimeout(f,0);
  };

  this.showChannel = function(channel,domOb) {
    var html = '';
    var res = false;
    var progs = this.progs[this.dayIndex][channel.id];
    var i;
    var od = (channel.od == '1');
    var odSize = 0;
    if(od) {
      var num = 0;
      for(i in progs) {
        num++;
      }
      if(num > 0) {
        odSize = parseInt(this.config.minuteScale * 60 * 24 / num,10);
      }
    }

    var index = 0;
    var vertical = this.isVertical();
    var posAttr = ['left','top'];
    var sizeAttr = ['width','height'];
    var configAttr = 'channelHeight';
    if(vertical) {
      posAttr = ['top','left'];
      sizeAttr = ['height','width'];
      configAttr = 'channelWidth';
    }

    for(i in progs) {
      var p = progs[i];
      //p.height = this.config.channelHeight;
      p[sizeAttr[1]] = this.config[configAttr];
      if(od) {
        p[posAttr[0]] = index * odSize;
        p[sizeAttr[0]] = odSize;
        p.niceTime = 'ANY TIME';
        if(((typeof channel.vod_alt) == 'string') && (channel.vod_alt !== '')) {
          // This stops the ajax call for extra info.
          p.desc = '';
          p.promo = {};
          p.other_airings = [];
        }
      } else if(p.start.match(/(\d\d)(\d\d)\d\d$/)) {
        var hr = RegExp.$1;
        var min = RegExp.$2;
        hr = parseInt(hr.replace(/^0/,''),10);
        min = parseInt(min.replace(/^0/,''),10);
        p[posAttr[0]] = parseInt((hr * 60 * this.config.minuteScale) + (min * this.config.minuteScale),10);
        p[sizeAttr[0]] = p.dur * this.config.minuteScale;
        p.niceTime = this.getTimeFromTimeCode(p.start);
      }
      html += this.formatProgramme(p,channel);
      //this.addProgrammeInBg(p,channel,domOb);
      index++;
    }

    if(domOb) {
      domOb.innerHTML = html;
      res = true;
    }

    var defOpac = '0.7';
    var dayIndex = channel.dayIndex;
    var thisOb = this;

    $(channel.container[dayIndex]).find('.prog').hover(
      function() {
        if(thisOb.drag.status != 'dragging') {
          $(this).addClass('proghilite titlehilite');
        }
      },
      function() {
        $(this).removeClass('proghilite titlehilite');
      }
    );

    $(channel.container[dayIndex]).hover(
      function() {
        $(channel.nameContainer).css('opacity',1);
      },
      function() {
        $(channel.nameContainer).css('opacity',defOpac);
      }
    );

    this.clearProgView(channel);
    return res;
  };

  this.formatChannelName = function(channel) {
    var cid = channel.id;
    if(channel.rg != '0') {
      cid = 'rg'+channel.rg;
    }
    var res = '';
    res += '<div class="channelnameinner">';
    res += '<img src="'+this.config.imagePath+'channel-logos/logo_'+cid+'.gif" width="68" height="38" alt="'+channel.name+'" />';
    res += '</div>';
    return res;
  };

  this.formatProgramme = function(prog,channel) {
    var res = '';
    var pId = prog.evid;;
    var detailsId = pId+'_details';
    var vertical = this.isVertical();
    var posAttr = 'left';
    var sizeAttr = 'width';
    if(vertical) {
      posAttr = 'top';
      sizeAttr = 'height';
    }

    var logoId = (channel.rg == '0') ? channel.id : ('rg'+channel.rg);
    res += '<div id="'+pId+'" class="prog" title="'+prog['title']+'" style="'+posAttr+': '+prog[posAttr]+'px; '+sizeAttr+': '+prog[sizeAttr]+'px;"><div class="proginner" onclick="'+this.instanceName+'.zoom(\''+prog.evid+'\')">';
    res += '<div class="chanlogo"><img src="'+this.config.imagePath+'channel-logos/logo_'+logoId+'.gif" width="68" height="38" alt="'+channel.name+'" /></div>';
    res += '<div class="infotop">';
    var titleClass = 'progtitle';
    if(!this.isVertical()) {
      titleClass += ' nowrap';
    }
    res += '<div class="'+titleClass+'">';
    if(prog.cat.match(/movie/i)) {
      res += '<img class="movieicon" src="'+this.config.imagePath+'movie_icon.gif" width="12" height="9" alt="Movie" style="margin-right: 2px;" />';
    }
    res += prog.title+'</div>';
    res += '<span class="progtime nowrap">'+prog.niceTime+'</span>';
    res += '</div>';
    res += '<div class="cat_icons">'+this.getCatIcons(prog.cat,0)+'</div>';
    res += '<div id="'+detailsId+'" class="details"></div>';
    res += '<div class="channelnumbers"></div>';
    res += '</div></div>';
    return res;
  };

  this.viewProgDetails = function(progId) {
    var i;
    var offset = 0;
    var p = this.progsById[progId];
    if(p) {
      this.goToProg(p);
    } else {
      var ob = this.tempProgsById[progId];
      if(ob) {
        this.goToProg(ob);
      } else {
        var thisOb = this;
        this.loadProgDetails(progId,function() {
          var ob = thisOb.tempProgsById[progId];
          if(ob) {
            thisOb.goToProg(ob);
          }
        },"temp");
      }
    }
  };

  this.goToProg = function(p) {
console.log('gotoprog');
    var thisOb = this;
    var channelId = p.channel_id;
    var chan = this.channelsById[channelId];
    if(chan) {
      var animOb = {};
      var vertical = this.isVertical();
      var posAttr = ['left','scrollLeft','scrollTop'];
      var configAttr = 'channelHeight';
      if(vertical) {
        posAttr = ['top','scrollTop','scrollLeft'];
        configAttr = 'channelWidth';
      }
      if((typeof p[posAttr[0]]) == 'number') {
        animOb[posAttr[1]] = p[posAttr[0]];
      }
      this.idToView = p.evid;
      var dayIndex = this.dayIndex;
      var func = function() {
        thisOb.render();
      };
      if(chan.od != '1') {
        if(p.start.match(/^(\d\d\d\d)(\d\d)(\d\d)/)) {
          var dt = RegExp.$1+'-'+RegExp.$2+'-'+RegExp.$3;
          if(this.daysByDate[dt]) {
            dayIndex = this.daysByDate[dt].index;
            func = function() {
              thisOb.setDay(dayIndex);
console.log('setDay');
            };
          }
        }
      }
    
      for(i = 0;i < this.channels.length;i++) {
        if(this.channels[i].id == channelId) {
          offset = i * this.config[configAttr];
          break;
        }
      }
      animOb[posAttr[2]] = offset;
      $("#"+this.config.elemGrid).animate(animOb,1000,func);
    }
  };

  this.closeInfoBox = function(immediate) {
    if(this.selectedProg && !immediate) {
      this.zoom(this.selectedProg);
    } else {
      $("#"+this.config.elemInfoBoxShad).hide();
      $("#"+this.config.elemInfoBox).hide();
      //this.selectedProg = '';
console.log('resetting selected prog - '+this.idToView+","+this.selectedProg);
    }
  };

  this.zoom = function(progId) {
    if(this.drag.status == 'dragged') {
      return;
    }
    var url = '';
    if(this.config.programmeLink == 'main') {
      url = '/tv/tv-guide/?id='+escape(progId);
    } else if(this.config.programmeLink == 'details') {
      url = '/tv/tv-guide/prog-details.php?id='+escape(progId);
    }
    if(url) {
      document.location = url;
      return;
    }
    var thisOb = this;
    var curIndex = this.selectedProg;
    var newIndex = progId;
    var g = $("#"+this.config.elemGrid);
    var gWidth = g.width();
    var gHeight = g.height();
    var gLeft = g.offset().left;
    var gTop = g.offset().top;
    var sLeft = g.get(0).scrollLeft;
    var sTop = g.get(0).scrollTop;
    var iBox = $("#"+thisOb.config.elemInfoBox);
    var iBoxShad = $("#"+thisOb.config.elemInfoBoxShad);
    var selOb = $("#"+newIndex);
    var obToShow = this.progsById[progId];
    if(!obToShow) {
      obToShow = this.tempProgsById[progId];
      if(obToShow) {
        var chan = this.channelsById[obToShow.channel_id];
        $("#tempprog").html(this.formatProgramme(obToShow,chan));
        selOb = $("#tempprog").find(".prog");
      } 
    }
    if(!selOb.length) {
      this.selectedProg = '';
      iBoxShad.hide();
      iBox.hide();
      return; 
    }
    var attr = {};
    attr.fontSize = '11px';
    attr.left = (selOb.offset().left - gLeft + sLeft)+'px';
    attr.top = (selOb.offset().top - gTop + sTop)+'px';
    attr.width = selOb.width();
    attr.height = selOb.height();
    iBoxShad.hide();
    if(curIndex == newIndex) {
      this.selectedProg = '';
      iBox.find(".details, .chanlogo, .channelnumbers").hide();
      iBox.animate(attr,500,function() {
        iBox.hide();
      });
    } else {
      this.selectedProg = newIndex;
//      var obToShow = this.progsById[progId];
      var html = '';
      if(obToShow) {
        html = selOb.html();
      }
      var attr2 = {};
      var iWidth = gWidth - 100;
      var iHeight = gHeight - 100;
      attr2.fontSize = '27px';
      attr2.left = sLeft + ((gWidth - iWidth) / 2);
      attr2.top = sTop + ((gHeight - iHeight) / 2);
      attr2.width = iWidth + 'px';
      attr2.height = iHeight + 'px';
      iBox.html(html).css(attr).show().animate(attr2,500,function() {
        iBoxShad.css(attr2).show();
        iBox.find('.chanlogo').show();
        if((typeof obToShow) == 'undefined' || (typeof obToShow.desc) == 'undefined') {
          thisOb.showProgDetails(null,newIndex);
          thisOb.loadProgDetails(progId);
        } else {
          thisOb.showProgDetails(obToShow,newIndex);
        }
      });
    }
    return;
  };

  this.updateVisible = function() {
    var vertical = this.isVertical();
    var configAttr = 'channelHeight';
    var posAttr = 'top';
    var viewStart = document.getElementById(this.config.elemGrid).scrollTop;
    var viewEnd = viewStart + $('#'+this.config.elemChannelNames).height();
    if(vertical) {
      configAttr = 'channelWidth';
      posAttr = 'left';
      viewStart = document.getElementById(this.config.elemGrid).scrollLeft;
      viewEnd = viewStart + $('#'+this.config.elemChannelNames).width();
    }

    var i;
    for(i = 0;i < this.channels.length;i++) {
      this.channels[i][posAttr] = i * this.config[configAttr];
      var chan = this.channels[i];
      var vis = false;
      if(((chan[posAttr] + this.config[configAttr]) >= viewStart) && (chan[posAttr] < viewEnd)) {
        vis = true;
      }
      this.channels[i].visible = vis;
    }
    this.render();
  };

  this.render = function() {
    var i;
    var channelsToLoad = [];
    var thisOb = this;
    for(i = 0;i < this.channels.length;i++) {
      var chan = this.channels[i];
      if(chan.dayIndex != this.dayIndex) {
        //chan = this.remChannelName(chan);
        chan = this.remChannel(chan);
      }
      if(chan.visible) {
        if(chan.status[this.dayIndex] === undefined) {
          channelsToLoad[channelsToLoad.length] = chan.id;
          chan.status[this.dayIndex] = 0;
        } else if(chan.status[this.dayIndex] == 1) {
          //chan = thisOb.addChannelName(chan);
          this.addChannelNameInBg(chan);
          //chan = thisOb.addChannel(chan);
          this.addChannelInBg(chan);
        }
      } else {
        //chan = this.remChannelName(chan);
        //chan = this.remChannel(chan);
      }
    }
    this.remUncachedChannels();
    if(channelsToLoad.length) {
      this.loadChannels(channelsToLoad);
    }
  };

  this.populateTimes = function() {
    var i,j;
    var size = this.config.minuteScale * 30;
    var sizeAttr = ['width','height','channelHeight'];
    if(this.isVertical()) {
      sizeAttr = ['height','width','channelWidth'];
    }
    var fullSize = (size * 24 * 2) + 1000;
    var html = '<div style="position: absolute; left: 0px; top: 0px; '+sizeAttr[0]+': '+fullSize+'px; '+sizeAttr[1]+':'+this.config[sizeAttr[2]]+'px;">';

    for(i = 0;i < 24;i++) {
      var hr = i;
      var suffix = 'am';
      if(hr >= 12) {
        suffix = 'pm';
        if(hr > 12) {
          hr -= 12;
        }
      } else if(hr === 0) {
        hr = '12';
      }
      for(j = 0;j < 2;j++) {
        var min = (j === 0) ? '00' : '30';
        var tm = hr+':'+min+suffix;
        html += '<div class="tm" style="'+sizeAttr[0]+': '+size+'px;"><div class="tminner">'+tm+'</div></div>';
      }
    }

    html += '</div>';
    $("#"+this.config.elemTimes).html(html);
  };

  this.populateOptions = function() {
    var i;
    var html = '';
    html += '<div style="padding: 4px;">';
    html += '<div style="float: left; width: 65px; overflow: hidden;">';

    html += '<div id="'+this.config.elemDays+'"></div>';

    html += '<div style="margin: 10px 0px;"><a href="#" onclick="'+this.instanceName+'.goToNow();return false;">Now</a></div>';

    if(!this.planner) {
      html += '<div class="change_settings tool">';
      html += '<a href="#" onclick="'+this.instanceName+'.loadSettings();return false;" title="Change your tv region or modify the channels to view" class="tooltip-js"><img id="settings_but" src="/tv/images/icons/gallery/change-settings.png" width="20" height="17" alt="" title="" style="margin-top: 7px;" /></a>';
      html += '</div>';
    }
    html += '<div class="md_spc"></div>';

    html += '</div>';

    html += '<div style="float: right; text-align: right;">';
    var index = 0;
    var exts = ['am','pm'];
    var exts2 = ['a','b'];
    for(i = 0;i < 2;i++) {
      html += '<div class="slots_'+exts[i]+'">';
      for(j = 0;j < 2;j++) {
        html += '<div class="slots_'+exts2[j]+'">';
        for(k = 0; k < 6;k++) {
          var hr = index;
          var suffix = 'am';
          if(hr >= 12) {
            suffix = 'pm';
            if(hr > 12) {
              hr -= 12;
            }
          } else if(hr === 0) {
            hr = 12;
          }
          html += '<div id="slot_'+index+'" class="slotnum""><a href="#" onclick="'+this.instanceName+'.setSlot('+index+');return false;">'+hr+' '+suffix+'</a></div>';
          index++;
        }
        html += '</div>';
      }
      html += '</div>';
    }
    html += '</div>';

    html += '</div>';

    //html += '<div class="md_clear"></div>';
    if(!this.planner) {
      html += this.getViewTypesHTML();
    }

    html += '<div class="md_clear"></div>';
    $("#"+this.config.elemOptions).html(html);
  };

  this.addToStructure = function(parentOb,layout) {
    var i;
    for(i = 0;i < layout.length;i++) {
      var ob = layout[i];
      var domOb = document.createElement('div');
      if(ob.id) {
        domOb.id = ob.id;
      }
      if(ob.className) {
        domOb.className = ob.className;
      }
      if(ob.content) {
        domOb.innerHTML = ob.content;
      }
      if(ob.height) {
        domOb.style.height = ob.height;
      }
      if(ob.title) {
        domOb.title = ob.title;
      }
      parentOb.appendChild(domOb);
      if(ob.children) {
        this.addToStructure(domOb,ob.children);
      }
    }
  };

  this.showVideo = function(ob) {
    var pl = '<playlist>';
    if(!ob.url) {
      ob.url = 'http://media.tiscali.co.uk/video/flv/'+ob.id+'.flv';
    }
    pl += '<item>';
    pl += '<title>'+ob.title+'</title>';
    pl += '<description>'+ob.description+'</description>';
    pl += '<filename>'+ob.url+'</filename>';
    pl += '</item>';
    pl += '</playlist>';
    var playlist = wimpy_getPlaylist();
    var i;
    var ok = true;
    for(i = 0;i < playlist.length;i++) {
      if(playlist[i].title == ob.title) {
        ok = false;
        break;
      }
    }
    if(ok) {
      wimpy_appendPlaylist(pl,true);
    }
  };

  this.handleScroll = function() {
    var ob = document.getElementById(this.config.elemGrid);
    var si = this.scrollInfo;
    si.lastLeft = si.left;
    si.lastTop = si.top;
    si.left = ob.scrollLeft;
    si.top = ob.scrollTop;
    si.lastScrollingH = si.scrollingH;
    si.lastScrollingV = si.scrollingV;
    si.scrollingH = (si.left != si.lastLeft);
    si.scrollingV = (si.top != si.lastTop);
    var dim = this.isVertical() ? 'H' : 'V';
    if(si['lastScrolling'+dim] && !si['scrolling'+dim]) {
      // Scrolling has stopped.
      this.updateVisible();
    }
  };

  this.reset = function() {
    this.channels = [];
    this.channelsById = {};
    this.progs = {};
    this.progsById = {};
    this.tempProgsById = {};
    this.castInfo = {};
    $("#"+this.config.elemGridInner).html('');
    $("#"+this.config.elemChannelNamesInner).html('');

    this.setUpChannels();
  };

  this.setUpLayout = function() {
    var cfg = this.config;
    var layout = [];
    var topStripH = this.isVertical() ? 39 : 32;
    var gridH = cfg.height - topStripH;
    var times = {id: cfg.elemTimes};
    var chanNames = {
      id: cfg.elemChannelNames,
      height: gridH+'px',
      children: [
        {id: cfg.elemChannelNamesInner}
      ]
    };
    var x = [times,chanNames];
    if(this.isVertical()) {
      times.height = chanNames.height;
      delete chanNames.height;
      x = [chanNames,times];
    }
    layout[layout.length] = {id: cfg.elemSettings,children: [{id: cfg.elemSettingsInner}]};
    if(this.config.showOptions == 1) {
      layout[layout.length] = {id: cfg.elemOptions,height: cfg.height+'px'};
    }
    layout[layout.length] = {id: 'topstrip',
       children: [
         {id: cfg.elemStatus,
           children: [
             {id: cfg.elemBusy,
              content: '<img src="'+this.config.imagePath+'busy.gif" width="18" height="18" />'},
             {id: cfg.elemClock}
           ]},
         x[0]
       ]};
    layout[layout.length] = x[1];
    layout[layout.length] = {id: cfg.elemGrid,
       height: gridH+'px',
       children: [
         {id: cfg.elemGridInner},
         {id: cfg.elemCurrentTimeLine, title: 'This line indicates the current time'},
         {id: cfg.elemInfoBoxShad},
         {id: cfg.elemInfoBox,title: 'Click to close this information box'},
         {id: 'tempprog'}
       ]
      };

    var container = document.getElementById(cfg.elemContainer); 
    //container.style.height = cfg.height+'px';
    this.addToStructure(container,layout);
    $("#"+cfg.elemCurrentTimeLine).css("opacity","0.2");
  };

  this.setUpChannels = function(progId) {
    this.busy(true);
    var thisOb = this;
    var url = this.config.scriptPath+'tv-setup-json.php?callback=?';
    if(this.planner) {
      url += '&planner=1';
    }
    if(typeof(progId) != 'undefined') {
      url += '&id='+escape(progId);
    }
    $.getJSON(url,function(data) {
      var i,j;
      var startChannelOffset = 0;

      thisOb.days = data.days;
      thisOb.services = data.services;

      var html = '';
      for(i = 0;i < thisOb.days.length;i++) {
        if(thisOb.days[i].date == data.start_date) {
          thisOb.dayIndex = i;
        }
        thisOb.daysByDate[thisOb.days[i].date] = thisOb.days[i];
        thisOb.progs[i] = {};
        html += '<div class="daylinks" id="daylink_'+i+'"><a href="#" onclick="'+thisOb.instanceName+'.setDay('+i+');return false;" title="'+thisOb.days[i].name+' '+thisOb.days[i].date+'">'+thisOb.days[i].rel_name+'</a></div>';
      }
      $("#"+thisOb.config.elemDays).html(html);

      thisOb.channels = data.channels;
      var vertical = thisOb.isVertical();
      var configAttr = 'channelHeight';
      if(vertical) {
        configAttr = 'channelWidth';
      }
      var chanH = thisOb.config[configAttr];
      for(i = 0;i < thisOb.channels.length;i++) {
        for(j = 0;j < thisOb.days.length;j++) {
          thisOb.channels[i].status = {};
          thisOb.channels[i].container = {};
          thisOb.channels[i].dayIndex = thisOb.dayIndex;
        }
        thisOb.channelsById[thisOb.channels[i].id] = thisOb.channels[i];
        if(thisOb.channels[i].id == data.start_channel_id) {
          startChannelOffset = i * thisOb.config[configAttr];
        }
      }

      // Set grid and channel list height.
      var gridSize = (thisOb.channels.length * thisOb.config[configAttr]);
      if(vertical) {
        var d = '<div style="position: absolute; left: '+(gridSize+20)+'px;">&nbsp;</div>';
        var d2 = '<div style="position: absolute; left: '+gridSize+'px;">&nbsp;</div>';
        $("#"+thisOb.config.elemChannelNamesInner).width(gridSize+200).html(d);
        $("#"+thisOb.config.elemGridInner).width(gridSize).html(d2);
      } else {
        $("#"+thisOb.config.elemChannelNamesInner).height(gridSize+20);
        $("#"+thisOb.config.elemGridInner).height(gridSize);
      }

      document.getElementById(thisOb.config.elemGrid).scrollTop = startChannelOffset;
      thisOb.days = data.days;

      if(progId) {
        thisOb.viewProgDetails(progId);
      } else {
        thisOb.setDay(thisOb.dayIndex);
        thisOb.setSlot(thisOb.getSlot());
      }

      // set up current time line.
      var tlSize = 6;
      var attr = {};
      if(thisOb.isVertical()) {
        attr = {left: '0px',
                width: (thisOb.channels.length * thisOb.config.channelWidth)+'px',
                height: tlSize+'px'};
      } else {
        attr = {top: '0px',
                width: tlSize+'px',
                height: (thisOb.channels.length * thisOb.config.channelHeight)+'px'}
      }
       $("#"+thisOb.config.elemCurrentTimeLine).css(attr);

      thisOb.busy(false);
    });

  };

  this.setUp = function(progId) {
    this.busy(true);
    var thisOb = this;
    this.setUpLayout();
    this.populateTimes();
    if(this.config.showOptions >= 1) {
      this.populateOptions();
    }

    var panes = [this.config.elemChannelNames,this.config.elemTimes];
    if(this.isVertical()) {
      panes = [this.config.elemTimes,this.config.elemChannelNames];
    }
    $("#"+this.config.elemGrid).scroll(function() {
      document.getElementById(panes[0]).scrollTop = document.getElementById(thisOb.config.elemGrid).scrollTop;
      document.getElementById(panes[1]).scrollLeft = document.getElementById(thisOb.config.elemGrid).scrollLeft;
    });

    // Scroll events
    setInterval(this.instanceName+'.handleScroll()',500);

    // Update clock.
    this.updateClock();
    setInterval(this.instanceName+'.updateClock()',10000);

    // Drag code start.
    $("#"+this.config.elemGrid).mousedown(function(e) {
      var d = thisOb.drag;
      d.status = '';
      var g = $("#"+thisOb.config.elemGrid);
      var pos = g.offset();
      var minx = pos.left + g.width() - 16;
      var miny = pos.top + g.height() - 16;
      if((e.pageX < minx) && (e.pageY < miny)) {
        d.mdx = e.pageX;
        d.mdy = e.pageY;
        d.scrx = g.get(0).scrollLeft;
        d.scry = g.get(0).scrollTop;
      }
    });

    $(document).mousemove(function(e) {
      var d = thisOb.drag;
      if(((typeof d.mdx) == 'number') && (thisOb.selectedProg === '')) {
        if(d.status != 'dragging') {
          document.onselectstart = function() {return false;};
          d.element = $("#"+thisOb.config.elemGrid).get(0);
          if(d.element) {
            d.element.style.MozUserSelect = 'none';
            //$(".prog").css('cursor','url(/tv/images/grabbing.cur), -moz-grabbing');
          }
          d.status = 'dragging';
        }
        var sx = d.scrx - e.pageX + d.mdx;
        var sy = d.scry - e.pageY + d.mdy;
        if(d.element) {
          d.element.scrollLeft = sx;
          d.element.scrollTop = sy;
        }
      }
    });

    $(document).mouseup(function(e) {
      var d = thisOb.drag;
      d.mdx = null;
      d.mdy = null;
      if(d.status == 'dragging') {
        document.onselectstart = null;
        e.preventDefault();
        e.stopPropagation();
        if(d.element) {
          d.element.style.MozUserSelect = null;
        }
        d.status = 'dragged';
        //$(".prog").css('cursor','pointer');
      }
    });
    // Drag code end.

    // Update cast info.
    setInterval(this.instanceName+'.displayCastInfo()',20000);

    this.setUpChannels(progId);
    this.busy(false);
  };

}
