PluginProbe
CoolClock / 3.2
CoolClock v3.2
4.3.8 trunk 0.1 2.0 2.9.8 3.0 3.1 3.2 3.3 4.0 4.1 4.2 4.3.3 4.3.4 4.3.5 4.3.6 4.3.7
coolclock / js / coolclock.js

coolclock.js in CoolClock 3.2, at js/coolclock.js

393 lines 14.1 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 /**
2 * CoolClock 3.0.1
3 *
4 Copyright (c) 2010-2013, Simon Baird.
5 All rights reserved.
6 *
7 * Applied path fix https://github.com/henrahmagix/CoolClock/commit/00d48a01b1aeadbeb0b7d167aca892de78659b76
8 * Added responsive styling by RavanH (lines 136/137) 26112014
9 *
10 Redistribution and use in source and binary forms, with or without
11 modification, are permitted provided that the following conditions are met:
12 - Redistributions of source code must retain the above copyright notice,
13 this list of conditions and the following disclaimer.
14 - Redistributions in binary form must reproduce the above copyright notice,
15 this list of conditions and the following disclaimer in the documentation
16 and/or other materials provided with the distribution.
17 - Neither the name Simon Baird nor the names of other contributors may be
18 used to endorse or promote products derived from this software without
19 specific prior written permission.
20 *
21 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
22 AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23 IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24 ARE DISCLAIMED. IN NO EVENT SHALL SIMON BAIRD BE LIABLE FOR ANY DIRECT,
25 INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
26 (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
27 LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
28 ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
30 THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31 *
32 * Display an analog clock using canvas.
33 * http://randomibis.com/coolclock/
34 *
35 */
36
37 // Constructor for CoolClock objects
38 window.CoolClock = function(options) {
39 return this.init(options);
40 }
41
42 // Config contains some defaults, and clock skins
43 CoolClock.config = {
44 tickDelay: 1000,
45 longTickDelay: 15000,
46 defaultRadius: 85,
47 renderRadius: 100,
48 defaultSkin: "chunkySwiss",
49 defaultFont: "15px sans-serif",
50
51 skins: {
52 // There are more skins in moreskins.js
53 // Try making your own skin by copy/pasting one of these and tweaking it
54 swissRail: {
55 outerBorder: { lineWidth: 2, radius:95, color: "black", alpha: 1 },
56 smallIndicator: { lineWidth: 2, startAt: 88, endAt: 92, color: "black", alpha: 1 },
57 largeIndicator: { lineWidth: 4, startAt: 79, endAt: 92, color: "black", alpha: 1 },
58 hourHand: { lineWidth: 8, startAt: -15, endAt: 50, color: "black", alpha: 1 },
59 minuteHand: { lineWidth: 7, startAt: -15, endAt: 75, color: "black", alpha: 1 },
60 secondHand: { lineWidth: 1, startAt: -20, endAt: 85, color: "red", alpha: 1 },
61 secondDecoration: { lineWidth: 1, startAt: 70, radius: 4, fillColor: "red", color: "red", alpha: 1 }
62 },
63 chunkySwiss: {
64 outerBorder: { lineWidth: 4, radius:97, color: "black", alpha: 1 },
65 smallIndicator: { lineWidth: 4, startAt: 89, endAt: 93, color: "black", alpha: 1 },
66 largeIndicator: { lineWidth: 8, startAt: 80, endAt: 93, color: "black", alpha: 1 },
67 hourHand: { lineWidth: 12, startAt: -15, endAt: 60, color: "black", alpha: 1 },
68 minuteHand: { lineWidth: 10, startAt: -15, endAt: 85, color: "black", alpha: 1 },
69 secondHand: { lineWidth: 4, startAt: -20, endAt: 85, color: "red", alpha: 1 },
70 secondDecoration: { lineWidth: 2, startAt: 70, radius: 8, fillColor: "red", color: "red", alpha: 1 }
71 },
72 chunkySwissOnBlack: {
73 outerBorder: { lineWidth: 4, radius:97, color: "white", alpha: 1 },
74 smallIndicator: { lineWidth: 4, startAt: 89, endAt: 93, color: "white", alpha: 1 },
75 largeIndicator: { lineWidth: 8, startAt: 80, endAt: 93, color: "white", alpha: 1 },
76 hourHand: { lineWidth: 12, startAt: -15, endAt: 60, color: "white", alpha: 1 },
77 minuteHand: { lineWidth: 10, startAt: -15, endAt: 85, color: "white", alpha: 1 },
78 secondHand: { lineWidth: 4, startAt: -20, endAt: 85, color: "red", alpha: 1 },
79 secondDecoration: { lineWidth: 2, startAt: 70, radius: 8, fillColor: "red", color: "red", alpha: 1 }
80 }
81
82 },
83
84 // Test for IE so we can nurse excanvas in a couple of places
85 isIE: !!document.all,
86
87 // Will store (a reference to) each clock here, indexed by the id of the canvas element
88 clockTracker: {},
89
90 // For giving a unique id to coolclock canvases with no id
91 noIdCount: 0
92 }
93
94 // Main tick handler. Refresh all the clocks then setup the next tick
95 CoolClock.tick = function() {
96
97 for (var clockId in CoolClock.config.clockTracker) {
98 var clock = CoolClock.config.clockTracker[clockId];
99 if (clock.stillHere() && clock.active) {
100 clock.refreshDisplay()
101 }
102 }
103 this.tickTimeout = setTimeout(CoolClock.tick, CoolClock.config.tickDelay);
104 };
105
106 // Define the CoolClock object's methods
107 CoolClock.prototype = {
108
109 // Initialise using the parameters parsed from the colon delimited class
110 init: function(options) {
111 // Parse and store the options
112 this.canvasId = options.canvasId;
113 this.skinId = options.skinId || CoolClock.config.defaultSkin;
114 this.font = options.font || CoolClock.config.defaultFont;
115 this.displayRadius = options.displayRadius || CoolClock.config.defaultRadius;
116 this.renderRadius = options.renderRadius || CoolClock.config.renderRadius;
117 this.showSecondHand = typeof options.showSecondHand == "boolean" ? options.showSecondHand : true;
118 this.gmtOffset = (options.gmtOffset != null && options.gmtOffset != '') ? parseFloat(options.gmtOffset) : null;
119 this.showDigital = typeof options.showDigital == "boolean" ? options.showDigital : false;
120 this.showDigital24 = typeof options.showDigital24 == "boolean" ? options.showDigital24 : false;
121 this.showDate = typeof options.showDate == "boolean" ? options.showDate : false;
122 this.logClock = typeof options.logClock == "boolean" ? options.logClock : false;
123 this.logClockRev = typeof options.logClock == "boolean" ? options.logClockRev : false;
124
125 this.tickDelay = CoolClock.config[ this.showSecondHand ? "tickDelay" : "longTickDelay" ];
126
127 // Get the canvas element
128 this.canvas = document.getElementById(this.canvasId);
129
130 // Make the canvas the requested size. It's always square.
131 this.canvas.setAttribute("width",this.displayRadius*2);
132 this.canvas.setAttribute("height",this.displayRadius*2);
133 this.canvas.style.width = this.displayRadius*2 + "px";
134 //this.canvas.style.height = this.displayRadius*2 + "px";
135 // some responsiveness please:
136 this.canvas.style.maxWidth = "100%";
137 this.canvas.style.height = "auto";
138
139 // Determine by what factor to relate skin values to canvas positions.
140 // renderRadius is the max skin positional value before leaving the
141 // canvas. displayRadius is half the width and height of the canvas in
142 // pixels. If they are equal, there is a 1:1 relation of skin position
143 // values to canvas pixels. Setting both to 200 allows 100px of space
144 // around clock skins to add your own things: this is due to current
145 // skins maxing out at a positional value of 100.
146 this.scale = this.displayRadius / this.renderRadius;
147
148 // Initialise canvas context
149 this.ctx = this.canvas.getContext("2d");
150 this.ctx.scale(this.scale,this.scale);
151
152 // Keep track of this object
153 CoolClock.config.clockTracker[this.canvasId] = this;
154
155 // Start the clock going
156 this.start();
157
158 return this;
159 },
160
161 // Draw a circle at point x,y with params as defined in skin
162 fullCircleAt: function(x,y,skin) {
163 this.ctx.save();
164 this.ctx.globalAlpha = skin.alpha;
165 this.ctx.lineWidth = skin.lineWidth;
166
167 if (CoolClock.config.isIE) {
168 // excanvas doesn't scale line width so we will do it here
169 this.ctx.lineWidth = this.ctx.lineWidth * this.scale;
170 }
171
172 this.ctx.beginPath();
173 this.ctx.arc(x, y, skin.radius, 0, 2*Math.PI, false);
174
175 if (CoolClock.config.isIE) {
176 // excanvas doesn't close the circle so let's fill in the tiny gap
177 this.ctx.arc(x, y, skin.radius, -0.1, 0.1, false);
178 }
179
180 if (skin.fillColor) {
181 this.ctx.fillStyle = skin.fillColor
182 this.ctx.fill();
183 }
184 if (skin.color) {
185 this.ctx.strokeStyle = skin.color;
186 this.ctx.stroke();
187 }
188 this.ctx.restore();
189 },
190
191 // Draw some text centered vertically and horizontally
192 drawTextAt: function(theText,x,y,skin) {
193 if (!skin) skin = this.getSkin();
194 this.ctx.save();
195 this.ctx.font = skin.font || this.font;
196 var tSize = this.ctx.measureText(theText);
197 // TextMetrics rarely returns a height property: use baseline instead.
198 if (!tSize.height) {
199 tSize.height = 0;
200 this.ctx.textBaseline = 'middle';
201 }
202 this.ctx.fillText(theText, x - tSize.width/2, y - tSize.height/2);
203 this.ctx.restore();
204 },
205
206 lpad2: function(num) {
207 return (num < 10 ? '0' : '') + num;
208 },
209
210 tickAngle: function(second) {
211 // Log algorithm by David Bradshaw
212 var tweak = 3; // If it's lower the one second mark looks wrong (?)
213 if (this.logClock) {
214 return second == 0 ? 0 : (Math.log(second*tweak) / Math.log(60*tweak));
215 }
216 else if (this.logClockRev) {
217 // Flip the seconds then flip the angle (trickiness)
218 second = (60 - second) % 60;
219 return 1.0 - (second == 0 ? 0 : (Math.log(second*tweak) / Math.log(60*tweak)));
220 }
221 else {
222 return second/60.0;
223 }
224 },
225
226 timeText: function(hour,min,sec,showAmPm,showSecs) {
227 return '' +
228 (showAmPm ? ((hour%12)==0 ? 12 : (hour%12)) : hour) + ':' +
229 this.lpad2(min) +
230 (showSecs ? ':' + this.lpad2(sec) : '') +
231 (showAmPm ? (hour < 12 ? ' am' : ' pm') : '')
232 ;
233 },
234
235 // Draw a radial line by rotating then drawing a straight line
236 // Ha ha, I think I've accidentally used Taus, (see http://tauday.com/)
237 radialLineAtAngle: function(angleFraction,skin) {
238 this.ctx.save();
239 this.ctx.translate(this.renderRadius,this.renderRadius);
240 this.ctx.rotate(Math.PI * (2.0 * angleFraction - 0.5));
241 this.ctx.globalAlpha = skin.alpha;
242 this.ctx.strokeStyle = skin.color;
243 this.ctx.lineWidth = skin.lineWidth;
244
245 if (CoolClock.config.isIE)
246 // excanvas doesn't scale line width so we will do it here
247 this.ctx.lineWidth = this.ctx.lineWidth * this.scale;
248
249 if (skin.radius) {
250 this.fullCircleAt(skin.startAt,0,skin)
251 }
252 else {
253 this.ctx.beginPath();
254 this.ctx.moveTo(skin.startAt,0)
255 this.ctx.lineTo(skin.endAt,0);
256 this.ctx.stroke();
257 }
258 this.ctx.restore();
259 },
260
261 render: function(hour,min,sec,date) {
262 // Get the skin
263 var skin = this.getSkin();
264
265 // Clear
266 this.ctx.clearRect(0,0,this.renderRadius*2,this.renderRadius*2);
267
268 // Draw the outer edge of the clock
269 if (skin.outerBorder)
270 this.fullCircleAt(this.renderRadius,this.renderRadius,skin.outerBorder);
271
272 // Draw the tick marks. Every 5th one is a big one
273 for (var i=0;i<60;i++) {
274 (i%5) && skin.smallIndicator && this.radialLineAtAngle(this.tickAngle(i),skin.smallIndicator);
275 !(i%5) && skin.largeIndicator && this.radialLineAtAngle(this.tickAngle(i),skin.largeIndicator);
276 }
277
278 // Write the time and or date
279 if (this.showDigital || this.showDigital24 || this.showDate) {
280 var text;
281 if (this.showDigital) text = this.timeText(hour,min,sec,true,false);
282 else if (this.showDigital24) text = this.timeText(hour,min,sec,false,true);
283 else text = date;
284 this.drawTextAt(
285 text,
286 this.renderRadius,
287 this.renderRadius+this.renderRadius/2
288 );
289 }
290
291 var hourA = (hour%12)*5 + min/12.0,
292 minA = min + sec/60.0,
293 secA = sec;
294
295 // Draw the hands
296 if (skin.hourHand)
297 this.radialLineAtAngle(this.tickAngle(hourA),skin.hourHand);
298
299 if (skin.minuteHand)
300 this.radialLineAtAngle(this.tickAngle(minA),skin.minuteHand);
301
302 if (this.showSecondHand && skin.secondHand)
303 this.radialLineAtAngle(this.tickAngle(secA),skin.secondHand);
304
305 // Hands decoration - not in IE
306 if (!CoolClock.config.isIE) {
307 if (skin.hourDecoration)
308 this.radialLineAtAngle(this.tickAngle(hourA), skin.hourDecoration);
309
310 if (skin.minDecoration)
311 this.radialLineAtAngle(this.tickAngle(minA), skin.minDecoration);
312
313 if (this.showSecondHand && skin.secondDecoration)
314 this.radialLineAtAngle(this.tickAngle(secA),skin.secondDecoration);
315 }
316
317 if (this.extraRender) {
318 this.extraRender(hour,min,sec);
319 }
320 },
321
322 // Check the time and display the clock
323 refreshDisplay: function() {
324 var now = new Date();
325 if (this.gmtOffset != null) {
326 // Use GMT + gmtOffset
327 var offsetNow = new Date(now.valueOf() + (this.gmtOffset * 1000 * 60 * 60));
328 this.render(offsetNow.getUTCHours(),offsetNow.getUTCMinutes(),offsetNow.getUTCSeconds(),offsetNow.toLocaleDateString());
329 }
330 else {
331 // Use local time
332 this.render(now.getHours(),now.getMinutes(),now.getSeconds(),now.toLocaleDateString());
333 }
334 },
335
336
337 // Check the canvas element hasn't been removed
338 stillHere: function() {
339 return document.getElementById(this.canvasId) != null;
340 },
341
342 // Stop this clock
343 stop: function() {
344 this.active = false;
345 },
346
347 // Start this clock
348 start: function() {
349 this.active = true;
350 },
351
352 getSkin: function() {
353 var skin = CoolClock.config.skins[this.skinId];
354 if (!skin) skin = CoolClock.config.skins[CoolClock.config.defaultSkin];
355 return skin;
356 }
357 };
358
359 // Find all canvas elements that have the CoolClock class and turns them into clocks
360 CoolClock.findAndCreateClocks = function() {
361 // (Let's not use a jQuery selector here so it's easier to use frameworks other than jQuery)
362 var canvases = document.getElementsByTagName("canvas");
363 for (var i=0;i<canvases.length;i++) {
364 // Pull out the fields from the class. Example "CoolClock:chunkySwissOnBlack:1000"
365 var fields = canvases[i].className.split(" ")[0].split(":");
366 if (fields[0] == "CoolClock") {
367 if (!canvases[i].id) {
368 // If there's no id on this canvas element then give it one
369 canvases[i].id = '_coolclock_auto_id_' + CoolClock.config.noIdCount++;
370 }
371 // Create a clock object for this element
372 new CoolClock({
373 canvasId: canvases[i].id,
374 skinId: fields[1],
375 displayRadius: fields[2],
376 showSecondHand: fields[3]!='noSeconds',
377 gmtOffset: fields[4],
378 showDigital: fields[5]=='showDigital',
379 showDigital24: fields[5]=='showDigital24',
380 showDate: fields[5]=='showDate',
381 logClock: fields[6]=='logClock',
382 logClockRev: fields[6]=='logClockRev'
383 });
384 }
385 }
386 // Start ticking
387 CoolClock.tick();
388 };
389
390 // If you don't have jQuery then you need a body onload like this: <body onload="CoolClock.findAndCreateClocks()">
391 // If you do have jQuery and it's loaded already then we can do it right now
392 if (window.jQuery) jQuery(document).ready(CoolClock.findAndCreateClocks);
393