Created
July 28, 2015 09:26
-
-
Save alxrz/97091184640708ed8ae7 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* Basic structure: TC_Class is the public class that is returned upon being called | |
* | |
* So, if you do | |
* var tc = $(".timer").TimeCircles(); | |
* | |
* tc will contain an instance of the public TimeCircles class. It is important to | |
* note that TimeCircles is not chained in the conventional way, check the | |
* documentation for more info on how TimeCircles can be chained. | |
* | |
* After being called/created, the public TimerCircles class will then- for each element | |
* within it's collection, either fetch or create an instance of the private class. | |
* Each function called upon the public class will be forwarded to each instance | |
* of the private classes within the relevant element collection | |
**/ | |
(function($) { | |
var useWindow = window; | |
// From https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/keys | |
if (!Object.keys) { | |
Object.keys = (function() { | |
'use strict'; | |
var hasOwnProperty = Object.prototype.hasOwnProperty, | |
hasDontEnumBug = !({toString: null}).propertyIsEnumerable('toString'), | |
dontEnums = [ | |
'toString', | |
'toLocaleString', | |
'valueOf', | |
'hasOwnProperty', | |
'isPrototypeOf', | |
'propertyIsEnumerable', | |
'constructor' | |
], | |
dontEnumsLength = dontEnums.length; | |
return function(obj) { | |
if (typeof obj !== 'object' && (typeof obj !== 'function' || obj === null)) { | |
throw new TypeError('Object.keys called on non-object'); | |
} | |
var result = [], prop, i; | |
for (prop in obj) { | |
if (hasOwnProperty.call(obj, prop)) { | |
result.push(prop); | |
} | |
} | |
if (hasDontEnumBug) { | |
for (i = 0; i < dontEnumsLength; i++) { | |
if (hasOwnProperty.call(obj, dontEnums[i])) { | |
result.push(dontEnums[i]); | |
} | |
} | |
} | |
return result; | |
}; | |
}()); | |
} | |
// Used to disable some features on IE8 | |
var limited_mode = false; | |
var tick_duration = 200; // in ms | |
var debug = (location.hash === "#debug"); | |
function debug_log(msg) { | |
if (debug) { | |
console.log(msg); | |
} | |
} | |
var allUnits = ["Days", "Hours", "Minutes", "Seconds"]; | |
var nextUnits = { | |
Seconds: "Minutes", | |
Minutes: "Hours", | |
Hours: "Days", | |
Days: "Years" | |
}; | |
var secondsIn = { | |
Seconds: 1, | |
Minutes: 60, | |
Hours: 3600, | |
Days: 86400, | |
Months: 2678400, | |
Years: 31536000 | |
}; | |
/** | |
* Converts hex color code into object containing integer values for the r,g,b use | |
* This function (hexToRgb) originates from: | |
* http://stackoverflow.com/questions/5623838/rgb-to-hex-and-hex-to-rgb | |
* @param {string} hex color code | |
*/ | |
function hexToRgb(hex) { | |
// Expand shorthand form (e.g. "03F") to full form (e.g. "0033FF") | |
var shorthandRegex = /^#?([a-f\d])([a-f\d])([a-f\d])$/i; | |
hex = hex.replace(shorthandRegex, function(m, r, g, b) { | |
return r + r + g + g + b + b; | |
}); | |
var result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex); | |
return result ? { | |
r: parseInt(result[1], 16), | |
g: parseInt(result[2], 16), | |
b: parseInt(result[3], 16) | |
} : null; | |
} | |
function isCanvasSupported() { | |
var elem = document.createElement('canvas'); | |
return !!(elem.getContext && elem.getContext('2d')); | |
} | |
/** | |
* Function s4() and guid() originate from: | |
* http://stackoverflow.com/questions/105034/how-to-create-a-guid-uuid-in-javascript | |
*/ | |
function s4() { | |
return Math.floor((1 + Math.random()) * 0x10000) | |
.toString(16) | |
.substring(1); | |
} | |
/** | |
* Creates a unique id | |
* @returns {String} | |
*/ | |
function guid() { | |
return s4() + s4() + '-' + s4() + '-' + s4() + '-' + | |
s4() + '-' + s4() + s4() + s4(); | |
} | |
/** | |
* Array.prototype.indexOf fallback for IE8 | |
* @param {Mixed} mixed | |
* @returns {Number} | |
*/ | |
if (!Array.prototype.indexOf) { | |
Array.prototype.indexOf = function(elt /*, from*/) | |
{ | |
var len = this.length >>> 0; | |
var from = Number(arguments[1]) || 0; | |
from = (from < 0) | |
? Math.ceil(from) | |
: Math.floor(from); | |
if (from < 0) | |
from += len; | |
for (; from < len; from++) | |
{ | |
if (from in this && | |
this[from] === elt) | |
return from; | |
} | |
return -1; | |
}; | |
} | |
function parse_date(str) { | |
var match = str.match(/^[0-9]{4}-[0-9]{2}-[0-9]{2}\s[0-9]{1,2}:[0-9]{2}:[0-9]{2}$/); | |
if (match !== null && match.length > 0) { | |
var parts = str.split(" "); | |
var date = parts[0].split("-"); | |
var time = parts[1].split(":"); | |
return new Date(date[0], date[1] - 1, date[2], time[0], time[1], time[2]); | |
} | |
// Fallback for different date formats | |
var d = Date.parse(str); | |
if (!isNaN(d)) | |
return d; | |
d = Date.parse(str.replace(/-/g, '/').replace('T', ' ')); | |
if (!isNaN(d)) | |
return d; | |
// Cant find anything | |
return new Date(); | |
} | |
function parse_times(diff, old_diff, total_duration, units, floor) { | |
var raw_time = {}; | |
var raw_old_time = {}; | |
var time = {}; | |
var pct = {}; | |
var old_pct = {}; | |
var old_time = {}; | |
var greater_unit = null; | |
for(var i = 0; i < units.length; i++) { | |
var unit = units[i]; | |
var maxUnits; | |
if (greater_unit === null) { | |
maxUnits = total_duration / secondsIn[unit]; | |
} | |
else { | |
maxUnits = secondsIn[greater_unit] / secondsIn[unit]; | |
} | |
var curUnits = (diff / secondsIn[unit]); | |
var oldUnits = (old_diff / secondsIn[unit]); | |
if(floor) { | |
if(curUnits > 0) curUnits = Math.floor(curUnits); | |
else curUnits = Math.ceil(curUnits); | |
if(oldUnits > 0) oldUnits = Math.floor(oldUnits); | |
else oldUnits = Math.ceil(oldUnits); | |
} | |
if (unit !== "Days") { | |
curUnits = curUnits % maxUnits; | |
oldUnits = oldUnits % maxUnits; | |
} | |
raw_time[unit] = curUnits; | |
time[unit] = Math.abs(curUnits); | |
raw_old_time[unit] = oldUnits; | |
old_time[unit] = Math.abs(oldUnits); | |
pct[unit] = Math.abs(curUnits) / maxUnits; | |
old_pct[unit] = Math.abs(oldUnits) / maxUnits; | |
greater_unit = unit; | |
} | |
return { | |
raw_time: raw_time, | |
raw_old_time: raw_old_time, | |
time: time, | |
old_time: old_time, | |
pct: pct, | |
old_pct: old_pct | |
}; | |
} | |
var TC_Instance_List = {}; | |
function updateUsedWindow() { | |
if(typeof useWindow.TC_Instance_List !== "undefined") { | |
TC_Instance_List = useWindow.TC_Instance_List; | |
} | |
else { | |
useWindow.TC_Instance_List = TC_Instance_List; | |
} | |
initializeAnimationFrameHandler(useWindow); | |
}; | |
function initializeAnimationFrameHandler(w) { | |
var vendors = ['webkit', 'moz']; | |
for (var x = 0; x < vendors.length && !w.requestAnimationFrame; ++x) { | |
w.requestAnimationFrame = w[vendors[x] + 'RequestAnimationFrame']; | |
w.cancelAnimationFrame = w[vendors[x] + 'CancelAnimationFrame']; | |
} | |
if (!w.requestAnimationFrame || !w.cancelAnimationFrame) { | |
w.requestAnimationFrame = function(callback, element, instance) { | |
if (typeof instance === "undefined") | |
instance = {data: {last_frame: 0}}; | |
var currTime = new Date().getTime(); | |
var timeToCall = Math.max(0, 16 - (currTime - instance.data.last_frame)); | |
var id = w.setTimeout(function() { | |
callback(currTime + timeToCall); | |
}, timeToCall); | |
instance.data.last_frame = currTime + timeToCall; | |
return id; | |
}; | |
w.cancelAnimationFrame = function(id) { | |
clearTimeout(id); | |
}; | |
} | |
}; | |
var TC_Instance = function(element, options) { | |
this.element = element; | |
this.container; | |
this.listeners = null; | |
this.data = { | |
paused: false, | |
last_frame: 0, | |
animation_frame: null, | |
interval_fallback: null, | |
timer: false, | |
total_duration: null, | |
prev_time: null, | |
drawn_units: [], | |
text_elements: { | |
Days: null, | |
Hours: null, | |
Minutes: null, | |
Seconds: null | |
}, | |
attributes: { | |
canvas: null, | |
context: null, | |
item_size: null, | |
line_width: null, | |
radius: null, | |
outer_radius: null | |
}, | |
state: { | |
fading: { | |
Days: false, | |
Hours: false, | |
Minutes: false, | |
Seconds: false | |
} | |
} | |
}; | |
this.config = null; | |
this.setOptions(options); | |
this.initialize(); | |
}; | |
TC_Instance.prototype.clearListeners = function() { | |
this.listeners = { all: [], visible: [] }; | |
}; | |
TC_Instance.prototype.addTime = function(seconds_to_add) { | |
if(this.data.attributes.ref_date instanceof Date) { | |
var d = this.data.attributes.ref_date; | |
d.setSeconds(d.getSeconds() + seconds_to_add); | |
} | |
else if(!isNaN(this.data.attributes.ref_date)) { | |
this.data.attributes.ref_date += (seconds_to_add * 1000); | |
} | |
}; | |
TC_Instance.prototype.initialize = function(clear_listeners) { | |
// Initialize drawn units | |
this.data.drawn_units = []; | |
for(var i = 0; i < Object.keys(this.config.time).length; i++) { | |
var unit = Object.keys(this.config.time)[i]; | |
if (this.config.time[unit].show) { | |
this.data.drawn_units.push(unit); | |
} | |
} | |
// Avoid stacking | |
$(this.element).children('div.time_circles').remove(); | |
if (typeof clear_listeners === "undefined") | |
clear_listeners = true; | |
if (clear_listeners || this.listeners === null) { | |
this.clearListeners(); | |
} | |
this.container = $("<div>"); | |
this.container.addClass('time_circles'); | |
this.container.appendTo(this.element); | |
// Determine the needed width and height of TimeCircles | |
var height = this.element.offsetHeight; | |
var width = this.element.offsetWidth; | |
if (height === 0) | |
height = $(this.element).height(); | |
if (width === 0) | |
width = $(this.element).width(); | |
if (height === 0 && width > 0) | |
height = width / this.data.drawn_units.length; | |
else if (width === 0 && height > 0) | |
width = height * this.data.drawn_units.length; | |
// Create our canvas and set it to the appropriate size | |
var canvasElement = document.createElement('canvas'); | |
canvasElement.width = width; | |
canvasElement.height = height; | |
// Add canvas elements | |
this.data.attributes.canvas = $(canvasElement); | |
this.data.attributes.canvas.appendTo(this.container); | |
// Check if the browser has browser support | |
var canvasSupported = isCanvasSupported(); | |
// If the browser doesn't have browser support, check if explorer canvas is loaded | |
// (A javascript library that adds canvas support to browsers that don't have it) | |
if(!canvasSupported && typeof G_vmlCanvasManager !== "undefined") { | |
G_vmlCanvasManager.initElement(canvasElement); | |
limited_mode = true; | |
canvasSupported = true; | |
} | |
if(canvasSupported) { | |
this.data.attributes.context = canvasElement.getContext('2d'); | |
} | |
this.data.attributes.item_size = Math.min(width / this.data.drawn_units.length, height); | |
this.data.attributes.line_width = this.data.attributes.item_size * this.config.fg_width; | |
this.data.attributes.radius = ((this.data.attributes.item_size * 0.8) - this.data.attributes.line_width) / 2; | |
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); | |
// Prepare Time Elements | |
var i = 0; | |
for (var key in this.data.text_elements) { | |
if (!this.config.time[key].show) | |
continue; | |
var textElement = $("<div>"); | |
textElement.addClass('textDiv_' + key); | |
textElement.css("top", Math.round(0.35 * this.data.attributes.item_size)); | |
textElement.css("left", Math.round(i++ * this.data.attributes.item_size)); | |
textElement.css("width", this.data.attributes.item_size); | |
textElement.appendTo(this.container); | |
var headerElement = $("<h4>"); | |
headerElement.text(this.config.time[key].text); // Options | |
headerElement.css("font-size", Math.round(this.config.text_size * this.data.attributes.item_size)); | |
headerElement.css("line-height", Math.round(this.config.text_size * this.data.attributes.item_size) + "px"); | |
headerElement.appendTo(textElement); | |
var numberElement = $("<span>"); | |
numberElement.css("font-size", Math.round(3 * this.config.text_size * this.data.attributes.item_size)); | |
numberElement.css("line-height", Math.round(this.config.text_size * this.data.attributes.item_size) + "px"); | |
numberElement.appendTo(textElement); | |
this.data.text_elements[key] = numberElement; | |
} | |
this.start(); | |
if (!this.config.start) { | |
this.data.paused = true; | |
} | |
// Set up interval fallback | |
var _this = this; | |
this.data.interval_fallback = useWindow.setInterval(function(){ | |
_this.update.call(_this, true); | |
}, 100); | |
}; | |
TC_Instance.prototype.update = function(nodraw) { | |
if(typeof nodraw === "undefined") { | |
nodraw = false; | |
} | |
else if(nodraw && this.data.paused) { | |
return; | |
} | |
if(limited_mode) { | |
//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 | |
this.data.attributes.context.clearRect(0, 0, this.data.attributes.canvas[0].width, this.data.attributes.canvas[0].hright); | |
} | |
var diff, old_diff; | |
var prevDate = this.data.prev_time; | |
var curDate = new Date(); | |
this.data.prev_time = curDate; | |
if (prevDate === null) | |
prevDate = curDate; | |
// If not counting past zero, and time < 0, then simply draw the zero point once, and call stop | |
if (!this.config.count_past_zero) { | |
if (curDate > this.data.attributes.ref_date) { | |
for(var i = 0; i < this.data.drawn_units.length; i++) { | |
var key = this.data.drawn_units[i]; | |
// Set the text value | |
this.data.text_elements[key].text("0"); | |
var x = (i * this.data.attributes.item_size) + (this.data.attributes.item_size / 2); | |
var y = this.data.attributes.item_size / 2; | |
var color = this.config.time[key].color; | |
this.drawArc(x, y, color, 0); | |
} | |
this.stop(); | |
return; | |
} | |
} | |
// Compare current time with reference | |
diff = (this.data.attributes.ref_date - curDate) / 1000; | |
old_diff = (this.data.attributes.ref_date - prevDate) / 1000; | |
var floor = this.config.animation !== "smooth"; | |
var visible_times = parse_times(diff, old_diff, this.data.total_duration, this.data.drawn_units, floor); | |
var all_times = parse_times(diff, old_diff, secondsIn["Years"], allUnits, floor); | |
var i = 0; | |
var j = 0; | |
var lastKey = null; | |
var cur_shown = this.data.drawn_units.slice(); | |
for (var i in allUnits) { | |
var key = allUnits[i]; | |
// Notify (all) listeners | |
if (Math.floor(all_times.raw_time[key]) !== Math.floor(all_times.raw_old_time[key])) { | |
this.notifyListeners(key, Math.floor(all_times.time[key]), Math.floor(diff), "all"); | |
} | |
if (cur_shown.indexOf(key) < 0) | |
continue; | |
// Notify (visible) listeners | |
if (Math.floor(visible_times.raw_time[key]) !== Math.floor(visible_times.raw_old_time[key])) { | |
this.notifyListeners(key, Math.floor(visible_times.time[key]), Math.floor(diff), "visible"); | |
} | |
if(!nodraw) { | |
// Set the text value | |
this.data.text_elements[key].text(Math.floor(Math.abs(visible_times.time[key]))); | |
var x = (j * this.data.attributes.item_size) + (this.data.attributes.item_size / 2); | |
var y = this.data.attributes.item_size / 2; | |
var color = this.config.time[key].color; | |
if (this.config.animation === "smooth") { | |
if (lastKey !== null && !limited_mode) { | |
if (Math.floor(visible_times.time[lastKey]) > Math.floor(visible_times.old_time[lastKey])) { | |
this.radialFade(x, y, color, 1, key); | |
this.data.state.fading[key] = true; | |
} | |
else if (Math.floor(visible_times.time[lastKey]) < Math.floor(visible_times.old_time[lastKey])) { | |
this.radialFade(x, y, color, 0, key); | |
this.data.state.fading[key] = true; | |
} | |
} | |
if (!this.data.state.fading[key]) { | |
this.drawArc(x, y, color, visible_times.pct[key]); | |
} | |
} | |
else { | |
this.animateArc(x, y, color, visible_times.pct[key], visible_times.old_pct[key], (new Date()).getTime() + tick_duration); | |
} | |
} | |
lastKey = key; | |
j++; | |
} | |
// Dont request another update if we should be paused | |
if(this.data.paused || nodraw) { | |
return; | |
} | |
// We need this for our next frame either way | |
var _this = this; | |
var update = function() { | |
_this.update.call(_this); | |
}; | |
// Either call next update immediately, or in a second | |
if (this.config.animation === "smooth") { | |
// Smooth animation, Queue up the next frame | |
this.data.animation_frame = useWindow.requestAnimationFrame(update, _this.element, _this); | |
} | |
else { | |
// Tick animation, Don't queue until very slightly after the next second happens | |
var delay = (diff % 1) * 1000; | |
if (delay < 0) | |
delay = 1000 + delay; | |
delay += 50; | |
_this.data.animation_frame = useWindow.setTimeout(function() { | |
_this.data.animation_frame = useWindow.requestAnimationFrame(update, _this.element, _this); | |
}, delay); | |
} | |
}; | |
TC_Instance.prototype.animateArc = function(x, y, color, target_pct, cur_pct, animation_end) { | |
if (this.data.attributes.context === null) | |
return; | |
var diff = cur_pct - target_pct; | |
if (Math.abs(diff) > 0.5) { | |
if (target_pct === 0) { | |
this.radialFade(x, y, color, 1); | |
} | |
else { | |
this.radialFade(x, y, color, 0); | |
} | |
} | |
else { | |
var progress = (tick_duration - (animation_end - (new Date()).getTime())) / tick_duration; | |
if (progress > 1) | |
progress = 1; | |
var pct = (cur_pct * (1 - progress)) + (target_pct * progress); | |
this.drawArc(x, y, color, pct); | |
//var show_pct = | |
if (progress >= 1) | |
return; | |
var _this = this; | |
useWindow.requestAnimationFrame(function() { | |
_this.animateArc(x, y, color, target_pct, cur_pct, animation_end); | |
}, this.element); | |
} | |
}; | |
TC_Instance.prototype.drawArc = function(x, y, color, pct) { | |
if (this.data.attributes.context === null) | |
return; | |
var clear_radius = Math.max(this.data.attributes.outer_radius, this.data.attributes.item_size / 2); | |
if(!limited_mode) { | |
this.data.attributes.context.clearRect( | |
x - clear_radius, | |
y - clear_radius, | |
clear_radius * 2, | |
clear_radius * 2 | |
); | |
} | |
if (this.config.use_background) { | |
this.data.attributes.context.beginPath(); | |
this.data.attributes.context.arc(x, y, this.data.attributes.radius, 0, 2 * Math.PI, false); | |
this.data.attributes.context.lineWidth = this.data.attributes.line_width * this.config.bg_width; | |
// line color | |
this.data.attributes.context.strokeStyle = this.config.circle_bg_color; | |
this.data.attributes.context.stroke(); | |
} | |
// Direction | |
var startAngle, endAngle, counterClockwise; | |
var defaultOffset = (-0.5 * Math.PI); | |
var fullCircle = 2 * Math.PI; | |
startAngle = defaultOffset + (this.config.start_angle / 360 * fullCircle); | |
var offset = (2 * pct * Math.PI); | |
if (this.config.direction === "Both") { | |
counterClockwise = false; | |
startAngle -= (offset / 2); | |
endAngle = startAngle + offset; | |
} | |
else { | |
if (this.config.direction === "Clockwise") { | |
counterClockwise = false; | |
endAngle = startAngle + offset; | |
} | |
else { | |
counterClockwise = true; | |
endAngle = startAngle - offset; | |
} | |
} | |
this.data.attributes.context.beginPath(); | |
this.data.attributes.context.arc(x, y, this.data.attributes.radius, startAngle, endAngle, counterClockwise); | |
this.data.attributes.context.lineWidth = this.data.attributes.line_width; | |
// line color | |
this.data.attributes.context.strokeStyle = color; | |
this.data.attributes.context.stroke(); | |
}; | |
TC_Instance.prototype.radialFade = function(x, y, color, from, key) { | |
// TODO: Make fade_time option | |
var rgb = hexToRgb(color); | |
var _this = this; // We have a few inner scopes here that will need access to our instance | |
var step = 0.2 * ((from === 1) ? -1 : 1); | |
var i; | |
for (i = 0; from <= 1 && from >= 0; i++) { | |
// Create inner scope so our variables are not changed by the time the Timeout triggers | |
(function() { | |
var delay = 50 * i; | |
var rgba = "rgba(" + rgb.r + ", " + rgb.g + ", " + rgb.b + ", " + (Math.round(from * 10) / 10) + ")"; | |
useWindow.setTimeout(function() { | |
_this.drawArc(x, y, rgba, 1); | |
}, delay); | |
}()); | |
from += step; | |
} | |
if (typeof key !== undefined) { | |
useWindow.setTimeout(function() { | |
_this.data.state.fading[key] = false; | |
}, 50 * i); | |
} | |
}; | |
TC_Instance.prototype.timeLeft = function() { | |
if (this.data.paused && typeof this.data.timer === "number") { | |
return this.data.timer; | |
} | |
var now = new Date(); | |
return ((this.data.attributes.ref_date - now) / 1000); | |
}; | |
TC_Instance.prototype.start = function() { | |
useWindow.cancelAnimationFrame(this.data.animation_frame); | |
useWindow.clearTimeout(this.data.animation_frame) | |
// Check if a date was passed in html attribute or jquery data | |
var attr_data_date = $(this.element).data('date'); | |
if (typeof attr_data_date === "undefined") { | |
attr_data_date = $(this.element).attr('data-date'); | |
} | |
if (typeof attr_data_date === "string") { | |
this.data.attributes.ref_date = parse_date(attr_data_date); | |
} | |
// Check if this is an unpause of a timer | |
else if (typeof this.data.timer === "number") { | |
if (this.data.paused) { | |
this.data.attributes.ref_date = (new Date()).getTime() + (this.data.timer * 1000); | |
} | |
} | |
else { | |
// Try to get data-timer | |
var attr_data_timer = $(this.element).data('timer'); | |
if (typeof attr_data_timer === "undefined") { | |
attr_data_timer = $(this.element).attr('data-timer'); | |
} | |
if (typeof attr_data_timer === "string") { | |
attr_data_timer = parseFloat(attr_data_timer); | |
} | |
if (typeof attr_data_timer === "number") { | |
this.data.timer = attr_data_timer; | |
this.data.attributes.ref_date = (new Date()).getTime() + (attr_data_timer * 1000); | |
} | |
else { | |
// data-timer and data-date were both not set | |
// use config date | |
this.data.attributes.ref_date = this.config.ref_date; | |
} | |
} | |
// Start running | |
this.data.paused = false; | |
this.update.call(this); | |
}; | |
TC_Instance.prototype.restart = function() { | |
this.data.timer = false; | |
this.start(); | |
}; | |
TC_Instance.prototype.stop = function() { | |
if (typeof this.data.timer === "number") { | |
this.data.timer = this.timeLeft(this); | |
} | |
// Stop running | |
this.data.paused = true; | |
useWindow.cancelAnimationFrame(this.data.animation_frame); | |
}; | |
TC_Instance.prototype.destroy = function() { | |
this.clearListeners(); | |
this.stop(); | |
useWindow.clearInterval(this.data.interval_fallback); | |
this.data.interval_fallback = null; | |
this.container.remove(); | |
$(this.element).removeAttr('data-tc-id'); | |
$(this.element).removeData('tc-id'); | |
}; | |
TC_Instance.prototype.setOptions = function(options) { | |
if (this.config === null) { | |
this.default_options.ref_date = new Date(); | |
this.config = $.extend(true, {}, this.default_options); | |
} | |
$.extend(true, this.config, options); | |
// Use window.top if use_top_frame is true | |
if(this.config.use_top_frame) { | |
useWindow = window.top; | |
} | |
else { | |
useWindow = window; | |
} | |
updateUsedWindow(); | |
this.data.total_duration = this.config.total_duration; | |
if (typeof this.data.total_duration === "string") { | |
if (typeof secondsIn[this.data.total_duration] !== "undefined") { | |
// If set to Years, Months, Days, Hours or Minutes, fetch the secondsIn value for that | |
this.data.total_duration = secondsIn[this.data.total_duration]; | |
} | |
else if (this.data.total_duration === "Auto") { | |
// If set to auto, total_duration is the size of 1 unit, of the unit type bigger than the largest shown | |
for(var i = 0; i < Object.keys(this.config.time).length; i++) { | |
var unit = Object.keys(this.config.time)[i]; | |
if (this.config.time[unit].show) { | |
this.data.total_duration = secondsIn[nextUnits[unit]]; | |
break; | |
} | |
} | |
} | |
else { | |
// If it's a string, but neither of the above, user screwed up. | |
this.data.total_duration = secondsIn["Years"]; | |
console.error("Valid values for TimeCircles config.total_duration are either numeric, or (string) Years, Months, Days, Hours, Minutes, Auto"); | |
} | |
} | |
}; | |
TC_Instance.prototype.addListener = function(f, context, type) { | |
if (typeof f !== "function") | |
return; | |
if (typeof type === "undefined") | |
type = "visible"; | |
this.listeners[type].push({func: f, scope: context}); | |
}; | |
TC_Instance.prototype.notifyListeners = function(unit, value, total, type) { | |
for (var i = 0; i < this.listeners[type].length; i++) { | |
var listener = this.listeners[type][i]; | |
listener.func.apply(listener.scope, [unit, value, total]); | |
} | |
}; | |
TC_Instance.prototype.default_options = { | |
ref_date: new Date(), | |
start: true, | |
animation: "smooth", | |
count_past_zero: true, | |
circle_bg_color: "#60686F", | |
use_background: true, | |
fg_width: 0.1, | |
bg_width: 1.2, | |
text_size: 0.07, | |
total_duration: "Auto", | |
direction: "Clockwise", | |
use_top_frame: false, | |
start_angle: 0, | |
time: { | |
Days: { | |
show: true, | |
text: "Days", | |
color: "#FC6" | |
}, | |
Hours: { | |
show: true, | |
text: "Hours", | |
color: "#9CF" | |
}, | |
Minutes: { | |
show: true, | |
text: "Minutes", | |
color: "#BFB" | |
}, | |
Seconds: { | |
show: true, | |
text: "Seconds", | |
color: "#F99" | |
} | |
} | |
}; | |
// Time circle class | |
var TC_Class = function(elements, options) { | |
this.elements = elements; | |
this.options = options; | |
this.foreach(); | |
}; | |
TC_Class.prototype.getInstance = function(element) { | |
var instance; | |
var cur_id = $(element).data("tc-id"); | |
if (typeof cur_id === "undefined") { | |
cur_id = guid(); | |
$(element).attr("data-tc-id", cur_id); | |
} | |
if (typeof TC_Instance_List[cur_id] === "undefined") { | |
var options = this.options; | |
var element_options = $(element).data('options'); | |
if (typeof element_options === "string") { | |
element_options = JSON.parse(element_options); | |
} | |
if (typeof element_options === "object") { | |
options = $.extend(true, {}, this.options, element_options); | |
} | |
instance = new TC_Instance(element, options); | |
TC_Instance_List[cur_id] = instance; | |
} | |
else { | |
instance = TC_Instance_List[cur_id]; | |
if (typeof this.options !== "undefined") { | |
instance.setOptions(this.options); | |
} | |
} | |
return instance; | |
}; | |
TC_Class.prototype.addTime = function(seconds_to_add) { | |
this.foreach(function(instance) { | |
instance.addTime(seconds_to_add); | |
}); | |
}; | |
TC_Class.prototype.foreach = function(callback) { | |
var _this = this; | |
this.elements.each(function() { | |
var instance = _this.getInstance(this); | |
if (typeof callback === "function") { | |
callback(instance); | |
} | |
}); | |
return this; | |
}; | |
TC_Class.prototype.start = function() { | |
this.foreach(function(instance) { | |
instance.start(); | |
}); | |
return this; | |
}; | |
TC_Class.prototype.stop = function() { | |
this.foreach(function(instance) { | |
instance.stop(); | |
}); | |
return this; | |
}; | |
TC_Class.prototype.restart = function() { | |
this.foreach(function(instance) { | |
instance.restart(); | |
}); | |
return this; | |
}; | |
TC_Class.prototype.rebuild = function() { | |
this.foreach(function(instance) { | |
instance.initialize(false); | |
}); | |
return this; | |
}; | |
TC_Class.prototype.getTime = function() { | |
return this.getInstance(this.elements[0]).timeLeft(); | |
}; | |
TC_Class.prototype.addListener = function(f, type) { | |
if (typeof type === "undefined") | |
type = "visible"; | |
var _this = this; | |
this.foreach(function(instance) { | |
instance.addListener(f, _this.elements, type); | |
}); | |
return this; | |
}; | |
TC_Class.prototype.destroy = function() { | |
this.foreach(function(instance) { | |
instance.destroy(); | |
}); | |
return this; | |
}; | |
TC_Class.prototype.end = function() { | |
return this.elements; | |
}; | |
$.fn.TimeCircles = function(options) { | |
return new TC_Class(this, options); | |
}; | |
}(jQuery)); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment