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() -...