/*
lib.js
by Caio Chassot (http://v2studio.com/k/code/)
parts by other authors, see commented version for details

version 0.5.75 
File generated on Fri Sep 24 00:35:55 E. South America Standard Time 2004

for documentation, see commented version.
get commented version at http://v2studio.com/k/code/lib/lib_c.js

Use freely. Credit is appreciated but not required.
*/
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;
}

Array.prototype.find = function(value, start, strict) {
    var i = this.indexOf(value, start, strict);
    if (i != -1) return this[i];
    return null
}

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;

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;
}

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;
}

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
}

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;
}

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;
}

Array.prototype.first = function() { return this[0] }

Array.prototype.last = function() { return this[this.length-1] }

Array.prototype.sjoin = function() { return this.join(' ') }

Array.prototype.njoin = function() { return this.join('\n') }

Array.prototype.cjoin = function() { return this.join(', ') }

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();
}

Array.prototype.all = function(fn) {
    return filter(this, fn).length == this.length;
}

Array.prototype.any = function(fn) {
    return filter(this, fn).length > 0;
}

Array.prototype.each = function(fn) { return each(this, fn) }

Array.prototype.map = function(fn) { return map(this, fn) }

Array.prototype.filter = function(fn) { return filter(this, fn) }

Array.prototype.select = Array.prototype.filter

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

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;
}

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)
        + ')'
        );
}

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);
}

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;
}

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) });
}

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;
}

function reduce(list, initial, fn) {
    if (undef(fn)) {
        fn      = initial;
        initial = window.undefined; 
    }
    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;
}

function isAlien(a)     { return isObject(a) && typeof a.constructor != 'function' }

function isArray(a)     { return isObject(a) && a.constructor == Array }

function isBoolean(a)   { return typeof a == 'boolean' }

function isFunction(a)  { return typeof a == 'function' }

function isNull(a)      { return typeof a == 'object' && !a }

function isNumber(a)    { return typeof a == 'number' && isFinite(a) }

function isObject(a)    { return (a && typeof a == 'object') || isFunction(a) }

function isRegexp(a)    { return a && a.constructor == RegExp }

function isString(a)    { return typeof a == 'string' }

function isUndefined(a) { return typeof a == 'undefined' }

function isEmpty(o)     {
    var i, v;
    if (isObject(o)) {
        for (i in o) {
            v = o[i];
            if (isUndefined(v) && isFunction(v)) {
                return false;
            }
        }
    }
    return true;
}

if (!Function.prototype.apply) Function.prototype.apply = function (o, a) {
    var r, x = '____apply';
    if (!isObject(o)) {
        o = {};
    }
    o[x] = this;
    switch ((a && a.length) || 0) {
    case 0:
        r = o[x]();
        break;
    case 1:
        r = o[x](a[0]);
        break;
    case 2:
        r = o[x](a[0], a[1]);
        break;
    case 3:
        r = o[x](a[0], a[1], a[2]);
        break;
    case 4:
        r = o[x](a[0], a[1], a[2], a[3]);
        break;
    case 5:
        r = o[x](a[0], a[1], a[2], a[3], a[4]);
        break;
    case 6:
        r = o[x](a[0], a[1], a[2], a[3], a[4], a[5]);
        break;
    default:
        alert('Too many arguments to apply.');
    }
    delete o[x];
    return r;
}

if (!Array.prototype.pop) Array.prototype.pop = function () {
    return this.splice(this.length - 1, 1)[0];
}

if (!Array.prototype.push) Array.prototype.push = function() {
    for (var i=0; i<arguments.length; i++) this[this.length] = arguments[i];
    return this.length;
}

if (!Array.prototype.shift) Array.prototype.shift = function () {
    return this.splice(0, 1)[0];
}

if (!Array.prototype.splice) Array.prototype.splice = function (s, d) {
    var max = Math.max,
        min = Math.min,
        a = [], 
        e,  
        i = max(arguments.length - 2, 0),   
        k = 0,
        l = this.length,
        n,  
        v,  
        x;  

    s = s || 0;
    if (s < 0) {
        s += l;
    }
    s = max(min(s, l), 0);  
    d = max(min(isNumber(d) ? d : l, l - s), 0);    
    v = i - d;
    n = l + v;
    while (k < d) {
        e = this[s + k];
        if (!isUndefined(e)) {
            a[k] = e;
        }
        k += 1;
    }
    x = l - s - d;
    if (v < 0) {
        k = s + i;
        while (x) {
            this[k] = this[k - v];
            k += 1;
            x -= 1;
        }
        this.length = n;
    } else if (v > 0) {
        k = 1;
        while (x) {
            this[n - k] = this[l - k];
            k += 1;
            x -= 1;
        }
    }
    for (k = 0; k < i; ++k) {
        this[s + k] = arguments[k + 2];
    }
    return a;
}

if (!Array.prototype.unshift) Array.prototype.unshift = function () {
    this.splice.apply(this,
        [0, 0].concat(Array.prototype.slice.apply(arguments)));
    return this.length;
}

if ( /^function\s*\(\s*\)\s*\{\s*\}$/.test('a'.replace(/a/,function(){})) ) {
    String.prototype.replace_ = String.prototype.replace;
    String.prototype.replace = function(rx, rf) { 
        if (isFunction(rf)) {
            var s = this,
                r,   
                rfv, 
                replaceList = [];
            while ((r=rx.exec(s))!==null) {
                rfv = rf.apply(rf, map(r).concat([r.index, s]));
                replaceList.push({ start: r.index, length: r[0].length, value: rfv });
                if (!rx.global) break;
            }
            for (var i=replaceList.length-1; i>=0; i--) {
                var rli = replaceList[i];
                s = s.splice(rli.start, rli.length, rli.value);
            }
            return s;
        } else return this.replace_(rx, rf);
    }
}

String.prototype.trimLeft = function() { return this.replace(/^\s+/,'') }

String.prototype.trimRight = function() { return this.replace(/\s+$/,'') }

String.prototype.trim = function() { return this.replace(/^\s+|\s+$/g,'') }

String.prototype.insert = function(idx,value) { return this.slice(0,idx) + value + this.slice(idx) }

String.prototype.strip = function(idx1,idx2) {
    if (arguments.length==1) idx2 = this.length;
    return this.slice(0,idx1) + this.slice(idx2);
}

String.prototype.splice = function(idx,count,value) { return this.strip(idx, idx+count).insert(idx, value) }

String.prototype.subArgs = function() { return this.subDict(map(arguments)) }

String.prototype.subDict = function(dict) {
    var
        parts = this.split('{'),
        r = parts[0],
        part, ci, k, v;
    for (var i = 1; i < parts.length; i++) {
        part = parts[i];
        ci   = part.indexOf('}');
        k    = part.substring(0,ci);
        v    = dict[k];
        r   += isdef(v) ? v + part.substring(ci+1) : '{' + part;
    }
    return r;
}

String.prototype.subn = function(d) {
    if (arguments.length > 1 || !isObject(arguments[0])) d = map(arguments);
    return this.subDict(d);
}

String.prototype.wrap = function(left,right) {
    if (undef(left)) throw 'S.wrap takes 1 argument (none given)';
    if (undef(right)) right = left;
    return left + this + right;
}

String.prototype.quote = function() { return this.wrap('"') }

String.prototype.squote = function() { return this.wrap("'") }

String.prototype.pad = function(side, len, chr) {
    if (undef(chr)) chr = ' ';
    var s = this;
    var left = side.toLowerCase()=='left';
    while (s.length<len) s = left? chr + s : s + chr;
    return s;
}

String.prototype.padLeft = function(len, chr) { return this.pad('left',len,chr) }

String.prototype.padRight = function(len, chr) { return this.pad('right',len,chr) }

String.prototype.zerofill = function(len) {
    var s = this;
    var ix = /^[+-]/.test(s) ? 1 : 0;
    while (s.length<len) s = s.insert(ix, '0');
    return s;
}

String.prototype.isEmpty = function(donttrim) { return !(donttrim? this : this.trim()).length }

function undef(v) { return  isUndefined(v) }

function isdef(v) { return !isUndefined(v) }

function cmp(a,b) { return a<b?-1:a==b?0:1 }

function list(s, sep) {
    if (!isString(sep) && !isRegexp(sep))
        sep = sep? ',' : /\s*,\s*/;
    return s.split(sep);
}

function range(start,stop,step) {
    if (isUndefined(stop)) return range(0,start,step);
    if (isUndefined(step)) step = 1;
    var ss = (step/Math.abs(step)); 
    var r = [];
    for (i=start; i*ss<stop*ss; i=i+step) r.push(i);
    return r;
}

function maprange(start, stop, fn) {
    if (arguments.length==2) return maprange(0, start, stop);
    if (arguments.length!=3) throw "maprange takes 2 or 3 arguments";
    return map(range(start,stop), fn);
}

function isList(o) { return o && isObject(o) && (isArray(o) || o.item) }

function isElement(o, strict) {
    return o && isObject(o) && ((!strict && (o==window || o==document)) || o.nodeType == 1)
}

function getElem(el) {
    var ge = (document.getElementById && function(id){return document.getElementById(id)} ) ||
             (document.all && function(id){return document.all[id]} ) ||
             function(){return null};
    return isElement(el)? el : isString(el) ? ge(el) : null;
}

function getElemList(el) {
    if      (isElement(el)) return [el];
    else if (isString(el) ) return getElemList(el.split(/\s+/g)); 
    else if (isList(el)   ) {
        var r = map(el, getElem);
        return filter(r, isElement).length==r.length? r : null;
    }
    else return null;
}

function filterElementNodes(nodeList, tagName) {
    return filter(nodeList, function(n){
        
        
        
        
        return n.nodeType==1 && n.nodeName!='!' && 
               (undef(tagName) || tagName == '*' || n.nodeName.toUpperCase()==tagName.toUpperCase())
    })
}

function getAll(tagName, parent) {
    parent = isdef(parent)? getElem(parent) : document;
    if (undef(tagName)) tagName = '*';
    var r = parent.getElementsByTagName(tagName);
    
    return r.length || tagName != '*'?  map(r) :
        reduce(filterElementNodes(parent.childNodes), [], function(l,c){
            return l.merge([c], getAll(tagName, c))
        })
}

function getElementsByClass(className, tagName, parentNode) {
    
    var noClassTags = list('#comment,BASE,BASEFONT,HEAD,HTML,META,PARAM,SCRIPT,STYLE,TITLE');
    return filter(getAll(tagName,parentNode),
        function(elem) {
            return !noClassTags.include(elem.nodeName) && hasClass(elem, className) 
        });
}

getElementsByClassName = getElementsByClass;

function hasClass(elem, className) {
    return getElem(elem).className.split(' ').count(className);
}

function remClass(elem, className, all) {
    elem = getElem(elem);
    elem.className = elem.className.split(' ').remove(className,all).join(' ');
}

function addClass(elem, className, allowDuplicates) {
    elem = getElem(elem);
    if (!allowDuplicates && elem.className.split(' ').contains(className)) return;
    elem.className += (elem.className.length?' ':'') + className;
}

function swapClass(e,c) {
    if (hasClass(e,c)) remClass(e,c); else addClass(e,c);
    return !!hasClass(e,c);
}

function condClass(e,c,cond) { (cond?addClass:remClass)(e,c) }

function insertBefore(newChild, refChild) {
    return refChild.parentNode.insertBefore(newChild, refChild);
}

function insertAfter(newChild, refChild) {
    if (refChild.nextSibling) insertBefore(newChild, refChild.nextSibling);
    else refChild.parentNode.appendChild(newChild);
    return newChild;
}

var ALLOW_LEGACY_EVENTS = true;

function getEventModel() {
    var d = document;
    return d.addEventListener? 'DOM' :
           d.attachEvent     ? 'IE'  :
                               'legacy';
}

function IE_Event(currentTarget) {
    this.currentTarget   = currentTarget;
    this.preventDefault  = function() { window.event.returnValue  = false }
    this.stopPropagation = function() { window.event.cancelBubble = true }
    this.target  = window.event.srcElement;
    var self = this;
    
    list('altKey,ctrlKey,shiftKey,clientX,clientY').map(function(p){ self[p] = event[p] });
    return this;
}

function Legacy_Event(currentTarget) {
    this.currentTarget   = currentTarget;
    return this;
}

function addEvent(els, ev, fn, capture) {
    if (!ALLOW_LEGACY_EVENTS && getEventModel()=='legacy') return false;
    if (undef(capture)) capture = true;
    function DOM_addEvent   (el, ev, fn, capture) { el.addEventListener(ev, fn, capture) }
    function legacy_addEvent(el, ev, fn) {
        var evn = 'on'+ev;
        if (!el[evn] || undef(el[evn].handlers)) {
            el[evn] = function() {
                map(el[evn].handlers, function(h){  h( new (el.attachEvent?IE_Event:Legacy_Event)(el) ) });
            }
            el[evn].handlers = [];
        }
        el[evn].handlers.push(fn);
    }
    var addEventFn = getEventModel()=='DOM'? DOM_addEvent : legacy_addEvent;
    map(getElemList(els), function(el) { addEventFn(el, ev, fn, capture) });
}

function remEvent(els, ev, fn, capture) {
    if (!ALLOW_LEGACY_EVENTS && getEventModel()=='legacy') return false;
    if (undef(capture)) capture = true;
    map(getElemList(els), function(el) {
        if(getEventModel()=='DOM') el.removeEventListener(ev, fn, capture);
        else el['on'+ev].handlers.remove(fn);
    });
}

function addEventDict(els, evDict, capture) {
    for (ev in evDict) addEvent(els, ev, evDict[ev], capture);
}

function addLoadEvent(fn) {
    
    var w = getEventModel()=="DOM" && !window.addEventListener ? document : window;
    return addEvent(w, 'load', fn, true)
}



// DOM EVENTS
function listen(event, elem, func) {
    elem = getElem(elem);
    if (elem.addEventListener)  // W3C DOM
        elem.addEventListener(event,func,false);
    else if (elem.attachEvent)  // IE DOM
        elem.attachEvent('on'+event, function(){ func(new W3CDOM_Event(elem)) } );
        // for IE we use a wrapper function that passes in a simplified faux Event object.
    else throw 'cannot add event listener';
}

function mlisten(event, elem_list, func) {
    map(elem_list, function(elem) { listen(event, elem, func) } );
}

function W3CDOM_Event(currentTarget) {
    this.currentTarget  = currentTarget;
    this.preventDefault = function() { window.event.returnValue = false }
    return this;
}

