otsukare Thoughts after a day of work

A mobile? Yes? No? Yes?

My last surprise with regards to user agent detection was the JavaScript available in the movistar home page, a Venezuelian Web site. Mozilla has an opened bug for removing the UA override which makes it possible for Firefox OS users to access the mobile version of the content.

Empty body

First of all, an unrelated issue to the topic of this blog post. The HTML code of this page will fail for people without JavaScript and for bots. The redirection is handled only through JavaScript and there is no information in the body as a fallback (I removed the JavaScript code here).

<html xmlns="http://www.w3.org/1999/xhtml">
<head>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
    <title>Movistar Venezuela</title>
    <script type="text/javascript">
    [ the JS code ]
    </script>
</head>
<body>
</body>
</html>

A very simple solution without changing most of the code here would have been to add the following HTML.

<body>
<p>This is the home page of Movistar. You do not have Javascript but you can access:</p>
<ul>
   <li><a href="http://movistar.com.ve/particulares/">Desktop Version</a></li>
   <li><a href="http://movistar.com.ve/particulares/m/">Mobile Version</a></li>
</ul>
</body>

But let's discuss about the main issue.

Not a mobile

Let me repeat again. User Agent detection is a future fail strategy. And the site movistar shows why. Currently, Firefox OS users (if the UA override was not in place) would be redirected to the desktop Web site. The Firefox User Agent String is

User-Agent: Mozilla/5.0 (Mobile; rv:18.0) Gecko/18.0 Firefox/18.0

Let's go through the JavaScript code on movistar Web site step by step:

var elemHeight = document.documentElement.clientHeight;

clientHeight will return the inner height of an element in pixels. In this case it will be the height of the view in the browser. You can play with it in the Web console of Firefox and change the height of the window of your browser. And it will be never used in the script later on.

var mobile = (/iphone|ipod|android|blackberry|mini|windows\sce|palm/i.test(navigator.userAgent.toLowerCase()));

A mobile variable is defined and will test some strings against the userAgent string returned by the browser. In our case the navigator.userAgent.toLowerCase() will return "mozilla/5.0 (mobile; rv:18.0) gecko/18.0 firefox/18.0" in lowercase. The test is checking if it finds

So Firefox OS is not identifed as a mobile and the mobile variable will take the value false. I removed then the section of the code about mobile to jump to the next test.

if (mobile) {
    // [… code specific for mobile …]
} else {
    if ((navigator.platform == "Series60") || (navigator.platform == "Nokia_Series_40") || (navigator.platform == "Symbian")) {
        document.location = "/particulares/m/default.asp";
    } else {
        document.location = "/particulares/default.asp";
    }
}

The next step is testing navigator.platform which in the case of Firefox OS returns an empty string as intended. The goal is to offer less hooks, not more.

Finally the last condition which is the fallback (which is good, some sites do not provide a fallback) to the desktop site.

document.location = "/particulares/default.asp";

So no lock for Firefox OS. Something could have been done right at the start by identifying the string mobi and that would have the benefit to catch more devices. Let's say, the mobi string is added at the start, what would happen?

And if it had been a mobile?

Let's discover a bit more the section we removed earlier in this post. First of all, surprisingly, this time a variable userAgent has been created. It could have been done right at the start. It's not very important, but it lacks of DRY

var userAgent = navigator.userAgent.toLowerCase();

Here again there is a lot of conditions related to the User Agent string.

if (
        (userAgent.search("android") > -1) 
     && (userAgent.search("mobile") > -1) 
     && (   ((screen.width == 800) && ((screen.height <= 1140) || (screen.height >= 500))) 
         || ((screen.width == 0) && (screen.height == 0))
        )
   ) { document.location = "/particulares/m/default.asp";}

The test is if the user agent string meet these following conditions:

then the browser is redirected to the mobile site document.location = "/particulares/m/default.asp". Let's go to the next test.

if (
        (userAgent.search("android") > -1) 
     && (userAgent.search("mobile") > -1) 
     && (
            (screen.width == 800) 
         && (
                ((screen.height >= 570) && (screen.height <= 580)) 
             || ((screen.height >= 358) && (screen.height <= 361)) 
             || (screen.height == 1130) 
             || (screen.height == 1131)
            )
        )
   ) { document.location = "/particulares/m/default.asp";}

This one is a bit harder to understand. Specifically because it starts with exactly the same two conditions:

then the browser is redirected to the mobile site document.location = "/particulares/m/default.asp". If not one of those, another set of tests is started.

else {
    if (userAgent.search("android") > -1) 
        {
        var str = navigator.userAgent;
        var n = str.search("Fennec");
        if   (n != -1) { document.location = "/particulares/default.asp";} 
        else { var n2 = str.search("Opera");
               if   (n2 != -1) { document.location = "/particulares/default.asp"; } 
               else {var n3 = str.search("LG");
                     if (n3 != -1) { document.location = "/particulares/m/default.asp";}
                     if (
                            (screen.width == 800) 
                         && (
                                ((screen.height >= 1180) && (screen.height <= 1330)) 
                             || ((screen.height >= 391) && (screen.height <= 668)))
                        ) {document.location = "/particulares/default.asp";}
                     if (
                            (screen.width == 1280) 
                         && ((screen.height >= 212) || (screen.height <= 496))
                         ) {document.location = "/particulares/default.asp";}
                    }
            }
        }
     }

It tests again for android for the third time. This could be simplified by putting android at the start, and then testing for subrequirements. Then defining again the user agent string but this time without lowercase var str = navigator.userAgent;. It is the third call to navigator.userAgent. It now searches for the string Fennec which is not used anymore at all by new devices. There are a few devices such as the Nokia N900 which are using Fennec. So what is happening here.

And if you thought it was finished for a last round. the last sequence of redirections to the mobile site.

if (navigator.platform == 'ipod') {
    document.location = "/particulares/m/default.asp";
}
if (navigator.platform == 'blackberry') {
    document.location = "/particulares/m/default.asp";
}
if (navigator.platform == 'iphone') {
    document.location = "/particulares/m/default.asp";
}
if (navigator.platform != "Linux armv7l") {
    document.location = "/particulares/m/default.asp";
}

Conclusion

Do not do that at home! Never ever. There are many ways this script could be simplified. The issue is not that much in simplifying it but to understand what where the issues and questions that led to this script. I would love to know why these decisions have been made. There is certainly some logic in the mind of the author. The values for the screen sizes made me think of an attempt to select between mobile and tablets. So if you are the author, please contact me.

Otsukare!