1/** 2 * CoolClock 2.1.4 3 * Copyright 2010, Simon Baird 4 * Released under the BSD License. 5 * 6 * Display an analog clock using canvas. 7 * http://randomibis.com/coolclock/ 8 * 9 */ 10 11// Constructor for CoolClock objects 12window.CoolClock = function(options) { 13 return this.init(options); 14} 15 16// Config contains some defaults, and clock skins 17CoolClock.config = { 18 tickDelay: 1000, 19 longTickDelay: 15000, 20 defaultRadius: 85, 21 renderRadius: 100, 22 defaultSkin: "chunkySwiss", 23 // Should be in skin probably... 24 // (TODO: allow skinning of digital display) 25 showSecs: true, 26 showAmPm: true, 27 28 skins: { 29 // There are more skins in moreskins.js 30 // Try making your own skin by copy/pasting one of these and tweaking it 31 swissRail: { 32 outerBorder: { lineWidth: 2, radius:95, color: "black", alpha: 1 }, 33 smallIndicator: { lineWidth: 2, startAt: 88, endAt: 92, color: "black", alpha: 1 }, 34 largeIndicator: { lineWidth: 4, startAt: 79, endAt: 92, color: "black", alpha: 1 }, 35 hourHand: { lineWidth: 8, startAt: -15, endAt: 50, color: "black", alpha: 1 }, 36 minuteHand: { lineWidth: 7, startAt: -15, endAt: 75, color: "black", alpha: 1 }, 37 secondHand: { lineWidth: 1, startAt: -20, endAt: 85, color: "red", alpha: 1 }, 38 secondDecoration: { lineWidth: 1, startAt: 70, radius: 4, fillColor: "red", color: "red", alpha: 1 } 39 }, 40 chunkySwiss: { 41 outerBorder: { lineWidth: 4, radius:97, color: "black", alpha: 1 }, 42 smallIndicator: { lineWidth: 4, startAt: 89, endAt: 93, color: "black", alpha: 1 }, 43 largeIndicator: { lineWidth: 8, startAt: 80, endAt: 93, color: "black", alpha: 1 }, 44 hourHand: { lineWidth: 12, startAt: -15, endAt: 60, color: "black", alpha: 1 }, 45 minuteHand: { lineWidth: 10, startAt: -15, endAt: 85, color: "black", alpha: 1 }, 46 secondHand: { lineWidth: 4, startAt: -20, endAt: 85, color: "red", alpha: 1 }, 47 secondDecoration: { lineWidth: 2, startAt: 70, radius: 8, fillColor: "red", color: "red", alpha: 1 } 48 }, 49 chunkySwissOnBlack: { 50 outerBorder: { lineWidth: 4, radius:97, color: "white", alpha: 1 }, 51 smallIndicator: { lineWidth: 4, startAt: 89, endAt: 93, color: "white", alpha: 1 }, 52 largeIndicator: { lineWidth: 8, startAt: 80, endAt: 93, color: "white", alpha: 1 }, 53 hourHand: { lineWidth: 12, startAt: -15, endAt: 60, color: "white", alpha: 1 }, 54 minuteHand: { lineWidth: 10, startAt: -15, endAt: 85, color: "white", alpha: 1 }, 55 secondHand: { lineWidth: 4, startAt: -20, endAt: 85, color: "red", alpha: 1 }, 56 secondDecoration: { lineWidth: 2, startAt: 70, radius: 8, fillColor: "red", color: "red", alpha: 1 } 57 } 58 59 }, 60 61 // Test for IE so we can nurse excanvas in a couple of places 62 isIE: !!document.all, 63 64 // Will store (a reference to) each clock here, indexed by the id of the canvas element 65 clockTracker: {}, 66 67 // For giving a unique id to coolclock canvases with no id 68 noIdCount: 0 69}; 70 71// Define the CoolClock object's methods 72CoolClock.prototype = { 73 74 // Initialise using the parameters parsed from the colon delimited class 75 init: function(options) { 76 // Parse and store the options 77 this.canvasId = options.canvasId; 78 this.skinId = options.skinId || CoolClock.config.defaultSkin; 79 this.displayRadius = options.displayRadius || CoolClock.config.defaultRadius; 80 this.showSecondHand = typeof options.showSecondHand == "boolean" ? options.showSecondHand : true; 81 this.gmtOffset = (options.gmtOffset != null && options.gmtOffset != '') ? parseFloat(options.gmtOffset) : null; 82 this.showDigital = typeof options.showDigital == "boolean" ? options.showDigital : false; 83 this.logClock = typeof options.logClock == "boolean" ? options.logClock : false; 84 this.logClockRev = typeof options.logClock == "boolean" ? options.logClockRev : false; 85 86 this.tickDelay = CoolClock.config[ this.showSecondHand ? "tickDelay" : "longTickDelay" ]; 87 88 // Get the canvas element 89 this.canvas = document.getElementById(this.canvasId); 90 91 // Make the canvas the requested size. It's always square. 92 this.canvas.setAttribute("width",this.displayRadius*2); 93 this.canvas.setAttribute("height",this.displayRadius*2); 94 this.canvas.style.width = this.displayRadius*2 + "px"; 95 this.canvas.style.height = this.displayRadius*2 + "px"; 96 97 // Explain me please...? 98 this.renderRadius = CoolClock.config.renderRadius; 99 this.scale = this.displayRadius / this.renderRadius; 100 101 // Initialise canvas context 102 this.ctx = this.canvas.getContext("2d"); 103 this.ctx.scale(this.scale,this.scale); 104 105 // Keep track of this object 106 CoolClock.config.clockTracker[this.canvasId] = this; 107 108 // Start the clock going 109 this.tick(); 110 111 return this; 112 }, 113 114 // Draw a circle at point x,y with params as defined in skin 115 fullCircleAt: function(x,y,skin) { 116 this.ctx.save(); 117 this.ctx.globalAlpha = skin.alpha; 118 this.ctx.lineWidth = skin.lineWidth; 119 120 if (!CoolClock.config.isIE) { 121 this.ctx.beginPath(); 122 } 123 124 if (CoolClock.config.isIE) { 125 // excanvas doesn't scale line width so we will do it here 126 this.ctx.lineWidth = this.ctx.lineWidth * this.scale; 127 } 128 129 this.ctx.arc(x, y, skin.radius, 0, 2*Math.PI, false); 130 131 if (CoolClock.config.isIE) { 132 // excanvas doesn't close the circle so let's fill in the tiny gap 133 this.ctx.arc(x, y, skin.radius, -0.1, 0.1, false); 134 } 135 136 if (skin.fillColor) { 137 this.ctx.fillStyle = skin.fillColor 138 this.ctx.fill(); 139 } 140 else { 141 // XXX why not stroke and fill 142 this.ctx.strokeStyle = skin.color; 143 this.ctx.stroke(); 144 }
145 this.ctx.restore(); 146 }, 147 148 // Draw some text centered vertically and horizontally 149 drawTextAt: function(theText,x,y) { 150 this.ctx.save(); 151 this.ctx.font = '15px sans-serif'; 152 var tSize = this.ctx.measureText(theText); 153 if (!tSize.height) tSize.height = 15; // no height in firefox.. :( 154 this.ctx.fillText(theText,x - tSize.width/2,y - tSize.height/2); 155 this.ctx.restore(); 156 }, 157 158 lpad2: function(num) { 159 return (num < 10 ? '0' : '') + num; 160 }, 161 162 tickAngle: function(second) { 163 // Log algorithm by David Bradshaw 164 var tweak = 3; // If it's lower the one second mark looks wrong (?) 165 if (this.logClock) { 166 return second == 0 ? 0 : (Math.log(second*tweak) / Math.log(60*tweak)); 167 } 168 else if (this.logClockRev) { 169 // Flip the seconds then flip the angle (trickiness) 170 second = (60 - second) % 60; 171 return 1.0 - (second == 0 ? 0 : (Math.log(second*tweak) / Math.log(60*tweak))); 172 } 173 else { 174 return second/60.0; 175 } 176 }, 177 178 timeText: function(hour,min,sec) { 179 var c = CoolClock.config; 180 return '' + 181 (c.showAmPm ? ((hour%12)==0 ? 12 : (hour%12)) : hour) + ':' + 182 this.lpad2(min) + 183 (c.showSecs ? ':' + this.lpad2(sec) : '') + 184 (c.showAmPm ? (hour < 12 ? ' am' : ' pm') : '') 185 ; 186 }, 187 188 // Draw a radial line by rotating then drawing a straight line 189 // Ha ha, I think I've accidentally used Taus, (see http://tauday.com/) 190 radialLineAtAngle: function(angleFraction,skin) { 191 this.ctx.save(); 192 this.ctx.translate(this.renderRadius,this.renderRadius); 193 this.ctx.rotate(Math.PI * (2.0 * angleFraction - 0.5)); 194 this.ctx.globalAlpha = skin.alpha; 195 this.ctx.strokeStyle = skin.color; 196 this.ctx.lineWidth = skin.lineWidth; 197 198 if (CoolClock.config.isIE) 199 // excanvas doesn't scale line width so we will do it here 200 this.ctx.lineWidth = this.ctx.lineWidth * this.scale; 201 202 if (skin.radius) { 203 this.fullCircleAt(skin.startAt,0,skin) 204 } 205 else { 206 this.ctx.beginPath(); 207 this.ctx.moveTo(skin.startAt,0) 208 this.ctx.lineTo(skin.endAt,0); 209 this.ctx.stroke(); 210 } 211 this.ctx.restore(); 212 }, 213 214 render: function(hour,min,sec) { 215 // Get the skin 216 var skin = CoolClock.config.skins[this.skinId]; 217 if (!skin) skin = CoolClock.config.skins[CoolClock.config.defaultSkin]; 218 219 // Clear 220 this.ctx.clearRect(0,0,this.renderRadius*2,this.renderRadius*2); 221 222 // Draw the outer edge of the clock 223 if (skin.outerBorder) 224 this.fullCircleAt(this.renderRadius,this.renderRadius,skin.outerBorder); 225 226 // Draw the tick marks. Every 5th one is a big one 227 for (var i=0;i<60;i++) { 228 (i%5) && skin.smallIndicator && this.radialLineAtAngle(this.tickAngle(i),skin.smallIndicator); 229 !(i%5) && skin.largeIndicator && this.radialLineAtAngle(this.tickAngle(i),skin.largeIndicator); 230 } 231 232 // Write the time 233 if (this.showDigital) { 234 this.drawTextAt( 235 this.timeText(hour,min,sec), 236 this.renderRadius, 237 this.renderRadius+this.renderRadius/2 238 ); 239 } 240 241 // Draw the hands 242 if (skin.hourHand) 243 this.radialLineAtAngle(this.tickAngle(((hour%12)*5 + min/12.0)),skin.hourHand); 244 245 if (skin.minuteHand) 246 this.radialLineAtAngle(this.tickAngle((min + sec/60.0)),skin.minuteHand); 247 248 if (this.showSecondHand && skin.secondHand) 249 this.radialLineAtAngle(this.tickAngle(sec),skin.secondHand); 250 251 // Second hand decoration doesn't render right in IE so lets turn it off 252 if (!CoolClock.config.isIE && this.showSecondHand && skin.secondDecoration) 253 this.radialLineAtAngle(this.tickAngle(sec),skin.secondDecoration); 254 }, 255 256 // Check the time and display the clock 257 refreshDisplay: function() { 258 var now = new Date(); 259 if (this.gmtOffset != null) { 260 // Use GMT + gmtOffset 261 var offsetNow = new Date(now.valueOf() + (this.gmtOffset * 1000 * 60 * 60)); 262 this.render(offsetNow.getUTCHours(),offsetNow.getUTCMinutes(),offsetNow.getUTCSeconds()); 263 } 264 else { 265 // Use local time 266 this.render(now.getHours(),now.getMinutes(),now.getSeconds()); 267 } 268 }, 269 270 // Set timeout to trigger a tick in the future 271 nextTick: function() { 272 setTimeout("CoolClock.config.clockTracker['"+this.canvasId+"'].tick()",this.tickDelay); 273 }, 274 275 // Check the canvas element hasn't been removed 276 stillHere: function() { 277 return document.getElementById(this.canvasId) != null; 278 }, 279 280 // Main tick handler. Refresh the clock then setup the next tick 281 tick: function() { 282 if (this.stillHere()) { 283 this.refreshDisplay() 284 this.nextTick(); 285 } 286 } 287}; 288 289// Find all canvas elements that have the CoolClock class and turns them into clocks 290CoolClock.findAndCreateClocks = function() { 291 // (Let's not use a jQuery selector here so it's easier to use frameworks other than jQuery)
292 var canvases = document.getElementsByTagName("canvas"); 293 for (var i=0;i<canvases.length;i++) { 294 // Pull out the fields from the class. Example "CoolClock:chunkySwissOnBlack:1000" 295 var fields = canvases[i].className.split(" ")[0].split(":"); 296 if (fields[0] == "CoolClock") { 297 if (!canvases[i].id) { 298 // If there's no id on this canvas element then give it one 299 canvases[i].id = '_coolclock_auto_id_' + CoolClock.config.noIdCount++; 300 } 301 // Create a clock object for this element 302 new CoolClock({ 303 canvasId: canvases[i].id, 304 skinId: fields[1], 305 displayRadius: fields[2], 306 showSecondHand: fields[3]!='noSeconds', 307 gmtOffset: fields[4], 308 showDigital: fields[5]=='showDigital', 309 logClock: fields[6]=='logClock', 310 logClockRev: fields[6]=='logClockRev' 311 }); 312 } 313 } 314}; 315 316// If you don't have jQuery then you need a body onload like this: <body onload="CoolClock.findAndCreateClocks()"> 317// If you do have jQuery and it's loaded already then we can do it right now 318if (window.jQuery) jQuery(document).ready(CoolClock.findAndCreateClocks);
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.