// Very general useful things that help you manipulate the HTML DOM.
// Includes some overlap of prototype.js functionality.
// Assumes prototype.js has been run already.


// use this here.
Dependencies.declare('dom');
Dependencies.dependsOn('prototype');


// Our own extensions to Element, via Prototype.
var WMElementMethods = {
   clearChildren: function(element) {
      element = $(element);
      while (element.firstChild) {
         element.removeChild(element.firstChild);
      }
   },

   addContent: function(element, content) {
      element = $(element);
      if (typeof content == "string") {
         element.appendChild(element.document.createTextNode(content));
      }
      else {
         element.appendChild(element.document.cloneNode(content));
      }
   },

   findChild: function(element, tagName, className) {
      element = $(element);
      var child = element.firstChild;
      while (child != null) {
         if ( (!tagName || child.tagName == tagName)
             && (!className || child.hasClassName(className))) {
            return child;
         }
         child = child.nextSibling;
      }
      return null;
   }
};

Element.addMethods(WMElementMethods);

// window-level accessor for prototype method, handles "null" case
function addClassName(element, className) {
   if (element) {
      $(element).addClassName(className);
   }
}

// window-level accessor for prototype method, handles "null" case
function findElement(parent, tagName, className) {

   if (!parent) {
      return null;
   }
   return $(parent).findChild(tagName, className);
}

//  window-level accessor for prototype method, handles "null" case
function hasClassName(element, className) {
  return element && $(element).hasClassName(className);
}

// This is the preferred method for scheduling initialization and
// cleanup functions to run.
//
// The first argument should be a function to be run after the onload
// event fires. Components that use window elements should do setup
// in this function. Functions will be executed in the order that they
// were scheduled.
//
// The second argument should be a function that cleans up the effects
// of the component. (Free memory by nulling variable references, close
// server connections, etc.) These functions will be executed in the
// reverse of the order that they were pushed, so that the last component
// initialized is the first component cleaned. If no cleanup is required,
// the second argument can be left off.
window.schedule = function(onloadFunction, onunloadFunction) {
  if (onloadFunction) {
    this.onload_queue.push(onloadFunction);
  }
  if (onunloadFunction) {
    this.onunload_stack.push(onunloadFunction);
  }
};

// use this method for scheduling events to run after the DOM has loaded,
// without necessarily waiting for all of the images to show up.
window.scheduleOnDomLoad = function(ondomloadFunction) {
  if (ondomloadFunction) {
    this.ondomload_queue.push(ondomloadFunction);
  }
};

// Provide onload queue for the window.
// Use window.schedule(foo) instead of window.onload = foo
// Queue is FIFO: functions will be executed in the order that they
// were pushed.
window.onload_queue = new Array();

// new Prototype-esqe mechanism
function __handle_onload_queue(event) {
   if (!this.onload_queue) {
      alert("onload queue for window " + window.location.href + " is null/false.");
   }
   // else if (this.onload_queue.length == 0) {
   //    this.alert("onload queue for window " + window.location.href + " is empty, nothing to do");
   // }
   else {
      // this.alert("handling onload queue for window "
      //         + window.location.href + ", "
      //         + this.onload_queue.length + " items.");
      for (var i = 0; i < this.onload_queue.length; i++) {
          this.onload_queue[i](window, event);
          this.onload_queue[i] = null;
      }
   }
}

Event.observe(window, "load", __handle_onload_queue.bindAsEventListener(window));

// load queue called when dom has finished loading
window.ondomload_queue = new Array();

// new Prototype-esqe mechanism
function __handle_ondomload_queue(event) {
   if (!this.ondomload_queue) {
      alert("onDomLoad queue for window " + window.location.href + " is null/false.");
   }
   // else if (this.ondomload_queue.length == 0) {
   //    this.alert("onDomLoad queue for window " + window.location.href + " is empty, nothing to do");
   // }
   else {
      // this.alert("handling onDomLoad queue for window "
      //         + window.location.href + ", "
      //         + this.ondomload_queue.length + " items.");
      for (var i = 0; i < this.ondomload_queue.length; i++) {
          this.ondomload_queue[i](window, event);
          this.ondomload_queue[i] = null;
      }
   }
}
window.document.observe("dom:loaded", __handle_ondomload_queue.bindAsEventListener(window));


// Provide onunload queue for the window. Put all object cleanup here
// (most importantly, detach all circular references you created,
// or primitive browsers (including IE) will leak memory).
// Use window.schedule(null, foo) instead of window.onunload = foo
// The stack is LIFO: functions will be executed in the reverse of the
// order that they were pushed, so that effects from onload are
// unraveled correctly.
window.onunload_stack = new Array();
function __handle_onunload_stack(event) {
   for (var i = 0; i < this.onunload_stack.length; i++) {
       this.onunload_stack[i](event);
       this.onunload_stack[i] = null;
   }
}
Event.observe(window, "unload", __handle_onunload_stack.bindAsEventListener(window));

/**
 * Get the closest ancestor node of htmlElement which expresses the
 * class className. Returns null if there is no such ancestor.
 * Example:
 * <div class="example">
 *  <div class="container">
 *   <div class="wrapper">
 *    <div class="d" onclick="flash(classedElementContaining(this, "container"));">click</div>
 *   </div>
 *  </div>
 * </div>
 * ... this would flash the "container"-classed div.
 */
function classedElementContaining(htmlElement, className) {

   if (!htmlElement) {
      return null;
   }

   htmlElement = $(htmlElement);
   
   if (htmlElement.hasClassName(className)) {
      return htmlElement;
   }
   return classedElementContaining(htmlElement.parentNode, className);
}

// I don't know what this function used to do, but it seems
// to have vanished, so I'm quickly rewriting it here:
function getXMLHttpRequest() {
  var req = null;
  try {
      req = new ActiveXObject("Msxml2.XMLHTTP");
  } catch (exc) {
      try {
          req = new ActiveXObject("Microsoft.XMLHTTP");
      } catch (exc2) {
          req = null;
      }
  }
  if (req == null && typeof XMLHttpRequest != "undefined") {
      req = new XMLHttpRequest();
  }
  return req;
}

//this code comes from http://xkr.us/articles/dom/iframe-document/
function getDocumentInIframe(iframeElement) {
  var oDoc = (iframeElement.contentWindow || iframeElement.contentDocument);
  if (oDoc.document) {
    oDoc = oDoc.document;
  }
  return oDoc;
}

// document fields tend to be read-only, so we use this low-tech method
// to copy an entire document into another window. It's similar to this:
// destinationFrame.location = sourceDocument.location
// with the important distinction that the document is completely rendered
// before the function returns.
//
// I'm not sure if this is to browser DOM spec, but when we document.write
// below, it clears the contents of the document before writing the new
// strings to it. Both IE6 and FF1.5 do this, even if an empty string is
// written. I previously had a call to set document.location = "about:blank" ,
// but it isn't necessary as long as the document.write call clears the
// document. Untested in FF1.0.
function copyDocumentElementToFrame(sourceDocument, destinationFrame) {
  var destinationDocument = getDocumentInIframe(destinationFrame);
  destinationDocument.open();
  destinationDocument.write("<HTML>");
  destinationDocument.write(sourceDocument.documentElement.innerHTML);
  destinationDocument.write("</HTML>");
  destinationDocument.close();


  // It would be nice if we could just call:
  /*
  var destHtmlElement = destinationDocument.documentElement;
  destHtmlElement.clearChildren();
  destHtmlElement.importElements(sourceDocument.documentElement.childNodes);
  */
  // .. but IE doesnt' support importElement yet. ("Yet" here is Jan 2009;
  // I'm referring to IE6 and IE7, as IE8 isn't currently stable enough to
  // check your webmail, let alone run Finder.)
  //
  //  Stupid IE.
}


