/*
* ============================================================================
*  Name        : imchatui.js
*  Part of     : XmppImUi / DOM-JScript
*  Description : Implements Chat UI functionality
*                - scrollable tab-pane
*                - managing chats ( append, remove, etc. )
*  Version     : %version: 16.1.3 %
*
*  Copyright c 2008-2008 Nokia.  All rights reserved.
*  This material, including documentation and any related computer
*  programs, is protected by copyright controlled by Nokia.  All
*  rights are reserved.  Copying, including reproducing, storing,
*  adapting or translating, any or all of this material requires the
*  prior written consent of Nokia.  This material also contains
*  confidential information which may not be disclosed to others
*  without the prior written consent of Nokia.
* ============================================================================
*/

// Globals
//
var gCountOfVisibleTabs = 2;  // max count of tabs visible in tabpane
//
var gFastScrollTimer = null;  // timer id isn't null only when fast scroll in progress
//
var gIsHindiOn = false;       //set value to true when mirrored layout is used

var gFocusedElement = null;

var gFocusedBubble = null;

var gCurrentWidth = 0; //current page width. Needed in background image positioning

// ---------------------------------------------------------------------------
// For test purposes
// ---------------------------------------------------------------------------
//
function init()
    {
    makePageVisible();
    }

//----------------------------------------------------------------------------
// Notify s60 client about 'onclick' event
//----------------------------------------------------------------------------
//
function onMouseClick( e )
    {
    eName = nodeAttr( e, "name" );
    if ( !eName )
        {
        eName = nodeAttr( e, "id" );
        }
    doClientRequest( 41, eName );
    }

// ---------------------------------------------------------------------------
// Appends new chat
//
// @param jid   Chat name ( use buddy's jid as unique chat name )
// ---------------------------------------------------------------------------
//
function appendChat( jid, title )
    {
    if ( findTab( jid ) )
        {
        // tab w/ given name already in tabpane - no need to add new one
        return;
        }

    gCurrentWidth = 0;
    updateLayout();

    // create an empty element node
    // without an ID, any attributes, or any content
    var td  = document.createElement("TD");
    var div = document.createElement("DIV");
    var img = document.createElement("IMG");

    // give it an name attribute w/ given jid
    td.setAttribute( "name", jid );
    td.setAttribute( "onclick", "onMouseClick(this);" );

    var username = getUsernameByJid( jid );
    img.setAttribute( "id", "status_icon" + username );

    // Set 'offline' as initial icon
    img.setAttribute( "src", "C:\private\2001a94d\qgn_stat_indi_y_away.png" );

    // create content for the newly created element.
    var div_paragraph = document.createElement("P");
    div_paragraph.setAttribute( "class", "tab_text" );
    var div_content = document.createTextNode( xmlUnescape( title ) );
    div_paragraph.appendChild ( div_content );

    div.setAttribute( "id", "status_bar" ); // TODO: is this "status_bar" used ?
    div.appendChild( img );

    // apply that content to the new element
    //div.appendChild( div_content );
    div.appendChild( div_paragraph );

    // insert div into td element
    td.appendChild( div );

    // insert td into tabpane before 'scroll button'
    var tabpane = document.getElementById( "tabpane" );

    // insert the new element into the DOM before 'right scroll button'/filler
    tabpane.insertBefore( td,
        document.getElementById( "tabpane_whitespace" ) );

    // activate chat
    if ( !document.getElementById( "tabpane_activetab" ) )
        {
        // there are no active tabs in tabpane, so make this chat active
        activateChat( jid );
        }
    else{
        // just re-activate active chat, to update tabpane
        activateChat();
        }
    }

// ---------------------------------------------------------------------------
// Unescapes escaped characters
//
// @param str   Chat title ( Users name, nick or user id )
// ---------------------------------------------------------------------------
//
function xmlUnescape( str )
    {
    return str.replace(/&lt;/g, "<")
        .replace(/&gt;/g, ">")
        .replace(/&quot;/g,"\"")
        .replace(/&amp;/g, "&")
        .replace(/&apos;/g,"'");
    }

// ---------------------------------------------------------------------------
// Remove chat
//
// @param jid  Chat name ( jid used as chatname )
// ---------------------------------------------------------------------------
//
function removeChat( jid )
    {
    // Normalize jid
    jid = getNormalJid( jid );
    deleteMessages(jid);

    var td = findTab( jid );
    if ( !td )
        return;

    // get tabpane element
    var currTab = document.getElementById( "tabpane_activetab" );
    if ( !currTab )
        return;

    var currName = nodeAttr( currTab, "name" );
    if ( currName.toUpperCase() == jid.toUpperCase() )
        {
        // It's active chat. Activate chat w/ next, or previous ( if last tab )
        // jid
        var nextTab = currTab.nextSibling;
        var prevTab = currTab.previousSibling;

        if ( nextTab && nodeAttr( nextTab, "name" ) )
            {
            activateChat( nodeAttr( nextTab, "name" ) );
            }
        else if ( prevTab && nodeAttr( prevTab, "name" ) )
            {
            activateChat( nodeAttr( prevTab, "name" ) );
            }
        else{
            // no more opened chats
            }
        }

    // Remove tab w/ given jid
    var tabpane = document.getElementById( "tabpane" );
    tabpane.removeChild( td );

    // Count of chats was changed - update tabpane
    activateChat();
    }

// ---------------------------------------------------------------------------
// Activate chat
//
// @param jid  Chat name ( jid used as chatname )
// ---------------------------------------------------------------------------
//
function activateChat( jid )
    {
    // Normalize jid
    jid = getNormalJid( jid );

    // If chat jid is not specified, use active chat's jid
    if ( !jid )
        {
        var currTab = document.getElementById( "tabpane_activetab" );
        if ( !currTab )
            return;

        jid = nodeAttr( currTab, "name" );
        if ( !jid )
            return;
        }

    var sheet = document.styleSheets[0];

    // Update css rule to show instant messages for given chat only
    //
    sheet.deleteRule( 1 );
    sheet.insertRule( "DIV[NAME='"+jid+"']" + "{ display:block; }" , 1 );
    sheet.cssRules[1].style.display = "block"; // just for initiate page reflow

    // Activate tab of given chat
    activateTabPane( jid );

    // After chat was activated, scroll window to last message
    scrollPageDown( false );


    // If focused element is not in THIS chat ( display == 'none' ), move focus to
    // last message in THIS chat

    var ancor = null;
    if ( !gFocusedElement )
        ancor = getLatestVisibleAnchor();
    else{
        var style = document.defaultView.getComputedStyle( gFocusedElement, null );
        if ( style && style.display.toLowerCase() == "none" ){
            ancor = getLatestVisibleAnchor();
            }
        }

    if ( ancor ){
        if ( gFocusedElement )
            gFocusedElement.blur();
        ancor.focus();
        }
    }


// ---------------------------------------------------------------------------
// Update status icon for given chat. If there are no chats w/ given jid - do
// nothing.
//
// @param jid     Chat name ( jid used as chatname )
// @param iconUrl Url of presence icon
// ---------------------------------------------------------------------------
//
function updateChatIcon( jid, iconUrl )
    {
    // Normalize jid
    jid = getNormalJid( jid );

    var jidTab = findTab( jid );
    if ( jidTab )
        {
        var statusbar = document.getElementById( "tabpane" );
        if ( statusbar )
            {
            var username   = getUsernameByJid( jid );
            var statusicon = document.getElementById("status_icon" + username);
            statusicon.src =iconUrl;
            activateChat();
            }
        }
    return;
    }

// ---------------------------------------------------------------------------
// Activate tab of given chat. If chat isn't specified, activates already active
// chat ( use to recalulate and update tabs, when count of tabs was changed ).
//
// @param jid  Chat name ( jid used as chatname ) or null
// ---------------------------------------------------------------------------
//
function activateTabPane( jid )
    {
    // Normalize jid
    jid = getNormalJid( jid );

    // If chat jid is not specified, use active chat's jid
    if ( !jid )
        {
        var currTab = document.getElementById( "tabpane_activetab" );
        if ( !currTab )
            return;

        jid = nodeAttr( currTab, "name" );
        if ( !jid )
            return;
        }

    // Move id 'tabpane_activetab' to given jid's tab
    //
    var curTab = document.getElementById( "tabpane_activetab" );
    if ( curTab )
        {
        curTab.setAttribute( "id", "" );
        }

    var jidTab = findTab( jid );
    if ( jidTab )
        {
        jidTab.setAttribute( "id", "tabpane_activetab" );
        }
    updateTabVisibility( jid );
    }

//----------------------------------------------------------------------------
// Updates visibility of tabs depending of active tab defined by parameter
//----------------------------------------------------------------------------
//
function updateTabVisibility( jid )
    {
    // get tabpane element
    var tabpane = document.getElementById( "tabpane" );

    // to get into jid's tab index
    var tabId = -1;
    // to get into leftmost visible tab index
    var mostleftVisibleTabId = -1;

    // go throw all tabpane childnodes, and find index of current tab
    // and index of tab to be activated
    for( var i = 0, counter = 0; i < tabpane.childNodes.length; i++ )
        {
        var td   = tabpane.childNodes[i];
        var name = nodeAttr( td, "name" );
        if ( !name )
            continue;

        if ( name.toUpperCase() == jid.toUpperCase() )
            {
            tabId = counter;
            }

        counter++;
        }

    // total count of tabs
    var countOfTabs = counter;


    // Calculate shift of mostleft visible tab
    //
    if ( tabId < Math.ceil( ( gCountOfVisibleTabs - 1 )/2 ) )
        {
        mostleftVisibleTabId = 0;
        }
    else if ( tabId > ( countOfTabs - 1 ) - Math.floor( ( gCountOfVisibleTabs - 1 )/2 )  )
        {
        mostleftVisibleTabId = countOfTabs - gCountOfVisibleTabs;
        }
    else
        {
        mostleftVisibleTabId = tabId - Math.ceil( ( gCountOfVisibleTabs - 1 )/2 );
        }

    // Update visibility of tabs:
    // make visible only 3 tabs w/ index from mostleftVisibleTabId.
    for( var i = 0, counter = 0; i < tabpane.childNodes.length; i++ )
        {
        var td   = tabpane.childNodes[i];
        var name = nodeAttr( td, "name" );
        if ( !name )
            continue;

        if ( mostleftVisibleTabId <= counter &&
            counter <= mostleftVisibleTabId + ( gCountOfVisibleTabs - 1 ) )
            {
            td.style.display = "";
            }
        else{
            td.style.display = "none";
            }
        counter++;
        }

    // Update visibility of scroll buttons
    //
    var leftScrollBtn = document.getElementById( "tabpane_scroll_lbutton" );
    if ( tabId == 0 )
        {
        //leftScrollBtn.style.display = "none";
    leftScrollBtn.className = "tabpane_scroll_lbutton_dimmed";
        }
    else{
    leftScrollBtn.className = "tabpane_scroll_lbutton";
        //leftScrollBtn.style.display = "";
        }

    var rightScrollBtn = document.getElementById( "tabpane_scroll_rbutton" );
    if ( countOfTabs - 1 == tabId )
        {
//        rightScrollBtn.style.display = "none";
        rightScrollBtn.className = "tabpane_scroll_rbutton_dimmed";
        }
    else{
//        rightScrollBtn.style.display = "";
        rightScrollBtn.className = "tabpane_scroll_rbutton";

        }
    }


// ---------------------------------------------------------------------------
// TODO: comment
// ---------------------------------------------------------------------------
function appendImMessage( chatJid, messageText, delLastMessage )
    {
    var messageId = parseInt(getLastMessageId())+1;
    // Normalize jid
    chatJid = getNormalJid( chatJid );

    // Find and replace email addresses, phone numbers and web-links to html
    // anchor tags
    var messageHtml = replacePlainTextUrlsToHtmlInMsg( messageText );

    // Replace "\n" to "<br>" tags
    messageHtml = messageHtml.replace( /\n/g, "<br>");

    // blur previous bubble before adding the new
//    if ( gPreviousBubbleLink )
//       {
//       gPreviousBubbleLink.blur();
//       }

    //
    var divElem = document.createElement( "div" );
    divElem.setAttribute( "id", messageId );
    divElem.setAttribute( "name", chatJid );
    divElem.innerHTML = messageHtml;

    document.getElementById(0).appendChild( divElem );


    setLastMessageId( messageId );

    // Make the last message selected, if this message in active chat
    // TODO
//    if ( gFocusedElement )
//        gFocusedElement.blur();
//    divElem.focus();


//    var links = document.getElementById( messageId ).getElementsByTagName("a");

    // Be sure that link[0] is valid, otherwise, method throw exception ->
    // client try call the appendImMessage(...) again, that couse 2 IM messages
    // instead 1.
/*
    if ( links && links[0] && links[0].focus )
        {
        links[0].focus();
        gScrollLocation = absolutePosition( links[0] ).y;
        gMousePositionY = gScrollLocation;
        }
*/
    if(delLastMessage)
        {
        deleteLastMessage( chatJid );
        }
    return messageId;
    }

// ---------------------------------------------------------------------------
// TODO: comment
// ---------------------------------------------------------------------------
function updateImMessage( chatJid, messageId, messageText )
    {
    if ( messageId != 0 )
        {
        // Normalize jid
        chatJid = getNormalJid( chatJid );

        // Find and replace email addresses, phone numbers and web-links to html
        // anchor tags
        var messageHtml = replacePlainTextUrlsToHtmlInMsg( messageText );

        // Replace "\n" to "<br>" tags
        messageHtml = messageHtml.replace( /\n/, "<br>");

        var innerHtml = "<div id='" + messageId + "' name='" + chatJid +"'>"
            + messageHtml  + "</div>";

        // TODO: what if the current bubble is selected?

        var element = document.getElementById( messageId );
        element.innerHTML = innerHtml;


        if ( !getAnchorType() )
            {
            // Make updated message selected, otherwise chat ui crashes sometimes
            var links = element.getElementsByTagName("a");

            // Be sure that link[0] is valid, otherwise, method throw exception ->
            // client try call the appendImMessage(...) again, that couse 2 IM messages
            // instead 1.
            if ( links && links[0] && links[0].focus )
                {
                links[0].focus();
                //gScrollLocation = absolutePosition( links[0] ).y;
                //gMousePositionY = gScrollLocation;
                }
            }
        }
    }

// ---------------------------------------------------------------------------
// TODO: comment
// ---------------------------------------------------------------------------
function getLastMessageId()
    {
    var lastId = document.getElementById( 'lastMessageId' ).title;
    return isNaN( lastId ) ? 0 : lastId;
    }

// ---------------------------------------------------------------------------
// TODO: comment
// ---------------------------------------------------------------------------
function setLastMessageId( value )
    {
    return document.getElementById( 'lastMessageId' ).title = value;
    }

// ---------------------------------------------------------------------------
// TODO: comment
// ---------------------------------------------------------------------------
function scrollPageDown( moveFocusDown )
    {
    //
    window.scrollTo( 0, document.body.scrollHeight );
    /*
    if ( moveFocusDown ){
        // Scroll page down and move focus down
        var anchor = getLatestVisibleAnchor();
        fastScrollDownToAnchor( anchor );
        }
    else{
        window.scrollTo( 0, document.body.scrollHeight );
        }
    */
    }

// ---------------------------------------------------------------------------
// Replaces input field's title with proper string and icon.
// ---------------------------------------------------------------------------
function setInputFieldTitle( value )
    {
    var elem = document.getElementById( 'inputFieldTitle' );
    if ( elem )
        {
        elem.innerHTML = value;
        }
    }


//////////////////////////////////////////////////////////////////////////////
//
// U T I L I T I E S
//
//////////////////////////////////////////////////////////////////////////////


// ---------------------------------------------------------------------------
// Get normalized jid: ( all letters in lower case, no resource part )
//
// @param jid  Jid
// @return Normalized Jid
// ---------------------------------------------------------------------------
//
function getNormalJid( jid ){
    if ( jid )
        return jid.split('/')[0].toLowerCase();
}

// ---------------------------------------------------------------------------
// Get username part of jid
//
// @param jid  Chat name ( jid used as chatname )
// @return username as text
// ---------------------------------------------------------------------------
//
function getUsernameByJid( jid )
    {
    // Normalize jid
    jid = getNormalJid( jid );

    var pos = jid.indexOf( '@' );
    if ( pos < 0 )
        {
        return jid;
        }
    return jid.substring( 0, pos );
    }

// ---------------------------------------------------------------------------
// Find tab by chat name
//
// @param jid  Chat name ( jid used as chatname )
// @return HTMLElement or null
// ---------------------------------------------------------------------------
//
function findTab( jid )
    {
    // Normalize jid
    jid = getNormalJid( jid );

    // get tabpane element
    var tabpane = document.getElementById( "tabpane" );

    // go throw all tabpane childnodes, and find node w/ name of jid
    for( var i = 0; i < tabpane.childNodes.length; i++ )
        {
        var td   = tabpane.childNodes[i];
        var name = nodeAttr( td, "name" );
        if ( name && name.toUpperCase() == jid.toUpperCase() )
            {
            return td;
            }
        }
    return null;
    }


// ---------------------------------------------------------------------------
// Get value of specified attribute of given node
//
// @return Attribute's value or null
// ---------------------------------------------------------------------------
//
function nodeAttr( node, attr )
    {
    if( !node || !node.attributes )
        return null;

    for ( var i = 0; i < node.attributes.length; i++ )
        {
        if ( node.attributes[i].name.toUpperCase() == attr.toUpperCase() )
            {
            return node.attributes[i].value;
            }
        }
    return null;
    }

// ---------------------------------------------------------------------------
//
// Change style of bubble, if  gBubbleFocusVisible is set.
// Also set the gCurrentBubbleElement which content may be returned with getCurrentBubbleText()
// and copied to Symbian clipboard.
//
// ---------------------------------------------------------------------------
//
function onFocusBubble( element )
    {
    onAnchorFocus( element );

    // If fast scroll timer is active, don't apply styles to selected bubble
    // to improve performance.
    //if ( gFastScrollTimer )
    //    return;

    var bubble = element.parentNode.parentNode;

    // TODO add className "selected" here
    var classNames = bubble.className.split(' ');
    for( var i=0; i<classNames.length; i++ )
        if( classNames[i] == 'selected' )
            return;

    bubble.className += ' selected';
    }

// ---------------------------------------------------------------------------
//
// Change bubble style back
//
// ---------------------------------------------------------------------------
//
function onBlurBubble( element )
    {
    // If fast scroll timer is active, don't apply styles to selected bubble
    // to improve performance.
    //if ( gFastScrollTimer )
    //    return;

    var bubble = element.parentNode.parentNode;

    // TODO remove className "selected" here
    var classNames = bubble.className.split(' ');
    for( var i=0; i<classNames.length; i++ )
        if( classNames[i] == 'selected' )
            classNames[i] = '';

    bubble.className = classNames.join(' ');
    gFocusedElement = null;
    }

// ---------------------------------------------------------------------------
//
// Show bubble selection. The last link is focused, if the last message
// has links, the last link is focused.
//
// ---------------------------------------------------------------------------
//
function setBubbleFocusVisible()
    {
    // Reset fast scroll timeout
    // fastScrollDownToAnchor( null );
/*
    if ( gFocusedElement )
        gFocusedElement.blur();
*/
    var ancor = getLatestVisibleAnchor();
    if ( ancor )
        {
        ancor.blur();
        ancor.focus();
        }
    }

// ---------------------------------------------------------------------------
//
// Hide bubble selection
//
// ---------------------------------------------------------------------------
//
function setBubbleFocusInvisible()
    {
/*
    if ( gFocusedElement )
        gFocusedElement.blur();

    // Scroll page down and move focus down
    var anchor = getLatestVisibleAnchor();
    fastScrollDownToAnchor( anchor );
*/
    }


// ---------------------------------------------------------------------------
// ---------------------------------------------------------------------------
//
function fastScrollDownToAnchor( anchor ){
    if ( anchor && gFocusedElement && gFocusedElement != anchor ){
        //
        gFastScrollTimer = window.setTimeout( function(){ fastScrollDownToAnchor(anchor) }, 25 );
        //gFastScrollMode = true;
        simulateKeyEvent( 0xf80a ); // EKeyDownArrow
        }
    else{
        if ( gFastScrollTimer )
            window.clearTimeout( gFastScrollTimer );
        //gFastScrollMode = false;
        gFastScrollTimer = null;
        }
    }


// ---------------------------------------------------------------------------
// Get the text of currently selected bubble, or link ( anchor )
//
// @return current bubble text;
// ---------------------------------------------------------------------------
//
function getCurrentBubbleText()
    {
//    var anchor = gCurrentBubbleElement ?
//        gCurrentBubbleElement : getFocusedVisibleAnchor();
    var result = "";
    var anchor = getFocusedVisibleAnchor();
    if ( anchor )
        {
        if ( anchor.name.toLowerCase() == "copy" )
            // This is bubble anchor, get innerText inside whole bubble
            result = getNodeInnerText( anchor.parentNode.parentNode );
        else
            result = getNodeInnerText( anchor );
        }
        else
        {
        //On some targets getFocusedVisibleAnchor() returns null anchor.
         if(gFocusedBubble)
            {
             anchor = gFocusedBubble;
             if ( anchor )
                 {
                 if ( anchor.name.toLowerCase() == "copy" )
                    // This is bubble anchor, get innerText inside whole bubble
                    result = getNodeInnerText( anchor.parentNode.parentNode );
                 else
                    result = getNodeInnerText( anchor );
                 }
             }
         }
    return result;
    }

// ---------------------------------------------------------------------------
// Get the name of the focused component
//
// @return Name of the focused item;
// ---------------------------------------------------------------------------
//
function getAnchorType()
    {
    return getFocusedVisibleAnchor();
    }

// ---------------------------------------------------------------------------
// Check if page can be scrolled down. Page cannot be scrolled down if:
// - position of window is bottom of document.
// - last link on page is focused
//
// @return true if bottom of page is not reached, false otherwise.
// ---------------------------------------------------------------------------
//
function canScrollPageDown()
    {
    //Check if screen can be scrolled down
    if ( window.innerHeight + window.pageYOffset < document.body.scrollHeight )
        {
        // Position of window not reached bottom of document, so we can do scroll
        // page down
        return true;
        }
    if(!gFocusedElement)
        return false;


    var lastAnchor = getLatestVisibleAnchor();
    if ( gFocusedElement == lastAnchor )
        return false;

    return true;
    }

//----------------------------------------------------------------------------
// Get absolute HTMLElement position in document. Used by onMouseMove( event )
// see below.
//----------------------------------------------------------------------------
//
function absolutePosition( e )
    {
    var x = y = 0;
    while( e )
        {
        x += e.offsetLeft;
        y += e.offsetTop;
        e  = e.offsetParent;
        }
    return {x:x, y:y};
    }

//----------------------------------------------------------------------------
// The same as DOMHTMLElement's innerText property, but <img> elements
// handled in different way:
// - <img> elements are replaced with value of 'alt' parameter.
//----------------------------------------------------------------------------
//
function getNodeInnerText( node )
    {
    var ret = "";
    var text = "";
    for( var i = 0; i < node.childNodes.length; i++ )
        {
        var childNode = node.childNodes[i];

        if ( 1 == childNode.nodeType /* node is element */)
            {
            ret += getNodeInnerText( childNode );
            }
        if ( 3 == childNode.nodeType /* node is text */)
            {
            text = childNode.nodeValue;
            // Deletes leading and trailing whitespace characters from the string.
            text = text.replace(/^\s+/, '').replace(/\s+$/, '');

            // Append space before, if text is not empty.
            if ( text.length )
                {
                ret += " " + text;
                }
            }
        if ( childNode.tagName == "IMG" && childNode.alt )
            {
            text = childNode.alt;

            // Append space before, if text is not empty.
            if ( text.length )
                {
                ret += " " + text;
                }
            }
        }
    return ret;
    }

//----------------------------------------------------------------------------
// focuses next visible anchor. If no achors are visible below, then just scroll down
//----------------------------------------------------------------------------
//
function focusNextVisibleAnchor()
    {
    }

//----------------------------------------------------------------------------
// focuses previous visible anchor. If no achors are visible below, then just scroll up
//----------------------------------------------------------------------------
//
function focusPreviousVisibleAnchor()
    {
    }

//----------------------------------------------------------------------------
// Return focused anchor i.e. visible anchor under 'cursor'
//----------------------------------------------------------------------------
//
function getFocusedVisibleAnchor()
    {
    if ( gFocusedElement )
        {
        var style  = document.defaultView.getComputedStyle( gFocusedElement, null );
        if ( style && style.display.toLowerCase() != "none" )
            {
            return gFocusedElement;
            }
        }
    return null;
    }

//----------------------------------------------------------------------------
// Return last visible anchor in document
//----------------------------------------------------------------------------
//
function getLatestVisibleAnchor()
    {
    for ( var i = document.links.length-1; i >=0 ; i-- )
        {
        var anchor = document.links[ i ];
        var style  = document.defaultView.getComputedStyle( anchor, null );
        if ( style && style.display.toLowerCase() != "none" )
            {
            return anchor;
            }
        }
    return null;
    }

//----------------------------------------------------------------------------
// Return count of visible anchors in document
//----------------------------------------------------------------------------
//
function getCountOfVisibleAnchors()
    {
    var count = 0;
    for ( var i = document.links.length-1; i >=0 ; i-- )
        {
        var anchor = document.links[ i ];
        var style  = document.defaultView.getComputedStyle( anchor, null );
        if ( style && style.display.toLowerCase() != "none" )
            {
            count++;
            }
        }
    return count;
    }


//----------------------------------------------------------------------------
// Utility: Replace all found email addresses, phone numbers and web-links in
// given plain text to html anchors tags with 'href' attribute.
// This operation looks for the start of message text and operates on every
// single word at a time.
//----------------------------------------------------------------------------
//

function replacePlainTextUrlsToHtmlInMsg( plainText )
    {
    var htmlText = plainText;
    var msgStartPattern = "</span><br><span>"; // Has to match outgoing_- and incoming_content.html!
    var pos = htmlText.search( msgStartPattern );

    if ( pos == -1 )
        {
        htmlText = replacePlainTextUrlsToHtml( htmlText );
        }
    else
        {
        var i;
        var msgText = htmlText.slice( pos + msgStartPattern.length );
        var msgBody = htmlText.slice( 0, htmlText.length - msgText.length );
        var singleWords = new Array();
        singleWords = msgText.split( " " );

        for ( i in singleWords )
            {
            singleWords[i] = replacePlainTextUrlsToHtml( singleWords[i] );
            }

        htmlText = msgBody + singleWords.join( " " );
        }

    return htmlText;
    }


//----------------------------------------------------------------------------
// Utility: Replace all found email addresses, phone numbers and web-links in
// given plain text to html anchors tags with 'href' attribute.
//----------------------------------------------------------------------------
//
function replacePlainTextUrlsToHtml( plainText )
    {
    var htmlText = plainText;

    // Replace all found email addresses in given text string to html anchors
    // tags with 'href' attribute
    //
    var regexpEmails = /(^|>|\s)(\b[\w\.-]+(\w?)@[\w\.]+\.+[a-z]{2,4})(\b|\W|$)/gi;
    htmlText = htmlText.replace( regexpEmails, "$1 <a name='mailto:' href='mailto:$2' onFocus = 'onAnchorFocus(this)' > $2 </a><br> $3" );

    // Check if already replaced
    if ( regexpEmails.test( htmlText ) )
        return htmlText;

    var regexpProtocolledLinks = /(^|>|\s)(http|https|ftp|rtsp)(:\/\/[\S]*[^\.!\?,;])(\b|\W|$)/gi; //
    htmlText = htmlText.replace( regexpProtocolledLinks, "$1 <a name='href:' href='$2$3'  onFocus = 'onAnchorFocus(this)'> $2$3 </a><br> " );

    // Check if already replaced
    if ( regexpProtocolledLinks.test( htmlText ) )
        return htmlText;

    // Replace all found web links in given text string to html anchors
    // tags with 'href' attribute
    //
    var regexpWebLinks  = /(^|>|\s)(http(s?):\/\/|\b)(\w[\-\w]*(?:\.[\-\w]+)*\.[a-z]{2,4}(\/[^\s]*|\b))(\b|\W|$)/gi;
    htmlText = htmlText.replace( regexpWebLinks, "$1 <a name='href:' href='http$3://$4'  onFocus = 'onAnchorFocus(this)'> $2$4 </a><br> $6" );

    // Check if already replaced
    if ( regexpWebLinks.test( htmlText ) )
        return htmlText;
        
    // Replace all found streaming links in given text string to html anchors
    // tags with 'href' attribute
    //
    var regexpStreamingLinks  = /(^|>|\s)(rtsp:\/\/|\b)(\w[\-\w]*(?:\.[\-\w]+)*\.[0-9a-z]{1,3}(\/[^\s]*|\b))(\b|\W|$)/gi;
    htmlText = htmlText.replace( regexpStreamingLinks, "$1 <a name='href:' href='rtsp://$3'  onFocus = 'onAnchorFocus(this)'> $2$3 </a><br> " );

    // Check if already replaced
    if ( regexpStreamingLinks.test( htmlText ) )
        return htmlText;

    // Replace all found phone numbers in given text string to html anchors
    // tags with 'href' attribute
    //
    var regexpPhoneNumbers = /(^|>|\s)(\+?[\d]{3,20})(\b|\W|$)/gi;

    htmlText = htmlText.replace( regexpPhoneNumbers, "$1 <a name='tel:' href='tel:$2' onFocus = 'onAnchorFocus(this)' > $2 </a><br> $3" );

    return htmlText;
    }

//-----------------------------------------------------------------------------
// Focus bubble with touch.
//-----------------------------------------------------------------------------
//
function focusBubbleElement( element )
    {
    if ( gFocusedElement )
        {
        gFocusedElement.blur();
        }
    element.focus();
    gFocusedElement = element;
    gFocusedBubble = element;
    var anchorRectTopLeftY = absolutePosition( element ).y;
    //Scroll so that anchor is gLinkFocusOffset pixels from the top of screen
    gScrollLocation = anchorRectTopLeftY;
    window.scrollTo( 0, gScrollLocation - gLinkFocusOffset );
    }

//----------------------------------------------------------------------------
// Switch on hindi
//----------------------------------------------------------------------------
//
function setHindi()
    {
    gIsHindiOn = true;
    e = document.getElementById( "chatui");
    if( e )
        {
        e.setAttribute( "class", "rl" );
        e.setAttribute( "dir", "rtl" );
        }
    m = document.getElementById( "main_style" );
    if( m )
       {
        m.setAttribute( "href", "C:\\private\\2001a94d\\main_hebrew.css" );
       }
    v = document.getElementById( "view_style" );
    if( v )
       {
        v.setAttribute( "href", "C:\\private\\2001a94d\\chatui_hebrew.css" );
       }
    tp = document.getElementById( "table_tabs" );
    if( tp )
       {
        tp.setAttribute( "dir", "rtl" );
       }
    }

//----------------------------------------------------------------------------
//----------------------------------------------------------------------------
//
function onAnchorFocus( anchor )
    {
    gFocusedElement = anchor;
    onMouseClick( anchor );

    if(gFocusedElement && focusedItemOutOfScreen())
        {
        // y offset from window top-left corner to anchor
        var offsetY = absolutePosition( anchor ).y - window.innerHeight/2;

        if( offsetY >= 0 && offsetY <= document.body.scrollHeight )
            {
            window.scrollTo( 0, offsetY );
            }
        else if( offsetY < 0 )
            {
            window.scrollTo( 0, 0 );
            }
        else //offsetY > scrollHeight
            {
            window.scrollTo( 0, window.body.scrollHeight );
            }
        }
    }
//----------------------------------------------------------------------------
// Check page layout and tune the background bubble style
//----------------------------------------------------------------------------
//
function updateLayout()
    {
    var portraitTabs = 2;
    var landscapeTabs = 3;
    //determine screen size
    if ( window.innerWidth > 320 || window.innerHeight > 320 )
        {
        //touch screen resolution - use tabcounts 2/3
        portraitTabs = 2;
        landscapeTabs = 3;
        }
    else
        {
        //smaller resolution - use tabcounts 3/4
        portraitTabs = 3;
        landscapeTabs = 4;
        }

    //if layout has changed, do further checking
    if ( window.innerWidth != gCurrentWidth ) {
        gCurrentWidth = window.innerWidth;
        e = document.getElementById( "bgr_bubbles" );
        //-10 needed to tackle athlon layout of x:240/y:248 (QVGA portrait)
        if ( ( window.innerWidth - 10 ) > window.innerHeight )
            {
            e.className = "background_bubbles_landscape";
            gCountOfVisibleTabs = landscapeTabs;
            }
        else
            {
            e.className = "background_bubbles_portrait";
            gCountOfVisibleTabs = portraitTabs;
            }
        var currTab = document.getElementById( "tabpane_activetab" );
        if ( !currTab )
            return;

        jid = nodeAttr( currTab, "name" );
        if ( !jid )
            return;
        updateTabVisibility( jid );
        }
    }


function focusedItemOutOfScreen()
    {
    var anchorRectTopLeftY = absolutePosition( gFocusedElement ).y;
    if( anchorRectTopLeftY > window.pageYOffset + window.innerHeight - 60 ||
        anchorRectTopLeftY < window.pageYOffset + 47 )//window includes also tabs and "Write message" label, this causes that magic numbers.
        {
        return true;
        }
    else
        {
        return false;
        }
    }

function scrollToFocusedItem()
    {
    if(focusedItemOutOfScreen())
        {
        var anchorRectTopLeftY = absolutePosition( gFocusedElement ).y;
        //Scroll so that anchor is gLinkFocusOffset pixels from the top of screen
        gScrollLocation = anchorRectTopLeftY;
        window.scrollTo( 0, gScrollLocation - window.innerHeight + 70 );
        }
    }

function deleteMessages( jid )
    {
    jid = getNormalJid(jid);
    var elementsCount = document.getElementsByName(jid).length;
    var messageParent = document.getElementById(0);
    var i;
    for(i = elementsCount - 1; i >= 0; i--)
        {
        var element = document.getElementsByName(jid)[i]
        if(element.parentNode == messageParent)
            messageParent.removeChild(element);
        }
    }

function deleteLastMessage( chatJid )
    {
        var elements = document.getElementsByName(chatJid);
        if(elements.length > 1)
            {
            document.getElementById(0).removeChild(elements[1]);
            }
    }
