Site Pinning: Rotating Overlay Icons

Share this article

In my last post, I went over how to use IE9′s Site Pinning API to implement overlay icons to enhance user notifications. The demo focused on how to display a numeric icon to indicate when a specific event (e.g.: messages in an inbox) had occurred.


Pinned site with overlay icon

It’s a really great way of letting your users know that there’s pending information for them to check into. But what happens if your site offers multiple types of notifications? With websites offering so much functionality nowadays, it’s pretty common for them to also serve up multiple types of notifications, from friend requests and event reminders to new messages and game invites.

Rotating Multiple Overlays Icons

The great thing about the Site Pinning API is that it’s very flexible and through some JavaScript magic, you can easily display multiple overlay icons for the various services you have. In this demo, I want to rotate through 3 different overlay icons that alert the user to pending messages, requests and actions.

As before, I had to flex some of my artistic talent by creating the overlay icons using the x-icon editor. I created 5 of each and here’s how the first three look:

The code changed slightly from the last demo in order to accommodate multiple bits of data per fetch. While previously, I was only fetching one piece of data, in this demo, I’m returning 3, one for each notification type:

 myPin.init([{ "data" : [{ "label" : "Messages", "ntype" : "M", "num": 2 }, { "label" : "Requests", "ntype" : "R", "num": 1 }, { "label" : "Actions", "ntype" : "A", "num": 3 }] },
		        { "data" : [{ "label" : "Messages", "ntype" : "M", "num": 1 }, { "label" : "Requests", "ntype" : "R", "num": 5 }, { "label" : "Actions", "ntype" : "A", "num": 2 }] },
		        { "data" : [{ "label" : "Messages", "ntype" : "M", "num": 5 }, { "label" : "Requests", "ntype" : "R", "num": 1 }, { "label" : "Actions", "ntype" : "A", "num": 4 }] }
			   ]);

As a reminder, the method getData() simulates grabbing remote data. So if we look at the data above, we can simulate pulling back three distinct bits of data. This is why we call the method every 10 seconds using setInterval. This allows us to see how notifications might look over a period of time.

setInterval(function () { myPin.getData() }, 10000);

The next thing that changed is the use of a timer to allow a slight delay while rendering the overlay icons. Using setTimeout() provides enough of delay so that an individual overlay icon is visible to the user before rotating on to the next icon. If we didn’t have this delay, the rotation would be way too fast to provide any useful notification. If we look at the following image, we can see what the notification will look like:


Overlay icon showing numeric notification

This is accomplished via the following code:

// Grab the current set of data...
currData = this.dataBin[this.currIndex++].data;		

/* We're going to display a new overlay every x number of seconds to display a new overlay icon so
   let's loop through the data elements for the current set of data... */
for (var i=0; i < currData.length; i++ ){

	(function(idx) { setTimeout( function(){ myPin.dispOverlay( currData[idx] ); }, 1000 * idx); }( i ));					

}

Here’s what’s happening. In the first line, I grab the current set of data that holds all of the notification information (messages, requests & actions). That data looks like this:

[{ "label" : "Messages", "ntype" : "M", "num": 2 },
{ "label" : "Requests", "ntype" : "R", "num": 1 },
{ "label" : "Actions", "ntype" : "A", "num": 3 }]

I loop through each group of data and assign a timer using setTimeout() that will call dispOverlay() at ~1 second intervals. That’s the magic code that allows for the gradual icon rendering delay I mentioned before. The expected functionality is that the “messages” icon will render followed by the “requests” icon 1 second later, and then finally the “actions” icon.

Now, you might be wondering why I have an anonymous function wrapping the setTimeout(). It’s because I have a closure within setTimeout which can cause a common scoping issue in which the variable ‘i’, which I use to grab the current index of data, will only be updated to the last index value. James Padolsey has a great explanation on it and thanks to John David Dalton for helping me troubleshoot this.

The final change is in dispOverlay() in which I need to determine which overlay icon needs to display. Since I now have three different types of notifications, I need a conditional statement to determine the type and build the correct icon name:

if (theData.ntype == "M") {
	oImg = "images/messages-" + theData.num + ".ico";
} else if (theData.ntype == "R") {
	oImg = "images/requests-" + theData.num + ".ico";
} else if (theData.ntype == "A") {
	oImg = "images/actions-" + theData.num + ".ico";
}

This checks the type and serves up the right icon based on the type and the number of notifications pending for that type.

The Demo and Final Code

You can check out the demo by going here in IE9:

http://reybango.com/demos/sprotate/index.html

When the page renders, drag the tab down to your taskbar and pin it. You should see a new window appear with your newly pinned site. Next, you’ll see the overlay icons appear in the taskbar and they should begin to cycle every 10 seconds.

Here’s the full source code. You can also download everything here.

<!DOCTYPE html>
<html>

<head>
<title>Pinned Site - Rotating Overlay Icons</title>
<link rel="shortcut icon" type="image/ico" href="favicon.ico" />

<meta name="application-name" content="Pinned Site Test" />
<meta name="msapplication-starturl" content="http://reybango.com/demos/sprotate/index.html" />
<meta name="msapplication-navbutton-color" content="#3480C0" />

<meta name="msapplication-window" content="width=1024;height=768" />
<meta name="msapplication-tooltip" content="Testing the Pinned Site API" />
<style>

body {
    background: none repeat scroll 0 0 #4492CE;
    font: 440%/1.4em 'Segoe Light',Segoe,'Segoe UI','Meiryo Regular','Meiryo',sans-serif;
    color: #EDEFF4;
}

</style>

</head>

<body>

<div>
<h1>Pinned Sites</h1>
<p>Rotating Overlay Icons</p>

</div>

<script>

	var myData = [];

    var myPin = {

        currIndex: 0,
        dataBin: [],

        getData: function () {

			var idx = 0, currData = [], cntr = 0, theData;

            // Determines whether the current page was launched as a pinned site...
            if (window.external.msIsSiteMode()) {

				// Grab the current set of data...
				currData = this.dataBin[this.currIndex++].data;		

				/* We're going to display a new overlay every x number of seconds to display a new overlay icon so
				   let's loop through the data elements for the current set of data... */
				for (var i=0; i < currData.length; i++ ){

					(function(idx) { setTimeout( function(){ myPin.dispOverlay( currData[idx] ); }, 1e3 * idx); }( i ));					

				}

				if (this.currIndex > 2) { this.currIndex = 0 }

            }

        },

        dispOverlay: function (theData) {

            var oImg = "";

            // Is there any data?
            if (theData) {

                // Clear any preexisting overlay icon
                window.external.msSiteModeClearIconOverlay();

				// Render the overlay icon based on the data returned...
				if (theData.ntype == "M") {
					oImg = "images/messages-" + theData.num + ".ico";
				} else if (theData.ntype == "R") {
					oImg = "images/requests-" + theData.num + ".ico";
				} else if (theData.ntype == "A") {
					oImg = "images/actions-" + theData.num + ".ico";
				}				

                // Go ahead and create the overlay image and it's label...
                this.setOverlay(oImg, theData.label);

            }

        },

        setOverlay: function (icon, desc) {

            // Sets the overlay icons...
            window.external.msSiteModeSetIconOverlay(icon, desc);
            window.external.msSiteModeActivate();

        },

        init: function (myData) {

            this.dataBin = myData;
			this.getData();

        }

    };

    // This clears out any previously set overlay icons...
    window.external.msSiteModeClearIconOverlay();

    // Run it once to kick everything off...
    myPin.init([{ "data" : [{ "label" : "Messages", "ntype" : "M", "num": 2 }, { "label" : "Requests", "ntype" : "R", "num": 1 }, { "label" : "Actions", "ntype" : "A", "num": 3 }] },
		        { "data" : [{ "label" : "Messages", "ntype" : "M", "num": 1 }, { "label" : "Requests", "ntype" : "R", "num": 5 }, { "label" : "Actions", "ntype" : "A", "num": 2 }] },
		        { "data" : [{ "label" : "Messages", "ntype" : "M", "num": 5 }, { "label" : "Requests", "ntype" : "R", "num": 1 }, { "label" : "Actions", "ntype" : "A", "num": 4 }] }
			   ]);

    // This is only here because I want to simulate pulling data on a regular interval...
    setInterval(function () { myPin.getData() }, 10000);


</script>
</body>
</html>

This article was originally published by the author. It is republished, with slight modifications, with permission.

Rey BangoRey Bango
View Author

As a Technical Evangelist for Microsoft, Rey focuses on promoting best practices for client-side web development and helping Microsoft meet the needs of this community. He is passionate about HTML5 and the possibilities that it brings for building rich, interactive web applications. In addition, Rey is a member of the jQuery JavaScript project team, and writer for .net magazine.

HTML5 Dev Center
Share this article
Read Next
Get the freshest news and resources for developers, designers and digital creators in your inbox each week