/*
 * prototype.js light.
 * From: Prototype JavaScript framework, version 1.7 (c) 2005-2010 Sam Stephenson
 * For details, see the Prototype web site: http://www.prototypejs.org/
 */
/*
 * In Edition 5, the following new properties are defined on built-in objects that exist in Edition 3:
 *  Object.getPrototypeOf,
 *  Object.getOwnPropertyDescriptor,
 *  Object.getOwnPropertyNames,
 *  Object.create,
 *  Object.defineProperty,
 *  Object.defineProperties,
 *  Object.seal,
 *  Object.freeze,
 *  Object.preventExtensions,
 *  Object.isSealed,
 *  Object.isFrozen,
 *  Object.isExtensible,
 *  Object.keys,
 *  Function.prototype.bind,
 *  Array.prototype.indexOf,
 *  Array.prototype.lastIndexOf,
 *  Array.prototype.every,
 *  Array.prototype.some,
 *  Array.prototype.forEach,
 *  Array.prototype.map,
 *  Array.prototype.filter,
 *  Array.prototype.reduce,
 *  Array.prototype.reduceRight,
 *  String.prototype.trim,
 *  Date.now,
 *  Date.prototype.toISOString,
 *  Date.prototype.toJSON.
 * See also http://markcaudill.com/index.php/2009/04/javascript-new-features-ecma5/
 */
Array.prototype.clone = function() {
  return [].concat(this);
};
Array.prototype.toArray = Array.prototype.clone;
// The primary use of Array.from() is to obtain an actual Array object based on anything that could pass as an array.
// the predefined arguments reference within your functions
Array.from = function(iterable) {
  if (!iterable) return [];
  if (iterable.toArray) return iterable.toArray();
  var length = iterable.length || 0, results = new Array(length);
  while (length--) results[length] = iterable[length];
  return results;
}
// extend Object
Object.extend = function(destination, source) {
  for (var property in source)
    destination[property] = source[property];
  return destination;
};
Object.extend(Object, {
  toJSON: function(object) {
    var type = typeof object;
    switch (type) {
      case 'undefined':
      case 'function':
      case 'unknown': return;
      case 'boolean': return object.toString();
    }
    if (object === null) return 'null';
    if (object.toJSON) return object.toJSON();
    var results = [];
    for (var property in object) {
      var value = Object.toJSON(object[property]);
      if (!Object.isUndefined(value))
        results.push(property.toJSON() + ': ' + value);
    }
    return '{' + results.join(', ') + '}';
  },
  keys: function(object) {
    var keys = [];
    for (var property in object)
      keys.push(property);
    return keys;
  },
  values: function(object) {
    var values = [];
    for (var property in object)
      values.push(object[property]);
    return values;
  },
  isArray: function(object) {
    return object != null && typeof object == "object" &&
      'splice' in object && 'join' in object;
  },
  isFunction: function(object) {
    return typeof object == "function";
  },
  isString: function(object) {
    return typeof object == "string";
  },
  isNumber: function(object) {
    return typeof object == "number";
  },
  isUndefined: function(object) {
    return typeof object == "undefined";
  }
});
// extend Function
Object.extend(Function.prototype, {
  argumentNames: function() {
    var names = this.toString().match(/^[\s\(]*function[^(]*\(([^\)]*)\)/)[1]
      .replace(/\s+/g, '').split(',');
    return names.length == 1 && !names[0] ? [] : names;
  },
  bind: function() {
    if (arguments.length < 2 && (typeof arguments[0] == "undefined")) return this;
    var __method = this, args = Array.from(arguments), object = args.shift();
    return function() {
      return __method.apply(object, args.concat(Array.from(arguments)));
    }
  },
  wrap: function(wrapper) {
    var __method = this;
    return function() {
      return wrapper.apply(this, [__method.bind(this)].concat(Array.from(arguments)));
    }
  }
});
Object.extend(String.prototype, {
  camelize: function(prefix, suffix) {
    var parts = this.split('-');
    if (prefix) parts.unshift(prefix);
    if (suffix) parts.push(suffix);
    var len = parts.length;
    if (len == 1) return parts[0];

    var camelized = this.charAt(0) == '-'
      ? parts[0].charAt(0).toUpperCase() + parts[0].substring(1)
      : parts[0];

    for (var i = 1; i < len; i++)
      camelized += parts[i].charAt(0).toUpperCase() + parts[i].substring(1);

    return camelized;
  },
  capitalize: function() {
    return this.charAt(0).toUpperCase() + this.substring(1).toLowerCase();
  },
  startsWith: function(pattern) {
    return this.indexOf(pattern) === 0;
  },
  endsWith: function(pattern) {
    var d = this.length - pattern.length;
    return d >= 0 && this.lastIndexOf(pattern) === d;
  },
  _empty: function() {
    return this == '';
  },
  trim: function() {
    return this.replace(/^\s+/, '').replace(/\s+$/, '');
  },
  toJSON: function() {
    // TODO Fix quotes...
    return '\'' + this.toString().replace(/([\\'])/g, '\\$1') + '\'';
  }
});
Object.extend(Number.prototype, {
  _toPaddedString: function(length, radix) {
    var string = this.toString(radix || 10);
    var pad = '00000';
    while (pad.length + string.length < length) {
        pad += pad;
    }
    return pad.substr(0, length - string.length) + string;
  },
  toJSON: function() {
    return isFinite(this) ? this.toString() : 'null';
  }
});
Object.extend(Array.prototype, {
  indexOf: function(object, from) {
    var len = this.length;
    from = from || 0;
    for (var i = from; i < len; i++) {
      if (this[i] === object) {
        return i;
      }
    }
    return -1;
  },
  _remove: function(object) {
    var index = 0;
    if (typeof object == 'number') {
      index = object;
    } else {
      index = this.indexOf(object);
      if (index < 0) {
        return null;
      }
    }
    return this.splice(index, 1)[0];
  },
  toJSON: function() {
    var results = [];
    for (var i = 0, length = this.length; i < length; i++) {
      var value = Object.toJSON(this[i]);
      if (!Object.isUndefined(value)) results.push(value);
    };
    return '[' + results.join(', ') + ']';
  }
});
Object.extend(Date.prototype, {
  // TODO Fix UTC
  toJSON: function() {
    return '"' + this.getUTCFullYear() + '-' +
      (this.getUTCMonth() + 1)._toPaddedString(2) + '-' +
      this.getUTCDate()._toPaddedString(2) + 'T' +
      this.getUTCHours()._toPaddedString(2) + ':' +
      this.getUTCMinutes()._toPaddedString(2) + ':' +
      this.getUTCSeconds()._toPaddedString(2) + 'Z"';
  }
});

