// Code is ECMA standard JavaScript.  It contains date code to ensure that
// only days from today onwards are accessed by the calx quick links.
// Adapted to new dates: Ordered YYYY-MM-DD syntax for checkbox name field. 

var ie  = document.all;
var ns6 = document.getElementById&&!document.all;
var isMenu  = false;
var menuSelObj = null;
var overpopupmenu = false;
var clockMode = "24";
var clockTimes = "";
var amTimes = "";
var pmTimes = "";
var includeWeekends = false;

// Rewritten calx code to implement a slightly better approach than simply
// toggling the results. The new design builds a list of all checkboxes, and then
// processes that separately.

function setClockMode(mode) {
  clockMode = mode;
}

function setIncludeWeekends(value) {
  includeWeekends = value;
}

function setClockTimes(times) {
  clockTimes = times;
}

function setAMTimes(times) {
  amTimes = times;
}

function setPMTimes(times) {
  pmTimes = times;
}

function updateSelection(elements) {
  var elementCount = elements.length;
  var add = new Array();
  for (var i = 0; i < elementCount; i++) {
    var element = elements[i];
    if (! element.checked) {
      add.push(element);
    }
  }
  if (add.length > 0) {
    toggleElements(add);
  } else {
    toggleElements(elements);
  }
}

function toggleElements(elements) {
  var elementCount = elements.length;
  for (var i = 0; i < elementCount; i++) {
    var element = elements[i];
    element.checked = !element.checked;
    if (element.name.length > 16) {
      updateResponseCheck(element);
    }
  }
}

function findSlot(element) {
  var sections = element.split(" ", 2);
  var date = sections[0];
  var time = sections[1];
  var parsedTime = parseTime(time);
  var block = document.getElementById(date);
  var slot = block.firstChild;
  while(slot) {
    var slotTime = slot.currentTime;
    if (slotTime && timeEquals(parsedTime, slotTime)) {
      return slot;
    }
    slot = slot.nextSibling;
  }
  return null;
}

function addSlot(element) {
  var sections = element.split(" ", 2);
  var date = sections[0];
  var time = sections[1];
  var parsedTime = parseTime(time);
  var block = document.getElementById(date);
  if (parsedTime === null || block === null) {
    return;
  }
  var slot = block.firstChild;
  var before = null;
  while(slot) {
    var slotTime = slot.currentTime;
    if (! (slotTime && timeLessThan(slotTime, parsedTime))) {
      before = slot;
      break;
      //var slot = addNewSlot(block, "setslot", renderTime(time));
      //slot.currentTime = time;
      //ensureSlot(slot);
    }
    slot = slot.nextSibling;
  }

  if (before) {
    var slot = addNewSlot(block, "setslot", renderTime(parsedTime));
    slot.currentTime = parsedTime;
    before.parentNode.insertBefore(slot, before);
    var hidden = before.parentNode.nextSibling;
    var values;
    if (hidden.value.length === 0) {
      values = new Array();
    } else {
      values = hidden.value.split(",");
    }
    //alert("Adding element: "+element);
    //alert("Current times: "+hidden.value);
    values.push(rawTime(parsedTime));
    hidden.value = values.join(",");
    //alert("New times: "+hidden.value);
    return slot;
  } else {
    //alert("Failed to insert");
    return;
  }
}

function removeSlot(element) {
  var sections = element.split(" ", 2);
  var date = sections[0];
  var time = sections[1];
  
  var slotElement = findSlot(element);
  slotElement.parentNode.removeChild(slotElement);
  
  var block = document.getElementById(date);
  var hidden = block.nextSibling;
  var values = hidden.value.split(",");
  //alert("Removing element: "+element);
  //alert("Current times: "+hidden.value);
  for(var i = 0; i < values.length; i++) {
    if (values[i] == time) {
      values.splice(i, 1);
      i--;
    }
  }
  hidden.value = values.join(",");
  //alert("New times: "+hidden.value);
}

function updateSlots(elements, addOnly) {
  var elementCount = elements.length;
  var addSlots = new Array();
  var removeSlots = new Array();
  
  for (var i = 0; i < elementCount; i++) {
    var element = elements[i];
    //alert("Checking: "+element);
    if (! findSlot(element)) {
      //alert("Add test passed: "+element);
      addSlots.push(element);
    } else if (! addOnly) {
      //alert("Remove test passed: "+element);
      removeSlots.push(element);
    }
  }
  
  var lastSlot;
  if (addSlots.length > 0) {
    for(var i = 0; i < addSlots.length; i++) {
      var result = addSlot(addSlots[i]);
      if (result && ! lastSlot) {
        lastSlot = result;
      }
    }
  } else if (removeSlots.length > 0) {
    for(var i = 0; i < removeSlots.length; i++) {
      removeSlot(removeSlots[i]);
    }
  }
  if (lastSlot) {
    ensureSlot(lastSlot);
  }
}

function sel(mode, value, base, increment, slots) {
  var values = base.split("-");
  var isBook = true;
  var form = document.getElementById("frmBook");
  if (! form) {
    form = document.getElementById("frmRespond");
    isBook = false;
  }
  if (! form) {
    return;
  }
  //alert("Calling sel: mode="+mode+"; base="+base+"; slots="+slots+"; isBook="+isBook);
  var day = values[2] || 1;
  var date = new Date(values[0], parseInt(values[1], 10) - 1, day);
  var candidates = new Array();
  if (mode == 'DY' || mode == 'AM' || mode == 'PM' || mode == 'WK') {
    var today = new Date();
    today.setHours(0);
    today.setMinutes(0);
    today.setSeconds(0);
    today.setMilliseconds(0);
    if (increment == 7) {
      var day = date.getDay();
      var diff = value - day;
      if (diff < 0) { diff = diff + 7; }
      date.setDate(date.getDate() + diff);
    }
    var month = date.getMonth();
    var doneDays = 0;
    while (date.getMonth() == month) {
      //alert(slots+date+today);
      doneDays++;
      if ((increment > 7) || (mode == 'WK') || ((isBook) ? (date >= today) : true)) {
        var newYear = date.getFullYear();
        var newMonth = date.getMonth() + 1;
        var newDate = date.getDate();
        var newDay = date.getDay();
        if (mode == 'WK' && doneDays > 7) {
          break;
        }
        if (isBook && (! includeWeekends)) {
          if (increment == 1 && (newDay == 0 || newDay == 6)) {
            if (mode == 'WK' && newDay == 0) {
              break;
            }
            date.setDate(date.getDate() + increment);
            continue;
          }
        }
        var key = newYear+"-"+((newMonth < 10) ? "0" : "")+newMonth+"-"+((newDate < 10) ? "0" : "")+newDate;
        var block = document.getElementById("D"+key);
        if (block) {
          if (isBook) {
            var elements = clockTimes.split(",");
            var elementCount = elements.length;
            for (var i = 0; i < elementCount; i++) {
              var element = elements[i];
              candidates.push("D"+key+" "+element);
            }
          } else {
            var elements = block.getElementsByTagName("input");
            var elementCount = elements.length;
            for (var i = 0; i < elementCount; i++) {
              var element = elements[i];
              if (element.type == "checkbox") {
                candidates.push(element);
              }
            }
          }
        }
        if (mode == 'DY' || mode == 'WK') {
          if (form["D"+key+"DY"] !== undefined) { candidates.push(form["D"+key+"DY"]); }
          if (form["D"+key+"AM"] !== undefined) { candidates.push(form["D"+key+"AM"]); }
          if (form["D"+key+"PM"] !== undefined) { candidates.push(form["D"+key+"PM"]); }
        } else {
          if (form["D"+key+mode] !== undefined) { candidates.push(form["D"+key+mode]); }
        }
        if (mode == 'WK' && (newDay == 0 || doneDays > 7)) {
          break;
        }
      }
      date.setDate(date.getDate() + increment);
    }
  }
  //alert("Slots: "+slots+", "+isBook+" => "+(slots && ! isBook));
  if (slots && isBook) {
    //alert(candidates);
    updateSlots(candidates, false);
  } else {
    //alert("Candidate count: "+candidates.length);
    updateSelection(candidates);
  }
  return false;
}

// When we are adding stuff in the advanced mode (i.e., isBook is true) , we need to handle selection
// differently, adding slots rather than handing selections. 

function stopReturnKey(evt) { 
  var evt = (evt) ? evt : ((event) ? event : null); 
  var node = (evt.target) ? evt.target : ((evt.srcElement) ? evt.srcElement : null); 
  if ((evt.keyCode == 13) && (node.type=="text"))  {return false;} 
}

function updateResponseCheck(element) {
  var block = element.parentNode;
  while (block !== undefined) {
    if (block.className.match("slot")) {
      var selected = element.checked;
      block.className = (selected) ? "responseslot" : "freeslot";
      return;
    }
    block = block.parentNode;
  }
}

// Used in a link to fake the transition to the month view, so that we can
// preserve dates and times.

function persistForm(form) {
  var elements = form.elements;
  var elementCount = elements.length;
  var pending = [];
  
  for (var i = 0; i < elementCount; i++) {
    var element = elements[i];
    if (element !== undefined && element.type == 'hidden' && element.value === "") {
      pending.push(element);
    }
  }
  
  var count = pending.length;
  for (i = 0; i < count; i++) {
    var child = pending[i];
    child.parentNode.removeChild(child);
  }

  form.action = "calendar.php";
  form.method = "get";
  form.submit();
  return false;
}

function addMonths(i) {
  var form = document.getElementById("frmBook");
  form.months.value = (parseInt(form.months.value, 10) + i);
  return persistForm(form);
}

function changeZone() {
  var form = document.getElementById("frmRespond");
  var field = document.getElementById("fakeChangeZone");
  field.name = "changeZone";
  form.submit();
  field.name = "fakeChangeZone";
  return false;
}

// To simplify things when changing mode, we try to remove all items in the form which have 
// an empty value. This is not ideal, but can be helpful to reduce things to a level
// a URL will work to persist state.

function changeMode(mode) {
  var form = document.getElementById("frmBook");
  form.mode.value = mode;
  return persistForm(form);
}

// Shows the menu when required. It seems likely that there is a better solution
// than this. 
function mouseSelect(e) {
  if (isMenu) {
    if (overpopupmenu === false) {
      closeMenu();
      return true;
    }
    return true;
  }
  return true;
}

function findPos(obj) {
  var curleft = 0;
  var curtop = 0;
  if (obj.offsetParent) {
    curleft = obj.offsetLeft;
    curtop = obj.offsetTop;
    while (true) {
      obj = obj.offsetParent;
      if (obj === null) {
        break;
      }
      curleft += obj.offsetLeft;
      curtop += obj.offsetTop;
    }
  }
  return [curleft,curtop];
}

var blockRender = [];
blockRender.DY = "All day";
blockRender.AM = "Morning";
blockRender.PM = "Afternoon";

function renderOneTime(hours, minutes, addsuffix) {
  var suffix = "";
  if (clockMode == "12") {
    if (hours >= 12) {
      suffix = "pm";
      hours = hours - 12;
    } else {
      suffix = "am";
    }
    hours = (hours === 0) ? 12 : hours;
  }
  return "".concat(hours, ":", ((minutes < 10) ? "0" : ""), minutes, ((addsuffix) ? suffix : ""));
}

function renderTime(time) {
  if (time === null || time === undefined) {
    return "Undefined";
  } else if ((time.BLOCK !== undefined) && (time.BLOCK !== null)) {
    return blockRender[time.BLOCK];
  } else {
    return "".concat(
      renderOneTime(time.HSTART, time.MSTART, false),
      "-",
      renderOneTime(time.HEND, time.MEND, true));
  }
}

function rawTime(time) {
  if (time === null || time === undefined) {
    return "";
  } else if ((time.BLOCK !== undefined) && (time.BLOCK !== null))  {
    return time.BLOCK;
  } else {
    return "".concat(
      (time.HSTART < 10) ? "0" : "",
      time.HSTART, ":",
      (time.MSTART < 10) ? "0" : "",
      time.MSTART, "-",
      (time.HEND < 10) ? "0" : "",
      time.HEND, ":",
      (time.MEND < 10) ? "0" : "",
      time.MEND);
  }
}

var blockOrder = [];
blockOrder.DY = 1;
blockOrder.AM = 2;
blockOrder.PM = 3;

function timeLessThan(time1, time2) {
  if (time1.BLOCK) {
    if (time2.BLOCK) {
      return blockOrder[time1.BLOCK] < blockOrder[time2.BLOCK];
    } else {
      return true;
    }
  } else if (time2.BLOCK) {
    return false;
  } else if (time1.HSTART != time2.HSTART) {
    return (time1.HSTART < time2.HSTART);
  } else if (time1.MSTART != time2.MSTART) {
    return (time1.MSTART < time2.MSTART);
  } else if (time1.HEND != time2.HEND) {
    return (time1.HEND < time2.HEND);
  } else {
    return (time1.MEND < time2.MEND);
  }
}

function timeEquals(time1, time2) {
  if (time1.BLOCK) {
    if (time2.BLOCK) {
      return time1.BLOCK == time2.BLOCK;
    } else {
      return false;
    }
  } else if (time2.BLOCK) {
    return false;
  } else if (time1.HSTART != time2.HSTART) {
    return false;
  } else if (time1.MSTART != time2.MSTART) {
    return false;
  } else if (time1.HEND != time2.HEND) {
    return false;
  } else {
    return true;
  }
}

function updateMenuButton(menudiv, type, time) {
  var currentValue = menudiv[type + "-CURRENT"];
  if (currentValue !== null) {
    var oldValue = document.getElementById(currentValue);
    if (oldValue !== null) {
      oldValue.className = oldValue.className.replace("selected", "");
    }
  }
  if (time !== undefined) {
    var newIndex = type + "-" + time[type];
    var newValue = document.getElementById(newIndex);
    if (newValue !== null) {
      newValue.className = newValue.className + "selected";
      menudiv[type + "-CURRENT"] = newIndex;
    }
  }
}

function addMouseDownHandler(document, eventHandler) {
  if (document.addEventListener) {
    document.addEventListener('mousedown', eventHandler, false);
  } else if (document.attachEvent) {
    document.attachEvent('onmousedown', eventHandler);
  }
}

function addClickHandler(block, eventHandler) {
  var fn = function(e) { eventHandler((e.target ? e.target : e.srcElement), e); };
  if (block.addEventListener) {
    block.addEventListener('click', fn, false);
  } else if (block.attachEvent) {
    block.attachEvent('onclick', fn);
  }
}

function addNewSlot(block, className, text) {
  var newdiv = document.createElement("div");
  newdiv.className = className;
  addClickHandler(newdiv, mouseTimeSlot);
  newdiv.innerHTML = text;
  block.appendChild(newdiv);
  return newdiv;
}

function addNewEmptySlot(block) {
  return addNewSlot(block, "slot", "Add time...");
}

// Just because duplicates have been removed does not stop them being left in the
// data structures elsewhere. 

function removeDuplicates(block) {
  var slots = block.getElementsByTagName('div');
  var count = slots.length;
  if (count == 0) {
    return;
  }
  var previous = slots.item(0);
  for(var i = 1; i < count; i++) {
    var next = slots.item(i);
    var same = rawTime(previous.currentTime) == rawTime(next.currentTime);
    if (same) {
      next.parentNode.removeChild(next);
      // Required because we remove from the collection 
      i--; 
    } else {
      previous = next;
    }
  }
}

function removeAll(block) {
  var slots = block.getElementsByTagName('div');
  var count = slots.length;
  if (count == 0) {
    return;
  }
  for(var i = 1; i < count; i++) {
    var next = slots.item(0);
    next.parentNode.removeChild(next);
  }
}

function findAddTimeSlot(block) {
  var slots = block.getElementsByTagName('div');
  var count = slots.length;
  return slots.item(count - 1);
}

// Works through the parent item to check for the Add slot. If we cant
// find one, add a new one at the end. 
function ensureSlot(element) {
  var block = element.parentNode;
  var slots = block.getElementsByTagName('div');
  var found = false;
  var mux = slots.item(0).offsetHeight;
  var count = slots.length;
  var index = 0;
  var i;
  for(i = 0; i < slots.length; i++) {
    if (slots.item(i) == element) {
      index = i;
    }
    if (slots.item(i).innerHTML == 'Add time...') {
      found = true;
    }
  }
  if (! found) {
    addNewEmptySlot(block);
    count++;
  }
  var totalSlots = count - 1;
  if (totalSlots > 1) {
    var validAbove = (index > 0) ? timeLessThan(slots.item(index - 1).currentTime, element.currentTime) : true;
    var validBelow = (index < count - 2) ? timeLessThan(element.currentTime, slots.item(index + 1).currentTime) : true;
    while(true) {
      if (! validAbove) {
        block.removeChild(element);
        block.insertBefore(element, slots.item(index - 1));
        index = index - 1;
        validAbove = (index > 0) ? timeLessThan(slots.item(index - 1).currentTime, element.currentTime) : true;
        validBelow = true;
      } else if (! validBelow) {
        var next = slots.item(index + 1);
        block.removeChild(element);
        block.insertBefore(element, next.nextSibling);
        index = index + 1;
        validAbove = true;
        validBelow = (index < count - 2) ? timeLessThan(element.currentTime, slots.item(index + 1).currentTime) : true;
      } else {
        break;
      }
    }
  }
  count = count * mux;
  if (count < 50) { 
    count = 50;
  }
  // For IE?
  //block.style.height = count + 'px';
  updateBlock(block);
}

function updateBlock(block) {
  var slots = block.getElementsByTagName('div');
  var times = [];
  var slot;
  for(i = 0; i < slots.length; i++) {
    slot = slots.item(i);
    if (slot.innerHTML == 'Add time...') {
      break;
    } else {
      var formatted = rawTime(slot.currentTime);
      times.push(formatted);
    }
  }
  var formValue = block.nextSibling;
  formValue.value = times.join(",");
}

// This rather substantial set of code is designed to manage the handling of the
// hour and minute menu buttons. Essentially, we fold almost everything into a 
// small set of related functions. These are then managed as a single main function
// at the JavaScript level.

function changeMenuButtonSelection(menudiv, slot, time) {
  var currentTime = slot.currentTime;
  updateMenuButton(menudiv, 'HSTART', time);
  updateMenuButton(menudiv, 'MSTART', time);
  updateMenuButton(menudiv, 'HEND', time);
  updateMenuButton(menudiv, 'MEND', time);
  document.getElementById("radioDY").checked = (time !== undefined && time.BLOCK == "DY");
  document.getElementById("radioAM").checked = (time !== undefined && time.BLOCK == "AM");
  document.getElementById("radioPM").checked = (time !== undefined && time.BLOCK == "PM");
  var btnRemove = document.getElementById("btnRemove");
  var btnAdd = document.getElementById("btnAdd");
  var btnClear = document.getElementById("btnClear");
  btnRemove.disabled = (time === undefined);
  btnAdd.disabled = (time === undefined);
  btnClear.disabled = (time === undefined);
}

/* We need a better approach to handling data than this. I'm
   proposing that we adopt an associative array as a model for
   each slot, and that we maintain this as . */
   
function hrUpdate(value, part) {
  var menudiv = document.getElementById("menudiv");
  var slot = menudiv.currentSlot;
  var time;
  var diff;
  if (part != 'BLOCK' && slot.currentTime !== undefined && slot.currentTime.HSTART === undefined) {
    slot.currentTime = undefined;
  }
  if (part == 'BLOCK' && slot.currentTime !== undefined && slot.currentTime.HSTART !== undefined) {
    slot.currentTime = undefined;
  }
  if (slot.currentTime === undefined) {
    time = [];
    if (part == 'BLOCK') {
      time.BLOCK = value;
    } else if (part == 'HSTART') {
      time.HSTART = value;
      time.MSTART = 0;
      time.HEND = value + 1;
      // Handle wraparound for initial times
      if (time.HSTART >= 21 && time.HEND >= 24) {
        time.HEND = time.HEND - 24;
      }
      time.MEND = 0; 
    } else {
      time = undefined;
    }
  } else {
    time = slot.currentTime;
    if (part == 'BLOCK') { 
      time.BLOCK = value;
    } else if (part == 'HSTART') {
      diff = value - time.HSTART;
      time.HSTART = time.HSTART + diff;
      if (time.HEND < time.HSTART) {
        time.HEND = time.HEND + 24;
      }
      time.HEND = time.HEND + diff;
      if (time.HEND >= 24) {
        time.HEND = time.HEND - 24;
      }
    } else if (part == 'MSTART') {
      diff = value - time.MSTART;
      time.MSTART = time.MSTART + diff;
      // There is a nasty gotcha here (see ticket #75). Basically, the start can make the 
      // end greater than 60. It should then tick over. Also, the start can reduce the end
      // making it tick back. However, it can get really pathological, and I'm beginning to
      // think this needs a complete change in structure, maybe. 
      if (diff > 0) {
        time.MEND = time.MEND + diff;
        if (time.MEND >= 60) {
          time.MEND = time.MEND - 60;
          time.HEND = time.HEND + 1;
        }
      } else {
        time.MEND = time.MEND + diff;
        if (time.MEND < 0) {
          time.MEND = time.MEND + 60;
          time.HEND = time.HEND + 1;
        }
      }
      if (time.HEND >= 24) {
        time.HEND = time.HEND - 24;
      }
    } else if (part == 'HEND') {
      if (time.HEND < time.HSTART) {
        time.HEND = time.HEND + 24;
      }
      if ((time.MEND > time.MSTART) ? value >= time.HSTART : value > time.HSTART) { 
        time.HEND = value;
      } else if (time.HSTART >= 21 && value < 12) {
        time.HEND = value;
      }
      if (time.HSTART >= 21 && time.HEND >= 24) {
        time.HEND = time.HEND - 24;
      }
    } else if (part == 'MEND') {
      if (time.HSTART >= 21 && time.HEND < time.HSTART) {
        time.MEND = value;
      } else if ((time.HEND > time.HSTART) ? true : value >= time.MSTART) { 
        time.MEND = value;
      }
    }
  }
  if (time == undefined) {
    return;
  }
  changeMenuButtonSelection(menudiv, slot, time);
  slot.innerHTML = renderTime(time);
  slot.currentTime = time;
  slot.style.color = '#000000';
  ensureSlot(slot);
}

/* This command should really remove any duplicated slots, as well as
   closing the menu. No need to do this earlier, as that might cause
   problems. */

function closeMenu() {
  isMenu = false;
  overpopupmenu = false;
  var menudiv = document.getElementById('menudiv');
  menudiv.style.display = "none";
  var slot = menudiv.currentSlot;
  var block = slot.parentNode;
  removeDuplicates(block);
  menudiv.currentSlot = null;
  slot.style.backgroundColor = slot.oldBackground;
  slot.oldBackground = null;
}

/*function mouseTimeSlot(element, event) {
  element.oldBackground = element.style.backgroundColor;
  element.style.backgroundColor = "#DDDDFF";
  var mode = element.mode;
  var menu = document.getElementById("menudiv");
  itemSelectMenu(element, element.parentNode, event);
}*/

function btnAddDayTimes() {
  btnAddTimes(amTimes+","+pmTimes);
}

function btnAddAMTimes() {
  btnAddTimes(amTimes);
}

function btnAddPMTimes() {
  btnAddTimes(pmTimes);
}

function btnAddTimes(times) {
  var menudiv = document.getElementById('menudiv');
  var element = menudiv.currentSlot;
  var date = element.parentNode.id;
  var candidates = [];
  times = times.split(",");
  var count = times.length;
  for(var i = 0; i < count; i++) {
    candidates.push(date+" "+times[i]);
  }
  updateSlots(candidates, true);
  closeMenu();
}

function btnAddClick() {
  var menudiv = document.getElementById('menudiv');
  var element = menudiv.currentSlot;
  element.style.backgroundColor = element.oldBackground;
  
  var block = element.parentNode;
  removeDuplicates(block);
  ensureUpdate(menudiv, block);
}
  
function ensureUpdate(menudiv, block) {
  var slots = block.getElementsByTagName('div');
  var count = slots.length;
  var element;
  for(var i = 0; i < count; i++) {
    var element = slots.item(i);
    if (element.innerText == "Add time...") {
      break;
    }
  }
  
  if (element) {
    element.oldBackground = element.style.backgroundColor;
    element.style.backgroundColor = "#DDDDFF";
    menudiv.currentSlot = element;
    changeMenuButtonSelection(menudiv, element, element.currentTime);
  }
}

function btnRemoveClick() {
  var menudiv = document.getElementById('menudiv');
  if (menudiv.currentSlot) {
    var slot = menudiv.currentSlot;
    var parent = slot.parentNode;
    parent.removeChild(slot);
    updateBlock(parent);
    menudiv.style.display = "none";
  }
}

function btnClearClick() {
  var menudiv = document.getElementById('menudiv');
  var element = menudiv.currentSlot;
  element.style.backgroundColor = element.oldBackground;
  
  var block = element.parentNode;
  removeAll(block);
  ensureUpdate(menudiv, block);
}

function hrMouseOver(elem) {
  elem.style.color = '#FFFFFF';
}

function hrMouseOut(elem) {
  elem.style.color = '#000000';
}

function hrMouseClick(elem, part) {
  var value = elem.getAttribute("id");
  value = parseInt(value.substr(value.indexOf("-") + 1), 10);
  hrUpdate(value, part);
}

function menuDay(elem, value) {
  hrUpdate(value, 'BLOCK');
}

function itemSelectMenu(element, block, event) {
  var position = findPos(block);
  var menudiv = document.getElementById('menudiv');
  menudiv.style.display = "none";
  menudiv.style.left = (position[0] + block.offsetWidth - 2) + "px";
  menudiv.style.top = position[1] + "px";
  menudiv.style.display = "block";
  menudiv.currentSlot = element;
  changeMenuButtonSelection(menudiv, element, element.currentTime);
  isMenu = true;
  event.cancelBubble = true;
}

function mouseTimeSlot(element, event) {
  element.oldBackground = element.style.backgroundColor;
  element.style.backgroundColor = "#DDDDFF";
  var mode = element.mode;
  var menu = document.getElementById("menudiv");
  itemSelectMenu(element, element.parentNode, event);
}

function mouseBox(element, event) {
  mouseTimeSlot(element.lastChild, event);
}

function getTimezoneName() {
	var tmSummer = new Date(Date.UTC(2005, 6, 30, 0, 0, 0, 0));
	var so = -1 * tmSummer.getTimezoneOffset();
	var tmWinter = new Date(Date.UTC(2005, 12, 30, 0, 0, 0, 0));
	var wo = -1 * tmWinter.getTimezoneOffset();

	if (-660 == so && -660 == wo) { return 'Pacific/Midway'; }
	if (-600 == so && -600 == wo) { return 'Pacific/Tahiti'; }
	if (-570 == so && -570 == wo) { return 'Pacific/Marquesas'; }
	if (-540 == so && -600 == wo) { return 'America/Adak'; }
	if (-540 == so && -540 == wo) { return 'Pacific/Gambier'; }
	if (-480 == so && -540 == wo) { return 'US/Alaska'; }
	if (-480 == so && -480 == wo) { return 'Pacific/Pitcairn'; }
	if (-420 == so && -480 == wo) { return 'US/Pacific'; }
	if (-420 == so && -420 == wo) { return 'US/Arizona'; }
	if (-360 == so && -420 == wo) { return 'US/Mountain'; }
	if (-360 == so && -360 == wo) { return 'America/Guatemala'; }
	if (-360 == so && -300 == wo) { return 'Pacific/Eastern'; }
	if (-300 == so && -360 == wo) { return 'US/Central'; }
	if (-300 == so && -300 == wo) { return 'America/Bogota'; }
	if (-240 == so && -300 == wo) { return 'US/Eastern'; }
	if (-240 == so && -240 == wo) { return 'America/Caracas'; }
	if (-240 == so && -180 == wo) { return 'America/Santiago'; }
	if (-180 == so && -240 == wo) { return 'Canada/Atlantic'; }
	if (-180 == so && -180 == wo) { return 'America/Montevideo'; }
	if (-180 == so && -120 == wo) { return 'America/Sao_Paulo'; }
	if (-150 == so && -210 == wo) { return 'America/St_Johns'; }
	if (-120 == so && -180 == wo) { return 'America/Godthab'; }
	if (-120 == so && -120 == wo) { return 'America/Noronha'; }
	if (-60 == so && -60 == wo) { return 'Atlantic/Cape_Verde'; }
	if (0 === so && -60 == wo) { return 'Atlantic/Azores'; }
	if (0 === so && 0 === wo) { return 'Africa/Casablanca'; }
	if (60 == so && 0 === wo) { return 'Europe/London'; }
	if (60 == so && 60 == wo) { return 'Africa/Algiers'; }
	if (60 == so && 120 == wo) { return 'Africa/Windhoek'; }
	if (120 == so && 60 == wo) { return 'Europe/Amsterdam'; }
	if (120 == so && 120 == wo) { return 'Africa/Harare'; }
	if (180 == so && 120 == wo) { return 'Europe/Athens'; }
	if (180 == so && 180 == wo) { return 'Africa/Nairobi'; }
	if (240 == so && 180 == wo) { return 'Europe/Moscow'; }
	if (240 == so && 240 == wo) { return 'Asia/Dubai'; }
	if (270 == so && 210 == wo) { return 'Asia/Tehran'; }
	if (270 == so && 270 == wo) { return 'Asia/Kabul'; }
	if (300 == so && 240 == wo) { return 'Asia/Baku'; }
	if (300 == so && 300 == wo) { return 'Asia/Karachi'; }
	if (330 == so && 330 == wo) { return 'Asia/Calcutta'; }
	if (345 == so && 345 == wo) { return 'Asia/Katmandu'; }
	if (360 == so && 300 == wo) { return 'Asia/Yekaterinburg'; }
	if (360 == so && 360 == wo) { return 'Asia/Colombo'; }
	if (390 == so && 390 == wo) { return 'Asia/Rangoon'; }
	if (420 == so && 360 == wo) { return 'Asia/Almaty'; }
	if (420 == so && 420 == wo) { return 'Asia/Bangkok'; }
	if (480 == so && 420 == wo) { return 'Asia/Krasnoyarsk'; }
	if (480 == so && 480 == wo) { return 'Australia/Perth'; }
	if (540 == so && 480 == wo) { return 'Asia/Irkutsk'; }
	if (540 == so && 540 == wo) { return 'Asia/Tokyo'; }
	if (570 == so && 570 == wo) { return 'Australia/Darwin'; }
	if (570 == so && 630 == wo) { return 'Australia/Adelaide'; }
	if (600 == so && 540 == wo) { return 'Asia/Yakutsk'; }
	if (600 == so && 600 == wo) { return 'Australia/Brisbane'; }
	if (600 == so && 660 == wo) { return 'Australia/Sydney'; }
	if (630 == so && 660 == wo) { return 'Australia/Lord_Howe'; }
	if (660 == so && 600 == wo) { return 'Asia/Vladivostok'; }
	if (660 == so && 660 == wo) { return 'Pacific/Guadalcanal'; }
	if (690 == so && 690 == wo) { return 'Pacific/Norfolk'; }
	if (720 == so && 660 == wo) { return 'Asia/Magadan'; }
	if (720 == so && 720 == wo) { return 'Pacific/Fiji'; }
	if (720 == so && 780 == wo) { return 'Pacific/Auckland'; }
	if (765 == so && 825 == wo) { return 'Pacific/Chatham'; }
	if (780 == so && 780 == wo) { return 'Pacific/Enderbury'; }
	if (840 == so && 840 == wo) { return 'Pacific/Kiritimati'; }
	return 'US/Pacific';
}

function addLoadEvent(func) {
  var oldonload = window.onload;
  if (typeof window.onload != 'function') {
    window.onload = func;
  } else {
    window.onload = function() {
      if (oldonload) {
        oldonload();
      }
      func();
    };
  }
}

function addPageShowEvent(func) {
  var oldonpageshow = window.onpageshow;
  if (typeof window.onpageshow != 'function') {
    window.onpageshow = func;
  } else {
    window.onpageshow = function() {
      if (oldonpageshow) {
        oldonpageshow();
      }
      func();
    };
  }
}

function updateTimezoneControl() {
  var item = document.getElementById("selTimezone");
  if (item === undefined || item === null) {
    return;
  }
  var items = item.options;
  var itemCount = items.length;
  var timeZone = getTimezoneName();
  var changeItem = -1;
  for(var i = 0; i < itemCount; i++) {
    var option = items[i];
    if (option.defaultSelected) {
      changeItem = -1;
      break;
    } else if (option.value == timeZone) {
      changeItem = i;
    }
  }
  if (changeItem != -1) {
    item.selectedIndex = changeItem;
  }
}

/* Firefox has an additional complication; although it remembers the JavaScript state
   it does not remember the DOM state. We need to regenerate the DOM state from 
   *something* to be able to get back to the page we expected. A second problem is that
   Firefox actually caches too much! We get extra form fields, even though they are
   hidden, being set. These are separate problems but both need to be solved. 
   
   The solution seems to be best based on the hack that when returning to a page, all
   the form text fields remain filled in. This is actually the Firefox bug! We just need
   to use this to our advantage. 
   
   IE makes this harder still by restrictively following the DOM and not allowing us to
   easily identify all the hidden fields in the form, which are slots. We have to work
   differently, identifying all the hidden form fields and loading them when they match
   the pattern we expect (i.e., when the name begins "D").
   */
   
function loadPage() {
  var fields = document.getElementsByTagName('input');
  var fieldCount = fields.length;
  for(var i = 0; i < fieldCount; i++) {
    var field = fields[i];
    if (field.type === 'hidden' && field.name.charAt(0) === 'D') {
      var div = field.previousSibling;
      if (field.value !== "") {
        loadRawTimes(div, field.value);
      }
    }
  }
}

function loadPageResponses() {
  var checks = document.getElementsByTagName('input');
  var checkCount = checks.length;
  for(var i = 0; i < checkCount; i++) {
    var check = checks[i];
    if (check.type != "checkbox") {
      continue;
    }
    var block = check.parentNode;
    if (! check.checked) {
      continue;
    }
    while(block !== undefined) {
      if (block.className.match("slot")) {
        block.className = "responseslot";
        break;
      }
      block = block.parentNode;
    }
  }
}

function parseTime(string) {
  var bounds = string.split("-");
  var parsed = [];
  if (bounds.length == 1) {
    parsed.BLOCK = bounds[0];
    return parsed;
  } else if (bounds.length == 2) {
    var start = bounds[0].split(":");
    var end = bounds[1].split(":");
    parsed.HSTART = parseInt(start[0], 10);
    parsed.MSTART = parseInt(start[1], 10);
    parsed.HEND = parseInt(end[0], 10);
    parsed.MEND = parseInt(end[1], 10);
    return parsed;
  }
  return null;
}

function loadRawTimes(div, time) {
  var times = time.split(",");
  var timeCount = times.length;
  var first = true;
  for(var i = 0; i < timeCount; i++) { 
    var next = times[i];
    var parsed = parseTime(next);
    if (! parsed) {
      break;
    }
    if (first) {
      first = false;
      var child = div.firstChild;
      while(child) {
        div.removeChild(child);
        child = div.firstChild;
      }
    }
    var newdiv = addNewSlot(div, "setslot", renderTime(parsed));
    newdiv.currentTime = parsed;
    if (i == timeCount - 1) {
      ensureSlot(newdiv);
    }
  }
}
