Building a 2013 No Code webpart for XKCD.com/now

Last week, XKCD (of nerd comic fame) produced this most excellent comic http://xkcd.com/now/

Now

 

This complex looking image describes essentially an outer ring and an inner ring.  The outer ring is the current time on your machine.  The inner ring is the regions of the world.  The chart tells you quickly what time it is for someone living in another part of the world.

Being the SharePoint lover that I am, you know what I'm thinking.

Time for a fun toy Web Part

Step 0.  Environment.
Step 1.  Create a SharePoint Sandbox Solution, add Client Web Part (Host Web)
Step 2.  Create the assets (images, HTML and CSS)
Step 3.  Plug in the javascript code.
Step 4.  Permissions.
Step 5.  Create a test page.  Add the App Part to play.  Change web.regional settings - see webpart change.

 

Step 0.  Environment

My Environment setup is very simple:

  • Office 365 with Developer site, at: https://sharepointgurus365.sharepoint.com/sites/Developer/
  • Visual Studio 2013
  • Running on Windows 8.1
  • I do not have SharePoint installed on this client PC
  • I have a paint program Paint.NET which is an excellent developer's tool when you don't have Photoshop

 

Step 1. Create a SharePoint Sandbox Solution, add Client Web Part (Host Web)


image

Select SharePoint-Hosted.  This App Part will be hosted by Office 365.

 

image

 

Step 2.  Create the images.

imageimage

 

I split the image into two layers.  Because I want to rotate them separately.  I also change the text labels for the time 6PM, Midnight and 6AM on the Time-Ring.  Since when it's upside down it still needs to be readable.

I add these to the Project, under Images

image

I also modify the App Part html

image

I added CSS for the two rings.  Position:fixed so that they overlap. 

<body>
    <style>
        .timezone-ring {
            background: url(../Images/xkcd-now-land.png) no-repeat top left;
            width:706px;
            height:705px;
            position:fixed;
            top:0px;
            left:0px;
        }
        .time-ring {
            background: url(../Images/xkcd-now-time.png) no-repeat top left;
            width: 706px;
            height: 705px;
            position:fixed;
            top: 0px;
            left: 0px;
        }
    </style>
    <div style="position:relative;">
        <div class="timezone-ring">
        </div>

        <div class="time-ring">
        </div>
    </div>
</body>

image

<ClientWebPart Name="xkcd-clock" Title="xkcd-clock" Description="Clock based on xkcd.com/now the outer ring rotates to current machine time.  The inner ring rotates to web region." DefaultWidth="720" DefaultHeight="720">

  • Note: tidy up the ClientWebPart definition.  Set the default width and height.

 

Step 3.  Plug in the javascript code.

 

There is very little JavaScript,  So I wrote them inline.  In practice, this made the debugging a lot more difficult.  I recommend always writing your Javascript in a separate file.

 

<script type="text/javascript">
    //'use strict';  // have to turn off 'use strict' because I use eval() later :-P

    // Set the style of the client web part page to be consistent with the host web.
    (function () {
        var hostUrl = '';
        if (document.URL.indexOf('?') != -1) {
            var params = document.URL.split('?')[1].split('&');
            for (var i = 0; i < params.length; i++) {
                var p = decodeURIComponent(params[i]);
                if (/^SPHostUrl=/i.test(p)) {
                    hostUrl = p.split('=')[1];
                    document.write('<link rel="stylesheet" href="' + hostUrl + '/_layouts/15/defaultcss.ashx" />');
                    break;
                }
            }
        }
        if (hostUrl == '') {
            document.write('<link rel="stylesheet" href="/_layouts/15/1033/styles/themable/corev15.css" />');
        }

/*** John's code starts here ***/

        function turn() {
            var hour = (new Date()).getHours() + 12; // clock faces up so need 12hr offset
            var degrees = -((hour % 24) * 15); // turn anti-clockwise so negative
            var rotate = "rotate(" + degrees + "deg)";

            $(".time-ring").css("transform", rotate);

            setTimeout(turn, 1000 * 60 * 15); // 15mins
        }

        var ctx = SP.ClientContext.get_current();
        var hostCtx = new SP.AppContextSite(ctx, hostUrl);
        var timezone = hostCtx.get_web().get_regionalSettings().get_timeZone();
        ctx.load(timezone);
        ctx.executeQueryAsync(function () {
            // has current timezone

            var description = timezone.get_description();
            var m = /UTC(.*\d+):/.exec(description);
            if (m) {
                var offset = eval(m[1]) -2;
                // map is aligned with Egypt (UTC+2) so need a 2hr offset.

                var degrees = -(offset * 15); // turn anti-clockwise so negative
                var rotate = "rotate(" + degrees + "deg)";

                $(".timezone-ring").css("transform", rotate);
            }

            turn();  // call turn

        }, function () {
            // failed.
        })

/*** JOHN'S CODE ENDS HERE ***/

    })();
</script>

 

Few quick notes here:

  • turn() function calculates the rotation degree for the outer ring, based on the current machine time.  See comments for all the offsets.
  • the clientContext query needs to get the App Host Web settting.  Otherwise it will return you the regional setting for the App Web which would be pretty pointless.
  • finally, once we have the timezone information from the regional setting, it looks like (UTC+10: Sydney).  I take the +10 and do more offset for the inner ring.
  • I only request region information once on page load.  But I put turn() on a timeout call every 15minutes.  So if you leave the webpart on a screen it'll keep checking and turn every 15mins.

 

Step 4.  Permissions.

 

image

By default, the App has no permissions to read from the Host Web.  We need to set up the permission to Read from the Host Web.

image

When deployed via VS.NET you'll see this pop up in the browser.  You'll also get this when you activate the App.

 

Step 5.  Create a test page.  Add the App Part to play. 

 

image

 

Results

 

image

I'm in Australia (AEST UTC+10).  Which is pointing up.  My current time is 11:40pm

image

Change the Host Web's region to (Pacific Time UTC-8).

image

Web Part still shows 11.45PM - that's my current time.  But the map has rotated to point to the West Coast.

 

Downloads

I did not test this on any other machine but it should work.  Let me know if it works for you.

And if you really enjoy using this in your company, please buy something from the XKCD gift shop.