
// ADD TO LIB
// as in ruby, but adds depth limit
Array.prototype.flatten = function(depth) {
    var r = this.map();
    if (depth === 0) return r;
    var d = 0;
    do {
        for (var i = r.length; --i >= 0;) {
            var item = r[i];
            if (isArray(item)) {
                r.splice(i, 1);
                item.map().reverse().each(function(itemitem){
                    r.splice(i, 0, itemitem);
                });
            }            
        }
        d++;
    } while (r.any("isArray(item)") && (undef(depth) || depth > d));
    return r;
}
    
// ADD TO LIB
// as in ruby.
String.prototype.scan = function(rx) {
    var m, r = [];
    while (m = rx.exec(this)) r.push(m.length > 1 ? m.slice(1) : m[0]);
    return r;
}

// ADD TO LIB
/* creates an element, optionally with attributes attrs, optionally appending children
the first parameter is a string in css selector form, so you can easily add ids and classes:
ce('ul#menu.floated.inline')
*/
function ce(selector, attrs, children){
    var tagName;
    attrs = dict(attrs);
    var classes = attrs['class'] ? attrs['class'].split(/\s+/) : [];
    selector.scan(/[#\.]?[^#\.]+/g).each(function(chunk){
        if      (chunk.match(/^\./)) classes.push(chunk.substring(1));
        else if (chunk.match(/^#/ )) attrs.id = chunk.substring(1);
        else    tagName = chunk;
    });
    var e = document.createElement(tagName);
    attrs['class'] = classes.sjoin();
    attrs.each(function(k, v) {
        e[{ 'class':'className', 'for':'htmlFor' }[k] || k] = v;
    });
    [children].flatten().filter().each(function(ch){
        e.appendChild(isObject(ch) ? ch : document.createTextNode(ch));
    });
    return e;
}

function getFirst(tagName, parent){
    return getAll(tagName, parent).first()
}

function getFirstOfClass(className, parent) {
    return getElementsByClass(className, '*', parent).first();
}
function getFirstOfName(name, parent) {
    return getAll('*', parent).find(function(e){ 
      return isString(name) ? e.name == name :
             isRegexp(name) ? name.test(e.name) : null;
    });
}

function removeNode(node) {
    node.parentNode.removeChild(node);
}
removeElement = removeNode

function clearElement(e) {
    while (e.hasChildNodes()) e.removeChild(e.lastChild);
}

function ancestors(e, upto) {
    upto = getElem(upto || document.documentElement);
    var p, a = [e];
    while ((p = a.last().parentNode)) {
        a.push(p);
        if (p == upto) break;
    }
    return a;
}

function filterChildren(parent, tagName){
    return parent.childNodes ? filterElementNodes(parent.childNodes, tagName) : null;
}

function replaceElement(newNode, refNode, copyAttrs){
  insertBefore(newNode, refNode)
  map(refNode.childNodes).each(function(ch){newNode.appendChild(ch)})
  if (copyAttrs) copyAttrs.each(function(attr){ newNode[attr] = refNode[attr] })
  removeElement(refNode)
}

function focusFirstControl(parent) {
  var control = getAll('*', getElem(parent)).
    select(function(e){ return /^(input|textarea|select)$/i.test(e.nodeName) }).
    reject(function(e){ return e.type == 'hidden' || e.disabled }).first()
  if (control) setTimeout(function(){control.focus()}, 0); // to happen after focus is given to the clicked item that might have trigerred this call
}
toggleClass = swapClass

// ADD TO LIB ?
function getInnerText(e) {
    var t = e.__innerTextCache || map(e.childNodes, function(c){
        switch (c.nodeType) {
            // ELEMENT_NODE
            case 1 : switch(c.nodeName.toLowerCase()) {
                case 'br': return '\n';
                default  : return getInnerText(c); 
            }
            // TEXT_NODE
            case 3 : return c.nodeValue || '';
            default: return ''
        }
    }).join('');
    e.__innerTextCache = t;
    return t;
}

function hasExtAttribute(elem, attr) {
    // needed because ie fails if attr is not present
    try {
        return elem.hasAttribute('ext:' + attr)
    } catch(e) {
        return false
    }
}

function getExtAttribute(elem, attr) {
    // needed because ie fails if attr is not present
    try {
        return elem.getAttribute('ext:' + attr)
    } catch(e) {
        return ''
    }
}

String.prototype.deaccentuate = function() {
    var subs = [
    // not using [] groups in regexps because of safari bug. e.g. 'á' would get replaced by 'áa' instead of 'a'
        [/á|à|ä|â|ã|å/g, 'a' ],
        [/Á|À|Ä|Â|Ã/g  , 'A' ],
        [/é|è|ë|ê/g    , 'e' ],
        [/É|È|Ë|Ê/g    , 'E' ],
        [/í|ì|ï|î/g    , 'i' ],
        [/Í|Ì|Ï|Î/g    , 'I' ],
        [/ó|ò|ö|ô|õ|ø/g, 'o' ],
        [/Ó|Ò|Ö|Ô|Õ|Ø/g, 'O' ],
        [/ú|ù|ü|û/g    , 'u' ],
        [/Ú|Ù|Ü|Û/g    , 'U' ],
        [/ý|ÿ/g        , 'y' ],
        [/Ý/g          , 'Y' ],
        [/ç/g          , 'c' ],
        [/Ç/g          , 'C' ],
        [/ñ/g          , 'n' ],
        [/Ñ/g          , 'N' ],
        [/ð|þ/g        , 'th'],
        [/Ð|Þ/g        , 'TH'],
        [/æ/g          , 'ae'],
        [/Æ/g          , 'AE'],
        [/ß/gi         , 'ss']];
    return subs.reduce(this,function(str, sub){ return str.replace(sub[0], sub[1]) });
}


