Posts tagged with 'javascript'

Making this blog work as an app on the iPhone (the wrap up)

So far in this series (one, two, three) we have specialized content, detection of phone orientation, basics of being Web Clip capable, and a back to home page functionality. This post wraps it all up by removing the latter, implementing some simple Back button functionality, a “loading” indicator, and I do a little bit of code housekeeping too.

Before we implement a back button, we need to implement a proper URL stack so that we can save where we’ve been in order that the Back button works properly. Since JavaScript’s arrays have push() and pop(), we might as well make use of an array (I’ve called it visitStack). It’s going to be a little peculiar though: we shall be pushing the page’s URL as we load it. That means when we use the Back button, the top of the stack will contain the current page, not the previous one. We shall have to pop off the current page’s URL, and then pop off the previous page’s URL in order to load it. The home page is “special” in that we don’t push it onto the stack: instead we shall use the fact that the stack is empty to signal that we’re moving back to the home page. The home page has some specific behavior – namely it doesn’t have a back button – that we want to trap.

Right. Now the Back button. I decided to use an image, rather than trying to implement it directly.

Back Button

I added it to the header div and gave it a name:

<img id="backbutton" alt="Back button" title="Back button" src="/somepath/backbutton.png" />

The CSS positions the button absolutely inside the area covered by the header div. I didn’t want to futz with floats, etc, trying to get it right, so I just placed it. It’s also described as display:none to begin with so that the code will display it as and when necessary.

#backbutton {display:none; position:absolute; top:27px; left:2px;}

OK, that’s it with the display semantics, time for some code. Here’s the code that shows and hides the button; it also adds/removes a click handler for the button:

  var showBackButton = function () {
    $("#backbutton").show().on("click", function (e) {
      var thisUrl = visitStack.pop();
      if (visitStack.length === 0) {
        loadPage("http://blog.boyet.com/", true);
      }
      else {
        var backUrl = visitStack.pop();
        loadPage(backUrl, false);
      }
    });
  };

  var hideBackButton = function () {
    $("#backbutton").off("click").hide();
  };

The hideBackButton() function is the simplest: it just removes the click handler and then hides the button. The showBackButton() function is a little more complex and we see visitStack in use. The code shows the button and then adds a click event handler to it. The event handler pops off the current page’s URL and then checks to see if the URL stack is empty. If it is, the home page is loaded. If it isn’t, the previous URL is popped off, and that URL is loaded. (Recall that the second parameter to loadPage() defines whether the URL defines the home page (true) or not (false).)

The rest of the stuff happens in a much changed loadPage() function. First of all I had to create a separate boolean (isOnHomePage) to define whether the current page is the home page or not (it’s set to true initially). Using this made it simple to detect the case where the user was navigating away from the home page to a secondary page and the Back button had to be displayed.

  var loadPage = function (url, isForHomePage) {
    window.scrollTo(0, 0);
    addProgressDiv();
    $("#wrapper").load(url + " #wrapper", function () {
      fixupLinks();

      if (isForHomePage) {
        hideBackButton();
        isOnHomePage = true;
      }
      else {
        if (isOnHomePage) {
          isOnHomePage = false;
          showBackButton();
        }
        visitStack.push(url);
      }
      removeProgressDiv();
    });
  };

So, taking it step by step: first we scroll the page to the top as before, we then display a progress div (I’ll get to that in a moment), and then we load the wrapper div. Compared with last time, I’ve moved everything inside the completion function for the load() function. Once the content has been loaded, the completion function fires. First thing to do is fix up all the anchor links like before. After that we get to the new stuff: if the URL just loaded was for the home page, we hide the Back button and set our flag to denote that we’re now showing the home page. If it wasn’t for the home page, but we’re in fact moving away from the home page, we show the Back button (and turn off the “on home page” flag). We then push the new page’s URL onto the stack. Finally, after all other processing, we remove the progress div.

A quick aside on the progress div: it’s just a div that gets positioned absolutely and that has some simple “Loading…” text. Here’s the CSS for it:

#progress {display:none; 
  position:absolute; top: 100px; left:60px; width:200px; margin: 0 auto; padding:20px 0;
  text-align:center; 
  font-size:20px;
  background-color: rgba(0,0,0,0.6); color:white; -webkit-border-radius:10px; }

And here’s the two functions that display it and hide it. Note that I’m adding the div dynamically, it’s not part of the original markup. I could equally well have done this with the Back button of course.

  var addProgressDiv = function () {
    $(document.body).append("<div id='progress'>Loading...</div>");
    $("#progress").show();
  };

  var removeProgressDiv = function () {
    $("#progress").hide().remove();
  };

And here is what it looks like (along with the Back button):

Progress div

Here is the current code as a whole, so you can better understand how it all fits together (there are a couple of tweaks elsewhere as well).

$(function () {
  var 
    canOrient = false,
    isOnHomePage = true,
    visitStack = [];

  var changeOrientation = function () {
    if (canOrient) {
      var currentOrientation = window.orientation;
      if ((currentOrientation === 0) || (currentOrientation === 180)) {
        $(document.body).css("width", 320);
        $("#progress").css("width", 200);
      }
      else {
        $(document.body).css("width", 480);
        $("#progress").css("width", 360);
      }
    }
  };

  var wireUpOrientationChange = function () {
    canOrient = window.orientation !== undefined;
    if (canOrient) {
      window.onorientationchange = changeOrientation;
    }
  };

  var addProgressDiv = function () {
    $(document.body).append("<div id='progress'>Loading...</div>");
    $("#progress").show();
  };

  var removeProgressDiv = function () {
    $("#progress").hide().remove();
  };

  var showBackButton = function () {
    $("#backbutton").show().on("click", function (e) {
      var thisUrl = visitStack.pop();
      if (visitStack.length === 0) {
        loadPage("http://blog.boyet.com/", true);
      }
      else {
        var backUrl = visitStack.pop();
        loadPage(backUrl, false);
      }
    });
  };

  var hideBackButton = function () {
    $("#backbutton").off("click").hide();
  };

  var updateContent = function () {
    changeOrientation();
    fixupLinks();
  };

  var loadPage = function (url, isForHomePage) {
    window.scrollTo(0, 0);
    addProgressDiv();
    $("#wrapper").load(url + " #wrapper", function () {
      updateContent();

      if (isForHomePage) {
        hideBackButton();
        isOnHomePage = true;
      }
      else {
        if (isOnHomePage) {
          isOnHomePage = false;
          showBackButton();
        }
        visitStack.push(url);
      }
      removeProgressDiv();
    });
  };

  var fixupLinks = function () {
    $("#wrapper a").on("click", function (ev) {
      var url = ev.target.href,
          re = /blog\.boyet\.com/i;
      if (url && url.match(re)) {
        ev.preventDefault();
        loadPage(url, false);
      }
    });
  };

  wireUpOrientationChange();
  updateContent();
});

Again, if you want to see how this all looks on your iPhone, you need to open up blog.boyet.com in Safari, touch the middle button in the button bar, select “Add to Home Screen”, give it a name (I use “Boyet blog” since it just fits), and hit Return. At that point, it’s ready to be used on your Home screen.

(Quick notes: the CanOrient flag was to help when I was using desktop Safari to debug the code – desktop Safari has an option whereby you can get it to pretend to be an iPhone, but of course there is no orientation feature. The progress div also participates in the orientation code: I widen it for the case the phone is in landscape mode. I also decided to grow/shrink the body element instead of futzing with the individual named divs.)

Now playing:
Digital Analog Band - The Blues
(from The Best)


Making this blog work as an app on the iPhone (part 3)

The story so far (one, two) is that I’ve simplified the content of this blog for viewing on the iPhone, I’ve added code that recognizes when the phone’s orientation is changed from portrait to landscape (and vice versa), and I’ve added the necessary elements to the head element to indicate that this site can be viewed as a Web Clip. With the current state of play though, the moment you touch on a link, Safari fires up to display the page.

iPhoneTo change this behavior we’re going to make use of the basic structure of my markup. In essence within my body element I have two divs: one named header, the other wrapper. What I shall do is, in effect, fix up each and every anchor element within the wrapper div to load the wrapper div of the requested URL. The header div will remain the same and will not change in appearance. Of course, once I’ve loaded a new wrapper div, I’ll have to change each anchor element to fix up its click behavior, just as before.

That’s the basics, but there are a couple of wrinkles. If an href attribute points to a page outside of my domain I want the old behavior to apply (that is, the URL should be loaded by Safari, not by this Web Clip). The other one is how to get back to the Home page if I click on a link – the Safari chrome with its Back button has gone.

The first part then is the fix up code. This is actually pretty straightforward with jQuery.

  var fixupLinks = function () {
    $("#wrapper a").on("click", function (ev) {
      var url = ev.target.href,
          re = /blog\.boyet\.com/i;
      if (url && url.match(re)) {
        ev.preventDefault();
        loadPage(url, false);
      }
    });
  };

So we get all the anchor links inside the wrapper div and to each we add a click event handler. (I’m using the new jQuery on() syntax here for a change – available from 1.7 onwards – I prefer the way it reads.) The event handler grabs the href attribute from the target element (which is an anchor element, obviously), and then checks to see if it is for blog.boyet.com using an pretty standard “ignore case” regular expression. If it is, the default behavior of the event is suppressed and a new function is called (loadPage) to load the URL.

So, the fun stuff happens in loadPage(). Let’s see that one now.

  var loadPage = function (url, isHomePage) {
    window.scrollTo(0,0);
    $("#wrapper").load(url + " #wrapper", fixupLinks);
    if (!isHomePage) {
      $("#header").one("click", function (e) {
        loadPage("http://blog.boyet.com/", true);
      });
    }
  };

This is a bit more complicated, but not overly so. We see that the second parameter is a boolean indicating whether the URL for for the home page or not. The reason, as we’ll see, is that we don’t want to have “Back button” functionality for the home page (“You’re on the home page, click here to return to the home page” doesn’t sound too well planned). The first thing that happens is that we scroll the page to the top. If we didn’t do this, when we load the new div, we’ll remain at the same relative window position which might, after all, be half way down the page. The user would much more prefer to be positioned at the top of the page after clicking a link rather than half way down.

Next we make use of jQuery’s load() function to load the URL. This is called on the selector we wish to use as the recipient of the new page (hence the wrapper div). The load() function has some special behavior: if we follow the URL by a space and a selector (so “http://example.com/something.html #container”), jQuery will just load the contents of that selector from the URL. In essence, what this code says is “replace the contents of wrapper with the contents of wrapper from this other page”. (There is some secondary behavior here as well: any script blocks in the replacement markup are ignored – which is also what we would like.) The second parameter to load() is a function to execute once the content has been loaded: in our case we want to fix up the anchor elements in the same manner as before.

After that all happens, if the new content is not for the home page, we shall make the header div clickable. I’m using a “one shot” click event handler here (note the function name is one()) so that I don’t have to explicitly unbind the click event handler when it’s used (in an earlier version of the code I didn’t do this and the header got more and more click event handlers tacked on, so that in the end I was firing five or six click events every time I touched the header). The event handler loads the home page and so the whole header acts as the site’s Back button.

The final thing to tie it all up is to make sure fixupLinks() is called when the home page is initially displayed. It’s is a simple matter to add this to a jQuery document.ready() function.

Looks very nice now, I must say. Except…

There is still an issue. It’s fairly obvious once you think about it: the “Back” functionality I implemented is a misnomer since it’s more of a “Go to home page” function. So if I have a post that refers to another, clicking the header on the second post returns to the home page and not to the first post. Yuk. In the next installment I shall have to build a URL stack so that the Back button (and I’ll have to have a real button methinks) works as you’d expect.

Album cover for The Fame MonsterNow playing:
Lady Gaga - Dance In The Dark
(from The Fame Monster)


 


Making this blog work as an app on the iPhone (part 2)

Now that we have a special web site that displays properly either in portrait or landscape mode, let’s make it a web application that we pin to the Home screen. Boiled down to its essence, this is easy: you bring the web page up in Safari, touch the middle button in the bar at the bottom of the screen, and then select the Add to Home Screen option.

Making a web clip

iOS selects a screenshot for the icon (bleugh!) and allows you to edit the caption for the icon. Once that’s done the web page will appear as a web application or Web Clip on some page of your Home screen. If this were all that there was to it, this would be a very short post, but luckily. . .

Of course the very first thing we should do is to define a cool icon for the Web Clip. None of that “screen snapshot” stuff for us. Fire up your favorite paint program and design a 57×57 PNG file. I came up with this one for blog.boyet.com:

Home icon for Web Clip

Yeah, so I’m not an artist. Sue me.

Now you have to specify this icon in your web pages. Add a link element to your head element, like this:

<link rel="apple-touch-icon" href="/somepath/HomeIcon.png"/>

You can also provide higher resolution images for the iPad and for the retina display on the iPhone 4+:

<link rel="apple-touch-icon" sizes="72x72" href="/somepath/HomeIcon-ipad.png"/>
<link rel="apple-touch-icon" sizes="114x114" href="/somepath/HomeIcon-iphone4.png"/>

Next up is to hide the Safari chrome: the address bar and button bar. This is done through another meta element within the head element, and needs to be set before the rest of the tags will work:

<meta name="apple-mobile-web-app-capable" content="yes" />

This will leave just the device’s status bar at the top of the screen. If you want to get rid of that too, tough. The closest you can get is to make it translucent – your content will appear behind it. It’s another meta element.

<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent" />

I just turned it to black for mine (the other alternative is to leave it at default, or remove the meta tag altogether):

<meta name="apple-mobile-web-app-status-bar-style" content="black" />

The other tweakable knob is to specify a start-up or splash image. This defaults to a screenshot of the last time the Web Clip was used, which is a little disconcerting especially if there’s no internet connection (it’ll look like you can interact with the page, but it’s just an image). Basically provide a portrait 320×480 pixel image:

<link rel="apple-touch-startup-image" href="/somepath/splash.png">

Now if you create a Web Clip from the web page, you will get all of that new nice behavior. The web app will still play nice when you re-orient the device. But, and it’s a big but, if you click on a “read more…” link, Safari will fire up up and the new page will load there. Ouch. Not really a web app if it’s just one page.

Next time, we’ll look at solving that with a judicious bit of JavaScript and jQuery.

Album cover for Land of confusionNow playing:
Genesis - Land of confusion (extended Version)
(from Land of confusion)



Making this blog work as an app on the iPhone (part 1)

With the iPhone you easily view a web site in Safari, but you get the annoying bits of chrome at the top (the address bar) and at the bottom (the buttons). What if you wanted your site to occupy the whole of the screen? Well, you can pin the site to the Home screen and, provided that you make a few changes to the HTML, CSS, and JavaScript, you can make your site behave as if it were pretty much a native app.

iPhone close-upFirst of all, like it or not, you’ve got to write your HTML and CSS to serve the content properly on a small screen (320px across in portrait mode, 480px in landscape mode). For readers’ small mobile devices, I created a special abbreviated form of this blog’s home page that just displays the first 500 characters or so of the post rather than all of it. That way the user can read the summary and, if they are enticed by the promise of a good article, they can tap the “Read more…” link.

To style this abbreviated content I have, in essence, two containers: the first for the header (whose id is imaginatively called “header”), and the second for the rest of the page (with an id of “wrapper”). We’ll be making more use of these div containers in part 2.

<body>
  <div id="header">
    <h1><em>Algorithms for the Masses</em><br />by Julian M Bucknall</h1>
  </div>
	
  <div id="wrapper">
  
    <!-- content goes here -->

 </div><!--end of wrapper -->
</body>

The main reason for creating these divs is to style them when the user changes the orientation of the device from portrait to landscape. For the initial CSS I had this:

#header {width:320px;}
#wrapper {width:320px;}

Which are the defaults for portrait mode.

Now we hit the fun parts. The first bit is to suppress the natural tendency for Safari on iOS to increase the size of the font when you switch from portrait to landscape (in essence Safari zooms the content to fit). Since one of the things I wanted to do was to display the content properly in landscape mode across the screen, I had to suppress this default in CSS:

html, body {-webkit-text-size-adjust: none;}

This magic CSS style will suppress the default behavior of iOS Safari to increase the text size when the device is re-oriented. Essentially you would add it to any element style you want to: here I reset the entire body element.

Now we need to add some magic sauce to the HTML markup in the head element.

  <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0">

This line instructs the iOS Safari that we are going to be responsible for defining the width of the content (it will be equal to the device’s physical width), and that we will be suppressing the zoom feature. When you view “normal” web pages in iOS Safari you can pinch to shrink the page or spread your fingers to expand the display. This meta tag essentially tells Safari that the site knows what it is doing with regard to the content and that the pinch gesture is not necessary.

Now for some JavaScript (yes, all this magic couldn’t be done without some JavaScript). We want to make sure that the content restyles itself when the user re-orients the device form portrait to landscape. To do this we make use of the window.onorientationchange event and the window.orientation property value.

var changeOrientation = function () {
  currentOrientation = window.orientation;
  if ((currentOrientation === 0) || (currentOrientation === 180)) {
    $("#header").css("width", 320);
    $("#wrapper").css("width", 320);
  }
  else {
    $("#header").css("width", 480);
    $("#wrapper").css("width", 480);
  }
};

var wireUpOrientationChange = function () {
  changeOrientation();
  window.onorientationchange = changeOrientation;
};

First of all we have the wireUpOrientationChange function, called when the page is initially loaded (document ready in jQuery terms). This calls the changeOrientation method to initialize the correct orientation at the moment the page is initially displayed, and then sets the event handler to track the changes to the device’s orientation from then on.

The changeOrientation method does the real work. It read the current orientation and if it’s 0 or 180 it assumes portrait mode, and anything else (actually –90 or 90 – the orientation is a value in degrees) it assumes landscape mode. In either case it alters the width style of the CSS for both the header and wrapper divs appropriately. Note the use of jQuery here; yes, jQuery is being loaded by the overall HTML page.

These changes will be enough to make iOS Safari display the web site appropriately in either portrait or landscape mode. Next time we shall look at how to pin the site to the Home screen and what needs to happen to fake the “native app'” look and feel.

Now playing:
Hancock, Herbie - One Finger Snap
(from The Best of Herbie Hancock: The Blue Note Years)

Programming the Extras tab

A reader asked me how the Extras tab on my blog was made. It’s a bit of CSS and a bit of JavaScript, so let’s describe it all.

Hand with wrenchThe first thing to note is that I wanted to clean up the way my blog looks so that – first – it’s simpler, and – second – to make the blog look good on a mobile device. To that end, as I mentioned before, I converted the CSS to LESS so that the whole experience of writing the styling was made easier.

With regard to the tab, it’s still hard to get CSS to display text vertically in every browser. So, stuff it, I decided to just create an image in Adobe Illustrator that had the text rotated. (Right click on the tab and select View Image to see what it looks like.) I decided to go for green to make it stand out in my blog’s highly monochrome look-and-feel. Being differentiated like that gives the impression of something happening over there and I wonder what happens if I go click on it…

Well, the first thing that happens is that the tab slides out a little bit (“hey, I’m active!”) when the mouse moves over it. This is purely a CSS effect: the tab is marked up as an anchor link:

<a id="hidesidebar"><img src="/files/themes/castleriggnew/images/extras.png" alt="extras" title="extras" width="56" height="128" /></a>

(Ignore the silly id: it should be something like “showextras”, if anything. This is a work in progress.) The CSS for it (in LESS format, as is all CSS code here) is this:

.sidebarRoundedCorners {
    border-top: 2px solid @sidebarBorder;
    border-left: 2px solid @sidebarBorder;
    border-bottom: 2px solid @sidebarBorder;
    -moz-border-radius-topleft: 10px;
    -webkit-border-top-left-radius: 10px;
    border-top-left-radius: 10px;
    -moz-border-radius-bottomleft: 10px;
    -webkit-border-bottom-left-radius: 10px;
    border-bottom-left-radius: 10px;
}

a#hidesidebar {
    position: fixed;
    top: 60px;
    right: 0px;
    background: @sidebarHeadBackground;
    padding: 5px;
    .sidebarRoundedCorners;
    &:hover {
        padding: 5px 8px 5px 5px;
    }
    img {
        border: 0 none;
    }
}

First up, I declare a fake class – sidebarRoundedCorners – that defines the rounded corners for both the tab and for the sidebar that slides in. The “@” identifiers are LESS identifiers for colors. The anchor tag itself is “fixed” positioned (that is, it is fixed in the browser window, not to the underlying page, and so won’t scroll with the page). I incorporate the fake class to provide the colored rounded corners, and then define a hover value for the anchor link. All that hover pseudo-class does is to modify the padding by a couple of pixels: if you look carefully it’s the left padding that increases by 3 pixels. So, when the mouse moves over the styled anchor link, the hover class gets activated and the tab slides out a little bit; 3 pixels’ worth. No JavaScript to be seen.

(Aside: In CSS3, the hover pseudo-class is supposed to work with any element, not just anchor elements. That’s all fine for the latest browsers, but for earlier ones, confusion is bound to ensue, so I just took the easy route of using an <a> tag and didn’t try for a <div>.)

Now the fun JavaScript stuff. If you click on the tab, it disappears, and the sidebar slides out, also themed in green. Nothing for this but some jQuery-ed JavaScript.

$(function() {
 
  var enableSidebarHiding = function () {
    var link,
        sidebarDiv,
        saveVisibility,
        isVisible,
        makeLinkHide,
        makeLinkShow,
        hideSidebar,
        showSidebar,
        cookieName = "sidebarvisibility";

    var mouseInside = false;

    saveVisibility = function (value) {
      $.cookie(cookieName, value, {expires: 30});
    };
    
    isVisible = function() {
      var isVisible = $.cookie(cookieName);
      return (isVisible && isVisible === "Y");
    };
  
    makeLinkHide = function() {
      link.unbind('click');
      link.hide();
    };
  
    makeLinkShow = function() {
      link.unbind('click');
      link.click(showSidebar);
      link.show();
    };

    hideSidebar = function() {
      sidebarDiv.hide("fast");
      makeLinkShow();
      saveVisibility("N");
      sidebarDiv.unbind("hover");
      $("body").unbind("mouseup");
    };

    showSidebar = function() {
      sidebarDiv.show("fast");
      makeLinkHide();
      saveVisibility("Y");

      sidebarDiv.hover(function () {
          mouseInside = true;
      }, function () {
          mouseInside = false;
      });
      $("body").mouseup(function () {
        if (!mouseInside) {
          hideSidebar();
        }
      });

    };

    link = $("#hidesidebar");
    sidebarDiv = $("#sidebarbox");

    if (!isVisible()) {
      hideSidebar();
    }
    else {
      showSidebar();
    }
  };

  enableSidebarHiding();
});

It’s a standard document.ready jQuery function. Reading from the bottom (where all the interesting stuff happens), we enable the hiding of the sidebar (which is the rest of the code). First up is to get the tab (link) and the div containing the actual sidebar (sidebarDiv). In the CSS, this latter div is marked as display:none so it won’t show when the page is initially rendered. I make a note of these elements at the start because it’s a little faster later on and I’m not modifying the structure of the DOM at all.

I then check the cookie to see if the user is currently showing the sidebar or not and call the relevant routine to make sure. hideSidebar() not only makes sure the tab is shown, the sidebar div is hidden, but also that the tab has a click event handler attached so we can trap a click event. showSidebar() does something similar: it forces the tab to hide, the sidebar div to show, and attaches mouse event handlers to both the sidebar div and the body of the page.

Let’s go through the event handlers. When the sidebar is hidden, the only active piece on the page is the green tab. I attach a click event (in makeLinkShow()) so that I can catch the user clicking on the tab. When the user does, the showSideBar() event handler gets triggered. This does the right stuff visually and then adds a hover event handler to the sidebar div. It also adds a mouseup event handler to the body element. What’s all this about?

The hover handler just tracks whether the mouse cursor is over the sidebar or not. It sets a boolean mouseInside variable to true (“inside the sidebar”) or false (“outside”). When the mouseup event handler gets triggered, the user has clicked somewhere in the body of the page (which includes the sidebar, by the way). The event handler checks our mouseInside variable. If true, the event handler does nothing. If false, the mouse cursor is outside the sidebar, and the handler hides it (and removes those two event handlers again).

So, as far as the user is concerned, they click outside the sidebar and that action hides it with a swoosh and redisplays the green tab. He clicks the green tab again and the sidebar shows with a swoosh, hiding the tab in the process. All JavaScript.

Now playing:
Pet Shop Boys - London (Genuine piano mix)
(from London)

The PacMan problem

I came across this mathematical problem the other day:

Consider n points on a circle, labeled clockwise from 0 to n-1. Initially PacMan begins at 0 and there is a dot at each of the remaining n-1 points. PacMan takes a random walk around the circle; at each step, it moves with probability 1/2 to one neighbor and with probability 1/2 to the other neighbor. (Note that points 0 and n-1 are neighbors.) The first time PacMan visits any point it eats the dot that is there. Which dot is most likely to be the last eaten?

(This is edited from the original since the professor who formulated it wants to reuse it in a different class and I’d rather not students find this with a simple search.)

Here’s the game plan then for 10 points around the circle:

PacManDots

My first basic thought was, since the question asks for a single dot, it would make sense from a symmetry point of view for that dot to be directly opposite. In other words: it’s the dot furthest away that gets eaten last most of the time. Seems like a viable proposition, no? But maybe I’m trying to be too clever in reading between the lines of the problem: maybe there is more than one dot that is likely to be the last eaten, say at 1/3 and at 2/3 of the way round the circle. It’s getting a bit complicated now.

Mitzenmacher indicated that many students decided to simulate the problem to find out the answer, and then to reason the proof from the statistical results. I decided to do the same. And because I’m into JavaScript at the moment, I used that as my language. It also gives me an opportunity to expand a little on how to write long-running programs that can run in a browser.

In essence I wrote a program to cycle through game after game, recording the last dot to be eaten. To make it statistically significant, I went for some 90,000 games. Here’s my code:

  var i, 
      gameNumber = 0, 
      totalGames = 90000,
      pointCount = 10,
      stats = [];

  // zero out the stats array
  for (i = 0; i < pointCount; i++) {
    stats[i] = 0;
  }

  var dotsToEat,
      dotsEaten,
      pacManPosition,
      goLeft,
      dotPositions = [false];
            
  for (gameNumber = 0; gameNumber < totalGames; gameNumber++) {

    // reset game
    dotsEaten = 0;
    dotsToEat = pointCount - 1;
    for (i = 1; i < pointCount; i++) {
      dotPositions[i] = true;
    }
    pacManPosition = 0;

    // continue moving PacMan until all dots are eaten
    while (dotsEaten < dotsToEat) {
      // go left or right?
      goLeft = Math.random() < 0.5;

      // move PacMan
      if (goLeft) {
        pacManPosition = pacManPosition - 1;
        if (pacManPosition < 0) {
          pacManPosition = pointCount - 1;
        }
      }
      else {
        pacManPosition = pacManPosition + 1;
        if (pacManPosition === pointCount) {
          pacManPosition = 0;
        }
      }
            
      // if there's a dot, eat it
      if (dotPositions[pacManPosition]) {
        dotPositions[pacManPosition] = false;
        dotsEaten += 1;
      }
    }

    // increment stats for the last position
    stats[pacManPosition] += 1;
  }

  alert(stats);

There’s nothing too difficult here: in essence, I set up a loop to run game after game. For each game I simulate PacMan doing a random walk around the circle, back and forth, eating dots when he gets to them. I make a note of the last dot eaten in a statistics array, before going round again and starting a new game. Finally after 90,000 games I report the stats array.

If you run this in Firebug (like I did) you’ll see that the browser times the script out because it’s taking too long. Time to refactor to use setTimeout.

(I would take a look at a three part series I did a while back on callbacks (one, two, three) since I will be using the same techniques here.)

The first thing to do is to make the code that runs a single game into its own function since it’ll be easier to pass it to the setTimeout function. For simplicity though, I’m going to nest it inside another function so I can take advantage of function scoping for the variables that apply across all games. Here’s the main outer function and its call.

var runSimulation = function () {

  // code goes here

};

runSimulation();

Now for the run-a-single-game function:

  var runGame = function () {
    // reset game
    var dotsEaten = 0,
        dotsToEat = pointCount - 1,
        pacManPosition = 0,
        i, goLeft,
        dotPositions = [false];

    for (i = 1; i < pointCount; i++) {
      dotPositions[i] = true;
    }

    // continue moving PacMan until all dots are eaten
    while (dotsEaten < dotsToEat) {
      // go left or right?
      goLeft = Math.random() < 0.5;

      // move PacMan
      if (goLeft) {
        pacManPosition = pacManPosition - 1;
        if (pacManPosition < 0) {
          pacManPosition = pointCount - 1;
        }
      }
      else {
        pacManPosition = pacManPosition + 1;
        if (pacManPosition === pointCount) {
          pacManPosition = 0;
        }
      }
            
      // if there's a dot, eat it
      if (dotPositions[pacManPosition]) {
        dotPositions[pacManPosition] = false;
        dotsEaten += 1;
      }
    }

    // increment stats for the last position
    stats[pacManPosition] += 1;
  };

The variables for this nested function that come from the outer function are stats[] and pointCount.

We’ll also write a self-delaying function to run all the games:

  var runAsync = function () {
    runGame();
    gameNumber += 1;
    if (gameNumber < totalGames)
      setTimeout(runAsync, 10);
    else 
      alert(stats);
  };

In other words, run a game, after it completes and there are more games to run, queue up the next one with setTimeout, otherwise display the stats array. The full code now looks like this:

var runSimulation = function () {

  var i, 
      gameNumber = 0, 
      totalGames = 90000,
      pointCount = 10,
      stats = [];

  var runGame = function () {
    // code above
  };

  var runAsync = function () {
    // code above
  };

  // zero out the stats array
  for (i = 0; i < pointCount; i++) {
    stats[i] = 0;
  }

  runAsync();
};

Way cool. Except that, since it runs one game every 10 milliseconds, it takes 900+ seconds (90,000 games * 10 ms) to give an answer. That’s a quarter of an hour. I’m sorry but, although I’d like to know the answer, 15 minutes to see it is a little too long. Let’s refactor the runAsync function to batch up the games. It should try and run as many games as it can during 50 milliseconds (say) before queuing up another batch. Replace the old runAsync function with this new one (plus a little helper function):

  var now = function () {
    return +new Date();
  };

  var runAsync = function () {
    var start = now();

    do {
      runGame();
      gameNumber += 1;
    } while ((gameNumber < totalGames) && ((now() - start) < 50));

    if (gameNumber < totalGames)
      setTimeout(runAsync, 10);
    else 
      alert(stats);
  };

Now we’re really rocking. On my machine just now, this runs 90,000 games and produces an answer in about 3 seconds. The answer just now?

0,9994,10079,10039,9919,10004,9879,10073,10027,9986

Wow. In other words, any dot is just as likely to be the last one eaten as any other. (If you like, the probability of any dot being the last one eaten is 1/9.)  My initial guess of the opposite dot is dead wrong.

To prove this mathematically involves rather more knowledge about random walks and Markov processes than I now know (it’s been a while since I did probability at University). It’s also linked to the Gambler’s Ruin problem.

 

Album cover for Black LightNow playing:
Groove Armada - Shameless feat. Bryan Ferry
(from Black Light)


Adding sharing links to the blog–JavaScript edition

The story so far: in essence I’ve added AddThis and Google +1 support to this blog using their “simple” markup. Unfortunately, the simple way breaks XHTML validation using the W3C validator because of the non-standard attributes (addthis:url, addthis:title) and elements (g:plusone). As I said previously, time to break out the JavaScript.

Let’s do this in reverse order, starting with the +1 button. The reason for doing it this way round is that Google’s documentation was easier to follow, and I wanted to make sure that the trick I’d decided on was going to work before starting on the AddThis code.

Since the problem with the +1 code is the non-standard element, I decided to use a named span instead and then modify it with jQuery. I needed to pass in (somehow) the url of the post to that span. Here’s the markup for GraffitiCMS’ template I used, added at the end of the post view:

<span class="googleplusone">
  <a href="$macros.FullUrl($post.Url)"></a>
</span>

So, a named span (named via class rather than id, since there will be 10 of them on the index page, remember) and that named span contains an anchor tag with the post’s URL. The post’s URL must be a fully qualified URL, not a relative one, hence the call to $macros.FullUrl. The plan I had was to find each span, extract the URL from the anchor, delete the anchor element (I don’t need it), and then call gapi.plusone.render on the span element.

var wireUpPlusOne = function () {
  $(".googleplusone").each(function () {
    var $this = $(this),
          url = $this.children("a").attr("href");
    $this.empty();
    gapi.plusone.render(this, {
      size: "small",
      count: "true",
      href: url
    });
  });
};

With jQuery, it’s pretty much a piece of cake. I find all elements with the relevant class name and call each on the returned collection to process them individually. For each of them, I grab the DOM element as a jQuery object, call children on it (in particular, I find those children that are anchor elements), and grab the href attribute value of the first (and only) one. After that I can empty the span element (bye-bye anchor element), and then call the Google API to render the button. Simple, eh? And, after I added it to the document ready function I have, it worked just fine as well.

Now for the AddThis toolbox. Here I will admit to being completely bamboozled by the documentation. The problem was, I think, is that there was not a complete example like with the Google documentation, but instead it relied on code snippets. In particular, I was confused with what happens explicitly versus implicitly. You see, you can just define the AddThis toolbox as I had before, and some JavaScript gets run automatically to set up the toolbar. Or, you can define the AddThis toolbox and explicitly call addthis.toolbox() on it. What happens to the implicit call then, I’ve no idea. Or, you can dynamically create the div with the anchor links inside it, and then explicitly call addthis.toolbox() on that newly created bit of DOM.

I decided to not even bother trying to work it all out. I decided to add a span as with the +1 markup, but this time with the post’s title for the anchor’s text.

<div class="addthis_toolbox addthis_default_style">
  <span class="addthisurlcontainer">
    <a href="$macros.FullUrl($post.Url)">
      $post.Title
    </a>
  </span> 
  <a class="addthis_button_preferred_1"></a>
  <a class="addthis_button_preferred_2"></a>
  ...etc...

The plan of attack here was to find the divs, get the named span, retrieve the URL and title, remove the span, and then add the qualified attributes that AddThis needs. If I did all this before the implicit AddThis code executes I should be fine.

var wireUpAddThis = function () {
  $(".addthis_toolbox").each(function () {
    var $this = $(this),
        $urlContainer = $this.children(".addthisurlcontainer"),
        $anchor = $urlContainer.children("a"),
        title = $anchor.html(),
        url = $anchor.attr("href");
    $urlContainer.remove();
    $this.attr("addthis:url", url);
    $this.attr("addthis:title", title);
  });
};

My utility JavaScript file is loaded well before the AddThis code, and it adds the attributes just fine in the document ready function well before the implicit AddThis code gets a chance to run.

Now playing:
Space Link - Time Zone
(from Conversions: A K&D Selection by Kruder & Dorfmeister)


Minor changes to look-and-feel

I’ve been meaning to do a bit of housework on this site to clean up the CSS and to add a bit of interactivity. After all, I write a lot about JavaScript, so I might as well start adding some to my blog.

Bansky street cleaner - Chalk Farmphoto © 2007 Dan Brady | more info (via: Wylio)First things first: if things look a little wacky for you, your browser is probably using the old CSS file. Refresh. That should force the new CSS file to be downloaded and used.

Second: the changes to the CSS are two-fold. The main impetus was to allow for the second rightmost sidebar to be hidden/shown. Originally both sidebars were absolutely positioned and so, to enable this new behavior, I had to float all three columns (content, sidebar 1, sidebar 2). That way, when the third column is hidden, the other two can grow/move correctly (and when it is shown, they can shrink/move back). The other main point of the CSS tweaking was to make the display of comments nicer. Previously the size of a single comment block was the maximum of the height of the metadata/avatar part to the left of the content or the height of the content itself. The first possibility made the gaps between comment blocks uneven and look bad, so I fixed this.

Third: I added some JavaScript and jQuery to allow that second sidebar to be hidden and shown. I’ve added an extra link on the “navigation bar” above that performs this task.

These changes should help those readers who are using a smaller screen (say, 1024 pixels wide) see the main content more easily. I have had a couple of emails complaining about the old layout, so these changes should alleviate the problem.

Now playing:
Heaven 17 - This Is Mine (2006 Digital Remaster)
(from Greatest Hits - Sight And Sound)


JavaScript for C# developers: some refactoring

I just published a blog post on my DevExpress ctodx blog about taking some non-idiomatic JavaScript code (actually from a DevExpress CodeCentral example program) and refactoring it to follow JavaScript best practices.

Topics covered include:

  • Minimizing the global namespace pollution
  • Using triple-equals instead of double-equals for comparisons
  • Function scoping
  • Passing JSLint tests by using declaration-first

Take a look!

Album cover for Carboot SoulNow playing:
Nightmares On Wax - Morse
(from Carboot Soul)


Chaining fun with window.onload

I set myself a task: write some raw JavaScript for a web page. By raw, I mean no jQuery, no Prototype, no MooTools, just me and the editor.

Because I wanted to reduce the effect I had on the global footprint, I declared one global object like so.

var FOO = {
    // stuff

    initialize: function() {
        // setup stuff
    }
};

So far so good. I then wanted that initialize() function to run once the page was loaded. The immediate solution was this:

window.onload = FOO.initialize;
But that causes a problem in that, when the browser runs initialize(), the this parameter will not point to FOO, but (presumably) to something like window. Meh. Another issue: what if window.onload was already set to something? I'd be stomping on it. I should instead call the old function and then call mine.

Some messing around later, I came up with this:

var FOO = {
    // stuff

    initialize: function() {
        // setup stuff
    },
    
    addLoadHandler: function(func) {
        var that = this;
        var oldEventHandler = window.onload;
        window.onload = function() {
            if (oldEventHandler) {
                oldEventHandler();
            }
            func.call(that);
        };
    }
};

FOO.addLoadHandler(FOO.initialize);

What's happening here?  First of all, I'm now calling a method called FOO.addLoadHandler and passing the FOO.initialize function to it. This first takes a copy of the this parameter (which is FOO) in a variable called that. It then takes a copy of the current value of window.onload, and then sets it to a new anonymous function. The really cool thing about this function is the closure: it has access to the func parameter, and the that and oldEventHandler variables when it is called. It checks to see if oldEventHandler has a value and if so calls it. After that it calls the passed in func.

Since we don't know what this is pointing to on the call to the anonymous function (or, rather, we're not that interested), we need to call the func parameter with the saved that object, so that inside func this is pointing to the right object. This is done through the call method.

And that's it. The code ensures that I'm chaining the existing handler (if any) for the window.onload event, and that my initialize() method is called with this set to the correct object.

Now playing:
Dreadzone - House of Dread
(from ...360 degrees)

Extras

Search

About Me

I'm Julian M Bucknall, the M because it's my middle initial and because I and the other Julian Bucknall (the movie guy) would like to differentiate ourselves.

I'm a programmer by trade, an actor by ambition, and an algorithms guy by osmosis. I write articles for PCPlus in my spare time, not that there's much of that.

Julian M Bucknall Apart from that, an ex-pat Brit, atheist, microbrew enthusiast, Pet Shop Boys fanboy, slide rule and HP calculator collector, amateur photographer, Altoids muncher.

DevExpress

I'm Chief Technology Officer at Developer Express, a software company that writes some great controls and tools for .NET and Delphi. I'm responsible for the technology oversight and vision of the company.

Validation

Validate markup as HTML5 (beta)     Validate CSS

Bottom swirl

Archives

May 2012 (4)
SMTWTFS
« Apr  
12345
6789101112
13141516171819
20212223242526
2728293031

Like this Archive Calendar widget? Download it here.

Social networking

The OUT Campaign

The OUT Campaign

My Tweets

  • Honest Movie Trailer of Phantom Menace http://t.co/sif8y4Ns and then Battleship, er, Transformers http://t.co/sif8y4Ns
  • Damn, Donna Summer and Chuck Brown both gone in the last 24 hours. Different types of music, sure, but enjoyed them both. :(
  • Just saw a company page showing a list of tweets with "Join the conversation" linked to their Twitter a/c. The tweets are 6 months old #fail
Bottom swirl