diff --git a/README.md b/README.md index 6671be2..0ce5d87 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,10 @@ -Take a look to the [demostration](http://reflejo.github.com/jquery-countdown/). This countdown has an amazing animation. +# jQuery Countdown -This project is still at beta stage, and probably it will remain that way :(. Now you can download the PSD file (It's [here](https://github.com/Reflejo/jquery-countdown/blob/master/img/digits.psd)) +jQuery Countdown is a countdown library with an amazing animation. Take a look +at the [demonstration](http://reflejo.github.com/jquery-countdown/) + +Now you can download the PSD file +[here](https://github.com/Reflejo/jquery-countdown/blob/master/img/digits.psd). ### Basic usage: @@ -23,12 +27,63 @@ This project is still at beta stage, and probably it will remain that way :(. No }); ``` -Did I mention that js code weighs just **1.7 KB**? +### Added continuous countdown + +```javascript + $('#counter').countdown({ + format: 'sss', + startTime: "120", + continuous: true, + timerEnd: function() { alert('end!!'); }, + image: "digits.png" + }); +``` + +### Countdown to a Date + +Relative to current hour: + +```javascript + $('#counter').countdown({ + image: "digits.png", + format: "mm:ss", + endTime: '50:00' + }); +``` -### Author +An absolute date: -Martín Conte Mac Donell - [@Reflejo](https://twitter.com/reflejo) + +```javascript + $('#counter').countdown({ + image: "digits.png", + format: "mm:ss", + endTime: new Date('07/16/13 05:00:00') + }); +``` + +Start manually the counter: + +```javascript + $('#counter').countdown({ + image: "digits.png", + format: "mm:ss", + endTime: new Date('07/16/13 05:00:00'), + start: false + }); + $("#startButton").click(function (e) { + $('#counter').start(); + }); +``` + +Did I mention that js code weighs just **4.0 KB**? + +### Developers + +- Martín Conte Mac Donell - - [@fz](https://twitter.com/fz) +- [Matt Neary](http://mattneary.com) - ### Demo -See the [demo](http://reflejo.github.com/jquery-countdown/) !!. +Look at the [demo](http://reflejo.github.com/jquery-countdown/). + diff --git a/css/media.css b/css/media.css index 8d52b74..8495c53 100644 --- a/css/media.css +++ b/css/media.css @@ -15,8 +15,7 @@ html, body, .wrapper { } #holder { - background: url(../img/time-placeholder.png) no-repeat; - width: 383px; + width: 603px; height: 383px; margin: auto; } diff --git a/index.html b/index.html index 11edf18..1742180 100644 --- a/index.html +++ b/index.html @@ -1,4 +1,4 @@ - + jquery-countdown plugin test @@ -9,8 +9,8 @@ $(function(){ $(".digits").countdown({ image: "img/digits.png", - format: "mm:ss", - startTime: "25:14" + format: "dd:hh:mm:ss", + endTime: new Date(2013, 12, 2) }); }); diff --git a/js/jquery.countdown.js b/js/jquery.countdown.js index df916a6..532668e 100644 --- a/js/jquery.countdown.js +++ b/js/jquery.countdown.js @@ -4,149 +4,240 @@ * Copyright (c) 2009 Martin Conte Mac Donell * Dual licensed under the MIT and GPL licenses. * http://docs.jquery.com/License + * */ -jQuery.fn.countdown = function(userOptions) -{ - // Default options - var options = { - stepTime: 60, - // startTime and format MUST follow the same format. - // also you cannot specify a format unordered (e.g. hh:ss:mm is wrong) - format: "dd:hh:mm:ss", - startTime: "01:12:32:55", - digitImages: 6, - digitWidth: 67, - digitHeight: 90, - timerEnd: function(){}, - image: "digits.png" - }; - var digits = [], intervals = []; - - // Draw digits in given container - var createDigits = function(where) - { - var c = 0; - // Iterate each startTime digit, if it is not a digit - // we'll asume that it's a separator - for (var i = 0; i < options.startTime.length; i++) - { - if (parseInt(options.startTime[i]) >= 0) - { - elem = $('
').css({ - height: options.digitHeight, - float: 'left', - background: 'url(\'' + options.image + '\')', - width: options.digitWidth - }); - - elem.current = parseInt(options.startTime[i]); - digits.push(elem); - - margin(c, -elem.current * options.digitHeight * options.digitImages); - - // Add max digits, for example, first digit of minutes (mm) has - // a max of 5. Conditional max is used when the left digit has reach - // the max. For example second "hours" digit has a conditional max of 4 - switch (options.format[i]) - { - case 'h': - digits[c]._max = function(pos, isStart) { - if (pos % 2 == 0) - return 2; - else - return (isStart) ? 3: 9; - }; - break; - case 'd': - digits[c]._max = function(){ return 9; }; - break; - case 'm': - case 's': - digits[c]._max = function(pos){ return (pos % 2 == 0) ? 5: 9; }; - } - ++c; - } - else - { - elem = $('
').css({float: 'left'}) - .text(options.startTime[i]); +// Draw digits in given container +var createDigits = function(where, options) { + var counter = 0; + // Iterate each startTime digit, if it is not a digit + // we'll asume that it's a separator + var mFirstPos, sFirstPos; + // reset digits and intervals array. + digits = []; + intervals = []; + + for (var i = 0; i < options.startTime.length; i++) { + if (parseInt(options.startTime[i]) >= 0) { + elem = $('
').css({ + height: options.digitHeight, + float: 'left', + background: 'url(\'' + options.image + '\')', + width: options.digitWidth + }); + + elem.current = parseInt(options.startTime[i]); + digits.push(elem); + + margin(counter, -elem.current * options.digitHeight * options.digitImages); + + if (options.continuous === true) { + digits[counter]._max = function() { return 9; }; + } else { + // Add max digits, for example, first digit of minutes (mm) has + // a max of 5. Conditional max is used when the left digit has reach + // the max. For example second "hours" digit has a conditional max of 4 + switch (options.format[i]) { + case 'h': + digits[counter]._max = function(pos, isStart) { + if (pos % 2 == 0) + return 2; + else + return (isStart) ? 3: 9; + }; + break; + case 'd': + digits[counter]._max = function() { return 9; }; + break; + case 'm': + digits[counter]._max = function(pos) { + if(!mFirstPos) { mFirstPos = pos; } + return pos == mFirstPos ? 9 : 5; + }; + break; + case 's': + digits[counter]._max = function(pos) { + if(!sFirstPos) { sFirstPos = pos; } + return pos == sFirstPos ? 9 : 5; + }; + } } - where.append(elem) + counter += 1; + } else { + elem = $('
').css({float: 'left'}) + .text(options.startTime[i]); } - }; + where.append(elem) + } +}; - // Set or get element margin - var margin = function(elem, val) - { - if (val !== undefined) - { - digits[elem].margin = val; - return digits[elem].css({'backgroundPosition': '0 ' + val + 'px'}); +var makeMovement = function(elem, steps, isForward, options) { + // Stop any other movement over the same digit. + if (intervals[elem]) + window.clearInterval(intervals[elem]); + + // Move to the initial position (We force that because in chrome + // there are some scenarios where digits lost sync) + var initialPos = -(options.digitHeight * options.digitImages * + digits[elem].current); + margin(elem, initialPos); + digits[elem].current = digits[elem].current + ((isForward) ? steps: -steps); + + var x = 0; + intervals[elem] = setInterval(function() { + if (x++ === options.digitImages * steps) { + window.clearInterval(intervals[elem]); + delete intervals[elem]; + return; } - return digits[elem].margin || 0; - }; + var diff = isForward ? -options.digitHeight: options.digitHeight; + margin(elem, initialPos + (x * diff)); + }, options.stepTime / steps); +}; - var makeMovement = function(elem, steps, isForward) - { - // Stop any other movement over the same digit. - if (intervals[elem]) - window.clearInterval(intervals[elem]); +// Set or get element margin +var margin = function(elem, val) { + if (val !== undefined) { + digits[elem].margin = val; + return digits[elem].css({'backgroundPosition': '0 ' + val + 'px'}); + } - // Move to the initial position (We force that because in chrome - // there are some scenarios where digits lost sync) - var initialPos = -(options.digitHeight * options.digitImages * - digits[elem].current); - margin(elem, initialPos); - digits[elem].current = digits[elem].current + ((isForward) ? steps: -steps); - - var x = 0; - intervals[elem] = setInterval(function(){ - if (x++ === options.digitImages * steps) - { - window.clearInterval(intervals[elem]); - delete intervals[elem]; - return; - } + return digits[elem].margin || 0; +}; - var diff = isForward ? -options.digitHeight: options.digitHeight; - margin(elem, initialPos + (x * diff)); - }, options.stepTime / steps); - }; - // Makes the movement. This is done by "digitImages" steps. - var moveDigit = function(elem) - { - if (digits[elem].current == 0) - { - // Is there still time left? - if (elem > 0) - { - var isStart = (digits[elem - 1].current == 0); - - makeMovement(elem, digits[elem]._max(elem, isStart), true); - moveDigit(elem - 1); - } - else // That condition means that we reach the end! 00:00. - { - for (var i = 0; i < digits.length; i++) - { - clearInterval(intervals[i]); - margin(i, 0); - } - options.timerEnd(); +// Makes the movement. This is done by "digitImages" steps. +var moveDigit = function(elem, options) { + if (digits[elem].current == 0) { + // Is there still time left? + if (elem > 0) { + var isStart = (digits[elem - 1].current == 0); + + makeMovement(elem, digits[elem]._max(elem, isStart), true, options); + moveDigit(elem - 1, options); + } else { // That condition means that we reach the end! 00:00. + for (var i = 0; i < digits.length; i++) { + clearInterval(intervals[i]); + clearInterval(intervals.main); + margin(i, 0); } - - return; + options.timerEnd(); } + return; + } + makeMovement(elem, 1, false, options); +}; + + + +// parses a date of the form hh:mm:ss, for example, where +// ... precision is the same as the format. +var parseRelativeDate = function(form, options) { + // give the date the values of now by default + var now = new Date(); + var d = now.getDate(); + var m = now.getMonth() + 1; + var y = now.getFullYear(); + var h = now.getHours(), mm, s; + + // read in components and render based on format + var format = options.format; + var parts = form.split(':'); + if( format.indexOf('dd') == 0 ) { + d = parts[0]; + parts = parts.slice(1); + format = format.substr(3); + } + if( format.indexOf('hh') == 0 ) { + h = parts[0]; + parts = parts.slice(1); + format = format.substr(3); + } + if( format.indexOf('mm') == 0 ) { + mm = parts[0]; + parts = parts.slice(1); + format = format.substr(3); + } + if( format.indexOf('ss') == 0 ) { + s = parts[0]; + parts = parts.slice(1); + format = format.substr(3); + } + // return our constructed date object + return new Date([m, d, y].join('/') + ' ' + [h, mm, s].map(pad).join(':') + ' GMT-0900'); +}; + - makeMovement(elem, 1); +// convert a date object to the format specified +var formatCompute = function(d, options) { + var format = options.format; + var parse = { + d: Math.floor( ( d - new Date( d.getFullYear(), 0, 1 ) ) / ( 1000 * 60 * 60 * 24 ) ), + h: d.getUTCHours(), + m: d.getUTCMinutes(), + s: d.getUTCSeconds() + }; + return format.replace(/(dd|hh|mm|ss)/g, function($0, form) { + return pad(parse[form[0]]); + }); +}; + +// add leading zeros +var pad = function(x){return (1e15+""+x).slice(-2)}; + +var start = function (element) { + if (element.attr('started') != 'true') { + element.attr('started', true) + intervals.main = setInterval(function () { + moveDigit(digits.length - 1, element.data('options')); + }, + 1000); + } +}; + +var digits = []; +var intervals = []; +jQuery.fn.countdown = function(userOptions) { + // Default options + var options = { + stepTime: 60, + // startTime and format MUST follow the same format. + // also you cannot specify a format unordered (e.g. hh:ss:mm is wrong) + format: "dd:hh:mm:ss", + startTime: "01:12:32:55", + digitImages: 6, + digitWidth: 67, + digitHeight: 90, + timerEnd: function(){}, + image: "digits.png", + continuous: false, + start: true }; + $.extend(options, userOptions); + // if an endTime is provided... + if( userOptions.endTime ) { + // calculate the difference between endTime and present time + var endDate = userOptions.endTime instanceof Date ? userOptions.endTime : parseRelativeDate(userOptions.endTime, options); + var diff = endDate.getTime() - (new Date()).getTime(); + // and set that as the startTime + userOptions.startTime = formatCompute(new Date(diff), options); + delete userOptions.endTime; + } $.extend(options, userOptions); - createDigits(this); - intervals.main = setInterval(function(){ moveDigit(digits.length - 1); }, - 1000); + if (this.length) { + clearInterval(intervals.main); + createDigits(this, options); + this.data('options', options); + if (options.start === true) { + start(this); + } + } +}; + +// start the counter +jQuery.fn.start = function () { + start(this); }; diff --git a/js/jquery.countdown.min.js b/js/jquery.countdown.min.js index 928f7de..644ced6 100644 --- a/js/jquery.countdown.min.js +++ b/js/jquery.countdown.min.js @@ -1,8 +1 @@ -/* - * jquery-counter plugin - * - * Copyright (c) 2009 Martin Conte Mac Donell - * Dual licensed under the MIT and GPL licenses. - * http://docs.jquery.com/License - */ -jQuery.fn.countdown=function(a){var b={stepTime:60,format:"dd:hh:mm:ss",startTime:"01:12:32:55",digitImages:6,digitWidth:67,digitHeight:90,timerEnd:function(){},image:"digits.png"};var c=[],d=[];var e=function(a){var d=0;for(var e=0;e=0){elem=$('
').css({height:b.digitHeight,"float":"left",background:"url('"+b.image+"')",width:b.digitWidth});elem.current=parseInt(b.startTime[e]);c.push(elem);f(d,-elem.current*b.digitHeight*b.digitImages);switch(b.format[e]){case"h":c[d]._max=function(a,b){if(a%2==0)return 2;else return b?3:9};break;case"d":c[d]._max=function(){return 9};break;case"m":case"s":c[d]._max=function(a){return a%2==0?5:9}}++d}else{elem=$('
').css({"float":"left"}).text(b.startTime[e])}a.append(elem)}};var f=function(a,b){if(b!==undefined){c[a].margin=b;return c[a].css({backgroundPosition:"0 "+b+"px"})}return c[a].margin||0};var g=function(a,e,g){if(d[a])window.clearInterval(d[a]);var h=-(b.digitHeight*b.digitImages*c[a].current);f(a,h);c[a].current=c[a].current+(g?e:-e);var i=0;d[a]=setInterval(function(){if(i++===b.digitImages*e){window.clearInterval(d[a]);delete d[a];return}var c=g?-b.digitHeight:b.digitHeight;f(a,h+i*c)},b.stepTime/e)};var h=function(a){if(c[a].current==0){if(a>0){var e=c[a-1].current==0;g(a,c[a]._max(a,e),true);h(a-1)}else{for(var i=0;i=0){if(elem=$('
').css({height:e.digitHeight,"float":"left",background:"url('"+e.image+"')",width:e.digitWidth}),elem.current=parseInt(e.startTime[a]),digits.push(elem),margin(r,-elem.current*e.digitHeight*e.digitImages),e.continuous===!0)digits[r]._max=function(){return 9};else switch(e.format[a]){case"h":digits[r]._max=function(t,e){return t%2==0?2:e?3:9};break;case"d":digits[r]._max=function(){return 9};break;case"m":digits[r]._max=function(t){return i||(i=t),t==i?9:5};break;case"s":digits[r]._max=function(t){return n||(n=t),t==n?9:5}}r+=1}else elem=$('
').css({"float":"left"}).text(e.startTime[a]);t.append(elem)}},makeMovement=function(t,e,i,n){intervals[t]&&window.clearInterval(intervals[t]);var r=-(n.digitHeight*n.digitImages*digits[t].current);margin(t,r),digits[t].current=digits[t].current+(i?e:-e);var a=0;intervals[t]=setInterval(function(){if(a++===n.digitImages*e)return window.clearInterval(intervals[t]),void delete intervals[t];var s=i?-n.digitHeight:n.digitHeight;margin(t,r+a*s)},n.stepTime/e)},margin=function(t,e){return void 0!==e?(digits[t].margin=e,digits[t].css({backgroundPosition:"0 "+e+"px"})):digits[t].margin||0},moveDigit=function(t,e){if(0!=digits[t].current)makeMovement(t,1,!1,e);else if(t>0){var i=0==digits[t-1].current;makeMovement(t,digits[t]._max(t,i),!0,e),moveDigit(t-1,e)}else{for(var n=0;n