// JavaScript for Web-UI-library

function applyClassCode(obj, func) {
  /* Loop through elements on the page and turn them into useful members of our UI */
  if (!obj) {
    obj = document;
  }
  var elems = obj.getElementsByTagName('*');
  var i;
  for (i=0; i<elems.length; i++) {
    func(elems[i]);
  }
}

function classCode(obj) {
  /* Apply behaviour to an object based on its class, ID, and other attributes. */
  var inputs;
  var j;
  var btn;
  switch (obj.tagName.toLowerCase()) {
    case 'form':
      addEvent(obj, 'submit', clearEmptyInputs);
      if (hasClass(obj, 'disabled')) {
        inputs = obj.getElementsByTagName('input');
        for (j=0; j<inputs.length; j++) {
          inputs[j].disabled = true;
        }
        inputs = obj.getElementsByTagName('select');
        for (j=0; j<inputs.length; j++) {
          inputs[j].disabled = true;
        }
        inputs = obj.getElementsByTagName('textarea');
        for (j=0; j<inputs.length; j++) {
          inputs[j].disabled = true;
        }
      }
      break;
    case 'textarea':
      if (obj.getAttribute('maxlength')) {
        addEvent(obj, 'keypress', charsLeft);
        addEvent(obj, 'keyup', charsLeft);
        addEvent(obj, 'change', charsLeft);
        addEvent(obj, 'mousedown', charsLeft);
      }
      break;
    case 'input':
      break;
    case 'a':
      if (hasClass(obj, 'new-window')) {
        obj.target = '_blank';
        if (! obj.getAttribute('title')) {
          obj.title = 'New window will open';
        }
      }
      if (hasClass(obj, 'munged-email')) {
        addEvent(obj, 'mouseover', unmungeEmail);
      }
      if (hasClass(obj, 'submit')) {
        obj.href = '#_submit-link';
        addEvent(obj, 'click', submitLink);
      }
      break;
  }
  if (obj.getAttribute('changeappear')) {
    addEvent(obj, 'keyup', changeAppear);
    addEvent(obj, 'change', changeAppear);
    changeAppear(obj);
  }
  if (obj.getAttribute('changeenable')) {
    addEvent(obj, 'keyup', changeEnable);
    addEvent(obj, 'change', changeEnable);
    changeEnable(obj);
  }
  if (obj.getAttribute('conditionenable')) {
    addEvent(obj, 'keyup', conditionEnable);
    addEvent(obj, 'change', conditionEnable);
    conditionEnable(obj);
  }
  if (obj.getAttribute('updateclasses') || obj.getAttribute('updateids')) {
    autoUpdate(obj);
    if (obj.tagName.toLowerCase() == 'input') {
      addEvent(obj, 'keyup', autoUpdate);
      addEvent(obj, 'change', autoUpdate);
    }
    else {
      addEvent(obj, 'change', autoUpdate);
    }
  }
  if (obj.getAttribute('tooltip')) {
    addEvent(obj, 'mouseover', popupTooltip);
  }
  if (obj.getAttribute('eg')) {
    addEvent(obj, 'focus', removeEg);
    addEvent(obj, 'blur', addEg);
    addEg(obj);
  }
  if (obj.getAttribute('rollover')) {
    addEvent(obj, 'mouseover', rollover);
    addEvent(obj, 'mouseout', rollout);
  }
  if (hasClass(obj, 'focus')) {
    obj.focus();
    if (obj.getAttribute('eg')) {
      addEg(obj); // Because obj.focus would've removed the Eg.
      obj.select();
    }
  }
  if (hasClass(obj, 'flash')) {
    flashDiv(obj.id, 0);
  }
  if (hasClass(obj, 'toggle')) {
    obj.href = '#_toggle-link';
    addEvent(obj, 'click', toggle);
  }  
  /* Site-specific IDs & classes go after this */
  
}

/*
 * Nasty complicated Event-handling indirections to
 * make sure IE works the same as Fx
 */

function addGlobalEvent(obj, eventName, func) {
  // This has to be first, because Opera has both but only attachEvent works how we want.
  if (obj.attachEvent) {
    obj.attachEvent("on" + eventName, func);
  } else if (obj.addEventListener) {
    obj.addEventListener(eventName, func, true);
  }
}

function removeGlobalEvent(obj, eventName, func) {
  if (document.attachEvent) { // See above
    obj.detachEvent("on" + eventName, func);
  } else if (document.addEventListener) {
    obj.removeEventListener(eventName, func, true);
  }
}

function eventTarget(e) {
  var obj;
  if (document.attachEvent) { // See above
    obj = e.srcElement;
  } else if (document.addEventListener) {
    obj = e.target;
  }
  return obj;
}

function addEvent(topelement, eventName, func, obj) {
  if (!obj) obj = topelement;
  addGlobalEvent(obj, eventName, eventHandler);
  if (!obj.eventFunctions) {
    obj.eventFunctions = {};
  }
  obj.eventFunctions[eventName] = [func, topelement];
}

function addDeepEvent(topelement, eventName, func) {
  var elems = topelement.getElementsByTagName('*');
  for (var i=0; i<elems.length; i++) {
    addEvent(topelement, eventName, func, elems[i]);
  }
}

function removeEvent(obj, eventName, func) {
  removeGlobalEvent(obj, eventName, eventHandler);
  obj.eventFunctions[eventName] = null;
}

function eventHandler(e) {
  var obj = eventTarget(e);
  var eventName = e.type;
  return fireEvent(obj, eventName, e);
}

function fireEvent(obj, eventName, e) {
  if (!obj.eventFunctions || !obj.eventFunctions[eventName]) {
    return false;
  }
  func = obj.eventFunctions[eventName][0];
  topelement = obj.eventFunctions[eventName][1];
  return func(topelement, e);
}


/*
 * Class-name utility functions
 */

function getClasses(obj) {
  return obj.className.split(' ');
}

function hasClass(obj, cName) {
  /* Thanks to Fred Bird from http://fredbird.org */
  return new RegExp('\\b'+cName+'\\b').test(obj.className);
}

function removeClass(obj, cName) {
  /* Thanks to Fred Bird from http://fredbird.org */
  if (!hasClass(obj, cName)) {
    return false;
  }
  var rep = obj.className.match(' '+cName) ? ' '+cName : cName;
  obj.className = obj.className.replace(rep, '');
  return true;
}

function addClass(obj, cName) {
  /* Thanks to Fred Bird from http://fredbird.org */
  if (!hasClass(obj,cName)) {
    obj.className += obj.className ? ' '+cName : cName;
  }
  return true;
}

function getElementsByClassName(className, container, tag) {
  /* Thanks to Fred Bird from http://fredbird.org */
  // default container to document
  container = container || document;
  // default tag to *
  tag = tag || '*';
  // listing container descendants
  var all = container.all || container.getElementsByTagName(tag);
  var found = [];
  // searching for targets
  var f;
  for (f=0; f<all.length; f++) {
    var el = all[f];
    if (hasClass(all[f], className)) {
      found.push(all[f]);
    }
  }
  return found;
}

function computedStyle(obj) {
  if (window.getComputedStyle) {
    return window.getComputedStyle(obj, null);
  }
  else {
    return obj.currentStyle;
  }
}

function contains(node, descendant) {
  var ret;
  try {
    if (!descendant) ret = false;
    else if (node.contains) ret = node.contains(descendant);
    else if (descendant == document.body) ret = false;
    else if (descendant.parentNode == node) ret = true;
    else ret = contains(node, descendant.parentNode);
  }
  catch (err) {
    ret = false;
  }
  return ret;
}

function ancestor(node, target, className) {
  /* find ancestor with tagName target
   * and class className if set
   */
  if ((target == '*') ||
      (node.parentNode.tagName.toLowerCase() == target.toLowerCase())) {
    if (className && hasClass(node.parentNode, className)) return node.parentNode;
  }
  if (node.parentNode == document.body) {
    return null;
  }
  return ancestor(node.parentNode, target, className); // recurse
}


/*
 * AJAX
 */

var ajaxTimeout = null;
var ajaxseq = 0;

function ajax(url, data, handler) {
  var req = false;
  ajaxseq += 1;

  if (window.XMLHttpRequest) req = new XMLHttpRequest(); // Firefox, Safari, Opera
  else if (window.ActiveXObject) {  // Internet Explorer
    try {
      req = new ActiveXObject("Msxml2.XMLHTTP");
    }
    catch (err1) {
      try {
        req = new ActiveXObject("Microsoft.XMLHTTP");
      }
      catch (err2) {}
    }
  }
  if (!req) {
    throw('Browser does not support Ajax!');
  }
  
  if (handler != null) {
    req.handler = handler;
    req.onreadystatechange = function() { processreq(req); };
    async = true;
  }
  else async = false;
  
  req.ajaxseq = ajaxseq;
  
  if (data) {
    req.open('POST', url, async);
    var datas = [];
    var i = 0;
    var idx;
    for (idx in data) {
      datas[i] = escape(idx) + '=' + escape(data[idx]);
      i++;
    }
    data = datas.join('&');
    req.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
    req.send(data);
  }
  else {
    req.open('GET', url, async);
    req.send(null);
  }
  ajaxTimeout = setTimeout(function() { timeoutHandler(req); }, 7000);
  if (async) return ajaxseq;
  else return processreq(req);
}

function unjsonize(str) {
  var dict;
  eval("dict = " + str);
  return dict;
}

function processreq(req) {
  if (req.readyState != 4) return;
  
  clearTimeout(ajaxTimeout);
  ajaxTimeout = null;
  var data;
  var stat;

  try {
    // Fx throws an exception when you access req.status for certain errors
    stat = req.status;
  }
  catch (err1) {
    stat = 500;
  }

  if (stat == 200 || stat == 403) {
    try {
      data = unjsonize(req.responseText);
    }
    catch (err2) {
      // crazy data, or possibly a formKey error
      stat = 777;
    }
  }
  if (stat != 777) {
    if (req.handler) req.handler(data, stat, req);
    else return [data, stat, req];
  }
  else throw('Ajax error: ' + req.responseText);
}

function timeoutHandler(req) {
  req.abort();
}

/*
 * Miscellaneous functions
 */

var currentTooltip = null;
var currentKeepVisible = null;

function popupTooltip(obj) {
  var tooltip = document.getElementById(obj.getAttribute('tooltip'));
  currentTooltip = tooltip;
  var keepvisible;
  var vId = tooltip.getAttribute('keepvisible');
  if (!vId) {
    keepvisible = obj;
  } else {
    keepvisible = document.getElementById(vId);
  }
  currentKeepVisible = keepvisible;
  addGlobalEvent(keepvisible, 'mouseout', hideTooltip);
  tooltip.style.display = 'block';
}

function hideTooltip(e) {
  var relatedTarget;
  if (e.relatedTarget) {
    relatedTarget = e.relatedTarget;
  } else if (e.toElement) {
    relatedTarget = e.toElement;
  }
  if (contains(currentKeepVisible, relatedTarget)) {
    return false;
  }
  if (relatedTarget == currentKeepVisible) {
    return false;
  }
  currentTooltip.style.display = 'none';
  removeGlobalEvent(currentKeepVisible, 'mouseout', hideTooltip);
  currentTooltip = null;
  currentKeepVisible = null;
  return true;
}

var preloadedImages = {};

function preload(filename) {
  preloadedImages[filename] = new Image();
  preloadedImages[filename].src = filename;
}

function clearEmptyInputs(form) {
  /* Empty out all elements that have example text in them */
  var elems = form.getElementsByTagName('*');
  var i;
  for (i=0; i<elems.length; i++) {
    if (hasClass(elems[i], 'empty') && elems[i].getAttribute('eg')) {
      elems[i].value = '';
      removeClass(elems[i], 'empty');
    }
  }
}

function removeEg(input) {
  if (hasClass(input, 'empty')) {
    if (hasClass(input, 'focus')) {         // Fix for IE to make sure the eg text doesn't go away the first time.
      removeClass(input, 'focus');          // Remove the 'focus' class, so we don't ever try to focus it again.
      addEvent(input, 'keydown', removeEg); // Make sure the text is not marked as 'empty' when they start typing
    } else {
      input.value = '';
    }
    removeClass(input, 'empty');
  }
}


function addEg(input) {
  if ((input.value == input.getAttribute('eg')) || (input.value === '')) {
    addClass(input, 'empty');
    input.value = input.getAttribute('eg');
  }
}


function autoUpdate(changed) {
  var updateids = changed.getAttribute('updateids');
  if (updateids) updateids = updateids.split(' ');
  var updateclasses = changed.getAttribute('updateclasses');
  if (updateclasses) updateclasses = updateclasses.split(' ');
  
  var toupdate = new Array();
  for (var i in updateids) {
    var updateid = updateids[i];
    toupdate[toupdate.length] = document.getElementById(updateid);
  }
  for (var i in updateclasses) {
    var className = updateclasses[i];
    var elems = getElementsByClassName(className, changed);
    for (var j in elems) {
      toupdate[toupdate.length] = elems[j];
    }
  }
  
  var val;
  if (changed.getAttribute('val')) val = changed.getAttribute('val');
  else val = changed.value;
  if (val === '') {
    for (var i in toupdate) {
      var autodefault = elem.getAttribute('default');
      var elem = toupdate[i];
      elem.firstChild.nodeValue = autodefault;
    }
  }
  else {
    for (var i in toupdate) {
      var elem = toupdate[i];
      var updatecode = elem.getAttribute('updatecode');
      var updatefunc = elem.getAttribute('updatefunc');

      var newvalue = val;
      if (updatecode) {
        updatecode = updatecode.replace(/%v/g, 'newvalue');
        updatecode = updatecode.replace(/%e.[a-z0-9]*/g, 'elem.getAttribute("$1")');
        updatecode = updatecode.replace(/%c.[a-z0-9]*/g, 'changed.getAttribute("$1")');
        updatecode = updatecode.replace(/%e/g, 'elem');
        updatecode = updatecode.replace(/%c/g, 'changed');
        var newvalue = eval(updatecode);
      }
      elem.firstChild.nodeValue = newvalue;
      
      if (updatefunc) eval(updatefunc + '(changed, elem);');
    }
  }
}


function changeAppear(input) {
  var div = document.getElementById(input.getAttribute('changeappear'));
  if (div == null) return;
  var appearvalue = input.getAttribute('appearvalue');
  if (input.getAttribute('val')) val = input.getAttribute('val');
  else val = input.value;
  
  if ((input.type == 'checkbox') || (input.type == 'radio')) {
    if (appearvalue) display = !!input.checked;
    else display = !input.checked;
  }
  else {
    if (val == appearvalue) display = true;
    else display = false;
  }
  
  if (display) div.style.display = 'block'; /* This should really be
                                             * div.style.display = 'whatever-it-was-before'; */
  else div.style.display = 'none';
}

function changeEnable(input) {
  var div = document.getElementById(input.getAttribute('changeenable'));
  if (div == null) return;
  var enablevalue = input.getAttribute('enablevalue');
  if (input.getAttribute('val')) val = input.getAttribute('val');
  else val = input.value;

  if ((input.type == 'checkbox') || (input.type == 'radio')) {
    if (enablevalue) enable = !!input.checked;
    else enable = !input.checked;
  }
  else {
    if (val == enablevalue) enable = true;
    else enable = false;
  }

  if (enable) removeClass(div, 'disabled');
  else addClass(div, 'disabled');
}

function conditionEnable(input) {
  var div = document.getElementById(input.getAttribute('conditionenable'));
  if (div == null) return;
  var condition = input.getAttribute('condition');
  if (input.getAttribute('val')) val = input.getAttribute('val');
  else val = input.value;

  condition = condition.replace(/%v/g, 'val');
  if (eval(condition)) enable = true;
  else enable = false;

  if (enable) {
    removeClass(div, 'disabled');
    div.disabled = false;
  }
  else {
    addClass(div, 'disabled');
    div.disabled = true;
  }
}

function flashDiv(id, state) {
  var div = document.getElementById(id);
  if (state == 6) {
    return;
  }
  if (state%2 === 0) {
    removeClass(div, 'flash-off');
    removeClass(div, 'flash');
    addClass(div, 'flash');
  } else {
    removeClass(div, 'flash');
    addClass(div, 'flash-off');
  }
  state += 1;
  var code = 'flashDiv("' + id + '", ' + state + ');';
  setTimeout(code, 250);
}

function unmungeEmail(anchor) {
  if (anchor.href.indexOf('mailto:') >= 0) {
    return;
  }
  var text = anchor.firstChild.nodeValue;
  var titleWords = anchor.title.split(' ');
  var email = titleWords[titleWords.length-1];
  if (text.indexOf('@') >= 0) {
    email = text.replace(/\.\.\./, email);
  } else {
    email = email.replace(/\[aht\]/, '@');
  }
  anchor.href = 'mailto:' + email;
  anchor.title = 'Click to email';
  // I cannot fathom why IE changes nodeValue without this, but it seems to!
  anchor.firstChild.nodeValue = email;
}

function toggle(anchor, e) {
  var show = document.getElementById(anchor.getAttribute('showid'));
  var hide = document.getElementById(anchor.getAttribute('hideid'));

  show.style.display = 'block';
  hide.style.display = 'none';

  var focusid = anchor.getAttribute('focusid');
  if (focusid) {
    var input = document.getElementById(focusid);
    input.focus();
  }
}

function submitLink(obj) {
  if (obj.tagName.toLowerCase() != 'a') {
    return;
  }
  var form = ancestor(obj, 'form');
  if (form) {
    form.submit();
  }
}

function rollover(obj, postfix) {
  var img;
  if (obj.tagName.toLowerCase() == 'img') {
    img = obj;
  } else {
    img = obj.getElementsByTagName('img')[0];
  }

  var altsrc = img.getAttribute('rollover');
  if (!img.origsrc) {
    img.origsrc = img.src;
  }
  if (!altsrc) {
    var parts = img.origsrc.split('.');
    altsrc = parts[0];
    var j;
    for (j = 1; j < (parts.length - 1); j++) {
      altsrc = altsrc + '.' + parts[j];
    }
    altsrc = altsrc + postfix + '.' + parts[parts.length - 1];
  }
  img.src = altsrc;
}

function rollout(obj) {
  var img;
  if (obj.tagName.toLowerCase() == 'img') {
    img = obj;
  } else {
    img = obj.getElementsByTagName('img')[0];
  }

  var origsrc = img.origsrc;
  img.src = origsrc;
}

function charsLeft(textarea) {
  var maxlength = parseInt(textarea.getAttribute('maxlength'), 10);
  var countdown = document.getElementById(textarea.getAttribute('countdown'));
  textarea.value = textarea.value.substring(0, maxlength);
  countdown.firstChild.nodeValue = maxlength - textarea.value.length;
}

