Creating a Web Messenger control from Scratch – Part 9

After a little break due to a very heavy work schedule, we’re back with Part 9 of this mini-series explaining how to create a Web Messenger control that you can just drop into your web sites. It’s hard to believe that we’re currently on Part 9 of this series of tutorials. In the last part we discussed Groups within Web Messenger. Users can create their own groups and place contacts within these groups, e.g. Co-Workers, Friends, Family etc. Now that we’ve got groups, it’s now time to place the actual contacts within the appropriate group.

contactsfinished_thumb_3f7755f4 Developer

What we will end up is something similar to the above. We will grab each contacts picture or assign a default one if the user does not have a custom image. Display their name and email address and create links so that you can start a conversation with them.

I’ve split groups and contacts into two separate classes. As you may recall we call the groups function from within our signInCompleted event handler :-

function signInCompleted(sender, e)
    {
        if (e.get_resultCode() === Microsoft.Live.Messenger.SignInResultCode.success)
        {
            msgruser.get_presence().add_propertyChanged(Delegate.create(null, user_Presence_PropertyChanged));
            var selectStatus = $get('selectStatus');
            selectStatus.selectedIndex = 6;
            getMessengerLinks();
            //Get the users IMAddressPresence
            displayUserInfo();
            //Now that the user has signed in, get their contacts
            displayContactGroups();
 
        }
    }

 

Previously this function was displayContacts, I’ve now renamed it displayContactGroups just to separate it from Contacts. The function remains the same except for one addition. We now call the function to display contacts at the end.

function displayContactGroups()
{
    var enumgroups = msgruser.get_groups().getEnumerator();
    while (enumgroups.moveNext())
    {
        var currentGroup = enumgroups.get_current();
        var name = currentGroup.get_name();
        var group = new Messenger.Group(name, currentGroup);
        group.DisplayGroup();
        //Show the users some progress
        contactTimeout = setTimeout(displayContacts, 500);
    }
}

 

 

We do this by setting a Javascript timeout. The main reason for doing this is that it shows users some progress. It will display the groups onscreen whilst it comes back to create the contacts rather than processing everything at once and making the user wait until everything has completed.

function displayContacts()
{
    //clear any timeouts.
    clearTimeout(contactTimeout);
    var enumgroups = msgruser.get_groups().getEnumerator();
    index = 0;
    while (enumgroups.moveNext())
    {
        var currentGroup = enumgroups.get_current();
        var name = currentGroup.get_name();
        var enumContacts = currentGroup.get_contacts().getEnumerator();
        while (enumContacts.moveNext())
        {
            var currentContact = enumContacts.get_current();
            //Need to pass in the contact and the group name
            var contact = new Messenger.Contacts(currentContact, name, index);
            contact.DisplayContact();
            index++;
        }
    }
}

 

The displayContacts function first clears any timeouts that may have been created (e.g. when calling from the displayContactGroups function). Next it gets a reference to each of the groups that the user has. Once we have that we iterate through each group and get the contacts for each of the groups.

messengergroups_thumb_2198d1f3 Developer

The Web Messenger Group class exposes two properties as shown above. We have already used the Name property in our own Group class.

  • Contacts – Gets the ContactCollection that contains the contacts in the group
  • Name – Gets or sets the name of the group

The other property gets a collection of all the Contacts for that particular Group. We use both of these properties for our Contacts class. We need the Name property so that we can tell which contact belongs to which group and we obviously need the list of Contacts.

As you can see from the code above, we iterate through the Contacts collection the same way that we iterate through the Groups collection. For each Contact in the Contacts collection we instantiate a new MessengerContact class. Then we call the Display method of this class to display the contact and repeat until all the contacts for that particular group have been processed, then we go onto the next group and repeat until all contacts have been displayed.

messengercontacts_thumb_55ccfb39 Developer  

  • Addresses – Contains all of the IMAddress objects associated with the contact
  • CurrentAddress – Gets the current IMAddress in use for the contact. Use this Address to initiate new conversations with the contact
  • DisplayName – Gets the contact’s display name
  • Groups – Gets the groups that the contact belongs to
  • IsAllowed – Gets whether the contact is allowed to view the user’s presence
  • IsBlocked – Gets whether the contact is blocked from viewing the user’s presence
  • NickName – Gets or sets the contact’s nickname
  • Presence – Gets the presence information (ContactPresence) associated with the contact

Our MessengerContact class defines a few key properties :-

Type.registerNamespace("Messenger");
 
Messenger.Contacts = function(contact, groupName, index)
{
    this.Contact = contact;
    this.GroupName = groupName;
    this.Index = index;
    var _contactName = contact.get_displayName();
    if (_contactName.indexOf("@") > -1)
    {
        this.ContactName = _contactName.substr(0, _contactName.indexOf("@"));
    }
    else
    {
        this.ContactName = _contactName;
    }
 
}

 

We pass in the contact object itself, the name of the group that this contact belongs to and also an index (which will be used for conversations. This is simply an incremental number).

Note that the groupName that is passed in we could have gotten from the Groups property that is exposed in the Contact object however if a contact is placed in multiple groups (which is allowed) then we want a separate instance of the contact for each group, hence why we pass in this value.

The last chunk of code assigns the name of the Contact. We will use this later. Some Contacts do not have display names and instead their email address is substituted for their display name. If this is the case then we want their display name to be the first part of their email address (the bit before the “@”).

Next we define our actual class prototype and start off with some simple getters and setters :-

Messenger.Contacts.prototype =
{
    get_ContactName: function()
    {
        return this.ContactName;
    },
    set_ContactName: function(value)
    {
        if (value.indexOf("@") > -1)
        {
            this.ContactName = value.substr(0, value.indexOf("@"));
        }
        else
        {
            this.ContactName = value;
        }
    },
    get_Index: function()
    {
        return this.Index;
    },
    set_Index: function(value)
    {
        this.Index = value;
    },
    get_GroupName: function()
    {
        return this.GroupName;
    },
    set_GroupName: function(value)
    {
        this.GroupName = value;
    },
    get_Contact: function()
    {
        return this.Contact;
    },
    set_Contact: function(value)
    {
        this.Contact = value;
    },

 

 

Next we define our DisplayContact method, which gets called immediately after the class is instantiated :-

DisplayContact: function()
{
    if ($get("Contact" + this.ContactName) == null)
    {
        //Get base group DOM element
        var link = document.createElement("a");
        link.href = "javascript:createConv('" + this.Index + "');";
        link.className = "ContactLink";
        link.id = "ContactImgLink" + this.ContactName;
        var textlink = document.createElement("a");
        textlink.href = "javascript:createConv('" + this.Index + "');";
        textlink.className = "ContactLink";
        textlink.id = "ContactTextLink" + this.ContactName;
        var outerdiv = document.createElement("div");
        outerdiv.id = "Contact" + this.ContactName;
        //Status and picture holder
        var PicOutline = document.createElement("div");
        PicOutline.id = "ContactStatus" + this.ContactName;
        PicOutline.className = "ContactImgOutline";
        var ContactPicHolder = document.createElement("div");
        ContactPicHolder.id = "ContactImgHolder" + this.ContactName;
        ContactPicHolder.className = "ContactImgHolder";
        //Create a span to place the name of the contact
        var ContactNameHolder = document.createElement("div");
        ContactNameHolder.id = "ContactNameHolder" + this.ContactName;
        ContactNameHolder.className = "ContactNameHolder";
        var name = document.createElement("span");
        name.className = "ContactName";
        name.id = "ContactName" + this.ContactName;
        //create a horizontal rule to seperate contact
        var hr = document.createElement("hr");
        //Build the heirarchy
        ContactPicHolder.appendChild(link);
        PicOutline.appendChild(ContactPicHolder);
        textlink.appendChild(name);
        ContactNameHolder.appendChild(PicOutline);
        ContactNameHolder.appendChild(textlink);
        outerdiv.appendChild(ContactNameHolder);
        outerdiv.appendChild(hr);
        this.DisplayContactImage(link)
        this.ChangeContactStatus(PicOutline);
        this.DisplayContactName(name);
        //get the groupDiv
        var grp = $get("group" + this.GroupName);
        //add the contact to the group
        grp.appendChild(outerdiv);
    }
},

 

 

This may look complicated but in reality it’s not. Most of the code above simply defines a hierarchy of html elements for placement of the various elements of our contact. In particular we have the contact picture, the contact display name and email address and to be like the new Wave 3 client Messenger product we’re also introducing a presence halo.  Both the Contact image and their name/email address will be hyperlinks so that we can start a conversation when the user clicks on that particular contact.

 

DisplayContactImage: function(ContactPicHolder)
{
    this.RemoveChildrenFromNode(ContactPicHolder.id);
    var img = new Image();
    img.onerror = function()
    {
        img.src = "WebMessenger/Images/MsgrNoImage.gif";
    }
    img.src = this.Contact.get_presence().get_displayPictureUrl();
    //Now we need to scale the image down to 40 pixels width
    //and the appropriate height
    var scale = Math.round(img.width / 40);
    img.style.width = "40";
    if (scale > 0)
    {
        img.style.height = Math.round(img.height / scale)
    }
    img.id = "ContactPic" + this.ContactName;
    img.className = "ContactImage";
    ContactPicHolder.appendChild(img);
},

 

 

The DisplayContact method makes a call into DisplayContactImage. This routine simply gets the Contacts picture from the Presence object, which we’ve talked about before when we were display the Users own picture and presence. We also apply some very rough scaling to the image and then put it in place.

ChangeContactStatus: function(StatusDiv)
{
    var stat = Enum.toString(Microsoft.Live.Messenger.PresenceStatus, this.Contact.get_presence().get_status())
    switch (stat)
    {
        case "online":
            StatusDiv.style.backgroundColor = "#00cc00";
            break;
        case "offline":
            StatusDiv.style.backgroundColor = "#cc0000";
            break;
        default:
            StatusDiv.style.backgroundColor = "#E3AC4F";
            break;
    }
},

 

The ChangeContactStatus method simply changes the color of the halo around our Contacts image to indicate whether they are busy (orange), offline (red) or online (green). The Contacts presence can be gathered from the Presence object and is actually an enum. Again we have covered this previously when we were talking about the actual user Object that we created.

DisplayContactName: function(NameDiv)
{
    this.RemoveChildrenFromNode(NameDiv);
    //Now we need to check if the name is actually a name
    //or an email address.
    var displayName = this.ContactName;
    if (this.ContactName.indexOf("@") == -1)
    {
        displayName += "  (";
        displayName += this.Contact.get_currentAddress().get_address();
        displayName += ")";
    }
    NameDiv.innerHTML = displayName;
},

 

The DisplayContactName function simply displays the contacts name and concatenates their email address onto the end of it. Therefore you end up with something like “Colin – [email protected]”.

    RemoveChildrenFromNode: function(id)
    {
        var node = document.getElementById(id);
        if (node == undefined || node == null)
        {
            return;
        }
        var len = node.childNodes.length;
        while (node.hasChildNodes())
        {
            node.removeChild(node.firstChild);
        }
    },
    dispose: function()
    { }
}
Messenger.Contacts.registerClass("Messenger.Contacts", null, Sys.IDisposable);
if (typeof (Sys) !== "undefined")
{
    Sys.Application.notifyScriptLoaded();
}

 

 

The rest of the class is straightforward and code that we’ve used in other classes. The RemoveChildrenFromNode simply removes any inner content from a particular html element. The standard Dispose functionality and lastly we actually register the class with the Asp.Net Ajax ClientScript object which will be found in the hosting page or it’s master page.

In our WebMessenger.ascx.cs code behind file for our Server Control page we need to register our Javascript object so that we can access it :-

if (!sm.Scripts.Contains(new ScriptReference("WebMessenger/Script/MessengerContacts.js")))
{
    sm.Scripts.Add(new ScriptReference("WebMessenger/Script/MessengerContacts.js"));
}

 

 

As with other classes that we’ve defined, I’ve also included a stylesheet object so that end users can modify how things look and the layout without having to delve into any code. Therefore again in our code behind we need to register the CSS style sheet we have for the MessengerContacts class :-

HtmlLink ContactSylteSheet = new HtmlLink();
ContactSylteSheet.Attributes.Add("href", "WebMessenger/Contact.css");
ContactSylteSheet.Attributes.Add("rel", "stylesheet");
ContactSylteSheet.Attributes.Add("type", "text/css");
header.Controls.Add(ContactSylteSheet);

 

And here is the appropriate StyleSheet :-

.ContactImage 
{
    border: 0px;
    text-decoration: none;
    border-width: 0px;
}
 
.ContactName
{
    text-decoration: none;
    font-size: 0.9em;
    padding: 2px;    
    text-align: center;
}
 
.ContactLink 
{
    border: 0px;
    border-width: 0px;
    text-decoration: none;
}
 
.ContactNameHolder
{
    position: relative;
    text-decoration: none;
}
 
.ContactImgOutline
{
    width: 44px;    
    padding: 2px;
}
 
.ContactImgHolder
{
    width: 40px;
}

 

 

And that’s all there is to displaying contacts within groups. What you should end up with should be similar to the image that we started out this tutorial with.