metro4
Version:
The front-end framework for Build responsive, mobile-first projects on the web with the first front-end component library in Metro Style
498 lines (405 loc) • 14.7 kB
JavaScript
var Countdown = {
init: function( options, elem ) {
this.options = $.extend( {}, this.options, options );
this.elem = elem;
this.element = $(elem);
this.breakpoint = (new Date()).getTime();
this.blinkInterval = null;
this.tickInterval = null;
this.zeroDaysFired = false;
this.zeroHoursFired = false;
this.zeroMinutesFired = false;
this.zeroSecondsFired = false;
this.current = {
d: 0, h: 0, m: 0, s: 0
};
this.locale = null;
this.inactiveTab = false;
this._setOptionsFromDOM();
this._create();
return this;
},
options: {
stopOnBlur: true,
animate: "none",
animationFunc: "swing",
inputFormat: null,
locale: METRO_LOCALE,
days: 0,
hours: 0,
minutes: 0,
seconds: 0,
date: null,
start: true,
clsCountdown: "",
clsPart: "",
clsZero: "",
clsAlarm: "",
clsDays: "",
clsHours: "",
clsMinutes: "",
clsSeconds: "",
onAlarm: Metro.noop,
onTick: Metro.noop,
onZero: Metro.noop,
onBlink: Metro.noop,
onCountdownCreate: Metro.noop
},
_setOptionsFromDOM: function(){
var element = this.element, o = this.options;
$.each(element.data(), function(key, value){
if (key in o) {
try {
o[key] = JSON.parse(value);
} catch (e) {
o[key] = value;
}
}
});
},
_create: function(){
var o = this.options;
this.locale = Metro.locales[o.locale] !== undefined ? Metro.locales[o.locale] : Metro.locales["en-US"];
this._build();
this._createEvents();
},
_setBreakpoint: function(){
var o = this.options;
var dm = 86400000, hm = 3600000, mm = 60000, sm = 1000;
this.breakpoint = (new Date()).getTime();
if (Utils.isValue(o.date) && Utils.isDate(o.date, o.inputFormat)) {
this.breakpoint = Utils.isValue(o.inputFormat) ? (o.date.toDate(o.inputFormat)).getTime() : (new Date(o.date)).getTime();
}
if (parseInt(o.days) > 0) {
this.breakpoint += parseInt(o.days) * dm;
}
if (parseInt(o.hours) > 0) {
this.breakpoint += parseInt(o.hours) * hm;
}
if (parseInt(o.minutes) > 0) {
this.breakpoint += parseInt(o.minutes) * mm;
}
if (parseInt(o.seconds) > 0) {
this.breakpoint += parseInt(o.seconds) * sm;
}
},
_build: function(){
var that = this, element = this.element, o = this.options;
var parts = ["days", "hours", "minutes", "seconds"];
var dm = 24*60*60*1000;
var delta_days;
var now = (new Date()).getTime();
var digit;
if (!Utils.isValue(element.attr("id"))) {
element.attr("id", Utils.elementId("countdown"));
}
element.addClass("countdown").addClass(o.clsCountdown);
this._setBreakpoint();
delta_days = Math.round((that.breakpoint - now) / dm);
$.each(parts, function(){
var part = $("<div>").addClass("part " + this).addClass(o.clsPart).attr("data-label", that.locale["calendar"]["time"][this]).appendTo(element);
if (this === "days") {part.addClass(o.clsDays);}
if (this === "hours") {part.addClass(o.clsHours);}
if (this === "minutes") {part.addClass(o.clsMinutes);}
if (this === "seconds") {part.addClass(o.clsSeconds);}
$("<div>").addClass("digit").appendTo(part);
$("<div>").addClass("digit").appendTo(part);
if (this === "days" && delta_days >= 100) {
for(var i = 0; i < String(Math.round(delta_days/100)).length; i++) {
$("<div>").addClass("digit").appendTo(part);
}
}
});
digit = element.find(".digit");
digit.append($("<span class='digit-placeholder'>").html("0"));
digit.append($("<span class='digit-value'>").html("0"));
Utils.exec(o.onCountdownCreate, [element], element[0]);
if (o.start === true) {
this.start();
} else {
this.tick();
}
},
_createEvents: function(){
var that = this, element = this.element, o = this.options;
// if (o.stopOnBlur === true) {
$(window).on(Metro.events.blur+"-"+element.attr("id"), function(){
// that.pause();
that.inactiveTab = true;
});
$(window).on(Metro.events.focus+"-"+element.attr("id"), function(){
// that.resume();
that.inactiveTab = false;
});
// }
},
blink: function(){
var element = this.element, o = this.options;
element.toggleClass("blink");
Utils.exec(o.onBlink, [this.current], element[0]);
},
tick: function(){
var element = this.element, o = this.options;
var dm = 24*60*60, hm = 60*60, mm = 60, sm = 1;
var left, now = (new Date()).getTime();
var d, h, m, s;
var days = element.find(".days"),
hours = element.find(".hours"),
minutes = element.find(".minutes"),
seconds = element.find(".seconds");
left = Math.floor((this.breakpoint - now)/1000);
if (left <= -1) {
this.stop();
element.addClass(o.clsAlarm);
Utils.exec(o.onAlarm, [now], element[0]);
return ;
}
d = Math.floor(left / dm);
left -= d * dm;
if (this.current.d !== d) {
this.current.d = d;
this.draw("days", d);
}
if (d === 0) {
if (this.zeroDaysFired === false) {
this.zeroDaysFired = true;
days.addClass(o.clsZero);
Utils.exec(o.onZero, ["days", days], element[0]);
}
}
h = Math.floor(left / hm);
left -= h*hm;
if (this.current.h !== h) {
this.current.h = h;
this.draw("hours", h);
}
if (d === 0 && h === 0) {
if (this.zeroHoursFired === false) {
this.zeroHoursFired = true;
hours.addClass(o.clsZero);
Utils.exec(o.onZero, ["hours", hours], element[0]);
}
}
m = Math.floor(left / mm);
left -= m*mm;
if (this.current.m !== m) {
this.current.m = m;
this.draw("minutes", m);
}
if (d === 0 && h === 0 && m === 0) {
if (this.zeroMinutesFired === false) {
this.zeroMinutesFired = true;
minutes.addClass(o.clsZero);
Utils.exec(o.onZero, ["minutes", minutes], element[0]);
}
}
s = Math.floor(left / sm);
if (this.current.s !== s) {
this.current.s = s;
this.draw("seconds", s);
}
if (d === 0 && h === 0 && m === 0 && s === 0) {
if (this.zeroSecondsFired === false) {
this.zeroSecondsFired = true;
seconds.addClass(o.clsZero);
Utils.exec(o.onZero, ["seconds", seconds], element[0]);
}
}
Utils.exec(o.onTick, [{days:d, hours:h, minutes:m, seconds:s}], element[0]);
},
draw: function(part, value){
var that = this, element = this.element, o = this.options;
var digits, digits_length, digit_value, digit_current, digit, digit_copy;
var len, i, duration = 900, height;
var removeOldDigit = function(_digit){
if (!document.hidden) {
setTimeout(function(){
_digit.remove();
}, 500);
} else {
_digit.remove();
}
};
var slideDigit = function(digit){
var digit_copy, height = digit.height();
digit_copy = digit.clone().appendTo(digit.parent());
digit_copy.css({
top: -1 * height,
opacity: .5
});
digit.addClass("-old-digit").animate({
top: height,
opacity: 0
}, duration, o.animationFunc, removeOldDigit(digit));
digit_copy.html(digit_value).animate({
top: 0,
opacity: 1
}, duration, o.animationFunc);
};
var fadeDigit = function(digit){
var digit_copy;
digit_copy = digit.clone().appendTo(digit.parent());
digit_copy.css({
opacity: 0
});
digit.addClass("-old-digit").animate({
opacity: 0
}, duration, o.animationFunc, removeOldDigit(digit));
digit_copy.html(digit_value).animate({
opacity: 1
}, duration, o.animationFunc);
};
var zoomDigit = function(digit){
var digit_copy,
height = digit.height(),
width = digit.width(),
fs = parseInt(Utils.getStyleOne(digit, "font-size"));
digit_copy = digit.clone().appendTo(digit.parent());
digit_copy.css({
opacity: 0,
fontSize: 0,
top: height/2,
left: width/2
});
digit.addClass("-old-digit").animate({
opacity: 0,
fontSize: 0,
top: height,
left: width/2
}, duration, o.animationFunc, removeOldDigit(digit));
digit_copy.html(digit_value).animate({
opacity: 1,
fontSize: fs,
top: 0,
left: 0
}, duration, o.animationFunc);
};
value = String(value);
if (value.length === 1) {
value = '0'+value;
}
len = value.length;
digits = element.find("."+part+" .digit");
digits_length = digits.length;
for(i = 0; i < len; i++){
digit = element.find("." + part + " .digit:eq("+ (digits_length - 1) +") .digit-value");
digit_value = Math.floor( value / Math.pow(10, i) ) % 10;
digit_current = parseInt(digit.text());
if (digit_current === digit_value) {
continue;
}
switch (String(o.animate).toLowerCase()) {
case "slide": slideDigit(digit); break;
case "fade": fadeDigit(digit); break;
case "zoom": zoomDigit(digit); break;
default: digit.html(digit_value);
}
digits_length--;
}
},
start: function(){
var that = this, element = this.element;
if (element.data("paused") === false) {
return;
}
clearInterval(this.blinkInterval);
clearInterval(this.tickInterval);
element.data("paused", false);
this._setBreakpoint();
this.tick();
this.blinkInterval = setInterval(function(){that.blink();}, 500);
this.tickInterval = setInterval(function(){that.tick();}, 1000);
},
stop: function(){
var element = this.element;
clearInterval(this.blinkInterval);
clearInterval(this.tickInterval);
element.data("paused", true);
element.find(".digit").html("0");
this.current = {
d: 0, h:0, m: 0, s:0
};
},
pause: function(){
clearInterval(this.blinkInterval);
clearInterval(this.tickInterval);
this.element.data("paused", true);
},
resume: function(){
var that = this;
this.element.data("paused", false);
this.blinkInterval = setInterval(function(){that.blink();}, 500);
this.tickInterval = setInterval(function(){that.tick();}, 1000);
},
reset: function(){
var that = this, element = this.element, o = this.options;
clearInterval(this.blinkInterval);
clearInterval(this.tickInterval);
element.find(".part").removeClass(o.clsZero);
element.find(".digit").html("0");
this._setBreakpoint();
element.data("paused", false);
this.tick();
this.blinkInterval = setInterval(function(){that.blink();}, 500);
this.tickInterval = setInterval(function(){that.tick();}, 1000);
},
togglePlay: function(){
if (this.element.attr("data-pause") === true) {
this.pause();
} else {
this.start();
}
},
isPaused: function(){
return this.element.data("paused");
},
getBreakpoint: function(asDate){
return asDate === true ? new Date(this.breakpoint) : this.breakpoint;
},
getLeft: function(){
var dm = 24*60*60*1000, hm = 60*60*1000, mm = 60*1000, sm = 1000;
var now = (new Date()).getTime();
var left_seconds = Math.floor(this.breakpoint - now);
return {
days: Math.round(left_seconds / dm),
hours: Math.round(left_seconds / hm),
minutes: Math.round(left_seconds / mm),
seconds: Math.round(left_seconds / sm)
};
},
i18n: function(val){
var that = this, element = this.element, o = this.options;
var parts = ["days", "hours", "minutes", "seconds"];
if (val === undefined) {
return o.locale;
}
if (Metro.locales[val] === undefined) {
return false;
}
o.locale = val;
this.locale = Metro.locales[o.locale];
$.each(parts, function(){
var cls = ".part." + this;
var part = element.find(cls);
part.attr("data-label", that.locale["calendar"]["time"][this]);
});
},
changeAttrLocale: function(){
var element = this.element;
var locale = element.attr('data-locale');
this.i18n(locale);
},
changeAttribute: function(attributeName){
switch (attributeName) {
case "data-pause": this.togglePlay(); break;
case "data-locale": this.changeAttrLocale(); break;
}
},
destroy: function(){
clearInterval(this.blinkInterval);
clearInterval(this.tickInterval);
this.element.html("");
this.element.removeClass("countdown").removeClass(this.options.clsCountdown);
}
};
Metro.plugin('countdown', Countdown);