/* String */
String.prototype.startsWith = function(value) {
    return this.indexOf(value) == 0;
}
String.prototype.endsWith = function(value) {
    var searchFrom = this.length - value.length;

    return this.indexOf(value, searchFrom) == searchFrom;
}
String.prototype.each = function(handler){
    for(var i = 0; i < this.length; i++){
        handler(this.charAt(i), i);
    }
}
String.prototype.trim = function() {
    return this.replace(/^\s+|\s+$/g, '');
};
String.empty = "";
String.prototype.isEmpty = function(){
    return this == String.empty;
}
String.prototype.isNotEmpty = function(){
    return !this.isEmpty();
}
/* Array */
if ( typeof(Array.prototype.indexOf) == "undefined"){
    Array.prototype.indexOf = function(item) {
        for( var i = 0 ; i < this.length ; i++ ) {
            if ( this[i] == item ) {
                return i;
            }
        }
        return -1;
    }
}

Array.prototype.add = function(item) {
    this.push(item);
    return this;
}

Array.prototype.remove = function(item) {
    var i = this.indexOf(item);
    while ( i != -1 ) {
        this.splice(i, 1);
        i = this.indexOf(item);
    }
}

Array.prototype.contains = function(value) {
    return this.indexOf(value) > -1;
}

Array.prototype.last = function(){
    return this[this.length - 1];
}

Array.each = function(items, handler){
    for(var i = 0; i < items.length; i++){
        handler(items[i], i);
    }
}

Array.prototype.each = function(handler){
    return Array.each(this, handler);
}

Array.filter = function(items, handler){
    var results = [];
    Array.each(items, function(item){
        if(handler(item)){
            results.add(item);
        }
    });
    return results;
}

if (typeof(Array.prototype.filter) == "undefined"){
    Array.prototype.filter = function(handler){
        return Array.filter(this, handler);
    }
}

Array.map = function(items, handler){
    var results = [];
    Array.each(items, function(item, index){
        results.add(handler(item, index));
    });
    return results;
}

if (typeof(Array.prototype.map) == "undefined"){
    Array.prototype.map = function(handler){
        return Array.map(this, handler);
    }
}


/* StringBuilder */

function StringBuilder() {
    this.buffer = [];
}

StringBuilder.prototype.append = function(value){
    this.buffer.add(value);
    return this;
}

StringBuilder.prototype.toString = function(separator) {
    return this.buffer.join(separator || "");
}

/* Path */

function Path() {
}

Path.extractFilename = function(path) {
    if ( path.contains("\\")){
        return path.split("\\").last();
    }
    else if ( path.contains("/")){
        return path.split("/").last();
    }
    return path;
}

/* Function */

Function.importIntoScope = function(functionToImport) {
    self.eval(functionToImport.toString());
}

/* Object
*  NB. Don't put instance / prototype methods as most libraries rely on object as a hash / map (i.e no extra stuff)
* */

Object.each = function(object, handler){
    for(var property in object){
        handler(property, object[property]);
    }
}


/* Map */

function Map(){
    this.pairs = [];
}

Map.Pair = function(key, value){
    this.key = key;
    this.value = value;
}

Map.prototype.set = function(key, value){
    this.remove(key);
    this.pairs.add(new Map.Pair(key, value));
    return this;
}

Map.prototype.remove = function(key){
    this.pairs.remove(this.get(key));
}

Map.prototype.contains = function(key){
    return this.get(key) != null;
}

Map.prototype.get = function(key){
    var value = null;
    this.each(function(pair){
        if(pair.key == key){
            value = pair.value;
        }
    });
    return value;
}

Map.prototype.each = function(handler){
    this.pairs.each(handler);
}

Map.prototype.merge = function(map){
    map.each(function(pair){
        this.set(pair.key, pair.value);
    })
}

/* Boolean */

Boolean.parse = function(value){
    return value.toLowerCase() == "true";

}

/* Event */
function BrowserEvent(event) {
    this.event = event;
}

BrowserEvent.prototype.htmlTarget = function() {
    var target;
    var event = this.event;
    if (!event) var event = window.event;
    if (event.target) target = event.target;
    else if (event.srcElement) target = event.srcElement;
    if (target.nodeType == 3)
        target = target.parentNode;
    return target;
}

/* Paramters */

function Parameters() {
    this.params = {};
}

Parameters.forBody = function() {
    var parameters = new Parameters();
    parameters.add("decorator", "body");

    return parameters;
}

Parameters.prototype.add = function(name, value) {
    this.params[name] = value;
    return this;
}

Parameters.prototype.toString = function() {
    var result = new StringBuilder();
    for (var name in this.params) {
        result.append(name).append("=").append(encodeURIComponent(this.params[name])).append("&");
    }
    return result.toString();
}

Parameters.prototype.contains = function(key) {
    return this.params[key] != null;

}
