Skip to content

Instantly share code, notes, and snippets.

@dcneiner
Created May 17, 2010 13:27
Show Gist options
  • Save dcneiner/403754 to your computer and use it in GitHub Desktop.
Save dcneiner/403754 to your computer and use it in GitHub Desktop.
(function($){
$.widget('pixel.countdown', {
options: {
milestones: [
{ label: "one_minute", minute: 1 },
{ label: "five_minutes", minute: 5, second: 30 }
],
interval: 1, // Interval in seconds to refresh the widget
formatter: function(min, sec){
if(min === 0 && sec === 0){
return "now.";
} else {
var second_label = sec + (sec == 1 ? " second." : " seconds.");
if( min === 0){
return "in " + second_label;
} else {
return "in " + min + (min == 1 ? " minute" : " minutes") + " and " + second_label;
}
}
}
},
_create: function(){
var dateTime = this.element.attr('datetime') || this.element.text(),
hour,
min,
setup = false;
dateTime = dateTime.split('T');
if( dateTime.length === 2 ){
// Create a new date based on the year, month, day
this._endTime = new Date(dateTime[0]);
// Get hours and minutes
dateTime = dateTime[1].split(':');
hour = parseInt( dateTime[0], 10);
min = parseInt( dateTime[1], 10);
if( !isNaN(hour) && !isNaN(min) ){
this._endTime.setUTCHours(hour, min);
setup = true;
}
}
// In this case we fail silently, and remove the widget
// We could throw an error instead if wanted.
if (!setup){
this.destroy(); // Undo everything we did
return false;
}
this._originalText = this.element.text();
this.milestones = this._cleanMilestones();
this.widgetEventPrefix = "countdown_";
this._timer = window.setInterval($.proxy(this.update, this), this.options.interval * 1000);
this.update(); // call it initially to avoid 1 second delay
},
timeLeft: function(){
var left = this._endTime - (new Date()).getTime(),
min = left / 1000 / 60,
sec = min % 1 * 60;
min = parseInt(min, 10);
sec = Math.round(sec);
return left <= 0 ? [0, 0] : [min, sec];
},
update: function(){
var timeLeft = this.timeLeft(),
vTL = timeLeft[0] + (timeLeft[1] / 100),
label;
if(this.milestones.length && this.milestones[0].time > vTL){
label = this.milestones.shift().label;
this._trigger('milestone_reached', null, label);
}
if(vTL === 0){
window.clearInterval(this._timer);
this._trigger('complete');
}
this.element.text(
this.options.formatter.apply(this.element, timeLeft)
);
},
_cleanMilestones: function(){
var timeLeft = this.timeLeft(),
vTL = timeLeft[0] + (timeLeft[1] / 100),
milestones = $.map(this.options.milestones, function(ms){
// Create a new literal that contains only the label
// and the min/sec as a single value
return { label: ms.label, time: ms.minute + ((ms.second || 0) / 100) };
}),
size = milestones.length;
// Sort largest to smallest
milestones.sort(function(a,b){
if (a.time === b.time) return 0;
else return a.time > b.time ? -1 : 1;
});
// Remove expired milestones
while(milestones.length && milestones[0].time > vTL){
milestones.shift();
}
return milestones;
},
_setOption: function(key, value){
if (this.options[key] === value) {
return; // Do nothing, the value didn't change
}
switch (key){
case "interval":
// Store the new value
this.options.interval = value;
// Clear the old timer
window.clearInterval(this._timer);
// Set a new timer based on the new interval
this._timer = window.setInterval(
$.proxy(this.update, this),
this.options.interval * 1000
);
break;
case "formatter":
this.options.formatter = value;
this.update();
break;
case "milestones":
this.options.milestones = value;
this.milestones = this._cleanMilestones();
break;
default:
this.options[key] = value;
}
return this;
},
destroy: function(){
window.clearInterval( this._timer );
this.element.text( this._originalText);
// Call the parent destroy method
$.Widget.prototype.destroy.call(this);
}
});
}(jQuery));
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment