1 /* A few useful utility functions. */
 
   3 var internetExplorer = document.selection && window.ActiveXObject && /MSIE/.test(navigator.userAgent);
 
   4 var webkit = /AppleWebKit/.test(navigator.userAgent);
 
   5 var safari = /Apple Computers, Inc/.test(navigator.vendor);
 
   7 // Capture a method on an object.
 
   8 function method(obj, name) {
 
   9   return function() {obj[name].apply(obj, arguments);};
 
  12 // The value used to signal the end of a sequence in iterators.
 
  13 var StopIteration = {toString: function() {return "StopIteration"}};
 
  15 // Apply a function to each element in a sequence.
 
  16 function forEach(iter, f) {
 
  18     try {while (true) f(iter.next());}
 
  19     catch (e) {if (e != StopIteration) throw e;}
 
  22     for (var i = 0; i < iter.length; i++)
 
  27 // Map a function over a sequence, producing an array of results.
 
  28 function map(iter, f) {
 
  30   forEach(iter, function(val) {accum.push(f(val));});
 
  34 // Create a predicate function that tests a string againsts a given
 
  35 // regular expression. No longer used but might be used by 3rd party
 
  37 function matcher(regexp){
 
  38   return function(value){return regexp.test(value);};
 
  41 // Test whether a DOM node has a certain CSS class. Much faster than
 
  42 // the MochiKit equivalent, for some reason.
 
  43 function hasClass(element, className){
 
  44   var classes = element.className;
 
  45   return classes && new RegExp("(^| )" + className + "($| )").test(classes);
 
  48 // Insert a DOM node after another node.
 
  49 function insertAfter(newNode, oldNode) {
 
  50   var parent = oldNode.parentNode;
 
  51   parent.insertBefore(newNode, oldNode.nextSibling);
 
  55 function removeElement(node) {
 
  57     node.parentNode.removeChild(node);
 
  60 function clearElement(node) {
 
  61   while (node.firstChild)
 
  62     node.removeChild(node.firstChild);
 
  65 // Check whether a node is contained in another one.
 
  66 function isAncestor(node, child) {
 
  67   while (child = child.parentNode) {
 
  74 // The non-breaking space character.
 
  76 var matching = {"{": "}", "[": "]", "(": ")",
 
  77                 "}": "{", "]": "[", ")": "("};
 
  79 // Standardize a few unportable event properties.
 
  80 function normalizeEvent(event) {
 
  81   if (!event.stopPropagation) {
 
  82     event.stopPropagation = function() {this.cancelBubble = true;};
 
  83     event.preventDefault = function() {this.returnValue = false;};
 
  86     event.stop = function() {
 
  87       this.stopPropagation();
 
  88       this.preventDefault();
 
  92   if (event.type == "keypress") {
 
  93     event.code = (event.charCode == null) ? event.keyCode : event.charCode;
 
  94     event.character = String.fromCharCode(event.code);
 
  99 // Portably register event handlers.
 
 100 function addEventHandler(node, type, handler, removeFunc) {
 
 101   function wrapHandler(event) {
 
 102     handler(normalizeEvent(event || window.event));
 
 104   if (typeof node.addEventListener == "function") {
 
 105     node.addEventListener(type, wrapHandler, false);
 
 106     if (removeFunc) return function() {node.removeEventListener(type, wrapHandler, false);};
 
 109     node.attachEvent("on" + type, wrapHandler);
 
 110     if (removeFunc) return function() {node.detachEvent("on" + type, wrapHandler);};
 
 114 function nodeText(node) {
 
 115   return node.textContent || node.innerText || node.nodeValue || "";
 
 118 function nodeTop(node) {
 
 120   while (node.offsetParent) {
 
 121     top += node.offsetTop;
 
 122     node = node.offsetParent;