<div data-timer="12/25/2018 04:22:44" data-timer-finish=".first"></div>
<div data-timer="10/11/2018 14:39:35" data-timer-finish=".second"></div>
<div class="first">
Первый блок таймера
</div>
<div class="second">
Второй блок таймера
</div>
(function($) {
'use strict';
var plugin = {
name: 'timer',
version: '1.0.1',
language: 'RU',
data: {
setting: 'setting',
general: 'general'
},
template: {
prefix: 'v-'
}
};
var defaults = {
date: Date.now() + 922e5,
double: true,
language: 'RU',
store: function() {
return {};
},
template: function(time, store) {
var seconds = '<span>'+ time.seconds.value + ' ' + time.seconds.text +'</span>';
var minutes = '<span>'+ time.minutes.value + ' ' + time.minutes.text +'</span>';
var hours = '<span>'+ time.hours.value + ' ' + time.hours.text +'</span>';
var days = '<span>'+ time.days.value + ' ' + time.days.text +'</span>';
return '<div>'+ days+ ' : ' +hours+ ' : ' +minutes+ ' : ' +seconds +'</div>';
},
finish: function(store) {
$(this).css('color', 'red');
}
};
var methods = {}, user = {};
methods.init = function($this, options, common) {
var setting = function() {
var params = $.extend(true, {}, defaults, options);
params.date = new Date(params.date).getTime();
params.language = params.language.toUpperCase();
$this.data(plugin.data.setting, params);
return params;
}();
var general = function() {
var params = $.extend(true, {}, {
date: Date.now(),
context: $this.get(0),
store: setting.store.call($this.get(0)),
interval: 0,
language: (function() {
var lang = (function() {
switch (setting.language) {
case 'RU': return {
declen: {
days: ['дней', 'день', 'дня'],
hours: ['часов', 'час', 'часа'],
minutes: ['минут', 'минута', 'минуты'],
seconds: ['секунд', 'секунда', 'секунды']
}
};
case 'EN': return {
declen: {
days: ['day', 'days'],
hours: ['hour', 'hours'],
minutes: ['minute', 'minutes'],
seconds: ['second', 'seconds']
}
};
}
}());
return common.language ? $.extend(true, {}, lang, common.language) : lang;
}())
}, common);
$this.data(plugin.data.general, params);
return params;
}();
initTimer();
general.interval = setInterval(initTimer, 1e3);
function initTimer() {
general.date = Date.now();
var remaining = setting.date - general.date;
general.time = makeTimeObject(remaining);
if (remaining <= 0) {
var html = setting.finish.call(general.context, general.store);
if (html) $this.html(templateEngine(html, general.time, general.store));
return clearInterval(general.interval);
}
var html = setting.template.call(general.context, general.time, general.store);
if (html) $this.html(templateEngine(html, general.time, general.store));
}
function makeTimeObject(remaining) {
var time = getDeclen(getDate(remaining));
if (setting.double) {
for (var item in time) {
time[item].value = doubleDigits(time[item].value);
}
}
return time;
}
function getDeclen(time) {
for (var item in time) {
time[item].text = getDeclenWord(general.language.declen[item], time[item].value, setting.language);
}
return time;
}
function getDate(time) {
var date = {};
date.seconds = {
value: Math.floor(time / 1000)
};
date.minutes = {
value: Math.floor(date.seconds.value / 60)
};
date.hours = {
value: Math.floor(date.minutes.value / 60)
};
date.days = {
value: Math.floor(date.hours.value / 24)
};
date.hours.value %= 24;
date.minutes.value %= 60;
date.seconds.value %= 60;
return date;
}
};
methods.set = function(key, value) {
var setting = $(this).data(plugin.data.setting);
switch (typeof key) {
case 'string':
if (key.includes('.')) {
nestedObjectPath(setting, key, value);
} else {
setting[key] = value;
}
break;
case 'object':
setting = $.extend(true, {}, setting, key);
break;
}
clearInterval($(this).data(plugin.data.general).interval);
return methods.init($(this), setting, {
type: 'update',
method: 'set'
});
};
methods.store = function(params) {
var setting = $(this).data(plugin.data.setting);
var store = setting.store.call(this);
setting.store = function() {
return $.extend(true, {}, store, params);
};
clearInterval($(this).data(plugin.data.general).interval);
return methods.init($(this), setting, {
type: 'update',
method: 'store'
});
};
methods.get = function(category, key) {
var section = $(this).data(category);
if (key) {
if (key.includes('.')) {
return nestedObjectPath(section, key);
} else {
return Object.getOwnPropertyDescriptor(section, key).value;
}
}
return section;
};
methods.simple = function(date, template, options) {
var setting = function() {
var params = $(this).data(plugin.data.setting);
var props = function() {
if (!options) return {};
return typeof options === 'function' ? {
finish: options
} : options;
}();
return $.extend(true, {}, params, props, {
date: date,
template: template
});
}.bind(this)();
return methods.init($(this), setting, {
type: 'init',
method: 'simple'
});
};
methods.destroy = function() {
$(this).removeData(plugin.data.setting).fadeOut(200, function() {
clearInterval($(this).data(plugin.data.general).interval);
$(this).empty().show();
});
};
methods.version = function() {
return plugin.version;
};
function nestedObjectPath(object, key, value) {
var path = key.split('.');
var get = function(path, object) {
return path.reduce(function(previous, current) {
return previous[current];
}, object);
};
if (value) {
var way = path.pop();
get(path, object)[way] = value;
return object;
} else {
return get(path, object);
}
}
function getDeclenWord(variants, value, lang) {
if (!lang) lang = plugin.language;
switch (lang.toUpperCase()) {
case 'EN':
return decEn(variants, value);
case 'RU':
return decRu(variants, value);
}
}
function parseHTML(raw) {
var page = document.implementation.createHTMLDocument();
page.body.innerHTML = raw;
return page.body;
}
function decRu(variants, s) {
var index = s % 100;
if (index >= 11 && index <= 14) {
index = 0;
} else {
index = (index %= 10) < 5 ? (index > 2 ? 2 : index): 0;
}
return variants[index];
}
function decEn(variants, value) {
return variants[value !== 1 ? 1 : 0];
}
function doubleDigits(value) {
return value < 10 ? '0' + value : String(value);
}
function templateEngine(html, time, store) {
if (!html) return '';
var $html = $(parseHTML(html));
var prefix = plugin.template.prefix;
var template = {
show: prefix + 'show',
if: prefix + 'if',
else: prefix + 'else',
hide: prefix + 'hide'
};
if ($('['+ template.show +']', $html).length) {
$.each($('['+ template.show +']', $html), function() {
var key = $(this).attr(template.show);
if (key.includes('.')) {
if (!nestedObjectPath(store, key)) {
$(this).remove();
}
} else {
if (!store[key]) {
$(this).remove();
}
}
$(this).removeAttr(template.show);
});
}
if ($('['+ template.if +']', $html).length) {
$.each($('['+ template.if +']', $html), function() {
var value = $(this).attr(template.if);
var $else = $(this).next('['+ template.else +']');
if (['false', 'null', 'undefined', ''].includes(value)) {
$else.removeAttr(template.else);
$(this).remove();
} else {
if (value.includes('.')) {
var nested = nestedObjectPath(store, value);
if (nested !== undefined) {
if (nested) {
$(this).removeAttr(template.if);
$else.remove();
} else {
$else.removeAttr(template.else);
$(this).remove();
}
} else {
$(this).removeAttr(template.if);
$else.remove();
}
} else {
if (store[value] !== undefined) {
if (store[value]) {
$(this).removeAttr(template.if);
$else.remove();
} else {
$else.removeAttr(template.else);
$(this).remove();
}
} else {
$(this).removeAttr(template.if);
$else.remove();
}
}
}
});
}
if ($('['+ template.hide +']', $html).length) {
$.each($('['+ template.hide +']', $html), function() {
$(this).hide().removeAttr(template.hide);
});
}
return $html.children().length === 0 ? $html.html() : $html.children().wrap();
}
user[plugin.name] = function(method) {
var params = Array.prototype.slice.call(arguments, 1);
switch (method.toLowerCase()) {
case 'declen':
return getDeclenWord.apply(null, params);
case 'version':
return methods.version();
}
};
$.extend(user);
$.fn[plugin.name] = function(setting) {
if (typeof setting === 'object' || !setting) {
return $.each(this, function() {
methods.init($(this), setting, {
type: 'init',
method: 'init'
});
});
} else if (typeof setting === 'string') {
var params = Array.prototype.slice.call(arguments, 1);
var context = this.get(0);
switch(setting.toLowerCase()) {
case 'set':
return methods.set.apply(context, params);
case 'store':
return methods.store.apply(context, params);
case 'get':
return methods.get.apply(context, params);
case 'destroy':
return methods.destroy.call(context);
case 'version':
return methods.version();
}
} else if (typeof arguments[1] === 'function') {
return methods.simple.apply(this, arguments);
}
};
})(jQuery);
// --------------------
$.each($('[data-timer]'), function() {
$(this).timer({
date: $(this).attr('data-timer'),
language: 'RU',
template: function(time) {
return `
<span>
Осталось
${time.hours.value} ${time.hours.text},
${time.minutes.value} ${time.minutes.text} и
<span v-if="${time.seconds.value > 5}">
${time.seconds.value} ${time.seconds.text}
</span>
<span v-else style="color:red;">
${time.seconds.value} ${time.seconds.text}
</span>
</span>
`;
},
finish: function() {
$($(this).attr('data-timer-finish')).fadeOut();
return 'Время вышло!';
}
});
});
This Pen doesn't use any external CSS resources.