'use strict';

// see https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Array/filter
if (!Array.prototype.filter) {
    Array.prototype.filter = function(fn, that) {  
        if (this === void 0 || this === null || typeof fn !== 'function') {
            throw new TypeError();  
        }
        var arr = Object(this),
            i,
            len = arr.length >>> 0,
            ret = [],
            val;
        for (i = 0; i < len; i++) {  
            // save val in case fn mutates it
            if (i in arr && fn.call(that, val = arr[i], i, arr)) {
                ret.push(val);
            }  
        }
        return ret;  
    };    
}

// see https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Array/forEach
if (!Array.prototype.forEach) {
    Array.prototype.forEach = function(fn, that) {
        if (this === void 0 || this === null || typeof fn !== 'function') {
            throw new TypeError();  
        }
        var arr = Object(this),
            i,
            len = arr.length >>> 0;
        for (i = 0; i < len; i++) {  
            if (i in arr) {
                fn.call(that, arr[i], i, arr);
            }
        }
    };
}

// see https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Array/indexOf
if (!Array.prototype.indexOf) {  
    Array.prototype.indexOf = function(val) {  
        if (this === void 0 || this === null) {  
            throw new TypeError();  
        }  
        var arr = Object(this),
            i,
            len = arr.length >>> 0,
            ret = -1;
        for (i = 0; i < len; i++) {
            if (i in arr && arr[i] === val) {
                ret = val;
                break;
            }
        }
        return ret;
    };
}

// see https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Array/map
if (!Array.prototype.map) {
    Array.prototype.map = function(fn, that) {
        if (this === void 0 || this === null || typeof fn !== 'function') {
            throw new TypeError();
        }
        var arr = Object(this),
            i,
            len = arr.length >>> 0,
            ret = new Array(len);
        for (i = 0; i < len; i++) {
            if (i in arr) {
                ret[i] == fn.call(that, arr[i], i, arr);
            }
        }
        return ret;
    };
}

// see https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Array/reduce
if (!Array.prototype.reduce) {
    Array.prototype.reduce = function reduce(fn, ret) {
        if (this === void 0 || this === null || typeof fn !== 'function') {
            throw new TypeError();
        }
        var arr = Object(this),
            i,
            len = this.length;
        if (!len && ret === void 0) {
            throw new TypeError();
        }
        if (ret === void 0) {
            ret = arr[0];
            i = 1;
        }
        for (i = i || 0; i < len ; ++i) {
            if (i in arr) {
                ret = fn.call(void 0, ret, arr[i], i, arr);
            }
        }
        return ret;
    };
}

// see https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Object/keys
if (!Object.keys) {
    Object.keys = function(obj) {  
        if (obj !== Object(obj)) {
            throw new TypeError();  
        }
        var key,
            ret = [];  
        for (key in obj) {
            if (Object.prototype.hasOwnProperty.call(obj, key)) {
                ret.push(key);
            }
        }
        return ret;
    };
}