var myrules = {
 'input.initial_focus' : function(element) {
  element.focus();
 }, 'span.hastooltip' : function(element) {
  var tip = element;
  while (tip && tip.className != "tooltip") {
   tip = tip.nextSibling;
  }

  // The second time we are here the tip won't be found because
  // the className is changed below.
  if (tip==null)
   return;
  tip.className = "tooltipjs";
  element.onmouseover = function() {
   clearTimeout(tip_show_timeout);
   active_tip = tip;
   tip_show_timeout = setTimeout(show_tip, 1000);
  }
  element.onmouseout = function() {
   clearTimeout(tip_show_timeout);
   tip.style.width = "auto";
   tip.style.display = "none";
  }
 }, 'input.gb' : function(element) {
  var o = get_over_and_out_name(element.src);
  element.onmouseover = function() {
   if (!element.is_disabled) {
    element.src = o.over;
   }
  }
  element.onmouseout = function() {
   if (!element.is_disabled) {
    element.src = o.out;
   }
  }
 }, 'input.gbd' : function(element) {
  // disabled "gb"
  var o = get_root_name(element.src);
  set_action_state(element, o, 1);
 }, 'img.gb' : function(element) {
  var o = get_over_and_out_name(element.src);
  element.onmouseover = function() {
   if (!element.is_disabled) {
    element.src = o.over;
   }
  }
  element.onmouseout = function() {
   if (!element.is_disabled) {
    element.src = o.out;
   }
  }
 }, 'img.tb' : function(element) {
  var o = get_over_and_out_name(element.src);
  element.onmouseover = function() {
   element.src = o.over;
  }
  element.onmouseout = function() {
   element.src = o.out;
  }
 }, 'input.ovr' : function(element) {
  element.onclick = function() {
   var p = element.parentNode;
   p = p.parentNode;
   var numrows = element.getAttribute("ovrlines");
   if (numrows == null || numrows == "") {
    numrows = 1;
   } else {
    numrows = parseInt(numrows);
   }
   for (; numrows; numrows--) {
    setGenericConfigLineEnabled(p, element.checked);
    p = p.nextSibling;
   }
  }

  // disable/enabled and set reset value (nodeclass value)
  var p = element.parentNode;
  p = p.parentNode;
  var numrows = element.getAttribute("ovrlines");
  if (numrows == null || numrows == "") {
   numrows = 1;
  } else {
   numrows = parseInt(numrows);
  }

  var checked = element.checked;
  for (; numrows; numrows--) {
   setGenericConfigLineEnabled(p, checked);
   p = p.nextSibling;
  }
 }, 'input.planovr' : function(element) {
  element.onclick = function() {
   var p = element.parentNode;
   p = p.parentNode;
   p = p.nextSibling;
   while (p) {
    setGenericConfigLineEnabled(p, element.checked);
    p = p.nextSibling;
   }
  }
  var p = element.parentNode;
  p = p.parentNode;
  p = p.nextSibling;
  while (p) {
   setGenericConfigLineEnabled(p, element.checked);
   p = p.nextSibling;
  }
 }, 'img.mm' : function(element) {
  element.style.width = '13px';
  element.style.height = '13px';
  if (element.getAttribute("init_collapsed")) {
   fold_group(element.getAttribute("group"));
   element.src = 'down_minmax.png';
  }
  element.onmouseover = function() {
   changeImgByMatch(element, 'down_minmax.png','down_minmax_over.png','up_minmax_over.png');
  }
  element.onmouseout = function() {
   changeImgByMatch(element, 'down_minmax_over.png','down_minmax.png','up_minmax.png');
  }
  element.onclick = function() {
   if (fold_group(element.getAttribute("group")))
    element.src = 'down_minmax_over.png';
   else
    element.src = 'up_minmax_over.png';
   return false;
  }
 }, 'tbody.rgc' : function(element) {
  // Change the image
  var pn = getFirstElement(element);
  toggleSiblings(pn);
 }, '#inputtable' : function(element) {
  freezeTable('inputtable');
 }, '#nodeinfo' : function(element) {
  freezeTable('nodeinfo');
 }, '#soundbutton' : function(element) {
  // If the sound button is available we should test for alert in
  // order to play sounds in views pages.
  test_alerts();
 }, '#testsound' : function(element) {
  // If the sound button is available we should test for alert in
  // order to play sounds in views pages.
  element.onclick = function() {
   test_sound();
   return false;
  }
 }, 'input.autosel' : function(element) {
  element.onchange = function() {
   var elm = element;
   while (elm.nodeName.toLowerCase() != "table")
    elm = elm.parentNode;

   var selects = elm.getElementsByTagName("select");
   if (selects.length != 1)
    return;
   selects[0].selectedIndex = 1;
  }
 }, 'input.autoenable' : function(element) {
  element.onchange = function() {
   var elm = element;
   while (elm.nodeName.toLowerCase() != "tr")
    elm = elm.parentNode;

   var inputs = elm.getElementsByTagName("input");
   for (var i = 0; i < inputs.length ; ++i) {
    if (inputs[i].type.toLowerCase() == "radio") {
     // This is the one to enable
     inputs[i].checked = true;
     return;
    }
   }
  }
 }, 'textarea.sotextarea' : function(element) {
  if (element.getAttribute("errpos")) {
   // Only works in IE.
   if (element.createTextRange) {
    var oTxtRange = element.createTextRange();
    oTxtRange.move("character", element.getAttribute("errpos"));
    oTxtRange.select();
   }
  }
  element.onclick = function() {
   element.style.color='black';
  }
 }, 'input[type="checkbox"]' : function(element) {
  // Remember all leaf checkboxes ie. the ones that is not of class
  // "depselect". This way we can display the dependencies correctly
  // when going back in browser history.
  if (element.className.indexOf("depselect") < 0) {
   // checked_boxes defined in behaviour_style.js
   if (checked_boxes == null) {
    // remember checked boxes
    checked_boxes = new Array();
   }
   if (element.checked) {
    // leaf checkboxes.
    checked_boxes.push(element);
   }
   return;
  }
  element.onclick = function() {
   set_checked(element, element.checked, false);
   return true;
  }
  element.update_state = function(n) { update_state(element,n); }

  // First add the connections to other checkboxes
  var connections = element.getAttribute("connections");
  element.num_checked = 0;
  element.num_total = 0;
  element.active_connections = new Array();
  if (connections) {
   var cons = connections.split(",");
   element.num_total = cons.length;

   var con = null;
   for (var i = 0; con = cons[i]; i++) {
    var elm = getById(con);
    element.active_connections.push(elm);
    elm.parent = element;
    if (!elm.onclick) {
     // This is handler for checkboxes that are
     // not depselect boxes. But they still need to
     // tell their parent to update.
     elm.onclick = function() {
      set_checked(this, this.checked, false);
      return true;
     }
    }
    if (elm.num_total) {
     element.num_total = element.num_total + elm.num_total;
     element.num_checked = element.num_checked + elm.num_checked;
    } else if (elm.checked) {
     elm.num_checked = elm.num_checked + 1;
    }
   }
  }
  if (element.parent) {
   element.parent.num_total = element.parent.num_total + element.num_total;
   element.parent.num_checked = element.parent.num_checked + element.num_checked;
  }

  // Now add the dependencies to the buttons
  element.active_dependencies = new Array();
  if (element.getAttribute("dependencies")) {
   element.active_dependencies = id2obj_array(element.getAttribute("dependencies").split(","));
  }
 }, 'a.descrlink' : function(element) {
  element.onclick = function() {
   // get the url from the tag (href).
   window.open(element.href, 'Windowname',
    'width=200,top=0,left=0,resizable,scrollbars,height=300'); 
   return false;
  }
 }, 'a.toollink' : function(element) {
  element.onclick = function() {
   // get the url from the tag (href).
   toolwin(element.href, yMousePos, xMousePos);
   return false;
  }
 }, 'img.la' : function(element) {
  element.title = element.alt + "\n " + "Click to see more.";
 }, 'img.lb' : function(element) {
  element.title = element.alt;
 }, 'img.tal' : function(element) {
  element.onmouseover = function() {
   element.src = "toolapplet_over.png";
  }
  element.onmouseout = function() {
   element.src = "toolapplet.png";
  }
 }, 'img.tals' : function(element) {
  element.onmouseover = function() {
   element.src = "toolapplet_small_over.png";
  }
  element.onmouseout = function() {
   element.src = "toolapplet_small.png";
  }
 }, 'img.date' : function(element) {
  element.onclick = function(e) {
   showCalendar(e, element);
  }
 }, 'img.time' : function(element) {
  element.onclick = function(e) {
   showTimeSel(e, element);
  }
 }, 'input.searchfield' : function(element) {
  element.style.color = "#aaaaaa";
  element.onfocus = function(e) {
   if (element.value == "Search")
    element.value = "";
   element.style.color = "#000000";
  };
  element.onblur = function(e) {
   if (element.value == "")
    element.value = "Search";
    element.style.color = "#aaaaaa";
   };
 },
  // On the "Move Items" of graphical views the elements are as follows
  // - graphical-view-image: the IMG element
  //
  // - graphical-view-entry-1, graphical-view-entry-<i>,.. : the <div> view
  //    entries placed on the image (via CSS style "position: absolute)
  //
  // - graphical-view-entry-1-x, graphical-view-entry-2-x, ..
  //   graphical-view-entry-2-y, graphical-view-entry-2-y, ..:
 'div.view' : function(element) {
  var image = getById("graphical-view-image"); // The PNG Image
  check_view_entry_position(image, element);
 }, 'div.view_move' : function(element) {
  var image = getById("graphical-view-image"); // The PNG Image
  check_view_entry_position(image, element);
  element.is_moving = false;
  element.style.cursor = "move";
  element.onmousedown = function(e) {
   var info = mouseInfo(e);
   element.is_moving = true;
   element.offsetX = info.mousePosX - info.elementPosX;
   element.offsetY = info.mousePosY - info.elementPosY;
   extraMouseMove.push(function(e) {
    if (element.is_moving) {
     var info = mouseInfo(e);
     element.style.top = info.mousePosY - element.offsetY;
     element.style.left = info.mousePosX - element.offsetX;
     var id_ = element.id;
     if (getById(id_+"-x") !==null) {
      var x      = element.style.left.replace(/px$/,"");
      var y      = element.style.top.replace( /px$/,"");
      var image  = getById("graphical-view-image"); // The PNG Image
      var ix     = image_x(image);
      var iy     = image_y(image);
      var right  = ix + image.width;
      var bottom = iy + image.height - 12; // 12 is slightly less than
                                           // the dot height
      if (x > right) {
        x = right;
        element.style.left = x + "px";
      }
      if (y > bottom) {
        y = bottom;
        element.style.top = y + "px";
      }
      if (x < ix) {
        x = ix + 1;
        element.style.left = x + "px";
      }
      if (y < iy) {
        y = iy + 1;
        element.style.top = y + "px";
      }
      // Mirror the top/left values into the hidden form entries (x/y)
      // The stored values are relative to the (top,left) of the image
      x = x - ix;
      y = y - iy;
      getById(id_+"-x").value = x < 0 ? 0 : x;
      getById(id_+"-y").value = y < 0 ? 0 : y;
     }
    }
    return false;
   });
   return false;
  };
  element.onmousemove = function(e) {
   if (element.is_moving) {
    var info = mouseInfo(e);
    element.style.top = info.mousePosY - element.offsetY;
    element.style.left = info.mousePosX - element.offsetX;
   }
   return false;
  };
  element.onmouseup = function(e) {
   var image = getById("graphical-view-image"); // The PNG Image
   check_view_entry_position(image, element);
   element.is_moving = false;
   extraMouseMove.pop();
   return false;
  };
 }
};

var shift_down = false;
var active_graph = null;
document.onkeydown = function(e) {
 if (!e) e = window.event;
 var code = e.keyCode ? e.keyCode : 0;
 shift_down = code == 16;
 if (active_graph && code == 27) {
  // We are showing a box on the graph, and the user presses ESC.
  destroyZoomBoxes(active_graph);
  active_graph = null;
 }
 return true;
}

document.onkeyup = function(e) {
  if (!e) e = window.event;
  var code = e.keyCode ? e.keyCode : 0;
  shift_down = !(code == 16);
  return true;
}

function mouseInfo(e)
{
 if (!e) e = window.event;
 var posx = 0;
 var posy = 0;
 if (e.pageX || e.pageY) {
  posx = e.pageX;
  posy = e.pageY;
 } else if (e.clientX || e.clientY) {
  posx = e.clientX + document.body.scrollLeft;
  posy = e.clientY + document.body.scrollTop;
 }
 // posx and posy contain the mouse position relative to the document
 // Do something with this information
 var targ = null;
 if (typeof e.override_target != "undefined") targ = e.override_target;
 else if (e.target) targ = e.target; 
 else if (e.srcElement) targ = e.srcElement;

 if (targ.nodeType == 3) // defeat Safari bug
  targ = targ.parentNode;

 elmposx = findPosX(targ);
 elmposy = findPosY(targ);
 var ret = new Object();
 ret.target = targ;
 ret.mousePosX = posx;
 ret.mousePosY = posy;
 ret.elementPosX = elmposx;
 ret.elementPosY = elmposy;
 ret.elementWidth = targ.width;
 ret.elementHeight = targ.height;

 // Detect which mouse button have been pressed.
 ret.mouse_button = 0;
 if (e.button) {
  // Newer browsers support the e.button property of the
  // event. However according to W3C, e.button should contain the
  // following values:
  //
  // 0 - Left button
  // 1 - Middle button
  // 2 - Right button
  //
  // Of couse MS would not want to be compatible, so for IE, the
  // values are:
  //
  // 1 - Left button
  // 2 - Right button
  // 4 - Middle button
  //
  // Fortunately Konqueror and Firefox seems to do the same... At
  // least on Linux.
  //
  ret.mouse_button = e.button;
 } else if (e.which) {
  // Old Netscape uses which to determine the button. Not really tested.
  if (e.which == 1) {
   ret.mouse_button = 1;
  } else if (e.which == 2) {
   ret.mouse_button = 2;
  } else if (e.which == 3) {
   ret.mouse_button = 4;
  }
 }
 return ret;
}

function cancelEventPropagation(e)
{
 if (!e) {
  // This is IE.
  window.event.cancelBubble = true;
 } else {
  e.stopPropagation();
 }
}

var zoom_down_works = false;
function setZoomInfo(obj, root)
{
 obj.zoomInfo = root;
 obj.title = "Select area to zoom";
 obj.style.cursor = "crosshair";
 obj.onmousedown = graphMouseDown;
 obj.onmouseup = graphMouseUp;
 obj.onmousemove = graphMouseMove;
 obj.onclick = graphMouseClick;

 // We also need to set the events on the sibling TDs.
 var parent_td = obj.parentNode;
 var all_tds = parent_td.parentNode.getElementsByTagName("td");
 for (var i = 0; i < all_tds.length; i++) {
  if (all_tds[i] == parent_td)
   continue;
  var cur_td = all_tds[i];
  cur_td.onmousedown = function(e) { return graphMouseDown(e, obj); };
  cur_td.onmouseup = graphMouseUp;
  cur_td.onmousemove = graphMouseMove;
  cur_td.onclick = function(e) { return graphMouseClick(e, obj); };

  for (var j = 0; j < cur_td.childNodes.length; ++j) {
   // Set the children event handlers to cancel propagation of
   // the event
   var child = cur_td.childNodes[j];
   if (child.nodeType == 3)
    // IE does not allow us to set events on #text entries
    continue;
   child.onmousedown = cancelEventPropagation;
   child.onmouseup = graphOtherMouseUp;
   child.onmousemove = cancelEventPropagation;
   child.onclick = cancelEventPropagation;
  }
 }
}

function fetchZoomInfo(obj)
{
 sendRequest(obj.src + "&xmlstore=1", function(req) {
  var resp_doc = req.responseXML;
  var graphs = resp_doc.getElementsByTagName("CartesianGraph");
  if (graphs.length == 1) {
   if (graphs[0].getAttribute("canZoom") == "true") {
    setZoomInfo(obj, graphs[0]);
    obj.is_tabular = false;
   }
  }
  graphs = resp_doc.getElementsByTagName("TabularGraph");
  if (graphs.length == 1) {
   if (graphs[0].getAttribute("canZoom") == "true") {
    setZoomInfo(obj, graphs[0]);
    obj.is_tabular = true;
   }
  }   
 });
}

function findCartesianZoomTime(active_graph, x, y)
{
 var x_width = active_graph.max_x - active_graph.min_x;
 var time_span = active_graph.time_end - active_graph.time_start;
 // Clamp X
 if (x < active_graph.min_x)
  x = active_graph.min_x;
 if (x > active_graph.max_x)
  x = active_graph.max_x;

 var x_offset = x - active_graph.min_x;
 var res = active_graph.time_start
  + x_offset * time_span / x_width;
 return res;
}

function findTabularZoomTime(active_graph, x, y)
{
 // We know the start time of the entire graph, and the individual
 // spans of the boxes, so figure out which box the current point is
 // located in, while adding to the start time
 var res = active_graph.time_start;
 var i = 0;
 while (i < active_graph.boxes.length && x >= active_graph.boxes[i].real_right) {
  res += active_graph.boxes[i].timespan;
  i++;
 }

 if (i < active_graph.boxes.length) {
  // So now we know what the time is at the point where the selected
  // box starts.
  var box = active_graph.boxes[i];
  var height = y - active_graph.min_y;
  if (height < 0)
   height = 0;

  var box_height = box.max_y - active_graph.min_y;
  if (height > box_height)
   height = box_height;

  var active_column_span = height * box.timespan / box_height;
  res += active_column_span;
 }
 return res;
}

function createTabularZoomBoxes(active_graph)
{
 // Extract the start/stop times, and the x-start and x-stop
 // positions.
 var zoom_info = active_graph.target.zoomInfo;
 var image_parent = active_graph.target.parentNode;

 active_graph.findTime = findTabularZoomTime;
 active_graph.min_y = active_graph.info.elementPosY
  + parseInt(zoom_info.getAttribute("yStart"));
 active_graph.max_y = active_graph.min_y + parseInt(zoom_info.getAttribute("yHeight"));
 active_graph.time_start = parseInt(zoom_info.getAttribute("timeStart"));
 active_graph.time_end = parseInt(zoom_info.getAttribute("timeEnd"));

 // Create zoom boxes.
 active_graph.boxes = new Array();
 var parts = zoom_info.getElementsByTagName("Part");
 for (i = 0; i < parts.length; ++i) {
  // For each of the parts, we create a new div-tag.
  var new_div = document.createElement("div");
  // It should be of the zoom class
  new_div.className = "zoom";
  var width = parseInt(parts[i].getAttribute("xWidth"));

  new_div.real_left = active_graph.info.elementPosX
   + parseInt(parts[i].getAttribute("xStart"));
  new_div.real_right = new_div.real_left + width;
  new_div.style.left = new_div.real_left + "px";
  new_div.timespan = parseInt(parts[i].getAttribute("span"));
  new_div.max_y = active_graph.min_y
   + parseInt(parts[i].getAttribute("usedLength"));

  if (is_ie) {
   new_div.style.width = width + "px";
  } else {
   new_div.style.width = width - 1 + "px";
  }
  setTabZoomBox(new_div, active_graph.info.mousePosY,
  active_graph.info.mousePosY, active_graph);

  if (active_graph.info.mousePosX >= new_div.real_left
   && active_graph.info.mousePosX < new_div.real_right) {
   active_graph.start_index = i;
   showElement(new_div);
   new_div.active = true;
  } else {
   new_div.active = false;
  }

  // IE7 now supports transparent PNG's!
  if (!is_ie || navigator.userAgent.indexOf("MSIE 7") >= 0) {
   new_div.style.backgroundImage = "url(\"graph_select_bg.png\")";
  }
  // Add the onmouseup and onmousemove handlers
  new_div.onmouseup = graphMouseUp;
  new_div.onmousemove = graphMouseMove;
  new_div.onclick = graphMouseClick;
  // Add to the list of boxes
  active_graph.boxes.push(new_div);
  // Insert it into the images parent object.
  image_parent.appendChild(new_div);
 }
}

function createZoomBoxes(active_graph)
{
 // Extract the start/stop times, and the x-start and x-stop
 // positions.
 var zoom_info = active_graph.target.zoomInfo;
 var image_parent = active_graph.target.parentNode;
 active_graph.findTime = findCartesianZoomTime;
 active_graph.min_x = active_graph.info.elementPosX
  + parseInt(zoom_info.getAttribute("xStart"));
 active_graph.max_x = active_graph.min_x + parseInt(zoom_info.getAttribute("xWidth"));
 active_graph.time_start = parseInt(zoom_info.getAttribute("timeStart"));
 active_graph.time_end = parseInt(zoom_info.getAttribute("timeEnd"));

 // Create zoom boxes.
 active_graph.boxes = new Array();
 var parts = zoom_info.getElementsByTagName("Part");
 for (i = 0; i < parts.length; ++i) {
  // For each of the parts, we create a new div-tag.
  var new_div = document.createElement("div");
  // It should be of the zoom class
  new_div.className = "zoom";
  new_div.active = true;
  new_div.style.width = "1px";
  new_div.style.left = active_graph.min_x;
  new_div.style.top = active_graph.info.elementPosY
   + parseInt(parts[i].getAttribute("yStart")) + "px";
  // The border is aparently not part of the height in
  // FireFox/Konq/Opera, but it is in IE.
  if (is_ie) {
   new_div.style.height = parts[i].getAttribute("yHeight") + "px";
  } else {
   new_div.style.height = parseInt(parts[i].getAttribute("yHeight")) - 2 + "px";
  }
  // IE7 now supports transparent PNG's!
  if (!is_ie || navigator.userAgent.indexOf("MSIE 7") >= 0) {
   new_div.style.backgroundImage = "url(\"graph_select_bg.png\")";
  }
  // Add the onmouseup and onmousemove handlers
  new_div.onmouseup = graphMouseUp;
  new_div.onmousemove = graphMouseMove;
  new_div.onclick = graphMouseClick;
  // Add to the list of boxes
  active_graph.boxes.push(new_div);
  // Insert it into the images parent object.
  image_parent.appendChild(new_div);
 }
}

function setTabZoomBox(box, point1, point2, active_graph)
{
 var top;
 var bottom;
 if (point1 < point2) {
  top = point1;
  bottom = point2;
 } else {
  top = point2;
  bottom = point1;
 }

 if (top < active_graph.min_y)
  top = active_graph.min_y;
 if (top > box.max_y)
  top = box.max_y;
 if (bottom > box.max_y)
  bottom = box.max_y;

 var height = bottom - top;
 if (height <= 0) {
  if (bottom == box.max_y) {
   // Move the top up.
   if (!is_ie)
   top = top - 2;
  }
  height = 0;
 }
 if (!is_ie)
  height = height < 2 ? 0 : height - 2;

 box.style.top = top + "px";
 box.style.height = height + "px";
}

function showZoomElement(div)
{
 showElement(div);
 div.active = true;
}

function hideZoomElement(div)
{
 hideElement(div);
 div.active = false;
}

function updateTabularZoomBoxes(active_graph, info)
{
 var start_div = active_graph.boxes[active_graph.start_index];
 // First check if the mouse is within the start column.
 if (info.mousePosX >= start_div.real_left
  && info.mousePosX < start_div.real_right) {
  // Well it is. This means that we should hide all the other columns.
  for (i = 0; i < active_graph.boxes.length; i++) {
   if (i != active_graph.start_index)
    hideZoomElement(active_graph.boxes[i]);
  }
  setTabZoomBox(start_div, info.mousePosY, active_graph.startTop, active_graph);
 } else  {
  var while_pred;
  var i_pred;
  var i_inc;
  var i_start;
  var point1;

  if (info.mousePosX < start_div.real_left) {
   // The mouse is to the left of the starting div.
   while_pred = function(i) {
     return i >= 0 && info.mousePosX < active_graph.boxes[i].real_left;
   };
   i_pred = function(i) { return i >= 0; };
   i_inc = -1;
   i_start = active_graph.boxes.length - 1;
   point1 = active_graph.max_y;

   setTabZoomBox(start_div, active_graph.startTop, active_graph.min_y, active_graph);
  } else {
   while_pred = function(i) {
    return i < active_graph.boxes.length
     && info.mousePosX > active_graph.boxes[i].real_right;
   };
   i_pred = function(i) { return i < active_graph.boxes.length; };
   i_inc = 1;
   point1 = active_graph.min_y;
   i_start = 0;

   setTabZoomBox(start_div, active_graph.startTop, active_graph.max_y, active_graph);
  }
  // Start by disabling all boxes up to the start_div
  var i = i_start;
  while (i_pred(i) && i != active_graph.start_index) {
   hideZoomElement(active_graph.boxes[i]);
   i += i_inc;
  }
  // Skip the start_div
  i += i_inc;
  // Find all the fully selected boxes.
  while (while_pred(i)) {
   // Well this box is fully selected.
   setTabZoomBox(active_graph.boxes[i], active_graph.min_y,
    active_graph.max_y, active_graph);
   showZoomElement(active_graph.boxes[i]);
   i = i + i_inc;
  }

  // The current box is not fully selected. Since we are moving
  // left, the bottom of this box is active_graph.max_y
  if (i_pred(i)) {
   setTabZoomBox(active_graph.boxes[i], point1, info.mousePosY, active_graph);
   showZoomElement(active_graph.boxes[i]);
  }
  i = i + i_inc;

  // Hide the rest of the boxes
  while (i_pred(i)) {
   hideZoomElement(active_graph.boxes[i]);
   i = i + i_inc;
  }
 }
}


function updateZoomBoxes(active_graph, info)
{
 // From the startLeft and the current x-pos of the mouse,
 // find the left and right positions of the graph
 var start_pos = active_graph.startLeft;
 var cur_pos = info.mousePosX;
 var left_pos, right_pos;
 if (start_pos < cur_pos) {
  left_pos = start_pos;
  right_pos = cur_pos;
 } else {
  left_pos = cur_pos;
  right_pos = start_pos;
 }

 // Clamp the left and right pos to the graph's settings
 if (left_pos < active_graph.min_x)
  left_pos = active_graph.min_x;
 if (right_pos > active_graph.max_x)
  right_pos = active_graph.max_x;

 // Now set the left position and the width of the zoom-divs.
 var width = right_pos - left_pos;
 if (width <= 0)
  width = 1;

 for (i = 0; i < active_graph.boxes.length; i++) {
  var box = active_graph.boxes[i];
  box.style.left = left_pos + "px";
  if (is_ie) {
   box.style.width = width + "px";
  } else {
   box.style.width = (width < 2 ? 0 : width - 2) + "px";
  }
  showElement(box);
 }
}

function destroyZoomBoxes(active_graph)
{
 var image_parent = active_graph.target.parentNode;
 for (i = 0; i < active_graph.boxes.length; ++i) {
  // Remove the element.
  image_parent.removeChild(active_graph.boxes[i]);
 }
}

function redirectZoom(point1, point2)
{
 var start, end;
 if (point1 < point2) {
  start = point1;
  end = point2;
 } else {
  start = point2;
  end = point1;
 }

 if (end - start < 3600) {
  // Since the span is less than an hour, we the graph on the center
  // of the selected area instead
  var middle = start + (end - start) / 2;
  start = middle - 3600 / 2;
  end = middle + 3600 / 2;
 }

 // Now we know the start and end intervals. Create the new URL.
 var path = getById("history_path").innerHTML.replace(/&amp;/g, "&");
 var arguments = path.split('?');
 var prefix = arguments[0];
 arguments = arguments[1].split('&');
 var query = "";
 for (i = 0; i < arguments.length; ++i) {
  var tmp = arguments[i].split('=');
  if (tmp[0] != "zstart" && tmp[0] != "zend"
   && tmp[0] != "custom_pos" && tmp[0] != "period"
   && tmp[0] != "periods" && tmp[0] != "starttime") {
   query += (query ? "&" : "") + arguments[i];
  }
 }
 query += "&period=custom";
 query += "&zstart=" + Math.round(start);
 query += "&zend=" + Math.round(end);
 document.location = prefix + "?" + query;
}

function doZoom()
{
 // First remove the zoom boxes.
 destroyZoomBoxes(active_graph);
 // Find the time interval.
 var point1 = active_graph.findTime(active_graph, active_graph.startLeft,
  active_graph.startTop);
 var point2 = active_graph.findTime(active_graph, active_graph.currentLeft,
  active_graph.currentTop);
 // Redirect.
 redirectZoom(point1, point2);
 active_graph = null;
}

function abs(a) {
 if (a < 0)
  return -a;
 return a;
}

function graphMouseUp(e)
{
 if (!e) e = window.event;
 var info = mouseInfo(e);
 if (info.mouse_button != 1)
  return;
 if (active_graph && active_graph.done) {
  // Determine if we should actually zoom.
  var x_diff = abs(active_graph.startLeft - active_graph.currentLeft);
  var y_diff = abs(active_graph.startTop - active_graph.currentTop);
  var time_diff = abs(active_graph.start_time - new Date());
  if (x_diff < 3 && y_diff < 3 && time_diff < 300)
   return;
  // Blink the selection boxes
  active_graph.is_selected = true;
  blink(active_graph.boxes, 100, 4, doZoom);
  return;
 }
}

function graphOtherMouseUp(e)
{
 if (active_graph) {
  // We have an active graph, so we should just perform the zoom
  return graphMouseUp(e);
 }
 // We don't have an active graph. Cancel the events.
 return cancelEventPropagation(e);
}

function graphMouseDown(e, real_target)
{
 zoom_down_works = true;
 return realGraphMouseDown(e, real_target);
}

function realGraphMouseDown(e, real_target)
{
 if (!e) e = window.event;
 if (active_graph)
  return;

 if (typeof real_target != "undefined") {
  e.override_target = real_target;
 }

 // Ensure that we have the zoom info loaded.
 var info = mouseInfo(e);
 if (info.mouse_button != 1)
  return;

 if (info.target.zoomInfo == null) {
  return;
 }

 active_graph = new Object();
 active_graph.done = false;
 active_graph.is_selected = false;
 active_graph.info = info;
 active_graph.target = info.target;
 active_graph.start_time = new Date();

 if (active_graph.target.is_tabular) {
  createTabularZoomBoxes(active_graph);
 } else {
  createZoomBoxes(active_graph);
 }

 if (info.mousePosX < active_graph.min_x)
  info.mousePosX = active_graph.min_x;
 if (info.mousePosX > active_graph.max_x)
  info.mousePosX = active_graph.max_x;

 active_graph.currentLeft = active_graph.startLeft = info.mousePosX;
 active_graph.currentTop = active_graph.startTop = info.mousePosY;
 active_graph.done = true;

 // Update the position of the zoom boxes, so that they are
 // positioned correctly, from the start, and not from the first
 // mousemove call.
 if (!active_graph.target.is_tabular)
  updateZoomBoxes(active_graph, info);
 else
  updateTabularZoomBoxes(active_graph, info);

 return false;
}

function graphMouseClick(e, real_target)
{
 if (zoom_down_works) {
  return true;
 }
 if (!active_graph) {
  return realGraphMouseDown(e, real_target);
 } else {
  return graphMouseUp(e, real_target);
 }
}

function graphMouseMove(e)
{
 if (!e) e = window.event;
 if (typeof active_graph == "undefined")
  return false;
 if (!active_graph)
  return false;
 if (active_graph.is_selected)
  return false;

 var info = mouseInfo(e);
 active_graph.currentTop = info.mousePosY;
 active_graph.currentLeft = info.mousePosX;
 if (!active_graph.target.is_tabular)
  updateZoomBoxes(active_graph, info);
 else
  updateTabularZoomBoxes(active_graph, info);

 return false;
}

function blink(objs, delay, times, final_func)
{
 if (!times) {
  final_func();
  return;
 }
 times = times - 1;

 for (i = 0; i < objs.length; ++i) {
  if (objs[i].active) {
   isVisible(objs[i]) ? hideElement(objs[i]) : showElement(objs[i]);
  }
 }
 setTimeout(function() { blink(objs, delay, times, final_func); }, delay);
}

var cur_enabled_deps = new Array();
function objLength(o)
{
 var res = 0;
 for (var i in o) {
  res += 1;
 }
 return res;
}

function set_deps_enabled(deps, is_enabled, activator)
{
 // Keep a set of any selected deps. Each dep has an array of activator
 // enabled it. First we find the number of activator's ie. the length of
 // the largest array.
 // Next we only enable the deps that has an array of that size, since
 // that means that all "activator's" can handle this dep.

 // First enable all the deps of this checkbox
 for (var i = 0; dep = deps[i]; i++) {
  if (!dep.activators)
   dep.activators = new Object();
  if (is_enabled)
   dep.activators[activator.id] = true;
  else if (dep.activators[activator.id])
   delete dep.activators[activator.id];

  cur_enabled_deps[dep.id] = dep;
 }
 // Get largest arraylen
 var maxlen = 0;
 for (dd in cur_enabled_deps) {
  var dep = cur_enabled_deps[dd];
  if (typeof dep == "function")
   continue;
  var ol = objLength(dep.activators);
  if (ol >= maxlen)
   maxlen = ol;
 }
 for (dd in cur_enabled_deps) {
  if (typeof cur_enabled_deps[dd] == "function")
   continue;
  dep = cur_enabled_deps[dd];
  if (dep.activators && objLength(dep.activators) == maxlen && maxlen != 0) {
   set_action_state2(dep, true);
  } else {
   set_action_state2(dep, false);
  }
 }
}

function set_list_checked(l, v, set_by_parent)
{
 for (var i=0; elm = l[i]; i++) {
  set_checked(elm, v, set_by_parent);
 }
}

function set_checked(element, v, set_by_parent)
{
 if (element.active_dependencies) {
  // En/disabled dependencies
  set_deps_enabled(element.active_dependencies, v, element);
 }
 if (element.active_connections)
  set_list_checked(element.active_connections, v, true);

 var num_checked_diff = 0;
 if (!set_by_parent) {
  if (element.checked)
   num_checked_diff = 1;
  else
   num_checked_diff = -1;
 } else {
  // Set by parent. compare with previous state in order
  // to get diff
  if (element.checked && !v)
   num_checked_diff = -1;
  else if (!element.checked && v)
   num_checked_diff = 1;
 }
 element.checked = v;

 if (element.parent) {
  // Set style of the same row as the checkbox to lightblue
  var maxp = 5;
  var rownode = element.parentNode;
  while (maxp--) {
   if (rownode.tagName == "TR") {
    break;
   }
   rownode = rownode.parentNode;
  }
  if (rownode.tagName == "TR") {
   if (v) {
    objModToOrigClass(rownode, "s", "");
   } else {
    objModToOrigClass(rownode, "", "s");
   }
  }
 }
 if (num_checked_diff && element.parent) {
  element.parent.num_checked = element.parent.num_checked + num_checked_diff;
  element.parent.update_state(num_checked_diff);
 }
}

function update_state(element,childdiff)
{
 var is_checked = element.num_total == element.num_checked;
 var diff = 0;
 if (element.checked && !is_checked) {
  diff = -1;
 } else if (!element.checked && is_checked) {
  diff = 1;
 }
 element.checked = is_checked;

 if (element.active_dependencies) {
  // En/disabled dependencies
  set_deps_enabled(element.active_dependencies, element.num_checked, element);
 }

 if (element.parent) {
  element.parent.num_checked = element.parent.num_checked + diff + childdiff;
  element.parent.update_state(diff+childdiff);
 }
}

// ToolTip
var active_tip, tip_show_timeout;
function show_tip() {
 // The tooltip is oddly misplaced on the view page, so the lousy
 // solution is to disable it for this specific page. XXX
 if (document.location.search.indexOf("path=1.3.14&") != -1)
  return;

 var windowInfo = getWindowInfo();
 var xPos = xMousePos;
 if (xPos + 100 > windowInfo.sizeX)
  xPos = windowInfo.sizeX - 100;

 active_tip.style.top = (yMousePos + 10) + "px";
 active_tip.style.left = (xPos + 0) + "px";
 active_tip.style.display = "block";

 var current_width = active_tip.offsetWidth;
 if (xPos + current_width > windowInfo.sizeX) {
  var new_width = windowInfo.sizeX - xPos;
  if (new_width < 100)
  new_width = 100;
  active_tip.style.width = new_width + "px";
 }
}

function logit(message)
{
// var dbg = getById("dbg");
// if (dbg)
//  dbg.innerHTML += message+"<br>";
// else
//  alert("logit: "+message);
}

// element must shortstatus ("Alert", "Unknown", ..") stored
// in className as shortstatus-<shortstatus>
function bubble_must_be_visible(element)
{
 var match = element.className.match(/shortstatus-(\w+)/);
 if (match) {
  var shortstatus = match[1];
  if (shortstatus == "Alert")         return gview_config.showPopupOnAlert;
  else if (shortstatus == "Warning")  return gview_config.showPopupOnWarning;
  else if (shortstatus == "Downtime") return gview_config.showPopupOnDowntime;
  else if (shortstatus == "Unknown")  return gview_config.showPopupOnUnknown;
  else if (shortstatus == "OK")       return gview_config.showPopupOnOK;
 }
 return true; // show on internal error
}

var view_entry_cascade_offset = 1;

// Make sure the view entry position is inside the PNG image
// - image element
// - element is the outermost view entry <div>
function check_view_entry_position(image, element)
{
  var x = parseInt(element.style.left.replace(/px$/,""), 10);
  var y = parseInt(element.style.top.replace(/px$/,""), 10);
  var ix = image_x(image);
  var iy = image_y(image);

  if (iy === undefined || iy < 0) {
   // Konqueror may return -20000000
   // IE returns undefined because it does not support image.y
   logit("image position not know yet iy="+iy+" "+element.id);
   return;
  }

  // First time here?
  if (element.sysorb_data === undefined) {
   var match = element.className.match(/shortstatus-(\w+)/);
   element.sysorb_data = {
    initial_x: x,
    initial_y: y,
    shortstatus: match[1]           // Value is not actually used
   };
   // This is the first time this function is called on this element
   // so x and y are the relative positions. Translated the position
   // relative to the image
   x += ix;
   y += iy;
   element.style.left = x;
   element.style.top = y;
  }

  // Locate images placed outside the image and put in the
  // top left corner of the image
  var right = ix + image.width;
  var bottom = iy + image.height;
  var hide_bubble = false;      // Hide if repositioned
  if (x <= ix || y <= iy || x > right || y > bottom) {
   logit("cascade "+element.id);
   hide_bubble = true;
   element.style.top  = y = iy + view_entry_cascade_offset;
   element.style.left = x = ix + view_entry_cascade_offset;
   view_entry_cascade_offset += 15; // Cascade the entries to the south-east
  }

  var bubble = getById(element.id + "-bubble");
  if (bubble !== null) {
   update_bubble(element, bubble, bubble_must_be_visible(element)
                 && !hide_bubble, undefined);
  }
}


// Show the bubble for dot
function graphicalViewEntryShow(dot)
{
 var bubble = getById(dot.id+"-bubble");
 if (bubble) {
  var m = dot.id.match(/^graphical-view-entry-(\d+)$/); // extract number
  update_bubble(dot, bubble, !isVisible(bubble), m[1]);
 }
}

function bubbleHide(bubble)
{
 hideElement(bubble);
}

var refresh_in_progress

function adjust_bubble_position(element, bubble)
{
 var x = parseInt(element.style.left.replace(/px$/,""), 10);
 var y = parseInt(element.style.top.replace(/px$/,""), 10);

 // Align the bubble so it points to the dot-icon
 bubble.style.left = x + 14;
 var top = y - bubble.clientHeight;
 bubble.style.top = top > 5 ? top : y + 16;
}

// Update the bubble position, visibility and content based on input parameters
// - element: element with id graphical-view-entry-<number>
// - bubble:  element with id graphical-view-entry-<number>-bubble
// - visible: bool that determines visibility
// - new_content string with temporary placeholder content.
// - i: bubble index (optional). Setting this
//   initiates ajax request to server that updates the bubble content
function update_bubble(element, bubble, visible, i)
{
 if (visible) {
  if (i !== undefined) {
   var url = "index.cgi?path=1.3.14.2&view="+gview_config.view_id
    +"&view_entry="+i;
   var bubble_content = getById("graphical-view-entry-"+i+"-bubble-content");

    // Set temporary content
   bubble_content.innerHTML = ""
    +"<div id=\""+element.id+"-bubble-content\">"
    +" <div class=\"shortstatus\""
    +" style=\"clear: none\">" + element.sysorb_data.shortstatus
    +"&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;"
    +" </div>"
    +" <div style=\"padding: 20; background-color: white;"
    +" opacity: 1.0\">"
    +"  <center><img src=\"wait22trans.gif\"></center>"
    +" </div>"
    +"</div>";

   // Opacity is disabled in temporary box because it does not work
   // properly in conjunction whith the transparent animated gif

   // Ajax request
   sendRequest(url, function(req) {
    var ii = i;        // close over i in this closure
    var node = getById("graphical-view-entry-"+ii+"-bubble-content");

    // Rewrite the bubble content
    node.innerHTML = req.responseText;
    adjust_bubble_position(element, bubble);
   });
  }
  adjust_bubble_position(element, bubble);
  showElement(bubble);
 } else {
  hideElement(bubble);
 }
}

// Ajax handler. Parses status lines of the form
//
//   1 1.3938595161 green 1 OK
//   2 1.3467975410 grey 0 Unknown
//   3 1.41492467 red 13 Alert
//
// and calls update_bubble if the short status has changed
//
function gview_tick_callback(req)
{
 var text = req.responseText;
 var lines = text.split(/\r\n|\r|\n/);
 var changes = 0;

 // The first line contains the column names
 if (!/^i id color status shortstatus /.test(lines[0])) {
  // We probably got the login html.

  // Put a red box around the image in case the reload below should fail
  getById("graphical-view-image").setAttribute("style",
                                                 "border: 3px dashed red");
  window.location.reload();
  return;
 }

 for (var i = 1; i < lines.length; i++) {
  // Each line must begin with a number
  if (/^[^\d]+\s/.test(lines[i]))
   return;
  if (lines[i] !== "") {
   var id = "graphical-view-entry-"+i;
   var dot = getById(id);
   if (!dot){
    // XXX somehow suggest to the user that he reloads page
    break;
   }
   var bubble = getById(id+"-bubble");
   var fields = lines[i].split(/\s+/);
   var color = fields[2];
   var status = fields[3];   // code for ES_NA, ES_GOOD, ..
   var shortstatus = fields[4]; // "OK", "Alert", "Unknown", ..
   var className = "view "+color+" shortstatus-"+shortstatus;
   // Only update the bubble if the shortstatus has changed
   if (dot.className !== className) {
    dot.className = className; // DOM change
    dot.sysorb_data.shortstatus = shortstatus;
    update_bubble(dot, bubble, bubble_must_be_visible(dot), i);
    changes++;
   }
  }
 }
 // We updated the DOM so reapply behaviours
 if (changes > 0)
  Behaviour.apply();
}

// Called periodically on the Graphical View "view page"
function gview_tick()
{
 var image = getById("graphical-view-image");
 logit("gview_tick y'="+image_y(image)+" w="+image.width+" x'="+image_x(image));
 // On the "move items" page gview_config is not defined and we don't
 // want status updates
 if (typeof gview_config != "undefined") {
  var url = "index.cgi?path=1.3.14.1&view="+gview_config.view_id;
  sendRequest(url, gview_tick_callback);
  setTimeout(gview_tick, 60000);
 }
}

var image_polling = 0;
// Only called on the Graphical View "view page" 1.3.14 and the "move
// items" page 1.3.12.11
function gview_start(enable_update) {
 var image = getById("graphical-view-image");
 var y = image_y(image);
 logit("gview_start y'="+y+" w="+image.width
       +" image.y="+image.y+" image.offsetTop="+image.offsetTop
       +" image.offsetLeft="+image.offsetLeft);
 if (y === undefined || y < 0) // Image not loaded yet
 {
  image_polling++;
  // Call this function again later
  setTimeout(gview_start, 20);
  return;
 }

 // Initiate status poller
 setTimeout(gview_tick, 15000);
 // If we know the image position and did not previously then reapply
 // behaviours
 if (image_polling > 0)
  Behaviour.apply();
}

Behaviour.register(myrules);

//
// Since many mainstream browsers leak on function closure we need to
// manually fix the leak. This is done by breaking a cyclic dependency
// to the closure. The closures are made in the behaviour styles, but
// we remember the closures by attaching them to the appropriate
// elements and remembers to null them.
//

