Merge pull request #3 from prmtl/master
[wolnelektury.git] / wolnelektury / static / js / jquery.countdown.js
index 06f6125..f9c58d3 100644 (file)
@@ -1,12 +1,10 @@
 /* http://keith-wood.name/countdown.html
-   Countdown for jQuery v1.5.7.
+   Countdown for jQuery v1.5.8.
    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 
-   MIT (http://dev.jquery.com/browser/trunk/jquery/MIT-LICENSE.txt) licenses. 
+   Dual licensed under the GPL (http://dev.jquery.com/browser/trunk/jquery/GPL-LICENSE.txt) and
+   MIT (http://dev.jquery.com/browser/trunk/jquery/MIT-LICENSE.txt) licenses.
    Please attribute the author if you use it. */
 
-/* Modified by Radek Czajka, Fundacja Nowoczesna Polska (radoslaw.czajka(at)nowoczesnapolska.org.pl) */
-
 /* Display a countdown timer.
    Attach it with options like:
    $('div selector').countdown(
@@ -23,9 +21,9 @@ function Countdown() {
                // The display texts for the counters if only one
                labels1: ['Year', 'Month', 'Week', 'Day', 'Hour', 'Minute', 'Second'],
                compactLabels: ['y', 'm', 'w', 'd'], // The compact texts for the counters
+               whichLabels: null, // Function to determine which labels to use
                timeSeparator: ':', // Separator for time periods
-               isRTL: false, // True for right-to-left languages, false for left-to-right
-               which: function(n) {return n}
+               isRTL: false // True for right-to-left languages, false for left-to-right
        };
        this._defaults = {
                until: null, // new Date(year, mth - 1, day, hr, min, sec) - date/time to count down to
@@ -41,6 +39,7 @@ function Countdown() {
                        // 'Y' years, 'O' months, 'W' weeks, 'D' days, 'H' hours, 'M' minutes, 'S' seconds
                layout: '', // Build your own layout for the countdown
                compact: false, // True to display in a compact format, false for an expanded one
+               significant: 0, // The number of periods with values to show, zero for all
                description: '', // The description displayed for the countdown
                expiryUrl: '', // A URL to load upon expiry, replacing the current page
                expiryText: '', // Text to display upon expiry, replacing the countdown
@@ -69,12 +68,12 @@ var S = 6; // Seconds
 $.extend(Countdown.prototype, {
        /* Class name added to elements to indicate already configured with countdown. */
        markerClassName: 'hasCountdown',
-       
+
        /* Shared timer for all countdowns. */
        _timer: setInterval(function() { $.countdown._updateTargets(); }, 980),
        /* List of currently active countdown targets. */
        _timerTargets: [],
-       
+
        /* Override the default settings for all instances of the countdown widget.
           @param  options  (object) the new settings to use as defaults */
        setDefaults: function(options) {
@@ -194,7 +193,7 @@ $.extend(Countdown.prototype, {
                var onTick = this._get(inst, 'onTick');
                if (onTick) {
                        var periods = inst._hold != 'lap' ? inst._periods :
-                               this._calculatePeriods(inst, inst._show, new Date());
+                               this._calculatePeriods(inst, inst._show, this._get(inst, 'significant'), new Date());
                        var tickInterval = this._get(inst, 'tickInterval');
                        if (tickInterval == 1 || this.periodsToSeconds(periods) % tickInterval == 0) {
                                onTick.apply(target, [periods]);
@@ -265,7 +264,7 @@ $.extend(Countdown.prototype, {
        _resetExtraLabels: function(base, options) {
                var changingLabels = false;
                for (var n in options) {
-                       if (n.match(/[Ll]abels/)) {
+                       if (n != 'whichLabels' && n.match(/[Ll]abels/)) {
                                changingLabels = true;
                                break;
                        }
@@ -278,7 +277,7 @@ $.extend(Countdown.prototype, {
                        }
                }
        },
-       
+
        /* Calculate interal settings for an instance.
           @param  target  (element) the containing division
           @param  inst    (object) the current settings for this instance */
@@ -363,7 +362,7 @@ $.extend(Countdown.prototype, {
                                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[3] + 'd' + sign + inst._periods[4] + 'h' +
                                                sign + inst._periods[5] + 'm' + sign + inst._periods[6] + 's');
                                this._addTarget(target);
                        }
@@ -380,7 +379,7 @@ $.extend(Countdown.prototype, {
        _getTimesCountdown: function(target) {
                var inst = $.data(target, PROP_NAME);
                return (!inst ? null : (!inst._hold ? inst._periods :
-                       this._calculatePeriods(inst, inst._show, new Date())));
+                       this._calculatePeriods(inst, inst._show, this._get(inst, 'significant'), new Date())));
        },
 
        /* Get a setting value, defaulting if necessary.
@@ -422,7 +421,7 @@ $.extend(Countdown.prototype, {
                                        case 'd': day += parseInt(matches[1], 10); break;
                                        case 'w': day += parseInt(matches[1], 10) * 7; break;
                                        case 'o':
-                                               month += parseInt(matches[1], 10); 
+                                               month += parseInt(matches[1], 10);
                                                day = Math.min(day, $.countdown._getDaysInMonth(year, month));
                                                break;
                                        case 'y':
@@ -449,57 +448,75 @@ $.extend(Countdown.prototype, {
                return 32 - new Date(year, month, 32).getDate();
        },
 
+       /* Determine which set of labels should be used for an amount.
+          @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.
           @param  inst  (object) the current settings for this instance
           @return  (string) the new HTML for the countdown display */
        _generateHTML: function(inst) {
                // Determine what to show
-               inst._periods = periods = (inst._hold ? inst._periods :
-                       this._calculatePeriods(inst, inst._show, new Date()));
+               var significant = this._get(inst, 'significant');
+               inst._periods = (inst._hold ? inst._periods :
+                       this._calculatePeriods(inst, inst._show, significant, new Date()));
                // Show all 'asNeeded' after first non-zero value
                var shownNonZero = false;
                var showCount = 0;
+               var sigCount = significant;
                var show = $.extend({}, inst._show);
-               for (var period = 0; period < inst._show.length; period++) {
-                       shownNonZero |= (inst._show[period] == '?' && periods[period] > 0);
+               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 compact = this._get(inst, 'compact');
                var layout = this._get(inst, 'layout');
                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 which = $.countdown._get(inst, 'which');
-            if (which) {
-                var labelsNum = $.countdown._get(inst, 'compactLabels' + which(periods[period]));
-            }
-                       return (show[period] ? periods[period] +
+                       var labelsNum = $.countdown._get(inst,
+                               'compactLabels' + whichLabels(inst._periods[period]));
+                       return (show[period] ? inst._periods[period] +
                                (labelsNum ? labelsNum[period] : labels[period]) + ' ' : '');
                };
                var showFull = function(period) {
-                       var which = $.countdown._get(inst, 'which');
-                       if (which) {
-                               var labelsNum = $.countdown._get(inst, 'labels' + which(periods[period]));
-                       }
-                       return (show[period] ?
+                       var labelsNum = $.countdown._get(inst, 'labels' + whichLabels(inst._periods[period]));
+                       return ((!significant && show[period]) || (significant && showSignificant[period]) ?
                                '<span class="countdown_section"><span class="countdown_amount">' +
-                               periods[period] + '</span><br/>' +
+                               inst._periods[period] + '</span><br/>' +
                                (labelsNum ? labelsNum[period] : labels[period]) + '</span>' : '');
                };
-               return (layout ? this._buildLayout(inst, show, layout, compact) :
+               return (layout ? this._buildLayout(inst, show, layout, compact, significant, showSignificant) :
                        ((compact ? // Compact version
                        '<span class="countdown_row countdown_amount' +
-                       (inst._hold ? ' countdown_holding' : '') + '">' + 
-                       showCompact(Y) + showCompact(O) + showCompact(W) + showCompact(D) + 
-                       (show[H] ? this._minDigits(periods[H], 2) : '') +
+                       (inst._hold ? ' countdown_holding' : '') + '">' +
+                       showCompact(Y) + showCompact(O) + showCompact(W) + showCompact(D) +
+                       (show[H] ? this._minDigits(inst._periods[H], 2) : '') +
                        (show[M] ? (show[H] ? timeSeparator : '') +
-                       this._minDigits(periods[M], 2) : '') +
+                       this._minDigits(inst._periods[M], 2) : '') +
                        (show[S] ? (show[H] || show[M] ? timeSeparator : '') +
-                       this._minDigits(periods[S], 2) : '') :
+                       this._minDigits(inst._periods[S], 2) : '') :
                        // Full version
-                       '<span class="countdown_row countdown_show' + showCount +
+                       '<span class="countdown_row countdown_show' + (significant || showCount) +
                        (inst._hold ? ' countdown_holding' : '') + '">' +
                        showFull(Y) + showFull(O) + showFull(W) + showFull(D) +
                        showFull(H) + showFull(M) + showFull(S)) + '</span>' +
@@ -507,16 +524,19 @@ $.extend(Countdown.prototype, {
        },
 
        /* Construct a custom layout.
-          @param  inst     (object) the current settings for this instance
-          @param  show     (string[7]) flags indicating which periods are requested
-          @param  layout   (string) the customised layout
-          @param  compact  (boolean) true if using compact labels
+          @param  inst             (object) the current settings for this instance
+          @param  show             (string[7]) 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[7]) other periods to show for significance
           @return  (string) the custom HTML */
-       _buildLayout: function(inst, show, layout, compact) {
+       _buildLayout: function(inst, show, layout, compact, significant, showSignificant) {
                var labels = this._get(inst, (compact ? 'compactLabels' : 'labels'));
+               var whichLabels = this._get(inst, 'whichLabels') || this._normalLabels;
                var labelFor = function(index) {
                        return ($.countdown._get(inst,
-                               (compact ? 'compactLabels' : 'labels') + inst._periods[index]) ||
+                               (compact ? 'compactLabels' : 'labels') + whichLabels(inst._periods[index])) ||
                                labels)[index];
                };
                var digit = function(value, position) {
@@ -553,10 +573,11 @@ $.extend(Countdown.prototype, {
                        s1000: digit(inst._periods[S], 1000)};
                var html = layout;
                // Replace period containers: {p<}...{p>}
-               for (var i = 0; i < 7; i++) {
+               for (var i = Y; i <= S; i++) {
                        var period = 'yowdhms'.charAt(i);
                        var re = new RegExp('\\{' + period + '<\\}(.*)\\{' + period + '>\\}', 'g');
-                       html = html.replace(re, (show[i] ? '$1' : ''));
+                       html = html.replace(re, ((!significant && show[i]) ||
+                               (significant && showSignificant[i]) ? '$1' : ''));
                }
                // Replace period values: {pn}
                $.each(subs, function(n, v) {
@@ -595,14 +616,15 @@ $.extend(Countdown.prototype, {
                show[S] = (format.match('s') ? '?' : (format.match('S') ? '!' : null));
                return show;
        },
-       
+
        /* Calculate the requested periods between now and the target time.
-          @param  inst  (object) the current settings for this instance
-          @param  show  (string[7]) flags indicating which periods are requested/required
-          @param  now   (Date) the current date and time
+          @param  inst         (object) the current settings for this instance
+          @param  show         (string[7]) 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[7]) the current time periods (always positive)
                    by year, month, week, day, hour, minute, second */
-       _calculatePeriods: function(inst, show, now) {
+       _calculatePeriods: function(inst, show, significant, now) {
                // Find endpoints
                inst._now = now;
                inst._now.setMilliseconds(0);
@@ -640,25 +662,17 @@ $.extend(Countdown.prototype, {
                        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
-                       var adjustDate = function(date, offset, last) {
-                               var wasLastDay = (date.getDate() == last);
-                               var lastDay = $.countdown._getDaysInMonth(date.getFullYear() + offset * periods[Y],
-                                       date.getMonth() + offset * periods[O]);
-                               if (date.getDate() > lastDay) {
-                                       date.setDate(lastDay);
-                               }
-                               date.setFullYear(date.getFullYear() + offset * periods[Y]);
-                               date.setMonth(date.getMonth() + offset * periods[O]);
-                               if (wasLastDay) {
-                                       date.setDate(lastDay);
-                               }
-                               return date;
-                       };
-                       if (inst._since) {
-                               until = adjustDate(until, -1, lastUntil);
+                       now = new Date(now.getTime());
+                       var wasLastDay = (now.getDate() == lastNow);
+                       var lastDay = $.countdown._getDaysInMonth(now.getFullYear() + periods[Y],
+                               now.getMonth() + periods[O]);
+                       if (now.getDate() > lastDay) {
+                               now.setDate(lastDay);
                        }
-                       else {
-                               now = adjustDate(new Date(now.getTime()), +1, lastNow);
+                       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);
@@ -691,6 +705,16 @@ $.extend(Countdown.prototype, {
                                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;
        }
 });