


function load_json(t) {
  

  
  if(window.JSON && JSON.parse) {
    return JSON.parse(t);
  } else {
    return eval("(" + t + ")");
  }
}














function abbreviate_middle(l, s) {
  if(s.length <= l) return s;
  var length_left = parseInt( (l-3) / 2, 10);
  return s.substr(0, length_left) + "..." + s.substr( - (l-3-length_left), l-3-length_left);
}





Number.prototype.padTo=function(n) {
  if(this.length > n) return this;
  var r = "" + this;
  while(r.length < n) r = "0" + r;
  return r;
};




function pad_2(num_string) {
  num_string = "" + num_string;
  if(num_string.length >= 2) {
    return num_string;
  } else {
    return "0"+num_string;
  }
}




function days_in_month(year, month) {
  month--; // To get 0-11 months
  month++; // Because we want to look at the next month!
  
  if(month>11) {
    month=0;
    year++;
  }
  var start_of_next_month = new Date(year, month);
  var end_of_this_month = new Date(start_of_next_month.getTime() - (1000*60*60*12));
  return end_of_this_month.getDate();
}





function set_max_day_from_value(name) {
  var i;
  var max_day=days_in_month(
    document.getElementById(name+".y").value, 
    document.getElementById(name+".m").value
  );
  for(i = 28; i<max_day+1; i++) {
    document.getElementById(name+".d."+i).disabled=false;
  }
  for(i = max_day+1; i<32; i++) {
    document.getElementById(name+".d."+i).disabled=true;
  }
  if(document.getElementById(name+".d").value > max_day)
    document.getElementById(name+".d").value = max_day;
}





function update_hidden_date(name) {
  document.getElementById(name+".h").value =
    document.getElementById(name+".y").value + "-" +
    pad_2(document.getElementById(name+".m").value) + "-" + 
    pad_2(document.getElementById(name+".d").value) +
    document.getElementById(name+".t").value;
}




function update_hidden_date_ym(name) {
  document.getElementById(name+".h").value =
    document.getElementById(name+".y").value + "-" +
    pad_2(document.getElementById(name+".m").value);
}






function find_element_root_offset(element) {
  var x=0, y=0;
  var e, i;
  while(element && element.offsetLeft!==undefined) {
    x+=element.offsetLeft;
    y+=element.offsetTop;
    for(e=element; e!==element.offsetParent; e=e.parentNode) {
      

      if(
        e.offsetLeft == 0 &&
        e.tagName=="TD" &&
        e!==e.parentNode.getElementsByTagName("TD").item(0)
      ) {
        var cn = e.parentNode.childNodes;
        for(i=0; i<cn.length && e!==cn.item(i); i++)
          if(cn.item(i).tagName && cn.item(i).tagName=="TD")
            x+=cn.item(i).offsetWidth;
      }
    }
    element=element.offsetParent;
  }
  return [x,y];
}













function overlay_near_element(target_element, move_element_id, approx_width, approx_height, move_element) {
  var target_position = find_element_root_offset(target_element);
  var x = target_position[0];
  var y = target_position[1];
  move_element = move_element || document.getElementById(move_element_id);
  var divstyle=move_element.style;
  if(x>approx_width/2) x-=approx_width/2; else x=0;
  if(y>approx_height/2) y-=approx_height/2; else y=0;
  divstyle.left=x+"px";
  divstyle.top=y+"px";
  divstyle.visibility='visible';
}





function try_refresh(time, url) {
  location.href=url;
  setTimeout("try_refresh(" + time + ", '" + url + "')",time*1000);
}




function set_action(this_form, new_action) {
  var i;
  var elements = this_form.elements;
  for(i = 0 ; i < elements.length ; ++i) {
    if(elements[i].name == "action")
      elements[i].value = new_action;
  }
  return true;
}
var page_title;






function override_page_title(t) {
  page_title = t;
}

var other_title_callbacks = [];








function set_title() {
  var i;
  var title_text_node;
  if(!page_title) {
    var heads = document.getElementsByTagName("h1");
    if(heads.length > 0) {
      for(i=0; i<heads.item(0).childNodes.length; i++) {
        if(heads.item(0).childNodes.item(i).data) {
          title_text_node = heads.item(0).childNodes.item(i);
          break;
        }
      }
    } else if(document.getElementById("effective-title")) {
      title_text_node=document.getElementById("effective-title").firstChild;
    } else {
      return false;
    }
    if(!title_text_node) return false;
    page_title = title_text_node.data;
  }
  var new_title = "Heart: ";
  // The first head should be the one we want
  new_title = new_title + page_title;
  for(i=0; i<other_title_callbacks.length; i++)
    other_title_callbacks[i](page_title);
  document.title = new_title;
  return true; 
}





function toggle_div(id) {
  var div=document.getElementById(id);
  if (div.style.display == 'none') {
    div.style.display = 'block';
  } else {
    div.style.display = 'none';
  }
}





function go_back_ish() {
  if(document.referrer && document.referrer != location.href) {
               history.go(-1); // We just go straight back.
  } else if(document.getElementById(':1.container')) {
    history.go(-2); // Go back twice if translator is loaded.
  } else {
    location.href = "/";
  }
  return false;
}





var broken_windows_browser=0;
if(navigator.userAgent.toLowerCase().indexOf("msie")>=0) broken_windows_browser=1;




var broken_windows_browser_le_8 = false;
var md = navigator.userAgent.match(/MSIE ([0-9]+)/);
if(md && parseInt(md[0]) < 9) broken_windows_browser_le_8 = true;

var xmlhttp_by_name = {};









function object_to_arg_string(args) {
  var post_content = "";
  var i, j;
  for(i in args) {
    if(args[i] === undefined) continue;
    if(args[i] instanceof Array) {
      for(j=0; j<args[i].length; j++)
        post_content += i + "=" + encodeURIComponent(args[i][j]) + ";";
    } else {
      post_content += i + "=" + encodeURIComponent(args[i]) + ";";
    }
  }
  return post_content;
}







function xmlhttp_call_with_args(name, orst, url, args, sync) {
  var post_content = object_to_arg_string(args);

  var xmlhttp_o;
  if(broken_windows_browser) {
    xmlhttp_o = new ActiveXObject("MsXml2.XmlHttp");
  } else {
    xmlhttp_o = new XMLHttpRequest();
  }
  xmlhttp_by_name[name] = xmlhttp_o;
  xmlhttp_o.onreadystatechange=orst;
  xmlhttp_o.open("POST", url, !sync);
  xmlhttp_o.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
  if(!navigator.userAgent.match(/AppleWebKit/)) {
    // WebKit hates this, but some other browsers may need it.
    xmlhttp_o.setRequestHeader("Content-length", post_content.length);
    xmlhttp_o.setRequestHeader("Connection", "close");
  }
  xmlhttp_o.send(post_content);
  if(sync) {
    // onreadystatechange won't have fired.
    orst();
  }
  return xmlhttp_o;
}







var alert_count = 0;
var alert_limit = 20;
function alert_l(str, chosen_limit) {
  alert_count++;
  if(!chosen_limit) chosen_limit=alert_limit;
  if(alert_count>= chosen_limit) return false;
  return window.alert(str);
}








function build_simple_xmlr_statechange(n, on_success, on_failure, check_for_success, options) {
  options = options||{};

  var orst_fired = false;
  return function() {
    if(orst_fired) return;
    
    // if LOADED
    var my_xmlhttp = xmlhttp_by_name[n];
    if(my_xmlhttp.readyState == 4 && my_xmlhttp.status == 200) {
      orst_fired = true;
      var xmldoc=my_xmlhttp.responseXML;
      if(!xmldoc) {
        if(on_failure) on_failure();
      } else if(!xmldoc.lastChild) {
        if(on_failure) on_failure(xmldoc.lastChild);
      } else if(check_for_success && !check_for_success(xmldoc.lastChild)) {
        if(on_failure) on_failure(xmldoc.lastChild);
      } else {
        if(on_success) on_success(xmldoc.lastChild);
      }
    } else if(my_xmlhttp.readyState == 4) {
      orst_fired = true;
      if(my_xmlhttp.status && !options.skipError) {
        if(!options.silentError) alert_l("HTTP error: "+my_xmlhttp.status);
        if(on_failure) on_failure();
      }
    }
  };
}
























function xmlhttp_simple_full(url, args, check_for_success, on_success, on_failure, options) {
  var i, j;
  options = options||{};

  // Convert the args to a POST string
  var post_content = "";
  for(i in args) {
    if(args[i] === undefined) continue;
    if(args[i] instanceof Array) {
      for(j=0; j<args[i].length; j++)
        post_content += i + "=" + encodeURIComponent(args[i][j]) + ";";
    } else {
      post_content += i + "=" + encodeURIComponent(args[i]) + ";";
    }
  }

  // Allocate the object
  var xmlhttp_o;
  if(broken_windows_browser) {
    xmlhttp_o = new ActiveXObject("MsXml2.XmlHttp");
  } else {
    xmlhttp_o = new XMLHttpRequest();
  }
  var on_load_fired = false;
  function on_load() {
    on_load_fired = true;
    if(xmlhttp_o.status == 200) {
      if(options.returnText) {
        if(on_success) on_success(xmlhttp_o.responseText);
      } else {
        var xmldoc=xmlhttp_o.responseXML;
        if(!xmldoc) {
          if(on_failure) on_failure();
        } else if(!xmldoc.lastChild) {
          if(on_failure) on_failure(xmldoc.lastChild);
        } else if(check_for_success && !check_for_success(xmldoc.lastChild)) {
          if(on_failure) on_failure(xmldoc.lastChild);
        } else {
          if(on_success) on_success(xmldoc.lastChild);
        }
      }
    } else {
      if(xmlhttp_o.status && !options.skipError) {
        if(!options.silentError) alert_l("HTTP error: "+xmlhttp_o.status);
        if(on_failure) on_failure();
      }
    }
  }
  var partial_load_failed = false;
  xmlhttp_o.onreadystatechange = function() {
    // if LOADED
    if(xmlhttp_o.readyState == 4) {
      // MSIE has to catch up here.
      if(partial_load_failed) options.onPartialLoad(xmlhttp_o.responseText);
      on_load();
    }
    // if partly loaded
    if(options.onPartialLoad && xmlhttp_o.readyState == 3) {
      try {
        options.onPartialLoad(xmlhttp_o.responseText);
      } catch(e) {
        // Do nothing - MSIE will just have to wait.
        partial_load_failed = true;
      }
    }
  };
  if(options.get) {
    var full_url = post_content.length ? url+"?"+post_content : url;
    xmlhttp_o.open("GET", full_url, options.sync ? false : true);
    xmlhttp_o.send();
  } else {
    xmlhttp_o.open("POST", url, options.sync ? false : true);
    xmlhttp_o.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
    if(!navigator.userAgent.match(/AppleWebKit/)) {
      // WebKit hates this, but some other browsers may need it.
      xmlhttp_o.setRequestHeader("Content-length", post_content.length);
      xmlhttp_o.setRequestHeader("Connection", "close");
    }
    xmlhttp_o.send(post_content);
  }
  if(options.sync && ! on_load_fired) on_load();
  return xmlhttp_o;
}

















function form_to_http_request(f, on_success, on_failure, check_for_success, override_action, include_last_submit, options) {
  var form_contents = {};

  function add_contents(e, default_name) {
    var name = e.name || default_name;
               if(!name) return false; // Should never happen, but does.
               form_contents[name]=form_contents[name] || [];
               form_contents[name].push(e.value);
    return true;
  }

  var last_submit;
  var i;

  for(i=0; i<f.elements.length; i++) {
    var e = f.elements.item(i);
    if(e.disabled) continue;
    if(e.type == "radio" || e.type == "checkbox") {
      if(!e.checked) continue;
    } else if(e.type == "submit") {
      last_submit=e;
      continue;
    } else if(e.type == "submit" || e.type == "image") {
      // Broken for now.
      continue;
    }
    add_contents(e);
  }
  if(include_last_submit && last_submit) add_contents(last_submit, "submit");
  form_contents['using-ajax-really']=["1"];
  var action_to_use = override_action || f.action;
  if(!action_to_use) return true; // Cannot work
  xmlhttp_simple_full(action_to_use, form_contents, check_for_success, on_success, on_failure, options);
  return false;
}





function replace_el_with_id(id, w) {
  var s = document.getElementById(id);
  s.parentNode.replaceChild(w, s);
  if(!w.id) w.id=id;
}







function _add_nodes(e, contents) {
  var i;
  if(!contents) return false;
  if(contents.constructor!=Array) contents=[contents];
  for(i=0; i<contents.length; i++) {
    var item = contents[i];
    if(item === undefined) continue; // Should never happen, but... you know the drill.
    if(item === null) continue;
    if(item.nodeType) {
      e.appendChild(item);
    } else {
      if(name=="input") {
        alert("Can't add node to element of type '" + name + "'");
      } else {
        e.appendChild(document.createTextNode(item));
      }
    }
  }
  return true;
}






















function _mkel(name, attributes, contents, tail_tweaks) {
  var n, s;
  var e = document.createElement(name);
  if(attributes && attributes.constructor != Object) {
    tail_tweaks = contents;
    contents = attributes;
    attributes = {};
  }
  if(attributes) {
    for(n in attributes) {
      if(!n) continue;
      if(broken_windows_browser && n == "type" && name == "button") continue; // Skip it, cross fingers.
      if(
        attributes[n] === null ||
        attributes[n] === undefined ||
        (
          attributes[n].constructor != String &&
          attributes[n].constructor != Boolean
        )
      ) {
        






        if(!attributes[n]) continue;
      }
      if(attributes[n] && attributes[n].constructor == Object) {
        for(s in attributes[n]) e[n][s] = attributes[n][s];
      } else {
        e[n]=attributes[n];
      }
    }
  }
  if(contents && contents.constructor==Function) {
    tail_tweaks = contents;
    contents = undefined;
  }
  _add_nodes(e, contents);
  if(tail_tweaks)
    tail_tweaks.call(e, e);
  return e;
}





var popup__detail_container;
function prepare_detail_container() {
  window.onscroll=function() {
    if(popup__detail_container)
      document.body.removeChild(popup__detail_container);
    popup__detail_container = undefined;
  };
}
























function show_popup_detail(ndc, desired_width, options) {
  var i;
  options=options||{};
  var duration=0.5;
  if(undefined != options.popup_duration)
    duration = options.popup_duration;
  var detail_container = popup__detail_container;
  if(detail_container) try {
    document.body.removeChild(detail_container);
    document.body.removeChild(document.getElementById("clickable-underlay"));
  } catch(e) {}

  popup__detail_container = detail_container = ndc;
  var underlay;
  var onclick_closes = function() {
    if(underlay) document.body.removeChild(underlay);
    new Effect.Shrink(detail_container.id, {duration: duration, direction: "center"});
    if(options.onclose) options.onclose();
  };
  function fix_close_widget(w) {
    var original_onclick = w.onclick;
    w.onclick = function() {
      onclick_closes.call(this);
      if(original_onclick) original_onclick.call(this);
    };
  }
  if(options.closeWidget) {
    var close_widgets = (options.closeWidget.constructor == Array) ?
      options.closeWidget :
      [ options.closeWidget ];
    for(i=0; i<close_widgets.length; i++)
      fix_close_widget( close_widgets[i] );
  }

  var anchor_p = document.createElement("p");
  anchor_p.style.textAlign="center";
  if(options.footer_extra) {
    for(i=0; i<options.footer_extra.length; i++)
      anchor_p.appendChild(options.footer_extra[i]);
  }
  if(!options.closeWidget) {
    var close_el = document.createElement("button");
    //close_el.type="button";
    close_el.onclick = onclick_closes;
    close_el.appendChild(document.createTextNode("Close"));
    anchor_p.appendChild(close_el);
  }
  detail_container.appendChild(anchor_p);
  var de = document.documentElement;
  var w = window.innerWidth || (de&&de.clientWidth) || document.body.clientWidth;
  var h = window.innerHeight || (de&&de.clientHeight) || document.body.clientHeight;

  detail_container.style.position= options.absolutePosition ? 'absolute' : 'fixed';
  detail_container.style.display="none";
  detail_container.style.zIndex=100;
  if(desired_width)
    detail_container.style.width = desired_width;
  if(options.be_square)
    detail_container.style.height = detail_container.style.width;
  detail_container.id="pseudo-popup";

  if(options.clickableUnderlay) {
    underlay = _mkel("div", {
      id: "clickable-underlay",
      style: {
        position: "fixed",
        top: "0px",
        left: "0px",
        width: w + "px",
        height: h + "px",
        zIndex: 99 
      },
      onclick: onclick_closes
    });
    if(options.shadowUnderlay) {
      underlay.style.background = "#888"; 
      if(broken_windows_browser) {
        underlay.style.background = "black";
        underlay.style.filter = "progid:DXImageTransform.Microsoft.Alpha(opacity=50)";
      } else {
        underlay.style.background = "rgba(0, 0, 0, 0.5)";
      }
    }
    document.body.appendChild(underlay);
  }

  document.body.appendChild(detail_container);

  var translate_x = 0;
  var translate_y = 0;
  if(options.adjustToScrollPos) {
    translate_x = window.scrollX || document.body.scrollLeft || document.documentElement.scrollLeft;
    translate_y = window.scrollY || document.body.scrollTop || document.documentElement.scrollTop;
  }

  detail_container.style.left = translate_x + Math.round((w - Element.getWidth(detail_container)) / 2 ) + "px";
  detail_container.style.top = translate_y + Math.round((h - Element.getHeight(detail_container)) / 2 ) + "px";

  if(detail_container.style.top.match(/^-/)) {
    detail_container.style.top = "20px";
  }
  new Effect.Grow(detail_container.id, {duration: duration, direction: "center"});
}






















function unhtml(content_to_decode, preserve_tags) {

  if(preserve_tags)
    content_to_decode =
      content_to_decode.replace(/</g, "&lt;").replace(/>/g, "&gt;");

  var span = document.createElement("span");
  span.innerHTML=content_to_decode;
  return(span.innerText || span.textContent);
}







function convert_html_entities(content_to_decode) {
  content_to_decode = content_to_decode.replace(/\n/g, '[BACKSLASH]n');
  var span = document.createElement("span");
  span.innerHTML=content_to_decode;
  return span.innerHTML.replace(/\[BACKSLASH\]n/g, '\n');
}







var rlbns__timeouts={};
function run_late_but_not_simultaneously(c, time) {
  var timeout = rlbns__timeouts[c];
  if(timeout) clearTimeout(timeout);
  rlbns__timeouts[c] = setTimeout(c, time);
  return rlbns__timeouts[c];
}




function _bind_to_any(t, f) { // Utility.
  return function() {return f.apply(t, arguments);};
}






var _utils_prototypes = {
  getElementsByClassName: function(strClassName){
    var i;
    if(!strClassName) return [];
    var arrElements = this.all;
    var arrReturnElements = [];
    strClassName = strClassName.replace(/-/g, "\\-");
    var oRegExp = new RegExp("(^|\\s)" + strClassName + "(\\s|$)");
    var oElement;
    for(i=0; i<arrElements.length; i++){
      oElement = arrElements[i];
      if(oRegExp.test(oElement.className)){
        arrReturnElements.push(oElement);
      }
    }
    return (arrReturnElements);
  },
  stripSuffix: function(s) {
     if(this.indexOf(s)==0) return this.substring(s.length);
     return this;
   }
};

  if(!broken_windows_browser) {
    if(!String.prototype.stripSuffix) 
      String.prototype.stripSuffix = _utils_prototypes.stripSuffix;
  }








function _u_prototype() {
  var name = arguments[0];
  var a = [];
  var i;
  for(i=1; i<arguments.length; i++)
    a[i-1]=arguments[i];
  if(!this[name])
    this[name] = _utils_prototypes[name];
  return this[name].apply(this, a);
}





function ordinal(n) {
  var end = 'th';
  if(n % 100 < 10 || n % 100 > 20) {
    switch(n % 10) {
      case 1: end = 'st'; break;
      case 2: end = 'nd'; break;
      case 3: end = 'rd'; break;
    }
  }

  return end;
}





var month_names = ['January', 'February', 'March',
                   'April', 'May', 'June',
                   'July', 'August', 'September',
                   'October', 'November', 'December'];




var month_numbers = ['01', '02', '03',
                     '04', '05', '06',
                     '07', '08', '09',
                     '10', '11', '12'];






function ISO8601_to_GB(dt) {
  var d_t = dt.split(/[T ]/);
  d_t[0] = d_t[0].split(/-/).reverse().join("/");
  return d_t.reverse().join(" ");
}







function binary_insert(top_element, sort_function, o, build_nodes, on_add) {
  var cn = top_element.childNodes;
  var insertion_point = binary_search(top_element, sort_function, o);
  var a = build_nodes(o);
  var i;
  if(insertion_point >= cn.length) {
    for(i=0; i<a.length; i++) {
      top_element.appendChild(a[i]);
      if(on_add) on_add.call(a[i]);
    }
  } else {
    var add_before = cn.item(insertion_point);
    for(i=0; i<a.length; i++) {
      top_element.insertBefore(a[i], add_before);
      if(on_add) on_add.call(a[i]);
    }
  }
}
























function merge_node_list(top_element, build_nodes, node_to_uid, detect_change, objects_by_uid, sort_function, on_delete, on_add, on_no_change) {
  var cn = top_element.childNodes;
  // Pass 1: remove dirty nodes
  var skip_uids = {};
  var i, uid;
  for(i=cn.length-1; i>=0; i--) {
    var element_to_consider = cn.item(i);
    uid = node_to_uid(element_to_consider);
    if(!uid) continue;
    var o = objects_by_uid[uid];
    if(o && !detect_change(element_to_consider, o)) {
      skip_uids[uid] = 1;
      if(on_no_change)
        on_no_change.call(element_to_consider);
    } else {
      if(on_delete) {
        if(on_delete.call(element_to_consider))
          top_element.removeChild(element_to_consider);
      } else {
        top_element.removeChild(element_to_consider);
      }
    }
  }

  // Pass 2: add fresh nodes
  for(uid in objects_by_uid)
    if(!skip_uids[uid])
      binary_insert(top_element, sort_function, objects_by_uid[uid], build_nodes, on_add);
}









function binary_search(top_element, sort_function, o) {
  var cn = top_element.childNodes;
  var top = cn.length; // Note that this is just past the end.
  var bottom = 0;
  if(bottom == top) return 0; // This covers the zero-content scenario.

  var last_position = -1;
  var test_position = Math.round((top+bottom) / 2);
  if(test_position == top) {
    


    test_position--;
  }
  var i=0;
  do {
    var e = cn.item(test_position);
    var result = sort_function(e, o);
    if(result>0) {
      // In correct order, ie we're at or past the bottom.
      bottom = test_position + 1;
    } else if(result<0) {
      // Out of order
      top = test_position - 1;
    } else {
      // If == 0, might as well abort.
      break;
    }

    last_position = test_position;
    test_position = Math.round((top+bottom) / 2);
    i++;
  } while(test_position != last_position && test_position < cn.length && i<32);

  return test_position;
}













function build_chain(values, f, tf, sleep_ms) {
  var i;
  tf = tf || function() {};
  function _build_tf(v, otf) {
    function to_call() {
      f(v, otf);
    }
    if(sleep_ms) {
      return function() {
        setTimeout(to_call, sleep_ms); 
      };
    } else {
      return function() {
        f(v, otf);
      };
    }
  }
  for(i=values.length - 1; i>=0; i--)
    tf = _build_tf(values[i], tf);
  return tf;
}











function chain(values, f, tf, sleep_ms) {
  return build_chain(values, f, tf, sleep_ms)();
}
















function build_function_chain(functions, tf) {
  var i;
  tf = tf || function() {};
  function _build_tf(f, otf) {
    return function() {
      f(otf);
    };
  }
  for(i=functions.length - 1; i>=0; i--)
    tf = _build_tf(functions.item(i), tf);
  return tf;
}




function emulate_html5_input_placeholder() {
  function add_placeholder_emulation(input) {
    

    if(input.value == input.getAttribute("placeholder"))
      input.value = "";
    var old_onfocus = input.onfocus;
    var old_colour = input.style.color;
    var placeholder_onfocus = function() {
      this.onfocus = old_onfocus;
      this.value = "";
      this.style.color = old_colour;
      if(this.onfocus) this.onfocus();
    };
    var old_onblur = input.onblur;
    input.onblur = function() {
      if(this.value == "") {
        this.onfocus = placeholder_onfocus;
        this.value = this.getAttribute("placeholder");
        this.style.color = "#888";
      }
      if(old_onblur) old_onblur.call(this);
    };
    input.onblur();
  }

  var inputs = document.getElementsByTagName("input");
  var i;
  for(i=0; i<inputs.length; i++) {
    var input = inputs.item(i);
    if(input.type=="text" && input.getAttribute("placeholder") && (broken_windows_browser || !input.placeholder) )
      add_placeholder_emulation(input);
  }
}





var override_css_animation = false;





var manual_animate_interval_o;






var manual_animate_callbacks = [];







function animate_height(node, from, to, animate_length, replacement) {
  function clean_up() {
    if(!node.parentNode) return;
    if(replacement) {
      node.parentNode.replaceChild(replacement, node);
    } else {
      node.parentNode.removeChild(node);
    }
  }
  setTimeout(clean_up, animate_length);  

  





  function animation_thread() {
    var i;
    if(manual_animate_callbacks.length == 0) {
      clearInterval(manual_animate_interval_o);
      manual_animate_interval_o = undefined;
    } else {
      var mac_out = [];
      for(i=0; i<manual_animate_callbacks.length; i++) {
        var cb = manual_animate_callbacks[i];
        if(cb()) mac_out.push(cb);
      }
      manual_animate_callbacks = mac_out;
    }  
  }
  var start_point;
  function ah_inner() {
    if(!node.parentNode) return false;
    
    var offset = new Date() - start_point;
    if(offset > animate_length) offset=animate_length;
    if(offset < 0) offset=10;
    var completeness = offset / animate_length;

    var ch_from = from * ( 1 - completeness );
    var ch_to = to * completeness;
    
    node.firstChild.style.height = Math.floor(ch_from + ch_to) + "px";
    if(offset < animate_length) {
      return true;
    } else {
      return false;
    }
  }

  if(navigator.userAgent.match(/webkit/i) && !override_css_animation) {
    node.style.height = from + "px";
    node.style.WebkitTransitionProperty = "height";
    node.style.WebkitTransitionDuration = (animate_length/1000) + "s";
    setTimeout(function() {
      node.style.height = to + "px";
    }, 0);
  } else if(navigator.userAgent.match(/gecko/i) && !override_css_animation) {
    node.style.height = from + "px";
    node.style.MozTransitionProperty = "height";
    node.style.MozTransitionDuration = (animate_length/1000) + "s";
    setTimeout(function() {
      node.style.height = to + "px";
    }, 0);
  } else {
    
    var animate_preferred_interval = 100;

    start_point = new Date();
    manual_animate_callbacks.push(ah_inner);

    if(!manual_animate_interval_o) {
      manual_animate_interval_o =
        setInterval(animation_thread, animate_preferred_interval);
    }
  }
}





























function hide_block_popup(hide_caption, block_content, options) {
  if(options.noPopup) return [];
  block_content.unshift(_mkel("h2", hide_caption));
  var pu = element_to_popup(block_content, options);
  var contents;
  if(options.initialContentElement) {
    contents = [options.initialContentElement.cloneNode(true)];
  } else {
    contents = [hide_caption];
  }
  var link;
  if(options.fakeAnchor) {
    link = contents[0];
    link.style.cursor = "pointer";
  } else {
    link =  _mkel("a", { href: '#' }, contents);
  }
  link.onclick = function(e) {
    
    if(options.clickThrough) {
      e = e || window.event;
      if(e.originalTarget !== e.currentTarget) return true;
    }
    
    if(pu.parentNode && pu.parentNode === document.body) {
    } else {
      document.body.appendChild(pu);
      position_centred(e, pu);
      if(options.onLoad) options.onLoad.call(pu);
    }
    return false;
  };
  if(!options.noWrapper) {
    link =  _mkel("div", {
        style: {
          width: "100%",
          textAlign: "center",
          paddingBottom: "5px"
        }
      }, [
        _mkel("img", {
          src: "/images/icon_info.png", 
          style: {
            padding: "0px 10px 0px 0px",
            marginBottom: "-3px"
          },
          alt: "Information"
        }),
        link
      ]);
  }
  return [ link ];
}











function element_to_popup(element, options) {
  var popup_style = {
    position: "absolute"
  };
  function shadow_filter(spec) {
    return "progid:DXImageTransform.Microsoft.Shadow(" + spec + ")";
  }
  if(options.shadow) {
    if(broken_windows_browser) {
      popup_style.filter = [
        shadow_filter("color=black, strength=5"), 
        shadow_filter("color=black, strength=2, direction=135"), 
        shadow_filter("color=black, strength=2, direction=315")
      ].join(" ");
    } else {
      popup_style.MozBoxShadow =
        popup_style.webkitBoxShadow = 
          popup_style.BoxShadow = "-4px 4px 11px black";
    }
  }
  var icon = options.skipIcon ? "" :
    _mkel("img", {src: "/images/icon_large_info.png", alt: "Information"});
  var pu = _mkel("div", {
      style: popup_style,
      className: "ftpinfopopup"
    }, [
    _mkel("a", {
      href: "#",
      title: "close",
      onclick: function() {
        pu.parentNode.removeChild(pu);
        return false;
      }
    }, [
      _mkel("img", {src: "/images/pop-close.png", className: "closebtn"})
    ]),
    _mkel("div", {style: {clear: "both"}}),
    icon,
    _mkel("div", {className: "popright"}, element)
  ]);
  return pu;
}



































function MenuSearch(top_of_link_tree_id, section_class_name) { // Start of wrapper function.
  if(top_of_link_tree_id.sectionClassName || top_of_link_tree_id.sectionDetect) {
    var options = top_of_link_tree_id;
    top_of_link_tree_id = options.topElementId;
    section_class_name = options.sectionClassName;
    this.sectionDetect=options.sectionDetect;
    this.gatheredResultsElementId = options.gatheredResultsElementId;
    this.replacementNodes = options.replacementNodes;
    this.sectionLimits = options.sectionLimits;
    this.overflowNotify = options.overflowNotify;
    this.noLinksExpected = options.noLinksExpected;
    this.dontAddLinks = options.dontAddLinks;
    this.dontSearchInternal = options.dontSearchInternal;
    this.clearLinkCaption = options.clearLinkCaption || "X";
    this.clearAfterLinks = options.clearAfterLinks;
    this.existingClearButtonID = options.existingClearButtonID;
    this.noLinkCopy = options.noLinkCopy;
  } else {
    this.gatheredResultsElementId = "autocomplete-results";
  }
  top_of_link_tree_id = top_of_link_tree_id || document.body;
  var top_of_link_tree;
  if(top_of_link_tree_id.tagName) {
    // It's really an element.
    top_of_link_tree = top_of_link_tree_id;  
    top_of_link_tree.id=top_of_link_tree.id|| Math.random();
    top_of_link_tree_id=top_of_link_tree.id;
  }

  this.topOfLinkTree = function() {
    if(!top_of_link_tree) {
      top_of_link_tree=document.getElementById(top_of_link_tree_id);
    }
    return top_of_link_tree;
  }

  this.sectionDetect=this.sectionDetect||function(e) {
    return(e.className==section_class_name);
  };

  var links = new Array(); // Set up below.

  var cache_l_search = [];
  var last_l_search;

  function _keyword_match(k_list, n) {
    for(var i=0; i<k_list.length; i++) {
      if(k_list[i].match(n)) return true;
    }
    return false;
  }

  function _find_matching_elements(e, f) {
    if(f(e)) {
      return [e];
    } else {
      var cn = e.childNodes;
      var r = [];
      for(var i=0; i<cn.length; i++) {
        if(!cn.item(i).tagName) continue;
        r=r.concat(_find_matching_elements(cn.item(i), f));
      }
      return r;
    }
  }

  








  this.menuLinkSearch = function(n) {
    var l;
    last_l_search = n;
    if(n.length == 0) {
      // Return to normal
      l = links;
    } else if(n.length > 0) {
      if(!cache_l_search[n]) {
        cache_l_search[n] = {l: []};
        for(var i=0;i<links.length; i++) {
          if(links[i].keywords.length>0) {
            if(_keyword_match(links[i].keywords, n))
              cache_l_search[n].l.push(links[i]);
          } else {
            // Just add it anyway - it's not searchable
            cache_l_search[n].l.push(links[i]);
          }
        }
      }
      l = cache_l_search[n].l;
    }
    if(!this.dontSearchInternal && this.gatheredResultsElementId) autocomplete_search.call(this, n);
    if(!this.dontAddLinks) add_links.call(this, l);
  }


  






  this.autocompleteSearchInProgress=0;
  var autocomplete_retest;
  this.autocompleteRestestIfNeeded = function() {
    if(autocomplete_retest) autocomplete_search.call(this, autocomplete_retest);
  }
  this.bind = function(f) {
    return _bind_to_any(this, f);
  }
  this.bindName = function(n) {
    return this.bind(this[n]);
  }
  this.autocompleteTypes = [];
  function autocomplete_search(n, opts) {
    n=n.replace(/^[\r\n\s]+/, "").replace(/[\r\n\s]+$/, "");
    if(this.autocompleteSearchInProgress) {
      autocomplete_retest=n;
      return;
    }
    opts=opts||{};
    autocomplete_retest="";
    this.autocompleteSearchInProgress=1;
    var default_filler = options.defaultFiller || "...";
    
    var tried=0;
    var autocomplete_types = this.autocompleteTypes;
    // Wipe all result sets
    var progress=0;
    for(var t=0;t<autocomplete_types.length; t++) {
      progress++; // Even bad ones are added here.
      var conf = autocomplete_types[t];
      var wrapper = document.getElementById(conf.topElementId||conf.id);
      if(!wrapper) continue;
      var w = wrapper.cloneNode(false);
      if(options.onSearch) options.onSearch.call(w);
      if(!this.dontAddLinks) {
        this.filler = _mkel("div", {className: "autocomplete-footer"}, [default_filler]);
        w.appendChild(this.filler);
      }
      if(this.clearAfterLinks && n.length) {
        w.appendChild(_mkel("div", {className: "autocomplete-footer", style: {textAlign: "right"}}, [this.wipeButton]));
      }
      wrapper.parentNode.replaceChild(w, wrapper);
    }
    var this_o=this;
    function onCompleteIsh() {
      if(--progress) return;
      this_o.autocompleteSearchInProgress=0;
      // Strip the filler.
      for(var t=0;t<autocomplete_types.length; t++) {
        var conf = autocomplete_types[t];
        var wrapper = document.getElementById(conf.topElementId||conf.id);
        if(!wrapper) continue;
        if(!wrapper.lastChild) continue;
        if(! ( this_o.filler && this_o.filler.parentNode ) ) continue;
        if(wrapper.lastChild.tagName && wrapper.lastChild.firstChild.data==default_filler) {
          

          if(this_o.filler.parentNode == wrapper)
            wrapper.removeChild(this_o.filler);
          if(n && tried && wrapper.childNodes.length==0) 
            wrapper.appendChild(_mkel("div", {className: "autocomplete-footer"}, "No results found... try fewer or more general keywords"));
        } else {
          

          if(this_o.filler.parentNode == wrapper)
            wrapper.removeChild(this_o.filler);
          if(n && tried && wrapper.childNodes.length==1) 
            wrapper.insertBefore(_mkel("div", {className: "autocomplete-footer"}, "No results found... try fewer or more general keywords"), wrapper.firstChild);
        }
        if(options.onClear && !n) options.onClear.call(wrapper);
      }
    }
    progress++; // It'll be decremented below
    function onSuccessFromConf(conf, lf) {
      return function() {conf.setResults.apply(conf, arguments); onCompleteIsh(); if(lf) lf()};
    }
    function onErrorFromConf(conf, lf) {
      return function() {(conf.onError||conf.setResults).apply(conf, arguments); onCompleteIsh(); if(lf) lf()};
    }

    var dispatch_values = [];
    function dispatcher(conf, lf) {
      conf.dispatch(autocomplete_results_check, n, onSuccessFromConf(conf, lf), onErrorFromConf(conf, lf), options);
    }
    function handle_dispatch(conf) {
      if(options.dispatchChain) {
        dispatch_values.push(conf);
      } else {
        return dispatcher(conf);
      }
    }
    // Set each result set to be filled
    for(var t=0;t<autocomplete_types.length; t++) {
      var conf = autocomplete_types[t];
      if(! conf.prerequisite(n)) {
        conf.lastSearch = "";
        progress--;
      } else if(conf.dispatch) {
        // Looks valid
        tried++;
        handle_dispatch(conf);
      } else {
        // Looks valid
        tried++;
        conf.lastSearch = n;
        xmlhttp_simple_full(
          conf.script,
          {q: n},
          autocomplete_results_check,
          onSuccessFromConf(conf),
          onErrorFromConf(conf),
          {sync: options.sync}
        );
      }
    }
    if(dispatch_values.length) chain(dispatch_values, dispatcher);
    onCompleteIsh();
  }

  




  function autocomplete_results_check(xml_el) {
    return(0 == xml_el.getAttribute("too-many"));
  }

  



  function add_links(l) {
    var by_section = [];
    var tolt = this.topOfLinkTree();
    var sections = tolt ? 
      _find_matching_elements(tolt, this.sectionDetect) :
      [] ;

    var found_by_section = [];
    for(var i=0; i<sections.length; i++) {
      by_section[sections[i].id] = [];
      found_by_section[sections[i].id] = 0;
    }

    for(var i=0;i<l.length;i++) {
      if(l[i].keywords.length>0) {
        found_by_section[l[i].section]++;
      }
      by_section[l[i].section].push(l[i]);
    }

    var top_element = this.gatheredResultsElementId ? 
      document.getElementById(this.gatheredResultsElementId) :
      undefined;
    var el;
    for(var i=0; i<sections.length; i++) {
      el = sections[i];
      if(!el.id) continue;
      // Remove everything
      // ... and add it back.
      var new_el = el.cloneNode(false);
      var in_this_section = by_section[el.id];

      var limit = in_this_section.length;
      if(this.sectionLimits && this.sectionLimits[el.id])
        if(this.sectionLimits[el.id] < limit)
          limit = this.sectionLimits[el.id]

      // Must go first
      for(var j=0; j<limit; j++) {
        new_el.appendChild(in_this_section[j].el);
        // Also add to the top list.
        if(top_element && links.length > l.length && ! this.noLinkCopy) {
          if(top_element.firstChild) {
            top_element.insertBefore(in_this_section[j].el.cloneNode(true), top_element.lastChild);
          } else {
            top_element.appendChild(in_this_section[j].el.cloneNode(true));
          }
        }
      }
      el.parentNode.replaceChild(new_el, el);
      // Must go second
      if(this.overflowNotify && this.overflowNotify[el.id])
        if(limit < in_this_section.length) {
          var missing_nodes=[];
          for(var j=limit; j<in_this_section.length; j++)
            missing_nodes.push(in_this_section[j].el);
          this.overflowNotify[el.id](
            in_this_section.length - limit, 
            missing_nodes,
            new_el
          );
        } else {
          this.overflowNotify[el.id](0, [], new_el);
        }

    }
    for(var section_id in by_section) {
      var el = document.getElementById(section_id);
      if(el && this.onSectionEmpty && ! found_by_section[section_id]) {
        this.onSectionEmpty.call(el);
      } else if(el && this.onSectionNotEmpty && found_by_section[section_id]) {
        this.onSectionNotEmpty.call(el, found_by_section[section_id]);
      }
    }
  }

  



  this.sloppyMenuLinkSearch = function(i) {
    return this.menuLinkSearch(document.getElementById(i).value);
  }
  // This helps ensure that all local links do not get indexed the same way
  var this_dir;
  var this_host;
  {
    var here = location.href;
    this_dir=here.replace(/[^\/]*$/,"");
    var split_href=here.split("//", 2);
    split_href[1] = split_href[1].replace(/\/.*/,"");
    this_host = split_href.join("//");
  }
  // -
  this.anchorToKeywords = function(a) {
    var keywords = [];
    var target_href=(a.href||"").toLowerCase();
    target_href = _u_prototype.call(target_href, "stripSuffix", this_dir) || _u_prototype.call(target_href, "stripSuffix", this_host) || target_href;

    if(target_href) keywords.push(target_href.replace(/\.[a-z0-9]*(\?|#|$)/, ""));
    if(a.title) keywords.push(a.title.toLowerCase());
    for(var i=0; i<a.childNodes.length; i++) {
      var child = a.childNodes.item(i);
      if(child.data && ! child.tagName)
        keywords.push(child.data.toLowerCase());
    }
    return keywords;
  }

  this.scanSubsection = function(subsection_top, suggested_id) {
    if(!subsection_top.id) subsection_top.id = suggested_id;
    var keywords=[];
    var uid_found = subsection_top.id;
    if(subsection_top.getElementsByTagName) {
      var links_in_section = subsection_top.getElementsByTagName("A");
      for(var j=0; j<links_in_section.length; j++) {
        keywords = keywords.concat(this.anchorToKeywords(links_in_section.item(j)));
      }
    }
    if(this.moreKeywords) keywords = keywords.concat(this.moreKeywords(subsection_top));
    return {el: subsection_top.cloneNode(true), keywords: keywords, uid: uid_found};
  }

  




  this.cacheLinks = function() { // Cache links
    links = new Array();
    var tolt = this.topOfLinkTree();
    var sections = tolt ?
      _find_matching_elements(tolt, this.sectionDetect) :
      [] ;
    var counts_by_keyword={};
    var link_with_keyword_count=0;
    var el;
    for(var i=0; i<sections.length; i++) {
      el = sections[i];
      if(!el.id) el.id = "menu-search-"+top_of_link_tree_id+"-"+i;

      var subsections = [];
      if(this.replacementNodes && this.replacementNodes[el.id]) {
        subsections=this.replacementNodes[el.id];
      } else {  
        for(var j=0; j<el.childNodes.length; j++)
          subsections.push(el.childNodes.item(j));
      }

      for(var m=0;m<subsections.length; m++) {
        var section_data = this.scanSubsection(subsections[m], el.id+"-"+m);
        for(var j=0; j<section_data.keywords.length; j++) {
          var keyword = section_data.keywords[j];
          counts_by_keyword[keyword]=(counts_by_keyword[keyword]||0)+1;
        }
        section_data.section = el.id;
        if(section_data.keywords.length>0) link_with_keyword_count++;
        links.push(section_data);
      }
    }
    var overused_keywords={};
    for(var keyword in counts_by_keyword)
      if(counts_by_keyword[keyword]==link_with_keyword_count)
        overused_keywords[keyword]=1;
    for(var i=0; i<links.length; i++) {
      var keywords = links[i].keywords;
      var new_keywords = [];
      for(var j=0; j<keywords.length; j++)
        if(!overused_keywords[keywords[j]]) new_keywords.push(keywords[j]);
      links[i].keywords=new_keywords;
    }
  }
  





  this.prepare = function(search_box_id) {
    var existing_search_box = search_box_id.tagName ?
      search_box_id : 
      document.getElementById(search_box_id);
    if(!existing_search_box) return;
    var i = existing_search_box.cloneNode(true);
    i.value="";
    var t = this;
    i.onkeyup = function() {
      run_late_but_not_simultaneously(function() {t.menuLinkSearch(i.value.toLowerCase())}, 400);
    };
    i.onmouseup = function() {
      run_late_but_not_simultaneously(function() {t.sloppyMenuLinkSearch(i.id)}, 200);
    };

    var wrapper;
    if(this.existingClearButtonID) {
      document.getElementById(this.existingClearButtonID).onclick = function() {
        i.value="";
        t.menuLinkSearch("");
        return false;
      };
      this.wipeButton = document.getElementById(this.existingClearButtonID);
      wrapper = i;
    } else {
      this.wipeButton = 
        _mkel("a", {
            onclick: function() {
              i.value="";
              t.menuLinkSearch("");
              return false;
            }
          }, [this.clearLinkCaption], function(e) {e.style.marginLeft = "0.5em"}
        );
      



      wrapper = _mkel("span", "", [i, this.clearAfterLinks ? "" : this.wipeButton], function(e) {e.style.whiteSpace = "nowrap"; e.style.display="inline-block"; e.style.verticalAlign="baseline";});
    }
    existing_search_box.parentNode.replaceChild(wrapper, existing_search_box);
      
    this.cacheLinks();
    if(links.length==0 && !this.noLinksExpected) alert("No links found");
  }
} // End of wrapper function

