Build a 'Cooler' Cool Clock!

I saw a forum post asking about creating custom 'World Clocks', so I figured I would write a quick guide for those of you who would like to do the same.

First of all, what is the 'World clock?' It is a Homepage UI item that we included with the default configuration as a sample of some of the cool things users could do in Service-now. You access it by selecting 'Add content >>' on a Homepage view. Select the 'World Clocks' > 'World Clocks' from the select menu and add it to a section of your Homepage:

The default 'World Clock' looks like this (in Firefox):

Not very pretty, right? Well that is because we just wanted to give you a taste of what you can do!

The code that creates those clocks is an open source Java script that is found in a UI Script called 'CoolClock', here is that code:

window.CoolClock = function(canvasId,displayRadius,skinId,showSecondHand,gmtOffset) {
	return this.init(canvasId,displayRadius,skinId,showSecondHand,gmtOffset);
}
 
CoolClock.findAndCreateClocks = function() {
	var canvases = document.getElementsByTagName("canvas");
	for (var i=0;i<canvases.length;i++) {
		var fields = canvases[i].className.split(" ")[0].split(":");
		if (fields[0] == "CoolClock") {
			new CoolClock(canvases[i].id,fields[2],fields[1],fields[3]!="noSeconds",fields[4]);
		}
	}
}
 
CoolClock.config = {
	clockTracker: {},
	tickDelay: 1000,
	longTickDelay: 15000,
	defaultRadius: 85,
	renderRadius: 100,
	defaultSkin: "swissRail",
	skins:	{
		// try making your own...
 
		swissRail: {
			outerBorder: { lineWidth: 1, radius:95, color: "black", alpha: 1 },
			smallIndicator: { lineWidth: 2, startAt: 89, endAt: 93, color: "black", alpha: 1 },
			largeIndicator: { lineWidth: 4, startAt: 80, endAt: 93, color: "black", alpha: 1 },
			hourHand: { lineWidth: 8, startAt: -15, endAt: 50, color: "black", alpha: 1 },
			minuteHand: { lineWidth: 7, startAt: -15, endAt: 75, color: "black", alpha: 1 },
			secondHand: { lineWidth: 1, startAt: -20, endAt: 85, color: "red", alpha: 1 },
			secondDecoration: { lineWidth: 1, startAt: 70, radius: 4, fillColor: "red", color: "red", alpha: 1 }
		},
 
		swissRailPM: {
			outerBorder: { lineWidth: 1, radius:95, color: "white", fillColor: "black", alpha: 1 },
			smallIndicator: { lineWidth: 2, startAt: 89, endAt: 93, color: "white", alpha: 1 },
			largeIndicator: { lineWidth: 4, startAt: 80, endAt: 93, color: "white", alpha: 1 },
			hourHand: { lineWidth: 8, startAt: -15, endAt: 50, color: "white", alpha: 1 },
			minuteHand: { lineWidth: 7, startAt: -15, endAt: 75, color: "white", alpha: 1 },
			secondHand: { lineWidth: 1, startAt: -20, endAt: 85, color: "red", alpha: 1 },
			secondDecoration: { lineWidth: 1, startAt: 70, radius: 4, fillColor: "red", color: "red", alpha: 1 }
		},
		chunkySwiss: {
			outerBorder: { lineWidth: 4, radius:97, color: "black", alpha: 1 },
			smallIndicator: { lineWidth: 4, startAt: 89, endAt: 93, color: "black", alpha: 1 },
			largeIndicator: { lineWidth: 8, startAt: 80, endAt: 93, color: "black", alpha: 1 },
			hourHand: { lineWidth: 12, startAt: -15, endAt: 60, color: "black", alpha: 1 },
			minuteHand: { lineWidth: 10, startAt: -15, endAt: 85, color: "black", alpha: 1 },
			secondHand: { lineWidth: 4, startAt: -20, endAt: 85, color: "red", alpha: 1 },
			secondDecoration: { lineWidth: 2, startAt: 70, radius: 8, fillColor: "red", color: "red", alpha: 1 }
		},
		fancy: {
			outerBorder: { lineWidth: 5, radius:95, color: "green", alpha: 0.7 },
			smallIndicator: { lineWidth: 1, startAt: 80, endAt: 93, color: "black", alpha: 0.4 },
			largeIndicator: { lineWidth: 1, startAt: 30, endAt: 93, color: "black", alpha: 0.5 },
			hourHand: { lineWidth: 8, startAt: -15, endAt: 50, color: "blue", alpha: 0.7 },
			minuteHand: { lineWidth: 7, startAt: -15, endAt: 92, color: "red", alpha: 0.7 },
			secondHand: { lineWidth: 10, startAt: 80, endAt: 85, color: "blue", alpha: 0.3 },
			secondDecoration: { lineWidth: 1, startAt: 30, radius: 50, fillColor: "blue", color: "red", alpha: 0.15 }
		},
		machine: {
			outerBorder: { lineWidth: 60, radius:55, color: "#dd6655", alpha: 1 },
			smallIndicator: { lineWidth: 4, startAt: 80, endAt: 95, color: "white", alpha: 1 },
			largeIndicator: { lineWidth: 14, startAt: 77, endAt: 92, color: "#dd6655", alpha: 1 },
			hourHand: { lineWidth: 18, startAt: -15, endAt: 40, color: "white", alpha: 1 },
			minuteHand: { lineWidth: 14, startAt: 24, endAt: 100, color: "#771100", alpha: 0.5 },
			secondHand: { lineWidth: 3, startAt: 22, endAt: 83, color: "green", alpha: 0 },
			secondDecoration: { lineWidth: 1, startAt: 52, radius: 26, fillColor: "#ffcccc", color: "red", alpha: 0.5 }
		},
 
		// these three created by bonstio from http://bonstio(.)net
		classic/*was gIG*/: {
			outerBorder: { lineWidth: 185, radius:1, color: "#E5ECF9", alpha: 1 },
			smallIndicator: { lineWidth: 2, startAt: 89, endAt: 94, color: "#3366CC", alpha: 1 },
			largeIndicator: { lineWidth: 4, startAt: 83, endAt: 94, color: "#3366CC", alpha: 1 },
			hourHand: { lineWidth: 5, startAt: 0, endAt: 60, color: "black", alpha: 1 },
			minuteHand: { lineWidth: 4, startAt: 0, endAt: 80, color: "black", alpha: 1 },
			secondHand: { lineWidth: 1, startAt: -20, endAt: 85, color: "red", alpha: .85 },
			secondDecoration: { lineWidth: 3, startAt: 0, radius: 2, fillColor: "black", color: "black", alpha: 1 }
		},
 
		modern/*was gIG2*/: {
			outerBorder: { lineWidth: 185, radius:1, color: "#E5ECF9", alpha: 1 },
			smallIndicator: { lineWidth: 5, startAt: 88, endAt: 94, color: "#3366CC", alpha: 1 },
			largeIndicator: { lineWidth: 5, startAt: 88, endAt: 94, color: "#3366CC", alpha: 1 },
			hourHand: { lineWidth: 8, startAt: 0, endAt: 60, color: "black", alpha: 1 },
			minuteHand: { lineWidth: 8, startAt: 0, endAt: 80, color: "black", alpha: 1 },
			secondHand: { lineWidth: 5, startAt: 80, endAt: 85, color: "red", alpha: .85 },
			secondDecoration: { lineWidth: 3, startAt: 0, radius: 4, fillColor: "black", color: "black", alpha: 1 }
		},
 
		simple/*was gIG3*/: {
			outerBorder: { lineWidth: 185, radius:1, color: "#E5ECF9", alpha: 1 },
			smallIndicator: { lineWidth: 10, startAt: 90, endAt: 94, color: "#3366CC", alpha: 1 },
			largeIndicator: { lineWidth: 10, startAt: 90, endAt: 94, color: "#3366CC", alpha: 1 },
			hourHand: { lineWidth: 8, startAt: 0, endAt: 60, color: "black", alpha: 1 },
			minuteHand: { lineWidth: 8, startAt: 0, endAt: 80, color: "black", alpha: 1 },
			secondHand: { lineWidth: 5, startAt: 80, endAt: 85, color: "red", alpha: .85 },
			secondDecoration: { lineWidth: 3, startAt: 0, radius: 4, fillColor: "black", color: "black", alpha: 1 }
		}
 
	}
};
 
CoolClock.prototype = {
	init: function(canvasId,displayRadius,skinId,showSecondHand,gmtOffset) {
		this.canvasId = canvasId;
		this.displayRadius = displayRadius || CoolClock.config.defaultRadius;
		this.skinId = skinId || CoolClock.config.defaultSkin;
		this.showSecondHand = typeof showSecondHand == "boolean" ? showSecondHand : true;
		this.tickDelay = CoolClock.config[ this.showSecondHand ? "tickDelay" : "longTickDelay"];
 
		this.canvas = document.getElementById(canvasId);
 
		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";
 
		this.renderRadius = CoolClock.config.renderRadius; 
 
		this.scale = this.displayRadius / this.renderRadius;
		this.ctx = this.canvas.getContext("2d");
		this.ctx.scale(this.scale,this.scale);
 
		this.gmtOffset = gmtOffset != null ? parseFloat(gmtOffset) : gmtOffset;
 
		CoolClock.config.clockTracker[canvasId] = this;
		this.tick();
		return this;
	},
 
	fullCircle: function(skin) {
		this.fullCircleAt(this.renderRadius,this.renderRadius,skin);
	},
 
	fullCircleAt: function(x,y,skin) {
		with (this.ctx) {
			save();
			globalAlpha = skin.alpha;
			lineWidth = skin.lineWidth;
			if (document.all)
				// excanvas doesn't scale line width so we will do it here
				lineWidth = lineWidth * this.scale;
			arc(x, y, skin.radius, 0, 2*Math.PI, false);
			if (document.all)
				// excanvas doesn't close the circle so let's color in the gap
				arc(x, y, skin.radius, -0.1, 0.1, false);
			if (skin.fillColor) {
				fillStyle = skin.fillColor
				fill();
			}
			else {
				// XXX why not stroke and fill
				strokeStyle = skin.color;
				stroke();
			}
			restore();
		}
	},
 
	radialLineAtAngle: function(angleFraction,skin) {
		with (this.ctx) {
			save();
			translate(this.renderRadius,this.renderRadius);
			rotate(Math.PI * (2 * angleFraction - 0.5));
			globalAlpha = skin.alpha;
			strokeStyle = skin.color;
			lineWidth = skin.lineWidth;
			if (document.all)
				// excanvas doesn't scale line width so we will do it here
				lineWidth = lineWidth * this.scale;
			if (skin.radius) {
				this.fullCircleAt(skin.startAt,0,skin)
			}
			else {
				beginPath();
				moveTo(skin.startAt,0)
				lineTo(skin.endAt,0);
				stroke();
			}
			restore();
		}
	},
 
	render: function(hour,min,sec) {
		var skin = CoolClock.config.skins[this.skinId];
		this.ctx.clearRect(0,0,this.renderRadius*2,this.renderRadius*2);
 
		this.fullCircle(skin.outerBorder);
 
		for (var i=0;i<60;i++)
			this.radialLineAtAngle(i/60,skin[ i%5 ? "smallIndicator" : "largeIndicator"]);
 
		this.radialLineAtAngle((hour+min/60)/12,skin.hourHand);
		this.radialLineAtAngle((min+sec/60)/60,skin.minuteHand);
		if (this.showSecondHand) {
			this.radialLineAtAngle(sec/60,skin.secondHand);
			if (!document.all)
				// decoration doesn't render right in IE so lets turn it off
				this.radialLineAtAngle(sec/60,skin.secondDecoration);
		}
	},
 
 
	nextTick: function() {
		setTimeout("CoolClock.config.clockTracker['"+this.canvasId+"'].tick()",this.tickDelay);
	},
 
	stillHere: function() {
		return document.getElementById(this.canvasId) != null;
	},
 
	refreshDisplay: function() {
		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());
		}
		else {
			// use local time
			this.render(now.getHours(),now.getMinutes(),now.getSeconds());
		}
	},
 
	tick: function() {
		if (this.stillHere()) {
			this.refreshDisplay()
			this.nextTick();
		}
	}
}
 
_addLoadEvent(CoolClock.findAndCreateClocks);

The code that displays those clocks in the Homepage gauge is found in a UI Page called 'example_cool_clock', here is the default code:

<j:if test="${RP.isPreview()}">
 <center>No preview available</center>
</j:if>
 
<j:if test="${!RP.isPreview()}">
  <script language="javascript" src="scripts/excanvas.jsx?v=${gs.getProperty('glide.builddate')}"/>
  <script language="javascript" src="CoolClock.jsdbx?v=${gs.getProperty('glide.builddate')}"/>
 
  <!-- works on IE, but draws different -->
  <table><tr>
    <td align="center"><div><canvas id="x1" class="CoolClock:swissRail:30::-8 leftRightPad"></canvas></div>San Diego</td>
    <td align="center"><div><canvas id="x2" class="CoolClock::30:noSeconds:-45leftRightPad"></canvas></div>New York</td>
    <td align="center"><div><canvas id="x3" class="CoolClock::30:noSeconds:+0 leftRightPad"></canvas></div>London</td>
    <td align="center"><div><canvas id="x4" class="CoolClock:swissRailPM:30:noSeconds:+2 leftRightPad"></canvas></div>Kiev</td>
  </tr></table>
</j:if>

What makes that UI Page appear in the 'Add content >>' box on a Homepage is a script found in a Widget, called 'World Clocks', here is that code:

function sections() {
    return {
      'World Clocks' : { 'name' : 'example_cool_clock' }
    };
}
 
function render() {
  var name = renderer.getPreferences().get("name")
  var gf = new GlideForm(renderer.getGC(), name, 0);
  gf.setDirect(true);
  gf.setRenderProperties(renderer.getRenderProperties());
  return gf.getRenderedPage();
}
 
function getEditLink() {
  return "sys_ui_page.do?sysparm_query=name=" + renderer.getPreferences().get("name");
}

This guide will show you the step by step directions on how to make your 'World Clocks' look like this:

If you want to tweak the code to customize even further, you can do that in the UI Script, but you will be on your own. If you do update the UI Script, and your clocks are not displaying properly, you may need to follow this guide, step by step, in order to get your clocks to appear correctly.

One final note, I do not believe this code automatically accounts for Daylight Savings Time, the calculations are off GMT, so you may have to manually update, although I am sure there is something out there that can automate that process too... If anybody knows of what that is, please send me the info and I will update the guide.

Thanks, and enjoy!

Update UI Script 'CoolClock'

The first step is to update the UI Script. You can find it under the System UI application in the UI Script module. There are some changes made to the code, which I won't go into, but you can browse through the script on this page and compare it to the script in your instance, or on the first page of this guide.

This is the area where somebody familiar with scripting can really go in and customize the look and feel of your clocks. A change that I made was to define that the clocks should alternate between blue and yellow at 6am and 6 pm, that way it was easy to see if that time zone was 'online' or not.

Here is the script to copy and paste into your UI Script 'CoolClock' Script field:

window.CoolClock = function(canvasId,displayRadius,skinId,showSecondHand,gmtOffset) {
	return this.init(canvasId,displayRadius,skinId,showSecondHand,gmtOffset);
}
 
CoolClock.findAndCreateClocks = function() {
	var canvases = document.getElementsByTagName("canvas");
	for (var i=0;i<canvases.length;i++) {
		var fields = canvases[i].className.split(" ")[0].split(":");
		if (fields[0] == "CoolClock") {
			new CoolClock(canvases[i].id,fields[2],fields[1],fields[3]!="noSeconds",fields[4]);
		}
	}
}
 
// borrowed from behaviour.js
// actually doesn't work right unless it's at the end of html document
// hence can't have a body onload
// this is a bug
// maybe have a setTimeout hack??
CoolClock.addLoadEvent = function(func){
	var oldonload = window.onload;
	if (typeof window.onload != 'function')
		window.onload = func;
	else
		window.onload = function() {
			oldonload();
			func();
		}
}
 
CoolClock.config = {
	clockTracker: {},
	tickDelay: 1000,
	longTickDelay: 15000,
	defaultRadius: 85,
	renderRadius: 100,
	defaultSkin: "modern",
	skins:	{
		// try making your own...
 
		swissRail: {
			outerBorder: { lineWidth: 1, radius:95, color: "black", alpha: 1 },
			smallIndicator: { lineWidth: 2, startAt: 89, endAt: 93, color: "black", alpha: 1 },
			largeIndicator: { lineWidth: 4, startAt: 80, endAt: 93, color: "black", alpha: 1 },
			hourHand: { lineWidth: 8, startAt: -15, endAt: 50, color: "black", alpha: 1 },
			minuteHand: { lineWidth: 7, startAt: -15, endAt: 75, color: "black", alpha: 1 },
			secondHand: { lineWidth: 1, startAt: -20, endAt: 85, color: "red", alpha: 1 },
			secondDecoration: { lineWidth: 1, startAt: 70, radius: 4, fillColor: "red", color: "red", alpha: 1 }
		},
		chunkySwiss: {
			outerBorder: { lineWidth: 4, radius:97, color: "black", alpha: 1 },
			smallIndicator: { lineWidth: 4, startAt: 89, endAt: 93, color: "black", alpha: 1 },
			largeIndicator: { lineWidth: 8, startAt: 80, endAt: 93, color: "black", alpha: 1 },
			hourHand: { lineWidth: 12, startAt: -15, endAt: 60, color: "black", alpha: 1 },
			minuteHand: { lineWidth: 10, startAt: -15, endAt: 85, color: "black", alpha: 1 },
			secondHand: { lineWidth: 4, startAt: -20, endAt: 85, color: "red", alpha: 1 },
			secondDecoration: { lineWidth: 2, startAt: 70, radius: 8, fillColor: "red", color: "red", alpha: 1 }
		},
		fancy: {
			outerBorder: { lineWidth: 5, radius:95, color: "green", alpha: 0.7 },
			smallIndicator: { lineWidth: 1, startAt: 80, endAt: 93, color: "black", alpha: 0.4 },
			largeIndicator: { lineWidth: 1, startAt: 30, endAt: 93, color: "black", alpha: 0.5 },
			hourHand: { lineWidth: 8, startAt: -15, endAt: 50, color: "blue", alpha: 0.7 },
			minuteHand: { lineWidth: 7, startAt: -15, endAt: 92, color: "red", alpha: 0.7 },
			secondHand: { lineWidth: 10, startAt: 80, endAt: 85, color: "blue", alpha: 0.3 },
			secondDecoration: { lineWidth: 1, startAt: 30, radius: 50, fillColor: "blue", color: "red", alpha: 0.15 }
		},
		machine: {
			outerBorder: { lineWidth: 60, radius:55, color: "#dd6655", alpha: 1 },
			smallIndicator: { lineWidth: 4, startAt: 80, endAt: 95, color: "white", alpha: 1 },
			largeIndicator: { lineWidth: 14, startAt: 77, endAt: 92, color: "#dd6655", alpha: 1 },
			hourHand: { lineWidth: 18, startAt: -15, endAt: 40, color: "white", alpha: 1 },
			minuteHand: { lineWidth: 14, startAt: 24, endAt: 100, color: "#771100", alpha: 0.5 },
			secondHand: { lineWidth: 3, startAt: 22, endAt: 83, color: "green", alpha: 0 },
			secondDecoration: { lineWidth: 1, startAt: 52, radius: 26, fillColor: "#ffcccc", color: "red", alpha: 0.5 }
		},
 
		// these three created by bonstio from http://bonstio(.)net
		classic/*was gIG*/: {
			outerBorder: { lineWidth: 185, radius:1, color: "#E5ECF9", alpha: 1 },
			smallIndicator: { lineWidth: 2, startAt: 89, endAt: 94, color: "#3366CC", alpha: 1 },
			largeIndicator: { lineWidth: 4, startAt: 83, endAt: 94, color: "#3366CC", alpha: 1 },
			hourHand: { lineWidth: 5, startAt: 0, endAt: 60, color: "black", alpha: 1 },
			minuteHand: { lineWidth: 4, startAt: 0, endAt: 80, color: "black", alpha: 1 },
			secondHand: { lineWidth: 1, startAt: -20, endAt: 85, color: "red", alpha: .85 },
			secondDecoration: { lineWidth: 3, startAt: 0, radius: 2, fillColor: "black", color: "black", alpha: 1 }
		},
 
		modern/*was gIG2*/: {
			outerBorder: { lineWidth: 185, radius:1, color: "#E5ECF9", alpha: 1 },
			smallIndicator: { lineWidth: 5, startAt: 88, endAt: 94, color: "#3366CC", alpha: 1 },
			largeIndicator: { lineWidth: 5, startAt: 88, endAt: 94, color: "#3366CC", alpha: 1 },
			hourHand: { lineWidth: 8, startAt: 0, endAt: 60, color: "black", alpha: 1 },
			minuteHand: { lineWidth: 8, startAt: 0, endAt: 80, color: "black", alpha: 1 },
			secondHand: { lineWidth: 5, startAt: 80, endAt: 85, color: "red", alpha: .85 },
			secondDecoration: { lineWidth: 3, startAt: 0, radius: 4, fillColor: "black", color: "black", alpha: 1 }
		},
 
		simple/*was gIG3*/: {
			outerBorder: { lineWidth: 185, radius:1, color: "#E5ECF9", alpha: 1 },
			smallIndicator: { lineWidth: 10, startAt: 90, endAt: 94, color: "#3366CC", alpha: 1 },
			largeIndicator: { lineWidth: 10, startAt: 90, endAt: 94, color: "#3366CC", alpha: 1 },
			hourHand: { lineWidth: 8, startAt: 0, endAt: 60, color: "black", alpha: 1 },
			minuteHand: { lineWidth: 8, startAt: 0, endAt: 80, color: "black", alpha: 1 },
			secondHand: { lineWidth: 5, startAt: 80, endAt: 85, color: "red", alpha: .85 },
			secondDecoration: { lineWidth: 3, startAt: 0, radius: 4, fillColor: "black", color: "black", alpha: 1 }
		}
 
	}
};
 
CoolClock.prototype = {
	init: function(canvasId,displayRadius,skinId,showSecondHand,gmtOffset) {
		this.canvasId = canvasId;
		this.displayRadius = displayRadius || CoolClock.config.defaultRadius;
		this.skinId = skinId || CoolClock.config.defaultSkin;
		this.showSecondHand = typeof showSecondHand == "boolean" ? showSecondHand : true;
		this.tickDelay = CoolClock.config[ this.showSecondHand ? "tickDelay" : "longTickDelay"];
 
		this.canvas = document.getElementById(canvasId);
 
		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";
 
		this.renderRadius = CoolClock.config.renderRadius; 
 
		this.scale = this.displayRadius / this.renderRadius;
		this.ctx = this.canvas.getContext("2d");
		this.ctx.scale(this.scale,this.scale);
 
		this.gmtOffset = gmtOffset != null ? parseFloat(gmtOffset) : gmtOffset;
 
		CoolClock.config.clockTracker[canvasId] = this;
		this.tick();
		return this;
	},
 
	fullCircle: function(skin) {
		this.fullCircleAt(this.renderRadius,this.renderRadius,skin);
	},
 
	fullCircleAt: function(x,y,skin) {
		with (this.ctx) {
			save();
			globalAlpha = skin.alpha;
			lineWidth = skin.lineWidth;
			if (document.all)
				// excanvas doesn't scale line width so we will do it here
				lineWidth = lineWidth * this.scale;
			arc(x, y, skin.radius, 0, 2*Math.PI, false);
			if (document.all)
				// excanvas doesn't close the circle so let's color in the gap
				arc(x, y, skin.radius, -0.1, 0.1, false);
			if (skin.fillColor) {
				fillStyle = skin.fillColor
				fill();
			}
			else {
				// XXX why not stroke and fill
				if ((this.currentHour <=6) || (this.currentHour >=18)){ //pm
					strokeStyle = "blue";
				} else { //am
					strokeStyle = "yellow";
				}
				stroke();
			}
			restore();
		}
	},
 
	radialLineAtAngle: function(angleFraction,skin) {
		with (this.ctx) {
			save();
			translate(this.renderRadius,this.renderRadius);
			rotate(Math.PI * (2 * angleFraction - 0.5));
			globalAlpha = skin.alpha;
			strokeStyle = skin.color;
			lineWidth = skin.lineWidth;
			if (document.all)
				// excanvas doesn't scale line width so we will do it here
				lineWidth = lineWidth * this.scale;
			if (skin.radius) {
				this.fullCircleAt(skin.startAt,0,skin)
			}
			else {
				beginPath();
				moveTo(skin.startAt,0)
				lineTo(skin.endAt,0);
				stroke();
			}
			restore();
		}
	},
 
	render: function(hour,min,sec) {
                this.currentHour = hour;
		var skin = CoolClock.config.skins[this.skinId];
		this.ctx.clearRect(0,0,this.renderRadius*2,this.renderRadius*2);
 
		this.fullCircle(skin.outerBorder);
 
		for (var i=0;i<60;i++)
			this.radialLineAtAngle(i/60,skin[ i%5 ? "smallIndicator" : "largeIndicator"]);
 
		this.radialLineAtAngle((hour+min/60)/12,skin.hourHand);
		this.radialLineAtAngle((min+sec/60)/60,skin.minuteHand);
		if (this.showSecondHand) {
			this.radialLineAtAngle(sec/60,skin.secondHand);
			if (!document.all)
				// decoration doesn't render right in IE so lets turn it off
				this.radialLineAtAngle(sec/60,skin.secondDecoration);
		}
	},
 
 
	nextTick: function() {
		setTimeout("CoolClock.config.clockTracker['"+this.canvasId+"'].tick()",this.tickDelay);
	},
 
	stillHere: function() {
		return document.getElementById(this.canvasId) != null;
	},
 
	refreshDisplay: function() {
		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());
		}
		else {
			// use local time
			this.render(now.getHours(),now.getMinutes(),now.getSeconds());
		}
	},
 
	tick: function() {
		if (this.stillHere()) {
			this.refreshDisplay()
			this.nextTick();
		}
	}
}
 
addLoadEvent(CoolClock.findAndCreateClocks);

Now click the update button on your script and you are ready to move on.

Update UI Page 'example_cool_clock'

This is the step where you define how many clocks, which design, what time zone and what label you want to list in your UI Page.

If you browse through the script below, you will see I used the 'chunkySwiss clock without seconds' for the six clocks that are displayed. You can add or remove as many as you want, just make sure that each clock has it's own 'canvas id'. I picked six because that was the quantity that displayed the nicest in the smaller column Homepage gauge.

Replace the script in the UI Page 'example_cool_clock' script field with this:

<j:if test="${RP.isPreview()}">
 <center>No preview available</center>
</j:if>
 
<j:if test="${!RP.isPreview()}">
  <script language="javascript" src="scripts/excanvas.jsx"/>
  <script language="javascript" src="CoolClock.jsdbx"/>
 
  <!-- works on IE, but draws different -->
  <table><tr>
    <td align="center"><div><canvas id="x4" class="CoolClock:chunkySwiss:30:noSeconds:+11 leftRightPad"></canvas></div>Sydney</td>
    <td align="center"><div><canvas id="x2" class="CoolClock:chunkySwiss:30:noSeconds:+6 leftRightPad"></canvas></div>Sri Lanka</td>
    <td align="center"><div><canvas id="x3" class="CoolClock:chunkySwiss:30:noSeconds:+3 leftRightPad"></canvas></div>Kiev</td>
    <td align="center"><div><canvas id="x6" class="CoolClock:chunkySwiss:30:noSeconds:+1 leftRightPad"></canvas></div>London</td>
    <td align="center"><div><canvas id="x5" class="CoolClock:chunkySwiss:30:noSeconds:-4 leftRightPad"></canvas></div>New York</td>
    <td align="center"><div><canvas id="x1" class="CoolClock:chunkySwiss:30:noSeconds:-7 leftRightPad"></canvas></div>San Diego</td>
  </tr></table>
</j:if>

Update Widget 'World Clocks'

For the final step, go ahead and open the 'World Clocks' widget, found in System UI > Widgets.

In the past, the new clocks would only render when this script was updated, so I would copy and paste the same script that appears in the script field over it and save. That seemed to do the trick, it may have just been a caching issue, now that I am reading what I was doing. :)

...Anyway, I will provide the script here, then do some testing and possibly update this later today.

Replace the script in the 'World Clocks' Widgets script field with this:

function sections() {
    return {
      'World Clocks' : { 'name' : 'example_cool_clock' }
    };
}
 
function render() {
  var name = renderer.getPreferences().get("name")
  var gf = new GlideForm(renderer.getGC(), name, 0);
  gf.setDirect(true);
  gf.setRenderProperties(renderer.getRenderProperties());
  return gf.getRenderedPage();
}
 
function getEditLink() {
  return "sys_ui_page.do?sysparm_query=name=" + renderer.getPreferences().get("name");
}

Make sure you test this in several browsers, as different browsers will display things differently.

Hope this helps!