// Includes some standard javascript libraries.

/*
Crossbrowser HTMLElement Prototyping
Copyright (C) 2005  Jason Davis, http://www.browserland.org
Additional thanks to Brothercake, http://www.brothercake.com

This code is licensed under the LGPL:
	http://www.gnu.org/licenses/lgpl.html
*/

if (navigator.vendor == "Apple Computer, Inc." || navigator.vendor == "KDE") { // WebCore/KHTML
  (function(c) {
    for (var i in c)
      window["HTML" + i + "Element"] = document.createElement(c[ i ]).constructor;
  })({Html: "html", Head: "head", Link: "link", Title: "title", Meta: "meta",
      Base: "base", IsIndex: "isindex", Style: "style", Body: "body", Form: "form",
      Select: "select", OptGroup: "optgroup", Option: "option", Input: "input",
      TextArea: "textarea", Button: "button", Label: "label", FieldSet: "fieldset",
      Legend: "legend", UList: "ul", OList: "ol", DList: "dl", Directory: "dir",
      Menu: "menu", LI: "li", Div: "div", Paragraph: "p", Heading: "h1", Quote: "q",
      Pre: "pre", BR: "br", BaseFont: "basefont", Font: "font", HR: "hr", Mod: "ins",
      Anchor: "a", Image: "img", Object: "object", Param: "param", Applet: "applet",
      Map: "map", Area: "area", Script: "script", Table: "table", TableCaption: "caption",
      TableCol: "col", TableSection: "tbody", TableRow: "tr", TableCell: "td",
      FrameSet: "frameset", Frame: "frame", IFrame: "iframe"
  });
  
  function HTMLElement() {}
  HTMLElement.prototype     = HTMLHtmlElement.__proto__.__proto__;	
  var HTMLDocument          = document.constructor;
  var Document              = document.constructor;
  var HTMLCollection        = document.links.constructor;
  var HTMLOptionsCollection = document.createElement("select").options.constructor;
  var Text                  = document.createTextNode("").constructor;
  var Node                  = Text;
}

/* End of HTMLElement Prototyping */


document.getElementsByClassName = function(class_name) {
  var elements = [];
  var myclass = new RegExp('\\b' + class_name + '\\b');
  var elem = this.getElementsByTagName('*');
  for (var i = 0; i < elem.length; i++) {
    var classes = elem[i].className;
    if (myclass.test(classes)) elements.push(elem[i]);
  }
  return elements;
}

function hasClassName(element, class_name) {
  if (element.className.indexOf(class_name) > -1)
    return true;
  return false;
}

Form.getElement = function(form, element_name) {
  form = $(form);
  var elements = Form.getInputs(form, null, element_name);
  return (elements.length > 0) ? elements[0] : null;
}

Form.focusElement = function(form, element_name) {
  element = Form.getElement(form, element_name);
  // alert('focusElement ' + element_name + ' ' + element);
  if (element != null) {
    element.focus();
  }
}

Form.focusFirstElement = function(form) {
  // alert('focusFirst ' + form + " " + $(form));
  element = Form.findFirstElement(form);
  if (element != null) {
    Field.focus(element);
  }
}

Form.activateFirstElement = function(form) {
  element = Form.findFirstElement(form);
  if (element != null) {
    Field.activate(element);
  }
}

function submitForm(form_name) {
  var form = $(form_name); /* document.forms[form_name]; */
  if (form.onsubmit == null || form.onsubmit()) {
    form.submit();
  }
}

function switchVisible(a, b) {
  hideElementsWithClassName(a);
  showElementsWithClassName(b, 'block');
}

function showElementsWithClassName(className, style) {
  var elts = document.getElementsByClassName(className);
  for (i = 0; i < elts.length; i++)
    if (!hasClassName(elts[i], 'hidden')) 
      elts[i].style.display = style;
}

function hideElementsWithClassName(className) {
 var elts = document.getElementsByClassName(className);
  for (i = 0; i < elts.length; i++)
    elts[i].style.display = 'none';
}

// This counts the number of forms currently being editted.  If you attempt to
// navigate away from a page while this is positive, you will get a warning,
// if the browser supports it.
var forms_in_edit_mode = 0;
// This is an array of toggles that should be counted by forms_in_edit_mode:
var counted_form_toggles = new Object();
var one_form_allowed = false;

function countToggle(toggle_id) {
  counted_form_toggles[toggle_id] = true;
}

function viewMode(toggle_id) {
  switchVisible(toggle_id + '_edit', toggle_id + '_view');
  if (counted_form_toggles[toggle_id]) {
    forms_in_edit_mode--;
    // alert('view ' + toggle_id + ' ' + forms_in_edit_mode);
  }
}

function editMode(toggle_id) {
  switchVisible(toggle_id + '_view', toggle_id + '_edit');
  if (counted_form_toggles[toggle_id]) {
    forms_in_edit_mode++;
    // alert('edit ' + toggle_id + ' ' + forms_in_edit_mode);
  }
}

// Call this before submitting a form that is revealed by a ViewEdit toggle
// and which has normal (non-ajax) submission.  It will suppress the complaint
// that a form is being editted as you try to leave the page, unless there is
// another form being editted.
function allowFormSubmit() {
  one_form_allowed = true;
  return true;
}

function confirmExit() {
  min = (one_form_allowed) ? 1 : 0;
  if (forms_in_edit_mode <= min) {
    return;
    one_form_allowed = false;
  }
  if (forms_in_edit_mode > 1) {
    return 'You are currently editing ' + forms_in_edit_mode + ' forms';
  } else if (forms_in_edit_mode == 1) {
    return 'You are currently editing a form';
  }
}

// function extendForm(template_id, insertion_id, extension) {
//   var new_node = $(template_id).cloneNode(true);
//   new_node.id = '';
//   new_node.style.display = 'block';
//   var new_fields = new_node.getElementsByTagName('*');
//   for (var i = 0; i < new_fields.length; ++i) {
//     var field_name = new_fields[i].name;
//     if (field_name) {
//       new_fields[i].name = field_name + extension;
//     }
//   }
//   var insertion_point = $(insertion_id);
//   insertion_point.parentNode.insertBefore(new_node, insertion_point);
// }

// Returns false so you can return it in a form's onsubmit in place of
// submitting the form.
var ajaxSubmitForm = function(form_name, url, callback) {
  //alert(form_name + " = " + Form.serialize(form_name));
  var req = new Ajax.Request(url, {
    method: 'post',
    parameters: Form.serialize(form_name),
    onComplete: callback
  });
  startWaiting();
  return false;
}

var hasField = function(document, name) {
  return (document.getElementsByTagName(name).length > 0);
}

var fieldValue = function(document, name) {
  var elements = document.getElementsByTagName(name);
  if (elements.length == 0) {
    return null;
  }
  element = elements[0];
  // If the value is long, it may be split up among multiple children.
  // normalize() will apparently put them back together, but may then be
  // truncated on Opera.  Is there any chance the children will be out of
  // order?
  // element.normalize();
  var value = '';
  for (var i = 0; i < element.childNodes.length; i++) {
    value += element.childNodes[i].nodeValue;
  }
  return value;
}

var startWaiting = function() {
  document.body.style.cursor = 'wait';
}

var stopWaiting = function() {
  document.body.style.cursor = 'auto';
}

var handleAjaxErrors = function(request) {
  stopWaiting();
  var xml_doc = request.responseXML;
  if (hasField(xml_doc, 'error')) {
    //$('errors').innerHTML = fieldValue(xml_doc, 'error');
    alert(fieldValue(xml_doc, 'error'));
    return true;
  } else {
    //$('errors').innerHTML = '';
    return false;
  }
}


// This sorts the children of an element based on their attributes.  The
// principle method is sort, which takes a container and an array of attribute
// names.  The first is the primary key, the second is the
// next-most-important, and so on.  If an attribute name is preceded by -, the
// sort is done in reverse order.
var ChildSorter = {
  numberRE: /^ *(\+|-)?((\d+(\.\d*)?)|((\d*\.)?\d+)) *$/,

  isNumber: function(v) {
    return ChildSorter.numberRE.test(v);
  },

  valueCompare: function(a, b) {
    if (ChildSorter.isNumber(a) && ChildSorter.isNumber(b)) {
      a = parseFloat(a);
      b = parseFloat(b);
    }
    return (a < b) ? -1 : (a > b) ? 1 : 0;
  },

  keyCompare: function(keys) {
    return function(a, b) {
      for (var i = 0; i < keys.length; i++) {
        key = keys[i];
        if (key[0] == '-') {
          key = key.substr(1);
          direction = -1;
        } else {
          direction = 1;
        }
        var av = (a.getAttribute) ? a.getAttribute(key) : null;
        var bv = (b.getAttribute) ? b.getAttribute(key) : null;
        // If one has a null value, it always comes first even if the sort is
        // reversed.
        if (av == null) {
          if (bv == null) {
            continue;
          } else {
            return -1;
          }
        } else if (bv == null) {
          return 1;
        }

        c = ChildSorter.valueCompare(av, bv);
        // alert('[' + av + '] [' + bv + '] ' + c);
        if (c != 0) return direction * c;
      }
      return ChildSorter.valueCompare(a.innerHTML, b.innerHTML);
    }
  },

  sort: function(container, keys) {
    var container = $(container);
    var children = new Array();
    var kids = container.childNodes;
    for (var i = kids.length - 1; i >= 0; i--) {
      children.push(kids[i]);
      container.removeChild(kids[i]);
    }
    children.sort(ChildSorter.keyCompare(keys));
    children.each(function(child, i) {
      container.appendChild(child);
    });
  }
}
