/**
 * extensions to core JavaScript objects: Function, Array...
 */

/**
 * Class method for copying psuedo-arrays to *real* arrays, such
 * as function arguments and dom node collections (ie, document.getElementsByTagName("div"))
 * @param {Object} original array
 * @return {Array} new *real* array
 */
Array.Copy = function(original){
  var i,result = [];
  for(i=0;i<original.length;i++){
    result.push(original[i]);
  }
  return result;
}
/**
 * alters the context in which a method will run, can be handy when used in conjunction with events
 * @param {Object} context for method to run (sets up the "this" reference)
 * @param... all additional parameters are passed to the bound method as parameters
 * @return {Function} invoking this function in any context will run the method in the correct context
 */
/*
  example:
  var myObject = {
    name:"fred"
  };
  function doTest(param1,param2){
    alert(this.name +'\n'+ param1 +'\n'+ param2);
  }
  var boundMethod = doTest.bind(myObject,"12","jane");
  boundMethod();
*/
Function.prototype.bind = function() {
  var method = this;
  var args = Array.Copy(arguments);
  var obj = args.shift();
  return function() {
    return method.apply(obj, args);
  };
}

// JavaScript 1.6 methods
if(!Array.forEach){
  // perform action on each item of array - used in place of: for(i=0;i<myArray.length;i++) {...}
  /*
    example:
    var myArray = ['apple','banana','pear'];
    myArray.forEach(function(fruit){
      document.write(fruit);
    });
  */
	Array.prototype.forEach = function(fn, thisObj) {
    var scope = thisObj || window;
    for ( var i=0, j=this.length; i < j; ++i){
      fn.call(scope, this[i], i, this);
    }
  }
}
if(!Array.every){
  // check if every item in array matches some criteria
  /*
    example:
    var myArray = [
      {name:'apple',type:'fruit'},
      {name:'steak',type:'meat'},
      {name:'pear',type:'fruit'}
    ];
    // returns false
    var allItemsAreFruits = myArray.every(function(food){
      return (food.type == "fruit");
    });
  */
  Array.prototype.every =  function(fn, thisObj) {
    var scope = thisObj || window;
    for ( var i=0, j=this.length; i < j; ++i){
      if(!fn.call(scope, this[i], i, this)){
        return false;
      }
    }
    return true;
  }
}
if(!Array.some){
  // check if every item in array matches some criteria
  /*
    example:
    var myArray = [
      {name:'apple',type:'fruit'},
      {name:'steak',type:'meat'},
      {name:'pear',type:'fruit'}
    ];
    // returns true
    var someItemsAreFruits = myArray.some(function(food){
      return (food.type == "fruit");
    });
  */
  Array.prototype.some = function(fn, thisObj) {
      var scope = thisObj || window;
    for ( var i=0, j=this.length; i < j; ++i){
          if(fn.call(scope, this[i], i, this)){
              return true;
          }
      }
      return false;
  }
}
if(!Array.map){
  // create a new array based on contents of original array
  /*
    example:
    var myArray = [
      {name:'apple',type:'fruit'},
      {name:'steak',type:'meat'},
      {name:'pear',type:'fruit'}
    ];
    var arrayOfHtml = myArray.map(function(food){
      return "<div>"+ food.name +" ("+ food.type +")</div>";
    });
  */
  Array.prototype.map = function(fn, thisObj) {
      var scope = thisObj || window;
      var a = [];
      for ( var i=0, j=this.length; i < j; ++i){
          a.push(fn.call(scope, this[i], i, this));
      }
      return a;
  }
}
if(!Array.filter){
  // create a new array of filtered results
  /*
    example:
    var myArray = [
      {name:'apple',type:'fruit'},
      {name:'steak',type:'meat'},
      {name:'pear',type:'fruit'}
    ];
    // returns true
    var fruits = myArray.map(function(food){
      return (food.type == "fruit");
    });
  */
  Array.prototype.filter =  function(fn, thisObj) {
      var scope = thisObj || window;
      var a = [];
      for ( var i=0, j=this.length; i < j; ++i){
          if(!fn.call(scope, this[i], i, this)){
              continue;
          }
          a.push(this[i]);
      }
      return a;
  }
}
if(!Array.indexOf){
  // returns index of specified element (-1 if not found)
  Array.prototype.indexOf = function(el, start) {
      var start = start || 0;
      for ( var i=start, j=this.length; i < j; ++i){
          if(this[i] === el){
              return i;
          }
      }
      return -1;
  }
}
if(!Array.lastIndexOf){
  // returns the last index of specified element (-1 if not found)
  Array.prototype.lastIndexOf = function(el, start) {
      var start = start || this.length;
      if(start >= this.length){
          start = this.length;
      }
      if(start < 0){
           start = this.length + start;
      }
      for ( var i=start; i >= 0; --i){
          if(this[i] === el){
              return i;
          }
      }
      return -1;
  }
}
// more useful array methods
if(!Array.splice){
  Array.prototype.splice = function (iIndex , iLength ) {
    var i,aResult  = new Array();
    var aRemoved  = new Array();
    for (i=0; i < iIndex; i++){
      aResult.push(this[i]);
    }
    for (i=iIndex; i < iIndex+iLength; i++) {
     aRemoved.push(this[i]);
    }
    if (arguments.length > 2) {
      for (i=2; i < arguments.length; i++) {
        aResult.push(arguments[i]);
      }
    }
    for (i=iIndex+iLength; i < this.length; i++) {
      aResult.push(this[i]);
    }
    for (i=0; i < aResult.length; i++) {
      this[i] = aResult[i];
    }
    this.length = aResult.length;
    return aRemoved;
  }
}
if(!Array.remove){
  Array.prototype.remove = function (vItem )  {
    this.removeAt(this.indexOf(vItem));
    return vItem;
  }
}
if(!Array.removeAt){
  Array.prototype.removeAt = function (iIndex )  {
    var vItem = this[iIndex];
    if (vItem) {
      this.splice(iIndex, 1);
    }
    return vItem;
  }
}