add timepicker to develop branch, closes #3

This commit is contained in:
t1m0n 2016-04-04 10:41:02 +03:00
parent 5956c93031
commit c648cba3ab
3 changed files with 430 additions and 10 deletions

430
dist/js/datepicker.js vendored
View File

@ -62,6 +62,17 @@
years: 'yyyy1 - yyyy2'
},
// timepicker
timepicker: false,
dateTimeSeparator: ' ',
timeFormat: '',
minHours: 0,
maxHours: 24,
minMinutes: 0,
maxMinutes: 59,
hoursStep: 1,
minutesStep: 1,
// events
onSelect: '',
onChangeMonth: '',
@ -155,11 +166,17 @@
this.$datepicker.addClass(this.opts.classes)
}
if (this.opts.timepicker) {
this.timepicker = new Datepicker.Timepicker(this, this.opts);
this._bindTimepickerEvents();
}
this.views[this.currentView] = new Datepicker.Body(this, this.currentView, this.opts);
this.views[this.currentView].show();
this.nav = new Datepicker.Navigation(this, this.opts);
this.view = this.currentView;
this.$el.on('clickCell.adp', this._onClickCell.bind(this));
this.$datepicker.on('mouseenter', '.datepicker--cell', this._onMouseEnterCell.bind(this));
this.$datepicker.on('mouseleave', '.datepicker--cell', this._onMouseLeaveCell.bind(this));
@ -173,9 +190,11 @@
_bindEvents : function () {
this.$el.on(this.opts.showEvent + '.adp', this._onShowEvent.bind(this));
this.$el.on('mouseup.adp', this._onMouseUpEl.bind(this));
this.$el.on('blur.adp', this._onBlur.bind(this));
this.$el.on('input.adp', this._onInput.bind(this));
$(window).on('resize.adp', this._onResize.bind(this));
$('body').on('mouseup.adp', this._onMouseUpBody.bind(this));
},
_bindKeyboardEvents: function () {
@ -184,6 +203,10 @@
this.$el.on('hotKey.adp', this._onHotKey.bind(this));
},
_bindTimepickerEvents: function () {
this.$el.on('timeChange.adp', this._onTimeChange.bind(this));
},
isWeekend: function (day) {
return this.opts.weekends.indexOf(day) !== -1;
},
@ -205,9 +228,24 @@
this.loc.dateFormat = this.opts.dateFormat
}
if (this.opts.timeFormat) {
this.loc.timeFormat = this.opts.timeFormat
}
if (this.opts.firstDay !== '') {
this.loc.firstDay = this.opts.firstDay
}
if (this.opts.timepicker) {
this.loc.dateFormat = [this.loc.dateFormat, this.loc.timeFormat].join(this.opts.dateTimeSeparator);
}
var boundary = this._getWordBoundaryRegExp;
if (this.loc.timeFormat.match(boundary('aa')) ||
this.loc.timeFormat.match(boundary('AA'))
) {
this.ampm = true;
}
},
_buildDatepickersContainer: function () {
@ -244,7 +282,13 @@
parsedSelected = datepicker.getParsedDate(selectedDates[0]),
formattedDates,
_this = this,
dates = new Date(parsedSelected.year, parsedSelected.month, parsedSelected.date);
dates = new Date(
parsedSelected.year,
parsedSelected.month,
parsedSelected.date,
parsedSelected.hours,
parsedSelected.minutes
);
formattedDates = selectedDates.map(function (date) {
return _this.formatDate(_this.loc.dateFormat, date)
@ -254,7 +298,13 @@
if (this.opts.multipleDates || this.opts.range) {
dates = selectedDates.map(function(date) {
var parsedDate = datepicker.getParsedDate(date);
return new Date(parsedDate.year, parsedDate.month, parsedDate.date)
return new Date(
parsedSelected.year,
parsedSelected.month,
parsedSelected.date,
parsedSelected.hours,
parsedSelected.minutes
);
})
}
@ -304,12 +354,28 @@
var result = string,
boundary = this._getWordBoundaryRegExp,
locale = this.loc,
leadingZero = datepicker.getLeadingZeroNum,
decade = datepicker.getDecade(date),
d = datepicker.getParsedDate(date);
d = datepicker.getParsedDate(date),
fullHours = d.fullHours,
hours = d.hours,
dayPeriod = 'am',
validHours;
if (this.opts.timepicker && this.timepicker && this.ampm) {
validHours = this.timepicker._getValidHoursFromDate(date);
fullHours = leadingZero(validHours.hours);
hours = validHours.hours;
dayPeriod = validHours.dayPeriod;
}
switch (true) {
case /@/.test(result):
result = result.replace(/@/, date.getTime());
case /aa/.test(result):
result = result.replace(boundary('aa'), dayPeriod);
case /AA/.test(result):
result = result.replace(boundary('AA'), dayPeriod.toUpperCase());
case /dd/.test(result):
result = result.replace(boundary('dd'), d.fullDate);
case /d/.test(result):
@ -326,6 +392,14 @@
result = result.replace(boundary('MM'), this.loc.months[d.month]);
case /M/.test(result):
result = result.replace(boundary('M'), locale.monthsShort[d.month]);
case /ii/.test(result):
result = result.replace(boundary('ii'), d.fullMinutes);
case /i/.test(result):
result = result.replace(boundary('i'), d.minutes);
case /hh/.test(result):
result = result.replace(boundary('hh'), fullHours);
case /h/.test(result):
result = result.replace(boundary('h'), hours);
case /yyyy/.test(result):
result = result.replace(boundary('yyyy'), d.year);
case /yyyy1/.test(result):
@ -353,6 +427,25 @@
if (!(date instanceof Date)) return;
this.lastSelectedDate = date;
// Set new time values from Date
if (this.timepicker) {
this.timepicker.hours = date.getHours();
this.timepicker.minutes = date.getMinutes();
}
// On this step timepicker will set valid values in it's instance
_this._trigger('selectDate', date);
// Set correct time values after timepicker's validation
// Prevent from setting hours or minutes which values are lesser then `min` value or
// greater then `max` value
if (this.timepicker) {
date.setHours(this.timepicker.hours)
date.setMinutes(this.timepicker.minutes)
}
if (_this.view == 'days') {
if (date.getMonth() != d.month && opts.moveToOtherMonthsOnSelect) {
newDate = new Date(date.getFullYear(), date.getMonth(), 1);
@ -429,6 +522,9 @@
if (!_this.selectedDates.length) {
_this.minRange = '';
_this.maxRange = '';
_this.lastSelectedDate = '';
} else {
_this.lastSelectedDate = _this.selectedDates[_this.selectedDates.length - 1];
}
_this.views[_this.currentView]._render();
@ -468,6 +564,7 @@
*/
update: function (param, value) {
var len = arguments.length;
if (len == 2) {
this.opts[param] = value;
} else if (len == 1 && typeof param == 'object') {
@ -492,6 +589,19 @@
this.$datepicker.addClass(this.opts.classes)
}
if (this.opts.timepicker) {
this.timepicker._handleDate(this.lastSelectedDate);
this.timepicker._updateRanges();
this.timepicker._updateCurrentTime();
// Change hours and minutes if it's values have been changed through min/max hours/minutes
if (this.lastSelectedDate) {
this.lastSelectedDate.setHours(this.timepicker.hours);
this.lastSelectedDate.setMinutes(this.timepicker.minutes);
}
}
this._setInputValue();
return this;
},
@ -929,7 +1039,8 @@
_onMouseUpDatepicker: function (e) {
this.inFocus = false;
this.$el.focus()
e.originalEvent.inFocus = true;
if (!e.originalEvent.timepickerFocus) this.$el.focus();
},
_onInput: function () {
@ -946,6 +1057,18 @@
}
},
_onMouseUpBody: function (e) {
if (e.originalEvent.inFocus) return;
if (this.visible && !this.inFocus) {
this.hide();
}
},
_onMouseUpEl: function (e) {
e.originalEvent.inFocus = true;
},
_onKeyDown: function (e) {
var code = e.which;
this._registerKey(code);
@ -1026,6 +1149,37 @@
this.silent = false;
},
_onTimeChange: function (e, h, m) {
var date = new Date(),
selectedDates = this.selectedDates,
selected = false;
if (selectedDates.length) {
selected = true;
date = this.lastSelectedDate;
}
date.setHours(h);
date.setMinutes(m);
if (!selected && !this._getCell(date).hasClass('-disabled-')) {
this.selectDate(date);
} else {
this._setInputValue();
if (this.opts.onSelect) {
this._triggerOnChange();
}
}
},
_onClickCell: function (e, date) {
if (this.timepicker) {
date.setHours(this.timepicker.hours);
date.setMinutes(this.timepicker.minutes);
}
this.selectDate(date);
},
set focused(val) {
if (!val && this.focused) {
var $cell = this._getCell(this.focused);
@ -1141,7 +1295,11 @@
fullMonth: (date.getMonth() + 1) < 10 ? '0' + (date.getMonth() + 1) : date.getMonth() + 1, // One based
date: date.getDate(),
fullDate: date.getDate() < 10 ? '0' + date.getDate() : date.getDate(),
day: date.getDay()
day: date.getDay(),
hours: date.getHours(),
fullHours: date.getHours() < 10 ? '0' + date.getHours() : date.getHours() ,
minutes: date.getMinutes(),
fullMinutes: date.getMinutes() < 10 ? '0' + date.getMinutes() : date.getMinutes()
}
};
@ -1184,6 +1342,10 @@
return date.getTime() > dateCompareTo.getTime();
};
datepicker.getLeadingZeroNum = function (num) {
return parseInt(num) < 10 ? '0' + num : num;
};
Datepicker.language = {
ru: {
days: ['Воскресенье', 'Понедельник', 'Вторник', 'Среда', 'Четверг', 'Пятница', 'Суббота'],
@ -1194,6 +1356,7 @@
today: 'Сегодня',
clear: 'Очистить',
dateFormat: 'dd.mm.yyyy',
timeFormat: 'hh:ii',
firstDay: 1
}
};
@ -1217,6 +1380,7 @@
})
})();
;(function () {
var templates = {
days:'' +
@ -1509,7 +1673,7 @@
alreadySelected = this.d._isSelected(selectedDate, this.d.cellType);
if (!alreadySelected) {
this.d.selectDate(selectedDate);
this.d._trigger('clickCell', selectedDate);
} else if (alreadySelected && this.opts.toggleSelected){
this.d.removeDate(selectedDate);
}
@ -1666,4 +1830,258 @@
}
})();
;(function () {
var template = '<div class="datepicker--time">' +
'<div class="datepicker--time-current">' +
' <span class="datepicker--time-current-hours">#{hourValue}</span>' +
' <span class="datepicker--time-current-colon">:</span>' +
' <span class="datepicker--time-current-minutes">#{minValue}</span>' +
'</div>' +
'<div class="datepicker--time-sliders">' +
' <div class="datepicker--time-row">' +
' <input type="range" name="hours" value="#{hourValue}" min="#{hourMin}" max="#{hourMax}" step="#{hourStep}"/>' +
' </div>' +
' <div class="datepicker--time-row">' +
' <input type="range" name="minutes" value="#{minValue}" min="#{minMin}" max="#{minMax}" step="#{minStep}"/>' +
' </div>' +
'</div>' +
'</div>',
datepicker = Datepicker;
datepicker.Timepicker = function (inst, opts) {
this.d = inst;
this.opts = opts;
this.init();
};
datepicker.Timepicker.prototype = {
init: function () {
var input = 'input';
this._setInitialTime(this.d.date);
this._buildHTML();
if (navigator.userAgent.match(/trident/gi)) {
input = 'change';
}
this.d.$el.on('selectDate', this._onSelectDate.bind(this));
this.$ranges.on(input, this._onChangeRange.bind(this));
this.$ranges.on('mouseenter', this._onMouseEnterRange.bind(this));
this.$ranges.on('mouseout blur', this._onMouseOutRange.bind(this));
},
_setInitialTime: function (date, parse) {
var _date = datepicker.getParsedDate(date);
this._handleDate(date);
this.hours = _date.hours < this.minHours ? this.minHours : _date.hours;
this.minutes = _date.minutes < this.minMinutes ? this.minMinutes : _date.minutes;
},
_setMinTimeFromDate: function (date) {
this.minHours = date.getHours();
this.minMinutes = date.getMinutes();
},
_setMaxTimeFromDate: function (date) {
this.maxHours = date.getHours();
this.maxMinutes = date.getMinutes();
},
_setDefaultMinMaxTime: function () {
var maxHours = 23,
maxMinutes = 59,
opts = this.opts;
this.minHours = opts.minHours < 0 || opts.minHours > maxHours ? 0 : opts.minHours;
this.minMinutes = opts.minMinutes < 0 || opts.minMinutes > maxMinutes ? 0 : opts.minMinutes;
this.maxHours = opts.maxHours < 0 || opts.maxHours > maxHours ? maxHours : opts.maxHours;
this.maxMinutes = opts.maxMinutes < 0 || opts.maxMinutes > maxMinutes ? maxMinutes : opts.maxMinutes;
},
/**
* Looks for min/max hours/minutes and if current values
* are out of range sets valid values.
* @private
*/
_validateHoursMinutes: function (date) {
if (this.hours < this.minHours) {
this.hours = this.minHours;
} else if (this.hours > this.maxHours) {
this.hours = this.maxHours;
}
if (this.minutes < this.minMinutes) {
this.minutes = this.minMinutes;
} else if (this.minutes > this.maxMinutes) {
this.minutes = this.maxMinutes;
}
},
_buildHTML: function () {
var lz = datepicker.getLeadingZeroNum,
data = {
hourMin: this.minHours,
hourMax: lz(this.maxHours),
hourStep: this.opts.hoursStep,
hourValue: lz(this.displayHours),
minMin: this.minMinutes,
minMax: lz(this.maxMinutes),
minStep: this.opts.minutesStep,
minValue: lz(this.minutes)
},
_template = datepicker.template(template, data);
this.$timepicker = $(_template).appendTo(this.d.$datepicker);
this.$ranges = $('[type="range"]', this.$timepicker);
this.$hours = $('[name="hours"]', this.$timepicker);
this.$minutes = $('[name="minutes"]', this.$timepicker);
this.$hoursText = $('.datepicker--time-current-hours', this.$timepicker);
this.$minutesText = $('.datepicker--time-current-minutes', this.$timepicker);
if (this.d.ampm) {
this.$ampm = $('<span class="datepicker--time-current-ampm">')
.appendTo($('.datepicker--time-current', this.$timepicker))
.html(this.dayPeriod);
this.$timepicker.addClass('-am-pm-');
}
},
_updateCurrentTime: function () {
var h = datepicker.getLeadingZeroNum(this.displayHours),
m = datepicker.getLeadingZeroNum(this.minutes);
this.$hoursText.html(h);
this.$minutesText.html(m);
if (this.d.ampm) {
this.$ampm.html(this.dayPeriod);
}
},
_updateRanges: function () {
this.$hours.attr({
min: this.minHours,
max: this.maxHours,
value: this.hours
});
this.$minutes.attr({
min: this.minMinutes,
max: this.maxMinutes,
value: this.minutes
});
},
/**
* Sets minHours, minMinutes etc. from date. If date is not passed, than sets
* values from options
* @param [date] {object} - Date object, to get values from
* @private
*/
_handleDate: function (date) {
this._setDefaultMinMaxTime();
if (date) {
if (datepicker.isSame(date, this.d.opts.minDate)) {
this._setMinTimeFromDate(this.d.opts.minDate);
} else if (datepicker.isSame(date, this.d.opts.maxDate)) {
this._setMaxTimeFromDate(this.d.opts.maxDate);
}
}
this._validateHoursMinutes(date);
},
update: function () {
this._updateRanges();
this._updateCurrentTime();
},
/**
* Calculates valid hour value to display in text input and datepicker's body.
* @param date {Date|Number} - date or hours
* @returns {{hours: *, dayPeriod: string}}
* @private
*/
_getValidHoursFromDate: function (date) {
var d = date,
hours = date;
if (date instanceof Date) {
d = datepicker.getParsedDate(date);
hours = d.hours;
}
var ampm = this.d.ampm,
dayPeriod = 'am';
if (ampm) {
switch(true) {
case hours == 0:
hours = 12;
break;
case hours == 12:
dayPeriod = 'pm';
break;
case hours > 11:
hours = hours - 12;
dayPeriod = 'pm';
break;
default:
break;
}
}
return {
hours: hours,
dayPeriod: dayPeriod
}
},
set hours (val) {
this._hours = val;
var displayHours = this._getValidHoursFromDate(val);
this.displayHours = displayHours.hours;
this.dayPeriod = displayHours.dayPeriod;
},
get hours() {
return this._hours;
},
// Events
// -------------------------------------------------
_onChangeRange: function (e) {
var $target = $(e.target),
name = $target.attr('name');
this[name] = $target.val();
this._updateCurrentTime();
this.d._trigger('timeChange', [this.hours, this.minutes])
},
_onSelectDate: function (e, data) {
this._handleDate(data);
this.update();
},
_onMouseEnterRange: function (e) {
var name = $(e.target).attr('name');
$('.datepicker--time-current-' + name, this.$timepicker).addClass('-focus-');
},
_onMouseOutRange: function (e) {
var name = $(e.target).attr('name');
if (this.d.inFocus) return; // Prevent removing focus when mouse out of range slider
$('.datepicker--time-current-' + name, this.$timepicker).removeClass('-focus-');
}
};
})()
})(window, jQuery);

File diff suppressed because one or more lines are too long

View File

@ -1,4 +1,4 @@
(function (window, $, datepicker) {
;(function () {
var template = '<div class="datepicker--time">' +
'<div class="datepicker--time-current">' +
' <span class="datepicker--time-current-hours">#{hourValue}</span>' +
@ -13,7 +13,8 @@
' <input type="range" name="minutes" value="#{minValue}" min="#{minMin}" max="#{minMax}" step="#{minStep}"/>' +
' </div>' +
'</div>' +
'</div>';
'</div>',
datepicker = Datepicker;
datepicker.Timepicker = function (inst, opts) {
this.d = inst;
@ -249,4 +250,4 @@
$('.datepicker--time-current-' + name, this.$timepicker).removeClass('-focus-');
}
};
})(window, jQuery, Datepicker);
})()