/**
ARRAY EXTENSIONS
by Caio Chassot (http://v2studio.com/k/code/)
*/

//function v2studio_com_code()
//{


/** 
 * Searches Array for <b>value</b>. 
 * returns the index of the first item 
 * which matches <b>value</b>, or -1 if not found.
 * searching starts at index 0, or at <b>start</b>, if specified.
 *
 * Here are the rules for an item to match <b>value</b>
 * if strict is false or not specified (default):
 * if <b>value</b> is a:
 *  # <b>function</b>    -> <b>value(item)</b> must be true
 *  # <b>RegExp</b>      -> <b>value.test(item)</b> must be true
 *  # anything else -> <b>item == value</b> must be true
 * @param value the value (function, regexp) to search
 * @param start where to start the search
 * @param strict use strict comparison (===) for everything
 */
Array.prototype.indexOf = function(value, start, strict) {
    start = start || 0;
    for (var i=start; i<this.length; i++) {
        var item = this[i];
        if (strict            ? item === value   :
            isRegexp(value)   ? value.test(item) :
            isFunction(value) ? value(item)      :
            item == value)
            return i;
    }
    return -1;
}

/** 
 * searches Array for <b>value</b> returns the first matched item, or null if not found
 * Parameters work the same as indexOf
 * @see #indexOf
 */
Array.prototype.find = function(value, start, strict) {
    var i = this.indexOf(value, start, strict);
    if (i != -1) return this[i];
    return null
}



/*  A.contains(value [, strict])
/** 
 * aliases: has, include
 * returns true if <b>value</b> is found in Array, otherwise false;
 * relies on indexOf, see its doc for details on <b>value</b> and <b>strict</b>
 * @see #indexOf
 */
Array.prototype.contains = function(value,strict) {
    return this.indexOf(value,0,strict) !== -1;
}


Array.prototype.has     = Array.prototype.contains;

Array.prototype.include = Array.prototype.contains;


/** 
 * counts occurences of <b>value</b> in Array
 * relies on indexOf, see its doc for details on <b>value</b> and <b>strict</b>
 * @see #indexOf
 */ 
Array.prototype.count = function(value, strict) {
    var pos, start = 0, count = 0;
    while ((pos = this.indexOf(value, start, strict)) !== -1) {
        start = pos + 1;
        count++;
    }
    return count;
}


/** 
 * if <b>all</b> is false or not provied:
 *        removes first occurence of <b>value</b> from Array
 *   if <b>all</b> is provided and true:
 *       removes all occurences of <b>value</b> from Array
 *   returns the array
 *   relies on indexOf, see its doc for details on <b>value</b> and <b>strict</b>
 * @see #indexOf
 */
Array.prototype.remove = function(value,all,strict) {
    while (this.contains(value,strict)) {
        this.splice(this.indexOf(value,0,strict),1);
        if (!all) break
    }
    return this;
}



/*  A.merge(a [, a]*)
    Append the contents of provided arrays into the current
    takes: one or more arrays
    returns: current array (modified)
*/
Array.prototype.merge = function() {
    var a = [];
    for (var i=0; i<arguments.length; i++)
        for (var j=0; j<arguments[i].length; j++)
            a.push(arguments[i][j]);
    for (var i=0; i<a.length; i++) this.push(a[i]);
    return this
}



/*  A.min()
    returns the smallest item in array by comparing them with >
*/
Array.prototype.min = function() {
    if (!this.length) return;
    var n = this[0];
    for (var i=1; i<this.length; i++) if (n>this[i]) n=this[i];
    return n;
}



/*  A.min()
    returns the graetest item in array by comparing them with <
*/
Array.prototype.max = function() {
    if (!this.length) return;
    var n = this[0];
    for (var i=1; i<this.length; i++) if (n<this[i]) n=this[i];
    return n;
}



/*  A.first()
    returns first element of Array
*/
Array.prototype.first = function() { return this[0] }



/*  A.last()
    returns last element of Array
*/
Array.prototype.last = function() { return this[this.length-1] }



/*  A.sjoin()
    Shorthand for A.join(' ')
*/
Array.prototype.sjoin = function() { return this.join(' ') }



/*  A.njoin()
    Shorthand for A.join('\n')
*/
Array.prototype.njoin = function() { return this.join('\n') }



/*  A.cjoin()
    Shorthand for A.join(', ')
*/
Array.prototype.cjoin = function() { return this.join(', ') }



/*  A.equals(a [, strict])
    true if all elements of array are equal to all elements of `a` in the same
    order. if strict is specified and true, all elements must be equal and of
    the same type.
*/
Array.prototype.equals = function(a, strict){
    if (this==a) return true;
    if (a.length != this.length) return false;
    return this.map(function(item,idx){
        return strict? item === a[idx] : item == a[idx]
    }).all();
}



/*  A.all([fn])
    Returns true if fn returns true for all elements in array
    if fn is not specified, returns true if all elements in array evaluate to
    true
*/
Array.prototype.all = function(fn) {
    return filter(this, fn).length == this.length;
}



/*  A.any([fn])
    Returns true if fn returns true for any elements in array
    if fn is not specified, returns true if at least one element in array 
    evaluates to true
*/
Array.prototype.any = function(fn) {
    return filter(this, fn).length > 0;
}



/*  A.each(fn)
    method form of each function
*/
Array.prototype.each = function(fn) { return each(this, fn) }



/*  A.map([fn])
    method form of map function
*/
Array.prototype.map = function(fn) { return map(this, fn) }



/*  A.filter([fn])
    method form of filter function
*/
Array.prototype.filter = function(fn) { return filter(this, fn) }


Array.prototype.select = Array.prototype.filter


/*  A.reduce([initial,] fn)
    method form of filter function
*/
Array.prototype.reduce = function() {
    var args = map(arguments);
    fn = args.pop();
    d  = args.pop();
    return reduce(this, d, fn); 
}


Array.prototype.inject = Array.prototype.reduce



/*  A.reject(fn)
    deletes items in A *in place* for which fn(item) is true
    returns a
*/
Array.prototype.reject = function(fn) {
    if (typeof(fn)=='string') fn = __strfn('item,idx,list', fn);
    var self = this;
    var itemsToRemove = [];
    fn = fn || function(v) {return v};
    map(self, function(item,idx,list) { if (fn(item,idx,list)) itemsToRemove.push(idx) } );
    itemsToRemove.reverse().each(function(idx) { self.splice(idx,1) });
    return self;
}



/*  __strfn(args, fn)
    this is used internally by each, map, combine, filter and reduce to accept
    strings as functions.

    takes:
        `args` -> a string of comma separated names of the function arguments
        `fn`   -> the function body

    if `fn` does not contain a return statement, a return keyword will be added
    before the last statement. the last statement is determined by removing the
    trailing semicolon (';') (if it exists) and then searching for the last
    semicolon, hence, caveats may apply (i.e. if the last statement has a
    string or regex containing the ';' character things will go wrong)
*/
function __strfn(args, fn) {
    function quote(s) { return '"' + s.replace(/"/g,'\\"') + '"' }
    if (!/\breturn\b/.test(fn)) {
        fn = fn.replace(/;\s*$/, '');
        fn = fn.insert(fn.lastIndexOf(';')+1, ' return ');
    }
    return eval('new Function('
        + map(args.split(/\s*,\s*/), quote).join()
        + ','
        + quote(fn)
        + ')'
        );
}



/*  each(list, fn)
    traverses `list`, applying `fn` to each item of `list`
    takes:
        `list` -> anything that can be indexed and has a `length` property.
                  usually an array.
        `fn`   -> either a function, or  a string containing a function body,
                  in which case the name of the paremeters passed to it will be
                  'item', 'idx' and 'list'.
                  se doc for `__strfn` for peculiarities about passing strings
                  for `fn`

    `each` provides a safe way for traversing only an array's indexed items,
    ignoring its other properties. (as opposed to how for-in works)
*/
function each(list, fn) {
    if (typeof(fn)=='string') return each(list, __strfn('item,idx,list', fn));
    for (var i=0; i < list.length; i++) fn(list[i], i, list);
}


/*  map(list [, fn])
    traverses `list`, applying `fn` to each item of `list`, returning an array
    of values returned by `fn`

    parameters work the same as for `each`, same `__strfn` caveats apply

    if `fn` is not provided, the list item is returned itself. this is an easy
    way to transform fake arrays (e.g. the arguments object of a function or
    nodeList objects) into real javascript arrays.
    e.g.: args = map(arguments)

    If you don't care about map's return value, you should use `each`

    this is a simplified version of python's map. parameter order is different,
    only a single list (array) is accepted, and the parameters passed to [fn]
    are different:
    [fn] takes the current item, then, optionally, the current index and a
    reference to the list (so that [fn] can modify list)
    see `combine` if you want to pass multiple lists
*/
function map(list, fn) {
    if (typeof(fn)=='string') return map(list, __strfn('item,idx,list', fn));

    var result = [];
    fn = fn || function(v) {return v};
    for (var i=0; i < list.length; i++) result.push(fn(list[i], i, list));
    return result;
}


/*  combine(list [, list]* [, fn])

    takes:
        `list`s -> one or more lists (see `each` for definition of a list)
        `fn`    -> Similar s `each` or `map`, a function or a string containing
                   a function body.
                   if a string is used, the name of parameters passed to the
                   created function will be the lowercase alphabet letters, in
                   order: a,b,c...
                   same `__strfn` caveats apply

    combine will traverse all lists concurrently, passing each row if items as
    parameters to `fn`
    if `fn` is not provided, a function that returns a list containing each
    item in the row is used.
    if a list is smaller than the other, `null` is used in place of its missing
    items

    returns:
        an array of the values returned by calling `fn` for each row of items
*/
function combine() {
    var args   = map(arguments);
    var lists  = map(args.slice(0,-1),'map(item)');
    var fn     = args.last();
    var toplen = map(lists, "item.length").max();
    var vals   = [];

    if (!fn) fn = function(){return map(arguments)};
    if (typeof fn == 'string') {
        if (lists.length > 26) throw 'string functions can take at most 26 lists';
        var a = 'a'.charCodeAt(0);
        fn = __strfn(map(range(a, a+lists.length),'String.fromCharCode(item)').join(','), fn);
    }

    map(lists, function(li) {
        while (li.length < toplen) li.push(null);
        map(li, function(item,ix){
            if (ix < vals.length) vals[ix].push(item);
            else vals.push([item]);
        });
    });

    return map(vals, function(val) { return fn.apply(fn, val) });
}



/*  filter(list [, fn])
    returns an array of items in `list` for which `fn(item)` is true

    parameters work the same as for `each`, same `__strfn` caveats apply

    if `fn` is not specified the items are evaluated themselves, that is,
    filter will return an array of the items in `list` which evaluate to true

    this is a similar to python's filter, but parameter order is inverted
*/
function filter(list, fn) {
    if (typeof(fn)=='string') return filter(list, __strfn('item,idx,list', fn));

    var result = [];
    fn = fn || function(v) {return v};
    map(list, function(item,idx,list) { if (fn(item,idx,list)) result.push(item) } );
    return result;
}



/*  reduce(list [, initial], fn)
    similar to python's reduce. paremeter onder inverted...

    TODO: document this properly

    takes:
        `list`   -> see doc for `each` to learn more about it
        `inirial -> TODO: doc`
        `fn`     -> similar to `each` too, but in the case where it's a string,
                    the name of the paremeters passed to it will be 'a' and 'b'
                    same `__strfn` caveats apply

*/
function reduce(list, initial, fn) {
    if (undef(fn)) {
        fn      = initial;
        initial = window.undefined; // explicit `window` object so browsers that do not have an `undefined` keyword will evaluate to the (hopefully) undefined parameter `undefined` of `window` 
    }
    if (typeof(fn)=='string') return reduce(list, initial, __strfn('a,b', fn));
    if (isdef(initial)) list.splice(0,0,initial);
    if (list.length===0) return false;
    if (list.length===1) return list[0];
    var result = list[0];
    var i = 1;
    while(i<list.length) result = fn(result,list[i++]);
    return result;
}

/*  range(start, stop, step)
    identical to python's range.
    range(stop)
    range(start,stop)
    range(start,stop,step)

    Return a list containing an arithmetic progression of integers.
    range(i, j) returns [i, i+1, i+2, ..., j-1]; start (!) defaults to 0.
    When step is given, it specifies the increment (or decrement).
    For example, range(4) returns [0, 1, 2, 3].  The end point is omitted!
    [from python's range's docstring]
*/
function range(start,stop,step) {
    if (isUndefined(stop)) return range(0,start,step);
    if (isUndefined(step)) step = 1;
    var ss = (step/Math.abs(step)); // step sign
    var r = [];
    for (i=start; i*ss<stop*ss; i=i+step) r.push(i);
    return r;
}