PluginProbe
CoolClock / 4.3.9
CoolClock v4.3.9
4.3.9 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 4.3.9, at js/coolclock.js

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