// $Id: util.js 771 2009-11-25 23:21:18Z jason $


/**
 * Converts an integral duration to HH:MM:SS.
 * @param   timeval duration in seconds
 * @return  @a timeval as HH:MM:SS.
 */
function _formatInterval( timeval )
{
    if( timeval <= 0 )
        return '00:00:00';

    var seconds = parseInt( timeval%60 );
    if( seconds <= 9 ) seconds = '0' + (seconds);
    var minutes = Math.floor(timeval/60)%60;
    if( minutes <= 9 ) minutes = '0' + (minutes);
    var hours = Math.floor(timeval/3600);
    if( hours <= 9 ) hours = '0' + (hours);

    return hours + ':' + minutes + ':' + seconds;
}


// Stolen from Chris's autosug.js :)
function _make_xhr( )
{
    var xhr = null;
    try {
        xhr = new XMLHttpRequest();
    } catch (trymicrosoft) {
        try {
            xhr = new ActiveXObject("Msxml2.XMLHTTP");
        } catch (othermicrosoft) {
            try {
                xhr = new ActiveXObject("Microsoft.XMLHTTP");
            } catch (failed) {
                xhr = false;
            }
        }
    }
    return xhr;
}


/* from http://onlinetools.org/articles/unobtrusivejavascript/chapter4.html
 * (in turn based on http://www.scottandrew.com/weblog/articles/cbs-events) */
function _addEvent( obj, evType, fn )
{
    if (obj.addEventListener){ 
        obj.addEventListener(evType, fn, false); 
        return true; 
    } else if (obj.attachEvent){ 
        var r = obj.attachEvent("on"+evType, fn); 
        return r; 
    } else { 
        return false; 
    }
}


// from: http://www.openjs.com/articles/prevent_default_action/
function _stopEvent(e) {
    e = e || window.event;

    //e.cancelBubble is supported by IE - this will kill the bubbling process.
    e.cancelBubble = true;
    e.returnValue = false;

    //e.stopPropagation works only in Firefox.
    if (e.stopPropagation) {
        e.stopPropagation();
        e.preventDefault();
    }
    return false;
}


//----------------------------------------------------
// credit for the following goes to PPK@quirksmode.org
// pp]{

function _findPos(obj)
{
    var curleft = 0;
    var curtop = 0;
    if (obj.offsetParent)
        while( obj )
        {
            curtop += obj.offsetTop;
            curleft += obj.offsetLeft;
            obj = obj.offsetParent;
        }
    else if (obj.x)
    {
        curleft += obj.x;
        curtop += obj.y;
    }
    return [ curleft,curtop ];
}

function _findMouse( e )
{
    var posx = 0;
    var posy = 0;
    e = e || window.event;

    if( e.pageX || e.pageY )
    {
        posx = e.pageX;
        posy = e.pageY;
    }
    else if( e.clientX || e.clientY )
    {
        posx = e.clientX + ( document.documentElement.scrollLeft ||
            document.body.scrollLeft || 0 );
        posy = e.clientY + ( document.documentElement.scrollTop ||
            document.body.scrollTop || 0 );
    }

    return [ posx,posy ];
}

//}[
//--


/**
 * Returns the value if the selected element of the provided radio button group.
 * Why radio groups act as single elements rather than one-element arrays when
 * only one item is present seems like a horrible design flaw.
 *
 * @param   el      radio button element
 *
 * @return  the value of the selected element, null if none chosen
 */
function radio_get_selected( el )
{
    if( typeof el.length == 'undefined' )
        return el.checked ? el.value : null;    // single element
    else
        for( var i=0; i < el.length; ++i )
            if( el[i].checked )
                return el[i].value;
    return null;
}


/**
 * Request a new page number for a large result set.
 * @a fieldid is updated to the new page number, and its associated form is
 * submitted.
 * @param   fieldid ID of the field to update
 * @param   pagenum page number to switch
 */
function changepage( fieldid, pagenum )
{
    var field = document.getElementById( fieldid );
    field.value = pagenum;
    field.form.submit( );
}


/**
 * Escapes characters matching <tt>[&amp;&lt;&gt;]</tt>.
 * @param   str string to escape
 * @return  escaped version of @a str
 */
function escape_html( str )
{
    return str.split("&").join("&amp;").split("<").join("&lt;").
        split(">").join("&gt;");
}


/**
 * Given a form ID, checks all of its check boxes.
 * @param   formId      ID of form
 * @param   selected    true to check, false to uncheck
 * @param   name        if not null, restrict to inputs with this name
 */
function form_select_all( formId, selected, name )
{
    var formEl = document.getElementById( formId );
    if( !formEl ) return;
    for( var i = 0; i < formEl.length; ++i )
        if(
            formEl.elements[i].type == 'checkbox' &&
            (name && formEl.elements[i].name == name)
        )
            formEl.elements[i].checked = selected;
}


/**
 * Simple function to push a class name onto a class tag.
 * @param   el      element instance
 * @param   classn  class tname
 * @see             popclass()
 */
function pushclass( el, classn )
{
    if( el.className )
        el.className = el.className + " " + classn;
    else
        el.className = classn;
}


/**
 * Simple function to pop off the last item in a class tag.
 * @param   el  element instance
 * @see         pushclass()
 */
function popclass( el )
{
    if( !el.className ) return; // dumbass :P

    len = el.className.length;
    spaceat = el.className.lastIndexOf( " " );

    if( spaceat == -1 )
        el.className = "";
    else
        el.className = el.className.substring( 0,spaceat );
}


/**
 * Returns the requested attribute value, or null if it does not exist.
 *
 * @note
 * The whole namespace thing only works with proper XML; that is, the document
 * must be served as <tt>application/xhtml+xml</tt>. We don't do that right now.
 * When we do, @a namespace must contain the namespace URI, not the namespace
 * prefix. Sigh.
 *
 * @note
 * Okay, I guess this is wholly pointless right now since IE doesn't do
 * hasAttribute().
 *
 * @param   element     instantiated element
 * @param   namespace   attribute namespace ('' for no namespace)
 * @param   attrname    attribute name (local part)
 * @return  attribute value, or null if not found
 */
function getAttribute( element, namespace, attrname )
{
    /* applicable when served as application/xhtml+xml...?
    if( element.getAttributeNS )
        return element.hasAttributeNS( namespace, attrname )
            ? element.getAttributeNS( namespace, attrname )
            : null;
    else */
        return element.hasAttribute( namespace + ':' + attrname )
            ? element.getAttribute( namespace + ':' + attrname )
            : null;
}


/**
 * Creates an XML document from the provided string.
 *
 * @note
 * It is important that the returned document *not* contain a DOCTYPE (or, that
 * it is correct for the returned document), and/or that the XML prologue is
 * present. If the IE parser chokes, make sure that you've met these conditions.
 *
 * @param   xmlstr  string representing XML content
 * @return          DOMDocument instance (?)
 */
function _loadXML( xmlstr )
{
    if( typeof DOMParser != 'undefined' )
    {
        var parser = new DOMParser();
        return parser.parseFromString( xmlstr, 'text/xml' );
    }
    else if( typeof ActiveXObject != 'undefined' )
    {
        var doc = new ActiveXObject( 'Microsoft.XMLDOM' );
        doc.async = 'false';
        doc.loadXML( xmlstr );
        return doc;
    }
    else return null;
}


/**
 * Inserts a retrieved HTML fragment into the page.
 * In a post-innerHTML world (specifically, when in XHTML mode) you must add
 * external nodes to the document using W3C methods. However, simply cloning
 * those external nodes won't work as they're missing the necessary XHTML
 * metadata. This function creates the XHTML-ified nodes and inserts them into
 * the document.
 *
 * @param   nodeset         the DOMXML node set (see _loadXML())
 * @param   parent          parent node
 * @param   insert_before   insert new nodes before this; if null, appends
 * @param   clear           if true, remove existing children of parent
 */
function _cloneHTML( nodeset, parent, insert_before, clear )
{
    if( clear )
        while( parent.hasChildNodes() )
            parent.removeChild( parent.firstChild );
    for( var node_idx = 0; node_idx < nodeset.length; ++node_idx )
    {
        parent.insertBefore(
            _replicate(nodeset.item(node_idx)), insert_before
        );
    }
}


/**
 * _cloneHTML helper function which replicates a node.
 * @param   node    node to replicate
 * @return          replicated version of @a node
 */
function _replicate( node )
{
    if( node.nodeName == '#text' )
        return document.createTextNode( node.nodeValue );

    var newnode = document.createElement( node.nodeName );
    /* IE has a strange concept of attributes -- they map to element properties.
     * For example, to set the "class" attribute, you must refer to it as
     * "className"; to set "colspan", you must refer to "colSpan". Luckily the
     * latter can be worked around by setting IE's proprietary third argument to
     * setAttribute() ("case_sensitive") to false. The former workaround must be
     * hardcoded... :P */
    var is_IE = typeof newnode.hasAttribute == 'undefined';
    for( var attr_idx = 0; attr_idx < node.attributes.length; ++attr_idx )
    {
        if( !is_IE )
            newnode.setAttribute(
                node.attributes[attr_idx].name, node.attributes[attr_idx].value
            );
        else
        {
            var attrname = node.attributes[attr_idx].name;
            if( attrname == 'class' )       attrname = 'className';
            else if( attrname == 'for' )    attrname = 'htmlFor';
            // ...any others?
            newnode.setAttribute(
                attrname, node.attributes[attr_idx].value, false
            );
        }
    }
    for( var node_idx = 0; node_idx < node.childNodes.length; ++node_idx )
        newnode.appendChild( _replicate( node.childNodes[node_idx] ) );
    return newnode;
}


/*!
 * Carlos R. L. Rodrigues
 * http://jsfromhell.com/array/average [rev. #1]
 */
average = function(a){
    var r = {mean: 0, variance: 0, deviation: 0}, t = a.length;
    for(var m, s = 0, l = t; l--; s += a[l]);
    for(m = r.mean = s / t, l = t, s = 0; l--; s += Math.pow(a[l] - m, 2));
    return r.deviation = Math.sqrt(r.variance = s / t), r;
}


