Team:NTNU Trondheim/Templates/Footer
From 2012.igem.org
(Difference between revisions)
Line 25: | Line 25: | ||
<script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min.js"></script> | <script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min.js"></script> | ||
<script src="//netdna.bootstrapcdn.com/twitter-bootstrap/2.1.1/js/bootstrap.min.js"></script> | <script src="//netdna.bootstrapcdn.com/twitter-bootstrap/2.1.1/js/bootstrap.min.js"></script> | ||
- | |||
- | |||
- | |||
- | |||
- | |||
- | |||
- | |||
- | |||
- | |||
- | |||
- | |||
- | |||
- | |||
- | |||
- | |||
<script type="text/javascript"> | <script type="text/javascript"> | ||
Line 55: | Line 40: | ||
<!-- jQuery countdown --> | <!-- jQuery countdown --> | ||
- | <script type="text/javascript" | + | <script type="text/javascript"> |
+ | |||
+ | /* http://keith-wood.name/countdown.html | ||
+ | Countdown for jQuery v1.5.11. | ||
+ | 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. | ||
+ | Please attribute the author if you use it. */ | ||
+ | |||
+ | /* Display a countdown timer. | ||
+ | Attach it with options like: | ||
+ | $('div selector').countdown( | ||
+ | {until: new Date(2009, 1 - 1, 1, 0, 0, 0), onExpiry: happyNewYear}); */ | ||
+ | |||
+ | (function($) { // Hide scope, no $ conflict | ||
+ | |||
+ | /* Countdown manager. */ | ||
+ | function Countdown() { | ||
+ | this.regional = []; // Available regional settings, indexed by language code | ||
+ | this.regional[''] = { // Default regional settings | ||
+ | // The display texts for the counters | ||
+ | labels: ['Years', 'Months', 'Weeks', 'Days', 'Hours', 'Minutes', 'Seconds'], | ||
+ | // 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 | ||
+ | }; | ||
+ | this._defaults = { | ||
+ | until: null, // new Date(year, mth - 1, day, hr, min, sec) - date/time to count down to | ||
+ | // or numeric for seconds offset, or string for unit offset(s): | ||
+ | // 'Y' years, 'O' months, 'W' weeks, 'D' days, 'H' hours, 'M' minutes, 'S' seconds | ||
+ | since: null, // new Date(year, mth - 1, day, hr, min, sec) - date/time to count up from | ||
+ | // or numeric for seconds offset, or string for unit offset(s): | ||
+ | // 'Y' years, 'O' months, 'W' weeks, 'D' days, 'H' hours, 'M' minutes, 'S' seconds | ||
+ | timezone: null, // The timezone (hours or minutes from GMT) for the target times, | ||
+ | // or null for client local | ||
+ | serverSync: null, // A function to retrieve the current server time for synchronisation | ||
+ | format: 'dHMS', // 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 | ||
+ | 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 | ||
+ | alwaysExpire: false, // True to trigger onExpiry even if never counted down | ||
+ | onExpiry: null, // Callback when the countdown expires - | ||
+ | // receives no parameters and 'this' is the containing division | ||
+ | onTick: null, // Callback when the countdown is updated - | ||
+ | // receives int[7] being the breakdown by period (based on format) | ||
+ | // and 'this' is the containing division | ||
+ | tickInterval: 1 // Interval (seconds) between onTick callbacks | ||
+ | }; | ||
+ | $.extend(this._defaults, this.regional['']); | ||
+ | this._serverSyncs = []; | ||
+ | // Shared timer for all countdowns | ||
+ | function timerCallBack(timestamp) { | ||
+ | var drawStart = (timestamp || new Date().getTime()); | ||
+ | if (drawStart - animationStartTime >= 1000) { | ||
+ | $.countdown._updateTargets(); | ||
+ | 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) { | ||
+ | setInterval(function() { $.countdown._updateTargets(); }, 980); // Fall back to good old setInterval | ||
+ | } | ||
+ | else { | ||
+ | animationStartTime = window.mozAnimationStartTime || new Date().getTime(); | ||
+ | requestAnimationFrame(timerCallBack); | ||
+ | } | ||
+ | } | ||
+ | |||
+ | var PROP_NAME = '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 | ||
+ | |||
+ | $.extend(Countdown.prototype, { | ||
+ | /* Class name added to elements to indicate already configured with countdown. */ | ||
+ | markerClassName: 'hasCountdown', | ||
+ | |||
+ | /* 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) { | ||
+ | this._resetExtraLabels(this._defaults, options); | ||
+ | extendRemove(this._defaults, options || {}); | ||
+ | }, | ||
+ | |||
+ | /* Convert a date/time to UTC. | ||
+ | @param tz (number) the hour or minute offset from GMT, e.g. +9, -360 | ||
+ | @param year (Date) the date/time in that timezone or | ||
+ | (number) the year in that timezone | ||
+ | @param month (number, optional) the month (0 - 11) (omit if year is a Date) | ||
+ | @param day (number, optional) the day (omit if year is a Date) | ||
+ | @param hours (number, optional) the hour (omit if year is a Date) | ||
+ | @param mins (number, optional) the minute (omit if year is a Date) | ||
+ | @param secs (number, optional) the second (omit if year is a Date) | ||
+ | @param ms (number, optional) the millisecond (omit if year is a Date) | ||
+ | @return (Date) the equivalent UTC date/time */ | ||
+ | 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[7]) the periods per year/month/week/day/hour/minute/second | ||
+ | @return (number) the corresponding number of seconds */ | ||
+ | periodsToSeconds: function(periods) { | ||
+ | return periods[0] * 31557600 + periods[1] * 2629800 + periods[2] * 604800 + | ||
+ | periods[3] * 86400 + periods[4] * 3600 + periods[5] * 60 + periods[6]; | ||
+ | }, | ||
+ | |||
+ | /* Retrieve one or more settings values. | ||
+ | @param name (string, optional) the name of the setting to retrieve | ||
+ | or 'all' for all instance settings or omit for all default settings | ||
+ | @return (any) the requested setting(s) */ | ||
+ | _settingsCountdown: function(target, name) { | ||
+ | if (!name) { | ||
+ | return $.countdown._defaults; | ||
+ | } | ||
+ | var inst = $.data(target, PROP_NAME); | ||
+ | return (name == 'all' ? inst.options : inst.options[name]); | ||
+ | }, | ||
+ | |||
+ | /* Attach the countdown widget to a div. | ||
+ | @param target (element) the containing division | ||
+ | @param options (object) the initial settings for the countdown */ | ||
+ | _attachCountdown: function(target, options) { | ||
+ | var $target = $(target); | ||
+ | if ($target.hasClass(this.markerClassName)) { | ||
+ | return; | ||
+ | } | ||
+ | $target.addClass(this.markerClassName); | ||
+ | var inst = {options: $.extend({}, options), | ||
+ | _periods: [0, 0, 0, 0, 0, 0, 0]}; | ||
+ | $.data(target, PROP_NAME, inst); | ||
+ | this._changeCountdown(target); | ||
+ | }, | ||
+ | |||
+ | /* Add a target to the list of active ones. | ||
+ | @param target (element) the countdown target */ | ||
+ | _addTarget: function(target) { | ||
+ | if (!this._hasTarget(target)) { | ||
+ | this._timerTargets.push(target); | ||
+ | } | ||
+ | }, | ||
+ | |||
+ | /* See if a target is in the list of active ones. | ||
+ | @param target (element) the countdown target | ||
+ | @return (boolean) true if present, false if not */ | ||
+ | _hasTarget: function(target) { | ||
+ | return ($.inArray(target, this._timerTargets) > -1); | ||
+ | }, | ||
+ | |||
+ | /* Remove a target from the list of active ones. | ||
+ | @param target (element) the countdown target */ | ||
+ | _removeTarget: function(target) { | ||
+ | this._timerTargets = $.map(this._timerTargets, | ||
+ | function(value) { return (value == target ? null : value); }); // delete entry | ||
+ | }, | ||
+ | |||
+ | /* Update each active timer target. */ | ||
+ | _updateTargets: function() { | ||
+ | for (var i = this._timerTargets.length - 1; i >= 0; i--) { | ||
+ | this._updateCountdown(this._timerTargets[i]); | ||
+ | } | ||
+ | }, | ||
+ | |||
+ | /* Redisplay the countdown with an updated display. | ||
+ | @param target (jQuery) the containing division | ||
+ | @param inst (object) the current settings for this instance */ | ||
+ | _updateCountdown: function(target, inst) { | ||
+ | var $target = $(target); | ||
+ | inst = inst || $.data(target, PROP_NAME); | ||
+ | if (!inst) { | ||
+ | return; | ||
+ | } | ||
+ | $target.html(this._generateHTML(inst)); | ||
+ | $target[(this._get(inst, 'isRTL') ? 'add' : 'remove') + 'Class']('countdown_rtl'); | ||
+ | var onTick = this._get(inst, 'onTick'); | ||
+ | if (onTick) { | ||
+ | var periods = inst._hold != 'lap' ? inst._periods : | ||
+ | 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]); | ||
+ | } | ||
+ | } | ||
+ | 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._hasTarget(target) || this._get(inst, 'alwaysExpire')) { | ||
+ | this._removeTarget(target); | ||
+ | var onExpiry = this._get(inst, 'onExpiry'); | ||
+ | if (onExpiry) { | ||
+ | onExpiry.apply(target, []); | ||
+ | } | ||
+ | var expiryText = this._get(inst, 'expiryText'); | ||
+ | if (expiryText) { | ||
+ | var layout = this._get(inst, 'layout'); | ||
+ | inst.options.layout = expiryText; | ||
+ | this._updateCountdown(target, inst); | ||
+ | inst.options.layout = layout; | ||
+ | } | ||
+ | var expiryUrl = this._get(inst, 'expiryUrl'); | ||
+ | if (expiryUrl) { | ||
+ | window.location = expiryUrl; | ||
+ | } | ||
+ | } | ||
+ | inst._expiring = false; | ||
+ | } | ||
+ | else if (inst._hold == 'pause') { | ||
+ | this._removeTarget(target); | ||
+ | } | ||
+ | $.data(target, PROP_NAME, inst); | ||
+ | }, | ||
+ | |||
+ | /* Reconfigure the settings for a countdown div. | ||
+ | @param target (element) the containing division | ||
+ | @param options (object) the new settings for the countdown or | ||
+ | (string) an individual property name | ||
+ | @param value (any) the individual property value | ||
+ | (omit if options is an object) */ | ||
+ | _changeCountdown: function(target, options, value) { | ||
+ | options = options || {}; | ||
+ | if (typeof options == 'string') { | ||
+ | var name = options; | ||
+ | options = {}; | ||
+ | options[name] = value; | ||
+ | } | ||
+ | var inst = $.data(target, PROP_NAME); | ||
+ | if (inst) { | ||
+ | this._resetExtraLabels(inst.options, options); | ||
+ | extendRemove(inst.options, options); | ||
+ | this._adjustSettings(target, inst); | ||
+ | $.data(target, PROP_NAME, inst); | ||
+ | var now = new Date(); | ||
+ | if ((inst._since && inst._since < now) || | ||
+ | (inst._until && inst._until > now)) { | ||
+ | this._addTarget(target); | ||
+ | } | ||
+ | this._updateCountdown(target, inst); | ||
+ | } | ||
+ | }, | ||
+ | |||
+ | /* Reset any extra labelsn and compactLabelsn entries if changing labels. | ||
+ | @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[0-9]/)) { | ||
+ | base[n] = null; | ||
+ | } | ||
+ | } | ||
+ | } | ||
+ | }, | ||
+ | |||
+ | /* Calculate interal settings for an instance. | ||
+ | @param target (element) the containing division | ||
+ | @param inst (object) the current settings for this instance */ | ||
+ | _adjustSettings: function(target, inst) { | ||
+ | var now; | ||
+ | var serverSync = this._get(inst, 'serverSync'); | ||
+ | var serverOffset = 0; | ||
+ | var serverEntry = null; | ||
+ | for (var i = 0; i < this._serverSyncs.length; i++) { | ||
+ | if (this._serverSyncs[i][0] == serverSync) { | ||
+ | serverEntry = this._serverSyncs[i][1]; | ||
+ | break; | ||
+ | } | ||
+ | } | ||
+ | if (serverEntry != null) { | ||
+ | serverOffset = (serverSync ? serverEntry : 0); | ||
+ | now = new Date(); | ||
+ | } | ||
+ | else { | ||
+ | var serverResult = (serverSync ? serverSync.apply(target, []) : null); | ||
+ | now = new Date(); | ||
+ | serverOffset = (serverResult ? now.getTime() - serverResult.getTime() : 0); | ||
+ | this._serverSyncs.push([serverSync, serverOffset]); | ||
+ | } | ||
+ | var timezone = this._get(inst, 'timezone'); | ||
+ | timezone = (timezone == null ? -now.getTimezoneOffset() : timezone); | ||
+ | inst._since = this._get(inst, '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(this._get(inst, 'until'), now)); | ||
+ | if (serverOffset) { | ||
+ | inst._until.setMilliseconds(inst._until.getMilliseconds() + serverOffset); | ||
+ | } | ||
+ | inst._show = this._determineShow(inst); | ||
+ | }, | ||
+ | |||
+ | /* Remove the countdown widget from a div. | ||
+ | @param target (element) the containing division */ | ||
+ | _destroyCountdown: function(target) { | ||
+ | var $target = $(target); | ||
+ | if (!$target.hasClass(this.markerClassName)) { | ||
+ | return; | ||
+ | } | ||
+ | this._removeTarget(target); | ||
+ | $target.removeClass(this.markerClassName).empty(); | ||
+ | $.removeData(target, PROP_NAME); | ||
+ | }, | ||
+ | |||
+ | /* Pause a countdown widget at the current time. | ||
+ | Stop it running but remember and display the current time. | ||
+ | @param target (element) the containing division */ | ||
+ | _pauseCountdown: function(target) { | ||
+ | this._hold(target, 'pause'); | ||
+ | }, | ||
+ | |||
+ | /* Pause a countdown widget at the current time. | ||
+ | Stop the display but keep the countdown running. | ||
+ | @param target (element) the containing division */ | ||
+ | _lapCountdown: function(target) { | ||
+ | this._hold(target, 'lap'); | ||
+ | }, | ||
+ | |||
+ | /* Resume a paused countdown widget. | ||
+ | @param target (element) the containing division */ | ||
+ | _resumeCountdown: function(target) { | ||
+ | this._hold(target, null); | ||
+ | }, | ||
+ | |||
+ | /* Pause or resume a countdown widget. | ||
+ | @param target (element) the containing division | ||
+ | @param hold (string) the new hold setting */ | ||
+ | _hold: function(target, hold) { | ||
+ | var inst = $.data(target, PROP_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._addTarget(target); | ||
+ | } | ||
+ | inst._hold = hold; | ||
+ | inst._savePeriods = (hold == 'pause' ? inst._periods : null); | ||
+ | $.data(target, PROP_NAME, inst); | ||
+ | this._updateCountdown(target, inst); | ||
+ | } | ||
+ | }, | ||
+ | |||
+ | /* Return the current time periods. | ||
+ | @param target (element) the containing division | ||
+ | @return (number[7]) the current periods for the countdown */ | ||
+ | _getTimesCountdown: function(target) { | ||
+ | var inst = $.data(target, PROP_NAME); | ||
+ | return (!inst ? null : (!inst._hold ? inst._periods : | ||
+ | this._calculatePeriods(inst, inst._show, this._get(inst, 'significant'), new Date()))); | ||
+ | }, | ||
+ | |||
+ | /* Get a setting value, defaulting if necessary. | ||
+ | @param inst (object) the current settings for this instance | ||
+ | @param name (string) the name of the required setting | ||
+ | @return (any) the setting's value or a default if not overridden */ | ||
+ | _get: function(inst, name) { | ||
+ | return (inst.options[name] != null ? | ||
+ | inst.options[name] : $.countdown._defaults[name]); | ||
+ | }, | ||
+ | |||
+ | /* A time may be specified as an exact value or a relative one. | ||
+ | @param setting (string or number or 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 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, $.countdown._getDaysInMonth(year, month)); | ||
+ | break; | ||
+ | case 'y': | ||
+ | year += parseInt(matches[1], 10); | ||
+ | day = Math.min(day, $.countdown._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. | ||
+ | @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(); | ||
+ | }, | ||
+ | |||
+ | /* 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 | ||
+ | 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 = 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 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 labelsNum = $.countdown._get(inst, 'labels' + whichLabels(inst._periods[period])); | ||
+ | return ((!significant && show[period]) || (significant && showSignificant[period]) ? | ||
+ | '<span class="countdown_section"><span class="countdown_amount">' + | ||
+ | inst._periods[period] + '</span><br/>' + | ||
+ | (labelsNum ? labelsNum[period] : labels[period]) + '</span>' : ''); | ||
+ | }; | ||
+ | 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(inst._periods[H], 2) : '') + | ||
+ | (show[M] ? (show[H] ? timeSeparator : '') + | ||
+ | this._minDigits(inst._periods[M], 2) : '') + | ||
+ | (show[S] ? (show[H] || show[M] ? timeSeparator : '') + | ||
+ | this._minDigits(inst._periods[S], 2) : '') : | ||
+ | // Full version | ||
+ | '<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>' + | ||
+ | (description ? '<span class="countdown_row countdown_descr">' + description + '</span>' : ''))); | ||
+ | }, | ||
+ | |||
+ | /* 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 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, 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') + whichLabels(inst._periods[index])) || | ||
+ | labels)[index]; | ||
+ | }; | ||
+ | var digit = function(value, position) { | ||
+ | return Math.floor(value / position) % 10; | ||
+ | }; | ||
+ | var subs = {desc: this._get(inst, 'description'), sep: this._get(inst, 'timeSeparator'), | ||
+ | yl: labelFor(Y), yn: inst._periods[Y], ynn: this._minDigits(inst._periods[Y], 2), | ||
+ | ynnn: this._minDigits(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: inst._periods[O], onn: this._minDigits(inst._periods[O], 2), | ||
+ | onnn: this._minDigits(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: inst._periods[W], wnn: this._minDigits(inst._periods[W], 2), | ||
+ | wnnn: this._minDigits(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: inst._periods[D], dnn: this._minDigits(inst._periods[D], 2), | ||
+ | dnnn: this._minDigits(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: inst._periods[H], hnn: this._minDigits(inst._periods[H], 2), | ||
+ | hnnn: this._minDigits(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: inst._periods[M], mnn: this._minDigits(inst._periods[M], 2), | ||
+ | mnnn: this._minDigits(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: inst._periods[S], snn: this._minDigits(inst._periods[S], 2), | ||
+ | snnn: this._minDigits(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 + '<\\}(.*)\\{' + 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. | ||
+ | @param value (number) the value to display | ||
+ | @param len (number) the minimum length | ||
+ | @return (string) the display text */ | ||
+ | _minDigits: function(value, len) { | ||
+ | value = '' + value; | ||
+ | if (value.length >= len) { | ||
+ | return value; | ||
+ | } | ||
+ | value = '0000000000' + value; | ||
+ | return value.substr(value.length - len); | ||
+ | }, | ||
+ | |||
+ | /* Translate the format into flags for each period. | ||
+ | @param inst (object) the current settings for this instance | ||
+ | @return (string[7]) flags indicating which periods are requested (?) or | ||
+ | required (!) by year, month, week, day, hour, minute, second */ | ||
+ | _determineShow: function(inst) { | ||
+ | var format = this._get(inst, '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. | ||
+ | @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, 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 = $.countdown._getDaysInMonth(now.getFullYear(), now.getMonth()); | ||
+ | var lastUntil = $.countdown._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 = $.countdown._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 extend now ignores nulls! | ||
+ | @param target (object) the object to update | ||
+ | @param props (object) the new settings | ||
+ | @return (object) the updated object */ | ||
+ | function extendRemove(target, props) { | ||
+ | $.extend(target, props); | ||
+ | for (var name in props) { | ||
+ | if (props[name] == null) { | ||
+ | target[name] = null; | ||
+ | } | ||
+ | } | ||
+ | return target; | ||
+ | } | ||
+ | |||
+ | /* Process the countdown functionality for a jQuery selection. | ||
+ | @param command (string) the command to run (optional, default 'attach') | ||
+ | @param options (object) the new settings to use for these countdown instances | ||
+ | @return (jQuery) for chaining further calls */ | ||
+ | $.fn.countdown = function(options) { | ||
+ | var otherArgs = Array.prototype.slice.call(arguments, 1); | ||
+ | if (options == 'getTimes' || options == 'settings') { | ||
+ | return $.countdown['_' + options + 'Countdown']. | ||
+ | apply($.countdown, [this[0]].concat(otherArgs)); | ||
+ | } | ||
+ | return this.each(function() { | ||
+ | if (typeof options == 'string') { | ||
+ | $.countdown['_' + options + 'Countdown'].apply($.countdown, [this].concat(otherArgs)); | ||
+ | } | ||
+ | else { | ||
+ | $.countdown._attachCountdown(this, options); | ||
+ | } | ||
+ | }); | ||
+ | }; | ||
+ | |||
+ | /* Initialise the countdown functionality. */ | ||
+ | $.countdown = new Countdown(); // singleton instance | ||
+ | |||
+ | })(jQuery); | ||
+ | |||
+ | |||
+ | </script> | ||
<script type="text/javascript"> | <script type="text/javascript"> | ||
$(function () { | $(function () { |
Revision as of 22:23, 26 September 2012