Over a million developers have joined DZone.

Developing with HTML5, CoffeeScript and Twitter's Bootstrap

DZone's Guide to

Developing with HTML5, CoffeeScript and Twitter's Bootstrap

· Web Dev Zone
Free Resource

Add user login and MFA to your next project in minutes. Create a free Okta developer account, drop in one of our SDKs to your application and get back to building.

This article is the fourth in a series about my adventures developing a Fitness Tracking application with HTML5, Play Scala, CoffeeScript and Jade. Previous articles can be found at:

  1. Integrating Scalate and Jade with Play 1.2.3
  2. Trying to make CoffeeScript work with Scalate and Play
  3. Integrating HTML5 Boilerplate with Scalate and Play

Developing Features
After getting my desired infrastructure setup, I started coding like a madman. The first feature I needed was a stopwatch to track the duration of a workout, so I started writing one with CoffeeScript. After spending 20 minutes playing with dates and setTimeout, I searched and found a stopwatch jQuery plug-in. I added this to my app, deployed it to Heroku, brought up the app on my iPhone 3G, clicked Start and started riding my bike to work.

When I arrived, I unlocked my phone and discovered that the time had stopped. At first, I thought this was a major setback. My disappointed disappeared when I found a Super Neat JavaScript Stopwatch and Kåre Byberg's version that worked just fine. This stopwatch used setTimeout, so by keeping the start time, the app on the phone would catch up as soon as you unlocked it. I ported Kåre's script to CoffeeScript and rejoiced in my working stopwatch.

# Created by Kåre Byberg © 21.01.2005. Please acknowledge if used 
# on other domains than http://www.timpelen.com.
# Ported to CoffeeScript by Matt Raible. Also added hours support.
flagClock = 0
flagStop = 0
stopTime = 0
refresh = null
clock = null

start = (button, display) ->
  clock = display
  startDate = new Date()
  startTime = startDate.getTime()
  if flagClock == 0
    flagClock = 1
    counter startTime, display
    flagClock = 0
    flagStop = 1

counter = (startTime) ->
  currentTime = new Date()
  timeDiff = currentTime.getTime() - startTime
  timeDiff = timeDiff + stopTime  if flagStop == 1
  if flagClock == 1
    $(clock).val formatTime timeDiff, ""
    callback = -> counter startTime
    refresh = setTimeout callback, 10
    window.clearTimeout refresh
    stopTime = timeDiff

formatTime = (rawTime, roundType) ->
  if roundType == "round"
    ds = Math.round(rawTime / 100) + ""
    ds = Math.floor(rawTime / 100) + ""
  sec = Math.floor(rawTime / 1000)
  min = Math.floor(rawTime / 60000)
  hour = Math.floor(rawTime / 3600000)
  ds = ds.charAt(ds.length - 1)
  start() if hour >= 24
  sec = sec - 60 * min + ""
  sec = prependZeroCheck sec
  min = min - 60 * hour + ""
  min = prependZeroCheck min
  hour = prependZeroCheck hour
  hour + ":" + min + ":" + sec + "." + ds

prependZeroCheck = (time) ->
  time = time + "" # convert from int to string
  unless time.charAt(time.length - 2) == ""
    time = time.charAt(time.length - 2) + time.charAt(time.length - 1)
    time = 0 + time.charAt(time.length - 1)

reset = ->
  flagStop = 0
  stopTime = 0
  window.clearTimeout refresh
  if flagClock == 1
    resetDate = new Date()
    resetTime = resetDate.getTime()
    counter resetTime
    $(clock).val "00:00:00.0"

@StopWatch = {
  start: start
  reset: reset

The Scalate/Jade template to render this stopwatch looks as follows:

script(type="text/javascript" src={uri("/public/javascripts/stopwatch.coffee")})

  input(id="clock" class="xlarge" type="text" value="00:00:00.0" readonly="readonly")
  button(id="start" type="button" class="btn primary") Start
  button(id="reset" type="button" class="btn :disabled") Reset

  <script type="text/coffeescript">
    $(document).ready ->
      $('#start').click ->
        StopWatch.start this, $('#clock')

      $('#reset').click ->

Next, I wanted to create a map that would show your location. For this, I used Merge Design's HTML 5 Geolocation Demo as a guide. The HTML5 Geo API is pretty simple, containing only three methods:

// Gets the users current position
// Request repeated updates of position
watchId = navigator.geolocation.watchPosition(successCallback, errorCallback);

// Cancel the updates

After rewriting the geolocation example in CoffeeScript, I ended up with the following code in my map.coffee script. You'll notice it uses Google Maps JavaScript API to show an actual map with a marker.

# Geolocation with HTML 5 and Google Maps API based on example from maxheapsize: 
# http://maxheapsize.com/2009/04/11/getting-the-browsers-geolocation-with-html-5/
# This script is by Merge Database and Design, http://merged.ca/ -- if you use some, 
# all, or any of this code, please offer a return link.

map = null
mapCenter = null
geocoder = null
latlng = null
timeoutId = null

initialize = ->
  if Modernizr.geolocation
    navigator.geolocation.getCurrentPosition showMap

showMap = (position) ->
  latitude = position.coords.latitude
  longitude = position.coords.longitude
  mapOptions = {
    zoom: 15,
    mapTypeId: google.maps.MapTypeId.ROADMAP
  map = new google.maps.Map(document.getElementById("map"), mapOptions)
  latlng = new google.maps.LatLng(latitude, longitude)

  geocoder = new google.maps.Geocoder()
  geocoder.geocode({'latLng': latlng}, addAddressToMap)

addAddressToMap = (results, status) ->
  if (status == google.maps.GeocoderStatus.OK) 
    if (results[1]) 
      marker = new google.maps.Marker({
          position: latlng,
          map: map
      $('#location').html('Your location: ' + results[0].formatted_address)
    alert "Sorry, we were unable to geocode that address."

start = ->
  timeoutId = setTimeout initialize, 500

reset = ->
  if (timeoutId)
    clearTimeout timeoutId

@Map = {
  start: start
  reset: reset

The template to show the map is a mere 20 lines of Jade:

script(type="text/javascript" src="http://www.google.com/jsapi")
script(type="text/javascript" src="http://maps.googleapis.com/maps/api/js?sensor=false")

  .demo-map {
    border: 1px solid silver;
    height: 200px;
    margin: 10px auto;
    width: 280px;


  span(class="label success") New
  | Fetching your location with HTML 5 geolocation...

script(type="text/javascript" src={uri("/public/javascripts/map.coffee")})

The last two features I wanted were 1) distance traveled and 2) drawing the route taken on the map. For this I learned from A Simple Trip Meter using the Geolocation API. As I was beginning to port the JS to CoffeeScript, I thought, "there's got to be a better way." I searched and found Js2coffee to do most of the conversion for me. If you know JavaScript and you're learning CoffeeScript, this is an invaluable tool.

I tried out the trip meter that night evening on a bike ride and noticed it said I'd traveled 3 miles when I'd really gone 6. I quickly figured out it was only calculating start point to end point and not taking into account all the turns in between. To view what was happening, I integrated my odometer.coffee with my map using Google Maps Polylines. Upon finishing the integration, I discovered two things, 1) HTML5 geolocation was highly inaccurate and 2) geolocation doesn't run in the background.

I was able to solve the first problem by passing in {enableHighAccuracy: true} to navigator.geolocation.watchPosition(). Below are two screenshots showing before high accuracy and after. Both screenshots are from the same two-block walk.

Without {enableHighAccuracy: true} With {enableHighAccuracy: true}

The second issue is a slight show-stopper. PhoneGap might be able to solve the problem, but I'm currently using a workaround → turning off auto-lock and keeping Safari in the foreground.

Making it look good
After I got all my desired features developed, I moved onto making the app look good. I started by using SASS for my CSS and installed Play's SASS module. I then switched to LESS when I discovered and added Twitter's Bootstrap to my project. At first I used Play's LESS module (version 0.3), but ran into compilation issues. I then tried Play's GreenScript module, but gave up on it when I found it was incompatible with the CoffeeScript module. Switching back to the LESS module and using the "0.3.compatibility" version solved all remaining issues.

You might remember that I integrated HTML5 Boilerplate and wondering why I have both Bootstrap and Boilerplate in my project. At this point, I don't think Boilerplate is needed, but I've kept it just in case it's doing something for HTML5 cross-browser compatibility. I've renamed its style.css to style.less and added the following so it has access to Bootstrap's variables.

/* Variables from Bootstrap */
@import "libs/variables.less";

Then I made my app look a lot better with layouts, stylish forms, a fixed topbar and alerts. For example, here's the CoffeeScript I wrote to display geolocation errors:

geolocationError = (error) ->
  msg = 'Unable to locate position. '
  switch error.code
    when error.TIMEOUT then msg += 'Timeout.'
    when error.POSITION_UNAVAILABLE then msg += 'Position unavailable.'
    when error.PERMISSION_DENIED then msg += 'Please turn on location services.'
    when error.UNKNOWN_ERROR then msg += error.code
  alert = $('<div class="alert-message error fade in" data-alert="alert">')
  alert.html('<a class="close" href="#">×</a>' + msg);

Then I set about styling up the app so it looked good on a smartphone with CSS3 Media Queries. Below is the LESS code I used to hide elements and squish the widths for smaller devices.

@media all and (max-device-width: 480px) {
  /* hide scrollbar on mobile */
  html { overflow-y:hidden }
  /* hide sidebar on mobile */
  .home .span4, .home .page-header, .topbar form {
    display: none
  .home .container {
    width: 320px;
  .about {
    .container, .span10 {
      width: 280px;
    .span10 {
      padding-top: 0px;

In the process of developing a stopwatch, odometer, displaying routes and making everything look good, I used a number of tools. I started out primarily with TextMate and its bundles for LESS, CoffeeScript and Jade. When I started writing more Scala, I installed the Scala TextMate Bundle. When I needed some debugging, I switched to IntelliJ and installed its Scala plugin. CoffeeScript, LESS and HAML plugins (for Jade) were already installed by default. I also used James Ward's Setup Play Framework with Scala in IntelliJ.

I think it's obvious that my biggest issue so far is the fact that a webapp can't multitask in the background like a native app can. Beyond that, there's accuracy issues with HTML5's geolocation that I haven't seen in native apps.

I also ran into a caching issue when calling getCurrentPosition(). It only worked the first time and I had to refresh my browser to get it to work again. Strangely enough, this only happened on my desktop (in Safari and Firefox) and worked fine on my iPhone. Unfortunately, it looks like PhoneGap has issues similar to this.

My workaround for no webapp multitasking is turning off auto-lock and leaving the browser in the foreground while I exercise. The downside to this is it really drains the battery quickly (~ 3 hours). I constantly have to charge my phone if I'm testing it throughout the day. The testing is a real pain too. I have to deploy to Heroku (which is easy enough), then go on a walk or bike ride. If something's broke, I have to return home, tweak some things, redeploy and go again. Also, there's been a few times where Safari crashes halfway through and I lose all the tracking data. This happens with native apps too, but seemingly not as often.

If you'd like to try the app on your mobile phone and see if you experience these issues, checkout play-more.com.

Going forward, there's still more HTML5 features I'd like to use. In particular, I'd like to play music while the fitness tracker is running. I'd love it if cloud music services (e.g. Pandora or Spotify) had an API I could use to play music in a webapp. Soundcloud might be an option, but I've also thought of just uploading some MP3s and playing them with the <audio> tag.

I've really enjoyed developing with all these technologies and haven't experienced much frustration so far. The majority has come from integrating Scalate into Play, but I've resolved most problems. Next, I'll talk about how I've improved Play's Scalate support and my experience working with Anorm.

Source: http://raibledesigns.com/rd/entry/developing_with_html5_coffeescript_and

Launch your application faster with Okta’s user management API. Register today for the free forever developer edition!


Opinions expressed by DZone contributors are their own.

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}