vendor: 2,393 bytes, lines 1-66
1/** 2 * Basic structure: TC_Class is the public class that is returned upon being called 3 * 4 * So, if you do 5 * var tc = $(".timer").TimeCircles(); 6 * 7 * tc will contain an instance of the public TimeCircles class. It is important to 8 * note that TimeCircles is not chained in the conventional way, check the 9 * documentation for more info on how TimeCircles can be chained. 10 * 11 * After being called/created, the public TimerCircles class will then- for each element 12 * within it's collection, either fetch or create an instance of the private class. 13 * Each function called upon the public class will be forwarded to each instance 14 * of the private classes within the relevant element collection 15 **/ 16(function($) { 17 18 var useWindow = window; 19 20 // From https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/keys 21 if (!Object.keys) { 22 Object.keys = (function() { 23 'use strict'; 24 var hasOwnProperty = Object.prototype.hasOwnProperty, 25 hasDontEnumBug = !({ 26 toString: null 27 }).propertyIsEnumerable('toString'), 28 dontEnums = [ 29 'toString', 30 'toLocaleString', 31 'valueOf', 32 'hasOwnProperty', 33 'isPrototypeOf', 34 'propertyIsEnumerable', 35 'constructor' 36 ], 37 dontEnumsLength = dontEnums.length; 38 39 return function(obj) { 40 if (typeof obj !== 'object' && (typeof obj !== 'function' || obj === null)) { 41 throw new TypeError('Object.keys called on non-object'); 42 } 43 44 var result = [], 45 prop, i; 46 47 for (prop in obj) { 48 if (hasOwnProperty.call(obj, prop)) { 49 result.push(prop); 50 } 51 } 52 53 if (hasDontEnumBug) { 54 for (i = 0; i < dontEnumsLength; i++) { 55 if (hasOwnProperty.call(obj, dontEnums[i])) { 56 result.push(dontEnums[i]); 57 } 58 } 59 } 60 return result; 61 }; 62 }()); 63 } 64 65 // Used to disable some features on IE8 66 var limited_mode = false;
67 var tick_duration = 200; // in ms 68 69 var debug = (location.hash === "#debug"); 70 71 function debug_log(msg) { 72 if (debug) { 73 console.log(msg); 74 } 75 } 76 77 var allUnits = ["Days", "Hours", "Minutes", "Seconds"]; 78 var nextUnits = { 79 Seconds: "Minutes", 80 Minutes: "Hours", 81 Hours: "Days", 82 Days: "Years" 83 }; 84 var secondsIn = { 85 Seconds: 1, 86 Minutes: 60, 87 Hours: 3600, 88 Days: 86400, 89 Months: 2678400, 90 Years: 31536000 91 }; 92 93 /** 94 * Converts hex color code into object containing integer values for the r,g,b use 95 * This function (hexToRgb) originates from: 96 * http://stackoverflow.com/questions/5623838/rgb-to-hex-and-hex-to-rgb 97 * @param {string} hex color code 98 */ 99 function hexToRgb(hex) { 100 // Expand shorthand form (e.g. "03F") to full form (e.g. "0033FF") 101 var shorthandRegex = /^#?([a-f\d])([a-f\d])([a-f\d])$/i; 102 hex = hex.replace(shorthandRegex, function(m, r, g, b) { 103 return r + r + g + g + b + b; 104 }); 105 106 var result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex); 107 return result ? { 108 r: parseInt(result[1], 16), 109 g: parseInt(result[2], 16), 110 b: parseInt(result[3], 16) 111 } : null; 112 } 113 114 function isCanvasSupported() { 115 var elem = document.createElement('canvas'); 116 return !!(elem.getContext && elem.getContext('2d')); 117 } 118 119 /** 120 * Function s4() and guid() originate from: 121 * http://stackoverflow.com/questions/105034/how-to-create-a-guid-uuid-in-javascript 122 */ 123 function s4() { 124 return Math.floor((1 + Math.random()) * 0x10000) 125 .toString(16) 126 .substring(1); 127 } 128 129 /** 130 * Creates a unique id 131 * @returns {String} 132 */ 133 function guid() { 134 return s4() + s4() + '-' + s4() + '-' + s4() + '-' + 135 s4() + '-' + s4() + s4() + s4(); 136 } 137 138 /** 139 * Array.prototype.indexOf fallback for IE8 140 * @param {Mixed} mixed 141 * @returns {Number} 142 */ 143 if (!Array.prototype.indexOf) { 144 Array.prototype.indexOf = function(elt /*, from*/ ) { 145 var len = this.length >>> 0; 146 147 var from = Number(arguments[1]) || 0; 148 from = (from < 0) ? 149 Math.ceil(from) : 150 Math.floor(from); 151 if (from < 0) 152 from += len; 153 154 for (; from < len; from++) { 155 if (from in this && 156 this[from] === elt) 157 return from; 158 } 159 return -1; 160 }; 161 } 162 163 function parse_date(str) { 164 var match = str.match(/^[0-9]{4}-[0-9]{2}-[0-9]{2}\s[0-9]{1,2}:[0-9]{2}:[0-9]{2}$/); 165 if (match !== null && match.length > 0) { 166 var parts = str.split(" "); 167 var date = parts[0].split("-"); 168 var time = parts[1].split(":"); 169 return new Date(date[0], date[1] - 1, date[2], time[0], time[1], time[2]); 170 } 171 // Fallback for different date formats 172 var d = Date.parse(str); 173 if (!isNaN(d)) 174 return d; 175 d = Date.parse(str.replace(/-/g, '/').replace('T', ' ')); 176 if (!isNaN(d)) 177 return d; 178 // Cant find anything 179 return new Date(); 180 } 181 182 function parse_times(diff, old_diff, total_duration, units, floor) { 183 var raw_time = {}; 184 var raw_old_time = {}; 185 var time = {}; 186 var pct = {}; 187 var old_pct = {}; 188 var old_time = {}; 189 190 var greater_unit = null; 191 for (var i = 0; i < units.length; i++) { 192 var unit = units[i]; 193 var maxUnits; 194 195 if (greater_unit === null) { 196 maxUnits = total_duration / secondsIn[unit]; 197 } else { 198 maxUnits = secondsIn[greater_unit] / secondsIn[unit]; 199 } 200 201 var curUnits = (diff / secondsIn[unit]); 202 var oldUnits = (old_diff / secondsIn[unit]); 203 204 if (floor) { 205 if (curUnits > 0) curUnits = Math.floor(curUnits); 206 else curUnits = Math.ceil(curUnits); 207 if (oldUnits > 0) oldUnits = Math.floor(oldUnits); 208 else oldUnits = Math.ceil(oldUnits); 209 } 210 211 if (unit !== "Days") { 212 curUnits = curUnits % maxUnits; 213 oldUnits = oldUnits % maxUnits; 214 } 215 216 raw_time[unit] = curUnits; 217 time[unit] = Math.abs(curUnits); 218 raw_old_time[unit] = oldUnits; 219 old_time[unit] = Math.abs(oldUnits); 220 pct[unit] = Math.abs(curUnits) / maxUnits; 221 old_pct[unit] = Math.abs(oldUnits) / maxUnits; 222 223 greater_unit = unit; 224 } 225 226 return { 227 raw_time: raw_time, 228 raw_old_time: raw_old_time, 229 time: time, 230 old_time: old_time, 231 pct: pct, 232 old_pct: old_pct 233 }; 234 } 235 236 var TC_Instance_List = {}; 237 238 function updateUsedWindow() { 239 if (typeof useWindow.TC_Instance_List !== "undefined") {
240 TC_Instance_List = useWindow.TC_Instance_List; 241 } else { 242 useWindow.TC_Instance_List = TC_Instance_List; 243 } 244 initializeAnimationFrameHandler(useWindow); 245 }; 246 247 function initializeAnimationFrameHandler(w) { 248 var vendors = ['webkit', 'moz']; 249 for (var x = 0; x < vendors.length && !w.requestAnimationFrame; ++x) { 250 w.requestAnimationFrame = w[vendors[x] + 'RequestAnimationFrame']; 251 w.cancelAnimationFrame = w[vendors[x] + 'CancelAnimationFrame']; 252 } 253 254 if (!w.requestAnimationFrame || !w.cancelAnimationFrame) { 255 w.requestAnimationFrame = function(callback, element, instance) { 256 if (typeof instance === "undefined") 257 instance = { 258 data: { 259 last_frame: 0 260 } 261 }; 262 var currTime = new Date().getTime(); 263 var timeToCall = Math.max(0, 16 - (currTime - instance.data.last_frame)); 264 var id = w.setTimeout(function() { 265 callback(currTime + timeToCall); 266 }, timeToCall); 267 instance.data.last_frame = currTime + timeToCall; 268 return id; 269 }; 270 w.cancelAnimationFrame = function(id) { 271 clearTimeout(id); 272 }; 273 } 274 }; 275 276 277 var TC_Instance = function(element, options) { 278 this.element = element; 279 this.container; 280 this.listeners = null; 281 this.data = { 282 paused: false, 283 last_frame: 0, 284 animation_frame: null, 285 interval_fallback: null, 286 timer: false, 287 total_duration: null, 288 prev_time: null, 289 drawn_units: [], 290 text_elements: { 291 Days: null, 292 Hours: null, 293 Minutes: null, 294 Seconds: null 295 }, 296 attributes: { 297 canvas: null, 298 context: null, 299 item_size: null, 300 line_width: null, 301 radius: null, 302 outer_radius: null 303 }, 304 state: { 305 fading: { 306 Days: false, 307 Hours: false, 308 Minutes: false, 309 Seconds: false 310 } 311 } 312 }; 313 314 this.config = null; 315 this.setOptions(options); 316 this.initialize(); 317 }; 318 319 TC_Instance.prototype.clearListeners = function() { 320 this.listeners = { 321 all: [], 322 visible: [] 323 }; 324 }; 325 326 TC_Instance.prototype.addTime = function(seconds_to_add) { 327 if (this.data.attributes.ref_date instanceof Date) { 328 var d = this.data.attributes.ref_date; 329 d.setSeconds(d.getSeconds() + seconds_to_add); 330 } else if (!isNaN(this.data.attributes.ref_date)) { 331 this.data.attributes.ref_date += (seconds_to_add * 1000); 332 } 333 }; 334 335 TC_Instance.prototype.initialize = function(clear_listeners) { 336 // Initialize drawn units 337 this.data.drawn_units = []; 338 for (var i = 0; i < Object.keys(this.config.time).length; i++) { 339 var unit = Object.keys(this.config.time)[i]; 340 if (this.config.time[unit].show) { 341 this.data.drawn_units.push(unit); 342 } 343 } 344 345 // Avoid stacking 346 $(this.element).children('div.time_circles').remove(); 347 348 if (typeof clear_listeners === "undefined") 349 clear_listeners = true; 350 if (clear_listeners || this.listeners === null) { 351 this.clearListeners(); 352 } 353 this.container = $("<div>"); 354 this.container.addClass('time_circles'); 355 this.container.appendTo(this.element); 356 357 // Determine the needed width and height of TimeCircles 358 var height = this.element.offsetHeight; 359 var width = this.element.offsetWidth; 360 if (height === 0) 361 height = $(this.element).height(); 362 if (width === 0) 363 width = $(this.element).width(); 364 365 if (height === 0 && width > 0) 366 height = width / this.data.drawn_units.length; 367 else if (width === 0 && height > 0) 368 width = height * this.data.drawn_units.length; 369 370 // Create our canvas and set it to the appropriate size 371 var canvasElement = document.createElement('canvas'); 372 canvasElement.width = width; 373 canvasElement.height = height; 374 375 // Add canvas elements 376 this.data.attributes.canvas = $(canvasElement); 377 this.data.attributes.canvas.appendTo(this.container); 378 379 // Check if the browser has browser support 380 var canvasSupported = isCanvasSupported(); 381 // If the browser doesn't have browser support, check if explorer canvas is loaded 382 // (A javascript library that adds canvas support to browsers that don't have it) 383 if (!canvasSupported && typeof G_vmlCanvasManager !== "undefined") { 384 G_vmlCanvasManager.initElement(canvasElement); 385 limited_mode = true; 386 canvasSupported = true; 387 } 388 if (canvasSupported) { 389 this.data.attributes.context = canvasElement.getContext('2d'); 390 } 391 392 this.data.attributes.item_size = Math.min(width / this.data.drawn_units.length, height); 393 this.data.attributes.line_width = this.data.attributes.item_size * this.config.fg_width; 394 this.data.attributes.radius = ((this.data.attributes.item_size * 0.8) - this.data.attributes.line_width) / 2; 395 this.data.attributes.outer_radius = this.data.attributes.radius + 0.5 * Math.max(this.data.attributes.line_width, this.data.attributes.line_width * this.config.bg_width); 396 397 // Prepare Time Elements 398 var i = 0; 399 for (var key in this.data.text_elements) { 400 if (!this.config.time[key].show) 401 continue; 402 403 var textElement = $("<div>"); 404 textElement.addClass('textDiv_' + key); 405 textElement.css("top", Math.round(0.35 * this.data.attributes.item_size)); 406 textElement.css("left", Math.round(i++ * this.data.attributes.item_size)); 407 textElement.css("width", this.data.attributes.item_size); 408 textElement.appendTo(this.container); 409 410 var headerElement = $("<h4>"); 411 headerElement.text(this.config.time[key].text); // Options 412 headerElement.css("font-size", Math.round(this.config.text_size * this.data.attributes.item_size)); 413 headerElement.css("line-height", Math.round(this.config.text_size * this.data.attributes.item_size) + "px"); 414 headerElement.appendTo(textElement); 415 416 var numberElement = $("<span>"); 417 numberElement.css("font-size", Math.round(3 * this.config.text_size * this.data.attributes.item_size)); 418 numberElement.css("line-height", Math.round(this.config.text_size * this.data.attributes.item_size) + "px"); 419 numberElement.appendTo(textElement); 420 421 this.data.text_elements[key] = numberElement; 422 } 423 424 this.start(); 425 if (!this.config.start) { 426 this.data.paused = true; 427 } 428 429 // Set up interval fallback 430 var _this = this; 431 this.data.interval_fallback = useWindow.setInterval(function() { 432 _this.update.call(_this, true); 433 }, 100); 434 }; 435 436 TC_Instance.prototype.update = function(nodraw) { 437 if (typeof nodraw === "undefined") { 438 nodraw = false;
439 } else if (nodraw && this.data.paused) { 440 return; 441 } 442 443 if (limited_mode) { 444 //Per unit clearing doesn't work in IE8 using explorer canvas, so do it in one time. The downside is that radial fade cant be used 445 this.data.attributes.context.clearRect(0, 0, this.data.attributes.canvas[0].width, this.data.attributes.canvas[0].hright); 446 } 447 var diff, old_diff; 448 449 var prevDate = this.data.prev_time; 450 var curDate = new Date(); 451 this.data.prev_time = curDate; 452 453 if (prevDate === null) 454 prevDate = curDate; 455 456 // If not counting past zero, and time < 0, then simply draw the zero point once, and call stop 457 if (!this.config.count_past_zero) { 458 if (curDate > this.data.attributes.ref_date) { 459 for (var i = 0; i < this.data.drawn_units.length; i++) { 460 var key = this.data.drawn_units[i]; 461 462 // Set the text value 463 this.data.text_elements[key].text("0"); 464 var x = (i * this.data.attributes.item_size) + (this.data.attributes.item_size / 2); 465 var y = this.data.attributes.item_size / 2; 466 var color = this.config.time[key].color; 467 this.drawArc(x, y, color, 0); 468 } 469 this.stop(); 470 return; 471 } 472 } 473 474 // Compare current time with reference 475 diff = (this.data.attributes.ref_date - curDate) / 1000; 476 old_diff = (this.data.attributes.ref_date - prevDate) / 1000; 477 478 var floor = this.config.animation !== "smooth"; 479 480 var visible_times = parse_times(diff, old_diff, this.data.total_duration, this.data.drawn_units, floor); 481 var all_times = parse_times(diff, old_diff, secondsIn["Years"], allUnits, floor); 482 483 var i = 0; 484 var j = 0; 485 var lastKey = null; 486 487 var cur_shown = this.data.drawn_units.slice(); 488 for (var i in allUnits) { 489 var key = allUnits[i]; 490 491 // Notify (all) listeners 492 if (Math.floor(all_times.raw_time[key]) !== Math.floor(all_times.raw_old_time[key])) { 493 this.notifyListeners(key, Math.floor(all_times.time[key]), Math.floor(diff), "all"); 494 } 495 496 if (cur_shown.indexOf(key) < 0) 497 continue; 498 499 // Notify (visible) listeners 500 if (Math.floor(visible_times.raw_time[key]) !== Math.floor(visible_times.raw_old_time[key])) { 501 this.notifyListeners(key, Math.floor(visible_times.time[key]), Math.floor(diff), "visible"); 502 } 503 504 if (!nodraw) { 505 // Set the text value 506 this.data.text_elements[key].text(Math.floor(Math.abs(visible_times.time[key]))); 507 508 var x = (j * this.data.attributes.item_size) + (this.data.attributes.item_size / 2); 509 var y = this.data.attributes.item_size / 2; 510 var color = this.config.time[key].color; 511 512 if (this.config.animation === "smooth") { 513 if (lastKey !== null && !limited_mode) { 514 if (Math.floor(visible_times.time[lastKey]) > Math.floor(visible_times.old_time[lastKey])) { 515 this.radialFade(x, y, color, 1, key); 516 this.data.state.fading[key] = true; 517 } else if (Math.floor(visible_times.time[lastKey]) < Math.floor(visible_times.old_time[lastKey])) { 518 this.radialFade(x, y, color, 0, key); 519 this.data.state.fading[key] = true; 520 } 521 } 522 if (!this.data.state.fading[key]) { 523 this.drawArc(x, y, color, visible_times.pct[key]); 524 } 525 } else { 526 this.animateArc(x, y, color, visible_times.pct[key], visible_times.old_pct[key], (new Date()).getTime() + tick_duration); 527 } 528 } 529 lastKey = key; 530 j++; 531 } 532 533 // Dont request another update if we should be paused 534 if (this.data.paused || nodraw) { 535 return; 536 } 537 538 // We need this for our next frame either way 539 var _this = this; 540 var update = function() { 541 _this.update.call(_this); 542 }; 543 544 // Either call next update immediately, or in a second 545 if (this.config.animation === "smooth") { 546 // Smooth animation, Queue up the next frame
547 this.data.animation_frame = useWindow.requestAnimationFrame(update, _this.element, _this); 548 } else { 549 // Tick animation, Don't queue until very slightly after the next second happens 550 var delay = (diff % 1) * 1000; 551 if (delay < 0) 552 delay = 1000 + delay; 553 delay += 50; 554 555 _this.data.animation_frame = useWindow.setTimeout(function() { 556 _this.data.animation_frame = useWindow.requestAnimationFrame(update, _this.element, _this); 557 }, delay); 558 } 559 }; 560 561 TC_Instance.prototype.animateArc = function(x, y, color, target_pct, cur_pct, animation_end) { 562 if (this.data.attributes.context === null) 563 return; 564 565 var diff = cur_pct - target_pct; 566 if (Math.abs(diff) > 0.5) { 567 if (target_pct === 0) { 568 this.radialFade(x, y, color, 1); 569 } else { 570 this.radialFade(x, y, color, 0); 571 } 572 } else { 573 var progress = (tick_duration - (animation_end - (new Date()).getTime())) / tick_duration; 574 if (progress > 1) 575 progress = 1; 576 577 var pct = (cur_pct * (1 - progress)) + (target_pct * progress); 578 this.drawArc(x, y, color, pct); 579 580 //var show_pct = 581 if (progress >= 1) 582 return; 583 var _this = this; 584 useWindow.requestAnimationFrame(function() { 585 _this.animateArc(x, y, color, target_pct, cur_pct, animation_end); 586 }, this.element); 587 } 588 }; 589 590 TC_Instance.prototype.drawArc = function(x, y, color, pct) { 591 if (this.data.attributes.context === null) 592 return; 593 594 var clear_radius = Math.max(this.data.attributes.outer_radius, this.data.attributes.item_size / 2); 595 if (!limited_mode) { 596 this.data.attributes.context.clearRect( 597 x - clear_radius, 598 y - clear_radius, 599 clear_radius * 2, 600 clear_radius * 2 601 ); 602 } 603 604 if (this.config.use_background) { 605 this.data.attributes.context.beginPath(); 606 this.data.attributes.context.arc(x, y, this.data.attributes.radius, 0, 2 * Math.PI, false); 607 this.data.attributes.context.lineWidth = this.data.attributes.line_width * this.config.bg_width; 608 609 // line color 610 this.data.attributes.context.strokeStyle = this.config.circle_bg_color; 611 this.data.attributes.context.stroke(); 612 } 613 614 // Direction 615 var startAngle, endAngle, counterClockwise; 616 var defaultOffset = (-0.5 * Math.PI); 617 var fullCircle = 2 * Math.PI; 618 startAngle = defaultOffset + (this.config.start_angle / 360 * fullCircle); 619 var offset = (2 * pct * Math.PI); 620 621 if (this.config.direction === "Both") { 622 counterClockwise = false; 623 startAngle -= (offset / 2); 624 endAngle = startAngle + offset; 625 } else { 626 if (this.config.direction === "Clockwise") { 627 counterClockwise = false; 628 endAngle = startAngle + offset; 629 } else { 630 counterClockwise = true; 631 endAngle = startAngle - offset; 632 } 633 } 634 635 this.data.attributes.context.beginPath(); 636 this.data.attributes.context.arc(x, y, this.data.attributes.radius, startAngle, endAngle, counterClockwise); 637 this.data.attributes.context.lineWidth = this.data.attributes.line_width; 638 639 // line color 640 this.data.attributes.context.strokeStyle = color; 641 this.data.attributes.context.stroke(); 642 }; 643 644 TC_Instance.prototype.radialFade = function(x, y, color, from, key) { 645 // TODO: Make fade_time option 646 var rgb = hexToRgb(color); 647 var _this = this; // We have a few inner scopes here that will need access to our instance 648 649 var step = 0.2 * ((from === 1) ? -1 : 1); 650 var i; 651 for (i = 0; from <= 1 && from >= 0; i++) { 652 // Create inner scope so our variables are not changed by the time the Timeout triggers 653 (function() {
654 var delay = 50 * i; 655 var rgba = "rgba(" + rgb.r + ", " + rgb.g + ", " + rgb.b + ", " + (Math.round(from * 10) / 10) + ")"; 656 useWindow.setTimeout(function() { 657 _this.drawArc(x, y, rgba, 1); 658 }, delay); 659 }()); 660 from += step; 661 } 662 if (typeof key !== undefined) { 663 useWindow.setTimeout(function() { 664 _this.data.state.fading[key] = false; 665 }, 50 * i); 666 } 667 }; 668 669 TC_Instance.prototype.timeLeft = function() { 670 if (this.data.paused && typeof this.data.timer === "number") { 671 return this.data.timer; 672 } 673 var now = new Date(); 674 return ((this.data.attributes.ref_date - now) / 1000); 675 }; 676 677 TC_Instance.prototype.start = function() { 678 useWindow.cancelAnimationFrame(this.data.animation_frame); 679 useWindow.clearTimeout(this.data.animation_frame) 680 681 // Check if a date was passed in html attribute or jquery data 682 var attr_data_date = $(this.element).data('date'); 683 if (typeof attr_data_date === "undefined") { 684 attr_data_date = $(this.element).attr('data-date'); 685 } 686 if (typeof attr_data_date === "string") { 687 this.data.attributes.ref_date = parse_date(attr_data_date); 688 } 689 // Check if this is an unpause of a timer 690 else if (typeof this.data.timer === "number") { 691 if (this.data.paused) { 692 this.data.attributes.ref_date = (new Date()).getTime() + (this.data.timer * 1000); 693 } 694 } else { 695 // Try to get data-timer 696 var attr_data_timer = $(this.element).data('timer'); 697 if (typeof attr_data_timer === "undefined") { 698 attr_data_timer = $(this.element).attr('data-timer'); 699 } 700 if (typeof attr_data_timer === "string") { 701 attr_data_timer = parseFloat(attr_data_timer); 702 } 703 if (typeof attr_data_timer === "number") { 704 this.data.timer = attr_data_timer; 705 this.data.attributes.ref_date = (new Date()).getTime() + (attr_data_timer * 1000); 706 } else { 707 // data-timer and data-date were both not set 708 // use config date 709 this.data.attributes.ref_date = this.config.ref_date; 710 } 711 } 712 713 // Start running 714 this.data.paused = false; 715 this.update.call(this); 716 }; 717 718 TC_Instance.prototype.restart = function() { 719 this.data.timer = false; 720 this.start(); 721 }; 722 723 TC_Instance.prototype.stop = function() { 724 if (typeof this.data.timer === "number") { 725 this.data.timer = this.timeLeft(this); 726 } 727 // Stop running 728 this.data.paused = true; 729 useWindow.cancelAnimationFrame(this.data.animation_frame); 730 }; 731 732 TC_Instance.prototype.destroy = function() { 733 this.clearListeners(); 734 this.stop(); 735 useWindow.clearInterval(this.data.interval_fallback); 736 this.data.interval_fallback = null; 737 738 this.container.remove(); 739 $(this.element).removeAttr('data-tc-id'); 740 $(this.element).removeData('tc-id'); 741 }; 742 743 TC_Instance.prototype.setOptions = function(options) { 744 if (this.config === null) { 745 this.default_options.ref_date = new Date(); 746 this.config = $.extend(true, {}, this.default_options); 747 } 748 $.extend(true, this.config, options); 749 750 // Use window.top if use_top_frame is true 751 if (this.config.use_top_frame) { 752 useWindow = window.top; 753 } else { 754 useWindow = window; 755 } 756 updateUsedWindow(); 757 758 this.data.total_duration = this.config.total_duration; 759 if (typeof this.data.total_duration === "string") { 760 if (typeof secondsIn[this.data.total_duration] !== "undefined") { 761 // If set to Years, Months, Days, Hours or Minutes, fetch the secondsIn value for that 762 this.data.total_duration = secondsIn[this.data.total_duration]; 763 } else if (this.data.total_duration === "Auto") { 764 // If set to auto, total_duration is the size of 1 unit, of the unit type bigger than the largest shown 765 for (var i = 0; i < Object.keys(this.config.time).length; i++) { 766 var unit = Object.keys(this.config.time)[i]; 767 if (this.config.time[unit].show) { 768 this.data.total_duration = secondsIn[nextUnits[unit]]; 769 break; 770 } 771 } 772 } else { 773 // If it's a string, but neither of the above, user screwed up. 774 this.data.total_duration = secondsIn["Years"]; 775 console.error("Valid values for TimeCircles config.total_duration are either numeric, or (string) Years, Months, Days, Hours, Minutes, Auto"); 776 } 777 } 778 }; 779 780 TC_Instance.prototype.addListener = function(f, context, type) { 781 if (typeof f !== "function") 782 return; 783 if (typeof type === "undefined") 784 type = "visible"; 785 this.listeners[type].push({ 786 func: f, 787 scope: context 788 }); 789 }; 790 791 TC_Instance.prototype.notifyListeners = function(unit, value, total, type) { 792 for (var i = 0; i < this.listeners[type].length; i++) { 793 var listener = this.listeners[type][i]; 794 listener.func.apply(listener.scope, [unit, value, total]); 795 } 796 }; 797 798 TC_Instance.prototype.default_options = { 799 ref_date: new Date(), 800 start: true, 801 animation: "smooth", 802 count_past_zero: true, 803 circle_bg_color: "#60686F", 804 use_background: true, 805 fg_width: 0.1, 806 bg_width: 1.2, 807 text_size: 0.07, 808 total_duration: "Auto", 809 direction: "Clockwise", 810 use_top_frame: false, 811 start_angle: 0, 812 time: { 813 Days: { 814 show: true, 815 text: "Days", 816 color: "#FC6" 817 }, 818 Hours: { 819 show: true, 820 text: "Hours", 821 color: "#9CF" 822 }, 823 Minutes: { 824 show: true, 825 text: "Minutes", 826 color: "#BFB" 827 }, 828 Seconds: { 829 show: true, 830 text: "Seconds", 831 color: "#F99" 832 } 833 } 834 }; 835 836 // Time circle class 837 var TC_Class = function(elements, options) { 838 this.elements = elements; 839 this.options = options; 840 this.foreach(); 841 }; 842 843 TC_Class.prototype.getInstance = function(element) { 844 var instance; 845 846 var cur_id = $(element).data("tc-id"); 847 if (typeof cur_id === "undefined") { 848 cur_id = guid(); 849 $(element).attr("data-tc-id", cur_id); 850 } 851 if (typeof TC_Instance_List[cur_id] === "undefined") { 852 var options = this.options; 853 var element_options = $(element).data('options'); 854 if (typeof element_options === "string") { 855 element_options = JSON.parse(element_options); 856 } 857 if (typeof element_options === "object") { 858 options = $.extend(true, {}, this.options, element_options); 859 } 860 instance = new TC_Instance(element, options); 861 TC_Instance_List[cur_id] = instance; 862 } else { 863 instance = TC_Instance_List[cur_id]; 864 if (typeof this.options !== "undefined") { 865 instance.setOptions(this.options); 866 } 867 } 868 return instance; 869 }; 870 871 TC_Class.prototype.addTime = function(seconds_to_add) { 872 this.foreach(function(instance) { 873 instance.addTime(seconds_to_add); 874 }); 875 }; 876 877 TC_Class.prototype.foreach = function(callback) { 878 var _this = this; 879 this.elements.each(function() { 880 var instance = _this.getInstance(this); 881 if (typeof callback === "function") { 882 callback(instance); 883 } 884 }); 885 return this; 886 }; 887 888 TC_Class.prototype.start = function() { 889 this.foreach(function(instance) { 890 instance.start(); 891 }); 892 return this; 893 }; 894 895 TC_Class.prototype.stop = function() { 896 this.foreach(function(instance) { 897 instance.stop(); 898 }); 899 return this; 900 }; 901 902 TC_Class.prototype.restart = function() { 903 this.foreach(function(instance) { 904 instance.restart(); 905 }); 906 return this; 907 }; 908 909 TC_Class.prototype.rebuild = function() { 910 this.foreach(function(instance) { 911 instance.initialize(false); 912 }); 913 return this; 914 }; 915 916 TC_Class.prototype.getTime = function() { 917 return this.getInstance(this.elements[0]).timeLeft(); 918 }; 919 920 TC_Class.prototype.addListener = function(f, type) { 921 if (typeof type === "undefined") 922 type = "visible"; 923 var _this = this; 924 this.foreach(function(instance) { 925 instance.addListener(f, _this.elements, type); 926 }); 927 return this; 928 }; 929 930 TC_Class.prototype.destroy = function() { 931 this.foreach(function(instance) { 932 instance.destroy(); 933 }); 934 return this; 935 }; 936 937 TC_Class.prototype.end = function() { 938 return this.elements; 939 }; 940 941 $.fn.TimeCircles = function(options) { 942 return new TC_Class(this, options); 943 }; 944}(jQuery));
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.