/**
 * ARRAYS
 * Added are:
 *  map(func) 
 *  ormap(func) 
 *  andmap(func)
 *  contains(value)
 *  filter(func)
 *  indexOf(value)
 *  copy() 
 *  findAssoc(key) 
 */

Array.prototype.map = function(func) {
  var mapped = new Array();
  for (var x = 0; x < this.length; x++) {
    mapped.push(func(this[x]));
  }

  return mapped;
}

Array.prototype.filter = function(func) {
  var filtered = new Array();
  for (var x = 0; x < this.length; x++) {
    if (func(this[x]))
    filtered.push(this[x]);
  }
  return filtered;
}

Array.prototype.ormap = function(func) {
  for (var x = 0; x < this.length; x++) 
    if (func(this[x])) 
      return true;

  return false;
}

Array.prototype.contains = function(value) {
  return this.ormap(function(x) { return value == x; });
}

Array.prototype.andmap = function(func) {
  for (var x = 0; x < this.length; x++) 
    if (!func(this[x])) 
      return false;

  return true;
}

Array.prototype.indexOf = function(value) {
  for (var x = 0; x < this.length; x++) 
    if (this[x] == value) 
      return x; 

  return -1;
}

Array.prototype.copy = function() {
  return this.map(function(x) { return x; });
}

Array.prototype.findAssoc = function(key) {
  for (var x = 0; x < this.length; ++x)
    if (this[x][0] == key || this[x].key == key)
      return this[x];

  return false;
}

/**
 * STRINGS
 * Added are:
 *   toArray() - returns an array of characters... necessary for compatability with IE
 *   pad(length[, character[, method]]) - 
 *   + character (space " " is default)
 *   + method = {String.STR_PAD_LEFT | String.STR_PAD_RIGHT} (left is default)
 */

String.prototype.toArray = function() {
  var chars = new Array();
  for (var x = 0; x < this.length; x++) 
    chars.push(this.charAt(x));

  return chars;
}

String.STR_PAD_LEFT = 1;
String.STR_PAD_RIGHT = 2;
String.prototype.pad = function(length, character, method) {
  if (!character) { character = " "; }
  if (!method) { method = String.STR_PAD_LEFT; }

  var chars = this.toArray();
  while (chars.length < length) {
    if (method == String.STR_PAD_LEFT) {
      chars.unshift(character);
    } else {
      chars.push(character);
    }
  }

  return chars.join("");
}

/**
 * Math
 * Added:
 *  toBase - takes a positive base 10 integer and converts it to the target base
 */
Math.toBase = function(x, base) {
  var numbers = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 'A', 'B', 'C', 'D', 'E', 'F'];
  var chars = new Array();
  var q, r;

  while (x > 0) {
    r = x % base;
    q = Math.floor(x / base);
    x = q;
    chars.unshift(numbers[r]);
  }

  return chars.join("");
}

/**
 * Returns a formatted number
 *   First two arguments are required, decimal_separator is not
 */
Math.numberFormat = function(num, dec, decimal_separator) {
  // first get the correct number of decimal places
  var mult = Math.pow(10, dec);
  num = Math.round(num * mult) / mult;

  var pieces = String(num).split(".");
  if (!pieces[1]) pieces[1] = "0";  // oops, we lost some zeroes

  if (new String(pieces[1]).length < dec) 
    pieces[1] = new String(pieces[1]).pad(dec, "0", String.STR_PAD_RIGHT);

  if (!decimal_separator)
    decimal_separator = ".";

  if (dec == 0)
    return pieces[0]; // we don't care about after the decimal place

  return pieces[0] + decimal_separator + pieces[1];
}

/**
 * DATE
 *   format() - uses MySQL date formatting... a few of the more obscure formattings are left unfinished
 */
Date.prototype.format = function(format) {
  var short_months = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"];
  var months = ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"];
  var short_days = ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"];
  var days = ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"];
  
  var escaped = false;
  var pieces = new Array();
  for (var x = 0; x < format.length; ++x) {
    if (!escaped) {
      if (format.charAt(x) != "%") {
	pieces.push(format.charAt(x));
      } else {
	escaped = true;
      }
      continue;
    }

    switch (format.charAt(x)) {
    case "a":
      pieces.push(short_days[this.getDay()]); break;
    case "b":
      pieces.push(short_months[this.getMonth()]); break;
    case "c":
      pieces.push(Number(this.getMonth()) + 1); break;
    case "D":
      pieces.push(this.getDate()); break;
    case "d":
      var day_of_month = new String(this.getDate());
      pieces.push(day_of_month.pad(2, "0")); break;
    case "e":
      pieces.push(this.getDate()); break;
    case "f":
      var microseconds = new String(this.getMilliseconds() * 1000);
      pieces.push(microseconds.pad(6, "0")); break;
    case "H":
      pieces.push(this.getHours()); break;
    case "h":
      var hours = this.getHours() % 12;
      hours = (hours == 0) ? "12" : new String(hours);
      pieces.push(hours.pad(2, "0")); break;
    case "I":
      var hours = this.getHours() % 12;
      hours = (hours == 0) ? "12" : new String(hours);
      pieces.push(hours.pad(2, "0")); break;
    case "i":
      var minutes = new String(this.getMinutes());
      pieces.push(minutes.pad(2, "0")); break;
    case "j": // day of the year 001...365
      pieces.push("???"); break;
    case "k":
      pieces.push(this.getHours()); break;
    case "l":
      var hours = this.getHours() % 12;
      pieces.push((hours == 0) ? "12" : hours); break;
    case "M":
      pieces.push(months[this.getMonth()]); break;
    case "m":
      var month = new String(Number(this.getMonth()) + 1);
      pieces.push(month.pad(2, "0")); break;
    case "p":
      pieces.push((this.getHours() < 12) ? "AM" : "PM"); break;
    case "r": // 12-hour hh:mm:ss AM/PM
      pieces.push("?"); break;
    case "S": case "s":
      pieces.push(this.getSeconds()); break;
    case "T": // 24-hour hh:mm:ss
      pieces.push("?"); break;
    case "u": case "U":
    case "v": case "V":
      pieces.push("?"); break;
    case "W":
      pieces.push(days[this.getDay()]); break;
    case "w":
      pieces.push(this.getDay()); break;
    case "x": case "X": // year for the week w/sunday or monday starting???
      pieces.push("?"); break;
    case "Y":
      pieces.push(this.getFullYear()); break;
    case "y":
      var full_year = new String(this.getFullYear());
      pieces.push(full_year.substr(2, 2)); break;
    default:
      pieces.push(format.charAt(x)); break;
    }
    escaped = false;
  }

  return pieces.join("");
}

