/*global ActiveXObject */

/* ------------------------------------------------------------------------- */
/*
 *   Miscellany
 */

function getCurrentUrl(appendQuery)
{
    var url = location.protocol + "//" + location.host + location.pathname + location.search;
    if (appendQuery)
    {
        url += (url.indexOf("?") == -1 ? "?" : "&") + appendQuery;
    }
   
    return url;
}

function updateLengthStatus(id, maxLength)
{
    var status = document.getElementById("_" + id + "_length");
    var elm = document.getElementById(id);

    status.innerText = "" + elm.value.length;
}

/*
 *    Because scrollIntoView truly is jarring, here's a replacement.
 *    [ http://lists.whatwg.org/pipermail/whatwg-whatwg.org/2008-April/014623.html ]
 */
function ensureElementIsViewable(element)
{
    var y = 0;
    var obj = element;
   
    // Determine actual Y position of this element.
    do
    {
        y += obj.offsetTop;
        obj = obj.offsetParent;
    }
    while (obj);

    // Obtain the viewport height. This works in almost any browser.
    var viewportHeight = window.innerHeight;
   
    // Now, use Voodoo(tm) to obtain the viewport height in Internet Explorer.
    if (!viewportHeight) viewportHeight = document.documentElement.clientHeight;
    if (!viewportHeight) viewportHeight = document.body.clientHeight;
    if (!viewportHeight) return; // give up
   
    // Once again. The non-IE way:
    var scrollX = window.pageXOffset;
    var scrollY = window.pageYOffset;
   
    // And the IE way.
    if (typeof(scrollX) != 'number')
    {
        scrollX = document.documentElement.scrollLeft;
        scrollY = document.documentElement.scrollTop;
    }
   
    // Calculate min and max scroll values for element to be visible.
    var minScrollY = y + element.offsetHeight - viewportHeight;
    var maxScrollY = y;
   
    //alert("" + this + ":\nClamping pageYOffset (" + scrollY + ") to [" + minScrollY + "; " + maxScrollY + "]");
   
    // Now, maxScrollY may be less than minScrollY, meaning that the element
    // won't fit on the screen. If so, we scroll such that the top element
    // becomes visible.
  
    if (scrollY < minScrollY) scrollY = minScrollY;
    if (scrollY > maxScrollY) scrollY = maxScrollY;
    window.scrollTo(scrollX, scrollY);
}

function activeToggle(iid)
{
    var element = document.getElementById("bis" + iid);
    if (!element) return;
   
    var active = element.getElementsByTagName('input').item(0).checked;

    var spans = element.getElementsByTagName('span');
    for (var i = spans.length - 1; i >= 0; i--)
    {
        var span = spans.item(i);
        if (span.className.indexOf('bookinfo') >= 0)
        {
            if (active) span.className = span.className.replace(/\bnonpublic\b/, "");
            else span.className = span.className + " nonpublic";
            break;
        }
    }
}

function toggleCmdLink(id)
{
    var elm = document.getElementById(id);
    if (!elm || !elm.style) return true;
   
    var li = elm.parentNode;
    if (!li) return true;
   
    switch (elm.style.display)
    {
    case "none":
        elm.style.display = "block";
        li.className = "expanded";
        ensureElementIsViewable(elm);
        return false;
       
    case "block":
        elm.style.display = "none";
        li.className = "";
        return false;
    }

    return true;
}

/* ------------------------------------------------------------------------- */
/*
 *   AJAX
 */

function log(msg)
{
    var logElm = document.getElementById("log");

    if (!logElm) return;

    logElm.innerText += msg;
    logElm.innerHTML += '<br />';
}

/* Create an XMLHttpRequest object in a cross-browser compatible manner */
function createRequest()
{
    if (window.XMLHttpRequest)
    {
        var http_request = new XMLHttpRequest();

        if (http_request.overrideMimeType)
            http_request.overrideMimeType('text/xml');
       
        return http_request;
    }
    else if (window.ActiveXObject)
    {
        try
        {
            return new ActiveXObject("Msxml2.XMLHTTP");
        }
        catch (e1)
        {
            try
            {
                return new ActiveXObject("Microsoft.XMLHTTP");
            }
            catch (e2)
            {
            }
        }
    }
    return null;
}

/*
 *  Replace the content of an element with the result of a server request.
 *
 *  The element temporarily gains the CSS class "ajaxreloading".
 *  We note that we're currently loading the content of the element by setting
 *  its reloadRequest property to the XMLHttpRequest object, enabling us to
 *  cancel the request by calling element.reloadRequest.abort.
 *  (We also automatically cancel previous requests on an element when starting 
 *  a new one.)
 *
 *  If the current document contains an element with id "log", it will receive
 *  log messages.
 *
 *  Returns true on success.
 *
 *  Tested under IE 6 SP 2, Firefox 3.0a8 (pre-release) and Opera 9.24.
 */
function replaceElement(element, url, onfinish)
{
    if (typeof(element) == 'string')
        element = document.getElementById(element);
   
    if (!element || element.innerHTML == null)
    {
        log("replaceElement(" + element + ") failed.");
        return false;
    }

    var http_request = createRequest();
    
    if (!http_request)
    {
        log("createRequest() failed.");
        return false;
    }
    
    // OK, we're ready to proceed. Start by aborting any pending replacement.
    abortReplaceElement(element);
   
    var oldClassName = element.className;
   
    http_request.onreadystatechange = function()
    {
        try
        {
            var state = http_request.readyState;
            
            if (state == 4)
            {
                // Microsoft's IXMLHTTPRequest documentation clearly states that
                // abort() returns the readyState to UNINITIALIZED (0).
                // However, neither IE nor Firefox does this before calling
                // onreadystatechange. Rather, they set readyState to 4 and
                // set their own special status codes.
                // Only after the call to onreadystatechange is the readyState
                // set to 0.
                
                // Work around the IE bug.
                if (http_request.status === 0) state = 0;
                // Work around the Firefox bug.
                if (http_request.status == 2147746065) state = 0;
            }
            
            switch (state)
            {
            case 0: // Uninitialized or aborted.
                log("Aborted " + url + ".");

                // Clean up.
                element.className = oldClassName;
                element.reloadRequest = null;
                delete http_request.onreadystatechange;
                return;

            case 4: // Done loading.
                log("Finished " + url + ".");

                // Clean up.
                element.className = oldClassName;
                element.reloadRequest = null;
                delete http_request.onreadystatechange;

                if (http_request.status != 200)
                {
                    log("HTTP error " + http_request.status);
                }
                else element.innerHTML = http_request.responseText;
               
                //log(element.innerHTML);

                if (onfinish) onfinish();
            }
        }
        catch (e)
        {
            log("Error in ORSC: " + e + " / " + e.description);
        }
    };

    element.reloadRequest = http_request;

    element.className += " ajaxreloading";
    
    log("Loading " + url + ".");

    http_request.open('GET', url, true);
    http_request.send(null);
    
    return true;
}

function abortReplaceElement(element)
{
    if (typeof(element) == 'string')
        element = document.getElementById(element);
   
    if (element.reloadRequest)
    {
        element.reloadRequest.abort();

        // Opera doesn't call onreadystatechange properly on abort.
        // If element.reloadRequest is still set, we must do it manually.
        if (element.reloadRequest) element.reloadRequest.onreadystatechange();
        return true;
    }
    return false;
}

/* ------------------------------------------------------------------------- */

function bookinfoclick(bid)
{
    var nodeList, i;
    
    var elm = document.getElementById("book" + bid);
    if (!elm) return;
   
    if (!elm.expansionState)
    {
        nodeList = elm.getElementsByTagName('ul');
        elm.expansionState = (nodeList.length > 0) ? 2 : 0;
    }

    switch (elm.expansionState)
    {
    case 0:
        elm.expansionState = 1;
       
        replaceElement("book" + bid, getCurrentUrl("xbe=" + bid), function()
            {
                elm.expansionState = 2;
                setTimeout(function() { ensureElementIsViewable(elm); }, 50);
            } );
        return false;
   
    case 1: // Clicked again before we were loaded. Return true, to follow the link normally (in case the AJAX failed).
        return true;
   
    case 2:
        elm.expansionState = 0;
       
        // Remove 'expanded' css class from span with class 'bookinfo'.
        nodeList = elm.getElementsByTagName('span');
        for (i = nodeList.length - 1; i >= 0; i--)
        {
            var span = nodeList.item(i);
            if (span.className.indexOf('bookinfo') >= 0)
            {
                span.className = span.className.replace(/\bexpanded\b/, '');
                break;
            }
        }
       
        // Remove all <ul>s
        nodeList = elm.getElementsByTagName('ul');
        for (i = nodeList.length - 1; i >= 0; i--)
            nodeList.item(i).parentNode.removeChild(nodeList.item(i));
       
        // Remove all <p class="footnotes">s
        nodeList = elm.getElementsByTagName('p');
        for (i = nodeList.length - 1; i >= 0; i--)
            if (nodeList.item(i).className.indexOf('footnotes') >= 0)
                nodeList.item(i).parentNode.removeChild(nodeList.item(i));
       
        return false;
    }
}

/* ------------------------------------------------------------------------- */

function searchBoxFocus()
{
    if (this.className.indexOf('emptyhint') >= 0)
    {
        this.className = this.className.replace(/\bemptyhint\b/, '');
        this.value = "";
    }
}

function searchBoxBlur()
{
    if (this.value === "")
    {
        if (this.className.indexOf('emptyhint') == -1)
            this.className += " emptyhint";
        this.value = this.hintText;
    }
}

function pageLoad(fixSearchLanguage, searchHint)
{
    var elm;
    
    if (fixSearchLanguage)
    {
        elm = document.getElementById("courses");
        replaceElement(elm.parentNode, "/coursedropdown/" + fixSearchLanguage, null);
    }
   
    elm = document.getElementById('q');
   
    elm.hintText = searchHint;
    elm.onfocus = searchBoxFocus;
    elm.onblur = searchBoxBlur;
    elm.onblur();
}

