version 1.1, 2012/03/18 13:51:11
|
version 1.2, 2014/01/15 14:45:11
|
Line 1
|
Line 1
|
/* http://keith-wood.name/countdown.html |
/* http://keith-wood.name/countdown.html |
Countdown for jQuery v1.5.11. |
Countdown for jQuery v1.6.3. |
Written by Keith Wood (kbwood{at}iinet.com.au) January 2008. |
Written by Keith Wood (kbwood{at}iinet.com.au) January 2008. |
Dual licensed under the GPL (http://dev.jquery.com/browser/trunk/jquery/GPL-LICENSE.txt) and |
Available under the MIT (https://github.com/jquery/jquery/blob/master/MIT-LICENSE.txt) license. |
MIT (http://dev.jquery.com/browser/trunk/jquery/MIT-LICENSE.txt) licenses. |
|
Please attribute the author if you use it. */ |
Please attribute the author if you use it. */ |
|
|
/* Display a countdown timer. |
/* Display a countdown timer. |
Line 22 function Countdown() {
|
Line 21 function Countdown() {
|
labels1: ['Year', 'Month', 'Week', 'Day', 'Hour', 'Minute', 'Second'], |
labels1: ['Year', 'Month', 'Week', 'Day', 'Hour', 'Minute', 'Second'], |
compactLabels: ['y', 'm', 'w', 'd'], // The compact texts for the counters |
compactLabels: ['y', 'm', 'w', 'd'], // The compact texts for the counters |
whichLabels: null, // Function to determine which labels to use |
whichLabels: null, // Function to determine which labels to use |
|
digits: ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9'], // The digits to display |
timeSeparator: ':', // Separator for time periods |
timeSeparator: ':', // Separator for time periods |
isRTL: false // True for right-to-left languages, false for left-to-right |
isRTL: false // True for right-to-left languages, false for left-to-right |
}; |
}; |
Line 53 function Countdown() {
|
Line 53 function Countdown() {
|
}; |
}; |
$.extend(this._defaults, this.regional['']); |
$.extend(this._defaults, this.regional['']); |
this._serverSyncs = []; |
this._serverSyncs = []; |
|
var now = (typeof Date.now == 'function' ? Date.now : |
|
function() { return new Date().getTime(); }); |
|
var perfAvail = (window.performance && typeof window.performance.now == 'function'); |
// Shared timer for all countdowns |
// Shared timer for all countdowns |
function timerCallBack(timestamp) { |
function timerCallBack(timestamp) { |
var drawStart = (timestamp || new Date().getTime()); |
var drawStart = (timestamp < 1e12 ? // New HTML5 high resolution timer |
|
(perfAvail ? (performance.now() + performance.timing.navigationStart) : now()) : |
|
// Integer milliseconds since unix epoch |
|
timestamp || now()); |
if (drawStart - animationStartTime >= 1000) { |
if (drawStart - animationStartTime >= 1000) { |
$.countdown._updateTargets(); |
plugin._updateTargets(); |
animationStartTime = drawStart; |
animationStartTime = drawStart; |
} |
} |
requestAnimationFrame(timerCallBack); |
requestAnimationFrame(timerCallBack); |
} |
} |
var requestAnimationFrame = window.requestAnimationFrame || window.webkitRequestAnimationFrame || |
var requestAnimationFrame = window.requestAnimationFrame || |
window.mozRequestAnimationFrame || window.oRequestAnimationFrame || |
window.webkitRequestAnimationFrame || window.mozRequestAnimationFrame || |
window.msRequestAnimationFrame || null; // this is when we expect a fall-back to setInterval as it's much more fluid |
window.oRequestAnimationFrame || window.msRequestAnimationFrame || null; |
|
// This is when we expect a fall-back to setInterval as it's much more fluid |
var animationStartTime = 0; |
var animationStartTime = 0; |
if (!requestAnimationFrame) { |
if (!requestAnimationFrame || $.noRequestAnimationFrame) { |
setInterval(function() { $.countdown._updateTargets(); }, 980); // Fall back to good old setInterval |
$.noRequestAnimationFrame = null; |
|
setInterval(function() { plugin._updateTargets(); }, 980); // Fall back to good old setInterval |
} |
} |
else { |
else { |
animationStartTime = window.mozAnimationStartTime || new Date().getTime(); |
animationStartTime = window.animationStartTime || |
|
window.webkitAnimationStartTime || window.mozAnimationStartTime || |
|
window.oAnimationStartTime || window.msAnimationStartTime || now(); |
requestAnimationFrame(timerCallBack); |
requestAnimationFrame(timerCallBack); |
} |
} |
} |
} |
|
|
var PROP_NAME = 'countdown'; |
|
|
|
var Y = 0; // Years |
var Y = 0; // Years |
var O = 1; // Months |
var O = 1; // Months |
var W = 2; // Weeks |
var W = 2; // Weeks |
Line 88 var S = 6; // Seconds
|
Line 96 var S = 6; // Seconds
|
$.extend(Countdown.prototype, { |
$.extend(Countdown.prototype, { |
/* Class name added to elements to indicate already configured with countdown. */ |
/* Class name added to elements to indicate already configured with countdown. */ |
markerClassName: 'hasCountdown', |
markerClassName: 'hasCountdown', |
|
/* Name of the data property for instance settings. */ |
|
propertyName: 'countdown', |
|
|
|
/* Class name for the right-to-left marker. */ |
|
_rtlClass: 'countdown_rtl', |
|
/* Class name for the countdown section marker. */ |
|
_sectionClass: 'countdown_section', |
|
/* Class name for the period amount marker. */ |
|
_amountClass: 'countdown_amount', |
|
/* Class name for the countdown row marker. */ |
|
_rowClass: 'countdown_row', |
|
/* Class name for the holding countdown marker. */ |
|
_holdingClass: 'countdown_holding', |
|
/* Class name for the showing countdown marker. */ |
|
_showClass: 'countdown_show', |
|
/* Class name for the description marker. */ |
|
_descrClass: 'countdown_descr', |
|
|
/* List of currently active countdown targets. */ |
/* List of currently active countdown targets. */ |
_timerTargets: [], |
_timerTargets: [], |
Line 96 $.extend(Countdown.prototype, {
|
Line 121 $.extend(Countdown.prototype, {
|
@param options (object) the new settings to use as defaults */ |
@param options (object) the new settings to use as defaults */ |
setDefaults: function(options) { |
setDefaults: function(options) { |
this._resetExtraLabels(this._defaults, options); |
this._resetExtraLabels(this._defaults, options); |
extendRemove(this._defaults, options || {}); |
$.extend(this._defaults, options || {}); |
}, |
}, |
|
|
/* Convert a date/time to UTC. |
/* Convert a date/time to UTC. |
Line 141 $.extend(Countdown.prototype, {
|
Line 166 $.extend(Countdown.prototype, {
|
periods[3] * 86400 + periods[4] * 3600 + periods[5] * 60 + periods[6]; |
periods[3] * 86400 + periods[4] * 3600 + periods[5] * 60 + periods[6]; |
}, |
}, |
|
|
/* Retrieve one or more settings values. |
|
@param name (string, optional) the name of the setting to retrieve |
|
or 'all' for all instance settings or omit for all default settings |
|
@return (any) the requested setting(s) */ |
|
_settingsCountdown: function(target, name) { |
|
if (!name) { |
|
return $.countdown._defaults; |
|
} |
|
var inst = $.data(target, PROP_NAME); |
|
return (name == 'all' ? inst.options : inst.options[name]); |
|
}, |
|
|
|
/* Attach the countdown widget to a div. |
/* Attach the countdown widget to a div. |
@param target (element) the containing division |
@param target (element) the containing division |
@param options (object) the initial settings for the countdown */ |
@param options (object) the initial settings for the countdown */ |
_attachCountdown: function(target, options) { |
_attachPlugin: function(target, options) { |
var $target = $(target); |
target = $(target); |
if ($target.hasClass(this.markerClassName)) { |
if (target.hasClass(this.markerClassName)) { |
return; |
return; |
} |
} |
$target.addClass(this.markerClassName); |
var inst = {options: $.extend({}, this._defaults), _periods: [0, 0, 0, 0, 0, 0, 0]}; |
var inst = {options: $.extend({}, options), |
target.addClass(this.markerClassName).data(this.propertyName, inst); |
_periods: [0, 0, 0, 0, 0, 0, 0]}; |
this._optionPlugin(target, options); |
$.data(target, PROP_NAME, inst); |
|
this._changeCountdown(target); |
|
}, |
}, |
|
|
/* Add a target to the list of active ones. |
/* Add a target to the list of active ones. |
Line 197 $.extend(Countdown.prototype, {
|
Line 208 $.extend(Countdown.prototype, {
|
} |
} |
}, |
}, |
|
|
|
/* Reconfigure the settings for a countdown div. |
|
@param target (element) the control to affect |
|
@param options (object) the new options for this instance or |
|
(string) an individual property name |
|
@param value (any) the individual property value (omit if options |
|
is an object or to retrieve the value of a setting) |
|
@return (any) if retrieving a value */ |
|
_optionPlugin: function(target, options, value) { |
|
target = $(target); |
|
var inst = target.data(this.propertyName); |
|
if (!options || (typeof options == 'string' && value == null)) { // Get option |
|
var name = options; |
|
options = (inst || {}).options; |
|
return (options && name ? options[name] : options); |
|
} |
|
|
|
if (!target.hasClass(this.markerClassName)) { |
|
return; |
|
} |
|
options = options || {}; |
|
if (typeof options == 'string') { |
|
var name = options; |
|
options = {}; |
|
options[name] = value; |
|
} |
|
if (options.layout) { |
|
options.layout = options.layout.replace(/</g, '<').replace(/>/g, '>'); |
|
} |
|
this._resetExtraLabels(inst.options, options); |
|
var timezoneChanged = (inst.options.timezone != options.timezone); |
|
$.extend(inst.options, options); |
|
this._adjustSettings(target, inst, |
|
options.until != null || options.since != null || timezoneChanged); |
|
var now = new Date(); |
|
if ((inst._since && inst._since < now) || (inst._until && inst._until > now)) { |
|
this._addTarget(target[0]); |
|
} |
|
this._updateCountdown(target, inst); |
|
}, |
|
|
/* Redisplay the countdown with an updated display. |
/* Redisplay the countdown with an updated display. |
@param target (jQuery) the containing division |
@param target (jQuery) the containing division |
@param inst (object) the current settings for this instance */ |
@param inst (object) the current settings for this instance */ |
_updateCountdown: function(target, inst) { |
_updateCountdown: function(target, inst) { |
var $target = $(target); |
var $target = $(target); |
inst = inst || $.data(target, PROP_NAME); |
inst = inst || $target.data(this.propertyName); |
if (!inst) { |
if (!inst) { |
return; |
return; |
} |
} |
$target.html(this._generateHTML(inst)); |
$target.html(this._generateHTML(inst)).toggleClass(this._rtlClass, inst.options.isRTL); |
$target[(this._get(inst, 'isRTL') ? 'add' : 'remove') + 'Class']('countdown_rtl'); |
if ($.isFunction(inst.options.onTick)) { |
var onTick = this._get(inst, 'onTick'); |
|
if (onTick) { |
|
var periods = inst._hold != 'lap' ? inst._periods : |
var periods = inst._hold != 'lap' ? inst._periods : |
this._calculatePeriods(inst, inst._show, this._get(inst, 'significant'), new Date()); |
this._calculatePeriods(inst, inst._show, inst.options.significant, new Date()); |
var tickInterval = this._get(inst, 'tickInterval'); |
if (inst.options.tickInterval == 1 || |
if (tickInterval == 1 || this.periodsToSeconds(periods) % tickInterval == 0) { |
this.periodsToSeconds(periods) % inst.options.tickInterval == 0) { |
onTick.apply(target, [periods]); |
inst.options.onTick.apply(target, [periods]); |
} |
} |
} |
} |
var expired = inst._hold != 'pause' && |
var expired = inst._hold != 'pause' && |
Line 222 $.extend(Countdown.prototype, {
|
Line 271 $.extend(Countdown.prototype, {
|
inst._now.getTime() >= inst._until.getTime()); |
inst._now.getTime() >= inst._until.getTime()); |
if (expired && !inst._expiring) { |
if (expired && !inst._expiring) { |
inst._expiring = true; |
inst._expiring = true; |
if (this._hasTarget(target) || this._get(inst, 'alwaysExpire')) { |
if (this._hasTarget(target) || inst.options.alwaysExpire) { |
this._removeTarget(target); |
this._removeTarget(target); |
var onExpiry = this._get(inst, 'onExpiry'); |
if ($.isFunction(inst.options.onExpiry)) { |
if (onExpiry) { |
inst.options.onExpiry.apply(target, []); |
onExpiry.apply(target, []); |
} |
} |
if (inst.options.expiryText) { |
var expiryText = this._get(inst, 'expiryText'); |
var layout = inst.options.layout; |
if (expiryText) { |
inst.options.layout = inst.options.expiryText; |
var layout = this._get(inst, 'layout'); |
|
inst.options.layout = expiryText; |
|
this._updateCountdown(target, inst); |
this._updateCountdown(target, inst); |
inst.options.layout = layout; |
inst.options.layout = layout; |
} |
} |
var expiryUrl = this._get(inst, 'expiryUrl'); |
if (inst.options.expiryUrl) { |
if (expiryUrl) { |
window.location = inst.options.expiryUrl; |
window.location = expiryUrl; |
|
} |
} |
} |
} |
inst._expiring = false; |
inst._expiring = false; |
Line 245 $.extend(Countdown.prototype, {
|
Line 291 $.extend(Countdown.prototype, {
|
else if (inst._hold == 'pause') { |
else if (inst._hold == 'pause') { |
this._removeTarget(target); |
this._removeTarget(target); |
} |
} |
$.data(target, PROP_NAME, inst); |
$target.data(this.propertyName, inst); |
}, |
|
|
|
/* Reconfigure the settings for a countdown div. |
|
@param target (element) the containing division |
|
@param options (object) the new settings for the countdown or |
|
(string) an individual property name |
|
@param value (any) the individual property value |
|
(omit if options is an object) */ |
|
_changeCountdown: function(target, options, value) { |
|
options = options || {}; |
|
if (typeof options == 'string') { |
|
var name = options; |
|
options = {}; |
|
options[name] = value; |
|
} |
|
var inst = $.data(target, PROP_NAME); |
|
if (inst) { |
|
this._resetExtraLabels(inst.options, options); |
|
extendRemove(inst.options, options); |
|
this._adjustSettings(target, inst); |
|
$.data(target, PROP_NAME, inst); |
|
var now = new Date(); |
|
if ((inst._since && inst._since < now) || |
|
(inst._until && inst._until > now)) { |
|
this._addTarget(target); |
|
} |
|
this._updateCountdown(target, inst); |
|
} |
|
}, |
}, |
|
|
/* Reset any extra labelsn and compactLabelsn entries if changing labels. |
/* Reset any extra labelsn and compactLabelsn entries if changing labels. |
Line 289 $.extend(Countdown.prototype, {
|
Line 307 $.extend(Countdown.prototype, {
|
} |
} |
if (changingLabels) { |
if (changingLabels) { |
for (var n in base) { // Remove custom numbered labels |
for (var n in base) { // Remove custom numbered labels |
if (n.match(/[Ll]abels[0-9]/)) { |
if (n.match(/[Ll]abels[02-9]|compactLabels1/)) { |
base[n] = null; |
base[n] = null; |
} |
} |
} |
} |
Line 298 $.extend(Countdown.prototype, {
|
Line 316 $.extend(Countdown.prototype, {
|
|
|
/* Calculate interal settings for an instance. |
/* Calculate interal settings for an instance. |
@param target (element) the containing division |
@param target (element) the containing division |
@param inst (object) the current settings for this instance */ |
@param inst (object) the current settings for this instance |
_adjustSettings: function(target, inst) { |
@param recalc (boolean) true if until or since are set */ |
|
_adjustSettings: function(target, inst, recalc) { |
var now; |
var now; |
var serverSync = this._get(inst, 'serverSync'); |
|
var serverOffset = 0; |
var serverOffset = 0; |
var serverEntry = null; |
var serverEntry = null; |
for (var i = 0; i < this._serverSyncs.length; i++) { |
for (var i = 0; i < this._serverSyncs.length; i++) { |
if (this._serverSyncs[i][0] == serverSync) { |
if (this._serverSyncs[i][0] == inst.options.serverSync) { |
serverEntry = this._serverSyncs[i][1]; |
serverEntry = this._serverSyncs[i][1]; |
break; |
break; |
} |
} |
} |
} |
if (serverEntry != null) { |
if (serverEntry != null) { |
serverOffset = (serverSync ? serverEntry : 0); |
serverOffset = (inst.options.serverSync ? serverEntry : 0); |
now = new Date(); |
now = new Date(); |
} |
} |
else { |
else { |
var serverResult = (serverSync ? serverSync.apply(target, []) : null); |
var serverResult = ($.isFunction(inst.options.serverSync) ? |
|
inst.options.serverSync.apply(target, []) : null); |
now = new Date(); |
now = new Date(); |
serverOffset = (serverResult ? now.getTime() - serverResult.getTime() : 0); |
serverOffset = (serverResult ? now.getTime() - serverResult.getTime() : 0); |
this._serverSyncs.push([serverSync, serverOffset]); |
this._serverSyncs.push([inst.options.serverSync, serverOffset]); |
} |
} |
var timezone = this._get(inst, 'timezone'); |
var timezone = inst.options.timezone; |
timezone = (timezone == null ? -now.getTimezoneOffset() : timezone); |
timezone = (timezone == null ? -now.getTimezoneOffset() : timezone); |
inst._since = this._get(inst, 'since'); |
if (recalc || (!recalc && inst._until == null && inst._since == null)) { |
if (inst._since != null) { |
inst._since = inst.options.since; |
inst._since = this.UTCDate(timezone, this._determineTime(inst._since, null)); |
if (inst._since != null) { |
if (inst._since && serverOffset) { |
inst._since = this.UTCDate(timezone, this._determineTime(inst._since, null)); |
inst._since.setMilliseconds(inst._since.getMilliseconds() + serverOffset); |
if (inst._since && serverOffset) { |
|
inst._since.setMilliseconds(inst._since.getMilliseconds() + serverOffset); |
|
} |
|
} |
|
inst._until = this.UTCDate(timezone, this._determineTime(inst.options.until, now)); |
|
if (serverOffset) { |
|
inst._until.setMilliseconds(inst._until.getMilliseconds() + serverOffset); |
} |
} |
} |
|
inst._until = this.UTCDate(timezone, this._determineTime(this._get(inst, 'until'), now)); |
|
if (serverOffset) { |
|
inst._until.setMilliseconds(inst._until.getMilliseconds() + serverOffset); |
|
} |
} |
inst._show = this._determineShow(inst); |
inst._show = this._determineShow(inst); |
}, |
}, |
|
|
/* Remove the countdown widget from a div. |
/* Remove the countdown widget from a div. |
@param target (element) the containing division */ |
@param target (element) the containing division */ |
_destroyCountdown: function(target) { |
_destroyPlugin: function(target) { |
var $target = $(target); |
target = $(target); |
if (!$target.hasClass(this.markerClassName)) { |
if (!target.hasClass(this.markerClassName)) { |
return; |
return; |
} |
} |
this._removeTarget(target); |
this._removeTarget(target[0]); |
$target.removeClass(this.markerClassName).empty(); |
target.removeClass(this.markerClassName).empty().removeData(this.propertyName); |
$.removeData(target, PROP_NAME); |
|
}, |
}, |
|
|
/* Pause a countdown widget at the current time. |
/* Pause a countdown widget at the current time. |
Stop it running but remember and display the current time. |
Stop it running but remember and display the current time. |
@param target (element) the containing division */ |
@param target (element) the containing division */ |
_pauseCountdown: function(target) { |
_pausePlugin: function(target) { |
this._hold(target, 'pause'); |
this._hold(target, 'pause'); |
}, |
}, |
|
|
/* Pause a countdown widget at the current time. |
/* Pause a countdown widget at the current time. |
Stop the display but keep the countdown running. |
Stop the display but keep the countdown running. |
@param target (element) the containing division */ |
@param target (element) the containing division */ |
_lapCountdown: function(target) { |
_lapPlugin: function(target) { |
this._hold(target, 'lap'); |
this._hold(target, 'lap'); |
}, |
}, |
|
|
/* Resume a paused countdown widget. |
/* Resume a paused countdown widget. |
@param target (element) the containing division */ |
@param target (element) the containing division */ |
_resumeCountdown: function(target) { |
_resumePlugin: function(target) { |
this._hold(target, null); |
this._hold(target, null); |
}, |
}, |
|
|
Line 372 $.extend(Countdown.prototype, {
|
Line 392 $.extend(Countdown.prototype, {
|
@param target (element) the containing division |
@param target (element) the containing division |
@param hold (string) the new hold setting */ |
@param hold (string) the new hold setting */ |
_hold: function(target, hold) { |
_hold: function(target, hold) { |
var inst = $.data(target, PROP_NAME); |
var inst = $.data(target, this.propertyName); |
if (inst) { |
if (inst) { |
if (inst._hold == 'pause' && !hold) { |
if (inst._hold == 'pause' && !hold) { |
inst._periods = inst._savePeriods; |
inst._periods = inst._savePeriods; |
Line 386 $.extend(Countdown.prototype, {
|
Line 406 $.extend(Countdown.prototype, {
|
} |
} |
inst._hold = hold; |
inst._hold = hold; |
inst._savePeriods = (hold == 'pause' ? inst._periods : null); |
inst._savePeriods = (hold == 'pause' ? inst._periods : null); |
$.data(target, PROP_NAME, inst); |
$.data(target, this.propertyName, inst); |
this._updateCountdown(target, inst); |
this._updateCountdown(target, inst); |
} |
} |
}, |
}, |
Line 394 $.extend(Countdown.prototype, {
|
Line 414 $.extend(Countdown.prototype, {
|
/* Return the current time periods. |
/* Return the current time periods. |
@param target (element) the containing division |
@param target (element) the containing division |
@return (number[7]) the current periods for the countdown */ |
@return (number[7]) the current periods for the countdown */ |
_getTimesCountdown: function(target) { |
_getTimesPlugin: function(target) { |
var inst = $.data(target, PROP_NAME); |
var inst = $.data(target, this.propertyName); |
return (!inst ? null : (!inst._hold ? inst._periods : |
return (!inst ? null : (inst._hold == 'pause' ? inst._savePeriods : (!inst._hold ? inst._periods : |
this._calculatePeriods(inst, inst._show, this._get(inst, 'significant'), new Date()))); |
this._calculatePeriods(inst, inst._show, inst.options.significant, new Date())))); |
}, |
|
|
|
/* Get a setting value, defaulting if necessary. |
|
@param inst (object) the current settings for this instance |
|
@param name (string) the name of the required setting |
|
@return (any) the setting's value or a default if not overridden */ |
|
_get: function(inst, name) { |
|
return (inst.options[name] != null ? |
|
inst.options[name] : $.countdown._defaults[name]); |
|
}, |
}, |
|
|
/* A time may be specified as an exact value or a relative one. |
/* A time may be specified as an exact value or a relative one. |
Line 440 $.extend(Countdown.prototype, {
|
Line 451 $.extend(Countdown.prototype, {
|
case 'w': day += parseInt(matches[1], 10) * 7; break; |
case 'w': day += parseInt(matches[1], 10) * 7; break; |
case 'o': |
case 'o': |
month += parseInt(matches[1], 10); |
month += parseInt(matches[1], 10); |
day = Math.min(day, $.countdown._getDaysInMonth(year, month)); |
day = Math.min(day, plugin._getDaysInMonth(year, month)); |
break; |
break; |
case 'y': |
case 'y': |
year += parseInt(matches[1], 10); |
year += parseInt(matches[1], 10); |
day = Math.min(day, $.countdown._getDaysInMonth(year, month)); |
day = Math.min(day, plugin._getDaysInMonth(year, month)); |
break; |
break; |
} |
} |
matches = pattern.exec(offset); |
matches = pattern.exec(offset); |
Line 477 $.extend(Countdown.prototype, {
|
Line 488 $.extend(Countdown.prototype, {
|
@param inst (object) the current settings for this instance |
@param inst (object) the current settings for this instance |
@return (string) the new HTML for the countdown display */ |
@return (string) the new HTML for the countdown display */ |
_generateHTML: function(inst) { |
_generateHTML: function(inst) { |
|
var self = this; |
// Determine what to show |
// Determine what to show |
var significant = this._get(inst, 'significant'); |
|
inst._periods = (inst._hold ? inst._periods : |
inst._periods = (inst._hold ? inst._periods : |
this._calculatePeriods(inst, inst._show, significant, new Date())); |
this._calculatePeriods(inst, inst._show, inst.options.significant, new Date())); |
// Show all 'asNeeded' after first non-zero value |
// Show all 'asNeeded' after first non-zero value |
var shownNonZero = false; |
var shownNonZero = false; |
var showCount = 0; |
var showCount = 0; |
var sigCount = significant; |
var sigCount = inst.options.significant; |
var show = $.extend({}, inst._show); |
var show = $.extend({}, inst._show); |
for (var period = Y; period <= S; period++) { |
for (var period = Y; period <= S; period++) { |
shownNonZero |= (inst._show[period] == '?' && inst._periods[period] > 0); |
shownNonZero |= (inst._show[period] == '?' && inst._periods[period] > 0); |
Line 504 $.extend(Countdown.prototype, {
|
Line 515 $.extend(Countdown.prototype, {
|
} |
} |
} |
} |
} |
} |
var compact = this._get(inst, 'compact'); |
var labels = (inst.options.compact ? inst.options.compactLabels : inst.options.labels); |
var layout = this._get(inst, 'layout'); |
var whichLabels = inst.options.whichLabels || this._normalLabels; |
var labels = (compact ? this._get(inst, 'compactLabels') : this._get(inst, 'labels')); |
|
var whichLabels = this._get(inst, 'whichLabels') || this._normalLabels; |
|
var timeSeparator = this._get(inst, 'timeSeparator'); |
|
var description = this._get(inst, 'description') || ''; |
|
var showCompact = function(period) { |
var showCompact = function(period) { |
var labelsNum = $.countdown._get(inst, |
var labelsNum = inst.options['compactLabels' + whichLabels(inst._periods[period])]; |
'compactLabels' + whichLabels(inst._periods[period])); |
return (show[period] ? self._translateDigits(inst, inst._periods[period]) + |
return (show[period] ? inst._periods[period] + |
|
(labelsNum ? labelsNum[period] : labels[period]) + ' ' : ''); |
(labelsNum ? labelsNum[period] : labels[period]) + ' ' : ''); |
}; |
}; |
var showFull = function(period) { |
var showFull = function(period) { |
var labelsNum = $.countdown._get(inst, 'labels' + whichLabels(inst._periods[period])); |
var labelsNum = inst.options['labels' + whichLabels(inst._periods[period])]; |
return ((!significant && show[period]) || (significant && showSignificant[period]) ? |
return ((!inst.options.significant && show[period]) || |
'<span class="countdown_section"><span class="countdown_amount">' + |
(inst.options.significant && showSignificant[period]) ? |
inst._periods[period] + '</span><br/>' + |
'<span class="' + plugin._sectionClass + '">' + |
|
'<span class="' + plugin._amountClass + '">' + |
|
self._translateDigits(inst, inst._periods[period]) + '</span><br/>' + |
(labelsNum ? labelsNum[period] : labels[period]) + '</span>' : ''); |
(labelsNum ? labelsNum[period] : labels[period]) + '</span>' : ''); |
}; |
}; |
return (layout ? this._buildLayout(inst, show, layout, compact, significant, showSignificant) : |
return (inst.options.layout ? this._buildLayout(inst, show, inst.options.layout, |
((compact ? // Compact version |
inst.options.compact, inst.options.significant, showSignificant) : |
'<span class="countdown_row countdown_amount' + |
((inst.options.compact ? // Compact version |
(inst._hold ? ' countdown_holding' : '') + '">' + |
'<span class="' + this._rowClass + ' ' + this._amountClass + |
|
(inst._hold ? ' ' + this._holdingClass : '') + '">' + |
showCompact(Y) + showCompact(O) + showCompact(W) + showCompact(D) + |
showCompact(Y) + showCompact(O) + showCompact(W) + showCompact(D) + |
(show[H] ? this._minDigits(inst._periods[H], 2) : '') + |
(show[H] ? this._minDigits(inst, inst._periods[H], 2) : '') + |
(show[M] ? (show[H] ? timeSeparator : '') + |
(show[M] ? (show[H] ? inst.options.timeSeparator : '') + |
this._minDigits(inst._periods[M], 2) : '') + |
this._minDigits(inst, inst._periods[M], 2) : '') + |
(show[S] ? (show[H] || show[M] ? timeSeparator : '') + |
(show[S] ? (show[H] || show[M] ? inst.options.timeSeparator : '') + |
this._minDigits(inst._periods[S], 2) : '') : |
this._minDigits(inst, inst._periods[S], 2) : '') : |
// Full version |
// Full version |
'<span class="countdown_row countdown_show' + (significant || showCount) + |
'<span class="' + this._rowClass + ' ' + this._showClass + (inst.options.significant || showCount) + |
(inst._hold ? ' countdown_holding' : '') + '">' + |
(inst._hold ? ' ' + this._holdingClass : '') + '">' + |
showFull(Y) + showFull(O) + showFull(W) + showFull(D) + |
showFull(Y) + showFull(O) + showFull(W) + showFull(D) + |
showFull(H) + showFull(M) + showFull(S)) + '</span>' + |
showFull(H) + showFull(M) + showFull(S)) + '</span>' + |
(description ? '<span class="countdown_row countdown_descr">' + description + '</span>' : ''))); |
(inst.options.description ? '<span class="' + this._rowClass + ' ' + this._descrClass + '">' + |
|
inst.options.description + '</span>' : ''))); |
}, |
}, |
|
|
/* Construct a custom layout. |
/* Construct a custom layout. |
Line 550 $.extend(Countdown.prototype, {
|
Line 560 $.extend(Countdown.prototype, {
|
@param showSignificant (boolean[7]) other periods to show for significance |
@param showSignificant (boolean[7]) other periods to show for significance |
@return (string) the custom HTML */ |
@return (string) the custom HTML */ |
_buildLayout: function(inst, show, layout, compact, significant, showSignificant) { |
_buildLayout: function(inst, show, layout, compact, significant, showSignificant) { |
var labels = this._get(inst, (compact ? 'compactLabels' : 'labels')); |
var labels = inst.options[compact ? 'compactLabels' : 'labels']; |
var whichLabels = this._get(inst, 'whichLabels') || this._normalLabels; |
var whichLabels = inst.options.whichLabels || this._normalLabels; |
var labelFor = function(index) { |
var labelFor = function(index) { |
return ($.countdown._get(inst, |
return (inst.options[(compact ? 'compactLabels' : 'labels') + |
(compact ? 'compactLabels' : 'labels') + whichLabels(inst._periods[index])) || |
whichLabels(inst._periods[index])] || labels)[index]; |
labels)[index]; |
|
}; |
}; |
var digit = function(value, position) { |
var digit = function(value, position) { |
return Math.floor(value / position) % 10; |
return inst.options.digits[Math.floor(value / position) % 10]; |
}; |
}; |
var subs = {desc: this._get(inst, 'description'), sep: this._get(inst, 'timeSeparator'), |
var subs = {desc: inst.options.description, sep: inst.options.timeSeparator, |
yl: labelFor(Y), yn: inst._periods[Y], ynn: this._minDigits(inst._periods[Y], 2), |
yl: labelFor(Y), yn: this._minDigits(inst, inst._periods[Y], 1), |
ynnn: this._minDigits(inst._periods[Y], 3), y1: digit(inst._periods[Y], 1), |
ynn: this._minDigits(inst, inst._periods[Y], 2), |
|
ynnn: this._minDigits(inst, inst._periods[Y], 3), y1: digit(inst._periods[Y], 1), |
y10: digit(inst._periods[Y], 10), y100: digit(inst._periods[Y], 100), |
y10: digit(inst._periods[Y], 10), y100: digit(inst._periods[Y], 100), |
y1000: digit(inst._periods[Y], 1000), |
y1000: digit(inst._periods[Y], 1000), |
ol: labelFor(O), on: inst._periods[O], onn: this._minDigits(inst._periods[O], 2), |
ol: labelFor(O), on: this._minDigits(inst, inst._periods[O], 1), |
onnn: this._minDigits(inst._periods[O], 3), o1: digit(inst._periods[O], 1), |
onn: this._minDigits(inst, inst._periods[O], 2), |
|
onnn: this._minDigits(inst, inst._periods[O], 3), o1: digit(inst._periods[O], 1), |
o10: digit(inst._periods[O], 10), o100: digit(inst._periods[O], 100), |
o10: digit(inst._periods[O], 10), o100: digit(inst._periods[O], 100), |
o1000: digit(inst._periods[O], 1000), |
o1000: digit(inst._periods[O], 1000), |
wl: labelFor(W), wn: inst._periods[W], wnn: this._minDigits(inst._periods[W], 2), |
wl: labelFor(W), wn: this._minDigits(inst, inst._periods[W], 1), |
wnnn: this._minDigits(inst._periods[W], 3), w1: digit(inst._periods[W], 1), |
wnn: this._minDigits(inst, inst._periods[W], 2), |
|
wnnn: this._minDigits(inst, inst._periods[W], 3), w1: digit(inst._periods[W], 1), |
w10: digit(inst._periods[W], 10), w100: digit(inst._periods[W], 100), |
w10: digit(inst._periods[W], 10), w100: digit(inst._periods[W], 100), |
w1000: digit(inst._periods[W], 1000), |
w1000: digit(inst._periods[W], 1000), |
dl: labelFor(D), dn: inst._periods[D], dnn: this._minDigits(inst._periods[D], 2), |
dl: labelFor(D), dn: this._minDigits(inst, inst._periods[D], 1), |
dnnn: this._minDigits(inst._periods[D], 3), d1: digit(inst._periods[D], 1), |
dnn: this._minDigits(inst, inst._periods[D], 2), |
|
dnnn: this._minDigits(inst, inst._periods[D], 3), d1: digit(inst._periods[D], 1), |
d10: digit(inst._periods[D], 10), d100: digit(inst._periods[D], 100), |
d10: digit(inst._periods[D], 10), d100: digit(inst._periods[D], 100), |
d1000: digit(inst._periods[D], 1000), |
d1000: digit(inst._periods[D], 1000), |
hl: labelFor(H), hn: inst._periods[H], hnn: this._minDigits(inst._periods[H], 2), |
hl: labelFor(H), hn: this._minDigits(inst, inst._periods[H], 1), |
hnnn: this._minDigits(inst._periods[H], 3), h1: digit(inst._periods[H], 1), |
hnn: this._minDigits(inst, inst._periods[H], 2), |
|
hnnn: this._minDigits(inst, inst._periods[H], 3), h1: digit(inst._periods[H], 1), |
h10: digit(inst._periods[H], 10), h100: digit(inst._periods[H], 100), |
h10: digit(inst._periods[H], 10), h100: digit(inst._periods[H], 100), |
h1000: digit(inst._periods[H], 1000), |
h1000: digit(inst._periods[H], 1000), |
ml: labelFor(M), mn: inst._periods[M], mnn: this._minDigits(inst._periods[M], 2), |
ml: labelFor(M), mn: this._minDigits(inst, inst._periods[M], 1), |
mnnn: this._minDigits(inst._periods[M], 3), m1: digit(inst._periods[M], 1), |
mnn: this._minDigits(inst, inst._periods[M], 2), |
|
mnnn: this._minDigits(inst, inst._periods[M], 3), m1: digit(inst._periods[M], 1), |
m10: digit(inst._periods[M], 10), m100: digit(inst._periods[M], 100), |
m10: digit(inst._periods[M], 10), m100: digit(inst._periods[M], 100), |
m1000: digit(inst._periods[M], 1000), |
m1000: digit(inst._periods[M], 1000), |
sl: labelFor(S), sn: inst._periods[S], snn: this._minDigits(inst._periods[S], 2), |
sl: labelFor(S), sn: this._minDigits(inst, inst._periods[S], 1), |
snnn: this._minDigits(inst._periods[S], 3), s1: digit(inst._periods[S], 1), |
snn: this._minDigits(inst, inst._periods[S], 2), |
|
snnn: this._minDigits(inst, inst._periods[S], 3), s1: digit(inst._periods[S], 1), |
s10: digit(inst._periods[S], 10), s100: digit(inst._periods[S], 100), |
s10: digit(inst._periods[S], 10), s100: digit(inst._periods[S], 100), |
s1000: digit(inst._periods[S], 1000)}; |
s1000: digit(inst._periods[S], 1000)}; |
var html = layout; |
var html = layout; |
// Replace period containers: {p<}...{p>} |
// Replace period containers: {p<}...{p>} |
for (var i = Y; i <= S; i++) { |
for (var i = Y; i <= S; i++) { |
var period = 'yowdhms'.charAt(i); |
var period = 'yowdhms'.charAt(i); |
var re = new RegExp('\\{' + period + '<\\}(.*)\\{' + period + '>\\}', 'g'); |
var re = new RegExp('\\{' + period + '<\\}([\\s\\S]*)\\{' + period + '>\\}', 'g'); |
html = html.replace(re, ((!significant && show[i]) || |
html = html.replace(re, ((!significant && show[i]) || |
(significant && showSignificant[i]) ? '$1' : '')); |
(significant && showSignificant[i]) ? '$1' : '')); |
} |
} |
Line 606 $.extend(Countdown.prototype, {
|
Line 622 $.extend(Countdown.prototype, {
|
}, |
}, |
|
|
/* Ensure a numeric value has at least n digits for display. |
/* Ensure a numeric value has at least n digits for display. |
|
@param inst (object) the current settings for this instance |
@param value (number) the value to display |
@param value (number) the value to display |
@param len (number) the minimum length |
@param len (number) the minimum length |
@return (string) the display text */ |
@return (string) the display text */ |
_minDigits: function(value, len) { |
_minDigits: function(inst, value, len) { |
value = '' + value; |
value = '' + value; |
if (value.length >= len) { |
if (value.length >= len) { |
return value; |
return this._translateDigits(inst, value); |
} |
} |
value = '0000000000' + value; |
value = '0000000000' + value; |
return value.substr(value.length - len); |
return this._translateDigits(inst, value.substr(value.length - len)); |
|
}, |
|
|
|
/* Translate digits into other representations. |
|
@param inst (object) the current settings for this instance |
|
@param value (string) the text to translate |
|
@return (string) the translated text */ |
|
_translateDigits: function(inst, value) { |
|
return ('' + value).replace(/[0-9]/g, function(digit) { |
|
return inst.options.digits[digit]; |
|
}); |
}, |
}, |
|
|
/* Translate the format into flags for each period. |
/* Translate the format into flags for each period. |
Line 623 $.extend(Countdown.prototype, {
|
Line 650 $.extend(Countdown.prototype, {
|
@return (string[7]) flags indicating which periods are requested (?) or |
@return (string[7]) flags indicating which periods are requested (?) or |
required (!) by year, month, week, day, hour, minute, second */ |
required (!) by year, month, week, day, hour, minute, second */ |
_determineShow: function(inst) { |
_determineShow: function(inst) { |
var format = this._get(inst, 'format'); |
var format = inst.options.format; |
var show = []; |
var show = []; |
show[Y] = (format.match('y') ? '?' : (format.match('Y') ? '!' : null)); |
show[Y] = (format.match('y') ? '?' : (format.match('Y') ? '!' : null)); |
show[O] = (format.match('o') ? '?' : (format.match('O') ? '!' : null)); |
show[O] = (format.match('o') ? '?' : (format.match('O') ? '!' : null)); |
Line 665 $.extend(Countdown.prototype, {
|
Line 692 $.extend(Countdown.prototype, {
|
var periods = [0, 0, 0, 0, 0, 0, 0]; |
var periods = [0, 0, 0, 0, 0, 0, 0]; |
if (show[Y] || show[O]) { |
if (show[Y] || show[O]) { |
// Treat end of months as the same |
// Treat end of months as the same |
var lastNow = $.countdown._getDaysInMonth(now.getFullYear(), now.getMonth()); |
var lastNow = plugin._getDaysInMonth(now.getFullYear(), now.getMonth()); |
var lastUntil = $.countdown._getDaysInMonth(until.getFullYear(), until.getMonth()); |
var lastUntil = plugin._getDaysInMonth(until.getFullYear(), until.getMonth()); |
var sameDay = (until.getDate() == now.getDate() || |
var sameDay = (until.getDate() == now.getDate() || |
(until.getDate() >= Math.min(lastNow, lastUntil) && |
(until.getDate() >= Math.min(lastNow, lastUntil) && |
now.getDate() >= Math.min(lastNow, lastUntil))); |
now.getDate() >= Math.min(lastNow, lastUntil))); |
Line 682 $.extend(Countdown.prototype, {
|
Line 709 $.extend(Countdown.prototype, {
|
// Adjust for months difference and end of month if necessary |
// Adjust for months difference and end of month if necessary |
now = new Date(now.getTime()); |
now = new Date(now.getTime()); |
var wasLastDay = (now.getDate() == lastNow); |
var wasLastDay = (now.getDate() == lastNow); |
var lastDay = $.countdown._getDaysInMonth(now.getFullYear() + periods[Y], |
var lastDay = plugin._getDaysInMonth(now.getFullYear() + periods[Y], |
now.getMonth() + periods[O]); |
now.getMonth() + periods[O]); |
if (now.getDate() > lastDay) { |
if (now.getDate() > lastDay) { |
now.setDate(lastDay); |
now.setDate(lastDay); |
Line 737 $.extend(Countdown.prototype, {
|
Line 764 $.extend(Countdown.prototype, {
|
} |
} |
}); |
}); |
|
|
/* jQuery extend now ignores nulls! |
// The list of commands that return values and don't permit chaining |
@param target (object) the object to update |
var getters = ['getTimes']; |
@param props (object) the new settings |
|
@return (object) the updated object */ |
/* Determine whether a command is a getter and doesn't permit chaining. |
function extendRemove(target, props) { |
@param command (string, optional) the command to run |
$.extend(target, props); |
@param otherArgs ([], optional) any other arguments for the command |
for (var name in props) { |
@return true if the command is a getter, false if not */ |
if (props[name] == null) { |
function isNotChained(command, otherArgs) { |
target[name] = null; |
if (command == 'option' && (otherArgs.length == 0 || |
} |
(otherArgs.length == 1 && typeof otherArgs[0] == 'string'))) { |
|
return true; |
} |
} |
return target; |
return $.inArray(command, getters) > -1; |
} |
} |
|
|
/* Process the countdown functionality for a jQuery selection. |
/* Process the countdown functionality for a jQuery selection. |
@param command (string) the command to run (optional, default 'attach') |
@param options (object) the new settings to use for these instances (optional) or |
@param options (object) the new settings to use for these countdown instances |
(string) the command to run (optional) |
@return (jQuery) for chaining further calls */ |
@return (jQuery) for chaining further calls or |
|
(any) getter value */ |
$.fn.countdown = function(options) { |
$.fn.countdown = function(options) { |
var otherArgs = Array.prototype.slice.call(arguments, 1); |
var otherArgs = Array.prototype.slice.call(arguments, 1); |
if (options == 'getTimes' || options == 'settings') { |
if (isNotChained(options, otherArgs)) { |
return $.countdown['_' + options + 'Countdown']. |
return plugin['_' + options + 'Plugin']. |
apply($.countdown, [this[0]].concat(otherArgs)); |
apply(plugin, [this[0]].concat(otherArgs)); |
} |
} |
return this.each(function() { |
return this.each(function() { |
if (typeof options == 'string') { |
if (typeof options == 'string') { |
$.countdown['_' + options + 'Countdown'].apply($.countdown, [this].concat(otherArgs)); |
if (!plugin['_' + options + 'Plugin']) { |
|
throw 'Unknown command: ' + options; |
|
} |
|
plugin['_' + options + 'Plugin']. |
|
apply(plugin, [this].concat(otherArgs)); |
} |
} |
else { |
else { |
$.countdown._attachCountdown(this, options); |
plugin._attachPlugin(this, options || {}); |
} |
} |
}); |
}); |
}; |
}; |
|
|
/* Initialise the countdown functionality. */ |
/* Initialise the countdown functionality. */ |
$.countdown = new Countdown(); // singleton instance |
var plugin = $.countdown = new Countdown(); // Singleton instance |
|
|
})(jQuery); |
})(jQuery); |