| 1 |
/* |
| 2 |
* jQuery-Simple-Timer v0.0.5 |
| 3 |
* https://github.com/caike/jQuery-Simple-Timer |
| 4 |
* |
| 5 |
* Creates a countdown timer. |
| 6 |
* |
| 7 |
* Example: |
| 8 |
* $('.timer').startTimer(); |
| 9 |
* |
| 10 |
*/ |
| 11 |
|
| 12 |
/* KEEP ALL "BOXY" MODIFICATIONS */ |
| 13 |
|
| 14 |
(function (factory) { |
| 15 |
// Using as a CommonJS module |
| 16 |
if(typeof module === "object" && typeof module.exports === "object") { |
| 17 |
// jQuery must be provided as argument when used |
| 18 |
// as a CommonJS module. |
| 19 |
// |
| 20 |
// For example: |
| 21 |
// let $ = require("jquery"); |
| 22 |
// require("jquery-simple-timer")($); |
| 23 |
module.exports = function(jq) { |
| 24 |
factory(jq, window, document); |
| 25 |
} |
| 26 |
} else { |
| 27 |
// Using as script tag |
| 28 |
// |
| 29 |
// For example: |
| 30 |
// <script src="jquery.simple.timer.js"></script> |
| 31 |
factory(jQuery, window, document); |
| 32 |
} |
| 33 |
}(function($, window, document, undefined) { |
| 34 |
|
| 35 |
var timer; |
| 36 |
|
| 37 |
var Timer = function(targetElement){ |
| 38 |
this._options = {}; |
| 39 |
this.targetElement = targetElement; |
| 40 |
return this; |
| 41 |
}; |
| 42 |
|
| 43 |
Timer.start = function(userOptions, targetElement){ |
| 44 |
timer = new Timer(targetElement); |
| 45 |
mergeOptions(timer, userOptions); |
| 46 |
return timer.start(userOptions); |
| 47 |
}; |
| 48 |
|
| 49 |
// Writes to `this._options` object so that other |
| 50 |
// functions can access it without having to |
| 51 |
// pass this object as argument multiple times. |
| 52 |
function mergeOptions(timer, opts) { |
| 53 |
opts = opts || {}; |
| 54 |
var classNames = opts.classNames || {}; |
| 55 |
|
| 56 |
timer._options.classNameSeconds = classNames.seconds || 'jst-seconds' |
| 57 |
, timer._options.classNameMinutes = classNames.minutes || 'jst-minutes' |
| 58 |
, timer._options.classNameHours = classNames.hours || 'jst-hours' |
| 59 |
, timer._options.classNameClearDiv = classNames.clearDiv || 'jst-clearDiv' |
| 60 |
, timer._options.classNameTimeout = classNames.timeout || 'jst-timeout'; |
| 61 |
} |
| 62 |
|
| 63 |
Timer.prototype.start = function(options) { |
| 64 |
|
| 65 |
var that = this; |
| 66 |
|
| 67 |
var createSubDivs = function(timerBoxElement){ |
| 68 |
var seconds = document.createElement('div'); |
| 69 |
seconds.className = that._options.classNameSeconds; |
| 70 |
|
| 71 |
var minutes = document.createElement('div'); |
| 72 |
minutes.className = that._options.classNameMinutes; |
| 73 |
|
| 74 |
var hours = document.createElement('div'); |
| 75 |
hours.className = that._options.classNameHours; |
| 76 |
|
| 77 |
var clearDiv = document.createElement('div'); |
| 78 |
clearDiv.className = that._options.classNameClearDiv; |
| 79 |
|
| 80 |
return timerBoxElement. |
| 81 |
append(hours). |
| 82 |
append(minutes). |
| 83 |
append(seconds). |
| 84 |
append(clearDiv); |
| 85 |
}; |
| 86 |
|
| 87 |
this.targetElement.each(function(_index, timerBox) { |
| 88 |
var that = this; |
| 89 |
var timerBoxElement = $(timerBox); |
| 90 |
var cssClassSnapshot = timerBoxElement.attr('class'); |
| 91 |
|
| 92 |
timerBoxElement.on('complete', function() { |
| 93 |
clearInterval(timerBoxElement.intervalId); |
| 94 |
}); |
| 95 |
|
| 96 |
timerBoxElement.on('complete', function() { |
| 97 |
timerBoxElement.onComplete(timerBoxElement); |
| 98 |
}); |
| 99 |
|
| 100 |
timerBoxElement.on('complete', function(){ |
| 101 |
timerBoxElement.addClass(that._options.classNameTimeout); |
| 102 |
}); |
| 103 |
|
| 104 |
timerBoxElement.on('complete', function(){ |
| 105 |
if(options && options.loop === true) { |
| 106 |
timer.resetTimer(timerBoxElement, options, cssClassSnapshot); |
| 107 |
} |
| 108 |
}); |
| 109 |
|
| 110 |
timerBoxElement.on('pause', function() { |
| 111 |
clearInterval(timerBoxElement.intervalId); |
| 112 |
timerBoxElement.paused = true; |
| 113 |
}); |
| 114 |
|
| 115 |
timerBoxElement.on('resume', function() { |
| 116 |
timerBoxElement.paused = false; |
| 117 |
that.startCountdown(timerBoxElement, { secondsLeft: timerBoxElement.data('timeLeft') }); |
| 118 |
}); |
| 119 |
|
| 120 |
createSubDivs(timerBoxElement); |
| 121 |
return this.startCountdown(timerBoxElement, options); |
| 122 |
}.bind(this)); |
| 123 |
}; |
| 124 |
|
| 125 |
/** |
| 126 |
* Resets timer and add css class 'loop' to indicate the timer is in a loop. |
| 127 |
* $timerBox {jQuery object} - The timer element |
| 128 |
* options {object} - The options for the timer |
| 129 |
* css - The original css of the element |
| 130 |
*/ |
| 131 |
Timer.prototype.resetTimer = function($timerBox, options, css) { |
| 132 |
var interval = 0; |
| 133 |
if(options.loopInterval) { |
| 134 |
interval = parseInt(options.loopInterval, 10) * 1000; |
| 135 |
} |
| 136 |
setTimeout(function() { |
| 137 |
$timerBox.trigger('reset'); |
| 138 |
$timerBox.attr('class', css + ' loop'); |
| 139 |
timer.startCountdown($timerBox, options); |
| 140 |
}, interval); |
| 141 |
} |
| 142 |
|
| 143 |
Timer.prototype.fetchSecondsLeft = function(element){ |
| 144 |
var secondsLeft = element.data('seconds-left'); |
| 145 |
var minutesLeft = element.data('minutes-left'); |
| 146 |
|
| 147 |
if(Number.isFinite(secondsLeft)){ |
| 148 |
return parseInt(secondsLeft, 10); |
| 149 |
} else if(Number.isFinite(minutesLeft)) { |
| 150 |
return parseFloat(minutesLeft) * 60; |
| 151 |
}else { |
| 152 |
throw 'Missing time data'; |
| 153 |
} |
| 154 |
}; |
| 155 |
|
| 156 |
Timer.prototype.startCountdown = function(element, options) { |
| 157 |
options = options || {}; |
| 158 |
|
| 159 |
var intervalId = null; |
| 160 |
var defaultComplete = function(){ |
| 161 |
clearInterval(intervalId); |
| 162 |
return this.clearTimer(element); |
| 163 |
}.bind(this); |
| 164 |
|
| 165 |
element.onComplete = options.onComplete || defaultComplete; |
| 166 |
element.allowPause = options.allowPause || false; |
| 167 |
if(element.allowPause){ |
| 168 |
element.on('click', function() { |
| 169 |
if(element.paused){ |
| 170 |
element.trigger('resume'); |
| 171 |
}else{ |
| 172 |
element.trigger('pause'); |
| 173 |
} |
| 174 |
}); |
| 175 |
} |
| 176 |
|
| 177 |
var secondsLeft = options.secondsLeft || this.fetchSecondsLeft(element); |
| 178 |
|
| 179 |
var refreshRate = options.refreshRate || 1000; |
| 180 |
var endTime = secondsLeft + this.currentTime(); |
| 181 |
var timeLeft = endTime - this.currentTime(); |
| 182 |
|
| 183 |
this.setFinalValue(this.formatTimeLeft(timeLeft), element); |
| 184 |
|
| 185 |
intervalId = setInterval((function() { |
| 186 |
timeLeft = endTime - this.currentTime(); |
| 187 |
// When timer has been idle and only resumed past timeout, |
| 188 |
// then we immediatelly complete the timer. |
| 189 |
if(timeLeft < 0 ){ |
| 190 |
timeLeft = 0; |
| 191 |
} |
| 192 |
element.data('timeLeft', timeLeft); |
| 193 |
this.setFinalValue(this.formatTimeLeft(timeLeft), element); |
| 194 |
|
| 195 |
/* BOXY */ element.trigger( 'update', timeLeft ); |
| 196 |
|
| 197 |
}.bind(this)), refreshRate); |
| 198 |
|
| 199 |
element.intervalId = intervalId; |
| 200 |
}; |
| 201 |
|
| 202 |
Timer.prototype.clearTimer = function(element){ |
| 203 |
element.find('.jst-seconds').text('00'); |
| 204 |
element.find('.jst-minutes').text('00:'); |
| 205 |
element.find('.jst-hours').text('00:'); |
| 206 |
}; |
| 207 |
|
| 208 |
Timer.prototype.currentTime = function() { |
| 209 |
return Math.round((new Date()).getTime() / 1000); |
| 210 |
}; |
| 211 |
|
| 212 |
Timer.prototype.formatTimeLeft = function(timeLeft) { |
| 213 |
|
| 214 |
var lpad = function(n, width) { |
| 215 |
width = width || 2; |
| 216 |
n = n + ''; |
| 217 |
|
| 218 |
var padded = null; |
| 219 |
|
| 220 |
if (n.length >= width) { |
| 221 |
padded = n; |
| 222 |
} else { |
| 223 |
padded = Array(width - n.length + 1).join(0) + n; |
| 224 |
} |
| 225 |
|
| 226 |
return padded; |
| 227 |
}; |
| 228 |
|
| 229 |
var hours = Math.floor(timeLeft / 3600); |
| 230 |
timeLeft -= hours * 3600; |
| 231 |
|
| 232 |
var minutes = Math.floor(timeLeft / 60); |
| 233 |
timeLeft -= minutes * 60; |
| 234 |
|
| 235 |
var seconds = parseInt(timeLeft % 60, 10); |
| 236 |
|
| 237 |
if (+hours === 0 && +minutes === 0 && +seconds === 0) { |
| 238 |
return []; |
| 239 |
} else { |
| 240 |
return [lpad(hours), lpad(minutes), lpad(seconds)]; |
| 241 |
} |
| 242 |
}; |
| 243 |
|
| 244 |
Timer.prototype.setFinalValue = function(finalValues, element) { |
| 245 |
|
| 246 |
if(finalValues.length === 0){ |
| 247 |
this.clearTimer(element); |
| 248 |
element.trigger('complete'); |
| 249 |
return false; |
| 250 |
} |
| 251 |
|
| 252 |
element.find('.' + this._options.classNameSeconds).text(finalValues.pop()); |
| 253 |
element.find('.' + this._options.classNameMinutes).text(finalValues.pop() + ':'); |
| 254 |
element.find('.' + this._options.classNameHours).text(finalValues.pop() + ':'); |
| 255 |
}; |
| 256 |
|
| 257 |
|
| 258 |
$.fn.startTimer = function(options) { |
| 259 |
this.TimerObject = Timer; |
| 260 |
Timer.start(options, this); |
| 261 |
return this; |
| 262 |
}; |
| 263 |
|
| 264 |
})); |
| 265 |
|