Exchanging Messages

In this final installment of the Windows Live Messenger Library tutorial, you will modify the application so users can conduct a conversation with any member of their contact lists.

Note   In this tutorial, you will edit the file Default.htm from the previous sections. The other files in the sample, Channel.htm, Privacy.htm, and Default.css, doenot change.

Overview

This sample carries over the functionality from the previous segment (Managing Presence), and adds the following new functionality:

  • Creating an instance of Microsoft.Live.Messenger.Conversation.
  • Subscribing to Microsoft.Live.Messenger.MessageReceivedEventHandler.
  • Creating new functions to send and receive messages.

The following Windows Live Messenger Library classes and members are used:

  • Microsoft.Live.Messenger.UI.SignInControl
  • Microsoft.Live.Messenger.User
  • Microsoft.Live.Messenger.PresenceStatus
  • Microsoft.Live.Messenger.Conversation

Modifying the Project Files

In this step, you will update Default.htm with a <div> element (sendMessage) that contains HTML for a conversation window, text input box, and Send button.

To Add Conversation Management Code

  1. Using the IDE or text editor of your choice, open the version of Default.htm created in the previous step (Managing Presence) for editing.

  2. Paste the following lines immediately after the line that reads contactItem.appendChild(personalMessage);. This code attaches an event handler to the contact as it is created.

    /* Contact click event handler. */
    contactItem.attachEvent("onclick", createOnContactClick(contact));
    
  3. Immediately after displayContent, paste the following function, which is called when a user clicks on a contact's name in the list. This function creates a handler function for the specific contact to respond to the click event. If the contact is online when the user clicks the contact, then it creates a new conversation in the user's conversation collection. Otherwise, it does nothing since the contact is offline.

    function createOnContactClick(contact)
    {
        return function()
        {
            var address = contact.get_currentAddress();
            if (address.get_isOnline())
            {
                return _user.get_conversations().create(address);
            }
            else
            {
                return null;
            }
        }
    }
    
  4. Locate signInCompleted, and add the following line to the end of the if statement (immediately after _ _user.get_offlineContacts().add_collectionChanged(offlineContacts_CollectionChanged);). This assigns a conversation_collectionChanged event handler, which is called each time the conversation collection changes (i.e. a conversation ends, or a new conversation is started).

    _user.get_conversations().add_propertyChanged(conversations_PropertyChanged);
    
  5. Paste the following function immediately after setPersonalMessage. The conversation_collectionChanged event handler function is called by the Messenger Library when the set of conversations changes.

    /* Called by the Messenger Library when the set of conversations changes. */
    function conversations_PropertyChanged(sender,e) 
    {
       displayConversations();
    }
    
  6. Next, paste the following three functions immediately after the conversations_PropertyChanged function. These functions handle sending messages, receiving messages, and displaying message text each time a message is sent or received.

    /* Send a text message in the current conversation. */
    function sendMsg()
    {
        var txtMessage = document.getElementById("txtMessage");
        var messageText = txtMessage.value;
        var message = new Microsoft.Live.Messenger.TextMessage(messageText);
    
        if (_user)
        {
            _conv.sendMessage(message, null);
        }
    
        displayMsg(message);
        txtMessage.value = "";
        txtMessage.focus();
    }
    
    /* Called by the Messenger Library when a new message is received. */
    function recvMsg(sender, e) 
    {
        switch (e.get_message().get_type())
        {
            case Microsoft.Live.Messenger.MessageType.textMessage:
                var message = e.get_message();
                displayMsg(message);
                break;
            default:
                break;
        }
    }
    
    /* Show text for a received message in the conversation window. */
    function displayMsg(message)
    {
        var elMsg = message.createTextElement();
        var txtConv = document.getElementById("txtConv");
    
        var sender = message.get_sender();
        var dispName = sender.get_presence().get_displayName() || sender.get_address();
    
        txtConv.appendChild(document.createTextNode(dispName + " says: "));
        txtConv.appendChild(elMsg);
        txtConv.appendChild(document.createElement("br"));
    }
    
  7. Next, paste in the following code for displayConversations(). This is a helper function that refreshes the list of active conversations.

    /* Loop through all active conversations and display the names of
    the conversation participants. */
    function displayConversations()
    {
        removeChildrenFromNode("divConversations");
    
        for (var i = 0; i < _user.get_conversations().get_count(); i++)
        {
            var c = _user.get_conversations().get_item(i);
    
            if (c.get_closed())
            {
                continue;
            }
    
            convLink(c, i);
        }
    }
    
  8. After displayConversations, paste the convLink function. This function generates a link for each conversation. The user can click to change the active conversation, or close a conversation.

    /* Generate a link for a conversation. */
    function convLink(c, item)
    {
        var roster = c.get_roster();
        var names = [];
    
        for (var i = 0; i < roster.get_count(); i++)
        {
            var address = roster.get_item(i);
    
            var dispName = address.get_presence().get_displayName() || address.get_address();
    
            names.push(dispName);
        }
    
        var allNames = names.join(", ");
        var nameList = document.createElement("span");
        nameList.appendChild(document.createTextNode(allNames + " "));
    
        if (c == _conv)
        {
            nameList.setAttribute("style", "font-weight:bold;");
        }
    
        // Create a hyperlink to switch to the active conversation.
        var hr = document.createElement("hr");
        var linkSwitch = document.createElement("a");
        var linkSwitchHref = document.createAttribute("href");
        linkSwitchHref.nodeValue="javascript:switchConv(" + item + ")";
        linkSwitch.setAttributeNode(linkSwitchHref);
        linkSwitch.appendChild(nameList);
    
        // Create a hyperlink to close the conversation.
        var hr = document.createElement("hr");
        var linkClose = document.createElement("a");
        var linkCloseHref = document.createAttribute("href");
        linkCloseHref.nodeValue="javascript:closeConv(" + item + ")";
        linkClose.setAttributeNode(linkCloseHref);
        linkClose.appendChild(document.createTextNode("[close]"));
    
        // Add the "switch" and "close" hyperlinks to the conversation list.
        document.getElementById("divConversations").appendChild(linkSwitch);
        document.getElementById("divConversations").appendChild(document.createTextNode(" "));
        document.getElementById("divConversations").appendChild(linkClose);
        document.getElementById("divConversations").appendChild(hr);
    }
    
  9. Next paste in the switchConv function, which switches the active conversation to the one selected by the user.

    /* Switch the active conversation. */
    function switchConv(id)
    {
        var c = _user.get_conversations().get_item(id);
        if (c)
        {
            if (_conv)
            {
                _conv.remove_messageReceived(_convSink);
            }
            _convSink = Delegate.create(null, recvMsg);
            _conv = c;
            _conv.add_messageReceived(_convSink);
    
            removeChildrenFromNode("txtConv");
            removeChildrenFromNode("divConversations");
    
            /* Display all messages from the conversation history. */
            var hist = c.get_history();
            for (var i = 0; i < hist.get_count(); i++)
            {
                displayMsg(hist.get_item(i));
            }
    
           document.getElementById("btnSend").disabled = false;
        }
        displayConversations();
        var txtMessage = document.getElementById("txtMessage");
        txtMessage.focus();
    }
    
  10. After switchConv, paste the closeConv function, which closes the selected conversation.

    /* Close the current conversation. */
    function closeConv(id)
    {
        var conversations = _user.get_conversations();
    
        var conversation = conversations.get_item(id);
    
        conversation.close();
    
        removeChildrenFromNode("divConversations");
        displayConversations();
    
        if (conversations.get_count() == 0)
        {
            document.getElementById("btnSend").disabled = true;
        }
    }
    
  11. Finally, in the user_Presence_PropertyChanged function, add the following statement after the alert statement. This disables sending messages when the user is offline.

        document.getElementById("btnSend").disabled = true;
    
  12. At the very end of the script, paste the following declarations:

    _user = null;
    _signin = null;
    _conv = null;
    _convSink = null;
    
  13. Replace the entire HTML body with the following:

    <body onload="scriptMain()">
        <div id="msgr">
            <table>
                <tr>
                    <td style="height:150px">
                        <div id="signinframe">
                        </div>
                    </td>
                </tr>
                <tr>
                    <td>
                        <div id="userInfo">
                        </div>
                    </td>
                </tr>
                <tr>
                    <td>
                        <div id="setUserStatus">
                            <span><b>Change Your Status:</b></span>
                            <select id="selectStatus" onchange="selectStatusChanged()">
                                <option>Appear Offline</option>
                                <option>Away</option>
                                <option>Be Right Back</option>
                                <option>Busy</option>
                                <option>Idle</option>
                                <option>In a Call</option>
                                <option selected>Online</option>
                                <option>Out to Lunch</option>
                            </select>
                        </div>
                    </td>
                </tr>
                <tr>
                    <td>
                        <div id="setPersonalMessage">
                            <span><b>Personal Message: </b></span>
                            <input id="personalMessage" type="text" />
                            <input onclick="setPersonalMessage()" id="btnSetPersonalMessage" type="button" value="Set" />
                        </div>
                    </td>
                </tr>
                <tr>
                    <td>
                        <div id="sendMessage">
                            <hr />
                            <span><b>Send a Message:</b></span>
                            <p id="contactLabel">
                            </p>
                            <p id="msgLastRecv">
                            </p>
                            <div id="txtConv">
                            </div>
                            <br />
                            <input id="txtMessage" type="text" /><br />
                            <input onclick="sendMsg()" id="btnSend" type="submit" value="Send Message" disabled="disabled" />
                        </div>
                    </td>
                </tr>
                <tr>
                    <td>
                        <p id="convHeader">
                            <b>Active Conversations (click to resume):</b></p>
                        <div id="divConversations">
                        </div>
                    </td>
                </tr>
                <tr>
                    <td>
                        <b>Online:</b><br />
                        <div id="divOnlineContacts">
                        </div>
                        <br />
                        <b>Offline:</b><br />
                        <div id="divOfflineContacts">
                        </div>
                    </td>
                </tr>
            </table>
    
    
        </div>
    </body>
    

To Run the Sample

  1. Upload the project files to your Web server.

  2. Log in to Messenger client with the Windows Live ID account that you have designated as the receiver.

  3. Navigate to Default.htm on your Web site.

  4. In the sample application, log in with the Windows Live ID account that you have designated as the sender. If the sender is not a member of the receiver's contact list, the Messenger client will prompt you to add the sender to the list. When the sender is added to the list, you will be able to see interaction between the client and the Web application.

  5. Click a contact in the list to begin a conversation. Now you will be able to send messages between the sender and the receiver.

Default.htm (completed)

The following code example shows the completed version of Default.htm.

<html xmlns="https://www.w3.org/1999/xhtml" >
<head>
<title>Windows Live Messenger Library</title>
<link rel="stylesheet" href="Default.css" type="text/css" />   
<script src="https://www.wlmessenger.net/api/2.5/messenger.js" type="text/javascript" 
    language="javascript"></script>
<script type="text/javascript" language="javascript">
function scriptMain() 
{
    var hostUrl = window.location.href;
    var index = hostUrl.lastIndexOf("/");
    hostUrl = hostUrl.substring(0, index);

    var privUrl = hostUrl + "/Privacy.htm";
    var chanUrl = hostUrl + "/Channel.htm";
    _signin = new Microsoft.Live.Messenger.UI.SignInControl('signinframe', privUrl, chanUrl, 'en-US');
    _signin.add_authenticationCompleted(authenticationCompleted);
}

function authenticationCompleted(sender, e) 
{
    _user = new Microsoft.Live.Messenger.User(e.get_identity());
    _user.add_signInCompleted(signInCompleted);
    _user.signIn(null);
}

function signInCompleted(sender, e)
{
    if (e.get_resultCode() === Microsoft.Live.Messenger.SignInResultCode.success)
    {               
        _user.get_presence().add_propertyChanged(user_Presence_PropertyChanged);
        displayUserInfo();
                
        // Set event handlers to online and offline contacts collections.
        _user.get_onlineContacts().add_collectionChanged(onlineContacts_CollectionChanged);
        _user.get_offlineContacts().add_collectionChanged(offlineContacts_CollectionChanged);
        
        _user.get_conversations().add_propertyChanged(conversations_PropertyChanged);
    }
}        

/* Contact handlers */
function onlineContacts_CollectionChanged()
{
    removeChildrenFromNode("divOnlineContacts");
    var onlineContactList = document.getElementById("divOnlineContacts");
            
    for (var i = 0; i < _user.get_onlineContacts().get_count(); i++)
    {
        var contactItem = createContactItem(_user.get_onlineContacts().get_item(i));
        onlineContactList.appendChild(contactItem);
    }
}
        
/* Refresh the offline contact list for the current user. */
function offlineContacts_CollectionChanged()
{
    removeChildrenFromNode("divOfflineContacts");
    var offlineContactList = document.getElementById("divOfflineContacts");
            
    for (var i = 0; i < _user.get_offlineContacts().get_count(); i++)
    {
        var contactItem = createContactItem(_user.get_offlineContacts().get_item(i));                
        offlineContactList.appendChild(contactItem);
    }
}        
        
function createContactItem(contact)
{
    /* Create the contact DOM elements. */
    var contactItem = document.createElement("div");
    contactItem.setAttribute("className", "ContactItem");
    var displayName = document.createElement("span");
    var status = document.createElement("span");
    var personalMessage = document.createElement("span");
    contactItem.appendChild(displayName);
    contactItem.appendChild(status);
    contactItem.appendChild(personalMessage);
        
    /* Contact click event handler. */
    contactItem.attachEvent("onclick", createOnContactClick(contact));

    /* Display contact presence. */
    displayContent(contact.get_displayName(), displayName);
    displayStatus(contact.get_presence().get_status(), status);

    /* Add the contact presence update event handler. */
    addOnContactPresenceUpdated(
        contact,
        displayName,
        status,
        personalMessage);
                
    return contactItem;        
}     
        
function addOnContactPresenceUpdated(
    contact,
    displayNameElement,
    statusElement,
    personalMessageElement)
{
    contact.add_propertyChanged(
        function(sender, e)
        {
            switch (e.get_propertyName())
            {
                case "DisplayName":
                    displayContent(contact.get_displayName(),  displayNameElement);
                    break;
            }
        });
                
    contact.get_presence().add_propertyChanged(
        function(sender, e)
        {
            var presence = contact.get_presence();
                    
            switch (e.get_propertyName())
            {
                case "Status":
                    displayStatus(presence.get_status(), statusElement);
                    break;
                case "PersonalMessage":
                    displayContent(presence.get_personalMessage(), personalMessageElement);
                    break;
            }
        });
}

function displayStatus(status, element)
{
    switch (status)
    {
        case Microsoft.Live.Messenger.PresenceStatus.offline: 
            element.innerText = " (Offline) ";
            break;
        case Microsoft.Live.Messenger.PresenceStatus.away: 
            element.innerText = " (Away) ";
            break;
        case Microsoft.Live.Messenger.PresenceStatus.busy:
            element.innerText = " (Busy) ";
            break;
        default: 
            element.innerText = " (Online) "; 
            break;  
    }
}

function displayContent(content, element)
{
    replaceChild(element, Microsoft.Live.Messenger.MessengerUtility.emoticonEncode(content));
}

function createOnContactClick(contact)
{
    return function()
    {
        var address = contact.get_currentAddress();
        if (address.get_isOnline())
        {
            return _user.get_conversations().create(address);
        }
        else
        {
            return null;
        }
    }
}

/* End contact handlers */       

/* Called by the Windows Live Messenger Library when status changes of the user occur. */
function user_Presence_PropertyChanged(sender, e)
{
    displayUserInfo();
    
    if (sender.get_status() == Microsoft.Live.Messenger.PresenceStatus.offline)
    {
        alert("Your presence has been set to offline.");
        document.getElementById("btnSend").disabled = true;
    }
}
        
/* Called when the user changes the status dropdown. */
function selectStatusChanged()
{
    var selectStatus = document.getElementById("selectStatus");
    var presenceStatus = [ Microsoft.Live.Messenger.PresenceStatus.appearOffline, Microsoft.Live.Messenger.PresenceStatus.away, Microsoft.Live.Messenger.PresenceStatus.beRightBack, Microsoft.Live.Messenger.PresenceStatus.busy, Microsoft.Live.Messenger.PresenceStatus.idle, Microsoft.Live.Messenger.PresenceStatus.inACall, Microsoft.Live.Messenger.PresenceStatus.online, Microsoft.Live.Messenger.PresenceStatus.outToLunch ];
    if (_user.get_presence().get_status() != Microsoft.Live.Messenger.PresenceStatus.offline)
    {
        _user.get_presence().set_status(presenceStatus[selectStatus.selectedIndex]);
    }
}
        
/* Populate the user information string. */
function displayUserInfo()
{
    var userInfo = document.getElementById("userInfo");
    var userAddress = _user.get_address().get_address();
    var userDisplayName = _user.get_presence().get_displayName();
    var userPersonalMessage = _user.get_presence().get_personalMessage();
    var userStatus = Enum.toString(Microsoft.Live.Messenger.PresenceStatus, _user.get_presence().get_status());
    var statusLine = document.createElement("p");
    removeChildrenFromNode("userInfo");
            
    if (userDisplayName)
    {
        statusLine.appendChild(Microsoft.Live.Messenger.MessengerUtility.emoticonEncode(userDisplayName));
        statusLine.appendChild(document.createTextNode(" (" + userAddress + "): " + userStatus));
    }
    else
    {
        statusLine.appendChild(document.createTextNode(userAddress + ": " + userStatus));
    }
    userInfo.appendChild(statusLine);
    document.getElementById("personalMessage").value = userPersonalMessage;
}        
        
/* Update the user's personal message. */
function setPersonalMessage()
{
    var personalMessage = document.getElementById('personalMessage');
    var messageText = personalMessage.value;
    messageText = messageText.replace(/</g, "").replace(/>/g, "");
    if (_user)
    {
        _user.get_presence().set_personalMessage(messageText);
    }
}
     
     
/* Called by the Messenger Library when the list of conversations changes. */
function conversations_PropertyChanged(sender,e) 
{
   displayConversations();
}

/* Send a text message in the current conversation. */
function sendMsg()
{
    var txtMessage = document.getElementById("txtMessage");
    var messageText = txtMessage.value;
    var message = new Microsoft.Live.Messenger.TextMessage(messageText);

    if (_user)
    {
        _conv.sendMessage(message, null);
    }

    displayMsg(message);
    txtMessage.value = "";
    txtMessage.focus();
}

/* Called by the Messenger Library when a new message is received. */
function recvMsg(sender, e) 
{
    switch (e.get_message().get_type())
    {
        case Microsoft.Live.Messenger.MessageType.textMessage:
            var message = e.get_message();
            displayMsg(message);
            break;
        default:
            break;
    }
}

/* Show text for a received message in the conversation window. */
function displayMsg(message)
{
    var elMsg = message.createTextElement();
    var txtConv = document.getElementById("txtConv");

    var sender = message.get_sender();
    var dispName = sender.get_presence().get_displayName() || sender.get_address();

    txtConv.appendChild(document.createTextNode(dispName + " says: "));
    txtConv.appendChild(elMsg);
    txtConv.appendChild(document.createElement("br"));
}

/* Loop through all active conversations and display the names of
the conversation participants. */
function displayConversations()
{
    removeChildrenFromNode("divConversations");

    for (var i = 0; i < _user.get_conversations().get_count(); i++)
    {
        var c = _user.get_conversations().get_item(i);

        if (c.get_closed())
        {
            continue;
        }

        convLink(c, i);
    }
}

/* Generate a link for a conversation. */
function convLink(c, item)
{
    var roster = c.get_roster();
    var names = [];

    for (var i = 0; i < roster.get_count(); i++)
    {
        var address = roster.get_item(i);

        var dispName = address.get_presence().get_displayName() || address.get_address();

        names.push(dispName);
    }

    var allNames = names.join(", ");
    var nameList = document.createElement("span");
    nameList.appendChild(document.createTextNode(allNames + " "));

    if (c == _conv)
    {
        nameList.setAttribute("style", "font-weight:bold;");
    }

    // Create a hyperlink to switch to the active conversation.
    var hr = document.createElement("hr");
    var linkSwitch = document.createElement("a");
    var linkSwitchHref = document.createAttribute("href");
    linkSwitchHref.nodeValue="javascript:switchConv(" + item + ")";
    linkSwitch.setAttributeNode(linkSwitchHref);
    linkSwitch.appendChild(nameList);

    // Create a hyperlink to close the conversation.
    var hr = document.createElement("hr");
    var linkClose = document.createElement("a");
    var linkCloseHref = document.createAttribute("href");
    linkCloseHref.nodeValue="javascript:closeConv(" + item + ")";
    linkClose.setAttributeNode(linkCloseHref);
    linkClose.appendChild(document.createTextNode("[close]"));

    // Add the "switch" and "close" hyperlinks to the conversation list.
    document.getElementById("divConversations").appendChild(linkSwitch);
    document.getElementById("divConversations").appendChild(document.createTextNode(" "));
    document.getElementById("divConversations").appendChild(linkClose);
    document.getElementById("divConversations").appendChild(hr);
}

/* Switch the active conversation. */
function switchConv(id)
{
    var c = _user.get_conversations().get_item(id);
    if (c)
    {
        if (_conv)
        {
            _conv.remove_messageReceived(_convSink);
        }
        _convSink = Delegate.create(null, recvMsg);
        _conv = c;
        _conv.add_messageReceived(_convSink);

        removeChildrenFromNode("txtConv");
        removeChildrenFromNode("divConversations");

        /* Display all messages from the conversation history. */
        var hist = c.get_history();
        for (var i = 0; i < hist.get_count(); i++)
        {
            displayMsg(hist.get_item(i));
        }

       document.getElementById("btnSend").disabled = false;
    }
    displayConversations();
    var txtMessage = document.getElementById("txtMessage");
    txtMessage.focus();
}

/* Close the current conversation. */
function closeConv(id)
{
    var conversations = _user.get_conversations();

    var conversation = conversations.get_item(id);

    conversation.close();

    removeChildrenFromNode("divConversations");
    displayConversations();

    if (conversations.get_count() == 0)
    {
        document.getElementById("btnSend").disabled = true;
    }
}


         
function replaceChild(element, newChild)
{
    if (typeof(element) == "string")
    {
        element = document.getElementById(element);
    }
    
    while (element.hasChildNodes())
    {
        element.removeChild(element.firstChild);
    }
    
    element.appendChild(newChild);
}

/* Clear all children from the specified node 
   (e.g. clear messages from the conversation window). */
function removeChildrenFromNode(id)
{
    var node = document.getElementById(id);
    if (!node)
    {
        return;
    }
            
    while (node.hasChildNodes())
    {
        node.removeChild(node.firstChild);
    }
}

_user = null;
_signin = null;
_conv = null;
_convSink = null;
</script>
</head>
<body onload="scriptMain()">
    <div id="msgr">
        <table>
            <tr>
                <td style="height:150px">
                    <div id="signinframe">
                    </div>
                </td>
            </tr>
            <tr>
                <td>
                    <div id="userInfo">
                    </div>
                </td>
            </tr>
            <tr>
                <td>
                    <div id="setUserStatus">
                        <span><b>Change Your Status:</b></span>
                        <select id="selectStatus" onchange="selectStatusChanged()">
                            <option>Appear Offline</option>
                            <option>Away</option>
                            <option>Be Right Back</option>
                            <option>Busy</option>
                            <option>Idle</option>
                            <option>In a Call</option>
                            <option selected>Online</option>
                            <option>Out to Lunch</option>
                        </select>
                    </div>
                </td>
            </tr>
            <tr>
                <td>
                    <div id="setPersonalMessage">
                        <span><b>Personal Message: </b></span>
                        <input id="personalMessage" type="text" />
                        <input onclick="setPersonalMessage()" id="btnSetPersonalMessage" type="button" value="Set" />
                    </div>
                </td>
            </tr>
            <tr>
                <td>
                    <div id="sendMessage">
                        <hr />
                        <span><b>Send a Message:</b></span>
                        <p id="contactLabel">
                        </p>
                        <p id="msgLastRecv">
                        </p>
                        <div id="txtConv">
                        </div>
                        <br />
                        <input id="txtMessage" type="text" /><br />
                        <input onclick="sendMsg()" id="btnSend" type="submit" value="Send Message" disabled="disabled" />
                    </div>
                </td>
            </tr>
            <tr>
                <td>
                    <p id="convHeader">
                        <b>Active Conversations (click to resume):</b></p>
                    <div id="divConversations">
                    </div>
                </td>
            </tr>
             <tr>
                <td>
                    <b>Online:</b><br />
                    <div id="divOnlineContacts">
                    </div>
                    <br />
                    <b>Offline:</b><br />
                    <div id="divOfflineContacts">
                    </div>
                </td>
            </tr>
        </table>


    </div>
</body>
</html>

See Also

Concepts

Windows Live Messenger Library Tutorial
How To: Monitor Presence
How To: Manage Conversations