# coolclock/4.3.4/js/coolclock.js

CoolClock, version 4.3.4. 401 lines.

- Page: https://pluginprobe.com/plugins/coolclock/4.3.4/code/js/coolclock.js
- Raw: https://pluginprobe.com/plugins/coolclock/4.3.4/raw/js/coolclock.js
- Modified: 2020-07-13T17:56:14+00:00

Line numbers below start at 1. Link to a line or a range by appending a fragment to the
page URL, for example `https://pluginprobe.com/plugins/coolclock/4.3.4/code/js/coolclock.js#L10-L20`.

```javascript
/**
* 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: <body onload="CoolClock.findAndCreateClocks()">
// 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);

```
