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
462 lines (376 loc) • 15.2 kB
JavaScript
var TimePicker = {
init: function( options, elem ) {
this.options = $.extend( {}, this.options, options );
this.elem = elem;
this.element = $(elem);
this.picker = null;
this.isOpen = false;
this.value = [];
this.locale = Metro.locales[METRO_LOCALE]['calendar'];
this._setOptionsFromDOM();
this._create();
return this;
},
options: {
hoursStep: 1,
minutesStep: 1,
secondsStep: 1,
value: null,
locale: METRO_LOCALE,
distance: 3,
hours: true,
minutes: true,
seconds: true,
showLabels: true,
scrollSpeed: 5,
copyInlineStyles: true,
clsPicker: "",
clsPart: "",
clsHours: "",
clsMinutes: "",
clsSeconds: "",
okButtonIcon: "<span class='default-icon-check'></span>",
cancelButtonIcon: "<span class='default-icon-cross'></span>",
onSet: Metro.noop,
onOpen: Metro.noop,
onClose: Metro.noop,
onScroll: Metro.noop,
onTimePickerCreate: Metro.noop
},
_setOptionsFromDOM: function(){
var that = this, 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 that = this, element = this.element, o = this.options;
var picker = this.picker;
var i;
if (o.distance < 1) {
o.distance = 1;
}
if (o.hoursStep < 1) {o.hoursStep = 1;}
if (o.hoursStep > 23) {o.hoursStep = 23;}
if (o.minutesStep < 1) {o.minutesStep = 1;}
if (o.minutesStep > 59) {o.minutesStep = 59;}
if (o.secondsStep < 1) {o.secondsStep = 1;}
if (o.secondsStep > 59) {o.secondsStep = 59;}
if (element.val() === "" && (!Utils.isValue(o.value))) {
o.value = (new Date()).format("%H:%M:%S");
}
this.value = Utils.strToArray(element.val() !== "" ? element.val() : String(o.value), ":");
for(i = 0; i < 3; i++) {
if (this.value[i] === undefined || this.value[i] === null) {
this.value[i] = 0;
} else {
this.value[i] = parseInt(this.value[i]);
}
}
this._normalizeValue();
if (Metro.locales[o.locale] === undefined) {
o.locale = METRO_LOCALE;
}
this.locale = Metro.locales[o.locale]['calendar'];
this._createStructure();
this._createEvents();
this._set();
Utils.exec(o.onTimePickerCreate, [element, picker]);
},
_normalizeValue: function(){
var o = this.options;
if (o.hoursStep > 1) {
this.value[0] = Utils.nearest(this.value[0], o.hoursStep, true);
}
if (o.minutesStep > 1) {
this.value[1] = Utils.nearest(this.value[1], o.minutesStep, true);
}
if (o.minutesStep > 1) {
this.value[2] = Utils.nearest(this.value[2], o.secondsStep, true);
}
},
_createStructure: function(){
var that = this, element = this.element, o = this.options;
var picker, hours, minutes, seconds, ampm, select, i;
var timeWrapper, selectWrapper, selectBlock, actionBlock;
var prev = element.prev();
var parent = element.parent();
var id = Utils.elementId("time-picker");
picker = $("<div>").attr("id", id).addClass("wheel-picker time-picker " + element[0].className).addClass(o.clsPicker);
if (prev.length === 0) {
parent.prepend(picker);
} else {
picker.insertAfter(prev);
}
element.attr("readonly", true).appendTo(picker);
timeWrapper = $("<div>").addClass("time-wrapper").appendTo(picker);
if (o.hours === true) {
hours = $("<div>").attr("data-title", this.locale['time']['hours']).addClass("hours").addClass(o.clsPart).addClass(o.clsHours).appendTo(timeWrapper);
}
if (o.minutes === true) {
minutes = $("<div>").attr("data-title", this.locale['time']['minutes']).addClass("minutes").addClass(o.clsPart).addClass(o.clsMinutes).appendTo(timeWrapper);
}
if (o.seconds === true) {
seconds = $("<div>").attr("data-title", this.locale['time']['seconds']).addClass("seconds").addClass(o.clsPart).addClass(o.clsSeconds).appendTo(timeWrapper);
}
selectWrapper = $("<div>").addClass("select-wrapper").appendTo(picker);
selectBlock = $("<div>").addClass("select-block").appendTo(selectWrapper);
if (o.hours === true) {
hours = $("<ul>").addClass("sel-hours").appendTo(selectBlock);
for (i = 0; i < o.distance; i++) $("<li>").html(" ").data("value", -1).appendTo(hours);
for (i = 0; i < 24; i = i + o.hoursStep) {
$("<li>").addClass("js-hours-"+i).html(i < 10 ? "0"+i : i).data("value", i).appendTo(hours);
}
for (i = 0; i < o.distance; i++) $("<li>").html(" ").data("value", -1).appendTo(hours);
}
if (o.minutes === true) {
minutes = $("<ul>").addClass("sel-minutes").appendTo(selectBlock);
for (i = 0; i < o.distance; i++) $("<li>").html(" ").data("value", -1).appendTo(minutes);
for (i = 0; i < 60; i = i + o.minutesStep) {
$("<li>").addClass("js-minutes-"+i).html(i < 10 ? "0"+i : i).data("value", i).appendTo(minutes);
}
for (i = 0; i < o.distance; i++) $("<li>").html(" ").data("value", -1).appendTo(minutes);
}
if (o.seconds === true) {
seconds = $("<ul>").addClass("sel-seconds").appendTo(selectBlock);
for (i = 0; i < o.distance; i++) $("<li>").html(" ").data("value", -1).appendTo(seconds);
for (i = 0; i < 60; i = i + o.secondsStep) {
$("<li>").addClass("js-seconds-"+i).html(i < 10 ? "0"+i : i).data("value", i).appendTo(seconds);
}
for (i = 0; i < o.distance; i++) $("<li>").html(" ").data("value", -1).appendTo(seconds);
}
selectBlock.height((o.distance * 2 + 1) * 40);
actionBlock = $("<div>").addClass("action-block").appendTo(selectWrapper);
$("<button>").attr("type", "button").addClass("button action-ok").html(o.okButtonIcon).appendTo(actionBlock);
$("<button>").attr("type", "button").addClass("button action-cancel").html(o.cancelButtonIcon).appendTo(actionBlock);
element[0].className = '';
if (o.copyInlineStyles === true) {
for (i = 0; i < element[0].style.length; i++) {
picker.css(element[0].style[i], element.css(element[0].style[i]));
}
}
if (o.showLabels === true) {
picker.addClass("show-labels");
}
this.picker = picker;
},
_createEvents: function(){
var that = this, element = this.element, o = this.options;
var picker = this.picker;
picker.on(Metro.events.start, ".select-block ul", function(e){
if (e.changedTouches) {
return ;
}
var target = this;
var pageY = Utils.pageXY(e).y;
$(document).on(Metro.events.move + "-picker", function(e){
target.scrollTop -= o.scrollSpeed * (pageY > Utils.pageXY(e).y ? -1 : 1);
pageY = Utils.pageXY(e).y;
});
$(document).on(Metro.events.stop + "-picker", function(e){
$(document).off(Metro.events.move + "-picker");
$(document).off(Metro.events.stop + "-picker");
});
});
picker.on(Metro.events.click, function(e){
if (that.isOpen === false) that.open();
e.stopPropagation();
});
picker.on(Metro.events.click, ".action-ok", function(e){
var h, m, s, a;
var sh = picker.find(".sel-hours li.active"),
sm = picker.find(".sel-minutes li.active"),
ss = picker.find(".sel-seconds li.active");
h = sh.length === 0 ? 0 : sh.data("value");
m = sm.length === 0 ? 0 : sm.data("value");
s = ss.length === 0 ? 0 : ss.data("value");
that.value = [h, m, s];
that._normalizeValue();
that._set();
that.close();
e.stopPropagation();
});
picker.on(Metro.events.click, ".action-cancel", function(e){
that.close();
e.stopPropagation();
});
this._addScrollEvents();
},
_addScrollEvents: function(){
var picker = this.picker, o = this.options;
var lists = ['hours', 'minutes', 'seconds'];
$.each(lists, function(){
var list_name = this;
var list = picker.find(".sel-" + list_name);
if (list.length === 0) return ;
list.on(Metro.events.scrollStart, function(){
list.find(".active").removeClass("active");
});
list.on(Metro.events.scrollStop, {latency: 50}, function(){
var target = Math.round((Math.ceil(list.scrollTop() + 40) / 40)) ;
var target_element = list.find("li").eq(target + o.distance - 1);
var scroll_to = target_element.position().top - (o.distance * 40) + list.scrollTop();
list.animate({
scrollTop: scroll_to
}, 100, function(){
target_element.addClass("active");
Utils.exec(o.onScroll, [target_element, list, picker]);
});
});
});
},
_removeScrollEvents: function(){
var picker = this.picker;
var lists = ['hours', 'minutes', 'seconds'];
$.each(lists, function(){
picker.find(".sel-" + this).off("scrollstart scrollstop");
});
},
_set: function(){
var that = this, element = this.element, o = this.options;
var picker = this.picker;
var h = "00", m = "00", s = "00";
if (o.hours === true) {
h = parseInt(this.value[0]);
if (h < 10) {
h = "0"+h;
}
picker.find(".hours").html(h);
}
if (o.minutes === true) {
m = parseInt(this.value[1]);
if (m < 10) {
m = "0"+m;
}
picker.find(".minutes").html(m);
}
if (o.seconds === true) {
s = parseInt(this.value[2]);
if (s < 10) {
s = "0"+s;
}
picker.find(".seconds").html(s);
}
element.val([h, m, s].join(":")).trigger("change");
Utils.exec(o.onSet, [this.value, element.val(), element, picker]);
},
open: function(){
var that = this, element = this.element, o = this.options;
var picker = this.picker;
var h, m, s;
var h_list, m_list, s_list;
var items = picker.find("li");
var select_wrapper = picker.find(".select-wrapper");
var select_wrapper_in_viewport, select_wrapper_rect;
var h_item, m_item, s_item;
select_wrapper.parent().removeClass("for-top for-bottom");
select_wrapper.show();
items.removeClass("active");
select_wrapper_in_viewport = Utils.inViewport(select_wrapper);
select_wrapper_rect = Utils.rect(select_wrapper);
if (!select_wrapper_in_viewport && select_wrapper_rect.top > 0) {
select_wrapper.parent().addClass("for-bottom");
}
if (!select_wrapper_in_viewport && select_wrapper_rect.top < 0) {
select_wrapper.parent().addClass("for-top");
}
var animateList = function(list, item){
list.scrollTop(0).animate({
scrollTop: item.position().top - (o.distance * 40) + list.scrollTop()
}, 100);
};
if (o.hours === true) {
h = parseInt(this.value[0]);
h_list = picker.find(".sel-hours");
h_item = h_list.find("li.js-hours-" + h).addClass("active");
animateList(h_list, h_item);
}
if (o.minutes === true) {
m = parseInt(this.value[1]);
m_list = picker.find(".sel-minutes");
m_item = m_list.find("li.js-minutes-" + m).addClass("active");
animateList(m_list, m_item);
}
if (o.seconds === true) {
s = parseInt(this.value[2]);
s_list = picker.find(".sel-seconds");
s_item = s_list.find("li.js-seconds-" + s).addClass("active");
animateList(s_list, s_item);
}
this.isOpen = true;
Utils.exec(o.onOpen, [this.value, element, picker]);
},
close: function(){
var picker = this.picker, o = this.options, element = this.element;
picker.find(".select-wrapper").hide();
this.isOpen = false;
Utils.exec(o.onClose, [this.value, element, picker]);
},
_convert: function(t){
var result;
if (Array.isArray(t)) {
result = t;
} else if (typeof t.getMonth === 'function') {
result = [t.getHours(), t.getMinutes(), t.getSeconds()];
} else if (Utils.isObject(t)) {
result = [t.h, t.m, t.s];
} else {
result = Utils.strToArray(t, ":");
}
return result;
},
val: function(t){
if (t === undefined) {
return this.element.val();
}
this.value = this._convert(t);
this._normalizeValue();
this._set();
},
time: function(t){
if (t === undefined) {
return {
h: this.value[0],
m: this.value[1],
s: this.value[2]
}
}
this.value = this._convert(t);
this._normalizeValue();
this._set();
},
date: function(t){
if (t === undefined || typeof t.getMonth !== 'function') {
var ret = new Date();
ret.setHours(this.value[0]);
ret.setMinutes(this.value[1]);
ret.setSeconds(this.value[2]);
ret.setMilliseconds(0);
return ret;
}
this.value = this._convert(t);
this._normalizeValue();
this._set();
},
changeAttribute: function(attributeName){
var that = this, element = this.element;
var changeValueAttribute = function(){
that.val(element.attr("data-value"));
};
switch (attributeName) {
case "data-value": changeValueAttribute(); break;
}
}
};
Metro.plugin('timepicker', TimePicker);
$(document).on(Metro.events.click, function(e){
$.each($(".time-picker"), function(){
$(this).find("input").data("timepicker").close();
});
});