/**
 * @project     CWC2
 * @revision    $Id: cwc_button.js,v 1.8 2005/05/16 11:26:27 bartvde Exp $
 * @purpose     Java Script for managed, multi-state HTML buttons
 * @author      DM Solutions Group (pspencer@dmsolutions.ca)
 * @copyright
 * <b>Copyright (c) 2003, DM Solutions Group Inc.</b>
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and associated documentation files (the "Software"),
 * to deal in the Software without restriction, including without limitation
 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
 * and/or sell copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included
 * in all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
 * DEALINGS IN THE SOFTWARE.
 */
 
/**
 * the purpose of this code is to conveniently manage fancy javascript buttons
 * for the application.  It provides three types of buttons managed via a 
 * button manager.  The global button manager is created when this file is
 * included.  The name of the global button manager is goCWCButtonManager.
 *
 * Buttons are created as javascript objects and added to the button manager.
 * The CWCButton constructor is described in comments just above the CWCButton
 * function, but here is a quick example of how to construct a button object:
 *
 * button = new CWCButton( "MyButton", "MyGroup" , MyFunction, null
 *                         "images/tool_zoombounds_off.gif", 
 *                         "images/tool_zoombounds_over.gif", 
 *                         "images/tool_zoombounds_on.gif" );
 *
 * goCWCButtonManager.AddButton( button );
 *
 * This builds a new button object with its user interface controlled by the
 * HTML image object named "MyButton" and adds it to the manager.
 *
 * Note that it is important for cross-browser compatibility to provide both
 * a name and an id in your images. Here is an example image to work with this
 * button:
 *
 * <img src="images/tool_zoombounds_off.gif" name="MyButton" id="MyButton">
 *
 * The button will belong to a group called MyGroup.  When a button belongs to
 * a group, it is automatically converted from a normal button to a radio
 * button (the types of buttons are described below).
 *
 * MyFunction is the name of a function to call when the button is activated.
 * Note that it is just the name of the function, and does not have quotes
 * around it.  This passes a function object to the button object so that when
 * the button is clicked, MyFunction is called.  When MyFunction is called, the
 * button object that called MyFunction is passed as the one and only argument
 * to the function.  A sample MyFunction would be:
 *
 * function MyFunction( oButton, aMyValues )
 * {
 *   alert( oButton.szImageName );
 * }
 *
 * The fourth argument is some value to pass to MyFuction.  This can null, a
 * single value, or an array of values and is completely dependent on how this
 * button is intended to be used.
 *
 * The fourth parameter is the "normal" state image, the image to be displayed
 * when the button is not activated in any way.  This is a URL to the image and
 * can be relative or absolute depending on your requirements.
 *
 * The remaining images are optional and will only be used if provided.  If
 * additional images are provided, then when the button would change to a
 * particular state (see below), then the button image will be replaced with
 * the associated image.
 *
 * Buttons can be one of three types:
 *
 * CWCBUTTONTYPE_CLICK - this is a button that is in the SELECTED state while
 * the mouse button is down, and in NORMAL state when it is up.  The function
 * associated with this type of button is called once when the mouse is
 * released.
 *
 * CWCBUTTONTYPE_TOGGLE - this is a sticky button that stays down after it is
 * clicked, and pops up when it is clicked again.  The function associated with
 * toggle buttons is called when the button is activated and disactivated.
 *
 * CWCBUTTON_RADIO - this is a sticky button that operates like a toggle button
 * except that it is part of a group of buttons of which only one can be active
 * at a time.  Clicking a radio button deselects all other buttons in the same
 * group.  Unlike toggle buttons, the function associated with radio buttons is
 * called only when the button is pressed.  Buttons that are deactivated when
 * another button in the same group is activated do not call their function.
 * Providing a group name in the constructor of a button automatically makes
 * the button a RADIO button.  
 *
 * Buttons have a variety of states.
 *
 * CWCBUTTONSTATE_NORMAL - the button is not activated
 * CWCBUTTONSTATE_HOVER - the mouse is over the button but it is not activated
 * CWCBUTTONSTATE_SELECTED - the button is selected (pressed in)
 * CWCBUTTONSTATE_DISABLED - the button is unresponsive
 */
 
//button states
var CWCBUTTONSTATE_NORMAL = 0;
var CWCBUTTONSTATE_HOVER = 1;
var CWCBUTTONSTATE_SELECTED = 2;
var CWCBUTTONSTATE_DISABLED = 3;

//button types
var CWCBUTTONTYPE_CLICK = 0;
var CWCBUTTONTYPE_TOGGLE = 1;
var CWCBUTTONTYPE_RADIO = 2;

//capture events in nav browsers
if (CWCIsNav4 || CWCIsNav6)
{
    document.captureEvents(Event.MOUSEDOWN);
    document.captureEvents(Event.MOUSEUP);
    document.captureEvents(Event.MOUSEMOVE);
}

var goCWCButtonManager = new CWCButtonManager();

/**
 * CWCButtonManager manages all the buttons and groups of buttons.
 * A single global instance of the CWCButtonManager should be created
 * to manage all the buttons in an application.  Adding buttons to the
 * button manager allows them to be organized into groups and managed
 * in an intelligent (radio button) manner
 */
function CWCButtonManager()
{
    this.aButtons = new Array();
    this.aGroups = new Array();
    this.oForm = null;
    
    this.currentButton = null;
    
    this.AddButton = CWCButtonManager_AddButton;
    this.GetButton = CWCButtonManager_GetButton;
}

/**
 * Return a CWCButton instance given a button name (which is the
 * image name, actually)
 * @param szImage the name of the button to retrieve
 * @return the CWCButton instance or null if not found
 */
function CWCButtonManager_GetButton( szImageName )
{
    var oImage = CWCDHTML_GetImage( szImageName );
    
    if (oImage != null && oImage.cwcButton != null)
        return oImage.cwcButton;
    else
        return null;
}

/**
 * Add a button to the button manager
 * @param oButton - the CWCButton instance to add
 */
function CWCButtonManager_AddButton( oButton )
{
    oButton.oManager = this;
    oButton.index = this.aButtons.length;
    this.aButtons[this.aButtons.length] = oButton;
    if (oButton.szGroupName != null && oButton.szGroupName != "null" && oButton.szGroupName != "")
    {
        var i, theGroup = null;
        for (i=0; i < this.aGroups.length; i++)
        {
            if (this.aGroups[i].szGroupName == oButton.szGroupName)
            {
                theGroup = this.aGroups[i];
                break;
            }
        }
        if (theGroup == null)
        {
            theGroup = new CWCButtonGroup( oButton.szGroupName );
            this.aGroups[this.aGroups.length] = theGroup;
        }
        theGroup.AddButton( oButton );
    }
}

/**
 * a group of CWCButton objects managed as a mutually exclusive set.
 * Activating one button in the group deactivates the rest.
 * @param szGroupName - the name of this group
 */
function CWCButtonGroup( szGroupName )
{
    this.szGroupName = szGroupName;
    this.aButtons = new Array();
    
    //function prototypes
    this.AddButton = CWCButtonGroup_AddButton;
    this.ActivateButton = CWCButtonGroup_ActivateButton;
}

/**
 * Add a CWCButton object to this group
 * @param oButton the button to add to this group
 */
function CWCButtonGroup_AddButton( oButton )
{
    oButton.oGroup = this;
    oButton.type = CWCBUTTONTYPE_RADIO;
    this.aButtons[this.aButtons.length] = oButton;
}

/**
 * Activate one button in the group, deactivating all others.
 * @param oButton the button to activate
 */
function CWCButtonGroup_ActivateButton( oButton )
{
    var i;
    for( i=0; i < this.aButtons.length; i++ )
    {
        if (this.aButtons[i].szImageName != oButton.szImageName)
        {
            this.aButtons[i].SetState( CWCBUTTONSTATE_NORMAL );
            this.aButtons[i].bActive = false;
            if (oButton.oForm != null)
            {
                oButton.oForm['BUTTON_TOOLSET_' + this.szGroupName].value = oButton.szImageName;
            }   
        }
    }
}

/*
 * construct a new button object
 * @param szImageName - the name in the image tag in the page, used
 *                      to grab the image object
 * @param nType - the button type (see definitions at start of file).
 * @param szGroup - the name of the group to put this button in, or null if no group.  Note
 *                  that an empty string is considered a group.
 * @param onClick - function to call when the button is clicked.  For toggle buttons, this will
 *                  be called when the button is activated and deactivated.  For radio buttons,
 *                  this will be called only when the button is activated (not when it is deactivated
 *                  by the button group as part of activating another button).
 * @param xValues - an unstructured value that is passed, as is, to the onClick
 *                  as the second value.
 * @param szNormalURL - the URL to use when the button is in normal state
 * @param szHoverURL - (opt) the URL to use when the button is in normal state
 * @param szSelectedlURL - (opt) the URL to use when the button is in normal state
 * @param szDisabledURL - (opt) the URL to use when the button is in normal state
 */
function CWCButton( szImageName, nType, szGroup, onClick, xValues, szNormalURL /* szHoverURL, szSelectedURL, szDisabledURL */ )
{
    this.szGroupName = szGroup;
    this.oGroup = null;
    
    //hidden feature to enable the button to control the visibility of an associated div
    this.szDivName = '';
    
    this.aImages = new Array( szNormalURL, null, null, null );
    
    //the index at which option arguments appear.
    var optIdx = 6;
    if (arguments.length > optIdx)
    {
        this.aImages[CWCBUTTONSTATE_HOVER] = arguments[optIdx ++];
    }
    if (arguments.length > optIdx)
    {
        this.aImages[CWCBUTTONSTATE_SELECTED] = arguments[optIdx ++];    
    }
    if (arguments.length > optIdx)
    {
        this.aImages[CWCBUTTONSTATE_DISABLED] = arguments[optIdx ++];
    }

    this.szImageName = szImageName;
    this.oImage = CWCDHTML_GetImage( szImageName );
    
    if (this.oImage != null)
    {
        this.oImage.onmouseover = CWCButton_OnMouseOver;
        this.oImage.onmouseout = CWCButton_OnMouseOut;
        //this.oImage.onclick = CWCButton_OnMouseClick;
        
        //IE specific handlers
        this.oImage.onmousedown = null; //CWCButton_OnMouseDown;
        this.oImage.onmouseup = null; //CWCButton_OnMouseUp;
        //in IE, this cancels the drag action
        this.oImage.ondragstart = CWCButton_OnDragStart;
        this.oImage.cwcButton = this; 
    }
    else
    {
        //alert( "image object for " + this.szImageName + " not found?" );
    }
    
    this.onClick = onClick;
    this.xValues = xValues;
    
    this.currentState = CWCBUTTONSTATE_NORMAL;
    this.bActive = false; //set to true when selected.
    
    //used to track old event handlers so they can be restored.
    this.oldMouseDown = null;
    this.oldMouseUp = null;
    this.oldMouseMove = null;
    
    this.oManager = null;
    this.index = 0;
    this.type = nType;
    this.bMouseDown = false;
    
    //function prototypes
    this.GetState = CWCButton_GetState;
    this.IsDisabled = CWCButton_IsDisabled;
    this.IsSelected = CWCButton_IsSelected;
    this.SetState = CWCButton_SetState;
}

/**
 * return the state of this button
 */
function CWCButton_GetState()
{
    return this.currentState;
}

/**
 * return true if the button is disabled
 */
function CWCButton_IsDisabled()
{
    return this.currentState == CWCBUTTONSTATE_DISABLED;
}

/**
 * return true if the button is selected
 */
function CWCButton_IsSelected()
{
    return this.currentState == CWCBUTTONSTATE_SELECTED;
}

/**
 * set the state of this button
 * @param nState the state to set
 */
function CWCButton_SetState( nState )
{
    if (!this.oImage) return false;
    var previousState = this.currentState;
    if (nState >=0 && nState <= CWCBUTTONSTATE_DISABLED)
    {
        //HOVER is a transient state, ignore it so toggle buttons work
        this.currentState = nState;
        if (this.aImages[nState] != null)
        {
            this.oImage.src = this.aImages[nState];
        }
        else
        {
            this.oImage.src = this.aImages[0];
        }
    }
    if (this.szDivName != '' && !this.bMouseDown )
    {
        if (this.currentState == CWCBUTTONSTATE_SELECTED)
        {
            //alert( 'showing div ' + this.szDivName );
            CWCDHTML_ShowLayer( this.szDivName );
            //if (this.oGroup != null)
            //{
            //   this.oGroup.ActivateButton( this );
            //}
        }
        else if (this.currentState == CWCBUTTONSTATE_NORMAL )
        {
            //alert( 'hiding div ' + this.szDivName );
            CWCDHTML_HideLayer( this.szDivName );
        }
    }
    
}

//global functions for handling events

function CWCButton_OnMouseOver( e )
{
    var target;
    if (CWCIsIE)
    {
        e = event;
        target = event.srcElement;
        e.cancelBubble = true;
        e.returnValue = false;
    }
    else
    {
        target = e.target
    }
    
    //catch the occasional error in IE
    if (target.cwcButton == null)
    {
        return;
    }
     
    if (!target.cwcButton.IsDisabled())
    {
        if (target.cwcButton.bMouseDown)
        {
            target.cwcButton.SetState( CWCBUTTONSTATE_SELECTED );
        }
        else if (!target.cwcButton.IsSelected())
        {
            target.cwcButton.SetState( CWCBUTTONSTATE_HOVER );
        }
    }
    
    if (!CWCIsIE)
    {
        //capture events if we are using netscape so we can grab mouse clicks
        target.cwcButton.oldMouseDown = document.onmousedown;
        document.onmousedown = CWCButton_OnMouseDown;
        target.cwcButton.oldMouseUp = document.onmouseup;
        document.onmouseup = CWCButton_OnMouseUp;
    }
    else
    {
        target.cwcButton.oImage.onmousedown = CWCButton_OnMouseDown;
        target.cwcButton.oImage.onmouseup = CWCButton_OnMouseUp;
    }
    
    target.cwcButton.oldMouseMove = document.onmousemove;
    document.onmousemove = null;
    return false;
}

/*
 * handle mouse out events by setting the image state to the appropriate
 * state depending on the type of the button.  If the image was not set
 * active then we reset it and don't generate a click when the mouse button
 * is released.
 */
function CWCButton_OnMouseOut( e )
{
    var target;
    if (CWCIsIE)
    {
        e = event;
        target = event.srcElement;
        e.cancelBubble = true;
        e.returnValue = false;
    }
    else
    {
        target = e.target
    }        

    //catch the occasional error in IE
    if (target.cwcButton == null)
    {
        return;
    }
    
    if (!target.cwcButton.IsDisabled())
    {
        if( !target.cwcButton.bActive || target.cwcButton.type == CWCBUTTONTYPE_CLICK)
        {
            target.cwcButton.SetState( CWCBUTTONSTATE_NORMAL );
        }
    }
    
    target.cwcButton.bMouseDown = false;
    
    //remove event handlers if necessary
    if (!CWCIsIE) 
    {
        document.onmouseup = target.cwcButton.oldMouseUp;
        target.cwcButton.oldMouseUp = null;
        document.onmousedown = target.cwcButton.oldMouseDown;
        target.cwcButton.oldMouseDown = null;
    }
    else
    {
        target.cwcButton.oImage.onmousedown = null;
        target.cwcButton.oImage.onmouseup = null;
    }
    
    document.onmousemove = target.cwcButton.oldMouseMove;
    target.cwcButton.oldMouseMove = null;

    
    return false;
}

/*
 * handle the onclick event on the image, note that in NN4 we are
 * faking this by calling this function from the onmouseup handler.
 */
function CWCButton_OnMouseClick( e )
{
    var target;
    if (CWCIsIE)
    {
        e = event;
        target = event.srcElement;
        e.cancelBubble = true;
        e.returnValue = false;
    }
    else
    {
        target = e.target
    }
    
    if (target.cwcButton.onClick != null)
    {
        target.cwcButton.onClick( target.cwcButton, target.cwcButton.xValues );
    }
    return false;
}

/*
 * handle mouse down events by setting the image state to the appropriate
 * state depending on the type of the button.
 */
function CWCButton_OnMouseDown( e )
{
    var target;
    if (CWCIsIE)
    {
        e = event;
        target = event.srcElement;
        e.cancelBubble = true;
        e.returnValue = false;
    }
    else
    {
        target = e.target
    }

    //catch the occasional error in IE
    if (target.cwcButton == null)
    {
        return;
    }
        
    if (!target.cwcButton.IsDisabled())
    {
        target.cwcButton.bMouseDown = true;
        target.cwcButton.SetState( CWCBUTTONSTATE_SELECTED );
    }
    return false;
}

/*
 * handle mouse up events by setting the image state to the appropriate
 * state depending on the type of the button.  Note that if this is a
 * Netscape Navigator 4 browser, then we are faking an onclick event
 * because it doesn't support onclick on images.
 */
function CWCButton_OnMouseUp( e )
{
    var target;
    var bNeedClick = false;
    if (CWCIsIE)
    {
        e = event;
        target = event.srcElement;
        e.cancelBubble = true;
        e.returnValue = false;
    }
    else
    {
        target = e.target
        if (e.cancelable)
            e.cancelBubble = true;
    }
    
    //catch the occasional error in IE
    if (target.cwcButton == null)
    {
        return;
    }
        
    if (!target.cwcButton.bMouseDown)
    {
        return;
    }        
    
    target.cwcButton.bMouseDown = false;
        
    if (!target.cwcButton.IsDisabled())
    {
        if (target.cwcButton.IsSelected() ) //&& CWCIsNav4)
        {
            bNeedClick = true;
        }
        if (target.cwcButton.type == CWCBUTTONTYPE_CLICK)
        {
            target.cwcButton.SetState( CWCBUTTONSTATE_SELECTED );
        }
        if (target.cwcButton.type == CWCBUTTONTYPE_RADIO)
        {
            target.cwcButton.SetState( CWCBUTTONSTATE_SELECTED );
            target.cwcButton.bActive = true;
            if (target.cwcButton.oGroup != null)
            {
                target.cwcButton.oGroup.ActivateButton( target.cwcButton );
            }
        }
        if (target.cwcButton.type == CWCBUTTONTYPE_TOGGLE)
        {
            if (target.cwcButton.bActive)
            {
                target.cwcButton.SetState( CWCBUTTONSTATE_NORMAL );           
                target.cwcButton.bActive = false;
            }
            else
            {
                target.cwcButton.SetState( CWCBUTTONSTATE_SELECTED );
                target.cwcButton.bActive = true;
            }
        }
    }
    
    if (bNeedClick)
    {
        CWCButton_OnMouseClick( e );
    }
    return false;
}

/**
 *
 */
function CWCButton_OnDragStart( e )
{
    //alert( "ondragstart" );
    var target;
    if (CWCIsIE)
    {
        e = event;
        target = event.srcElement;
        e.cancelBubble = true;
        e.returnValue = false;
    }
    else
    {
        target = e.target;
        if (e.cancelable)
            e.cancelBubble = true;
    }
}

