Rey Bango

JavaScript, HTML, CSS & Random Stuff…

Hey Paydirt: Your Site Works Just Fine in IE

One of the things that I’m passionate about in my role at Microsoft is helping developers ensure that their sites provide an awesome experience in every browser. Yeah, yeah I know it seems odd that a Microsoft evangelist would say this but it’s the honest truth and anyone who’s worked with me before knows that I take great pride in this effort.

We’re Not Supporting IE!

Today, I came across this article on HackerNews which caught me totally by surprise:

We don’t support Internet Explorer, and we’re calling that a feature

When I checked out the site entry, it directed me to a startup called Paydirt which has a very slick looking time-tracking & invoicing app. In their blog post, they rattled off a couple of reasons why they’re not supporting Internet Explorer and they were pretty upfront about it:

“That’s why we made a key decision at Paydirt: we don’t support IE – at all – and we don’t pretend to. You can’t even sign up for Paydirt using IE.”

They also mentioned that sensible browsers support cool features:

“Sensible browsers can do amazing things (canvas, SVG animations, CSS3, web-sockets, blazingly fast JS), and limiting usage to these lets Paydirt take full advantage of these new technologies.”

So of course, I had to dig in to figure out what magical features they were using that IE9, and especially IE10, didn’t handle.

Um…it works in IE

Sure enough, when I tried to sign up, I was blocked. So I used the IE F12 tools to fake the Chrome user agent string and, bam, I was able to sign up with no issues. The signup was painless and I waded through the app with Chrome & IE side-by-side. As I went through, I didn’t notice any differences in the functionality. Buttons worked as expected, data was being saved and even panels with fading functionality worked as expected. I was stumped because I couldn’t see what was preventing a user from using Internet Explorer on this site.


Chrome, IE10, IE9/Win8, IE9/Win7 Screenshots of Paydirt

Why is there code in here for IE support?

Next, I dug into the code and again, didn’t see anything that I could pinpoint as an issue. In fact, the Paydirt team had done a nice job of laying out their site using standards-based code. This is precisely what you want to see; developers using standards. As I dug through more files, I came across their main CSS stylesheet called paydirt-e08a29afb369fe41806659f40ff86301.css. When I looked in it, two things immediately popped out:

  • They were using many CSS3 features supported since IE9, like transforms & border-radius, but just not adding the –ms vendor prefix entry
  • They had the “-ms” prefix for CSS3 gradients which was added in IE10
  • They added support for -khtml-box-shadow but not -ms-box-shadow???

This threw me off because in their blog post, their concern was the IE didn’t offer amazing feature support yet many of the features they implemented are readily available in IE9. In addition, they had actually done some initial work on supporting CSS3 features that are vailable in IE10 (e.g.: transitions & CSS3 gradients).

At this point, I’m completely stumped because:

  • Their site works great in both IE9 & IE10
  • They’ve included support for some IE features in their CSS
  • They want sensible browsers that do amazing things and everything they’re doing is supported by IE9 & IE10. All they have to do is add the “-ms” vendor prefix just like they did for Webkit and Firefox. (They didn’t support Opera either unfortunately).

It works in IE so why not just Open it Up

So we’re left to wonder if IE issues are the real reason they didn’t support the browser. And trust me, I totally get the pain developers went through. I’ve only been with Microsoft for 2 years and was a web developer WAY before I joined them (even part of the jQuery project team). But IE9, and especially IE10, have come an incredibly long way since the IE6-8 days and I’m having a hard time rationalizing Paydirt’s decision here – particularly since it’s exactly this type of behavior (writing for or against specific browsers) that sets the Web back again.

Mind you, if their only reason was that their traffic didn’t justify the support for IE6-8, I could understand it. But one of the things that Microsoft has been advocating for is the concept of “same markup” and Paydirt is a great example of this. Despite them not wanting to support IE, because they used standards-based markup, the site just works in IE9 & IE10. And that totally contradicts Paydirt’s arguments about saving time and lack of feature support. Their site JUST WORKS!

Now, it’s not to say that if you really dig deep you may not find some issue. That happens actually in every browser and is not unique to IE. But seriously Paydirt, do you really want to use these arguments when your site actually does render great in Internet Explorer? Have you checked it out? Perhaps being a Mac-based company, testing may be the issue. If it is, then let’s discuss the options.

Claiming, though, that not supporting IE saves you time when your site is working just fine in IE9/10 is a tough sell. Let’s talk. I’m happy to help.

Update: Closing comments on the post. Plenty of lively feedback which regardless of your stance, I truly appreciate.

iOS to IE10 Metro: Building Cross-Browser Plugin-Free Experiences

I’ve had the good fortune of working with my friend Jonathan Sampson recently on figuring out how to help developers build plugin-free experiences. With IE10 Metro going plugin-free, it’s incredibly important to document steps to help developers provide their users with great experiences without the need for proprietary 3rd party add-ons.

If you’ve built a plug-in-free browsing experience for the iPad, a few changes will make it ready for the new IE10 plug-in-free experience on Windows 8. As more browsers adopt the plug-in-free approach, now is a good time to start thinking about it. I’ll show you how to do this in a few steps below by writing code that works well in all modern browsers.

Today we’re going to work with a MSNBC plug-in-free experience for rich media. It breaks down to two things: styles and scripts.

To modify the files of MSNBC, I will be using a proxy application known as Fiddler. You can download this tool from http://fiddler2.com. This tool allows me to modify remote files as though they were on my local machine. If you have direct access to your own site, you can ignore Fiddler, and work directly with your files. Fiddler provides a great way for testing changes without the risk of breaking your live site.

Step 1: Declare Standards mode and valid markup for modern browsers

In order to use the HTML5 elements we’ll be utilizing below, you’ll first need to ensure that you are operating in standards mode. One way to ensure this is to include the HTML5 doctype at the top of your document:

<!DOCTYPE html>

Step 2: Update your CSS vendor prefixes

The CSS language is constantly in a state of change as new features are suggested, updated, and standardized. In order to allow developers to learn how to use these new features, browser vendors typically offer experimental implementations via prefixed properties.

A key part of using vendor prefixes responsibly is to ensure that prefixes from each vendor are included in your site to allow for the broadest level of feature support. In many cases, especially when building an iPad-centric site, you may have focused solely on -webkit properties, omitting the prefixes which target other browsers such as -o, -ms, and -moz. The end result of this is that you greatly limit the target devices that can render your plugin-free site to as well as provide a degraded experience for users of other modern browsers, many of which could serve up equally engaging functionality.

For instance, we find the following on MSNBC:

background: -webkit-gradient(
  linear,
  left top,
  left bottom,
  color-stop(1, rgba(192,192,192,.6)),
  color-stop(0.5, rgba(0,0,0,.6))
);

With the growing trend towards an HTML5 plugin-free experience, it’s important to expand these rules to provide the vendor prefixes of other major browsers as well.

background: -webkit-linear-gradient(
  top, rgba( 0, 0, 0, 0.0 ) 0%, rgba( 0, 0, 0, 0.6 ) 50% );
background: -moz-linear-gradient(
  top, rgba( 0, 0, 0, 0.0 ) 0%, rgba( 0, 0, 0, 0.6 ) 50% );
background: -ms-linear-gradient(
  top, rgba( 0, 0, 0, 0.0 ) 0%, rgba( 0, 0, 0, 0.6 ) 50% );
background: -o-linear-gradient(
  top, rgba( 0, 0, 0, 0.0 ) 0%, rgba( 0, 0, 0, 0.6 ) 50% );
background: linear-gradient(
  top, rgba( 0, 0, 0, 0.0 ) 0%, rgba( 0, 0, 0, 0.6 ) 50% );

While more verbose but the benefits to broad browser feature support certainly outweigh the extra typing involved. In addition, there are a number of great tools that can break down this workload, such as SASS and Compass, -prefix-free, or even CSS Snippets in the upcoming Visual Studio 2011.

Also, if you’re working predominantly in JavaScript and would like to save time determining which features are supported by your client’s browser, review the instructions in A Best Practice for Programming with Vendor Prefixes on the IEBlog.

Step 3: Get rid of browser sniffing methods

There are two methods used to determine what the user’s browser and device are capable of. One method, which unfortunately is somewhat popular, is browser sniffing. This method consists of examining the navigator object for certain patterns or values.

if ( navigator.userAgent.indexOf("iPad") > -1 ) {
  // Load HTML5 Experience
} else {
  // Load Flash Experience
}

The above code looks at the user agent string for the value “iPad”, and if found delivers a plug-in-free HTML5 experience. Otherwise, it’s assumed you are on a device that has Flash installed. This will result in a broken experience for non-iPad users who are browsing with plug-ins disabled, even though their browser is capable of handling HTML5 features.

Here is an attempt to find the version of Internet Explorer.

if ( tests.IE ) {
  j = /msie.(\d\.\d+)/i;
  k = navigator.userAgent.match(j)[1];
}

The user agent string is tested for a pattern that attempts to target the version number. This pattern looks for a single digit, followed by a period, followed by any number of additional digits. While this test will find values like “MSIE 8.0” and “MSIE 9.0”, it will not identify the latest version of Internet Explorer, which identifies itself as “MSIE 10.0”, since only one digit is expected before the period.

These are just a couple examples of why browser sniffing is not a best practice. The user agent string is not immutable – it is a read-write value that is easily changed by plugins, or even the user. Most modern browsers include the ability to easily change this value from their development tools, which some users take advantage of to get around poorly-developed websites.

If we disable plugins, or visit MSNBC from a device/browser that doesn’t have Flash, we would expect it to attempt a plug-in-free experience. Unfortunately, this is not the case. Rather than seeing an HTML5 experience, we’re instead asked to download Flash. This is because the site puts the user in one of two categories: an iPad user, or a Flash-enabled user.

Feature Detection

Rather than trying to guess what a browser is capable of by sniffing its user agent string (which will fail you eventually), it is much wiser to actually test features directly in the browser. If you wanted to test the browser’s ability to deliver video and audio via HTML5, you could actually attempt to create these elements via JavaScript, and see if the browser understands them. This practice is called feature detection.

if ( !!document.createElement(“video”).canPlayType  ) {
  // Load HTML5 Video
} else {
  // Load Flash Video
}

In the above example, we start by testing whether the canPlayType method exists on our newly-created video tag. We’re using double-negation to cast the response to a boolean. If the browser understands what a video element is, the canPlayType method will be present. If the video element is unknown to the browser, the canPlayType method will not exist. If this test passes, we load our HTML5 video. If the test does not pass, we attempt to load Flash. Deeper feature detection could take place here, since Flash may not be on the machine, or may be disabled.

Feature detection is the preferred method of determining what a browser is capable of, since there is no guesswork involved. If the browser passes properly-constructed tests, it absolutely supports the features you would like to use.

Many great tools exist to provide feature tests for you. Once such tool, which provides over 40 tests, is Modernizr. This tool creates a global object called “Modernizr” which contains the results of your tests. With Modernizr, testing for HTML5 video support is extremely easy:

if ( Modernizr.video ) {
  // Load HTML5 Video
}

MSNBC engages in browser sniffing to see if the device accessing the page is an iPad or not. Our first step is to remove the browser sniffing code, and replace it with feature detection code.

Before we can modify browser sniffing code, we first need to locate it. While in Internet Explorer, pressing F12 will pull up our Developer Tools. Within the tools, open the Script tab and do a search for “userAgent”. This search will seek out any instance of this property name in all of the site’s script files. We’re interested in the result from line 41 of http://www.msnbc.msn.com/id/37156949/.

Now that we know what we want to edit, we can open up Fiddler and load up our traffic. Once Fiddler is opened, perform a hard-refresh (Ctrl+F5 in IE) on the MSNBC page. This results in all of the page sessions being listed in Fiddler.

Looking carefully, you’ll notice our resource is the third from the top. Next I will setup an AutoResponder for this session file so that anytime it is requested, my own custom file is substituted in the place of the server response:

  1. Right-click this session and select “Decode Selected Sessions” from the context menu.
  2. Select the AutoResponder tab on the right.
  3. Click the “Enable automatic responses” checkbox in the AutoResponder tab.
  4. Drag the selected session from the left panel into the AutoResponder tab.

At this point, you should have an entry within your AutoResponder tab with the following rules:

  • If URI matches: EXACT:http://www.msnbc.msn.com/id/37156949/
  • Then respond with: *200-SESSION_3

Right-click the entry in the AutoResponder and select Edit Response. In the popup that follows, switch to the SyntaxView tab where we will find the source for this file. As expected, line 41 contains our browser sniffing code:

if(!(navigator.userAgent.toLowerCase().indexOf("ipad")>-1)){
  // Flash Experience
}

Rather than test the contents of the userAgent, we’re going to instead look for support for the HTML5 video tag. Switch the above condition to the following:

if ( !document.createElement("video").canPlayType ) {
  // Flash Experience
}

This test checks to see if we cannot use the video element. If canPlayType comes back as undefined, it will be cast to true and the first code block will be entered, setting up the Flash experience.

Step 4: Update touch and pointer events

Safari supports both a touch event model and a mouse event model. Internet Explorer 10 groups touch, mouse, and stylus events into a single abstract item known as a pointer. In fact, Internet Explorer 10 is the first browser to work for all input types, across all devices. This abstraction cuts down drastically on the amount of effort involved to determine which event model you ought to bind to and how to detect user-interaction. This pointer is then handled through MSPointer events. If necessary, you can determine the type of pointer by accessing the pointerType property.

Due to the fact Internet Explorer doesn’t support Apple’s proprietary event model, which includes touch events like touchstart, touchmove, and touchend, MSNBC’s event listeners will need to be amended to listen for MSPointer events like MSPointerDown, MSPointerUP, and MSPointerMove.

Due to the difference in event model implementations, use a feature detection tool like Modernizr or code like this to target all major event models:

if (window.navigator.msPointerEnabled) {
  myCanvas.addEventListener("MSPointerMove", paint, false);
} else {
  myCanvas.addEventListener("mousemove", paint, false);
  myCanvas.addEventListener(“touchmove”, paint, false);
}

MSNBC only supports touch events, which we will need to change so that visitors who happen to be using a mouse can still interact with the page:

Our events are tied up in http://www.msnbc.msn.com/id/43662671/15:

document.addEventListener("touchstart", touchHandler, false);
document.addEventListener("touchmove", touchHandler, false);
document.addEventListener("touchend", touchHandler, false);

We’re going to update this to include the MSPointer events as well:

if (window.navigator.msPointerEnabled) {
  document.addEventListener("MSPointerDown", touchHandler, false);
  document.addEventListener("MSPointerMove", touchHandler, false);
  document.addEventListener("MSPointerUp", touchHandler, false);
} else {
  document.addEventListener("touchstart", touchHandler, false);
  document.addEventListener("touchmove", touchHandler, false);
  document.addEventListener("touchend", touchHandler, false);
  document.addEventListener("mousedown", touchHandler, false);
  document.addEventListener("mousemove", touchHandler, false);
  document.addEventListener("mouseup", touchHandler, false);
}

First we’re checking for the presence of pointers. Since the MSPointer covers the mouse, fingers, and pens, we don’t need anything else besides them. We fall back, if necessary, to provide both touch and mouse events.

Next we need to create cases for these event types in http://www.msnbc.com/id/44937131/. Currently, MSNBC starts with the following:

if ( event.type == "touchstart" ) {
  /* Start drag logic */
} else
if ( event.type == "touchmove" ) {
  /* Drag logic */
} else
if ( event.type == "touchend" ) {
  /* Complete drag logic */
}

We’ll modify this to listen for all of the registered event types:

if ( event.type.match( /(down|start)$/i ) ) {
  /* Start drag logic */
} else
if ( event.type.match( /move$/i ) ) {
  /* Drag logic */
} else
if ( event.type.match( /(up|end)$/i ) ) {
  /* Complete drag logic */
}

The above uses the match method and a series of regular expressions to determine which event was raised. If the event raised ends with a case-insensitive “down” or “start”, we begin our drag code. If the event ends with a case-insensitive “move”, we perform the actual drag logic itself. And lastly, if the event ends with a case-insensitive “up” or “end”, we end our dragging event. Note: other events may be caught here as well, like onresizeend and keyup. Be sure to consider this in your project.

The above is an implementation of Ted Johnson’s solution in Handling Multi-touch and Mouse Input in All Browsers.

The drag logic itself initially relies upon the event.targetTouches TouchList. This member does not exist in Internet Explorer. The drag logic attempts to gather the pageX and pageY properties from the first item in the TouchList, however in Internet Explorer these values are found directly on the event object.

var curX = event.targetTouches[0].pageX;

Using the logical OR operator, I instruct curX to hold the value of event.pageX as long as event.pageX is present on the event object. If this property is not found, look within the targetTouches list:

var curX = event.pageX || event.targetTouches[0].pageX;

If event.pageX is not found, we fall back to assigning the value of targetTouches[0].pageX to our variable.

Another important item to keep in mind is that this site initially responds to touchmove. When this event is raised while touching the playlist, the code attempts to reposition the playlist based upon your touch movement. There is no hovering when it comes to touch – you’re either touching, or you’re not.

Now that we have mouse events tied into this logic, we have introduced the possibility for hovering. So while touchmove is free to reposition our playlist when it is over the playlist, we don’t want to do the same for mousemove. In fact, we only want the mousemove event to reposition the playlist when the mouse button is pressed.

For further reading, and examples on how to target all browsers, see Handling Multi-touch and Mouse Input in All Browsers.

Testing both experiences

Recall our feature detection from earlier, how we first check to see if HTML5 video support is in the user’s browser. If it is, we give them HTML5. If it is not, we give them Flash. One easy way to test our work is to use a browser, or document mode, that doesn’t support HTML5 features. This is very easy to test with Internet Explorer:

  1. Press F12 to reveal the Developer Tools
  2. Change your Document Mode to Internet Explorer 7 Standards
  3. Refresh the page

If our feature detection condition was written properly, you should now be watching a Flash-based presentation. Switching your Document Mode back into Internet Explorer 9 Standards (or “Standards” if you’re using IE10), will return you to the HTML5 experience.

Get it Done!

Hopefully this post helps to define the types of changes that will allow your iOS site to work properly in IE10 Metro and other plugin-free environments. By including best practices such as feature detection and responsibly using vendor prefixes for great new features, you should be able to provide your users a great experience, regardless of which browser or device they’re using. To assist with testing in other plug-in-free environments, download Internet Explorer 10 (currently available only in the Windows 8 CP) and begin testing today!

Update: In the rush to get this post up, I realized that I forgot to thank and give credit to Jonathan Sampson for helping investigate and write about the great techniques mentioned above. He was a huge help in generating many of these great techniques. Thanks JS!

Fix Common IE Problems: Update your Docmode for Web Standards

Document compatibility defines how a browser renders your website. The more specific you are at telling the browser what to expect, the better the experience for your users. When using web standards like HTML5, start by explicitly declaring the HTML5 document type:

<!DOCTYPE html>

This markup triggers standards mode in Internet Explorer 9 and 10. And it also works well in Chrome and Firefox. Four steps will get your site ready for many browsers and devices:

Step 1: Validate that your site uses standards mode

Check whether or not your site is currently in standards mode:

  1. Open the website in IE10 platform preview.
    • Note: You can also follow the same steps to update the docmode for IE9 only without downloading the preview.
  2. Press F12 to launch the IE Developer Tools or find it on the Tools menu as shown below:

    • Note: If you’re not familiar with using the IE F12 Developer Tools to debug your webpages, please read the following tutorial.
  3. Check if your site indicates Browser Mode: IE10 and Document Mode: IE10 standards as shown in the toolbar below:

    Click to Enlarge
  4. If your site is in Browser Mode: IE10 and Document Mode: IE10 Standards, you’re done! Note if the Browser Mode and Document Mode of your site are different than above. A common example is Browser Mode = IE8 and Document Mode = Quirks which indicates that your website was designed for older versions of IE and may not be ready for web standards.

    Click to Enlarge

Step 2: Implement docmode for web standards

Force IE10 standards mode to test your website:

  1. Insert
    <!DOCTYPE html>

    into your website’s HTML page

    • Learn more about how to update your doctypes here
  2. Reload your page in the browser and check the Browser Mode and Document Mode again using the F12 Developer Tools. If Browser Mode: IE10 and Document Mode: IE10 standards are not shown, continue below.

Step 3: Determine why your site is not in Standards Mode

Most problems are related to supporting older versions of IE. Start by ensuring your standards-based code is rendered in IE9 and 10. Then keep your non-standards-based code for older versions of IE.

  1. My page is not in Browser Mode: IE10

    • Possible Cause: Your website may flagged in Compatibility View and forced into an older browser mode to ensure the site functions
      • Resolution: Check if your site is on the list here. Learn more about the Compatibility View list and request removal here.
  2. My page is not in Document Mode = IE10
    • Possible Cause: Your website’s doctype is invalid or missing

      • Resolution: Check for a valid, well-formed doctype like:

        <!DOCTYPE html>
        <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
        <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">

        Learn more about how to update your doctypes here.

      • Possible Cause: Docmode being forced via X-UA-Compatible meta tag

        • Resolution: Check for code similar to this in the of the page.

          <meta http-equiv="X-UA-Compatible" content="IE=EmulateIE7" >
          <meta http-equiv="X-UA-Compatible" content="IE=8" >

          Remove it and reload your page. Continue testing. Learn more about Specifying Document Compatibility Modes here.

Step 4: Resolve common IE problems when updating docmode

Most problems are related to supporting older versions of IE. Start by ensuring your standards-based code is rendered in IE9 and 10. Then keep your non-standards-based code for older versions of IE.

  • Possible Cause: Conditional comments support browser version-specific features

    • Resolution: Check for conditional comments that run non-standard code. These are often used on specific features supported by older versions of IE to allow the page to degrade gracefully. Check for code similar to this:

      <!--[if IE 8]>
      
      <p>Welcome to Internet Explorer 8.</p>
      
      <![endif]-->
      

      Remove it and reload your page. Continue testing. Learn more about Conditional Comments here.

  • Possible Cause: User agent sniffing supports browser version-specific features
    • Resolution: Check for user agent sniffing. These are often used to specifically target a browser based on the user agent string presented via the browser mode. Check for code similar to this:

      if (version = /MSIE (\d+\.\d+)/.exec(navigator.userAgent)) {
      
      	isIE = true;
      
      browserVersion = parseFloat(version[1]);
      }

      Start by implementing feature detection where possible with web standards. Learn more about User-Agent Strings here. The IE10 User-Agent String is located here.

Other reasons my page does not render correctly:

  • Possible Cause: Your website may be using browser specific features that are no longer supported. Use web standards whenever possible.

  • Possible Cause: Your website may be using 3rd party plug-ins or like Flash, Quicktime, and Silverlight that are no longer supported by the IE10 metro. Use web standards whenever possible.
    • Resolution: Learn how to create plug-in free experiences. A complete step-by-step guide will be available shortly.
  • Possible Cause: Your website may be loading browser version-specific CSS files:
    • Resolution: Ensure layout is avoiding CSS hacks where possible. Learn more about investigating CSS issues here.

A list of common problems is available in the IE Compatibility Cookbook.

If you’re unable to update your docmode with these resolution steps, tweet us @IE or check the Forums on MSDN.

For further detail, try these articles:

Site Pinning: Rotating Overlay Icons for Multiple Service Notifications

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>

Using Site Pinning and Overlay Icons for Enhanced User Notifications and Engagement

I was recently doing some testing of IE9′s Site Pinning API and found out about a cool bit of functionality that can enhance user notifications. If you’re not familiar with site pinning, it’s a great way to allow users to have easy and quick access to their favorite sites via the Windows taskbar. There’s a really nice overview on Beauty of the Web that explains how it works.

Keeping Users Up-to-Date

One of the features the API provides is the notion of notifications that can allow developers to provide alerts to end users. The functionality allows you to dynamically insert custom overlay icons that can alert users when an important bit of information is available. These overlay icons are rendered over the favicon that is pinned to the taskbar. If you look at the image below, you can see it in action:


Pinned site with no overlay icon


Pinned site with overlay icon

So if you think about the possibilities, any site that offers users an inbox, special deals or sends out time-sensitive alerts could use this notification capability to keep their users up-to-date and more engaged on the site. Sites like the Huffington Post have already discovered that users that pinned HuffPost spent 49% more time on the site.

The best part is that adding this capability is insanely easy.

Setting it Up

For this post, we’re not going to go into the basics of how to pin a site. If you want to learn more, here’s a GREAT resource for getting you up to speed quickly: BuildMyPinnedSite.com. In fact, I used that site to help get me up-to-speed on the basics and it’s well-worth visiting.

To add notifications, you’ll need a couple of things:

  • A cool favicon for your site. If you don’t have one, you can use the handy web-based X-Icon Editor to create one.
  • A set of overlay icons to use. The recommended size is 16×16.

The API is JavaScript-based and we’ll use the following methods:

window.external.msSiteModeClearIconOverlay()
window.external.msSiteModeSetIconOverlay()
window.external.msSiteModeActivate()
window.external.msIsSiteMode()

The window.external.msSiteModeClearIconOverlay method is used to clear out any previously set overlay icons. window.external.msSiteModeSetIconOverlay allows you to specify the name of the notification icon as well as a accessible description. Lastly, we’ll use window.external.msSiteModeActivate to flash the pinned icon to notify the user of the update. Lastly, window.external.msIsSiteMode will let us know if the page was launched as a pinned site, thus allowing us to better determine when to run the code.

For the overlay icons, I’m using five images that display numbers 1 through 5 respectively to designate the number of messages are in a user’s inbox.

The Code

The first thing I need to add is the reference to my favicon. Note that if you don’t add one, then the Internet Explorer’s icon will be used by default.

<link rel="shortcut icon" type="image/ico" href="favicon.ico" />

Next, I want to create some sample data to work with. What I want to do for my demo is to have the overlay icon dynamically change every 5 seconds to simulate a more real-world scenario. The data is a simple array containing JSON data in each element.

myPin.init([{ "num": 1, "label": "Label 1" },
                { "num": 2, "label": "Label 2" },
                { "num": 3, "label": "Label 3" },
                { "num": 4, "label": "Label 4" },
                { "num": 5, "label": "Label 5" }
                ]);

By setting a timer, I’ll be able to pull a new set of data every 5 seconds.

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

The main thing to keep in mind is that I’m “simulating” getting data from some remote host. In reality, all that the myPin.getData() method does is use a running counter to grab a new set of data and render a new overlay icon:

getData: function () {
            // A function that just simulates returning a result set...
            var idx = 0;

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

                idx = this.currIndex++;
                this.currIndex = (this.currIndex < 5) ? this.currIndex : 0;

                this.dispOverlay(this.dataBin[idx]);

            }

}

As you can see, it uses the running counter var currIndex to determine which array element to grab and then passes the data to dispOverlay(). This is where we use window.external.msSiteModeClearIconOverlay() to clear out any previously displayed overlay icons and also generate a string for the actual icon name. You can see that the oImg var is created on the fly based on the data we’re using.

dispOverlay: function (theData) {

            var oImg = "";

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

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

                // Create the image string...
                oImg = "images/num_" + theData.num + ".ico";

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

            }

}

That icon name, along with the accessible label text for the icon, is passed to setOverlay() which sets the overlay icon via window.external.msSiteModeSetIconOverlay and flashes the taskbar icon using window.external.msSiteModeActivate.

setOverlay: function (icon, desc) {

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

}

Test it Out

To test this out, it’s a simple matter of running your newly pinned page in Internet Explorer 9, grabbing the tab and dragging it down to your taskbar:


Tab being dragged to the taskbar


Pinned site with no overlay icon

Five seconds after the page has been pinned, the code will fire off the first notification and continue to cycle through the other icons every subsequent five seconds.


Pinned site with overlay icon

An important thing to remember is that the IE F12 Developer tools are available to you to use in debugging your pinned site. So if you run into quirks, simply press the F12 key and the tools will appear.

The Demo and Final Code

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

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

When the page renders, drag the tab down to your taskbar and pin it. You should see a new windows appear with your newly pinned site. Five seconds later, you’ll see the first overlay icon appear in the taskbar.

Here’s the full source code. You can also download everything here. The really great part is that it isn’t a lot of code to implement this. In fact, to use the API only required 4 method calls. The bulk of the code was to simulate pulling in data. And the “>impact on user engagement is certainly worth adding in the capability.

<!DOCTYPE html>
<html>
<head>
<title>Pinned Site Test</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/sitepinning/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;
    color: #EDEFF4;
}

h1 {
    float: left;
    font: 440%/1.4em 'Segoe Light',Segoe,'Segoe UI','Meiryo Regular','Meiryo',sans-serif;
    margin-left: 10px;
    position: relative;
}
</style>

</head>

<body>

<h1>Pinned Site Test</h1>

<div></div>

<script>

    var myPin = {

        currIndex: 0,
        dataBin: [],

        getData: function () {
            // A function that just simulates returning a result set...
            var idx = 0;

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

                idx = this.currIndex++;
                this.currIndex = (this.currIndex < 5) ? this.currIndex : 0;

                this.dispOverlay(this.dataBin[idx]);

            }

        },

        setOverlay: function (icon, desc) {

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

        },

        dispOverlay: function (theData) {

            var oImg = "";

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

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

                // Create the image string...
                oImg = "images/num_" + theData.num + ".ico";

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

            }

        },

        init: function (myData) {

            this.dataBin = myData;

        }

    };

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

    // Run it once to kick everything off...
    myPin.init([{ "num": 1, "label": "Label 1" },
                { "num": 2, "label": "Label 2" },
                { "num": 3, "label": "Label 3" },
                { "num": 4, "label": "Label 4" },
                { "num": 5, "label": "Label 5" }
                ]);

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

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

Being in a Coffin..Sorta.

Since having my gall bladder removed last year, I’ve had this nagging pain in my right side rib cage area. It’s a consistent dull pain which I was told should go away a couple of months after my surgery. Well it hasn’t and after an ultrasound and an endoscopy (tube down the throat), it was time to pull out the big gun. Today I had an MRI and it was not fun.

The Machine

MRI stands for magnetic resonance imaging and to be honest, I don’t know how it works. I just know that it’s one of the best ways to look inside the human body without having surgery. If you’ve never had an MRI, it can be a bit of an experience. The machine is rather large and circular in shape. If you look at the picture I included, you can see what it looks like. Now you see that tiny little donut hole in the center? That’s where you go in! Yep, they stick you in that hole to be able to scan the body parts they need. At 6’4″ and 235lbs, I’m a pretty big guy so being squeezed into that little orifice was, how should I say, THE SUCK!

As they slide you into this tube feet first, you have two choices. You can either put your hands behind you (like you’re raising both hands) or you can put them at your side. I chose the latter because it was just more comfortable, especially because I had an IV in my arm. The dude then covered my eyes with a small towel and slapped some headphones on my ears so I could listen to music. As I’m being pushed in, I could see slightly from under the towel and that’s when it hit me that I was INSIDE THIS INSANELY TIGHT, SMALL TUBE WITH NO ABILITY TO MOVE!!

A Coffin

Now, I’m by no means claustrophobic but being in there made me feel like I was in a coffin and it was the creepiest feeling ever. For about 30 seconds, I considered asking them to pull me out but I composed myself, closed my eyes and focused on the music. From there things went fairly smoothly until about 10 minutes before the end of the procedure at which point, it started getting incredibly warm. I had an imaging device sitting on top of my chest and every time they used it, I could feel the heat on my chest. Couple that with the blankets they placed on me and I was a little more than toasty. :P

The Experience

Would I do this again? Hell yes! The alternative to this is to have someone cut me open and poke around. As a general rule, I try to avoid having sharp instruments opening me up so while being confined truly sucked, the alternative is far worse. Hopefully, they’ll be able to figure out what’s causing the pain and I can plan out my next steps.

BioShock Infinite is Like “WOAH”! Check Out the Newest Gameplay Trailer.

I just checked out the gameplay trailer for BioShock Infinite and all I can say is “WOW!”. If you’re a gamer, you’re going to want to check this out, especially if you’re a major fan of BioShock 1 & 2 (like me!).

Which Online Backup Service Should I Use?

I already do backups locally but I really would like to consider a cloud-based backup service. So I wanted to reach out to the community to see who is using what and what’s been really good to work with. Normally I’d put this on Twitter but organizing all of the feedback, along with regular tweets, becomes a bit crazy and the comments are better for future reference.

So which online backup services should I consider?

MIX11 Interviews: Mike Taylor, Robert Nyman, Andrew Duthie, and John Bristowe

#ms_mix11_w

MIX11: So long MIX, Can’t Wait to See You Next Year

Today’s the last day of MIX11 and it was clearly evident. The rooms weren’t nearly as full as the first two days but there were still plenty of folks and great sessions going on. I gave my session on HTML5 polyfills and shims. Feedback seemed really good so I’m happy to have had the opportunity to present and want to thank Giorgo Sardo for entrusting me with such an important topic. Some of the feedback:

Attending a cool session by @ReyBango on HTML5 Polyfills and Shims #MIX11 by @anotherlab

@reybango Really enjoyed your talk. Cleared up some questions I had. Great job. by @FwdAnimation

Thanks to @reybango for a good session on #HTML5 polyfills! #MIX11 by @jimfields3

It’s always great to get good feedback and I’m grateful to those that attended and came away with something great from my talk. I was especially impressed by the number of developers who said they were actively using HTML5 and CSS3. It reinforces the need for this type of presentation so that developer can continue to push forward while not leaving their customers, who may not be able to use the latest greatest browser, twiddling their thumbs.

From here I fly off to San Francisco for the jQuery Conference where I’ll give my talk again before heading home. Good times!

#ms_mix11_w

Learn JavaScript!

What to Read to Get Up to Speed in JavaScript.

The best books & blogs for learning JavaScript development. Broken down by experience levels!


My BIG LIST of JavaScript, CSS & HTML Development Tools, Libraries, Projects, and Books.

Constantly updated with the latest and greatest tools. Check it out!

Contact Me

GMail: reybango at gmail dot com
Twitter: @reybango

Haters gonna hate: What's that you said, Cobra?






JavaScript JS Documentation: JS Function Example: Specifying arguments with the Function constructor, JavaScript Function Example: Specifying arguments with the Function constructor

Twitter

Rey Bango is Stephen Fry proof thanks to caching by WP Super Cache