KnockOut! and SVG Example - StopWatch

This is an example of using KnockOut! with SVG. It was taken from an article by Jacob Gable on MVVM DataBinding Javascript with Knockout & HTML5 Boilerplate http://blogs.claritycon.com/blog/2011/02/20/mvvm-databinding-javascript-with-knockout-html5-boilerplate/

HTML

<script src="http://rniemeyer.github.com/KnockMeOut/Scripts/knockout-latest.debug.js"></script>
<script src="http://rniemeyer.github.com/KnockMeOut/Scripts/jquery.tmpl.js"></script>
<p> The original article by <a href="http://blogs.claritycon.com/blog/author/jgable/">Jacob Gable</a> that this was taken from can be found <a href="http://blogs.claritycon.com/blog/2011/02/20/mvvm-databinding-javascript-with-knockout-html5-boilerplate/">here</a>.
</p><br/>

<div id="watchContainer" style="cursor: pointer;" data-bind='template: { name: "faceTemplate", data: watch()}, click: toggleTimer'>
    
</div>

<script id="faceTemplate" type="text/x-jquery-tmpl">
    <div id="timerInfo" style="font-weight: bold; font-size: 24px; float: left;">
        <span class="minutes" data-bind="text: minutes()"></span>
        <span>:</span>
        <span class="seconds" data-bind="text: secondsDisplay()"></span>
    </div>
    <svg
    xmlns:svg="http://www.w3.org/2000/svg"
    xmlns="http://www.w3.org/2000/svg"
    xmlns:xlink="http://www.w3.org/1999/xlink"
    width="300"
    height="300"
    id="watchFace"
    version="1.1">
        
        <g
        id="layer1"
        transform="scale(5) translate(-18.25,-17.292185)">
            <path
            style="fill:none;stroke:#000000;stroke-width:3.5;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
            id="path2822"
            d="m 50,22.362183 c 0,11.045695 -8.954305,20 -20,20 -11.045695,0 -20,-8.954305 -20,-20 0,-11.045695 8.954305,-20.0000004 20,-20.0000004 11.045695,0 20,8.9543054 20,20.0000004 z"
            transform="translate(10,20)" />
            <path
            style="fill:none;stroke:#000000;stroke-width:0.63245553px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
            d="m 55,42.362183 -2,0"
            id="path3624"
            />
            
            <path
           ...

CSS

/**
* HTML5 ✰ Boilerplate
*
* style.css contains a reset, font normalization and some base styles.
*
* Credit is left where credit is due.
* Much inspiration was taken from these projects:
* - yui.yahooapis.com/2.8.1/build/base/base.css
* - camendesign.com/design/
* - praegnanz.de/weblog/htmlcssjs-kickstart
*/
/**
* html5doctor.com Reset Stylesheet (Eric Meyer's Reset Reloaded + HTML5 baseline)
* v1.6.1 2010-09-17 | Authors: Eric Meyer & Richard Clark
* html5doctor.com/html-5-reset-stylesheet/
*/

html, body, div, span, object, iframe,
h1, h2, h3, h4, h5, h6, p, blockquote, pre,
abbr, address, cite, code, del, dfn, em, img, ins, kbd, q, samp,
small, strong, sub, sup, var, b, i, dl, dt, dd, ol, ul, li,
fieldset, form, label, legend,
table, caption, tbody, tfoot, thead, tr, th, td,
article, aside, canvas, details, figcaption, figure,
footer, header, hgroup, menu, nav, section, summary,
time, mark, audio, video {
    margin: 0;
    padding: 0;
    border: 0;
    font-size: 100%;
    font: inherit;
    vertical-align: baseline;
}

article, aside, details, figcaption, figure,
footer, header, hgroup, menu, nav, section {
    display: block;
}

blockquote, q { quotes: none; }

blockquote:before, blockquote:after,
q:before, q:after { content: ''; content: none; }

ins { background-color: #ff9; color: #000; text-decoration: none; }

mark { background-color: #ff9; color: #000; font-style: italic; font-weight: bold; }

del { text-decoration: line-through; }

abbr[title], dfn[title] { border-bottom: 1px dotted; cursor: help; }

table { border-collapse: collapse; border-spacing: 0; }

hr { display: block; height: 1px; border: 0; border-top: 1px solid #ccc; margin: 1em 0; padding: 0; }

input, select { vertical-align: middle; }


/**
* Font normalization inspired by YUI Library's fonts.css: developer.yahoo.com/yui/
*/

body { font:13px/1.231 sans-serif; *font-size:small; } /* Hack retained to preserve specificity. */
select, input, textarea, button { font:99% sans-serif; }

/*...

JavaScript

/// The timer class.
var timer = function() {
    this.started = new ko.observable(false);
    this.totalSeconds = new ko.observable(0);

    this.seconds = new ko.dependentObservable(function() {
        return (this.totalSeconds() % 60).toFixed(0);
    }, this);

    this.secondsDisplay = new ko.dependentObservable(function() {
        var secs = this.seconds();
        var display = '' + secs;
        if (secs < 10) {
            display = '0' + secs;
        }

        // Hack for weird edge case because of setInterval.
        if (display == '010') {
            display = '10';
        }

        return display;
    }, this);

    this.minutes = new ko.dependentObservable(function() {
        return ((this.totalSeconds() / 60) % 60).toFixed(0);
    }, this);

    this.hours = new ko.dependentObservable(function() {
        return (((this.totalSeconds() / 60) / 60) % 60).toFixed(0);
    }, this);

    this.secondHandAngle = new ko.dependentObservable(function() {
        return this.seconds() * 6;
    }, this);

    this.minuteHandAngle = new ko.dependentObservable(function() {
        return this.minutes() * 6;
    }, this);

    this.hourHandAngle = new ko.dependentObservable(function() {
        return this.hours() * 6;
    }, this);

    this.alarm = function() {
        log('alarm fired');
    };
};

// timer.start
timer.prototype.start = function() {
    this.started(true);
    this.startTime = new Date();
    var self = this;

    this.intervalId = setInterval(function() {
        var oldTime = self.startTime;
        self.startTime = new Date();

        var diff = secondsBetween(self.startTime, oldTime);
        var currSeconds = self.totalSeconds();
        self.totalSeconds(currSeconds + diff);
    }, 100);
};

// timer.stop
timer.prototype.stop = function() {
    this.started(false);
    if (this.intervalId) {
        clearInterval(this.intervalId);
    }
};

// helper...


function secondsBetween(date1, date2) {
    return (date1.getTime() -...