/** * CoolClock 3.2.2 * Copyright (c) 2010-2013, Simon Baird. All rights reserved. * * Applied path fix https://github.com/henrahmagix/CoolClock/commit/00d48a01b1aeadbeb0b7d167aca892de78659b76 * Added responsive styling by RavanH (lines 136/137) 26112014 * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - 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. - 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. * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL SIMON BAIRD BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * Display an analog clock using canvas. * http://randomibis.com/coolclock/ * Based on version 3.1.0 by Simon Baird. Modified for WordPress CoolClock plugin by RavanH 2020 Version 3.2.0 - added showDigital24,showSecs,showDate,showText,font,fontColor&text parameters 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. */ // Constructor for CoolClock objects window.CoolClock = function(options) { return this.init(options); } // Config contains some defaults, and clock skins CoolClock.config = { tickDelay: 1000, //longTickDelay: 15000, defaultRadius: 85, renderRadius: 100, defaultColor: "black", defaultAlpha: 1, defaultFont: "15px monospace", defaultFontColor: "#333", /* * SKIN ELEMENTS * outerBorder - static element, can be used to draw an outer circle or solid background (requires radius) * largeIndicator - hour markers, 12 static elements devided around the clock scale * smallIndicator - minute markers, 48 static elements devided around the clock scale between the hour markers * hourHand - element rotating in 12 hours * minuteHand - element rotating in 1 hour * secondHand - element rotating in 1 minute * secondDecoration - additional decoration element, rotating in 1 minute * * ELEMENTS PARAMETERS * lineWidth - width of line drawn (default 0) * color - color of line drawn (default black) * startAt - starting point from center, can be negative (default 0) * endAt - end point from center (default 0) * radius - transform line to circle with this radius around startAt as center (endAt will be ignored) * fillColor - collor to fill circle (ignored when radius not used) * alpha - transparency value (default 1) */ skin: { /* similar to swissrail but solid white background */ outerBorder: { lineWidth: 2, radius: 95, fillColor: "white" }, smallIndicator: { lineWidth: 2, startAt: 88, endAt: 92 }, largeIndicator: { lineWidth: 4, startAt: 79, endAt: 92 }, hourHand: { lineWidth: 8, startAt: -15, endAt: 50 }, minuteHand: { lineWidth: 7, startAt: -15, endAt: 75 }, secondHand: { lineWidth: 1, startAt: -20, endAt: 85, color: "red" }, secondDecoration: { lineWidth: 1, startAt: 70, radius: 4, fillColor: "red", color: "red" } }, // Can store skin objects by skinId reference (see moreskins.js) skins: {}, // Test for IE so we can nurse excanvas in a couple of places isIE: !!document.all, // Will store (a reference to) each clock here, indexed by the id of the canvas element clockTracker: {}, // For giving a unique id to coolclock canvases with no id noIdCount: 0 } // Main tick handler. Refresh all the clocks then setup the next tick CoolClock.tick = function() { // TODO switch to CoolClock.config.longTickDelay if none of the clocks show second hand or digital time with seconds, or dynamicSkin is set // can be useful for large number of clocks to reduce CPU usage with possible skips in second hand movement this.tickTimeout = setTimeout(CoolClock.tick, CoolClock.config.tickDelay); for (var clockId in CoolClock.config.clockTracker) { var clock = CoolClock.config.clockTracker[clockId]; if ( clock.stillHere() && clock.active && ! clock.busy ) { clock.refreshDisplay() } } }; // Define the CoolClock object's methods CoolClock.prototype = { // Initialise using the parameters parsed from the colon delimited class init: function(options) { // Parse and store the options this.canvasId = options.canvasId; this.skinId = options.skinId; this.font = options.font || CoolClock.config.defaultFont; this.fontColor = options.fontColor || CoolClock.config.defaultFontColor; this.displayRadius = options.displayRadius || CoolClock.config.defaultRadius; this.renderRadius = options.renderRadius || CoolClock.config.renderRadius; this.showSecondHand = typeof options.showSecondHand == "boolean" ? options.showSecondHand : true; this.gmtOffset = (options.gmtOffset != null && options.gmtOffset != '') ? parseFloat(options.gmtOffset) : null; this.showDigital = typeof options.showDigital == "boolean" ? options.showDigital : false; this.showDigital24 = typeof options.showDigital24 == "boolean" ? options.showDigital24 : false; this.showSecs = typeof options.showSecs == "boolean" ? options.showSecs : false; this.showDate = typeof options.showDate == "boolean" ? options.showDate : false; this.showText = typeof options.showText == "boolean" ? options.showText : false; this.logClock = typeof options.logClock == "boolean" ? options.logClock : false; this.logClockRev = typeof options.logClock == "boolean" ? options.logClockRev : false; this.text = options.text || ''; this.dynamicSkin = typeof options.dynamicSkin == "boolean" ? options.dynamicSkin : false; // Get the canvas element this.canvas = document.getElementById(this.canvasId); // Make the canvas the requested size. It's always square. this.canvas.setAttribute("width", this.displayRadius * 2); this.canvas.setAttribute("height", this.displayRadius * 2); this.canvas.style.width = this.displayRadius * 2 + "px"; //this.canvas.style.height = this.displayRadius * 2 + "px"; // some responsiveness please: this.canvas.style.maxWidth = "100%"; this.canvas.style.height = "auto"; // Determine by what factor to relate skin values to canvas positions. // renderRadius is the max skin positional value before leaving the // canvas. displayRadius is half the width and height of the canvas in // pixels. If they are equal, there is a 1:1 relation of skin position // values to canvas pixels. Setting both to 200 allows 100px of space // around clock skins to add your own things: this is due to current // skins maxing out at a positional value of 100. this.scale = this.displayRadius / this.renderRadius; // Initialise canvas context this.ctx = this.canvas.getContext("2d"); this.ctx.scale(this.scale, this.scale); // Keep track of this object CoolClock.config.clockTracker[this.canvasId] = this; // Start the clock going this.start(); return this; }, // Draw a circle at point x,y with params as defined in skin fullCircleAt: function(x, y, skin) { this.ctx.save(); this.ctx.globalAlpha = skin.alpha || CoolClock.config.defaultAlpha; this.ctx.strokeStyle = skin.color || CoolClock.config.defaultColor; this.ctx.lineWidth = skin.lineWidth; if (CoolClock.config.isIE) { // excanvas doesn't scale line width so we will do it here this.ctx.lineWidth = this.ctx.lineWidth * this.scale; } this.ctx.beginPath(); this.ctx.arc(x, y, skin.radius, 0, 2 * Math.PI, false); if (CoolClock.config.isIE) { // excanvas doesn't close the circle so let's fill in the tiny gap this.ctx.arc(x, y, skin.radius, -0.1, 0.1, false); } if (skin.fillColor) { this.ctx.fillStyle = skin.fillColor this.ctx.fill(); } this.ctx.stroke(); this.ctx.restore(); }, // Draw some text centered vertically and horizontally drawTextAt: function(theText, x, y, skin) { if (!skin) skin = this.getSkin(); this.ctx.save(); this.ctx.font = skin.font || this.font; var tSize = this.ctx.measureText(theText); // TextMetrics rarely returns a height property: use baseline instead. if (!tSize.height) { tSize.height = 0; this.ctx.textBaseline = 'middle'; } this.ctx.fillStyle = skin.fontColor || this.fontColor; this.ctx.fillText(theText, x - tSize.width / 2, y - tSize.height / 2); this.ctx.restore(); }, lpad2: function(num) { return (num < 10 ? '0' : '') + num; }, tickAngle: function(second) { // Log algorithm by David Bradshaw var tweak = 3; // If it's lower the one second mark looks wrong (?) if (this.logClock) { return second == 0 ? 0 : (Math.log(second * tweak) / Math.log(60 * tweak)); } else if (this.logClockRev) { // Flip the seconds then flip the angle (trickiness) second = (60 - second) % 60; return 1.0 - (second == 0 ? 0 : (Math.log(second * tweak) / Math.log(60 * tweak))); } else { return second / 60.0; } }, timeText: function(hour, min, sec, showAmPm, showSecs) { return '' + (showAmPm ? ((hour % 12) == 0 ? 12 : (hour % 12)) : hour) + ':' + this.lpad2(min) + (showSecs ? ':' + this.lpad2(sec) : '') + (showAmPm ? (hour < 12 ? ' am' : ' pm') : '') ; }, // Draw a radial line by rotating then drawing a straight line // Ha ha, I think I've accidentally used Taus, (see http://tauday.com/) radialLineAtAngle: function(angleFraction, skin) { this.ctx.save(); this.ctx.translate(this.renderRadius, this.renderRadius); this.ctx.rotate(Math.PI * (2.0 * angleFraction - 0.5)); if (CoolClock.config.isIE) // excanvas doesn't scale line width so we will do it here this.ctx.lineWidth = this.ctx.lineWidth * this.scale; if (skin.radius) { this.fullCircleAt(skin.startAt, 0, skin) } else { this.ctx.globalAlpha = skin.alpha || CoolClock.config.defaultAlpha; this.ctx.strokeStyle = skin.color || CoolClock.config.defaultColor; this.ctx.lineWidth = skin.lineWidth; this.ctx.beginPath(); this.ctx.moveTo(skin.startAt, 0) this.ctx.lineTo(skin.endAt, 0); this.ctx.stroke(); } this.ctx.restore(); }, render: function(hour, min, sec, date) { // Get the skin var skin = this.getSkin(); // Clear this.ctx.clearRect(0, 0, this.renderRadius * 2, this.renderRadius * 2); // Draw the outer edge of the clock if (skin.outerBorder) this.fullCircleAt(this.renderRadius, this.renderRadius, skin.outerBorder); // Draw the tick marks. Every 5th one is a big one for (var i = 0; i < 60; i++) { (i % 5) && skin.smallIndicator && this.radialLineAtAngle(this.tickAngle(i), skin.smallIndicator); !(i % 5) && skin.largeIndicator && this.radialLineAtAngle(this.tickAngle(i), skin.largeIndicator); } // Write the time and or date if (this.showDigital || this.showDigital24 || this.showDate || this.showText) { var text = this.showDigital || this.showDigital24 ? this.timeText(hour, min, sec, !this.showDigital24, this.showSecs) : this.showDate ? date : this.text; this.drawTextAt( text, this.renderRadius, this.renderRadius + this.renderRadius / 2, skin ); } var hourA = (hour % 12) * 5 + min / 12.0, minA = min + sec / 60.0, secA = sec; // Draw the hands if (skin.hourHand) this.radialLineAtAngle(this.tickAngle(hourA), skin.hourHand); if (skin.minuteHand) this.radialLineAtAngle(this.tickAngle(minA), skin.minuteHand); if (this.showSecondHand && skin.secondHand) this.radialLineAtAngle(this.tickAngle(secA), skin.secondHand); // Hands decoration - not in IE if (!CoolClock.config.isIE) { if (skin.hourDecoration) this.radialLineAtAngle(this.tickAngle(hourA), skin.hourDecoration); if (skin.minDecoration) this.radialLineAtAngle(this.tickAngle(minA), skin.minDecoration); if (this.showSecondHand && skin.secondDecoration) this.radialLineAtAngle(this.tickAngle(secA), skin.secondDecoration); } }, // Check the time and display the clock refreshDisplay: function() { this.busy = true; var now = new Date(); if (this.gmtOffset != null) { // Use GMT + gmtOffset var offsetNow = new Date(now.valueOf() + (this.gmtOffset * 1000 * 60 * 60)); this.render(offsetNow.getUTCHours(), offsetNow.getUTCMinutes(), offsetNow.getUTCSeconds(), offsetNow.toLocaleDateString()); } else { // Use local time this.render(now.getHours(), now.getMinutes(), now.getSeconds(), now.toLocaleDateString()); } this.busy = false; }, // Check the canvas element hasn't been removed stillHere: function() { return document.getElementById(this.canvasId) != null; }, // Stop this clock stop: function() { this.active = false; }, // Start this clock start: function() { this.active = true; }, getSkin: function() { if ( ! this.skin || this.dynamicSkin ) { var skin = CoolClock.config.skins[this.skinId]; if ( typeof skin === 'undefined' || skin.length === 0 ) skin = CoolClock.config.skin; this.skin = skin; } return this.skin; } }; // Find all canvas elements that have the CoolClock class and turns them into clocks CoolClock.findAndCreateClocks = function() { // (Let's not use a jQuery selector here so it's easier to use frameworks other than jQuery) var canvases = document.getElementsByTagName("canvas"); for (var i = 0; i < canvases.length; i++) { // Pull out the fields from the class. Example "CoolClock:chunkySwissOnBlack:1000" var fields = canvases[i].className.split(" ")[0].split(":"); if (fields[0] == "CoolClock") { if (!canvases[i].id) { // If there's no id on this canvas element then give it one canvases[i].id = '_coolclock_auto_id_' + CoolClock.config.noIdCount++; } // Create a clock object for this element new CoolClock({ canvasId: canvases[i].id, skinId: fields[1], displayRadius: fields[2], showSecondHand: fields[3] != 'noSeconds', gmtOffset: fields[4], showDigital: fields[5] == 'showDigital' || fields[5] == 'showDigital+Secs', showDigital24: fields[5] == 'showDigital24' || fields[5] == 'showDigital24+Secs', showSecs: fields[5].indexOf('+Secs', 10) != -1, showDate: fields[5] == 'showDate', showText: fields[5] == 'showText', logClock: fields[6] == 'logClock', logClockRev: fields[6] == 'logClockRev', fontColor: fields[7], font: fields[8] ? fields[8].replace(/\|/g, ' ') : null, text: fields[9] ? fields[9].replace(/\|/g, ' ') : null, dynamicSkin: fields[10] == 'dynamicSkin' }); } } // Start ticking CoolClock.tick(); }; // If you don't have jQuery then you need a body onload like this: // If you do have jQuery and it's loaded already then we can do it right now //if (window.jQuery) jQuery(document).ready(CoolClock.findAndCreateClocks);