// File: /include/js/callView.js
// Desc: individual Call div
// $Revision: 61$
// $Date: 5/23/2007 12:16:41 PM$
// $Author: Donnie Tognazzini$
// $NoKeywords$

function VM_CallView( call, callListView, elemInsertAfter )
{
    try
    {
        // Create the globally unique name.
        var name = call.m_cid.toString( ) + '_' + callListView.GetCallViewSerialNumber( );

        UTILS.inheritFromBase( this, name );

        // list specific attributes
        this.m_callListView = callListView;

        // call specific attributes
        this.m_call             = call;
        this.m_callerNameTip    = this.getNameTip( this.m_call );
        this.m_callerId         = this.m_call.m_fmtCallerId || "unavailable";
        this.m_callerName       = this.m_call.m_name || this.m_callerId;

        // call-static specific attributes
        this.m_includeEdit  = this.m_callerId != "unavailable";

        // environment specific attributes
        this.m_tz               = this.m_callListView.m_callListHandler.m_tz;
        this.m_playableMediaExists = this.m_call.m_durSecs &&
                                     ( this.m_callListView.flashDetectionState == this.m_callListView.FLASH_OK ||
                                       this.m_callListView.flashDetectionState == this.m_callListView.FLASH_OK_BUT_NOT_REFRESHED );

        // jQuery assigns a Guid ( basically an identity key )to functions to check equality
        // when removing a specific handler function. It doesn't really expose
        // this interface but we do
        this.m_undeleteGuid     = null;
        this.m_clearMessageGuid = null;

        // add the flash function to the global namespace
        var self = this;
        window[ "player_" + this.name + "_DoFSCommand" ] = function ( eventName, arg )
        {
            UTILS.raise( "Flash" + eventName.substr( 2 ), [  self.GetCallIdString( ), arg ] );
        };

        // we cache name widths so we may be able to determine whether the ellipsis
        // is needed prior to creating the DOM object. This is better performance
        var wasCallerNameHandled = this.m_callListView.m_callWidthManager.handleColumnBeforeInsert( this, "callerName", this.m_callerName )
        var wasCallerIdHandled = !this.m_playableMediaExists || this.m_callListView.m_callWidthManager.handleColumnBeforeInsert( this, "callerId", this.m_call.m_fmtCallerId )
        var wasDeletedRemoved = false;

        this.insertCall( elemInsertAfter );

        this.setJObject( "#" + this.name );
        this.jcallerName = $( "div.callerName", this.jObject );

        if ( this.m_playableMediaExists )
        {
            this.m_mediaPlayer = new VM_MediaPlayer( this.name, this.jObject, this.m_call.getMessagePrimaryFetch( ), this.m_call.getMessageSecondaryFetch( ) );

            this.jcallerId = $( "div.callerId", this.jObject );
        }

        this.m_isSelected = false;
        this.m_isEditing  = false;

        // If we didn't know the measurements in advance, calculate now.
        // If the call is deleted, we have to display it to measure it
        if ( ! wasCallerNameHandled )
        {
            wasDeletedRemoved = this.m_callListView.m_callWidthManager.handleColumnAfterInsert( this, "callerName", this.m_callerName, wasDeletedRemoved );
        }

        if ( ! wasCallerIdHandled )
        {
            wasDeletedRemoved = this.m_callListView.m_callWidthManager.handleColumnAfterInsert( this, "callerId", this.m_call.m_fmtCallerId, wasDeletedRemoved );
        }

        if ( wasDeletedRemoved )
        {
            this.jObject.addClass( "deleted" );
        }

        this.bindToEvent( this.jObject, "click" );
    }
    catch ( e )
    {
        EX_ASSERT_NO_EXCEPTIONS( e, "VM_CallView::VM_CallView( )" );
    }
}

var VM_CallView_prototype =
{
    GetCallIdString : function( )
    {
        return this.m_call.m_cid.toString( );
    },

    IsEditing : function( )
    {
        return this.m_isEditing;
    },

    onclick: function( event )
    {
        try
        {
            switch ( event.target.title )
            {
            case "Play":

                this.UpdateReviewStatus( CL_REVIEW_STATE_SAVED );

                if ( this.m_mediaPlayer )
                {
                    this.m_mediaPlayer.play( );
                }
                break;

            case "Stop":

                this.UpdateReviewStatus( CL_REVIEW_STATE_SAVED );

                if ( this.m_mediaPlayer )
                {
                    this.m_mediaPlayer.stop( );
                }
                break;

            case "Edit":
                this.UpdateEditingState( true );
                return;

            case "Delete":
                // If the object is in an Edit state, cancel that state.
                this.UpdateEditingState( false );

                // Display un-delete link in the message area.
                CW_GetMessageArea( ).display( "<a id=\"lnkUndelete\" href=\"javascript:void(0)\" onmouseover=\"window.status=''\">Call deleted - Click here to undo.</a>",
                                              CW_GetMessageArea( ).MESSAGE_TYPE_STATUS );

                // Set state to Delete.
                // This will also bind the object's onclick handler to the click event of the undelete link now displayed in the message area.
                this.UpdateReviewStatus( CL_REVIEW_STATE_DELETED );

                return;

            case "Save Changes":
                var elemPhoneType = $( "select", this.jObject )[ 0 ];
                var newPhoneType = elemPhoneType.options[ elemPhoneType.selectedIndex ].value;
                var newCallerName = $( "input", this.jObject ).val ( );

                if ( newCallerName != this.m_call.m_name ||
                     newCallerName != this.m_callerId )
                {
                    // remove trailing and leading spaces
                    newCallerName = newCallerName.replace(/^\s+/,"").replace(/\s+$/,"");

                    // empty contact name not allowed
                    // display message and rollback input value
                    if ( newCallerName.length == 0 )
                    {
                        CW_GetMessageArea( ).display( "Contact name is required.", CW_GetMessageArea( ).MESSAGE_TYPE_ALERT );

                        // rollback input to previous value
                        document.getElementById( "callerName_" + this.name ).value = this.m_call.m_name

                        return;
                    }

                    // save value
                    this.m_callListView.updateCallerName( this.GetCallIdString( ), newCallerName );
                }

                if ( newPhoneType != this.m_call.m_phoneType )
                {
                    this.m_callListView.updatePhoneType( this.GetCallIdString( ), newPhoneType );
                }
                // fall through

            case "Cancel Edit":
                // clear any edit messages that might have been displayed during validation
                CW_GetMessageArea( ).Clear( );

                this.UpdateEditingState( false );

                return;

            default:
                if ( "INPUT|SELECT|OPTION|OBJECT".indexOf( event.target.tagName ) != -1 )
                {
                    return;
                }

                // fall through
            }

            this.UpdateSelectedState( true );
        }
        catch ( e )
        {
            EX_ASSERT_NO_EXCEPTIONS( e, "VM_CallView::onclick( )" )
        }
    },

    UpdateSelectedState : function( newValue )
    {
        try
        {
            if ( newValue == this.m_isSelected )
            {
                return;
            }

            this.m_isSelected = newValue;

            if ( newValue )
            {
                if ( this.m_playableMediaExists )
                {
                    this.jObject.addClass( "hotHasMsg" );
                }
                else
                {
                    this.UpdateReviewStatus( CL_REVIEW_STATE_SAVED );
                }

                this.jObject.addClass( "hot" );

                this.m_callListView.OnCallSelected( this.GetCallIdString( ) );

                return;
            }

            if ( this.m_mediaPlayer )
            {
                this.m_mediaPlayer.stop( );
            }

            if ( this.m_playableMediaExists )
            {
                this.jObject.removeClass( "hotHasMsg" );
            }

            this.jObject.removeClass( "hot" );

            this.UpdateEditingState( false );

            this.m_callListView.OnCallUnselected( this.GetCallIdString( ) );
        }
        catch ( e )
        {
            EX_ASSERT_NO_EXCEPTIONS( e, "VM_CallView::UpdateSelectedState( )" )
        }
    },

    UpdateEditingState: function( newValue )
    {
        try
        {
            if ( newValue == this.m_isEditing )
            {
                return;
            }

            this.m_isEditing = newValue;

            // clear any edit messages that might have been displayed during validation of any call (since you can switch from edit mode of
            // one call to edit mode of another)
            CW_GetMessageArea( ).Clear( );

            var elemEdit = document.getElementById( $( "img.btnEdit", this.jObject )[ 0 ].useMap.substr( 1 ) ).getElementsByTagName("area")[ 0 ];

            if ( ! newValue )
            {
                this.jObject.removeClass( "editing" );
                elemEdit.title = elemEdit.alt = "Edit";

                this.m_callListView.OnCallEditingEnd( this.GetCallIdString( ) );

                return;
            }

            this.jObject.addClass( "editing" );

            this.m_callListView.OnCallEditingBegin( this.GetCallIdString( ) );

            elemEdit.title = "Cancel Edit";
            elemEdit.alt = "Cancel Edit";

            var sel = document.getElementById( "phoneTypeSelector_" + this.name );

            for ( var i = 0; i < sel.options.length; ++i )
            {
                if ( sel.options[ i ].value == this.m_call.m_phoneType )
                {
                    sel.selectedIndex = i;
                    break;
                }
            }

            var callerNameField = document.getElementById( "callerName_" + this.name );

            callerNameField.value = this.m_call.m_name;
            callerNameField.select( );
            callerNameField.focus( );
        }
        catch ( e )
        {
            EX_ASSERT_NO_EXCEPTIONS( e, "VM_CallView::UpdateEditingState( )" )
        }
    },

    UpdateReviewStatus: function( newReviewStatus )
    {
        try
        {
            var oldReviewStatus = this.m_call.getReviewStatus( );

            if ( oldReviewStatus == newReviewStatus )
            {
                return;
            }

            this.m_callListView.UpdateCallReviewStatus( this, oldReviewStatus, newReviewStatus );
        }
        catch ( e )
        {
            EX_ASSERT_NO_EXCEPTIONS( e, "VM_CallView::onreviewStatusChange( )" )
        }
    },

    ApplyNewReviewStatus: function( oldReviewStatus, newReviewStatus )
    {
        try
        {
            switch ( newReviewStatus )
            {
            case CL_REVIEW_STATE_DELETED:
                {
                    this.OnDelete( );
                }
                break;

            case CL_REVIEW_STATE_SAVED:
            case CL_REVIEW_STATE_NEW:
                {
                    if ( oldReviewStatus == CL_REVIEW_STATE_DELETED )
                    {
                        this.OnUndelete( );
                    }

                    if ( newReviewStatus == CL_REVIEW_STATE_SAVED )
                    {
                        this.jObject.removeClass( "new" );
                    }
                    else
                    {
                        this.jObject.addClass( "new" );
                    }

                    this.m_callListView.m_callWidthManager.handleColumnAfterInsert( this, "callerName", this.m_callerName, false );
                }
                break;

            default:

                throw new Error( "Invalid review status: " + newReviewStatus );
            }
        }
        catch ( e )
        {
            EX_ASSERT_NO_EXCEPTIONS( e, "VM_CallView::ApplyNewReviewStatus( )" )
        }
    },

    OnDelete : function( )
    {
        try
        {
            if ( this.m_call.getReviewStatus( ) == CL_REVIEW_STATE_SAVED )
            {
                this.jObject.addClass( "new" );
                this.m_callListView.m_callWidthManager.handleColumnAfterInsert( this, "callerName", this.m_callerName, true );
            }

            this.jObject.addClass( "deleted" );

            this.UpdateSelectedState( false );
            this.UpdateEditingState( false );

            var callId       = this.GetCallIdString( );
            var callListView = this.m_callListView;

            var onUndeleteClicked = function( event )
            {
                callListView.OnUndeleteClicked( callId );
            };

            var onClearMessage = function( event )
            {
                callListView.OnClearMessage( callId );
            };

            this.m_undeleteGuid     = this.bindToEvent( "#lnkUndelete", "click", onUndeleteClicked );
            this.m_clearMessageGuid = this.bindToEvent( CW_GetMessageArea( ).jObject, "clear", onClearMessage );
        }
        catch ( e )
        {
            EX_ASSERT_NO_EXCEPTIONS( e, "VM_CallView::OnDelete( )" )
        }
    },

    OnUndelete: function( )
    {
        try
        {
            this.jObject.removeClass( "deleted" );

            CW_GetMessageArea( ).display( "Call undeleted.", CW_GetMessageArea( ).MESSAGE_TYPE_STATUS, 5 );
        }
        catch ( e )
        {
            EX_ASSERT_NO_EXCEPTIONS( e, "VM_CallView::OnUndelete( )" )
        }
    },

    onUndeleteLinkClicked: function( event )
    {
        try
        {
            this.UpdateReviewStatus( CL_REVIEW_STATE_NEW );
        }
        catch ( e )
        {
            EX_ASSERT_NO_EXCEPTIONS( e, "VM_CallView::onUndeleteLinkClicked( )" )
        }
    },

    onClearMessage: function( )
    {
        try
        {
            UTILS.unbind( $("#lnkUndelete"), "click",  this.m_undeleteGuid );
            this.m_undeleteGuid = null;
            UTILS.unbind( CW_GetMessageArea( ).jObject, "clear", this.m_clearMessageGuid );
            this.m_clearMessageGuid = null;
        }
        catch ( e )
        {
            EX_ASSERT_NO_EXCEPTIONS( e, "VM_CallView::onClearMessage( )" )
        }
    },

    updateFrom: function( newCall )
    {
        try
        {
            if ( newCall.equals( this.m_call ) )
            {
                this.m_call = newCall;

                // nothing else to do
                return;
            }

            if ( this.m_phoneTypeWord != newCall.getPhoneTypeAsString( ) )
            {
                this.updatePhoneType( newCall.m_phoneType );
            }

            var newCallCallerName    = newCall.m_name || newCall.m_fmtCallerId || "unavailable";
            var callerNameHasChanged = this.m_callerName != newCallCallerName;

            if ( callerNameHasChanged )
            {
                this.jcallerName.html( newCallCallerName );

                var newCallCallerNameTip = this.getNameTip( newCall );

                this.jcallerName.attr( "title", newCallCallerNameTip );

                $( "input", this.jObject ).val( this.m_call.m_name || this.m_call.m_callerId );
            }

            // unless time zone changes, neither call date nor time should change;
            var currentTimeZone = this.m_callListView.m_callListHandler.m_tz;
            if ( this.m_tz != currentTimeZone )
            {
                if (this.m_call.m_fmtDate != newCall.m_fmtDate )
                {
                    $( ".date", this.jObject ).html( newCall.m_fmtDate );
                }

                $( ".time", this.jObject ).html( newCall.m_fmtTime ).attr( "title", newCall.m_fmtTime + " " + currentTimeZone );

                this.m_tz = currentTimeZone;
            }

            var oldReviewStatus = this.m_call.m_revStatus;

            this.ApplyNewReviewStatus( oldReviewStatus, newCall.m_revStatus );

            // if we switched between saved and some other state, updating the review status updated the ellipsis display
            // otherwise, if we updated the name, we have to do it ourself.
            if ( callerNameHasChanged &&
                 ( oldReviewStatus == newCall.m_revStatus ||
                 ( oldReviewStatus != CL_REVIEW_STATE_SAVED && newCall.m_revStatus != CL_REVIEW_STATE_SAVED ) ) )
            {
                 if ( this.m_callListView.m_callWidthManager.handleColumnAfterInsert( this, "callerId", newCallCallerName, false ) )
                 {
                    this.jObject.addClass( "deleted" );
                 }
            }

            this.m_call = newCall;
        }
        catch ( e )
        {
            EX_ASSERT_NO_EXCEPTIONS( e, "VM_CallView::updateFrom( )" )
        }
    },

    updatePhoneType: function( newPhoneType )
    {
        try
        {
            var elem = $("img.phoneType",this.jObject);
            var newPhoneTypeWord = CL_GetPhoneTypeAsString( newPhoneType );

            if ( newPhoneTypeWord == "Unknown" )
            {
                elem.hide();
            }
            else
            {
                elem.show();
                elem.attr("title", newPhoneTypeWord);
                elem.attr("alt", newPhoneTypeWord);

                elem.src( elem.src( ).replace( /Work|Home|Cell|Unknown/, newPhoneTypeWord ) );
            }
            if ( this.m_includeEdit )
            {
                $("select option", this.jObject).each( function() { this.selected = this.value == newPhoneType; });
            }
            this.m_call.m_phoneType = newPhoneType;
            this.m_phoneTypeWord = newPhoneTypeWord;
        }
        catch ( e )
        {
            EX_ASSERT_NO_EXCEPTIONS( e, "VM_CallView::updatePhoneType( )" )
        }
    },

    updateCallerName: function( newCallerName )
    {
        try
        {
            this.m_call.m_name      = newCallerName;
            this.m_callerName       = this.m_call.m_name || this.m_callerId;
            this.m_callerNameTip    = this.getNameTip( this.m_call );

            this.jcallerName.html( UTILS.escapeHTML( this.m_callerName ) );
            this.jcallerName.attr( "title", this.m_callerNameTip );

            $( "input", this.jObject ).val( this.m_callerName );

            if ( this.m_callListView.m_callWidthManager.handleColumnAfterInsert( this, "callerName", this.m_callerName, false ) )
            {
                this.jObject.addClass( "deleted" );
            }
        }
        catch ( e )
        {
            EX_ASSERT_NO_EXCEPTIONS( e, "VM_CallView::updateCallerName( )" )
        }
    },

    getCallInfoCallerName: function( )
    {
        try
        {
            var callerName = this.m_call.getCallerName( );

            if ( callerName && callerName.length > 0 )
            {
                return callerName;
            }

            return this.m_call.getFormattedCallerId( );
        }
        catch ( e )
        {
            EX_ASSERT_NO_EXCEPTIONS( e, "VM_CallView::getCallInfoCallerName( )" )
        }
    },

    getNameTip: function( call )
    {
        try
        {
            var callerName = call.getCallerName( );

            if ( callerName &&
                 ( callerName.toLowerCase( ) == 'private caller' ||
                   callerName.toLowerCase( ) == 'unavailable' ) )
            {
                return '';
            }

            var nameTipSep = "\n";

            // use a space on firefox since on pre-2.0 versions
            // newlines get displayed as funky ASCII characters.
            if ( navigator.userAgent.toLowerCase( ).indexOf( "firefox" ) != -1 )
            {
                nameTipSep = ' ';
            }

            var nameTip = '' ;

            var callerId = call.getFormattedCallerId( );

            if ( callerId && callerId.length > 0 )
            {
                nameTip = callerId;

                if ( callerName && callerName.length > 0 )
                {
                    nameTip = UTILS.escapeHTML( callerName ) + nameTipSep + callerId;
                }
            }

            // append the location if it's available
            var callLoc = call.getLocation( );
            if ( callLoc && callLoc.length > 0 )
            {
                nameTip += nameTip.length > 0 ? nameTipSep : '';
                nameTip += callLoc.replace( /United States/g, '' );
            }

            return nameTip;
        }
        catch ( e )
        {
            EX_ASSERT_NO_EXCEPTIONS( e, "VM_CallView::getNameTip( )" )
        }
    },

    PLAYER_HTML_TEMPLATE: '' +
        '<map id="mapPlay_##VIEW_NAME##" name="mapPlay_##VIEW_NAME##">' +
          '<area title="Play" alt= "Play" shape="circle" coords="10,10,10" href="javascript:void(0)" />' +
        '</map>' +

        '<map id="mapStop_##VIEW_NAME##" name="mapStop_##VIEW_NAME##">' +
          '<area title="Stop" alt="Stop" shape="circle" coords="10,10,10" href="javascript:void(0)" />' +
        '</map>' +

        '<img class="actionButton" src="##PLAY_BUTTON_IMAGE##" height="21" width="21" usemap="#mapPlay_##VIEW_NAME##" alt="" />' +

        '<object id="player_##VIEW_NAME##"' +
        ' classid="clsid:d27cdb6e-ae6d-11cf-96b8-444553540000"' +
        ' codebase="http://fpdownload.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=6,0,40,0"' +
        ' type="application/x-shockwave-flash"' +
        ' height="26"' +
        ' width="142">' +

          '<param name="movie" value="mediaPlayerWidgetEx.swf" />' +
          '<param name="quality" value="high" />' +
          '<param name="wmode" value="transparent" />' +
          '<param name="allowScriptAccess" value="always" />' +
          '<param name="FlashVars" value="color=0xbf0000&autoPlay=false&messageFetchTimeout_ms=30000&md=##DURATION_MS##" />' +

          '<embed src="mediaPlayerWidgetEx.swf"' +
          ' name="player_##VIEW_NAME##"' +
          ' quality="high"' +
          ' wmode="transparent"' +
          ' allowscriptaccess="always"' +
          ' flashvars="color=0xbf0000&autoPlay=false&messageFetchTimeout_ms=30000&md=##DURATION_MS##"' +
          ' swliveconnect="true"' +
          ' pluginspage="http://www.macromedia.com/go/getflashplayer"' +
          ' type="application/x-shockwave-flash"' +
          ' height="26"' +
          ' width="142" />' +

        '</object>' +

        '<' + 'sc' + 'ript event=FSCommand(command,args) for=player_##VIEW_NAME##>\n player_##VIEW_NAME##_DoFSCommand( command, args );<' + '/' + 'scr' + 'ipt>' +
    '',

    EXPANDED_ROW_HTML_TEMPLATE: '' +
        '<div class="callerId">##FORMATTED_CALLER_ID##</div>' +
        '<div class="allowStripe"></div>' +
    '',

    EDIT_HTML_TEMPLATE: '' +
        '<input type="text" id="callerName_##VIEW_NAME##" value="##ESC_CALLER_NAME##" maxlength="64" />' +
        '<select id="phoneTypeSelector_##VIEW_NAME##">' +
          '<option value="H">Home</option>' +
          '<option value="W">Work</option>' +
          '<option value="C">Cell</option>' +
          '<option value="U">Unknown</option>' +
        '</select>' +

        '<div class="ok" title="Save Changes" alt="Save Changes">ok</div>' +

        '<map id="mapEdit_##VIEW_NAME##" name="mapEdit_##VIEW_NAME##">' +
        '  <area title="Edit" alt="Edit" shape="circle" coords="10,10,10" href="javascript:void(0)" />' +
        '</map>' +

        '<img class="btnEdit" src="##EDIT_BUTTON_IMAGE##" height="21" width="21" usemap="#mapEdit_##VIEW_NAME##" alt="" />' +
    '',

    CALL_HTML_TEMPLATE: '' +
        '<img ##PHONE_TYPE_DISPLAY## src="images/y_icon_##PHONE_TYPE_WORD##.gif" height="14" width="14"' +
        ' class="phoneType" title="##PHONE_TYPE_WORD##" alt="##PHONE_TYPE_WORD##" />' +

        '<div class="callerName" title="##CALLER_NAME_TIP##">##ESC_CALLER_NAME##</div>' +

        '<div class="date restOfInfo" title="##DATE## ##TIME## ##TIME_ZONE##">##DATE##</div>' +
        '<div class="time restOfInfo" title="##DATE## ##TIME## ##TIME_ZONE##">##TIME##</div>' +
        '<div class="outcome restOfInfo">##OUT_COME##</div>' +

        '<map id="mapDelete_##VIEW_NAME##" name="mapDelete_##VIEW_NAME##">' +
        '  <area title="Delete" alt="Delete" shape="circle" coords="10,10,10" href="javascript:void(0)" />' +
        '</map>' +

        '<img class="btnDelete" src="##DELETE_BUTTON_IMAGE##" height="21" width="21" usemap="#mapDelete_##VIEW_NAME##" alt="" />' +
        '<div class="upperbackdrop allowStripe"></div>' +
    '',

    DIV_HTML_TEMPLATE: '<div class="call##CALL_CLASS_SUFFIX##" id="##VIEW_NAME##"></div>',

    insertCall: function( elemInsertAfter )
    {
        try
        {
            // insert the DIV HTML
            var divHTML = this.DIV_HTML_TEMPLATE.replace( /##CALL_CLASS_SUFFIX##/g,
                                                          this.m_call.m_revStatus == CL_REVIEW_STATE_SAVED ? '' :
                                                          ( ' new' + ( this.m_call.m_revStatus == CL_REVIEW_STATE_DELETED ? ' deleted' : '' ) ) );

            divHTML = divHTML.replace( /##VIEW_NAME##/g, this.name );

            $( divHTML ).insertAfter( elemInsertAfter );

            // insert the call HTML into the DIV
            var callHTML = this.CALL_HTML_TEMPLATE;

            if ( this.m_playableMediaExists )
            {
                callHTML += this.PLAYER_HTML_TEMPLATE;
            }

            callHTML += this.EXPANDED_ROW_HTML_TEMPLATE;

            if ( this.m_includeEdit )
            {
                callHTML += this.EDIT_HTML_TEMPLATE;
            }

            // replace all the template parameters
            callHTML = callHTML.replace( /##VIEW_NAME##/g, this.name );
            callHTML = callHTML.replace( /##CALLER_ID##/g, this.m_callerId );
            callHTML = callHTML.replace( /##CALLER_NAME_TIP##/g, this.getNameTip( this.m_call ) );
            callHTML = callHTML.replace( /##DATE##/g, this.m_call.m_fmtDate );
            callHTML = callHTML.replace( /##DURATION_MS##/g, this.m_call.m_durSecs * 1000 );
            callHTML = callHTML.replace( /##ESC_CALLER_NAME##/g, UTILS.escapeHTML( this.getCallInfoCallerName( ) ) );
            callHTML = callHTML.replace( /##OUT_COME##/g, this.m_call.m_outcome );
            callHTML = callHTML.replace( /##PHONE_TYPE_DISPLAY##/g, this.m_call.m_phoneType == 'U' ? ' style="display: none"' : '' );
            callHTML = callHTML.replace( /##PHONE_TYPE_WORD##/g, this.m_call.getPhoneTypeAsString( ) );
            callHTML = callHTML.replace( /##TIME##/g, this.m_call.m_fmtTime );
            callHTML = callHTML.replace( /##TIME_ZONE##/g, this.m_tz );

            callHTML = callHTML.replace( /##FORMATTED_CALLER_ID##/g, this.m_call.getFormattedCallerId( ) );

            callHTML = callHTML.replace( /##PLAY_BUTTON_IMAGE##/g, VM_RESOURCE_PLAY_IMAGE );
            callHTML = callHTML.replace( /##EDIT_BUTTON_IMAGE##/g, VM_RESOURCE_EDIT_IMAGE );
            callHTML = callHTML.replace( /##DELETE_BUTTON_IMAGE##/g, VM_RESOURCE_DELETE_IMAGE );

            document.getElementById( this.name ).innerHTML = callHTML;
        }
        catch ( e )
        {
            EX_ASSERT_NO_EXCEPTIONS( e, "VM_CallView::insertCall( )" );
        }
    },

    onFlashError: function( )
    {
        this.m_mediaPlayer.onFlashError( );
    },

    onFlashComplete: function( )
    {
        this.m_mediaPlayer.onFlashComplete( );
    },

    onFlashSeekStart : function( )
    {
        this.m_mediaPlayer.onFlashSeekStart( );
    },

    onFlashSeekComplete : function( )
    {
        this.m_mediaPlayer.onFlashSeekComplete( );
    },

    IsPlayerInUse: function( )
    {
        if ( ! this.m_mediaPlayer )
        {
            return false;
        }

        return this.m_mediaPlayer.isInUse( );
    },

    destructor: function( )
    {
        try
        {
            if ( this.m_undeleteGuid !== null )
            {
                 CW_GetMessageArea( ).Clear( );
            }

            UTILS.unbindAll( this.jObject );

            this.UpdateSelectedState( false );
            this.UpdateEditingState( false );

            if ( this.m_mediaPlayer )
            {
                delete this.m_mediaPlayer;
            }

            if ( window[ "player_" + this.name + "_DoFSCommand" ] )
            {
                window[ "player_" + this.name + "_DoFSCommand" ] = null;

                try
                {
                    delete window[ "player_" + this.name + "_DoFSCommand" ];
                }
                catch ( e )
                {
                    // ignore
                }
            }

            this.jObject.remove( );

            delete this.jObject;
            delete this.m_callListView;
        }
        catch ( e )
        {
            EX_ASSERT_NO_EXCEPTIONS( e, "VM_CallView::destructor( )" );
        }
    }
};
