///////////////////////// Arrays /////////////////////////////////

// smart array elt deletion that tests to see if val is an integer.  
// If so, it deletes the index.  If not, it deletes the value
function array_delete(arr, val) {
  //debug('called array_delete');
  return (isNaN(val)) ? array_delete(arr, val) : array_delete_index(arr, val);
}

// helper function for deleting value from an array
function array_delete_val(arr, val) {
  //debug('called array_delete_val');
  var o = new Array;
  for (var i in arr) {
    if (arr[i] != val) o.push(arr[i]);
  }
  return o;
}

// helper function for deleting an index from an array
function array_delete_index(arr, id) {
  //debug('called array_delete_index on id: ' + id + ', array length: ' + arr.length);
  var o = new Array;
  for (var i in arr) {
    if (id != i) {
      o.push(arr[i]);
    } else { debug('- value deleted at  ' + id); }
  }
  return o;
}

// returns the number of occurrences of a value in an array
function array_contains(arr, val) {
  //debug('called array_contains');
  var o = 0;
  for (var i in arr) {
    if (arr[i] == val) o++;
  }
  return o;
}

/////////////////////////  Misc ///////////////////////////////////

// creates a random color
function random_color() {
  var a = '0123456789abc';
  var o = '';
  for (var i = 0; i < 3; i++) {
    o += a[(random_number(13 - 1))];
  }
  //o += rand(1000 - 1);
  //while (o.length < 3) { o = '0' + o; } 
  return '#' + o;
}

// http://www.codeave.com/javascript/code.asp?u_log=7031
function random_number(i) {
  return (Math.round((Math.random()*(i-1))+1))
}

///////////////////////// DOM  ///////////////////////////////////

function getChildrenByTagName(elt, tag) {
  debug('called getChildrenByTagName');
  var cs = elt.childNodes;
  var o  = new Array;
  for (var i = 0; i < cs.length; i++) {
    if ((cs[i].tagName == tag.toUpperCase()) | (cs[i].tagName == tag.toLowerCase())) {
      o.push(cs[i]);
      debug(' - getChildrenByTagName - added: ' + o);
    }
  }
  return o;
}

function getElementsByTagNameAndClass(tn, c, parent) {
  var elts = (parent) ? parent.getElementsByTagName(tn) : document.getElementsByTagName(tn);
  if (!elts) return;
  o = new Array;
  f = elts.length;  // length grows as new elements are added via debug, so fix ending value
  for (var i = 0; i < f; i++) {
    elt = elts[i];
    cs = elt.className.split(' ');
      if (array_contains(cs, c)) o.push(elt);
  }
  return o;
}

function getElementFromEvent(evt) {
  evt = (evt) ? evt : ((window.event) ? window.event : null);
  var elem;
  if (evt) 
    elem = (evt.target) ? evt.target : ((evt.srcElement) ? evt.srcElement : null);
  return elem;
}

// finds the next proper tag sibling of elt.  Matches tag to tagName if supplied
function find_sib(elt, tag) {
  debug('called find_sib on elt: ' + elt + ', with tag: ' + tag + ', nodeType = ' + elt.nodeType)
  var o = elt.nextSibling
  if (!o) { debug('sibling not found'); return null; }
  if (o.nodeType != 1) {
    // not an elemen node - keep moving
    return find_sib(o, tag);
  } else if (tag) {
    if ((o.tagName != tag.toUpperCase()) & (o.tagName != tag.toLowerCase())) {
      return find_sib(o, tag);
    } else {
      debug(' - find_sib returning ' + o); return o;
    }
  } else { debug(' - find_sib returning ' + o); return o; }
}

/////////////////////  Debug  /////////////////////////////

do_debug        = false;
debug_count     = 0;
debug_functions = new Object;

//////////////////////////////////////////////////////////
//  Instructions for using JS Debug:
//  - run debug_setup to create
//  - at the beginning of a function, call debug('called function_name')
//  --  This creates the class for the function
//  - for subsequent debug notes, call debug(' - function_name - message')
//////////////////////////////////////////////////////////

function debug_setup() {
  // create the uls to hold the output
  if (do_debug) {
    j  = document.createElement('div'); j.id  = 'jsOuterWrapper';
    jj = document.createElement('div'); jj.id  = 'jsInnerWrapper';
    j1 = document.createElement('ul'); j1.id  = 'jsdebug';
    j2 = document.createElement('ul'); j2.id  = 'jsdebugcheck';
    jb = document.createElement('input'); jb.type = 'button'; jb.onclick = debug_clear; jb.value = 'Clear Debug Output';
    jc = document.createElement('div'); jc.style.clear = 'both';
    jj.appendChild(j1); jj.appendChild(j2); jj.appendChild(jb); jj.appendChild(jc);
    j.appendChild(jj);
    // append the uls to the end of the body
    b = document.getElementsByTagName('body');
    b = b[0];
    b.appendChild(j);
  }
}

function debug_toggle(evt) {
  var evt = (evt) ? evt : ((window.event) ? window.event : null);
  if (evt) {
    var elem = (evt.target) ? evt.target : ((evt.srcElement) ? evt.srcElement : null);
    //elem.checked = !elem.checked;
    var val  = elem.value;
    //alert('debug_toggle works with value ' + val);
    var p    = document.getElementById('jsdebug');
    var elts = (p) ? getElementsByTagNameAndClass('li', val, p) : new Array;
    //alert('number of elements to show/hide: ' + elts.length + ' with display setting: ' + elem.checked);
    for (var i = 0; i < elts.length; i++) {
      elts[i].style.display = (elem.checked) ? '' : 'none';
    }
  }
}

function debug_clear() {
  document.getElementById('jsdebug').innerHTML = '';
  document.getElementById('jsdebugcheck').innerHTML = '';
  debug_count = 0;
  debug_functions = new Object;
}

// NOTE: debug will run into an infinite loop if any functions
// called within debug make recursive call to debug.  Duh.
function debug(message) {
  if (!do_debug) return;
  show = true;
  
  if (message.indexOf('called') == 0) {
    // first time function was called
    fname = message.replace(/^called\s(\w+).*$/, "$1");
    if (!debug_functions[fname]) {
    // create check boxes if they don't exist already
      debug_functions[fname] = random_color();
      debug_check = document.getElementById('jsdebugcheck');
      // create list element to hold check box
      f_li                       = document.createElement('li');
      f_li.style.backgroundColor = debug_functions[fname];
      f_li.style.color           = '#fff';
      f_li.className             = fname;
      // create check box
      f_chk         = document.createElement('input');
      f_chk.value   = fname;
      f_chk.checked = true;
      f_chk.type    = 'checkbox';
      f_chk.onclick = debug_toggle;
      // create link/text node
      f_txt           = document.createElement('a');
      f_txt.innerHTML = fname;
      f_txt.href      = '#';
      f_txt.className = 'debug_name';
      // place the check box and link into the list element and then add to the page
      f_li.appendChild(f_chk); f_li.appendChild(f_txt);
      debug_check.appendChild(f_li);
    }
  } else {
    // function has been called before - get function name
    fname = message.replace(/^[ -]*(\w+).*$/, "$1");
  }
  // hide if necessary
  var chks = document.getElementsByTagName('input');
  var chk  = null;
  for (var i = 0; i < chks.length; i++) {
    chk = ((chks[i].type == 'checkbox') & (chks[i].value == fname)) ? chks[i] : chk;
    if (chk) i = chks.length + 1; // only take first checkbox and quit looking
  }
   //alert(chk);
   show = (chk) ? chk.checked : true;
  // write the status
  status_box = document.getElementById('jsdebug');
  if (status_box) {
    msg = document.createElement('li');
    msg.innerHTML = debug_count + ': ' + message;
    msg.className = fname;
    msg.style.color = (debug_functions[fname]) ? debug_functions[fname] : '#000';
    msg.style.display = (show) ? '' : 'none';
    elt = status_box.getElementsByTagName('li');
    elt = elt[0];
    //status_box.appendChild(msg);
    status_box.insertBefore(msg, elt);
  }
  debug_count++;
}
