(function($) {
/**
* @class flipTimer
* @constructor
*
* @param element {HTMLElement} the element flipTimer is called on
*/
var flipTimer = function(element, options) {
this.element = element;
// ensures the HTMLElement has a class of 'flipTimer'
if (!this.element.hasClass('flipTimer')) {
this.element.addClass('flipTimer');
}
// attach users options to instance
this.userOptions = options;
// attach default options to instance
this.defaultOptions = flipTimer.defaults;
// merge default options with user options and attach to instance
this.options = $.extend({}, this.defaultOptions, this.userOptions);
// detects if the seconds digits should be used
if (this.element.find('.seconds').length > 0) {
this.options.seconds = this.element.find('.seconds')[0];
}
// detects if the minutes digits should be used
if (this.element.find('.minutes').length > 0) {
this.options.minutes = this.element.find('.minutes')[0];
}
// detects if the hours digits should be used
if (this.element.find('.hours').length > 0) {
this.options.hours = this.element.find('.hours')[0];
}
// detects if the days digits should be used
if (this.element.find('.days').length > 0) {
this.options.days = this.element.find('.days')[0];
}
// store the date/time when initialised
this.initDate = new Date();
// make the date into a javascript date
this.options.date = new Date(this.options.date);
// untested
this.calculateDate();
};
flipTimer.defaults = {
seconds: false,
minutes: false,
hours: false,
days: false,
date: new Date().toDateString(),
direction: 'up',
callback: null,
start_timer: false,
digitTemplate: '' +
'
' +
'
' +
' ' +
'
' +
'
' +
'
' +
' ' +
'
' +
'
' +
'
'
};
flipTimer.prototype = {
/**
* Calculates the difference in date for the timer
*
* @method calculateDate
*/
calculateDate: function() {
var dateDiff;
// calculates the difference in dates
if (this.options.direction == 'down') {
dateDiff = this.options.date - this.initDate;
} else if (this.options.direction == 'up') {
dateDiff = this.initDate - this.options.date;
}
// sets the date/time on the instance
this.seconds = Math.floor(dateDiff/1000) % 60;
this.minutes = Math.floor(dateDiff/1000/60) % 60;
this.hours = Math.floor(dateDiff/1000/3600) % 24;
this.days = Math.floor(dateDiff/1000/60/60/24);
// render the html for the plugin
this.render();
},
/**
* Dictates what needs rendering for the plugin
*
* @method render
*/
render: function() {
// if using seconds, populate it
if (this.options.seconds) {
this.renderDigits(this.options.seconds, this.seconds);
}
// if using minutes, populate it
if (this.options.minutes) {
this.renderDigits(this.options.minutes, this.minutes);
}
// if using hours, populate it
if (this.options.hours) {
this.renderDigits(this.options.hours, this.hours);
}
// if using days, populate it
if (this.options.days) {
this.renderDigits(this.options.days, this.days);
}
if (this.options.start_timer)
{
this.startTimer();
}
},
/**
* Renders the digits for a given subject
*
* @method renderDigits
* @param subject {HTMLElement} the element to generate digits for
*/
renderDigits: function(subject, value) {
var i, x, max, maxDigit, currentDigit, _this = this, number_array;
// if digits are not already rendered...
if ($(subject).find('.digit').length == 0) {
// split the value into two individual digits
// unless time has ran out
if (_this.days < 0 && _this.hours < 0 && _this.minutes < 0 && _this.seconds < 0) {
number_array = [0,0];
}
else {
number_array = String(value).split(""); // split all digits
// ensure the set is at least 2 digits long
if (number_array.length < 2) {
number_array.unshift(0)
}
}
// set maximum digits for seconds/minutes/hours
if (subject == _this.options.seconds || subject == _this.options.minutes) {
// minutes and seconds max digit
maxDigit = 5;
} else if (subject == _this.options.hours) {
// hours max digit
maxDigit = 2;
} else {
// everything else digit max
maxDigit = 9;
}
// append a div for each digit
number_array.forEach(function() {
$(subject).append('');
});
// for each digit-set in the subject
$(subject).find('.digit-set').each(function(el) {
// if first digit, then use digit max
max = (el == 0) ? maxDigit : 9;
// generate the right number of digits
for(i=0; i<=max; i++) {
// append the digit template
$(this).append(_this.options.digitTemplate);
// if direction is down then make numbers decline
x = (_this.options.direction == 'down') ? max - i : i;
// select the current digit and apply the number to it
currentDigit = $(this).find('.digit')[i];
$(currentDigit).find('.digit-wrap').append(x);
// if the current number matches the value then apply active class
if (x == number_array[el]) {
$(currentDigit).addClass('active');
} else if (number_array[el] != 0 && ((x + 1) == number_array[el])) {
// if the current number is one less than active but not zero
$(currentDigit).addClass('previous');
} else if (number_array[el] == 0 && x == max) {
// if the current number is zero then apply previous to max
$(currentDigit).addClass('previous');
}
}
});
}
},
/**
* Start a timer with an interval of 1 second
*
* @method startTimer
*/
startTimer: function() {
var _this = this;
clearInterval(this.timer);
this.timer = setInterval(function() {
// if timer runs out stop the timer
if (_this.days <= 0 && _this.hours <= 0 && _this.minutes <= 0 && _this.seconds <= 0) {
// execute callback if one exists
if (_this.options.callback) {
_this.options.callback();
}
clearInterval(_this.timer);
return;
}
// if timer runs out stop the timer
if ((_this.days > 999) || (_this.days == 999 && _this.hours == 23 && _this.minutes == 59 && _this.seconds == 59)) {
clearInterval(_this.timer);
return;
}
// increase/decrease seconds
(_this.options.direction == 'down') ? _this.seconds-- : _this.seconds++;
if (_this.options.seconds) _this.increaseDigit(_this.options.seconds);
// increase/decrease minutes
if (_this.seconds == 60 || _this.seconds == -1) {
if (_this.options.direction == 'down') {
_this.seconds = 59;
_this.minutes--;
} else {
_this.seconds = 0;
_this.minutes++;
}
if (_this.options.minutes) _this.increaseDigit(_this.options.minutes);
}
// increase/decrease hours
if (_this.minutes == 60 || _this.minutes == -1) {
if (_this.options.direction == 'down') {
_this.minutes = 59;
_this.hours--;
} else {
_this.minutes = 0;
_this.hours++;
}
if (_this.options.hours) _this.increaseDigit(_this.options.hours);
}
// increase/decrease days
if (_this.hours == 24 || _this.hours == -1) {
if (_this.options.direction == 'down') {
_this.hours = 23;
_this.days--;
} else {
_this.hours = 0;
_this.days++;
}
if (_this.options.days) _this.increaseDigit(_this.options.days);
}
},1000);
},
/**
* Changes classes on the digits to increase the number
*
* @method increaseDigit
* @param target {HTMLElement} the element to increase digit for
*/
increaseDigit: function(target) {
var digitSets = new Array(), _this = this;
// find all digit-sets related to digit type
$(target).find('.digit-set').each(function() {
digitSets.push(this);
});
// increase individual digit
increase(digitSets[digitSets.length - 1]);
/**
* Increases individual digit in a digit-set
*
* @param el {HTMLElement} the digit-set being increased
*/
function increase(el) {
var current = $(el).find('.active'),
previous = $(el).find('.previous'),
index = $.inArray(el, digitSets);
previous.removeClass('previous');
current.removeClass('active').addClass('previous');
if (current.next().length == 0) {
if (_this.options.direction == 'down'
&& target == _this.options.hours
&& (_this.hours == -1 || _this.hours == 23)
&& $(el).find('.digit').length == 10) {
// if the hours digit reaches 0 it should make 24 active
$($(el).find('.digit')[6]).addClass('active');
} else {
// increase to first digit in set
$(el).find('.digit:first-child').addClass('active');
}
if (index != 0) {
// increase digit of sibling digit-set
increase(digitSets[index - 1]);
}
} else {
if (_this.options.direction == "up"
&& target == _this.options.hours
&& _this.hours == 24) {
// if the hours digit reaches 24 it should make 0 active
$(el).find('.digit:first-child').addClass('active');
increase(digitSets[index - 1]);
} else {
// increase the next digit
current.next().addClass('active');
}
}
}
}
};
$.fn.flipTimer = function(options) {
return this.each(function() {
if (!$(this).data('flipTimer')) {
$(this).data('flipTimer', new flipTimer($(this), options));
}
});
};
})(jQuery);