﻿/* Rydal Williams
My javascript extension class
------------------------------------------------------
TODO: http://mironabramson.com/blog/post/2008/05/Some-simple-but-usefull-C-methods-implemented-in-js.aspx
*/

// returns true of false if the specified array has that value or not
//==================================================================================
Array.prototype.containsValue = function(value) {
    for (var i = 0; i < this.length; i++) {
        if (this[i] === value) {
            return true;
        }
    }
    return false;
};

// Array Remove - By John Resig (MIT Licensed)
// http://ejohn.org/blog/javascript-array-remove/
//==================================================================================
Array.prototype.remove = function(from, to) {
    var rest = this.slice((to || from) + 1 || this.length);
    this.length = from < 0 ? this.length + from : from;
    return this.push.apply(this, rest);
};

// Converts and array to a delimeted string
//==================================================================================
Array.prototype.toDelimetedString = function(delimeter) {
    return this.toString().replace(',', delimeter);
}

// Converts and array to a delimeted string
//==================================================================================
if (!Array.prototype.indexOf) {
    Array.prototype.indexOf = function(obj, fromIndex) {
        if (fromIndex == null) {
            fromIndex = 0;
        } else if (fromIndex < 0) {
            fromIndex = Math.max(0, this.length + fromIndex);
        }
        for (var i = fromIndex, j = this.length; i < j; i++) {
            if (this[i] === obj)
                return i;
        }
        return -1;
    };
}




// later
// executes a method after a certain wait time(Douglas Crockford)
// i.e = myObject.later(1000, "erase", true);
//==================================================================================
//Object.prototype.later = function(msec, method) {
//    var that = this, args = Array.prototype.slice.apply(arguments, [2]);
//    if (typeof method === 'string') {
//        method = that[method];
//    }
//    setTimeout(function() {
//        method.apply(that, args);
//    }, msec);
//    return that;
//};

// Ellipsis
//==================================================================================
String.prototype.ellipsisText = function(location, maxCharacters) {
    var text = this;
    if (this.length > 0 && this.length > maxCharacters) {
        if (typeof (location) == 'undefined') location = 'end';
        switch (location) {
            case 'center':
                var center = (maxCharacters / 2);
                text = this.slice(0, center) + '...' + this.slice(-center);
                break;
            case 'end':
                text = this.slice(0, maxCharacters - 3) + '...';
                break;
        }
    }
    return text;
}

// Trim
//==================================================================================
String.prototype.trim = function() {
    return this.replace(/^\s*(\S*(\s+\S+)*)\s*$/, "$1");
};

// formats a string in the correct notation
//==================================================================================
String.prototype.addCommas = function(nStr) {
    nStr += '';
    x = nStr.split('.');
    x1 = x[0];
    x2 = x.length > 1 ? '.' + x[1] : '';
    var rgx = /(\d+)(\d{3})/;
    while (rgx.test(x1)) {
        x1 = x1.replace(rgx, '$1' + ',' + '$2');
    }
    return x1 + x2;
};

// Supplant
// Can be used to find an replace values from an array within a string
//
// Example
/*
var template = '<tr><td>{border}</td></tr><tr><td></td>{first}</tr>';
var data = {first:Rydal, border:3};
var results = template.supplant(data);
*/
//==================================================================================
String.prototype.supplant = function(obj) {
    return this.replace(/{([^{}]*)}/g, function(a, b) {
        var r = obj[b];
        return typeof (r) === 'string' ? r : a;
    });
};

// format
// Similates C# string.format
//==================================================================================
String.prototype.format = function(text) {
    for (i = 1; i < arguments.length; i++) {
        text = text.replace('{' + (i - 1) + '}', arguments[i]);
    }
    return text;
}

// Simple JavaScript Templating
// John Resig - http://ejohn.org/ - MIT Licensed
// http://ejohn.org/blog/javascript-micro-templating/#postcomment
//==================================================================================
String.prototype.tmpl = function(data) {
    var str = this;
    var cache = {};
    // Figure out if we're getting a template, or if we need to
    // load the template - and be sure to cache the result.
    var fn = !/\W/.test(str) ?
      cache[str] = cache[str] ||
        tmpl(document.getElementById(str).innerHTML) :

    // Generate a reusable function that will serve as a template
    // generator (and which will be cached).
      new Function("obj",
        "var p=[],print=function(){p.push.apply(p,arguments);};" +

    // Introduce the data as local variables using with(){}
        "with(obj){p.push('" +

    // Convert the template into pure JavaScript
        str
          .replace(/[\r\t\n]/g, " ")
          .split("<%").join("\t")
          .replace(/((^|%>)[^\t]*)'/g, "$1\r")
          .replace(/\t=(.*?)%>/g, "',$1,'")
          .split("\t").join("');")
          .split("%>").join("p.push('")
          .split("\r").join("\\'")
      + "');}return p.join('');");

    // Provide some basic currying to the user
    return data ? fn(data) : fn;
};




// Steve's Ultimate Date Library
// copyright Stephen Chapman 6th May 2009
// instructions for use - http://www.felgall.com/datemethods.htm
// permission to use this JavaScript on your web page is granted
// provided that all of the code below in this script (including these
// comments) is used without any alteration
//RWiliams - I modified this script to return data, some of them weren't
//==================================================================================
Date.prototype.addDays = function(days) { this.setDate(this.getDate() + days); };
Date.prototype.addMonths = function(months) { this.setMonth(this.getMonth() + months); };
Date.prototype.addYears = function(years) { this.setFullYear(this.getFullYear() + years); };
Date.prototype.addHours = function(hours) { this.setHours(this.getHours() + hours); };
Date.prototype.addMinutes = function(minutes) { this.setMinutes(this.getMinutes() + minutes); };
Date.prototype.addSeconds = function(seconds) { this.setSeconds(this.getSeconds() + seconds); };
Date.prototype.addPeriod = function(y, m, d, h, i, s) { this.addYears(y); this.addMonths(m); this.addDays(d); this.addHours(h); this.addMinutes(i); this.addSeconds(s); };
Date.prototype.dayDiff = function(d2) { var d = Math.abs(this - d2); return Math.floor(d / (86400000)); };
Date.prototype.busDayDiff = function(d2) { var d = this.dayDiff(d2); var t = d % 7; if (this < d2) { w1 = this.getDay(); w2 = d2.getDay(); } else { w2 = this.getDay(); w1 = d2.getDay(); } if (w1 > w2) t -= 2; if ((w1 == 0 && w2 == 6) || w1 == 6) t--; return Math.abs((Math.floor(d / 7) * 5) + t); };
Date.prototype.ageLastBirthday = function(dob) { var cy = this.getFullYear(); var by = dob.getFullYear(); var db = new Date(dob); db.setFullYear(cy); var adj = (this - db < 0) ? 1 : 0; return cy - by - adj; };
Date.prototype.isBetween = function(d1, d2) { return (this > Math.min(d1, d2) && this < Math.max(d1, d2)); };
Date.prototype.getSDay = function() { return (this.getDay() + 1) % 7; };
Date.prototype.getMDay = function() { return (this.getDay() + 6) % 7; }; Date.prototype.getISOYear = function() { var thu = new Date(this.getFullYear(), this.getMonth(), this.getDate() + 3 - this.getMDay()); return thu.getFullYear(); };
Date.prototype.getISOWeek = function() { var onejan = new Date(this.getISOYear(), 0, 1); var wk = Math.ceil((((this - onejan) / 86400000) + onejan.getMDay() + 1) / 7); if (onejan.getMDay() > 3) wk--; return wk; };
Date.prototype.getJulian = function() { return Math.floor((this / 86400000) - (this.getTimezoneOffset() / 1440) + 2440587.5); };
Date.prototype.getMonthName = function() { var m = ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December']; return m[this.getMonth()]; };
Date.prototype.getMonthShort = function() { var m = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec']; return m[this.getMonth()]; };
Date.prototype.getDayName = function() { var d = ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday']; return d[this.getDay()]; };
Date.prototype.getDayShort = function() { var d = ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat']; return d[this.getDay()]; };
Date.prototype.getOrdinal = function() { var d = this.getDate(); switch (d) { case 1: case 21: case 31: return 'st'; case 2: case 22: return 'nd'; case 3: case 23: return 'rd'; default: return 'th'; } };
Date.prototype.getDOY = function() { var onejan = new Date(this.getFullYear(), 0, 1); return Math.ceil((this - onejan) / 86400000); };
Date.prototype.getWeek = function() { var onejan = new Date(this.getFullYear(), 0, 1); return Math.ceil((((this - onejan) / 86400000) + onejan.getDay() + 1) / 7); };
Date.prototype.getStdTimezoneOffset = function() { var jan = new Date(this.getFullYear(), 0, 1); var jul = new Date(this.getFullYear(), 6, 1); return Math.max(jan.getTimezoneOffset(), jul.getTimezoneOffset()); };
Date.prototype.getDST = function() { return this.getTimezoneOffset() < this.getStdTimezoneOffset(); };
Date.prototype.getSwatch = function() { var swatch = ((this.getUTCHours() + 1) % 24) + this.getUTCMinutes() / 60 + this.getUTCSeconds() / 3600; return Math.floor(swatch * 1000 / 24); }; function _daysInMonth(month, year) { var dd = new Date(year, month, 0); return dd.getDate(); };
Date.prototype.format = function(f) { var fmt = f.split(''); var res = ''; for (var i = 0, l = fmt.length; i < l; i++) { switch (fmt[i]) { case '^': res += fmt[++i]; break; case 'd': var d = this.getDate(); res += ((d < 10) ? '0' : '') + d; break; case 'D': res += this.getDayShort(); break; case 'j': res += this.getDate(); break; case 'l': res += this.getDayName(); break; case 'S': res += this.getOrdinal(); break; case 'w': res += this.getDay(); break; case 'z': res += this.getDOY() - 1; break; case 'R': var dy = this.getDOY(); if (dy < 9) dy = '0' + dy; res += (dy > 99) ? dy : '0' + dy; break; case 'F': res += this.getMonthName(); break; case 'm': var m = this.getMonth() + 1; res += ((m < 10) ? '0' : '') + m; break; case 'M': res += this.getMonthShort(); break; case 'n': res += (this.getMonth() + 1); break; case 't': res += _daysInMonth(this.getMonth() + 1, this.getFullYear()); break; case 'L': res += (_daysInMonth(2, this.getFullYear()) == 29) ? 1 : 0; break; case 'Y': res += this.getFullYear(); break; case 'y': var y = this.getFullYear().toString().substr(3); res += ((y < 10) ? '0' : '') + y; break; case 'a': res += (this.getHours() > 11) ? 'pm' : 'am'; break; case 'A': res += (this.getHours() > 11) ? 'PM' : 'AM'; break; case 'g': var h = this.getHours() % 12; res += (h == 0) ? 12 : h; break; case 'G': res += this.getHours(); break; case 'h': var h = this.getHours() % 12; res += (h == 0) ? 12 : (h > 9) ? h : '0' + h; break; case 'H': var h = this.getHours(); res += (h > 9) ? h : '0' + h; break; case 'i': var m = this.getMinutes(); res += (m > 9) ? m : '0' + m; break; case 's': var s = this.getSeconds(); res += (s > 9) ? s : '0' + s; break; case 'O': var m = this.getTimezoneOffset(); var s = (m < 0) ? '+' : '-'; m = Math.abs(m); var h = (m / 60).toFixed(0); m = m % 60; res += s + ((h > 9) ? h : '0' + h) + ((m > 9) ? m : '0' + m); break; case 'P': var m = this.getTimezoneOffset(); var s = (m < 0) ? '+' : '-'; m = Math.abs(m); var h = (m / 60).toFixed(0); m = m % 60; res += s + ((h > 9) ? h : '0' + h) + ':' + ((m > 9) ? m : '0' + m); break; case 'U': res += (this.getTime() / 1000).toFixed(0); break; case 'I': res += this.getDST() ? 1 : 0; break; case 'K': res += this.getDST() ? 'DST' : 'Std'; break; case 'c': res += this.format('Y-m-d^TH:i:sP'); break; case 'r': res += this.format('D, j M Y H:i:s P'); break; case 'Z': var tz = this.getTimezoneOffset() * -60; res += tz; break; case 'W': res += this.getISOWeek(); break; case 'X': res += this.getWeek(); break; case 'x': var w = this.getWeek(); res += ((w < 10) ? '0' : '') + w; break; case 'B': res += this.getSwatch(); break; case 'N': var d = this.getDay(); res += d ? d : 7; break; case 'u': res += this.getMilliseconds() * 1000; break; case 'o': res += this.getISOYear(); break; case 'J': res += this.getJulian(); break; case 'e': case 'T': break; default: res += fmt[i]; } } return res; }


// closure prototype function
// Here is a function that can access an HTML element without creating an inline closure that leaks memory.
// http://laurens.vd.oever.nl/weblog/items2005/closures/
/*
function attach()
{
var element = document.getElementById("my-element");
element.attachEvent("onclick", function()
{
alert("Clicked: " + this.innerHTML);
}.closure(element));
}
*/
//==================================================================================
Function.prototype.closure = function(obj) {
    // Init object storage.
    if (!window.__objs) {
        window.__objs = [];
        window.__funs = [];
    }

    // For symmetry and clarity.
    var fun = this;

    // Make sure the object has an id and is stored in the object store.
    var objId = obj.__objId;
    if (!objId)
        __objs[objId = obj.__objId = __objs.length] = obj;

    // Make sure the function has an id and is stored in the function store.
    var funId = fun.__funId;
    if (!funId)
        __funs[funId = fun.__funId = __funs.length] = fun;

    // Init closure storage.
    if (!obj.__closures)
        obj.__closures = [];

    // See if we previously created a closure for this object/function pair.
    var closure = obj.__closures[funId];
    if (closure)
        return closure;

    // Clear references to keep them out of the closure scope.
    obj = null;
    fun = null;

    // Create the closure, store in cache and return result.
    return __objs[objId].__closures[funId] = function() {
        return __funs[funId].apply(__objs[objId], arguments);
    };
};














// -- Standard functions
// http: //4umi.com/web/javascript/array.php
// It contains prototypes for all array methods, each wrapped in an if statement to avoid overwriting any that are natively supported.

// Array.concat() - Join two arrays
if (typeof Array.prototype.concat === 'undefined') {
    Array.prototype.concat = function(a) {
        for (var i = 0, b = this.copy(); i < a.length; i++) {
            b[b.length] = a[i];
        }
        return b;
    };
}

// Array.copy() - Copy an array
if (typeof Array.prototype.copy === 'undefined') {
    Array.prototype.copy = function() {
        var a = [], i = this.length;
        while (i--) {
            a[i] = typeof this[i].copy !== 'undefined' ? this[i].copy() : this[i];
        }
        return a;
    };
}

// Array.pop() - Remove and return the last element of an array
if (typeof Array.prototype.pop === 'undefined') {
    Array.prototype.pop = function() {
        var b = this[this.length - 1];
        this.length--;
        return b;
    };
}

// Array.push() - Add an element to the end of an array, return the new length
if (typeof Array.prototype.push === 'undefined') {
    Array.prototype.push = function() {
        for (var i = 0, b = this.length, a = arguments, l = a.length; i < l; i++) {
            this[b + i] = a[i];
        }
        return this.length;
    };
}

// Array.shift() - Remove and return the first element
if (typeof Array.prototype.shift === 'undefined') {
    Array.prototype.shift = function() {
        for (var i = 0, b = this[0], l = this.length - 1; i < l; i++) {
            this[i] = this[i + 1];
        }
        this.length--;
        return b;
    };
}

// Array.slice() - Copy and return several elements
if (typeof Array.prototype.slice === 'undefined') {
    Array.prototype.slice = function(a, c) {
        var i, l = this.length, r = [];
        if (!c) { c = l; }
        if (c < 0) { c = l + c; }
        if (a < 0) { a = l - a; }
        if (c < a) { i = a; a = c; c = i; }
        for (i = 0; i < c - a; i++) { r[i] = this[a + i]; }
        return r;
    };
}

// Array.splice() - Remove or replace several elements and return any deleted elements
if (typeof Array.prototype.splice === 'undefined') {
    Array.prototype.splice = function(a, c) {
        var i = 0, e = arguments, d = this.copy(), f = a, l = this.length;
        if (!c) { c = l - a; }
        for (i; i < e.length - 2; i++) { this[a + i] = e[i + 2]; }
        for (a; a < l - c; a++) { this[a + e.length - 2] = d[a - c]; }
        this.length -= c - e.length + 2;
        return d.slice(f, f + c);
    };
}

// Array.unshift() - Add an element to the beginning of an array
if (typeof Array.prototype.unshift === 'undefined') {
    Array.prototype.unshift = function() {
        this.reverse();
        var a = arguments, i = a.length;
        while (i--) { this.push(a[i]); }
        this.reverse();
        return this.length;
    };
}

// -- 4umi additional functions

// Array.forEach( function ) - Apply a function to each element
Array.prototype.forEach = function(f) {
    var i = this.length, j, l = this.length;
    for (i = 0; i < l; i++) { if ((j = this[i])) { f(j); } }
};

// Array.indexOf( value, begin, strict ) - Return index of the first element that matches value
Array.prototype.indexOf = function(v, b, s) {
    for (var i = +b || 0, l = this.length; i < l; i++) {
        if (this[i] === v || s && this[i] == v) { return i; }
    }
    return -1;
};

// Array.insert( index, value ) - Insert value at index, without overwriting existing keys
Array.prototype.insert = function(i, v) {
    if (i >= 0) {
        var a = this.slice(), b = a.splice(i);
        a[i] = v;
        return a.concat(b);
    }
};

// Array.lastIndexOf( value, begin, strict ) - Return index of the last element that matches value
Array.prototype.lastIndexOf = function(v, b, s) {
    b = +b || 0;
    var i = this.length; while (i-- > b) {
        if (this[i] === v || s && this[i] == v) { return i; }
    }
    return -1;
};

// Array.random( range ) - Return a random element, optionally up to or from range
Array.prototype.random = function(r) {
    var i = 0, l = this.length;
    if (!r) { r = this.length; }
    else if (r > 0) { r = r % l; }
    else { i = r; r = l + r % l; }
    return this[Math.floor(r * Math.random() - i)];
};

// Array.shuffle( deep ) - Randomly interchange elements
Array.prototype.shuffle = function(b) {
    var i = this.length, j, t;
    while (i) {
        j = Math.floor((i--) * Math.random());
        t = b && typeof this[i].shuffle !== 'undefined' ? this[i].shuffle() : this[i];
        this[i] = this[j];
        this[j] = t;
    }
    return this;
};

// Array.unique( strict ) - Remove duplicate values
Array.prototype.unique = function(b) {
    var a = [], i, l = this.length;
    for (i = 0; i < l; i++) {
        if (a.indexOf(this[i], 0, b) < 0) { a.push(this[i]); }
    }
    return a;
};

// Array.walk() - Change each value according to a callback function
Array.prototype.walk = function(f) {
    var a = [], i = this.length;
    while (i--) { a.push(f(this[i])); }
    return a.reverse();
};
