
// variant of string.replace() that works globally
// bwm 2006/7/6: the g flag on RegExp seems not to work in FF.
function sreplace(inStr, inRe, inReplacement) {
    var cn = inStr;
    var ncn = inStr.replace(inRe, inReplacement);
    while (ncn != cn) {
        cn = ncn;
        ncn = cn.replace(inRe, inReplacement);
    }
    return ncn;
}

// Convert inElemID to a DOM Element
// allow functions below to take either a DOM Element or an id attribute name
function to_element(inElemID) {
    var elem = null;
    elem = is_object(inElemID)?inElemID:document.getElementById(inElemID);

    if (!is_object(elem)) {
        if (window.error) {
            window.error('invalid element id: '+inElemID);
        }
        return false;
    }
    return elem;
} // to_element

// find and remove any occurrences of inOldClass and inNewClass
// in inElem.className.  Append inNewClass to inElem.className
function replace_class(inElem, inOldClass, inNewClass) {
    var elem = null;
    elem = to_element(inElem);

    if (!is_object(elem)) {
        if (window.error) {
            window.error('replace_class('+inOldClass + ', ' + inNewClass+'): invalid element' + elem);
        }
        return false;
    }
    if (window.debug) {
        window.debug('replace_class('+inOldClass + ', ' + inNewClass+')');
    }
    var cn = elem.className;
    var re_hyphen = new RegExp('\\-', 'g');
    var oldClass = sreplace(inOldClass, re_hyphen, '\\-');
    var re_old = new RegExp('(^|\\s)' + oldClass + '(\\s|$)', 'ig');

    var newClass = sreplace(inNewClass, re_hyphen, '\\-');
    var re_new = new RegExp('(^|\\s)' + newClass + '(\\s|$)', 'ig');

    cn = sreplace(cn, re_old, ' ');
    cn = sreplace(cn, re_new, ' ');

    cn = ''+ cn + ' ' + inNewClass;
    //if (window.info) {
    //    window.info('  replacing "' + elem.className + '" with "' + cn +'".');
    //}
    elem.className = cn;
    return true;
} // replace_class


/*
 * getElementsByClassName
   find all elements like <strTagName class="... strClassName ...">
 *
 * 2008-05-08 BWM: Firebug says Snook/Nyman (SN) implementation accounts for
 *    50% of all JS execution time and a whole second during page load.
 */

function getElementsByClassName(inElm, strTagName, strClassName){
    if (document.getElementsByClassName !== undefined) {
        // Gecko 1.9, webkit, HTML5 native
        window.debug('Native getElementsByClassName() not supported');
    }

    if (document.evaluate  !== undefined) {
        // FF2 et al have DOM3/XPath
        // window.info('Using XPath getElementsByClassName');
        if (inElm == document) {
            inElm = to_element('body');
        }
        return getElementsByClassName_XPath(inElm, strTagName, strClassName);
    }

    window.info('Using DOM getElementsByClassName');
    return getElementsByClassName_SN(inElm, strTagName, strClassName);
} // getElementsByClassName

function getElementsByClassName_XPath(inElm, strTagName, strClassName){
    // http://ejohn.org/blog/getelementsbyclassname-speed-comparison/
    // http://www.prototypejs.org/assets/2007/11/6/prototype.js
    var startt = Date.now();
    var q = ".//"+strTagName + "[contains(concat(' ', @class, ' '), ' " + strClassName + " ')]";

    var oElm = to_element(inElm);
    var results = [];
    var query = document.evaluate(q, oElm || document, null,
                                  XPathResult.ORDERED_NODE_SNAPSHOT_TYPE,
                                  null);
    for (var i = 0, len = query.snapshotLength; i < len; i++) {
        results.push(query.snapshotItem(i));
    }
    var elapset = Date.now() - startt;
    if (elapset > 400) {
        window.error('slow getElementsByClassName('+to_string_shallow(oElm) + ', '
                     + strTagName + ', ' + strClassName +')');
    }
    return results;
} // getElementsByClassName_XP

function getElementsByClassName_DOM(inElm, strTagName, strClassName){

    // Written by Jonathan Snook, http://www.snook.ca/jonathan
    // Add-ons by Robert Nyman, http://www.robertnyman.com
    var oElm = to_element(inElm); // bwm, allow id
    var arrElements = (strTagName == "*" && document.all)? document.all : oElm.getElementsByTagName(strTagName);
    var arrReturnElements = new Array();
    strClassName = strClassName.replace(/\-/g, "\\-");
    var oRegExp = new RegExp("(^|\\s)" + strClassName + "(\\s|$)");
    var oElement;
    for(var i=0; i<arrElements.length; i++){
        oElement = arrElements[i];
        if(oRegExp.test(oElement.className)){
            arrReturnElements.push(oElement);
        }
    }
    return (arrReturnElements)
} // getElementsByClassName_DOM

// Master reset button.
// do form.reset() on every form on this page.
function revert_forms() {
    var form_nodes = window.document.getElementsByTagName('form');
    var i = 0;
    var len = form_nodes.length;
    for(i = 0; i < len; i++) {
        if (form_nodes[i]) {
            form_nodes[i].reset();
        }
    }
} // revert_forms

// Find the first form element with substring inAliasName somewhere
// under the inRecord element.  Search is case insensitive.
function get_form_field(inRecord, inAliasName) {
    var record = to_element(inRecord);

    var candidates = getElementsByTagNames('input,select,textarea', record);
    var ic = candidates.length;
    var cn = '';
    var alias = inAliasName.toLowerCase();
    var found = null;
    for(var i = 0; i < ic; i++) {
        cn = candidates[i].name.toLowerCase();
        if (cn.indexOf(alias) > -1) {
            found = candidates[i];
            break;
        }
    }
    if (!found) {
        warn('cannot find ' + inAliasName + ' under ' + inRecord);
    }
    return found;
} // get_form_field


// Find elements by multiple tag names, return them in
// document order (when possible).
// taken from
// http://www.quirksmode.org/dom/getElementsByTagNames.html
function getElementsByTagNames(list,inObj) {
    var obj;
    if (!inObj) {
        obj = document;
    } else {
        obj = inObj;
    }
 var tagNames = list.split(',');
 var resultArray = new Array();
 for (var i=0;i<tagNames.length;i++) {
  var tags = obj.getElementsByTagName(tagNames[i]);
  for (var j=0;j<tags.length;j++) {
   resultArray.push(tags[j]);
  }
 }
 var testNode = resultArray[0];
 if (!testNode) return [];
 if (testNode.sourceIndex) {
  resultArray.sort(function (a,b) {
    return a.sourceIndex - b.sourceIndex;
  });
 }
 else if (testNode.compareDocumentPosition) {
  resultArray.sort(function (a,b) {
    return 3 - (a.compareDocumentPosition(b) & 6);
  });
 }
 return resultArray;
} // getElementsByTagNames

// return ancestor element with name inTag that
// contains inElem
function getAncestorByTag(inElem, inTag) {
    var current = to_element(inElem);
    var tag = inTag.toLowerCase();
    while (current && current.tagName && (current.tagName.toLowerCase() != tag)) {
        if (current.parentNode) {
            current = current.parentNode;
        } else {
            current = null;
        }
    }
    return current;
} // getAncestorByTag

// return ancestor element of inElem with CSS class inClass
function getAncestorByClass(inElem, inClass) {
    var current = to_element(inElem);
    var find_class = inClass.replace(/\-/g, "\\-");
    var re = new RegExp("(^|\\s)" + find_class + "(\\s|$)");
    while (current && (current.className !== undefined)
           && (!re.test(current.className)) ) {
        if (current.parentNode) {
            current = current.parentNode;
        } else {
            current = null;
        }
    }
    return current;
} // getAncestorByClass

// return form element with name attribute matching inRe
function getElementMatchingName(inElem, inRe) {
    var current = inElem?to_element(inElem):document;

    // find elements under current that are allowed to have name attr
    var list = 'input,select,textarea';
    var tagNames = list.split(',');
    var resultArray = new Array();
    for (var i=0;i<tagNames.length;i++) {
        var tags = current.getElementsByTagName(tagNames[i]);
        if (inRe instanceof RegExp) {
            // regex based search
            for (var j=0;j<tags.length;j++) {
                if (tags[j].name && inRe.test(tags[j].name)) {
                    window.debug('getElementMatchingName found regexp '+tags[j].name);
                    return tags[j];
                }
            }
        } else {
            // string based search
            for (var j=0;j<tags.length;j++) {
                if (tags[j].name && (tags[j].name.indexOf(inRe) > -1)) {
                    window.debug('getElementMatchingName found substring '+tags[j].name);
                    return tags[j];
                }
            }
        }
    }
    return false;
} // getElementMatchingName
