diff --git a/jquery.countdown.css b/css/jquery.countdown.css similarity index 100% rename from jquery.countdown.css rename to css/jquery.countdown.css diff --git a/countdownGlowing.gif b/img/countdownGlowing.gif similarity index 100% rename from countdownGlowing.gif rename to img/countdownGlowing.gif diff --git a/countdownLED.png b/img/countdownLED.png similarity index 100% rename from countdownLED.png rename to img/countdownLED.png diff --git a/jquery.countdown.js b/jquery.countdown.js deleted file mode 100644 index 57a302b..0000000 --- a/jquery.countdown.js +++ /dev/null @@ -1,857 +0,0 @@ -/* http://keith-wood.name/countdown.html - Countdown for jQuery v2.0.0. - Written by Keith Wood (kbwood{at}iinet.com.au) January 2008. - Available under the MIT (https://github.com/jquery/jquery/blob/master/MIT-LICENSE.txt) license. - Please attribute the author if you use it. */ - -(function($) { // Hide scope, no $ conflict - - var pluginName = 'countdown'; - - var Y = 0; // Years - var O = 1; // Months - var W = 2; // Weeks - var D = 3; // Days - var H = 4; // Hours - var M = 5; // Minutes - var S = 6; // Seconds - - /** Create the countdown plugin. -

Sets an element to show the time remaining until a given instant.

-

Expects HTML like:

-
<div></div>
-

Provide inline configuration like:

-
<div data-countdown="name: 'value'"></div>
- @module Countdown - @augments JQPlugin - @example $(selector).countdown({until: +300}) */ - $.JQPlugin.createPlugin({ - - /** The name of the plugin. */ - name: pluginName, - - /** Countdown expiry callback. - Triggered when the countdown expires. - @callback expiryCallback */ - - /** Countdown server synchronisation callback. - Triggered when the countdown is initialised. - @callback serverSyncCallback - @return {Date} The current date/time on the server as expressed in the local timezone. */ - - /** Countdown tick callback. - Triggered on every tickInterval ticks of the countdown. - @callback tickCallback - @param periods {number[]} The breakdown by period (years, months, weeks, days, - hours, minutes, seconds) of the time remaining/passed. */ - - /** Countdown which labels callback. - Triggered when the countdown is being display to determine which set of labels - (labels, labels1, ...) are to be used for the current period value. - @callback whichLabelsCallback - @param num {number} The current period value. - @return {number} The suffix for the label set to use. */ - - /** Default settings for the plugin. - @property until {Date|number|string} The date/time to count down to, or number of seconds - offset from now, or string of amounts and units for offset(s) from now: - 'Y' years, 'O' months, 'W' weeks, 'D' days, 'H' hours, 'M' minutes, 'S' seconds. - @example until: new Date(2013, 12-1, 25, 13, 30) - until: +300 - until: '+1O -2D' - @property [since] {Date|number|string} The date/time to count up from, or - number of seconds offset from now, or string for unit offset(s): - 'Y' years, 'O' months, 'W' weeks, 'D' days, 'H' hours, 'M' minutes, 'S' seconds. - @example since: new Date(2013, 1-1, 1) - since: -300 - since: '-1O +2D' - @property [timezone=null] {number} The timezone (hours or minutes from GMT) for the target times, - or null for client local timezone. - @example timezone: +10 - timezone: -60 - @property [serverSync=null] {serverSyncCallback} A function to retrieve the current server time - for synchronisation. - @property [format='dHMS'] {string} The format for display - upper case for always, lower case only if non-zero, - 'Y' years, 'O' months, 'W' weeks, 'D' days, 'H' hours, 'M' minutes, 'S' seconds. - @property [layout=''] {string} Build your own layout for the countdown. - @example layout: '{d<}{dn} {dl}{d>} {hnn}:{mnn}:{snn}' - @property [compact=false] {boolean} True to display in a compact format, false for an expanded one. - @property [padZeroes=false] {boolean} True to add leading zeroes - @property [significant=0] {number} The number of periods with non-zero values to show, zero for all. - @property [description=''] {string} The description displayed for the countdown. - @property [expiryUrl=''] {string} A URL to load upon expiry, replacing the current page. - @property [expiryText=''] {string} Text to display upon expiry, replacing the countdown. This may be HTML. - @property [alwaysExpire=false] {boolean} True to trigger onExpiry even if target time has passed. - @property [onExpiry=null] {expiryCallback} Callback when the countdown expires - - receives no parameters and this is the containing division. - @example onExpiry: function() { - ... - } - @property [onTick=null] {tickCallback} Callback when the countdown is updated - - receives number[7] being the breakdown by period - (years, months, weeks, days, hours, minutes, seconds - based on - format) and this is the containing division. - @example onTick: function(periods) { - var secs = $.countdown.periodsToSeconds(periods); - if (secs < 300) { // Last five minutes - ... - } - } - @property [tickInterval=1] {number} The interval (seconds) between onTick callbacks. */ - defaultOptions: { - until: null, - since: null, - timezone: null, - serverSync: null, - format: 'dHMS', - layout: '', - compact: false, - padZeroes: false, - significant: 0, - description: '', - expiryUrl: '', - expiryText: '', - alwaysExpire: false, - onExpiry: null, - onTick: null, - tickInterval: 1 - }, - - /** Localisations for the plugin. - Entries are objects indexed by the language code ('' being the default US/English). - Each object has the following attributes. - @property [labels=['Years','Months','Weeks','Days','Hours','Minutes','Seconds']] {string[]} - The display texts for the counter periods. - @property [labels1=['Year','Month','Week','Day','Hour','Minute','Second']] {string[]} - The display texts for the counter periods if they have a value of 1. - Add other labelsn attributes as necessary to - cater for other numeric idiosyncrasies of the localisation. - @property [compactLabels=['y','m','w','d']] {string[]} The compact texts for the counter periods. - @property [whichLabels=null] {whichLabelsCallback} A function to determine which - labelsn to use. - @example whichLabels: function(num) { - return (num > 1 ? 0 : 1); - } - @property [digits=['0','1',...,'9']] {number[]} The digits to display (0-9). - @property [timeSeparator=':'] {string} Separator for time periods in the compact layout. - @property [isRTL=false] {boolean} True for right-to-left languages, false for left-to-right. */ - regionalOptions: { // Available regional settings, indexed by language/country code - '': { // Default regional settings - English/US - labels: ['Years', 'Months', 'Weeks', 'Days', 'Hours', 'Minutes', 'Seconds'], - labels1: ['Year', 'Month', 'Week', 'Day', 'Hour', 'Minute', 'Second'], - compactLabels: ['y', 'm', 'w', 'd'], - whichLabels: null, - digits: ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9'], - timeSeparator: ':', - isRTL: false - } - }, - - /** Names of getter methods - those that can't be chained. */ - _getters: ['getTimes'], - - /* Class name for the right-to-left marker. */ - _rtlClass: pluginName + '-rtl', - /* Class name for the countdown section marker. */ - _sectionClass: pluginName + '-section', - /* Class name for the period amount marker. */ - _amountClass: pluginName + '-amount', - /* Class name for the period name marker. */ - _periodClass: pluginName + '-period', - /* Class name for the countdown row marker. */ - _rowClass: pluginName + '-row', - /* Class name for the holding countdown marker. */ - _holdingClass: pluginName + '-holding', - /* Class name for the showing countdown marker. */ - _showClass: pluginName + '-show', - /* Class name for the description marker. */ - _descrClass: pluginName + '-descr', - - /* List of currently active countdown elements. */ - _timerElems: [], - - /** Additional setup for the countdown. - Apply default localisations. - Create the timer. */ - _init: function() { - var self = this; - this._super(); - 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 - function timerCallBack(timestamp) { - 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) { - self._updateElems(); - animationStartTime = drawStart; - } - requestAnimationFrame(timerCallBack); - } - var requestAnimationFrame = window.requestAnimationFrame || - window.webkitRequestAnimationFrame || window.mozRequestAnimationFrame || - window.oRequestAnimationFrame || window.msRequestAnimationFrame || null; - // This is when we expect a fall-back to setInterval as it's much more fluid - var animationStartTime = 0; - if (!requestAnimationFrame || $.noRequestAnimationFrame) { - $.noRequestAnimationFrame = null; - setInterval(function() { self._updateElems(); }, 980); // Fall back to good old setInterval - } - else { - animationStartTime = window.animationStartTime || - window.webkitAnimationStartTime || window.mozAnimationStartTime || - window.oAnimationStartTime || window.msAnimationStartTime || now(); - requestAnimationFrame(timerCallBack); - } - }, - - /** Convert a date/time to UTC. - @param tz {number} The hour or minute offset from GMT, e.g. +9, -360. - @param year {Date|number} the date/time in that timezone or the year in that timezone. - @param [month] {number} The month (0 - 11) (omit if year is a Date). - @param [day] {number} The day (omit if year is a Date). - @param [hours] {number} The hour (omit if year is a Date). - @param [mins] {number} The minute (omit if year is a Date). - @param [secs] {number} The second (omit if year is a Date). - @param [ms] {number} The millisecond (omit if year is a Date). - @return {Date} The equivalent UTC date/time. - @example $.countdown.UTCDate(+10, 2013, 12-1, 25, 12, 0) - $.countdown.UTCDate(-7, new Date(2013, 12-1, 25, 12, 0)) */ - UTCDate: function(tz, year, month, day, hours, mins, secs, ms) { - if (typeof year == 'object' && year.constructor == Date) { - ms = year.getMilliseconds(); - secs = year.getSeconds(); - mins = year.getMinutes(); - hours = year.getHours(); - day = year.getDate(); - month = year.getMonth(); - year = year.getFullYear(); - } - var d = new Date(); - d.setUTCFullYear(year); - d.setUTCDate(1); - d.setUTCMonth(month || 0); - d.setUTCDate(day || 1); - d.setUTCHours(hours || 0); - d.setUTCMinutes((mins || 0) - (Math.abs(tz) < 30 ? tz * 60 : tz)); - d.setUTCSeconds(secs || 0); - d.setUTCMilliseconds(ms || 0); - return d; - }, - - /** Convert a set of periods into seconds. - Averaged for months and years. - @param periods {number[]} The periods per year/month/week/day/hour/minute/second. - @return {number} The corresponding number of seconds. - @example var secs = $.countdown.periodsToSeconds(periods) */ - periodsToSeconds: function(periods) { - return periods[0] * 31557600 + periods[1] * 2629800 + periods[2] * 604800 + - periods[3] * 86400 + periods[4] * 3600 + periods[5] * 60 + periods[6]; - }, - - _instSettings: function(elem, options) { - return {_periods: [0, 0, 0, 0, 0, 0, 0]}; - }, - - /** Add an element to the list of active ones. - @private - @param elem {Element} The countdown element. */ - _addElem: function(elem) { - if (!this._hasElem(elem)) { - this._timerElems.push(elem); - } - }, - - /** See if an element is in the list of active ones. - @private - @param elem {Element} The countdown element. - @return {boolean} True if present, false if not. */ - _hasElem: function(elem) { - return ($.inArray(elem, this._timerElems) > -1); - }, - - /** Remove an element from the list of active ones. - @private - @param elem {Element} The countdown element. */ - _removeElem: function(elem) { - this._timerElems = $.map(this._timerElems, - function(value) { return (value == elem ? null : value); }); // delete entry - }, - - /** Update each active timer element. - @private */ - _updateElems: function() { - for (var i = this._timerElems.length - 1; i >= 0; i--) { - this._updateCountdown(this._timerElems[i]); - } - }, - - _optionsChanged: function(elem, inst, options) { - 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(elem, inst, - options.until != null || options.since != null || timezoneChanged); - var now = new Date(); - if ((inst._since && inst._since < now) || (inst._until && inst._until > now)) { - this._addElem(elem[0]); - } - this._updateCountdown(elem, inst); - }, - - /** Redisplay the countdown with an updated display. - @private - @param elem {Element|jQuery} The containing division. - @param inst {object} The current settings for this instance. */ - _updateCountdown: function(elem, inst) { - elem = elem.jquery ? elem : $(elem); - inst = inst || elem.data(this.name); - if (!inst) { - return; - } - elem.html(this._generateHTML(inst)).toggleClass(this._rtlClass, inst.options.isRTL); - if ($.isFunction(inst.options.onTick)) { - var periods = inst._hold != 'lap' ? inst._periods : - this._calculatePeriods(inst, inst._show, inst.options.significant, new Date()); - if (inst.options.tickInterval == 1 || - this.periodsToSeconds(periods) % inst.options.tickInterval == 0) { - inst.options.onTick.apply(elem[0], [periods]); - } - } - var expired = inst._hold != 'pause' && - (inst._since ? inst._now.getTime() < inst._since.getTime() : - inst._now.getTime() >= inst._until.getTime()); - if (expired && !inst._expiring) { - inst._expiring = true; - if (this._hasElem(elem[0]) || inst.options.alwaysExpire) { - this._removeElem(elem[0]); - if ($.isFunction(inst.options.onExpiry)) { - inst.options.onExpiry.apply(elem[0], []); - } - if (inst.options.expiryText) { - var layout = inst.options.layout; - inst.options.layout = inst.options.expiryText; - this._updateCountdown(elem[0], inst); - inst.options.layout = layout; - } - if (inst.options.expiryUrl) { - window.location = inst.options.expiryUrl; - } - } - inst._expiring = false; - } - else if (inst._hold == 'pause') { - this._removeElem(elem[0]); - } - }, - - /** Reset any extra labelsn and compactLabelsn entries if changing labels. - @private - @param base {object} The options to be updated. - @param options {object} The new option values. */ - _resetExtraLabels: function(base, options) { - var changingLabels = false; - for (var n in options) { - if (n != 'whichLabels' && n.match(/[Ll]abels/)) { - changingLabels = true; - break; - } - } - if (changingLabels) { - for (var n in base) { // Remove custom numbered labels - if (n.match(/[Ll]abels[02-9]|compactLabels1/)) { - base[n] = null; - } - } - } - }, - - /** Calculate internal settings for an instance. - @private - @param elem {jQuery} The containing division. - @param inst {object} The current settings for this instance. - @param recalc {boolean} True if until or since are set. */ - _adjustSettings: function(elem, inst, recalc) { - var now; - var serverOffset = 0; - var serverEntry = null; - for (var i = 0; i < this._serverSyncs.length; i++) { - if (this._serverSyncs[i][0] == inst.options.serverSync) { - serverEntry = this._serverSyncs[i][1]; - break; - } - } - if (serverEntry != null) { - serverOffset = (inst.options.serverSync ? serverEntry : 0); - now = new Date(); - } - else { - var serverResult = ($.isFunction(inst.options.serverSync) ? - inst.options.serverSync.apply(elem[0], []) : null); - now = new Date(); - serverOffset = (serverResult ? now.getTime() - serverResult.getTime() : 0); - this._serverSyncs.push([inst.options.serverSync, serverOffset]); - } - var timezone = inst.options.timezone; - timezone = (timezone == null ? -now.getTimezoneOffset() : timezone); - if (recalc || (!recalc && inst._until == null && inst._since == null)) { - inst._since = inst.options.since; - if (inst._since != null) { - inst._since = this.UTCDate(timezone, this._determineTime(inst._since, null)); - 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._show = this._determineShow(inst); - }, - - /** Remove the countdown widget from a div. - @param elem {jQuery} The containing division. - @param inst {object} The current instance object. */ - _preDestroy: function(elem, inst) { - this._removeElem(elem[0]); - elem.empty(); - }, - - /** Pause a countdown widget at the current time. - Stop it running but remember and display the current time. - @param elem {Element} The containing division. - @example $(selector).countdown('pause') */ - pause: function(elem) { - this._hold(elem, 'pause'); - }, - - /** Pause a countdown widget at the current time. - Stop the display but keep the countdown running. - @param elem {Element} The containing division. - @example $(selector).countdown('lap') */ - lap: function(elem) { - this._hold(elem, 'lap'); - }, - - /** Resume a paused countdown widget. - @param elem {Element} The containing division. - @example $(selector).countdown('resume') */ - resume: function(elem) { - this._hold(elem, null); - }, - - /** Toggle a paused countdown widget. - @param elem {Element} The containing division. - @example $(selector).countdown('toggle') */ - toggle: function(elem) { - var inst = $.data(elem, this.name) || {}; - this[!inst._hold ? 'pause' : 'resume'](elem); - }, - - /** Toggle a lapped countdown widget. - @param elem {Element} The containing division. - @example $(selector).countdown('toggleLap') */ - toggleLap: function(elem) { - var inst = $.data(elem, this.name) || {}; - this[!inst._hold ? 'lap' : 'resume'](elem); - }, - - /** Pause or resume a countdown widget. - @private - @param elem {Element} The containing division. - @param hold {string} The new hold setting. */ - _hold: function(elem, hold) { - var inst = $.data(elem, this.name); - if (inst) { - if (inst._hold == 'pause' && !hold) { - inst._periods = inst._savePeriods; - var sign = (inst._since ? '-' : '+'); - inst[inst._since ? '_since' : '_until'] = - this._determineTime(sign + inst._periods[0] + 'y' + - sign + inst._periods[1] + 'o' + sign + inst._periods[2] + 'w' + - sign + inst._periods[3] + 'd' + sign + inst._periods[4] + 'h' + - sign + inst._periods[5] + 'm' + sign + inst._periods[6] + 's'); - this._addElem(elem); - } - inst._hold = hold; - inst._savePeriods = (hold == 'pause' ? inst._periods : null); - $.data(elem, this.name, inst); - this._updateCountdown(elem, inst); - } - }, - - /** Return the current time periods. - @param elem {Element} The containing division. - @return {number[]} The current periods for the countdown. - @example var periods = $(selector).countdown('getTimes') */ - getTimes: function(elem) { - var inst = $.data(elem, this.name); - return (!inst ? null : (inst._hold == 'pause' ? inst._savePeriods : (!inst._hold ? inst._periods : - this._calculatePeriods(inst, inst._show, inst.options.significant, new Date())))); - }, - - /** A time may be specified as an exact value or a relative one. - @private - @param setting {string|number|Date} The date/time value as a relative or absolute value. - @param defaultTime {Date} The date/time to use if no other is supplied. - @return {Date} The corresponding date/time. */ - _determineTime: function(setting, defaultTime) { - var self = this; - var offsetNumeric = function(offset) { // e.g. +300, -2 - var time = new Date(); - time.setTime(time.getTime() + offset * 1000); - return time; - }; - var offsetString = function(offset) { // e.g. '+2d', '-4w', '+3h +30m' - offset = offset.toLowerCase(); - var time = new Date(); - var year = time.getFullYear(); - var month = time.getMonth(); - var day = time.getDate(); - var hour = time.getHours(); - var minute = time.getMinutes(); - var second = time.getSeconds(); - var pattern = /([+-]?[0-9]+)\s*(s|m|h|d|w|o|y)?/g; - var matches = pattern.exec(offset); - while (matches) { - switch (matches[2] || 's') { - case 's': second += parseInt(matches[1], 10); break; - case 'm': minute += parseInt(matches[1], 10); break; - case 'h': hour += parseInt(matches[1], 10); break; - case 'd': day += parseInt(matches[1], 10); break; - case 'w': day += parseInt(matches[1], 10) * 7; break; - case 'o': - month += parseInt(matches[1], 10); - day = Math.min(day, self._getDaysInMonth(year, month)); - break; - case 'y': - year += parseInt(matches[1], 10); - day = Math.min(day, self._getDaysInMonth(year, month)); - break; - } - matches = pattern.exec(offset); - } - return new Date(year, month, day, hour, minute, second, 0); - }; - var time = (setting == null ? defaultTime : - (typeof setting == 'string' ? offsetString(setting) : - (typeof setting == 'number' ? offsetNumeric(setting) : setting))); - if (time) time.setMilliseconds(0); - return time; - }, - - /** Determine the number of days in a month. - @private - @param year {number} The year. - @param month {number} The month. - @return {number} The days in that month. */ - _getDaysInMonth: function(year, month) { - return 32 - new Date(year, month, 32).getDate(); - }, - - /** Default implementation to determine which set of labels should be used for an amount. - Use the labels attribute with the same numeric suffix (if it exists). - @private - @param num {number} The amount to be displayed. - @return {number} The set of labels to be used for this amount. */ - _normalLabels: function(num) { - return num; - }, - - /** Generate the HTML to display the countdown widget. - @private - @param inst {object} The current settings for this instance. - @return {string} The new HTML for the countdown display. */ - _generateHTML: function(inst) { - var self = this; - // Determine what to show - inst._periods = (inst._hold ? inst._periods : - this._calculatePeriods(inst, inst._show, inst.options.significant, new Date())); - // Show all 'asNeeded' after first non-zero value - var shownNonZero = false; - var showCount = 0; - var sigCount = inst.options.significant; - var show = $.extend({}, inst._show); - for (var period = Y; period <= S; period++) { - shownNonZero |= (inst._show[period] == '?' && inst._periods[period] > 0); - show[period] = (inst._show[period] == '?' && !shownNonZero ? null : inst._show[period]); - showCount += (show[period] ? 1 : 0); - sigCount -= (inst._periods[period] > 0 ? 1 : 0); - } - var showSignificant = [false, false, false, false, false, false, false]; - for (var period = S; period >= Y; period--) { // Determine significant periods - if (inst._show[period]) { - if (inst._periods[period]) { - showSignificant[period] = true; - } - else { - showSignificant[period] = sigCount > 0; - sigCount--; - } - } - } - var labels = (inst.options.compact ? inst.options.compactLabels : inst.options.labels); - var whichLabels = inst.options.whichLabels || this._normalLabels; - var showCompact = function(period) { - var labelsNum = inst.options['compactLabels' + whichLabels(inst._periods[period])]; - return (show[period] ? self._translateDigits(inst, inst._periods[period]) + - (labelsNum ? labelsNum[period] : labels[period]) + ' ' : ''); - }; - var minDigits = (inst.options.padZeroes ? 2 : 1); - var showFull = function(period) { - var labelsNum = inst.options['labels' + whichLabels(inst._periods[period])]; - return ((!inst.options.significant && show[period]) || - (inst.options.significant && showSignificant[period]) ? - '' + - '' + - self._minDigits(inst, inst._periods[period], minDigits) + '' + - '' + - (labelsNum ? labelsNum[period] : labels[period]) + '' : ''); - }; - return (inst.options.layout ? this._buildLayout(inst, show, inst.options.layout, - inst.options.compact, inst.options.significant, showSignificant) : - ((inst.options.compact ? // Compact version - '' + - showCompact(Y) + showCompact(O) + showCompact(W) + showCompact(D) + - (show[H] ? this._minDigits(inst, inst._periods[H], 2) : '') + - (show[M] ? (show[H] ? inst.options.timeSeparator : '') + - this._minDigits(inst, inst._periods[M], 2) : '') + - (show[S] ? (show[H] || show[M] ? inst.options.timeSeparator : '') + - this._minDigits(inst, inst._periods[S], 2) : '') : - // Full version - '' + - showFull(Y) + showFull(O) + showFull(W) + showFull(D) + - showFull(H) + showFull(M) + showFull(S)) + '' + - (inst.options.description ? '' + - inst.options.description + '' : ''))); - }, - - /** Construct a custom layout. - @private - @param inst {object} The current settings for this instance. - @param show {boolean[]} Flags indicating which periods are requested. - @param layout {string} The customised layout. - @param compact {boolean} True if using compact labels. - @param significant {number} The number of periods with values to show, zero for all. - @param showSignificant {boolean[]} Other periods to show for significance. - @return {string} The custom HTML. */ - _buildLayout: function(inst, show, layout, compact, significant, showSignificant) { - var labels = inst.options[compact ? 'compactLabels' : 'labels']; - var whichLabels = inst.options.whichLabels || this._normalLabels; - var labelFor = function(index) { - return (inst.options[(compact ? 'compactLabels' : 'labels') + - whichLabels(inst._periods[index])] || labels)[index]; - }; - var digit = function(value, position) { - return inst.options.digits[Math.floor(value / position) % 10]; - }; - var subs = {desc: inst.options.description, sep: inst.options.timeSeparator, - yl: labelFor(Y), yn: this._minDigits(inst, 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), - y1000: digit(inst._periods[Y], 1000), - ol: labelFor(O), on: this._minDigits(inst, 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), - o1000: digit(inst._periods[O], 1000), - wl: labelFor(W), wn: this._minDigits(inst, 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), - w1000: digit(inst._periods[W], 1000), - dl: labelFor(D), dn: this._minDigits(inst, 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), - d1000: digit(inst._periods[D], 1000), - hl: labelFor(H), hn: this._minDigits(inst, 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), - h1000: digit(inst._periods[H], 1000), - ml: labelFor(M), mn: this._minDigits(inst, 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), - m1000: digit(inst._periods[M], 1000), - sl: labelFor(S), sn: this._minDigits(inst, 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), - s1000: digit(inst._periods[S], 1000)}; - var html = layout; - // Replace period containers: {p<}...{p>} - for (var i = Y; i <= S; i++) { - var period = 'yowdhms'.charAt(i); - var re = new RegExp('\\{' + period + '<\\}([\\s\\S]*)\\{' + period + '>\\}', 'g'); - html = html.replace(re, ((!significant && show[i]) || - (significant && showSignificant[i]) ? '$1' : '')); - } - // Replace period values: {pn} - $.each(subs, function(n, v) { - var re = new RegExp('\\{' + n + '\\}', 'g'); - html = html.replace(re, v); - }); - return html; - }, - - /** Ensure a numeric value has at least n digits for display. - @private - @param inst {object} The current settings for this instance. - @param value {number} The value to display. - @param len {number} The minimum length. - @return {string} The display text. */ - _minDigits: function(inst, value, len) { - value = '' + value; - if (value.length >= len) { - return this._translateDigits(inst, value); - } - value = '0000000000' + value; - return this._translateDigits(inst, value.substr(value.length - len)); - }, - - /** Translate digits into other representations. - @private - @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. - @private - @param inst {object} The current settings for this instance. - @return {string[]} Flags indicating which periods are requested (?) or - required (!) by year, month, week, day, hour, minute, second. */ - _determineShow: function(inst) { - var format = inst.options.format; - var show = []; - show[Y] = (format.match('y') ? '?' : (format.match('Y') ? '!' : null)); - show[O] = (format.match('o') ? '?' : (format.match('O') ? '!' : null)); - show[W] = (format.match('w') ? '?' : (format.match('W') ? '!' : null)); - show[D] = (format.match('d') ? '?' : (format.match('D') ? '!' : null)); - show[H] = (format.match('h') ? '?' : (format.match('H') ? '!' : null)); - show[M] = (format.match('m') ? '?' : (format.match('M') ? '!' : null)); - show[S] = (format.match('s') ? '?' : (format.match('S') ? '!' : null)); - return show; - }, - - /** Calculate the requested periods between now and the target time. - @private - @param inst {object} The current settings for this instance. - @param show {string[]} Flags indicating which periods are requested/required. - @param significant {number} The number of periods with values to show, zero for all. - @param now {Date} The current date and time. - @return {number[]} The current time periods (always positive) - by year, month, week, day, hour, minute, second. */ - _calculatePeriods: function(inst, show, significant, now) { - // Find endpoints - inst._now = now; - inst._now.setMilliseconds(0); - var until = new Date(inst._now.getTime()); - if (inst._since) { - if (now.getTime() < inst._since.getTime()) { - inst._now = now = until; - } - else { - now = inst._since; - } - } - else { - until.setTime(inst._until.getTime()); - if (now.getTime() > inst._until.getTime()) { - inst._now = now = until; - } - } - // Calculate differences by period - var periods = [0, 0, 0, 0, 0, 0, 0]; - if (show[Y] || show[O]) { - // Treat end of months as the same - var lastNow = this._getDaysInMonth(now.getFullYear(), now.getMonth()); - var lastUntil = this._getDaysInMonth(until.getFullYear(), until.getMonth()); - var sameDay = (until.getDate() == now.getDate() || - (until.getDate() >= Math.min(lastNow, lastUntil) && - now.getDate() >= Math.min(lastNow, lastUntil))); - var getSecs = function(date) { - return (date.getHours() * 60 + date.getMinutes()) * 60 + date.getSeconds(); - }; - var months = Math.max(0, - (until.getFullYear() - now.getFullYear()) * 12 + until.getMonth() - now.getMonth() + - ((until.getDate() < now.getDate() && !sameDay) || - (sameDay && getSecs(until) < getSecs(now)) ? -1 : 0)); - periods[Y] = (show[Y] ? Math.floor(months / 12) : 0); - periods[O] = (show[O] ? months - periods[Y] * 12 : 0); - // Adjust for months difference and end of month if necessary - now = new Date(now.getTime()); - var wasLastDay = (now.getDate() == lastNow); - var lastDay = this._getDaysInMonth(now.getFullYear() + periods[Y], - now.getMonth() + periods[O]); - if (now.getDate() > lastDay) { - now.setDate(lastDay); - } - now.setFullYear(now.getFullYear() + periods[Y]); - now.setMonth(now.getMonth() + periods[O]); - if (wasLastDay) { - now.setDate(lastDay); - } - } - var diff = Math.floor((until.getTime() - now.getTime()) / 1000); - var extractPeriod = function(period, numSecs) { - periods[period] = (show[period] ? Math.floor(diff / numSecs) : 0); - diff -= periods[period] * numSecs; - }; - extractPeriod(W, 604800); - extractPeriod(D, 86400); - extractPeriod(H, 3600); - extractPeriod(M, 60); - extractPeriod(S, 1); - if (diff > 0 && !inst._since) { // Round up if left overs - var multiplier = [1, 12, 4.3482, 7, 24, 60, 60]; - var lastShown = S; - var max = 1; - for (var period = S; period >= Y; period--) { - if (show[period]) { - if (periods[lastShown] >= max) { - periods[lastShown] = 0; - diff = 1; - } - if (diff > 0) { - periods[period]++; - diff = 0; - lastShown = period; - max = 1; - } - } - max *= multiplier[period]; - } - } - if (significant) { // Zero out insignificant periods - for (var period = Y; period <= S; period++) { - if (significant && periods[period]) { - significant--; - } - else if (!significant) { - periods[period] = 0; - } - } - } - return periods; - } - }); - -})(jQuery); diff --git a/js/jquery.countdown.js b/js/jquery.countdown.js new file mode 100644 index 0000000..9a224ee --- /dev/null +++ b/js/jquery.countdown.js @@ -0,0 +1,852 @@ +/* http://keith-wood.name/countdown.html + Countdown for jQuery v2.0.0. + Written by Keith Wood (kbwood{at}iinet.com.au) January 2008. + Available under the MIT (https://github.com/jquery/jquery/blob/master/MIT-LICENSE.txt) license. + Please attribute the author if you use it. */ + +(function($) { // Hide scope, no $ conflict + + var pluginName = 'countdown'; + + var Y = 0; // Years + var O = 1; // Months + var W = 2; // Weeks + var D = 3; // Days + var H = 4; // Hours + var M = 5; // Minutes + var S = 6; // Seconds + + /** Create the countdown plugin. +

Sets an element to show the time remaining until a given instant.

+

Expects HTML like:

+
<div></div>
+

Provide inline configuration like:

+
<div data-countdown="name: 'value'"></div>
+ @module Countdown + @augments JQPlugin + @example $(selector).countdown({until: +300}) */ + $.JQPlugin.createPlugin({ + + /** The name of the plugin. */ + name: pluginName, + + /** Countdown expiry callback. + Triggered when the countdown expires. + @callback expiryCallback */ + + /** Countdown server synchronisation callback. + Triggered when the countdown is initialised. + @callback serverSyncCallback + @return {Date} The current date/time on the server as expressed in the local timezone. */ + + /** Countdown tick callback. + Triggered on every tickInterval ticks of the countdown. + @callback tickCallback + @param periods {number[]} The breakdown by period (years, months, weeks, days, + hours, minutes, seconds) of the time remaining/passed. */ + + /** Countdown which labels callback. + Triggered when the countdown is being display to determine which set of labels + (labels, labels1, ...) are to be used for the current period value. + @callback whichLabelsCallback + @param num {number} The current period value. + @return {number} The suffix for the label set to use. */ + + /** Default settings for the plugin. + @property until {Date|number|string} The date/time to count down to, or number of seconds + offset from now, or string of amounts and units for offset(s) from now: + 'Y' years, 'O' months, 'W' weeks, 'D' days, 'H' hours, 'M' minutes, 'S' seconds. + @example until: new Date(2013, 12-1, 25, 13, 30) + until: +300 + until: '+1O -2D' + @property [since] {Date|number|string} The date/time to count up from, or + number of seconds offset from now, or string for unit offset(s): + 'Y' years, 'O' months, 'W' weeks, 'D' days, 'H' hours, 'M' minutes, 'S' seconds. + @example since: new Date(2013, 1-1, 1) + since: -300 + since: '-1O +2D' + @property [timezone=null] {number} The timezone (hours or minutes from GMT) for the target times, + or null for client local timezone. + @example timezone: +10 + timezone: -60 + @property [serverSync=null] {serverSyncCallback} A function to retrieve the current server time + for synchronisation. + @property [format='dHMS'] {string} The format for display - upper case for always, lower case only if non-zero, + 'Y' years, 'O' months, 'W' weeks, 'D' days, 'H' hours, 'M' minutes, 'S' seconds. + @property [layout=''] {string} Build your own layout for the countdown. + @example layout: '{d<}{dn} {dl}{d>} {hnn}:{mnn}:{snn}' + @property [compact=false] {boolean} True to display in a compact format, false for an expanded one. + @property [padZeroes=false] {boolean} True to add leading zeroes + @property [significant=0] {number} The number of periods with non-zero values to show, zero for all. + @property [description=''] {string} The description displayed for the countdown. + @property [expiryUrl=''] {string} A URL to load upon expiry, replacing the current page. + @property [expiryText=''] {string} Text to display upon expiry, replacing the countdown. This may be HTML. + @property [alwaysExpire=false] {boolean} True to trigger onExpiry even if target time has passed. + @property [onExpiry=null] {expiryCallback} Callback when the countdown expires - + receives no parameters and this is the containing division. + @example onExpiry: function() { + ... + } + @property [onTick=null] {tickCallback} Callback when the countdown is updated - + receives number[7] being the breakdown by period + (years, months, weeks, days, hours, minutes, seconds - based on + format) and this is the containing division. + @example onTick: function(periods) { + var secs = $.countdown.periodsToSeconds(periods); + if (secs < 300) { // Last five minutes + ... + } + } + @property [tickInterval=1] {number} The interval (seconds) between onTick callbacks. */ + defaultOptions: { + until: null, + since: null, + timezone: null, + serverSync: null, + format: 'dHMS', + layout: '', + compact: false, + padZeroes: false, + significant: 0, + description: '', + expiryUrl: '', + expiryText: '', + alwaysExpire: false, + onExpiry: null, + onTick: null, + tickInterval: 1 + }, + + /** Localisations for the plugin. + Entries are objects indexed by the language code ('' being the default US/English). + Each object has the following attributes. + @property [labels=['Years','Months','Weeks','Days','Hours','Minutes','Seconds']] {string[]} + The display texts for the counter periods. + @property [labels1=['Year','Month','Week','Day','Hour','Minute','Second']] {string[]} + The display texts for the counter periods if they have a value of 1. + Add other labelsn attributes as necessary to + cater for other numeric idiosyncrasies of the localisation. + @property [compactLabels=['y','m','w','d']] {string[]} The compact texts for the counter periods. + @property [whichLabels=null] {whichLabelsCallback} A function to determine which + labelsn to use. + @example whichLabels: function(num) { + return (num > 1 ? 0 : 1); + } + @property [digits=['0','1',...,'9']] {number[]} The digits to display (0-9). + @property [timeSeparator=':'] {string} Separator for time periods in the compact layout. + @property [isRTL=false] {boolean} True for right-to-left languages, false for left-to-right. */ + regionalOptions: { // Available regional settings, indexed by language/country code + '': { // Default regional settings - English/US + labels: ['Years', 'Months', 'Weeks', 'Days', 'Hours', 'Minutes', 'Seconds'], + labels1: ['Year', 'Month', 'Week', 'Day', 'Hour', 'Minute', 'Second'], + compactLabels: ['y', 'm', 'w', 'd'], + whichLabels: null, + digits: ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9'], + timeSeparator: ':', + isRTL: false + } + }, + + /** Names of getter methods - those that can't be chained. */ + _getters: ['getTimes'], + + /* Class name for the right-to-left marker. */ + _rtlClass: pluginName + '-rtl', + /* Class name for the countdown section marker. */ + _sectionClass: pluginName + '-section', + /* Class name for the period amount marker. */ + _amountClass: pluginName + '-amount', + /* Class name for the period name marker. */ + _periodClass: pluginName + '-period', + /* Class name for the countdown row marker. */ + _rowClass: pluginName + '-row', + /* Class name for the holding countdown marker. */ + _holdingClass: pluginName + '-holding', + /* Class name for the showing countdown marker. */ + _showClass: pluginName + '-show', + /* Class name for the description marker. */ + _descrClass: pluginName + '-descr', + + /* List of currently active countdown elements. */ + _timerElems: [], + + /** Additional setup for the countdown. + Apply default localisations. + Create the timer. */ + _init: function() { + var self = this; + this._super(); + 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 + function timerCallBack(timestamp) { + 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) { + self._updateElems(); + animationStartTime = drawStart; + } + requestAnimationFrame(timerCallBack); + } + var requestAnimationFrame = window.requestAnimationFrame || + window.webkitRequestAnimationFrame || window.mozRequestAnimationFrame || + window.oRequestAnimationFrame || window.msRequestAnimationFrame || null; + // This is when we expect a fall-back to setInterval as it's much more fluid + var animationStartTime = 0; + if (!requestAnimationFrame || $.noRequestAnimationFrame) { + $.noRequestAnimationFrame = null; + setInterval(function() { self._updateElems(); }, 980); // Fall back to good old setInterval + } else { + animationStartTime = window.animationStartTime || + window.webkitAnimationStartTime || window.mozAnimationStartTime || + window.oAnimationStartTime || window.msAnimationStartTime || now(); + requestAnimationFrame(timerCallBack); + } + }, + + /** Convert a date/time to UTC. + @param tz {number} The hour or minute offset from GMT, e.g. +9, -360. + @param year {Date|number} the date/time in that timezone or the year in that timezone. + @param [month] {number} The month (0 - 11) (omit if year is a Date). + @param [day] {number} The day (omit if year is a Date). + @param [hours] {number} The hour (omit if year is a Date). + @param [mins] {number} The minute (omit if year is a Date). + @param [secs] {number} The second (omit if year is a Date). + @param [ms] {number} The millisecond (omit if year is a Date). + @return {Date} The equivalent UTC date/time. + @example $.countdown.UTCDate(+10, 2013, 12-1, 25, 12, 0) + $.countdown.UTCDate(-7, new Date(2013, 12-1, 25, 12, 0)) */ + UTCDate: function(tz, year, month, day, hours, mins, secs, ms) { + if (typeof year == 'object' && year.constructor == Date) { + ms = year.getMilliseconds(); + secs = year.getSeconds(); + mins = year.getMinutes(); + hours = year.getHours(); + day = year.getDate(); + month = year.getMonth(); + year = year.getFullYear(); + } + var d = new Date(); + d.setUTCFullYear(year); + d.setUTCDate(1); + d.setUTCMonth(month || 0); + d.setUTCDate(day || 1); + d.setUTCHours(hours || 0); + d.setUTCMinutes((mins || 0) - (Math.abs(tz) < 30 ? tz * 60 : tz)); + d.setUTCSeconds(secs || 0); + d.setUTCMilliseconds(ms || 0); + return d; + }, + + /** Convert a set of periods into seconds. + Averaged for months and years. + @param periods {number[]} The periods per year/month/week/day/hour/minute/second. + @return {number} The corresponding number of seconds. + @example var secs = $.countdown.periodsToSeconds(periods) */ + periodsToSeconds: function(periods) { + return periods[0] * 31557600 + periods[1] * 2629800 + periods[2] * 604800 + + periods[3] * 86400 + periods[4] * 3600 + periods[5] * 60 + periods[6]; + }, + + _instSettings: function(elem, options) { + return {_periods: [0, 0, 0, 0, 0, 0, 0]}; + }, + + /** Add an element to the list of active ones. + @private + @param elem {Element} The countdown element. */ + _addElem: function(elem) { + if (!this._hasElem(elem)) { + this._timerElems.push(elem); + } + }, + + /** See if an element is in the list of active ones. + @private + @param elem {Element} The countdown element. + @return {boolean} True if present, false if not. */ + _hasElem: function(elem) { + return ($.inArray(elem, this._timerElems) > -1); + }, + + /** Remove an element from the list of active ones. + @private + @param elem {Element} The countdown element. */ + _removeElem: function(elem) { + this._timerElems = $.map(this._timerElems, + function(value) { return (value == elem ? null : value); }); // delete entry + }, + + /** Update each active timer element. + @private */ + _updateElems: function() { + for (var i = this._timerElems.length - 1; i >= 0; i--) { + this._updateCountdown(this._timerElems[i]); + } + }, + + _optionsChanged: function(elem, inst, options) { + 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(elem, inst, + options.until != null || options.since != null || timezoneChanged); + var now = new Date(); + if ((inst._since && inst._since < now) || (inst._until && inst._until > now)) { + this._addElem(elem[0]); + } + this._updateCountdown(elem, inst); + }, + + /** Redisplay the countdown with an updated display. + @private + @param elem {Element|jQuery} The containing division. + @param inst {object} The current settings for this instance. */ + _updateCountdown: function(elem, inst) { + elem = elem.jquery ? elem : $(elem); + inst = inst || elem.data(this.name); + if (!inst) { + return; + } + elem.html(this._generateHTML(inst)).toggleClass(this._rtlClass, inst.options.isRTL); + if ($.isFunction(inst.options.onTick)) { + var periods = inst._hold != 'lap' ? inst._periods : + this._calculatePeriods(inst, inst._show, inst.options.significant, new Date()); + if (inst.options.tickInterval == 1 || + this.periodsToSeconds(periods) % inst.options.tickInterval == 0) { + inst.options.onTick.apply(elem[0], [periods]); + } + } + var expired = inst._hold != 'pause' && + (inst._since ? inst._now.getTime() < inst._since.getTime() : + inst._now.getTime() >= inst._until.getTime()); + if (expired && !inst._expiring) { + inst._expiring = true; + if (this._hasElem(elem[0]) || inst.options.alwaysExpire) { + this._removeElem(elem[0]); + if ($.isFunction(inst.options.onExpiry)) { + inst.options.onExpiry.apply(elem[0], []); + } + if (inst.options.expiryText) { + var layout = inst.options.layout; + inst.options.layout = inst.options.expiryText; + this._updateCountdown(elem[0], inst); + inst.options.layout = layout; + } + if (inst.options.expiryUrl) { + window.location = inst.options.expiryUrl; + } + } + inst._expiring = false; + }else if (inst._hold == 'pause') { + this._removeElem(elem[0]); + } + }, + + /** Reset any extra labelsn and compactLabelsn entries if changing labels. + @private + @param base {object} The options to be updated. + @param options {object} The new option values. */ + _resetExtraLabels: function(base, options) { + var changingLabels = false; + for (var n in options) { + if (n != 'whichLabels' && n.match(/[Ll]abels/)) { + changingLabels = true; + break; + } + } + if (changingLabels) { + for (var n in base) { // Remove custom numbered labels + if (n.match(/[Ll]abels[02-9]|compactLabels1/)) { + base[n] = null; + } + } + } + }, + + /** Calculate internal settings for an instance. + @private + @param elem {jQuery} The containing division. + @param inst {object} The current settings for this instance. + @param recalc {boolean} True if until or since are set. */ + _adjustSettings: function(elem, inst, recalc) { + var now; + var serverOffset = 0; + var serverEntry = null; + for (var i = 0; i < this._serverSyncs.length; i++) { + if (this._serverSyncs[i][0] == inst.options.serverSync) { + serverEntry = this._serverSyncs[i][1]; + break; + } + } + if (serverEntry != null) { + serverOffset = (inst.options.serverSync ? serverEntry : 0); + now = new Date(); + } + else { + var serverResult = ($.isFunction(inst.options.serverSync) ? + inst.options.serverSync.apply(elem[0], []) : null); + now = new Date(); + serverOffset = (serverResult ? now.getTime() - serverResult.getTime() : 0); + this._serverSyncs.push([inst.options.serverSync, serverOffset]); + } + var timezone = inst.options.timezone; + timezone = (timezone == null ? -now.getTimezoneOffset() : timezone); + if (recalc || (!recalc && inst._until == null && inst._since == null)) { + inst._since = inst.options.since; + if (inst._since != null) { + inst._since = this.UTCDate(timezone, this._determineTime(inst._since, null)); + 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._show = this._determineShow(inst); + }, + + /** Remove the countdown widget from a div. + @param elem {jQuery} The containing division. + @param inst {object} The current instance object. */ + _preDestroy: function(elem, inst) { + this._removeElem(elem[0]); + elem.empty(); + }, + + /** Pause a countdown widget at the current time. + Stop it running but remember and display the current time. + @param elem {Element} The containing division. + @example $(selector).countdown('pause') */ + pause: function(elem) { + this._hold(elem, 'pause'); + }, + + /** Pause a countdown widget at the current time. + Stop the display but keep the countdown running. + @param elem {Element} The containing division. + @example $(selector).countdown('lap') */ + lap: function(elem) { + this._hold(elem, 'lap'); + }, + + /** Resume a paused countdown widget. + @param elem {Element} The containing division. + @example $(selector).countdown('resume') */ + resume: function(elem) { + this._hold(elem, null); + }, + + /** Toggle a paused countdown widget. + @param elem {Element} The containing division. + @example $(selector).countdown('toggle') */ + toggle: function(elem) { + var inst = $.data(elem, this.name) || {}; + this[!inst._hold ? 'pause' : 'resume'](elem); + }, + + /** Toggle a lapped countdown widget. + @param elem {Element} The containing division. + @example $(selector).countdown('toggleLap') */ + toggleLap: function(elem) { + var inst = $.data(elem, this.name) || {}; + this[!inst._hold ? 'lap' : 'resume'](elem); + }, + + /** Pause or resume a countdown widget. + @private + @param elem {Element} The containing division. + @param hold {string} The new hold setting. */ + _hold: function(elem, hold) { + var inst = $.data(elem, this.name); + if (inst) { + if (inst._hold == 'pause' && !hold) { + inst._periods = inst._savePeriods; + var sign = (inst._since ? '-' : '+'); + inst[inst._since ? '_since' : '_until'] = + this._determineTime(sign + inst._periods[0] + 'y' + + sign + inst._periods[1] + 'o' + sign + inst._periods[2] + 'w' + + sign + inst._periods[3] + 'd' + sign + inst._periods[4] + 'h' + + sign + inst._periods[5] + 'm' + sign + inst._periods[6] + 's'); + this._addElem(elem); + } + inst._hold = hold; + inst._savePeriods = (hold == 'pause' ? inst._periods : null); + $.data(elem, this.name, inst); + this._updateCountdown(elem, inst); + } + }, + + /** Return the current time periods. + @param elem {Element} The containing division. + @return {number[]} The current periods for the countdown. + @example var periods = $(selector).countdown('getTimes') */ + getTimes: function(elem) { + var inst = $.data(elem, this.name); + return (!inst ? null : (inst._hold == 'pause' ? inst._savePeriods : (!inst._hold ? inst._periods : + this._calculatePeriods(inst, inst._show, inst.options.significant, new Date())))); + }, + + /** A time may be specified as an exact value or a relative one. + @private + @param setting {string|number|Date} The date/time value as a relative or absolute value. + @param defaultTime {Date} The date/time to use if no other is supplied. + @return {Date} The corresponding date/time. */ + _determineTime: function(setting, defaultTime) { + var self = this; + var offsetNumeric = function(offset) { // e.g. +300, -2 + var time = new Date(); + time.setTime(time.getTime() + offset * 1000); + return time; + }; + var offsetString = function(offset) { // e.g. '+2d', '-4w', '+3h +30m' + offset = offset.toLowerCase(); + var time = new Date(); + var year = time.getFullYear(); + var month = time.getMonth(); + var day = time.getDate(); + var hour = time.getHours(); + var minute = time.getMinutes(); + var second = time.getSeconds(); + var pattern = /([+-]?[0-9]+)\s*(s|m|h|d|w|o|y)?/g; + var matches = pattern.exec(offset); + while (matches) { + switch (matches[2] || 's') { + case 's': second += parseInt(matches[1], 10); break; + case 'm': minute += parseInt(matches[1], 10); break; + case 'h': hour += parseInt(matches[1], 10); break; + case 'd': day += parseInt(matches[1], 10); break; + case 'w': day += parseInt(matches[1], 10) * 7; break; + case 'o': + month += parseInt(matches[1], 10); + day = Math.min(day, self._getDaysInMonth(year, month)); + break; + case 'y': + year += parseInt(matches[1], 10); + day = Math.min(day, self._getDaysInMonth(year, month)); + break; + } + matches = pattern.exec(offset); + } + return new Date(year, month, day, hour, minute, second, 0); + }; + var time = (setting == null ? defaultTime : + (typeof setting == 'string' ? offsetString(setting) : + (typeof setting == 'number' ? offsetNumeric(setting) : setting))); + if (time) time.setMilliseconds(0); + return time; + }, + + /** Determine the number of days in a month. + @private + @param year {number} The year. + @param month {number} The month. + @return {number} The days in that month. */ + _getDaysInMonth: function(year, month) { + return 32 - new Date(year, month, 32).getDate(); + }, + + /** Default implementation to determine which set of labels should be used for an amount. + Use the labels attribute with the same numeric suffix (if it exists). + @private + @param num {number} The amount to be displayed. + @return {number} The set of labels to be used for this amount. */ + _normalLabels: function(num) { + return num; + }, + + /** Generate the HTML to display the countdown widget. + @private + @param inst {object} The current settings for this instance. + @return {string} The new HTML for the countdown display. */ + _generateHTML: function(inst) { + var self = this; + // Determine what to show + inst._periods = (inst._hold ? inst._periods : + this._calculatePeriods(inst, inst._show, inst.options.significant, new Date())); + // Show all 'asNeeded' after first non-zero value + var shownNonZero = false; + var showCount = 0; + var sigCount = inst.options.significant; + var show = $.extend({}, inst._show); + for (var period = Y; period <= S; period++) { + shownNonZero |= (inst._show[period] == '?' && inst._periods[period] > 0); + show[period] = (inst._show[period] == '?' && !shownNonZero ? null : inst._show[period]); + showCount += (show[period] ? 1 : 0); + sigCount -= (inst._periods[period] > 0 ? 1 : 0); + } + var showSignificant = [false, false, false, false, false, false, false]; + for (var period = S; period >= Y; period--) { // Determine significant periods + if (inst._show[period]) { + if (inst._periods[period]) { + showSignificant[period] = true; + } else { + showSignificant[period] = sigCount > 0; + sigCount--; + } + } + } + var labels = (inst.options.compact ? inst.options.compactLabels : inst.options.labels); + var whichLabels = inst.options.whichLabels || this._normalLabels; + var showCompact = function(period) { + var labelsNum = inst.options['compactLabels' + whichLabels(inst._periods[period])]; + return (show[period] ? self._translateDigits(inst, inst._periods[period]) + + (labelsNum ? labelsNum[period] : labels[period]) + ' ' : ''); + }; + var minDigits = (inst.options.padZeroes ? 2 : 1); + var showFull = function(period) { + var labelsNum = inst.options['labels' + whichLabels(inst._periods[period])]; + return ((!inst.options.significant && show[period]) || + (inst.options.significant && showSignificant[period]) ? + '' + + '' + + self._minDigits(inst, inst._periods[period], minDigits) + '' + + '' + + (labelsNum ? labelsNum[period] : labels[period]) + '' : ''); + }; + return (inst.options.layout ? this._buildLayout(inst, show, inst.options.layout, + inst.options.compact, inst.options.significant, showSignificant) : + ((inst.options.compact ? // Compact version + '' + + showCompact(Y) + showCompact(O) + showCompact(W) + showCompact(D) + + (show[H] ? this._minDigits(inst, inst._periods[H], 2) : '') + + (show[M] ? (show[H] ? inst.options.timeSeparator : '') + + this._minDigits(inst, inst._periods[M], 2) : '') + + (show[S] ? (show[H] || show[M] ? inst.options.timeSeparator : '') + + this._minDigits(inst, inst._periods[S], 2) : '') : + // Full version + '' + + showFull(Y) + showFull(O) + showFull(W) + showFull(D) + + showFull(H) + showFull(M) + showFull(S)) + '' + + (inst.options.description ? '' + + inst.options.description + '' : ''))); + }, + + /** Construct a custom layout. + @private + @param inst {object} The current settings for this instance. + @param show {boolean[]} Flags indicating which periods are requested. + @param layout {string} The customised layout. + @param compact {boolean} True if using compact labels. + @param significant {number} The number of periods with values to show, zero for all. + @param showSignificant {boolean[]} Other periods to show for significance. + @return {string} The custom HTML. */ + _buildLayout: function(inst, show, layout, compact, significant, showSignificant) { + var labels = inst.options[compact ? 'compactLabels' : 'labels']; + var whichLabels = inst.options.whichLabels || this._normalLabels; + var labelFor = function(index) { + return (inst.options[(compact ? 'compactLabels' : 'labels') + + whichLabels(inst._periods[index])] || labels)[index]; + }; + var digit = function(value, position) { + return inst.options.digits[Math.floor(value / position) % 10]; + }; + var subs = {desc: inst.options.description, sep: inst.options.timeSeparator, + yl: labelFor(Y), yn: this._minDigits(inst, 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), + y1000: digit(inst._periods[Y], 1000), + ol: labelFor(O), on: this._minDigits(inst, 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), + o1000: digit(inst._periods[O], 1000), + wl: labelFor(W), wn: this._minDigits(inst, 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), + w1000: digit(inst._periods[W], 1000), + dl: labelFor(D), dn: this._minDigits(inst, 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), + d1000: digit(inst._periods[D], 1000), + hl: labelFor(H), hn: this._minDigits(inst, 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), + h1000: digit(inst._periods[H], 1000), + ml: labelFor(M), mn: this._minDigits(inst, 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), + m1000: digit(inst._periods[M], 1000), + sl: labelFor(S), sn: this._minDigits(inst, 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), + s1000: digit(inst._periods[S], 1000)}; + var html = layout; + // Replace period containers: {p<}...{p>} + for (var i = Y; i <= S; i++) { + var period = 'yowdhms'.charAt(i); + var re = new RegExp('\\{' + period + '<\\}([\\s\\S]*)\\{' + period + '>\\}', 'g'); + html = html.replace(re, ((!significant && show[i]) || + (significant && showSignificant[i]) ? '$1' : '')); + } + // Replace period values: {pn} + $.each(subs, function(n, v) { + var re = new RegExp('\\{' + n + '\\}', 'g'); + html = html.replace(re, v); + }); + return html; + }, + + /** Ensure a numeric value has at least n digits for display. + @private + @param inst {object} The current settings for this instance. + @param value {number} The value to display. + @param len {number} The minimum length. + @return {string} The display text. */ + _minDigits: function(inst, value, len) { + value = '' + value; + if (value.length >= len) { + return this._translateDigits(inst, value); + } + value = '0000000000' + value; + return this._translateDigits(inst, value.substr(value.length - len)); + }, + + /** Translate digits into other representations. + @private + @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. + @private + @param inst {object} The current settings for this instance. + @return {string[]} Flags indicating which periods are requested (?) or + required (!) by year, month, week, day, hour, minute, second. */ + _determineShow: function(inst) { + var format = inst.options.format; + var show = []; + show[Y] = (format.match('y') ? '?' : (format.match('Y') ? '!' : null)); + show[O] = (format.match('o') ? '?' : (format.match('O') ? '!' : null)); + show[W] = (format.match('w') ? '?' : (format.match('W') ? '!' : null)); + show[D] = (format.match('d') ? '?' : (format.match('D') ? '!' : null)); + show[H] = (format.match('h') ? '?' : (format.match('H') ? '!' : null)); + show[M] = (format.match('m') ? '?' : (format.match('M') ? '!' : null)); + show[S] = (format.match('s') ? '?' : (format.match('S') ? '!' : null)); + return show; + }, + + /** Calculate the requested periods between now and the target time. + @private + @param inst {object} The current settings for this instance. + @param show {string[]} Flags indicating which periods are requested/required. + @param significant {number} The number of periods with values to show, zero for all. + @param now {Date} The current date and time. + @return {number[]} The current time periods (always positive) + by year, month, week, day, hour, minute, second. */ + _calculatePeriods: function(inst, show, significant, now) { + // Find endpoints + inst._now = now; + inst._now.setMilliseconds(0); + var until = new Date(inst._now.getTime()); + if (inst._since) { + if (now.getTime() < inst._since.getTime()) { + inst._now = now = until; + } else { + now = inst._since; + } + } else { + until.setTime(inst._until.getTime()); + if (now.getTime() > inst._until.getTime()) { + inst._now = now = until; + } + } + // Calculate differences by period + var periods = [0, 0, 0, 0, 0, 0, 0]; + if (show[Y] || show[O]) { + // Treat end of months as the same + var lastNow = this._getDaysInMonth(now.getFullYear(), now.getMonth()); + var lastUntil = this._getDaysInMonth(until.getFullYear(), until.getMonth()); + var sameDay = (until.getDate() == now.getDate() || + (until.getDate() >= Math.min(lastNow, lastUntil) && + now.getDate() >= Math.min(lastNow, lastUntil))); + var getSecs = function(date) { + return (date.getHours() * 60 + date.getMinutes()) * 60 + date.getSeconds(); + }; + var months = Math.max(0, + (until.getFullYear() - now.getFullYear()) * 12 + until.getMonth() - now.getMonth() + + ((until.getDate() < now.getDate() && !sameDay) || + (sameDay && getSecs(until) < getSecs(now)) ? -1 : 0)); + periods[Y] = (show[Y] ? Math.floor(months / 12) : 0); + periods[O] = (show[O] ? months - periods[Y] * 12 : 0); + // Adjust for months difference and end of month if necessary + now = new Date(now.getTime()); + var wasLastDay = (now.getDate() == lastNow); + var lastDay = this._getDaysInMonth(now.getFullYear() + periods[Y], + now.getMonth() + periods[O]); + if (now.getDate() > lastDay) { + now.setDate(lastDay); + } + now.setFullYear(now.getFullYear() + periods[Y]); + now.setMonth(now.getMonth() + periods[O]); + if (wasLastDay) { + now.setDate(lastDay); + } + } + var diff = Math.floor((until.getTime() - now.getTime()) / 1000); + var extractPeriod = function(period, numSecs) { + periods[period] = (show[period] ? Math.floor(diff / numSecs) : 0); + diff -= periods[period] * numSecs; + }; + extractPeriod(W, 604800); + extractPeriod(D, 86400); + extractPeriod(H, 3600); + extractPeriod(M, 60); + extractPeriod(S, 1); + if (diff > 0 && !inst._since) { // Round up if left overs + var multiplier = [1, 12, 4.3482, 7, 24, 60, 60]; + var lastShown = S; + var max = 1; + for (var period = S; period >= Y; period--) { + if (show[period]) { + if (periods[lastShown] >= max) { + periods[lastShown] = 0; + diff = 1; + } + if (diff > 0) { + periods[period]++; + diff = 0; + lastShown = period; + max = 1; + } + } + max *= multiplier[period]; + } + } + if (significant) { // Zero out insignificant periods + for (var period = Y; period <= S; period++) { + if (significant && periods[period]) { + significant--; + } + else if (!significant) { + periods[period] = 0; + } + } + } + return periods; + } + }); + +})(jQuery); diff --git a/jquery.countdown.min.js b/js/jquery.countdown.min.js similarity index 100% rename from jquery.countdown.min.js rename to js/jquery.countdown.min.js diff --git a/jquery.plugin.js b/js/jquery.plugin.js similarity index 100% rename from jquery.plugin.js rename to js/jquery.plugin.js diff --git a/jquery.plugin.min.js b/js/jquery.plugin.min.js similarity index 100% rename from jquery.plugin.min.js rename to js/jquery.plugin.min.js diff --git a/jquery.countdown-ar.js b/js/localisations/ar.js similarity index 100% rename from jquery.countdown-ar.js rename to js/localisations/ar.js diff --git a/jquery.countdown-bg.js b/js/localisations/bg.js similarity index 100% rename from jquery.countdown-bg.js rename to js/localisations/bg.js diff --git a/jquery.countdown-bn.js b/js/localisations/bn.js similarity index 100% rename from jquery.countdown-bn.js rename to js/localisations/bn.js diff --git a/jquery.countdown-bs.js b/js/localisations/bs.js similarity index 100% rename from jquery.countdown-bs.js rename to js/localisations/bs.js diff --git a/jquery.countdown-ca.js b/js/localisations/ca.js similarity index 100% rename from jquery.countdown-ca.js rename to js/localisations/ca.js diff --git a/jquery.countdown-cs.js b/js/localisations/cs.js similarity index 100% rename from jquery.countdown-cs.js rename to js/localisations/cs.js diff --git a/jquery.countdown-cy.js b/js/localisations/cy.js similarity index 100% rename from jquery.countdown-cy.js rename to js/localisations/cy.js diff --git a/jquery.countdown-da.js b/js/localisations/da.js similarity index 100% rename from jquery.countdown-da.js rename to js/localisations/da.js diff --git a/jquery.countdown-de.js b/js/localisations/de.js similarity index 100% rename from jquery.countdown-de.js rename to js/localisations/de.js diff --git a/jquery.countdown-el.js b/js/localisations/el.js similarity index 100% rename from jquery.countdown-el.js rename to js/localisations/el.js diff --git a/jquery.countdown-es.js b/js/localisations/es.js similarity index 100% rename from jquery.countdown-es.js rename to js/localisations/es.js diff --git a/jquery.countdown-et.js b/js/localisations/et.js similarity index 100% rename from jquery.countdown-et.js rename to js/localisations/et.js diff --git a/jquery.countdown-fa.js b/js/localisations/fa.js similarity index 100% rename from jquery.countdown-fa.js rename to js/localisations/fa.js diff --git a/jquery.countdown-fi.js b/js/localisations/fi.js similarity index 100% rename from jquery.countdown-fi.js rename to js/localisations/fi.js diff --git a/jquery.countdown-fr.js b/js/localisations/fr.js similarity index 100% rename from jquery.countdown-fr.js rename to js/localisations/fr.js diff --git a/jquery.countdown-gl.js b/js/localisations/gl.js similarity index 100% rename from jquery.countdown-gl.js rename to js/localisations/gl.js diff --git a/jquery.countdown-gu.js b/js/localisations/gu.js similarity index 100% rename from jquery.countdown-gu.js rename to js/localisations/gu.js diff --git a/jquery.countdown-he.js b/js/localisations/he.js similarity index 100% rename from jquery.countdown-he.js rename to js/localisations/he.js diff --git a/jquery.countdown-hr.js b/js/localisations/hr.js similarity index 100% rename from jquery.countdown-hr.js rename to js/localisations/hr.js diff --git a/jquery.countdown-hu.js b/js/localisations/hu.js similarity index 100% rename from jquery.countdown-hu.js rename to js/localisations/hu.js diff --git a/jquery.countdown-hy.js b/js/localisations/hy.js similarity index 100% rename from jquery.countdown-hy.js rename to js/localisations/hy.js diff --git a/jquery.countdown-id.js b/js/localisations/id.js similarity index 100% rename from jquery.countdown-id.js rename to js/localisations/id.js diff --git a/jquery.countdown-it.js b/js/localisations/it.js similarity index 100% rename from jquery.countdown-it.js rename to js/localisations/it.js diff --git a/jquery.countdown-ja.js b/js/localisations/ja.js similarity index 100% rename from jquery.countdown-ja.js rename to js/localisations/ja.js diff --git a/jquery.countdown-kn.js b/js/localisations/kn.js similarity index 100% rename from jquery.countdown-kn.js rename to js/localisations/kn.js diff --git a/jquery.countdown-ko.js b/js/localisations/ko.js similarity index 100% rename from jquery.countdown-ko.js rename to js/localisations/ko.js diff --git a/jquery.countdown-lt.js b/js/localisations/lt.js similarity index 100% rename from jquery.countdown-lt.js rename to js/localisations/lt.js diff --git a/jquery.countdown-lv.js b/js/localisations/lv.js similarity index 100% rename from jquery.countdown-lv.js rename to js/localisations/lv.js diff --git a/jquery.countdown-ml.js b/js/localisations/ml.js similarity index 100% rename from jquery.countdown-ml.js rename to js/localisations/ml.js diff --git a/jquery.countdown-ms.js b/js/localisations/ms.js similarity index 100% rename from jquery.countdown-ms.js rename to js/localisations/ms.js diff --git a/jquery.countdown-my.js b/js/localisations/my.js similarity index 100% rename from jquery.countdown-my.js rename to js/localisations/my.js diff --git a/jquery.countdown-nb.js b/js/localisations/nb.js similarity index 100% rename from jquery.countdown-nb.js rename to js/localisations/nb.js diff --git a/jquery.countdown-nl.js b/js/localisations/nl.js similarity index 100% rename from jquery.countdown-nl.js rename to js/localisations/nl.js diff --git a/jquery.countdown-pl.js b/js/localisations/pl.js similarity index 100% rename from jquery.countdown-pl.js rename to js/localisations/pl.js diff --git a/jquery.countdown-pt-BR.js b/js/localisations/pt-BR.js similarity index 100% rename from jquery.countdown-pt-BR.js rename to js/localisations/pt-BR.js diff --git a/jquery.countdown-ro.js b/js/localisations/ro.js similarity index 100% rename from jquery.countdown-ro.js rename to js/localisations/ro.js diff --git a/jquery.countdown-ru.js b/js/localisations/ru.js similarity index 100% rename from jquery.countdown-ru.js rename to js/localisations/ru.js diff --git a/jquery.countdown-sk.js b/js/localisations/sk.js similarity index 100% rename from jquery.countdown-sk.js rename to js/localisations/sk.js diff --git a/jquery.countdown-sl.js b/js/localisations/sl.js similarity index 100% rename from jquery.countdown-sl.js rename to js/localisations/sl.js diff --git a/jquery.countdown-sq.js b/js/localisations/sq.js similarity index 100% rename from jquery.countdown-sq.js rename to js/localisations/sq.js diff --git a/jquery.countdown-sr-SR.js b/js/localisations/sr-SR.js similarity index 100% rename from jquery.countdown-sr-SR.js rename to js/localisations/sr-SR.js diff --git a/jquery.countdown-sr.js b/js/localisations/sr.js similarity index 100% rename from jquery.countdown-sr.js rename to js/localisations/sr.js diff --git a/jquery.countdown-sv.js b/js/localisations/sv.js similarity index 100% rename from jquery.countdown-sv.js rename to js/localisations/sv.js diff --git a/jquery.countdown-th.js b/js/localisations/th.js similarity index 100% rename from jquery.countdown-th.js rename to js/localisations/th.js diff --git a/jquery.countdown-tr.js b/js/localisations/tr.js similarity index 100% rename from jquery.countdown-tr.js rename to js/localisations/tr.js diff --git a/jquery.countdown-uk.js b/js/localisations/uk.js similarity index 100% rename from jquery.countdown-uk.js rename to js/localisations/uk.js diff --git a/jquery.countdown-ur.js b/js/localisations/ur.js similarity index 100% rename from jquery.countdown-ur.js rename to js/localisations/ur.js diff --git a/jquery.countdown-uz.js b/js/localisations/uz.js similarity index 100% rename from jquery.countdown-uz.js rename to js/localisations/uz.js diff --git a/jquery.countdown-vi.js b/js/localisations/vi.js similarity index 100% rename from jquery.countdown-vi.js rename to js/localisations/vi.js diff --git a/jquery.countdown-zh-CN.js b/js/localisations/zh-CN.js similarity index 100% rename from jquery.countdown-zh-CN.js rename to js/localisations/zh-CN.js diff --git a/jquery.countdown-zh-TW.js b/js/localisations/zh-TW.js similarity index 100% rename from jquery.countdown-zh-TW.js rename to js/localisations/zh-TW.js