/* Pre-defined status colors. */
var transparent_color = "transparent";
var neutral_color = "white";
var ok_color = "limegreen";
var stale_color = "red";
var warn_color = "yellow";
var error_color = "red";

var grayout_color = "gainsboro";

var off_color = "skyblue";
var partial_error_color = "yellow";  // A partial error or warning.

var above_zero_color = 'forestgreen';
var below_zero_color = 'blue';
var blue_gray = "lightsteelblue"; // Used for "spreadsheet"-like alternation lines in tables

/* Create a MMTDiv Javascript class.  
 * A new instance of the class is created as follows:
 * var my_div = new MMTDiv( my_div_id );
 *
 * Functionality will be added below and can be further added within individual web pages. 
 */ 
function MMTDiv( index ) {
  this.index = null;
    
  if ( typeof( index ) == 'string' ) {
    /* The "index" is the div element id. */
    this.index = index;
    /* This is a persistent point to the div (or td or whatever) element */
    this.element = document.getElementById(index);
  } else if  ( typeof( index ) == 'object' ) {
    /* Else, we are passing in an element directly. */
    this.element = index;
  } else {
    /* Else, we need to create the DIV element. */
    this.element = document.createElement('DIV');
  }
    
  /* Set data to a space as a default. */
  this.data = "&nbsp;";
   
  /* Set innerHTML to a space as a default.
   * The element's real innerHTML is set only when the
   * htm() or  html() functions are used.
   *
   * Leave the element's innerHTML empty if you want to
   * use textNodes since innerHTML also creats textNodes.
   */
  this.innerHTML = "&nbsp;";

  /* Set nodeValue for the optional textnode to a space as a default. */
  this.nodeValue = "&nbsp;";
   
  /* An array to handle difference CSS style attributes. */
  this.css = [];
}

/* Extend the MMTDiv class' functionality. 
 * 
 * Changes the HTML within the div (or whatever) id'ed element.
 */
MMTDiv.prototype.html = function ( html_str )  {
  if ( this.innerHTML !== html_str ) {
    this.htm(html_str);
    this.innerHTML = html_str;
  }
};

/* 
 * Changes the HTML within the div, td, or whatever id'ed element.
 * Use if you know that innerHTML will always be changing.
 */MMTDiv.prototype.htm = function ( html_str )  {
  this.element.innerHTML = html_str;
};

/* 
 * Note:  using textnodes is much more efficient than using innerHTML,
 * which, in turn, is much more efficient than using prototype.js update().
 */

/* The next few functions add textnode functionality to MMTDiv. */

/* Add a text node to the MMTDiv element.
 * Note:  Only append one text node to the element!!!
 */
MMTDiv.prototype.addTextNode = function ( txt_str )  {
  if ( ! isset( txt_str ) || isnull( txt_str ) ) {
    txt_str = "";
  }
  this.textNode = document.createTextNode( txt_str );
  this.element.appendChild( this.textNode )
  this.nodeValue = txt_str;
};

/* Short alias for above: addTextNode(). */
MMTDiv.prototype.atn = function ( txt_str )  {
    this.addTextNode( txt_str.toString() );
};

/* Update the nodeValue (i.e., displayed content) of an appended textnode. */
MMTDiv.prototype.updateTextNode = function ( txt_str )  {
    if ( ! isset( txt_str ) || isnull( txt_str ) ) {
	txt_str = "";
    }
    var str = txt_str.toString();
    if ( this.nodeValue != str ) {
	this.textNode.nodeValue = str;
	this.nodeValue = str;
    }
};

/* Short alias for above: updateTextNode(). */
MMTDiv.prototype.utn = function ( txt_str )  {
    this.updateTextNode( txt_str );
};

/* Probably don't really need this. */
MMTDiv.prototype.removeTextNode = function ( )  {
  this.element.removeChild(this.textNode);
};


/* Some generic data functions for storing data associated with a MMTDiv. */
MMTDiv.prototype.data = function ( data_str )  {
  if ( this.data !== data_str ) {
    this.data = data_str;
  }
  return data_str;
};

/* See if the data related to this element has changed. */ 
MMTDiv.prototype.changed = function ( val ) { 
  if ( this.data != val ) { 
    this.data = val; 
    return true; 
  } else { 
    return false; 
  } 
  /* Returns true or false rather than "this". */ 
}; 


/*  
 * Changes the source for a img element.
 */
MMTDiv.prototype.source = function ( img_str )  {
  if ( this.src !== img_str ) {
    this.element.src = img_str;
    this.src = img_str;
  }
};

/*  
 * Changes the source for a img element.
 */
MMTDiv.prototype.src = function ( img_str )  {
    this.source( img_str );
};


/* Doesn't seem to be working yet. ??? */
MMTDiv.prototype.lineHeight = function ( h_str )  {
  if ( this.lineHeight !== h_str ) {
    this.element.lineHeight = h_str;
    this.lineHeight = h_str;
  }
};


/*  
 * Changes the source for a img element and prevents caching.
 */
MMTDiv.prototype.src_nocache = function ( img_str )  {
  /* Appends a new timestamp so that the image is always loaded from the server. */
  this.element.source = img_str + "&timestamp_ms=" + new Date().getTime();
};

/* Setting the style all at once.
 * use for multiple style assignments, e.g., "position:absolute;top:265px;left:108px;"
 */
MMTDiv.prototype.style = function ( val )  {
  if ( this.style != val ) {	
    this.element.style = val;
    this.style = val;
  }
};


/* Setting a CSS style within the MMTDiv object.
 * Can be used later to concatenate styles.
 * See build_table().
 */
MMTDiv.prototype.css = function ( key, val )  {
  if ( this.css[key] != val ) {	
    this.css[key] = val;
  }
};

/* 
 * CSS border: . 
 */
MMTDiv.prototype.border = function ( val )  {
  if ( this.border != val ) {
    this.element.style.border = val;
    this.border = val;
  }
};

MMTDiv.prototype.paddingLeft = function ( val )  {
  if ( this.paddingLeft != val ) {
    this.element.style.paddingLeft = val;
    this.paddingLeft = val;
  }
};

MMTDiv.prototype.paddingRight = function ( val )  {
  if ( this.paddingRight != val ) {
    this.element.style.paddingRight = val;
    this.paddingRight = val;
  }
};

MMTDiv.prototype.paddingTop = function ( val )  {
  if ( this.paddingTop != val ) {
    this.element.style.paddingTop = val;
    this.paddingTop = val;
  }
};

MMTDiv.prototype.paddingBottom = function ( val )  {
  if ( this.paddingBottom != val ) {
    this.element.style.paddingBottom = val;
    this.paddingBottom = val;
  }
};

MMTDiv.prototype.fontSize = function ( val )  {
  if ( this.fontSize != val ) {
    this.element.style.fontSize = val;
    this.fontSize = val;
  }
};

/* 
 * CSS position: absolute or relative. 
 */
MMTDiv.prototype.position = function ( val )  {
  if ( this.position != val ) {
    this.element.style.position = val;
    this.position = val;
  }
};

MMTDiv.prototype.left = function ( val )  {
  if ( this.left != val ) {
    this.element.style.left = val;
    this.left = val;
  }
};

MMTDiv.prototype.top = function ( val )  {
  if ( this.top != val ) {
    this.element.style.top = val;
    this.top = val;
  }
};


/*
 * Changes the height of the div, td or whatever id'ed element
 */
MMTDiv.prototype.height = function ( y_val )  {
  if ( this.h != y_val ) {
    this.element.style.height = y_val;
    this.h = y_val;
  }
};

/* an alias for "vbar() */
MMTDiv.prototype.assign_vertical_bargraph = function ( value, scale_factor, warn_level, error_level, maximum_height ) {
  this.vbar(  value, scale_factor, warn_level, error_level, maximum_height );
}

/* Assigns the height and background colors of a vertical bargraph (a div HTML element), 
 * with a scaling factor and a maximum height (in pixels)
 * warn_level is the (unscaled) value when the background color will be yellow; error_level is when it will go red.
 */
  MMTDiv.prototype.vbar = function ( value, scale_factor, warn_level, error_level, maximum_height ) {
    if ( this.v_bar != value ) {
      if ( Math.abs( value ) <= warn_level ) {
	this.height(  Math.round( Math.abs( value * scale_factor)   )  +  "px"  );
	    
	if ( value < 0 ) {
	  this.bg( below_zero_color );
	} else {
	  this.bg( above_zero_color );
	}
	    
	/* Making the background yellow if over warn_level but under error_level. */
      } else if  ( Math.abs( value ) <= error_level ) {
	this.height(  Math.round( Math.abs( value * scale_factor)   )  +  "px"  );
	this.warn();	 
	    
	/* Limiting the height to maximum_height pixels and making the background red if over error_level. */
      } else {
	this.height( Math.round( maximum_height ) + "px"  ) ;
	this.error();	 
      }
      this.v_bar = value;
    }
  };

/* an alias for "hbar() */
MMTDiv.prototype.assign_horizontal_bargraph = function ( value, scale_factor, warn_level, error_level, maximum_height ) {
  this.hbar(  value, scale_factor, warn_level, error_level, maximum_height );
}

/* Assigns the width and background colors of a vertical bargraph (a div HTML element), 
 * with a scaling factor and a maximum width (in pixels)
 * warn_level is the (unscaled) value when the background color will be yellow; error_level is when it will go red.
 */
  MMTDiv.prototype.hbar = function ( value, scale_factor, warn_level, error_level, maximum_width ) {
    if ( this.h_bar != value ) {
      if ( Math.abs( value ) <= warn_level ) {
	this.width(  Math.round( Math.abs( value * scale_factor)   )  +  "px"  );
	if ( value < 0 ) {
	  this.bg( below_zero_color );
	} else {
	  this.bg( above_zero_color );
	}
	    
	/* Making the background yellow if over warn_level but under error_level. */
      } else if  ( Math.abs( value ) <= error_level ) {
	this.width(  Math.round( Math.abs( value * scale_factor)   )  +  "px"  );
	this.warn();	 
	    
	/* Limiting the width to maximum_width pixels and making the background red if over error_level. */
      } else {
	this.width( Math.round( maximum_width ) + "px"  ) ;
	this.error();	 
      }
      this.h_bar = value;
    }
  };


/*
 * Changes the width of the div, td or whatever id'ed element
 */
MMTDiv.prototype.width = function ( x_val )  {
  if ( this.w != x_val ) {
    this.element.style.width = x_val;
    this.w = x_val;
  }
};

/*
 * Changes the background color of the div, td or whatever id'ed element
 */
MMTDiv.prototype.bg = function ( color )  {
  if ( this.background !== color )  {
    this.element.style.background = color;
    this.background = color;
  }
};

/* A 'title' is really a "tool tip"-like short message popup. */
MMTDiv.prototype.title = function ( tooltip )  {
  if ( this.tooltip !== tooltip )  {
    this.element.title = tooltip;
    this.tooltip = tooltip;
  }
};

/*
 * Changes the background color to the OK state, nominally green.
 */
MMTDiv.prototype.ok = function ( )  {
  this.bg( ok_color );
};

/*
 * Changes the background color to the OFF state, nominally skyblue.
 */
MMTDiv.prototype.off = function ( )  {
  this.bg( off_color );
};

/*
 * Changes the background color to the WARN (warning) state, nominally yellow.
 */

MMTDiv.prototype.warn = function ( )  {
  this.bg( warn_color );
};

/*
 * Changes the background color to the stale state, nominally red.
 */
MMTDiv.prototype.stale = function ( )  {
  this.bg( stale_color );
};

/*
 * Changes the background color to the ERROR state, nominally red.
 */
MMTDiv.prototype.error = function ( )  {
  this.bg( error_color );
};

/*
 * An alias for .error() 
 */
MMTDiv.prototype.err = function ( )  {
  this.bg( error_color );
};


/*
 * Changes the background color to the "grayout" state, nominally gray,
 * indicate that an element cannot be modified by the user.
 */
MMTDiv.prototype.grayout = function ( )  {
  this.bg( grayout_color );
};

/*
 * Changes the background color to "transparent", used to remove an error or
 * other background color,
 */
MMTDiv.prototype.transparent = function ( )  {
  this.bg( transparent_color );
};

/*
 * Changes the background color to "neutral", nominally white, used to 
 * remove an error or other background color,
 */
MMTDiv.prototype.neutral = function ( )  {
  this.bg( neutral_color );
};

/*
 * Changes the foreground color, i.e., the color of text, in an element.
 */
MMTDiv.prototype.fg = function ( color )  {
  if ( this.color !== color )  {
    this.element.style.color = color;
    this.color = color;
  }
};

/* 
 * Changes the text and makes it red.
 */
MMTDiv.prototype.redText = function ( str ) {
  this.fg( "red" );
  this.html( str );
};

/* 
 * Changes the text and makes it black.
 */
MMTDiv.prototype.blackText = function (str ) {
  this.fg( "black");
  this.html( str );
};

/* 
 * Changes the text and makes it blue.
 */
MMTDiv.prototype.blueText = function ( str ) {
  this.fg( "blue");
  this.html( str );
};

/* 
 * Changes the text and makes it green.
 */
MMTDiv.prototype.greenText = function ( str ) {
  this.fg( "forestgreen" );
  this.html( str );
};

/* 
 * Changes the text and makes it orange.
 */
MMTDiv.prototype.orangeText = function ( str ) {
  this.fg( "orange" );
  this.html( str );
};

/* 
 * Changes the text and makes it purple (#800080).
 */
MMTDiv.prototype.purpleText = function ( str ) {
  this.fg( "#800080" ); 
  this.html( str );
};

/* 
 * Changes the text and makes it maroon (#B03060).
 */
MMTDiv.prototype.maroonText = function ( str ) {
  this.fg( "#B03060" ); 
  this.html( str );
};
  
/* 
 * Changes the text alignment to "right" justified..
 */
MMTDiv.prototype.right = function ( )  {
  if ( this.textAlign !== "right" )  {
    this.element.style.textAlign = "right";
    this.textAlign = "right";
  }
};

/* 
 * Changes the text alignment to "left" justified..
 */
MMTDiv.prototype.left = function ( )  {
  if ( this.textAlign !== "left" )  {
    this.element.style.textAlign = "left";
    this.textAlign = "left";
  }
};

/* 
 * Changes the text alignment to "center" justified..
 */
MMTDiv.prototype.center = function ( )  {
  if ( this.textAlign !== "center" )  {
    this.element.style.textAlign = "center";
    this.textAlign = "center";
  }
};
  
/* 
 * Generates a bargraph within a div element by using "|" symbols.
 * Above zero values are green text and left justified.
 * Below zero values are blue text and right justified.
 * The "max" is the maximum number of "|" ticks that will be concatenated.
 * "max" can be as big as you want.  It's just to prevent huge strings of "|" characters
 * when there is bad data.
 * The background goes yellow when this maximum number of ticks 
 * is exceeded.
 */
MMTDiv.prototype.bargraph = function ( temperature, max  ) {
  var str = "";
  var count = 1;
  var temperature_2 = Math.abs( Math.round( temperature ) );
    
  while ( ( count <= temperature_2) && ( count < max ) ) {
    str += "|";
    count++;
  }
    
  if ( str.length == 0 ) {
    str = "&nbsp;"
  }

  /* Sub-zero is blue text for the "|"s and right-aligned. */
  if ( temperature < 0 ) {
    this.right();
    this.blueText( str );
	
    /* Above zero is green text for the "|"s and left-aligned. */
  } else {
    this.left();
    this.greenText( str );
  }
    
  /* Create a yellow background for temperatures that are greater than 
   * the maximum allowed display. 
   */
  if ( Math.abs( temperature  ) > max ) {
    this.warn();
  } else {
    this.neutral();
  }

}

/* 
 * Generates a bargraph within a div element by using "|" symbols.
 * Above zero values are green text and left justified.
 * Below zero values are blue text and right justified.
 * The "max" is the maximum number of "|" ticks that will be concatenated.
 * "max" can be as big as you want.  It's just to prevent huge strings of "|" characters
 * when there is bad data.
 * The background goes yellow when this maximum number of ticks 
 * is exceeded.
 */
  MMTDiv.prototype.v_bargraph = function ( value, max, bar_length  ) {    
    if ( this.data != value ) {
      var str = "";
      var count = 1;
      var value_2 = Math.abs( Math.round( value ) );
	
      while ( ( count <= value_2) && ( count < max ) ) {
	for (var i = 1; i <= bar_length; i++) {
	  str += "&mdash;";    /* The HTML  em dash */
	}
	str += "<br>";    /* The HTML  em dash */
	count++;
      }
	
      if ( str.length == 0 ) {
	str = "&nbsp;"
      }
	
	
      /* Create a yellow background for values that are greater than 
       * the maximum allowed display. 
       */
      if ( Math.abs( value  ) > max ) {
	this.html( "<font color='red'>" + str + "</font>" );
	this.warn();
      } else {
	/* Sub-zero is blue text for the "|"s. */
	if ( value < 0 ) {
	  this.html( "<font color='blue'>" + str + "</font>" );
	    
	  /* Above zero is green text for the "|"s. */
	} else {
	  this.html( "<font color='green'>" + str + "</font>" );
	}
	this.transparent();
      }
      this.data = value;
    }
  }
    
    
  /* Possible parameter values:  none, overline, underline, line-through, blink */
    MMTDiv.prototype.textDecoration = function ( value )  {
      if ( this.textDecoration !== value )  {
	this.element.style.textDecoration = value;
	this.textDecoration = value;
      }
    };

/* 
 * Turns text blinking on.  Internet Explorer doesn't support this.
 */
MMTDiv.prototype.blink = function ()  {
  this.textDecoration( "blink" );
};

/* 
 * Turns text blinking off.  Internet Explorer doesn't support this.
 */
MMTDiv.prototype.nonblink = function ()  {
  this.textDecoration( "none" );
};


/* New, Oct 10, 2007 JDG. */
/* Using the data and css array of the MMTDiv object
 * to build a td element.
 * See also build_table().
 */
MMTDiv.prototype.toTD = function () { 
  /* Setting CSS style for TD element */
  var css_str = "";
  for ( var i in this.css ) { 
    css_str += i + ":" + this.css[i] + ";"; 
  } 

  var str = "";
  if ( css_str.length > 0 ) {
    str += '<td style="' + css_str + '">'; 
  } else {
    str += "<td>"; 
  }

  /* Setting the contents for TD element. */
  str += this.data; 
  str += "</td>"; 
  return str; 
}; 

/* New, Oct 10, 2007 JDG. */
/* Build a HTML table, assumes fixed table layout,
 * which has columns of equal width.
 * "arr" is an array of MMTDiv elements, indexed on row
 * and col, separated by a "_" symbol.
 * Uses the MMTDiv.data and MMTDiv.css fields.
 * You may want to add style statements in the header,
 * such as for width=100%, centered-text, and bold fonts.
 *
 * Use MMTDiv.data() and MMTDiv.css() to set contents of
 * TD's before calling this.
 */ 
function build_table( arr , rows, cols ) { 
  var str = "<table>"; 
  for ( var i = 0; i < rows; i++ ) { 
    str += "<tr>"; 
    for ( var j = 0; j < cols; j++ ) { 
      str += arr[ i + "_" + j].toTD(); 
    } 
    str += "</tr>"; 
  } 
  str += "</table>"; 
  return str; 
} 

/* New, Oct 10, 2007 JDG. */
/* Build a two-column, pre-formatted "table". */
function build_2col_pre( arr, col_a_width, col_b_width ) { 
  var str = "<pre>\n"; 
  for ( var i in arr ) { 
    str += fillLeft( i, col_a_width ) + " : " + fillRight( arr[i], col_b_width ) + "\n";
  } 
  str += "</pre>"; 
  return str; 
} 

/* Build a two-column, pre-formatted, centered "table". */
function build_2col_centered( arr, col_a_width, col_b_width ) { 
  var str = "<pre>\n"; 
  for ( var i in arr ) { 
    str += fillEnds( i, col_a_width ) + " : " + fillEnds( arr[i], col_b_width ) + "\n";
  } 
  str += "</pre>"; 
  return str; 
} 


/*
 * Helper function to test is a variable has been defined, i.e., "is set"
 * Returns true or false, similar to the PHP isset() function.
 */
function isset  ( variable ) {
  return ( typeof (variable) != 'undefined' );
}

/* Helper function to see if variable has been set to null */
function isnull  ( variable ) {
  return ( variable == null );
}

/* This does some validation for numbers. */
function isNumeric(sText)
{
  var ValidChars = "0123456789.+-";
  var SignChars = "+-";
  var IsNumber=true;
  var Char;
  
  for (var i = 0; i < sText.length && IsNumber == true; i++) { 
    Char = sText.charAt(i); 
    if (ValidChars.indexOf(Char) == -1) 
      {
	IsNumber = false;
      }
    
    if ( i > 0 ) {
      if (SignChars.indexOf(Char) != -1) 
	{
	  IsNumber = false;
	} 
    }
  }
  return IsNumber;   
}

/* Converts Degrees C to Degrees F */
function c2f ( temp_c ) {
  return ( ( temp_c * 1.8 ) + 32.0 );
}

/* Converts Degrees F to Degrees C */
function f2c ( temp_f ) {
  return ( ( temp_f - 32.0 ) / 1.8 );
}

/* The 'fill' function can be used for fixed pitch strings to 
 * do left-align, right-align, or center-align. */ 
function fillLeft( str, len ) { 
  left = len - str.length; 
  return doFill( str, left, 0 ); 
} 
 
/* Left justifies to a given length (for <pre> formatting) */
function fillRight( str, len ) { 
  right = len - str.length; 
  return doFill( str, 0, right ); 
} 
 
/* Center justifies to a given length (for <pre> formatting) */
function fillEnds( str, len ) { 
  right = (len - str.length)/2; 
  left = right; 
  /* Account for odd values of 'len', the desired length. */ 
  while ( left+right < len ) { 
    left++; 
  } 
  return doFill( str, left, right ); 
} 
 
/* Append the spaces for fixed-pitch string justification. */
function doFill ( str, left, right ) { 
  var my_str = str; 
  for (var i=0;i<left;i++) { 
    my_str = " " + my_str; 
  } 
  for (var i=0;i<right;i++) { 
    my_str = my_str + " "; 
  } 
  return my_str; 
} 


/*  
 * Parses an "all" format set of strings and puts the key-pairs into an array.
 * This avoids having to do an eval() on the data string that is returned
 * from the web browser.
 * The "all_str" parameters is a multi-line string of key-value pairs,
 * terminated by an .EOF line.
 * Can process output of http://hacksaw.mmto.arizona.edu/engineering/mini2all.php?miniserver=MINI_MOUNT, for example.
 */
function all2array ( all_str )  {
  var array = [ ];
  /* Split the all string by lines into an array. */
  var lines = all_str.split("\n");
  /* Process each line, which is a key value pair.  ".EOF" signals the end of data. */
  for (var i = 0; i < lines.length; i++) {
    if ( lines[i] == ".EOF" ) {
      break;
    }
    var pair = lines[i].split(" ", 2); /* Split into a key and value. The value may contain spaces. */
    if ( pair.length == 2 ) {
      array[ pair[0] ] = pair[1];
    }
  }
  return array;
};

/* Similar to all2array(), but returns an array of two arrays, which  
 * contain the keys and values, respectively. 
 */ 
function all2arrays ( all_str ) { 
  var keys = []; 
  var values = []; 
  var key; 
  var value; 
  /* Split the 'all' multi-line string by lines into an array. */ 
  var lines = all_str.split("\n"); 
  /* Process each line, which is usually a key-value pair.  ".EOF" signals the end of data. \
   */ 
    
  for (var i = 0; i < lines.length; i++) { 
    if ( lines[i] == ".EOF" ) { 
      break; 
    } 
    /* split() with a limit of splits specified doesn't seem to work!!!??? */ 
    var pair = lines[i].split(" "); /* Split into a key and value pair. */ 
    var num_split =  pair.length; 
    if ( num_split == 2 ) { 
      key = pair[0]; 
      value = pair[1]; 
    } else if  ( num_split > 2 ) { 
      key = pair[0]; 
      value = pair[1]; 
      for  (var ii = 2; ii < num_split; ii++) {  
	value += " " + pair[ii]; 
      } 
    } else { /* Only read one word - unknown error, so break. */ 
      break; 
    } 
    keys.push( key ); 
    values.push( value ); 
  } 
  /* Returning an array of arrays, where array[0] is an array of keys and 
   * array[1] is an array of values.  The two arrays have the same length.  
   */ 
  return  [ keys, values ]; 
}; 


/*   
 * Parses an "all" format set of strings and puts the key-pairs into an object.  
 * This avoids having to do an eval() on the data string that is returned from the web browser. 
 * The "all_str" parameters is a multi-line (separated by "\n") string of key-value pairs, 
 * terminated by an .EOF line. 
 * Can process output of:  
 * http://hacksaw.mmto.arizona.edu/webservices/carrier/all/. 
 */ 
function all2object ( all_str ) { 
  /* Trying to force to a hash data type rather than an object. */ 
  var obj = { }; 
  var my_key; 
  var value; 
  /* Split the 'all' multi-line string by lines into an array. */ 
  var lines = all_str.split("\n"); 
  /* Process each line, which is usually a key-value pair.  
   * ".EOF" signals the end of data. 
   */ 
  for (var i = 0; i < lines.length; i++) { 
    if ( lines[i] == ".EOF" ) { 
      break; 
    } 
    /* split() with a limit of splits specified doesn't seem to work!!!??? */ 
    var pair = lines[i].split(" "); /* Split into a key and value pair. */ 
    var num_split =  pair.length; 
    if ( num_split == 2 ) { 
      my_key = pair[0]; 
      value = pair[1]; 
    } else if  ( num_split > 2 ) { 
      my_key = pair[0]; 
      value = pair[1]; 
      for  (var ii = 2; ii < num_split; ii++) {  
	value += " " + pair[ii]; 
      } 
    } else { /* Only read one word - unknown error, so break. */ 
      break; 
    } 
    obj[ my_key ] = value; 
  } 
  return obj; 
}; 

/* from http://www.dustindiaz.com/top-ten-javascript/  */
function getElementsByClass(searchClass,node,tag) {
    var classElements = new Array();
    if ( node == null )
	node = document;
    if ( tag == null )
	tag = '*';
    var els = node.getElementsByTagName(tag);
    var elsLen = els.length;
    var pattern = new RegExp('(^|\\\\s)'+searchClass+'(\\\\s|$)');
    for (i = 0, j = 0; i < elsLen; i++) {
	if ( pattern.test(els[i].className) ) {
	    classElements[j] = els[i];
	    j++;
	}
    }
    return classElements;
}

function toggle(obj) {
    var el = document.getElementById(obj);
    if ( el.style.display != 'none' ) {
	el.style.display = 'none';
    }
    else {
	el.style.display = '';
    }
}

function getCookie( name ) {
    var start = document.cookie.indexOf( name + "=" );
    var len = start + name.length + 1;
    if ( ( !start ) && ( name != document.cookie.substring( 0, name.length ) ) ) {
	return null;
    }
    if ( start == -1 ) return null;
    var end = document.cookie.indexOf( ';', len );
    if ( end == -1 ) end = document.cookie.length;
    return unescape( document.cookie.substring( len, end ) );
}

function setCookie( name, value, expires, path, domain, secure ) {
    var today = new Date();
    today.setTime( today.getTime() );
    if ( expires ) {
	expires = expires * 1000 * 60 * 60 * 24;
    }
    var expires_date = new Date( today.getTime() + (expires) );
    document.cookie = name+'='+escape( value ) +
	( ( expires ) ? ';expires='+expires_date.toGMTString() : '' ) + //expires.toGMTString()
	( ( path ) ? ';path=' + path : '' ) +
	( ( domain ) ? ';domain=' + domain : '' ) +
	( ( secure ) ? ';secure' : '' );
}

function deleteCookie( name, path, domain ) {
    if ( getCookie( name ) ) document.cookie = name + '=' +
	( ( path ) ? ';path=' + path : '') +
	( ( domain ) ? ';domain=' + domain : '' ) +
	';expires=Thu, 01-Jan-1970 00:00:01 GMT';
}

Array.prototype.inArray = function (value) {
    var i;
    for (i=0; i < this.length; i++) {
	if (this[i] === value) {
	    return true;
	}
    }
    return false;
};

function $() {
    var elements = new Array();
    for (var i = 0; i < arguments.length; i++) {
	var element = arguments[i];
	if (typeof element == 'string')
	    element = document.getElementById(element);
	if (arguments.length == 1)
	    return element;
	elements.push(element);
    }
    return elements;
}

// Sample Usage:
/*
var obj1 = document.getElementById('element1');
var obj2 = document.getElementById('element2');
function alertElements() {
    var i;
    var elements = $('a','b','c',obj1,obj2,'d','e');
    for ( i=0;i
*/
