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
487 lines (394 loc) • 17.3 kB
JavaScript
/* global Metro, METRO_LOCALE, Datetime, datetime */
(function(Metro, $) {
'use strict';
var Utils = Metro.utils;
var DatePickerDefaultConfig = {
label: "",
datepickerDeferred: 0,
gmt: 0,
format: "YYYY-MM-DD",
inputFormat: null,
locale: METRO_LOCALE,
value: null,
distance: 3,
month: true,
day: true,
year: true,
minYear: null,
maxYear: null,
defaultYearDistance: 100,
scrollSpeed: 4,
copyInlineStyles: false,
clsPicker: "",
clsPart: "",
clsMonth: "",
clsDay: "",
clsYear: "",
clsLabel: "",
clsButton: "",
clsOkButton: "",
clsCancelButton: "",
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,
onDatePickerCreate: Metro.noop
};
Metro.datePickerSetup = function (options) {
DatePickerDefaultConfig = $.extend({}, DatePickerDefaultConfig, options);
};
if (typeof window["metroDatePickerSetup"] !== undefined) {
Metro.datePickerSetup(window["metroDatePickerSetup"]);
}
Metro.Component('date-picker', {
init: function( options, elem ) {
this._super(elem, options, DatePickerDefaultConfig, {
picker: null,
isOpen: false,
value: datetime(),
locale: null,
listTimer: {
day: null,
month: null,
year: null
}
});
return this;
},
_create: function(){
var element = this.element, o = this.options;
var date = datetime();
if (o.distance < 1) {
o.distance = 1;
}
if (Utils.isValue(element.val())) {
o.value = element.val();
}
if (Utils.isValue(o.value)) {
this.value = o.inputFormat ? Datetime.from(o.value, o.inputFormat, o.locale) : datetime(o.value);
}
if (Metro.locales[o.locale] === undefined) {
o.locale = METRO_LOCALE;
}
this.locale = Metro.locales[o.locale]['calendar'];
if (o.minYear === null) {
o.minYear = date.year() - o.defaultYearDistance;
}
if (o.maxYear === null) {
o.maxYear = date.year() + o.defaultYearDistance;
}
this._createStructure();
this._createEvents();
this._set();
this._fireEvent("datepicker-create", {
element: element
});
},
_createStructure: function(){
var element = this.element, o = this.options;
var picker, month, day, year, i, j;
var dateWrapper, selectWrapper, selectBlock, actionBlock;
var id = Utils.elementId("datepicker");
picker = $("<div>").attr("id", id).addClass("wheel-picker date-picker " + element[0].className).addClass(o.clsPicker);
picker.insertBefore(element);
element.appendTo(picker);
if (o.label) {
var label = $("<label>").addClass("label-for-input").addClass(o.clsLabel).html(o.label).insertBefore(picker);
if (element.attr("id")) {
label.attr("for", element.attr("id"));
}
if (element.attr("dir") === "rtl") {
label.addClass("rtl");
}
}
dateWrapper = $("<div>").addClass("date-wrapper").appendTo(picker);
if (o.month === true) {
month = $("<div>").addClass("month").addClass(o.clsPart).addClass(o.clsMonth).appendTo(dateWrapper);
}
if (o.day === true) {
day = $("<div>").addClass("day").addClass(o.clsPart).addClass(o.clsDay).appendTo(dateWrapper);
}
if (o.year === true) {
year = $("<div>").addClass("year").addClass(o.clsPart).addClass(o.clsYear).appendTo(dateWrapper);
}
selectWrapper = $("<div>").addClass("select-wrapper").appendTo(picker);
selectBlock = $("<div>").addClass("select-block").appendTo(selectWrapper);
if (o.month === true) {
month = $("<ul>").addClass("sel-month").appendTo(selectBlock);
for (i = 0; i < o.distance; i++) $("<li>").html(" ").data("value", -1).appendTo(month);
for (i = 0; i < 12; i++) {
$("<li>").addClass("js-month-"+i+" js-month-real-"+this.locale['months'][i].toLowerCase()).html(this.locale['months'][i]).data("value", i).appendTo(month);
}
for (i = 0; i < o.distance; i++) $("<li>").html(" ").data("value", -1).appendTo(month);
}
if (o.day === true) {
day = $("<ul>").addClass("sel-day").appendTo(selectBlock);
for (i = 0; i < o.distance; i++) $("<li>").html(" ").data("value", -1).appendTo(day);
for (i = 0; i < 31; i++) {
$("<li>").addClass("js-day-"+i+" js-day-real-"+(i+1)).html(i + 1).data("value", i + 1).appendTo(day);
}
for (i = 0; i < o.distance; i++) $("<li>").html(" ").data("value", -1).appendTo(day);
}
if (o.year === true) {
year = $("<ul>").addClass("sel-year").appendTo(selectBlock);
for (i = 0; i < o.distance; i++) $("<li>").html(" ").data("value", -1).appendTo(year);
for (i = o.minYear, j = 0; i <= o.maxYear; i++, j++) {
$("<li>").addClass("js-year-"+ j + " js-year-real-" + i).html(i).data("value", i).appendTo(year);
}
for (i = 0; i < o.distance; i++) $("<li>").html(" ").data("value", -1).appendTo(year);
}
selectBlock.height((o.distance * 2 + 1) * 40);
actionBlock = $("<div>").addClass("action-block").appendTo(selectWrapper);
$("<button>").attr("type", "button").addClass("button action-ok").addClass(o.clsButton).addClass(o.clsOkButton).html(o.okButtonIcon).appendTo(actionBlock);
$("<button>").attr("type", "button").addClass("button action-cancel").addClass(o.clsButton).addClass(o.clsCancelButton).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 (element.prop("disabled")) {
picker.addClass("disabled");
}
this.picker = picker;
},
_createEvents: function(){
var that = this, 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, function(e){
target.scrollTop -= o.scrollSpeed * (pageY > Utils.pageXY(e).y ? -1 : 1);
pageY = Utils.pageXY(e).y;
}, {ns: picker.attr("id")});
$(document).on(Metro.events.stop, function(){
$(document).off(Metro.events.move, {ns: picker.attr("id")});
$(document).off(Metro.events.stop, {ns: picker.attr("id")});
}, {ns: picker.attr("id")});
});
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 m, d, y;
var sm = picker.find(".sel-month li.active"),
sd = picker.find(".sel-day li.active"),
sy = picker.find(".sel-year li.active");
m = sm.length === 0 ? that.value.value.getMonth() : sm.data("value");
d = sd.length === 0 ? that.value.value.getDate() : sd.data("value");
y = sy.length === 0 ? that.value.value.getFullYear() : sy.data("value");
that.value = datetime(y, m, d);
that._correct();
that._set();
that.close();
e.stopPropagation();
});
picker.on(Metro.events.click, ".action-cancel", function(e){
that.close();
e.stopPropagation();
});
var scrollLatency = 150;
$.each(["month", "day", "year"], function(){
var part = this, list = picker.find(".sel-"+part);
list.on("scroll", function(){
if (that.isOpen) {
if (that.listTimer[part]) {
clearTimeout(that.listTimer[part]);
that.listTimer[part] = null;
}
if (!that.listTimer[part]) that.listTimer[part] = setTimeout(function () {
var target, targetElement, scrollTop;
that.listTimer[part] = null;
target = Math.round((Math.ceil(list.scrollTop()) / 40));
targetElement = list.find(".js-" + part + "-" + target);
scrollTop = targetElement.position().top - (o.distance * 40);
list.find(".active").removeClass("active");
list[0].scrollTop = scrollTop;
targetElement.addClass("active");
Utils.exec(o.onScroll, [targetElement, list, picker], list[0]);
}, scrollLatency);
}
})
});
},
_correct: function(){
var m = this.value.month(),
d = this.value.day(),
y = this.value.year();
this.value = datetime(y, m, d);
},
_set: function(){
var element = this.element, o = this.options;
var picker = this.picker;
var m = this.locale['months'][this.value.month()],
d = this.value.day(),
y = this.value.year();
if (o.month === true) {
picker.find(".month").html(m);
}
if (o.day === true) {
picker.find(".day").html(d);
}
if (o.year === true) {
picker.find(".year").html(y);
}
element.val(this.value.format(o.format, o.locale)).trigger("change");
this._fireEvent("set", {
value: this.value.val(),
elementValue: element.val(),
picker: picker
})
},
open: function(){
var o = this.options;
var picker = this.picker;
var m = this.value.month(), d = this.value.day() - 1, y = this.value.year();
var m_list, d_list, y_list;
var select_wrapper = picker.find(".select-wrapper");
var select_wrapper_in_viewport, select_wrapper_rect;
select_wrapper.parent().removeClass("for-top for-bottom");
select_wrapper.show(0);
picker.find("li").removeClass("active");
select_wrapper_in_viewport = Utils.inViewport(select_wrapper[0]);
select_wrapper_rect = Utils.rect(select_wrapper[0]);
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");
}
if (o.month === true) {
m_list = picker.find(".sel-month");
m_list
.scrollTop(0)
.animate({
draw: {
scrollTop: m_list.find("li.js-month-" + m).addClass("active").position().top - (40 * o.distance)
},
dur: 100
});
}
if (o.day === true) {
d_list = picker.find(".sel-day");
d_list
.scrollTop(0)
.animate({
draw: {
scrollTop: d_list.find("li.js-day-" + d).addClass("active").position().top - (40 * o.distance)
},
dur: 100
});
}
if (o.year === true) {
y_list = picker.find(".sel-year");
y_list
.scrollTop(0)
.animate({
draw: {
scrollTop: y_list.find("li.js-year-real-" + y).addClass("active").position().top - (40 * o.distance)
},
dur: 100
});
}
this.isOpen = true;
this._fireEvent("open", {
value: this.value.val(),
picker: picker
})
},
close: function(){
var picker = this.picker;
picker.find(".select-wrapper").hide(0);
this.isOpen = false;
this._fireEvent("close", {
value: this.value.val(),
picker: picker
});
},
val: function(value){
var o = this.options;
if (!Utils.isValue(value)) {
return this.element.val();
}
this.value = o.inputFormat ? Datetime.from(value, o.inputFormat, o.locale) : datetime(value);
this._set();
},
date: function(t, f){
if (t === undefined) {
return this.value.val();
}
try {
this.value = Datetime.from(t, f, this.options.locale);
this._set();
} catch (e) {
return false;
}
},
i18n: function(locale){
var element = this.element, o = this.options;
var month, i;
o.locale = locale ? locale : element.attr("data-locale");
this.locale = Metro.locales[o.locale]['calendar'];
if (o.month === true) {
month = element.closest(".date-picker").find(".sel-month").html("");
for (i = 0; i < o.distance; i++) $("<li>").html(" ").data("value", -1).appendTo(month);
for (i = 0; i < 12; i++) {
$("<li>").addClass("js-month-"+i+" js-month-real-"+this.locale['months'][i].toLowerCase()).html(this.locale['months'][i]).data("value", i).appendTo(month);
}
for (i = 0; i < o.distance; i++) $("<li>").html(" ").data("value", -1).appendTo(month);
}
this._set();
},
disable: function(){
this.element.data("disabled", true);
this.element.parent().addClass("disabled");
},
enable: function(){
this.element.data("disabled", false);
this.element.parent().removeClass("disabled");
},
toggleState: function(){
if (this.elem.disabled) {
this.disable();
} else {
this.enable();
}
},
changeAttribute: function(attr, newValue){
switch (attr) {
case "disabled": this.toggleState(); break;
case "data-value": this.val(newValue); break;
case "data-locale": this.i18n(newValue); break;
case "data-format":
this.options.format = newValue;
this._set();
break;
}
},
destroy: function(){
var element = this.element, picker = this.picker;
$.each(["moth", "day", "year"], function(){
picker.find(".sel-"+this).off("scroll");
});
picker.off(Metro.events.start, ".select-block ul");
picker.off(Metro.events.click);
picker.off(Metro.events.click, ".action-ok");
picker.off(Metro.events.click, ".action-cancel");
return element;
}
});
$(document).on(Metro.events.click, function(){
$.each($(".date-picker"), function(){
$(this).find("input").each(function(){
Metro.getPlugin(this, "datepicker").close();
});
});
});
}(Metro, m4q));