/*
 * DisplayUitls.js
 *
 * Utility functions for altering the display of HTML componenets from javascript
 *
 * Company: Hyfinity Ltd
 * Copyright (c) 2003
 */


/**
 * Generic function for toggling the display of a component.
 * @param targetComponent the HTML component (or its ID) to show/hide
 * @param method An optional string specifying the required visibility of the
 *               component ('show' or 'hide')
 */
function toggleComponent(targetComponent, method, animate)
{
    var component
    if (typeof(targetComponent) == 'string')
        component = document.getElementById(targetComponent);
    else
        component = targetComponent;
    if ((component != null) && (typeof(component) != 'undefined'))
    {
        if ((method == null) || (typeof(method) == 'undefined'))
        {
            if (getCurrentStyle(component, 'display') == 'none')
            {
                showComponent(component, animate);
            }
            else
            {
                hideComponent(component, animate);
            }
        }
        else
        {
            if (method == 'hide')
            {
                hideComponent(component, animate);
            }
            else
            {
                showComponent(component, animate);
            }
        }
    }
}

if (typeof(dojo) != 'undefined')
{
    try
    {
        dojo.require("dojo.fx");
    }
    catch (e)
    {}
}

/*generic utility function for hiding a specific component*/
function hideComponent(component, animate)
{
    if (component == null)
        return;

    if ((animate == true) && (typeof(dojo) != 'undefined'))
    {
        dojo.fx.wipeOut({node: component, duration: 250}).play();
    }
    else
    {
        if ((component._oldCSSDisplay == undefined) && (component.style.display != 'none'))
            component._oldCSSDisplay = component.style.display;

        component.style.visibility = 'hidden';
        component.style.display = 'none';
    }
}
/*generic utility function for showing a particular component*/
function showComponent(component, animate)
{
    if (component == null)
        return;

    if ((animate == true) && (typeof(dojo) != 'undefined'))
    {
        dojo.fx.wipeIn({node: component, duration: 250}).play();
    }
    else
    {
        component.style.visibility = 'visible';
        if (component._oldCSSDisplay != undefined)
            component.style.display= component._oldCSSDisplay;
        else
            component.style.display = 'block';
    }
}

/* utility function for disabling a component
 * may want to extend this so children components are disabled as well, ie if a div component is passed in*/
function disableComponent(component, deep)
{
    if ((component == null) || (typeof(component) == 'undefined') || (typeof(component.tagName) == 'undefined'))
        return;

    if ((component.tagName == 'INPUT') || (component.tagName == 'TEXTAREA') || (component.tagName == 'SELECT'))
    {

        if (component.getAttribute("_oldCSSClass") == null)
        {
            component.setAttribute("_oldCSSClass", component.className)
        }
        component.className = component.className + ' readOnlyTxt';
        component.disabled=true;
    }
    else if (component.tagName == 'A')
    {
        if (component.getAttribute("_oldHref") == null)
        {
            component.setAttribute("_oldHref", component.href)
        }
        component.removeAttribute('href');
        component.disabled=true;
    }
    else
    {
        component.disabled=true;
    }

    if (deep && component.hasChildNodes())
    {
        for (var i = 0; i < component.childNodes.length; ++i)
        {
            disableComponent(component.childNodes.item(i), deep);
        }
    }
}

/* utility function for enabling a component*/
function enableComponent(component, deep)
{
    if ((component == null) || (typeof(component) == 'undefined') || (typeof(component.tagName) == 'undefined'))
        return;

    component.disabled=false;

    if ((component.tagName == 'INPUT') || (component.tagName == 'TEXTAREA') || (component.tagName == 'SELECT'))
    {
        if (component.getAttribute("_oldCSSClass") != null)
        {
            var oldClass = component.getAttribute("_oldCSSClass")
            component.removeAttribute("_oldCSSClass");
            component.className = oldClass;
        }
    }
    if (component.tagName == 'A')
    {
        if (component.getAttribute("_oldHref") != null)
        {
            var oldHref = component.getAttribute("_oldHref")
            component.removeAttribute("_oldHref");
            component.href = oldHref;
        }
    }

    if (deep && component.hasChildNodes())
    {
        for (var i = 0; i < component.childNodes.length; ++i)
        {
            enableComponent(component.childNodes.item(i), deep);
        }
    }
}

/**
 * Returns the current value for the given style property on the given object
 */
function getCurrentStyle(object, property)
{
    var currentDisplay;
    if (window.getComputedStyle)
        //currentDisplay = document.defaultView.getComputedStyle(object,null).getPropertyValue(property);
        currentDisplay = document.defaultView.getComputedStyle(object,null)[property];
    else if (object.currentStyle)
        currentDisplay = object.currentStyle[property];
    return currentDisplay;
}

/* Functions for adjustable dividers. */
var dividerMode = 'stop'

var widthAdjustTarget;
var fixedPoint;
var directionMode;

var currentWidth;
var moveDivider;

function dividerPressed(target, mode)
{
    if (typeof(mode) == 'undefined')
    {
        mode = 'left';
    }

    evt = window.event;

    dividerMode = 'move';
    widthAdjustTarget = target;
    directionMode = mode;

    if (directionMode == 'left')
    {
        fixedPoint = getLeftPosition(widthAdjustTarget);
        if (getCurrentStyle(widthAdjustTarget, 'display') == 'none')
            fixedPoint = getLeftPosition(evt.srcElement);
    }
    else
    {
        fixedPoint = getRightPosition(widthAdjustTarget);
        if (getCurrentStyle(widthAdjustTarget, 'display') == 'none')
            fixedPoint = getRightPosition(evt.srcElement);
    }

    moveDivider = document.getElementById('temp_divider');

    moveDivider.style.top = getTopPosition(widthAdjustTarget);
    //moveDivider.style.height = widthAdjustTarget.offsetHeight;
    moveDivider.style.height = evt.srcElement.offsetHeight;

    var pos = getMouseCoords();

    moveDivider.style.left = pos.x - 5;
    moveDivider.style.display = 'block';

    if(evt.preventDefault){
        evt.preventDefault();
    }
    evt.cancelBubble=true;
    evt.returnValue = false;

}

function dividerMove(e, base)
{
    //alert(dividerMode);
    if (dividerMode == 'move')
    {
        var pos = getMouseCoords(e, base);
        var evt;
        var target;
        if (!e)
        {
            evt = window.event;
            target = evt.srcElement;
        }
        else //netscape
        {
            evt = e;
            target = evt.currentTarget;
        }

        if(evt.preventDefault){
            evt.preventDefault();
        }
        evt.cancelBubble=true;
        evt.returnValue = false;

        if (directionMode == 'left')
        {
            currentWidth = pos.x - fixedPoint;
        }
        else
        {
            currentWidth = fixedPoint - pos.x;
        }

        moveDivider.style.left = pos.x - 5;
    }

}

function dividerReleased()
{
    dividerMode = 'stop';

    if (currentWidth > 0)
    {
        showComponent(widthAdjustTarget);
        widthAdjustTarget.style.width = currentWidth;
        currentWidth = -1;
    }

    if (moveDivider != null)
    {
        moveDivider.style.display = 'none';
    }
}

function dividerDoubleClicked(target)
{
    toggleComponent(target);
}

/**
 * Returns an object containing the coordinates of the mouse from the given event.
 * The returned object will have two properties, 'x' and 'y'
 */
function getMouseCoords(e, base)
{
    var coords= new Object();

    var evt
    if (!e)
    {
        evt = window.event;
    }
    else //netscape
    {
        evt = e;
    }

    //get the mouse coords
    if (evt.pageX || evt.pageY)
    {
        //Gecko based
        coords.x = evt.pageX;
        coords.y = evt.pageY;
    }
    else if (evt.clientX || evt.clientY)
    {
        coords.x = evt.clientX
        coords.y = evt.clientY
        if ((document.body) && (document.body.scrollLeft || document.body.scrollTop))
        {
            //IE 4, 5, and 6 (Non standards compliant mode)
            coords.x += document.body.scrollLeft;
            coords.y += document.body.scrollTop;
        }
        else if ((document.documentElement) &&
                 ((document.documentElement.scrollLeft) ||
                 (document.documentElement.scrollTop)))
        {
            //IE 6 (Standards compliant mode)
            coords.x += document.documentElement.scrollLeft;
            coords.y += document.documentElement.scrollTop;
        }
    }

    if ((typeof(base) != 'undefined') && (typeof(base.x) != 'undefined') && (typeof(base.y) != 'undefined'))
    {
        coords.x += base.x;
        coords.y += base.y;
    }

    return coords;

}

/**
 * Returns the location of the left of the given object.
 * If the screen parameter is set to true, then the returned value will be in screen coords.
 * ie it adjusts for scrollable regions, to allow comparison with mouse coordinates
 * returned from getMouseCoords method
 */
function getLeftPosition(obj, screen)
{
    var ol=obj.offsetLeft;
    while ((obj=obj.offsetParent) != null)
    {
        ol += obj.offsetLeft;
        if (screen)
        {
            if (obj.scrollLeft != 0)
            {
                ol -= obj.scrollLeft;
            }
        }
    }
    return ol;
}

function getRightPosition(obj, screen)
{
    return getLeftPosition(obj, screen) + obj.offsetWidth;
}

/**
 * Returns the location of the top of the given object.
 * If the screen parameter is set to true, then the returned value will be in screen coords.
 * ie it adjusts for scrollable regions, to allow comparison with mouse coordinates
 * returned from getMouseCoords method
 */
function getTopPosition(obj, screen)
{
    var ot=obj.offsetTop;
    while ((obj=obj.offsetParent) != null)
    {
        ot += obj.offsetTop;
        if (screen)
        {
            if (obj.scrollTop != 0)
            {
                ot -= obj.scrollTop;
            }
        }
    }
    return ot;
}

/**
 * Returns the current location coordinates of the given object.
 * The returned object will have four properties, 'x' and 'y' which give
 * the position of the top left corner, and 'width' and 'height'
 * If the screen parameter is set to true, the returned values are in 'screen coords', ie they are
 * adjusted for scrollable regions, to allow comparison with the mouse corrdinates returned from
 * the getMouseCoords method.
 */
function getComponentPosition(obj, screen)
{
    var objCoords = new Object();
    objCoords.x = getLeftPosition(obj, screen);
    objCoords.y = getTopPosition(obj, screen);
    objCoords.width = obj.offsetWidth;
    objCoords.height = obj.offsetHeight;
    return objCoords;
}

/**
 * Returns the size of the available window viewport
 * @return an object containing two properties, width and height.
 */
function getViewportSize()
{
    var size = new Object();
    if (window.innerWidth)
    {
        size.width = window.innerWidth;
        size.height = window.innerHeight;
    }
    else if (document.documentElement && document.documentElement.clientWidth)
    {
        size.width = document.documentElement.clientWidth;
        size.height = document.documentElement.clientHeight;
    }
    else if (document.body && document.body.clientWidth)
    {
        size.width = document.body.clientWidth;
        size.height = document.body.clientHeight;
    }
    return size;
}



/**
 * Adjusts the scrollTop and scrollLeft properties of the given container
 * to try and make the entry visible on the screen.
 * @param container The HTML component that contains the entry, and has overflow set to scroll.
 * @param entry The HTML component within the container that should be shown.
 */
function scrollViewToShow(container, entry)
{
    if ((container == null) || (typeof(container) == 'undefined') ||
        (entry == null) || (typeof(entry) == 'undefined'))
    {
        //not recieved the required details so can't continue
        return;
    }

    var cScrollTop = container.scrollTop;
    var cPos = getComponentPosition(container, false);
    var cClientHeight = container.clientHeight;
    var cScrollLeft = container.scrollLeft;
    var cClientWidth = container.clientWidth;

    var ePos = getComponentPosition(entry, false);
    var eClientWidth = entry.clientWidth;

    if (((cScrollTop + cPos.y + cClientHeight) < ePos.y) || (ePos.y < (cScrollTop + cPos.y)))
    { //hidden off the top or bottom of the scrolling window, so need to adjust scroll top
        container.scrollTop = (ePos.y - cPos.y - (cClientHeight/2));
    }

    if (eClientWidth < cClientWidth)
    {  //entry width less than window width so show in the middle
        if (((cScrollLeft + cPos.x + cClientWidth) < (ePos.x + eClientWidth)) || (ePos.x < (cScrollLeft + cPos.x)))
        {
            container.scrollLeft = (ePos.x - cPos.x - ((cClientWidth - eClientWidth)/2));
        }
    }
    else
    { //entry width greater than window width so show left hand section
        if (((cScrollLeft + cPos.x + cClientWidth) < (ePos.x + eClientWidth)) || (ePos.x < (cScrollLeft + cPos.x)))
        {
            container.scrollLeft = (ePos.x - cPos.x - 10);
        }
    }
}



/**
 * Sets the opacity of the given object.
 * @param obj The object to set the opactiry on.
 * @param opacity The opacity value to set - 0.0 = transparent, 1.0 = opaque.
 */
function setOpacity(obj, opacity)
{
    //this only supports IE!
    if (obj != null)
        obj.style.filter = 'Alpha(Opacity=' + opacity * 100 + ')';

}


/**
 * Clears the opacity setting on the given object.
 */
function clearOpacity(obj)
{
    //TODO: this will clear out all filters, not just opacity, so need to change.
    if (obj != null)
        obj.style.filter = '';
}




/**
 * The scrollable div manager is used to provide extra scrolling features
 * for divs with the overflow setting set to scroll.
 * It will enable automatic scrolling of the container when the user has the mouse button
 * pressed, and moves the mouse towards the top or bottom of the container.
 * ie when they are doing a drag and drop operation.
 */
function scrollableDivManager()
{

}

/**
 * Adds a container to be managed by the scriollable div manager
 */
scrollableDivManager.attachContainer = function(cont)
{
    //check that the container is set to scroll
    if (getCurrentStyle(cont, 'overflow') == 'scroll')
    {
        cont.attachEvent('onmousemove', scrollableDivManager.mouseMove);
        cont.attachEvent('onmouseout', scrollableDivManager.mouseOut);
        cont.setAttribute('scrollableDivContainer', 'true');
    }
}


scrollableDivManager.mouseMove = function(e)
{
    if (!e) { e = window.event; }

    window.clearTimeout(scrollableDivManager.timeoutID);

    var elem = (e.srcElement) ? e.srcElement : e.target;

    while ((elem != null) && (elem.getAttribute('scrollableDivContainer') == null))
    {
        elem = elem.parentNode;
    }

    if (elem == null)
        return;


    //check that the mouse button is pressed
    if ((e.button == 1) || (e.which == 1))
    {
        //alert(elem.getAttribute('id'));
        var mouseCoords = getMouseCoords(e);
        var elemCoords = getComponentPosition(elem, true);

        scrollableDivManager.scrollContainer = elem;

        if (mouseCoords.y < (elemCoords.y + scrollableDivManager.DETECTION_OFFSET))
        {
            elem.scrollTop = elem.scrollTop - scrollableDivManager.SCROLL_AMOUNT;
            scrollableDivManager.timeoutID = window.setTimeout("scrollableDivManager.mouseHeldVertical(-"+scrollableDivManager.SCROLL_AMOUNT+")", 5);
        }
        else if (mouseCoords.y > ((elemCoords.y + elemCoords.height) - scrollableDivManager.DETECTION_OFFSET))
        {
            elem.scrollTop = elem.scrollTop + scrollableDivManager.SCROLL_AMOUNT;
            scrollableDivManager.timeoutID = window.setTimeout("scrollableDivManager.mouseHeldVertical("+scrollableDivManager.SCROLL_AMOUNT+")", 5);
        }
        else if (mouseCoords.x < (elemCoords.x + scrollableDivManager.DETECTION_OFFSET))
        {
            elem.scrollLeft = elem.scrollLeft - scrollableDivManager.SCROLL_AMOUNT;
            scrollableDivManager.timeoutID = window.setTimeout("scrollableDivManager.mouseHeldHorizontal(-"+scrollableDivManager.SCROLL_AMOUNT+")", 5);
        }
        else if (mouseCoords.x > ((elemCoords.x + elemCoords.width) - scrollableDivManager.DETECTION_OFFSET))
        {
            elem.scrollLeft = elem.scrollLeft + scrollableDivManager.SCROLL_AMOUNT;
            scrollableDivManager.timeoutID = window.setTimeout("scrollableDivManager.mouseHeldHorizontal("+scrollableDivManager.SCROLL_AMOUNT+")", 5);
        }



    }
}

scrollableDivManager.mouseOut = function(e)
{
    window.clearTimeout(scrollableDivManager.timeoutID);
}

scrollableDivManager.mouseHeldVertical = function(changeAmount)
{
    scrollableDivManager.scrollContainer.scrollTop = scrollableDivManager.scrollContainer.scrollTop + changeAmount;
    scrollableDivManager.timeoutID = window.setTimeout("scrollableDivManager.mouseHeldVertical("+changeAmount+")", 5);
}

scrollableDivManager.mouseHeldHorizontal = function(changeAmount)
{
    scrollableDivManager.scrollContainer.scrollLeft = scrollableDivManager.scrollContainer.scrollLeft + changeAmount;
    scrollableDivManager.timeoutID = window.setTimeout("scrollableDivManager.mouseHeldHorizontal("+changeAmount+")", 5);
}

scrollableDivManager.DETECTION_OFFSET = 20;
scrollableDivManager.SCROLL_AMOUNT = 10;


//-------------------------------------------------------------------------

/**
 * MenuManager provides support for drop down menu functionality.
 */
var menuManager = new Object();

menuManager.showMenu = function(menuID, source)
{
    if (menuManager.timeoutMenu == menuID)
    {
        menuManager.maintainMenu(menuID);
    }
    else
    {
        var menuComponent = document.getElementById(menuID + '_menu');
        //find the lcoation of the source object, to position the menu under
        var sourcePos = getComponentPosition(source, false);
        menuComponent.style.position = 'absolute';
        menuComponent.style.left = sourcePos.x + 15;
        menuComponent.style.top = sourcePos.y + sourcePos.height + 5;

        showComponent(menuComponent);
    }
}

menuManager.hideMenu = function(menuID)
{
    menuManager.timeoutMenu = menuID;
    menuManager.timeoutID = window.setTimeout("menuManager._hide('"+menuID+"')", 100);

}

menuManager.maintainMenu = function(menuID)
{
    window.clearTimeout(menuManager.timeoutID);
    menuManager.timeoutMenu = null;
}

menuManager._hide = function(menuID)
{
    menuManager.timeoutMenu = null;
    var menuComponent = document.getElementById(menuID + '_menu');
    hideComponent(menuComponent);
}


// TODO: Ideally should use the actual browser detection functinality, but studio is IE only for now...
var is_ie = true;

// This stuff is copied from the runtime displayUtils file that is used within the FM generated apps
// should probably look at reusing the existing file rather than having another copy here!!!

/**
 * Root object to contain all hyfinity functions.
 * This should remove the chance of a name conflict with other functionality.
 *
 * TODO: At some point we need to update all existing functionality to use this namespace.
 */
if (typeof(hyf) == 'undefined')
    var hyf =
    {
        version : '1.0'
    };


/**
 * The hyf.textarea namespace contains all functionality relating to textarea field controls.
 */
hyf.textarea =
{
    desc : "Handles functionality relating to textareas, eg auto expanding fields.",
    offsetBuffer : (is_ie) ? 2 : ((is_opera) ? 4 : 0)
};

/**
 * Handles automatically updating the size of the supplied textarea to support the current content.
 *
 * @param evt The (keypress) event object that triggered the call.
 * @param field The textarea object to adjust
 * @param min The minim number of rows to show in the textarea.
 * @param max The maximum number of rows to expand the textarea to.
 */
hyf.textarea.adjustHeight = function(evt, field, min, max)
{
    if ((evt != null) && (typeof(evt) != 'undefined'))
        var charCode = (evt.which) ? evt.which : event.keyCode;

    //ignore the arrow keys and shift/ctrl key
    if ((charCode >= 37 && charCode <= 40) || (charCode == 16) || (charCode == 17) || (evt && evt.ctrlKey))
    {
        return;
    }

    if ((max == null) || typeof(max) == 'undefined')
        max = Number.MAX_VALUE;
    if ((min == null) || typeof(min) == 'undefined')
        min = 1;

    //if the delete or backspace keys have been pressed then check whether to remove a row
    if ((field.rows > min) && ((charCode == 8) || (charCode == 46)))
    {
        if (is_ie)  //does this work for other browsers???
        {
            var needToAddBack = false;
            while ((field.rows >= min ) && (!hyf.textarea.hasScroll(field)))
            {
                field.rows--;
                //needToAddBack = true;
            }
            //alert('hello');
            if (needToAddBack)
                field.rows++;
        }
        else
        {
            //find a guess of the number of rows of text
            var numRows = hyf.textarea.countNumTextRows(field);

            //set based on this
            field.rows = (numRows < min) ? min : ((numRows <= max) ? numRows : max);
        }
    }
    //otherwise look at adding a row
    else if ((field.rows < max ) && (hyf.textarea.hasScroll(field)) && !((charCode == 8) || (charCode == 46)))
    {
        if (is_ie || is_gecko)
        {
            while ((field.rows < max ) && (hyf.textarea.hasScroll(field)))
            {
                field.rows++;
            }
        }
        else
        {
            //find a guess of the number of rows of text
            var numRows = hyf.textarea.countNumTextRows(field);

            //set based on this
            field.rows = (numRows < min) ? min : ((numRows <= max) ? numRows : max);
        }
    }
};

/**
 * Tries to determine whether the given textarea field currently has a scroll bar or not.
 * @param field The textarea object.
 * @return true or false depending on whether a scrollbar is currently required.
 *
 * @private
 */
hyf.textarea.hasScroll = function(field)
{
    //attempt to scroll to the bottom
    field.scrollTop = field.scrollHeight;
    //check if the field has been scrolled at all
    if (field.scrollTop != 0)
    {
        //field.value = content;
        return true;
    }
    else
    {
        //field.value = content;
        return false;
    }
}


/**
 * Returns an estimate of the number of rows used by the text content in the
 * provided textarea.
 * @param field The textarea object.
 *
 * @private
 */
hyf.textarea.countNumTextRows = function(field)
{
    //count number of line breaks
    var re = /\r\n|\r|\n/g;
    var numBreaks = 0;
    var lineStart = 0;
    while (re.exec(field.value))
    {
        numBreaks++;
        //for each line of text between physical breaks, try to work out how many lines it wraps to
        var newLineStart = re.lastIndex;
        var lineString = field.value.substring(lineStart, newLineStart);
        if (lineString.length > field.cols)
            numBreaks += Math.ceil(lineString.length / field.cols);

        lineStart = newLineStart;
    }

    var lineString = field.value.substring(lineStart);
    if (lineString.length == 0)
        return Number(numBreaks) + Number(1);
    else
        return Math.ceil(lineString.length / field.cols) + Number(numBreaks);

};
