// File: /include/js/callWidthManager.js
// Desc: Do the width stuff
// $Revision: 20$
// $Date: 5/23/2007 12:16:41 PM$
// $Author: Donnie Tognazzini$
// $NoKeywords$
function VM_callWidthManager( callListView )
{
    try
    {
        UTILS.inheritFromBase( this, "VM_callWidthManager", callListView.jObject );

        this.m_callListView     = callListView;
        this.infoFixedWidth     = 97; // widths of 4 images
        this.playerFixedWidth   = 175;
        this.measurements       = { };

        // there are a few things that ought to be handled within a style sheet
        // but browser support for adding style rules is still primitive;
        this.cssRules = this.getCssRules( );

        var columnOffsets =
        {
            callerName  : 0,
            time        : 0,
            date        : 0,
            outcome     : 0
        };

        var columnWidths =
        {
            select      : "select",
            callerId    : "div",
            input       : "input"
        };

        this.GetColumnMetaData( columnOffsets, columnWidths );

        this.AdjustColumnCSSStyles( columnOffsets );

        // Update the backdrop
        var timeRight = parseInt( this.cssRules.time.right );
        var timeWidth = parseInt( this.cssRules.time.width );

        var OVERLAY_SPACE_WIDTH_PIXELS = 4;
        this.cssRules.upperbackdrop.width = ( timeRight + timeWidth + OVERLAY_SPACE_WIDTH_PIXELS ) + 'px';

        // do the calculations to make it quick to update on resize
        this.editFixedWidth = 111 + columnWidths.select + columnWidths.selectExtra + columnWidths.inputExtra;

        var callerNameMinWidth      = columnOffsets.callerName.offsetWidth;
        var txtCallerNameMinWidth   = columnWidths.input;
        var callerIdMinWidth        = columnWidths.callerId;

        this.callMinWidth = Math.max( Math.max( this.infoFixedWidth + callerNameMinWidth ,
                                                this.editFixedWidth + txtCallerNameMinWidth ),
                                      this.playerFixedWidth + callerIdMinWidth );

        this.setTempStyles( );

        if ( window )
        {
            this.bindToEvent( window , "resize" );
        }
    }
    catch( e )
    {
        EX_ASSERT_NO_EXCEPTIONS( e, "VM_callWidthManager::VM_callWidthManager( )" );
    }
}

var VM_callWidthManager_prototype =
{
    COLUMN_NAMES        : [ "callerName", "time", "date", "outcome" ],

    GetColumnMetaData : function( columnOffsets, columnWidths )
    {
        try
        {
            var savedWidths = UTILS.getCookie( "v2wWidths" );

            var widthsToSave = '';

            /******************************************************
            Determine the column offsets for all the call columns
            ******************************************************/

            var columnDOMElements = document.getElementById( "divBaseAlign" ).getElementsByTagName( "span" );

            for ( var i = 0 ; i < columnDOMElements.length; ++i )
            {
                var columnName = this.COLUMN_NAMES[ i ];

                // parse out the saved widths
                if ( savedWidths.length != 0 )
                {
                    columnOffsets[ columnName ] =
                    {
                        offsetTop   : parseInt( savedWidths.substr( i * 4, 2 ), 36 ),
                        offsetWidth : parseInt( savedWidths.substr( i * 4 + 2, 2 ), 36 )
                    };

                    continue;
                }

                // calculate the widths from the DOM
                var columnDOMElement = columnDOMElements.item( i );

                columnOffsets[ columnName ] =
                {
                    offsetTop   : columnDOMElement.offsetTop + 2,
                    offsetWidth : columnDOMElement.offsetWidth
                };

                // keep track of the widths so we can save them below
                widthsToSave += ( "0" + columnOffsets[ columnName ].offsetTop.toString( 36 ) ).slice( -2 ) +
                                ( "0" + columnOffsets[ columnName ].offsetWidth.toString( 36 ) ).slice( -2 );
            }

            document.getElementById( "divBaseAlign" ).style.display = "none";

            /******************************************************
            Determine the column widths for all the call columns
            ******************************************************/

            var divForWidths = document.getElementById( "divForWidths" );

            for ( var col in columnWidths )
            {
                // skip Extra widths
                if ( col.slice( -5 ) == "Extra" )
                {
                    continue;
                }

                // use the saved values
                if ( savedWidths.length != 0 )
                {
                    columnWidths[ col ]           = parseInt( savedWidths.substr( i++ * 2 + 8, 2 ), 36 );
                    columnWidths[ col + "Extra" ] = parseInt( savedWidths.substr( i++ * 2 + 8, 2 ), 36 );

                    continue;
                }

                var elem = divForWidths.getElementsByTagName( columnWidths[ col ] ).item( 0 );

                // calculate from the DOM
                columnWidths[ col ]           = $( elem ).width( );
                columnWidths[ col + "Extra" ] = elem.offsetWidth - columnWidths[ col ];

                // store the values in a cookie
                widthsToSave += ( "0" + columnWidths[ col ].toString( 36 ) ).slice( -2 );
                widthsToSave += ( "0" + columnWidths[ col + "Extra" ].toString( 36 ) ).slice( -2 );
            }

            document.getElementById( "divForWidths" ).style.display = "none";

            // save the calculated widths if necessary
            if ( widthsToSave.length != 0 )
            {
                document.cookie = "v2wWidths=" + widthsToSave;
            }
        }
        catch( e )
        {
            EX_ASSERT_NO_EXCEPTIONS( e, "VM_callWidthManager::GetColumnMetaData( )" );
        }
    },

    AdjustColumnCSSStyles : function( columnOffsets )
    {
        try
        {
            var previousColumn = null;
            var currentRight   = 52;

            for ( var i = this.COLUMN_NAMES.length - 1; i >= 0; --i )
            {
                var column = this.COLUMN_NAMES[ i ];

                var columnCSSRules = this.cssRules[ column ];

                columnCSSRules.top = columnOffsets[ column ].offsetTop + "px";
                columnCSSRules.right = currentRight + "px";

                if ( previousColumn !== null )
                {
                    this.cssRules[ previousColumn ].width = columnOffsets[ previousColumn ].offsetWidth + "px";

                    this.infoFixedWidth += columnOffsets[ previousColumn ].offsetWidth + 4;
                }

                currentRight += columnOffsets[ column ].offsetWidth + 4;
                previousColumn = column;
            }
        }
        catch ( e )
        {
            EX_ASSERT_NO_EXCEPTIONS( e, "VM_callWidthManager::AdjustColumnCSSStyles( )" );
        }
    },

    /***********************************************************************
      getCssRules

        This method iterates over all the style sheets and attempts to find
        the one that contains our writeable call list styles. Precautions
        are taken to avoid accessing styles inside of non-CW sheets as the
        Google Gadget framework libraries can inject such sheets and attempting
        to access them raises security restrictions in some browsers.

    ***********************************************************************/
    getCssRules: function( )
    {
        try
        {
            function GetStyleSheetTitle( styleSheet )
            {
                if ( styleSheet.title !== undefined && styleSheet.title !== null )
                {
                    return styleSheet.title;
                }

                if ( styleSheet.ownerNode !== undefined && styleSheet.ownerNode.title !== undefined && styleSheet.ownerNode.title !== null )
                {
                    return styleSheet.ownerNode.title;
                }

                return null;
            }

            function GetStyleSheetRulesMatchWriteableCallListSheet( rules )
            {
                var EXPECTED_STYLE_COUNT = 8;

                return ( rules.length == EXPECTED_STYLE_COUNT &&
                         rules[0].selectorText                &&
                         rules[0].selectorText.search( /#WriteableCallListStyles/ ) != -1 );
            }

            // Iterate across every style sheet in the document.
            for ( var n = 0; n < document.styleSheets.length; n++ )
            {
                var styleSheet = document.styleSheets[ n ];

                // We are looking for our inline style sheet. Use the
                // title to identify this sheet if we can.
                var styleSheetTitle = GetStyleSheetTitle( styleSheet );

                if ( styleSheetTitle === null )
                {
                    // The title doesn't exist. Avoid accessing sheets that
                    // are not inline.
                    if ( styleSheet.href && styleSheet.href.length != 0 )
                    {
                        continue;
                    }

                    // There's a pretty good chance that this is the CW
                    // inline style sheet so dip into the sheet and look
                    // for our sentinel style. This is our final attempt
                    // for browsers that don't support the title attribute
                    // of the style tag. This code may raise security
                    // warnings possibly rendering our gadget unusable.

                    var oRules = styleSheet.rules || styleSheet.cssRules;

                    if ( ! GetStyleSheetRulesMatchWriteableCallListSheet( oRules ) )
                    {
                        // can't possibly be ours
                        continue;
                    }

                    // Found it - fall through to process below.
                }
                else if ( styleSheetTitle != "WriteableCallListStyles" )
                {
                    // If the title is defined and it's not our title
                    // then we know the sheet is not ours, so move on
                    // to the next.
                    continue;
                }

                // We've found the writeable call list styles sheet.
                // Do some sanity checking to make sure it's really the right one.
                var oRules = styleSheet.rules || styleSheet.cssRules;

                if ( ! GetStyleSheetRulesMatchWriteableCallListSheet( oRules ) )
                {
                    throw new VM_EX_StylesheetNotFound( '#WriteableCallListStyles', "Style sheet failed sanity check" );
                }

                // initialize our look-aside map
                var cssRules =
                {
                    call                : null,
                    callerName          : null,
                    upperbackdrop       : null,
                    date                : null,
                    time                : null,
                    outcome             : null,
                    input               : null
                };

                // Iterate through each of the styles in the sheet
                // and update our look-aside map to point at the
                // DOM style.
                for ( var i = 0; i < oRules.length; i++ )
                {
                    var styleSheetRule = oRules[ i ];

                    if ( ! styleSheetRule.selectorText )
                    {
                        throw new Error( "Failed accessing WriteableCallListStyles CSS" );
                    }

                    // Look for styles like: #callListFrame div.upperbackdrop
                    var columnStyleSelectorTextMatches = /.*(?:div\.|[ ])([^ \.]+)$/i.exec( styleSheetRule.selectorText );

                    if ( ! columnStyleSelectorTextMatches || columnStyleSelectorTextMatches.length != 2 )
                    {
                        // ignore this style
                        continue;
                    }

                    // The column-name style is the second match
                    var columnStyle = columnStyleSelectorTextMatches[ 1 ];

                    // Find the style in the CSS rules we care about
                    for ( var cssRule in cssRules )
                    {
                        if ( cssRule.toLowerCase( ) == columnStyle.toLowerCase( ) )
                        {
                            // map our CSS rule to the DOM CSS style
                            cssRules[ cssRule ] = styleSheetRule.style;

                            break;
                        }
                    }
                }

                return cssRules;
            }

            // If we reached here then we went through all the sheets
            // in the document and couldn't find the sheet we were looking for.
            throw new VM_EX_StylesheetNotFound( '#WriteableCallListStyles', "Exhausted all style sheets" );
        }
        catch ( e )
        {
            EX_ASSERT_NO_EXCEPTIONS( e, "VM_callWidthManager::getCssRules( )" );
        }
    },

    setTempStyles: function( )
    {
        try
        {
            var callWidth = document.getElementById( "callListFrame" ).offsetWidth;
            var callWidthStyle = "100%";

            if ( callWidth < this.callMinWidth )
            {
                callWidth = this.callMinWidth;
                callWidthStyle = callWidth + "px";
            }

            if ( callWidth == this.callWidth )
            {
                return false; // no need to update widths
            }
            this.cssRules.call.width = callWidthStyle;

            this.callWidth = callWidth;
            this.cssRules.input.width = ( callWidth - this.editFixedWidth ) + "px";
            this.callerNameWidth = callWidth - this.infoFixedWidth;
            this.callerIdWidth = callWidth - this.playerFixedWidth;
            return true; // widths need updating
        }
        catch( e )
        {
            EX_ASSERT_NO_EXCEPTIONS( e, "VM_callWidthManager::setTempStyles( )" );
        }

    },

    getMeasurementInfo: function( callView, columnName, newValue )
    {
        try
        {
            var ret =
            {
                index: columnName + "\t" + newValue.replace( /\d/g, "8" ),
                subIndex: columnName == "callerId"
                        ? CL_REVIEW_STATE_NEW
                        : ( callView.m_revStatus == CL_REVIEW_STATE_DELETED ? CL_REVIEW_STATE_NEW : callView.m_revStatus )
            };
            return ret;
        }
        catch( e )
        {
            EX_ASSERT_NO_EXCEPTIONS( e, "VM_callWidthManager::getMeasurementInfo( )" );
        }
    },

    handleColumnBeforeInsert: function( callView, columnName, newValue )
    {
        try
        {
            var measInfo = this.getMeasurementInfo( callView, columnName, newValue );
            if ( !this.measurements.hasOwnProperty( measInfo.index ) )
            {
                this.measurements[ measInfo.index ] = { N: 0, S: 0 };
                return false;
            }

            var meas = this.measurements[ measInfo.index ];

            if ( !meas[ measInfo.subIndex ] )
                return false;

            callView[ columnName + "Width" ] = meas[ measInfo.subIndex ];

            return true;
        }
        catch( e )
        {
            EX_ASSERT_NO_EXCEPTIONS( e, "VM_callWidthManager::handleColumnBeforeInsert( )" );
        }
    },

    handleColumnAfterInsert: function( callView, columnName, newValue, wasDeletedRemoved )
    {
        try
        {
            var measInfo = this.getMeasurementInfo( callView, columnName, newValue );
            if ( !this.measurements.hasOwnProperty( measInfo.index ) )
            {
                // initialize the measinfo
                this.measurements[ measInfo.index ] = { N: 0, S: 0 };
            }
            var meas = this.measurements[ measInfo.index ];
            var dataWidth = meas[ measInfo.subIndex ];
            var wasEditingRemoved = false;

            if ( !dataWidth )
            {
                if ( callView.m_revStatus == CL_REVIEW_STATE_DELETED && !wasDeletedRemoved )
                {
                    callView.jObject.removeClass( "deleted" );
                    wasDeletedRemoved = true;
                }
                else if ( columnName == "callerName" && callView.IsEditing( ) )
                {
                    if ( callView.m_revStatus == CL_REVIEW_STATE_DELETED )
                    {
                        callView.UpdateEditingState( false );
                    }
                    else
                    {
                        callView.jObject.removeClass( "editing" );
                        wasEditingRemoved = true;
                    }
                }

                dataWidth = meas[ measInfo.subIndex ] = callView[ "j" + columnName ][ 0 ].offsetWidth;

                if ( wasEditingRemoved )
                {
                    callView.jObject.addClass( "editing" );
                }
            }

            callView[ columnName + "Width" ] = dataWidth;

            return wasDeletedRemoved;
        }
        catch( e )
        {
            EX_ASSERT_NO_EXCEPTIONS( e, "VM_callWidthManager::handleColumnAfterInsert( )" );
        }
    },

    onresize: function( )
    {
        try
        {
            this.setTempStyles( );
        }
        catch( e )
        {
            EX_ASSERT_NO_EXCEPTIONS( e, "VM_callWidthManager::onresize( )" );
        }
    }
};
