fomantic-ui
Version:
Fomantic empowers designers and developers by creating a shared vocabulary for UI.
948 lines (865 loc) • 101 kB
JavaScript
/*!
* # Fomantic-UI - Calendar
* https://github.com/fomantic/Fomantic-UI/
*
*
* Released under the MIT license
* https://opensource.org/licenses/MIT
*
*/
(function ($, window, document) {
'use strict';
function isFunction(obj) {
return typeof obj === 'function' && typeof obj.nodeType !== 'number';
}
window = window !== undefined && window.Math === Math
? window
: globalThis;
$.fn.calendar = function (parameters) {
var
$allModules = $(this),
$document = $(document),
time = Date.now(),
performance = [],
query = arguments[0],
methodInvoked = typeof query === 'string',
queryArguments = [].slice.call(arguments, 1),
returnedValue,
timeGapTable = {
5: { row: 4, column: 3 },
10: { row: 3, column: 2 },
15: { row: 2, column: 2 },
20: { row: 3, column: 1 },
30: { row: 2, column: 1 },
},
numberText = ['', 'one', 'two', 'three', 'four', 'five', 'six', 'seven', 'eight']
;
$allModules.each(function () {
var
settings = $.isPlainObject(parameters)
? $.extend(true, {}, $.fn.calendar.settings, parameters)
: $.extend({}, $.fn.calendar.settings),
className = settings.className,
namespace = settings.namespace,
selector = settings.selector,
formatter = settings.formatter,
parser = settings.parser,
metadata = settings.metadata,
timeGap = timeGapTable[settings.minTimeGap],
error = settings.error,
eventNamespace = '.' + namespace,
moduleNamespace = 'module-' + namespace,
$module = $(this),
$input = $module.find(selector.input),
$activator = $module.find(selector.activator),
element = this,
instance = $module.data(moduleNamespace),
$container = instance && instance.popupId ? $document.find('#' + instance.popupId) : $module.find(selector.popup),
isTouch,
isTouchDown = false,
isInverted = $module.hasClass(className.inverted),
focusDateUsedForRange = false,
selectionComplete = false,
classObserver,
module
;
module = {
initialize: function () {
module.debug('Initializing calendar for', element, $module);
isTouch = module.get.isTouch();
module.setup.config();
module.setup.popup();
module.setup.inline();
module.setup.input();
module.setup.date();
module.create.calendar();
module.bind.events();
module.observeChanges();
module.instantiate();
},
instantiate: function () {
module.verbose('Storing instance of calendar');
instance = module;
$module.data(moduleNamespace, instance);
},
destroy: function () {
module.verbose('Destroying previous calendar for', element);
$module.removeData(moduleNamespace);
module.unbind.events();
module.disconnect.classObserver();
},
setup: {
config: function () {
if (module.get.minDate() !== null) {
module.set.minDate($module.data(metadata.minDate));
}
if (module.get.maxDate() !== null) {
module.set.maxDate($module.data(metadata.maxDate));
}
module.setting('type', module.get.type());
module.setting('on', settings.on || 'click');
},
popup: function () {
if (settings.inline) {
return;
}
if ($activator.length === 0) {
$activator = $module.children().first();
if ($activator.length === 0) {
return;
}
}
if ($.fn.popup === undefined) {
module.error(error.popup);
return;
}
if ($container.length === 0) {
if (settings.context) {
module.popupId = namespace + '_popup_' + (Math.random().toString(16) + '000000000').slice(2, 10);
$container = $('<div/>', { id: module.popupId }).addClass(className.popup).appendTo($document.find(settings.context));
} else {
// prepend the popup element to the activator's parent so that it has less chance of messing with
// the styling (eg input action button needs to be the last child to have correct border radius)
var
$activatorParent = $activator.parent(),
domPositionFunction = $activatorParent.closest(selector.append).length > 0 ? 'appendTo' : 'prependTo'
;
$container = $('<div/>').addClass(className.popup)[domPositionFunction]($activatorParent);
}
}
$container.addClass(className.calendar);
if (isInverted) {
$container.addClass(className.inverted);
}
var onVisible = function () {
module.refreshTooltips();
return settings.onVisible.apply($container, arguments);
};
var onHidden = function () {
module.blur();
return settings.onHidden.apply($container, arguments);
};
if ($input.length === 0) {
// no input, $container has to handle focus/blur
$container.attr('tabindex', '0');
onVisible = function () {
module.refreshTooltips();
module.focus();
return settings.onVisible.apply($container, arguments);
};
}
var onShow = function () {
// reset the focus date onShow
module.set.focusDate(module.get.date());
module.set.mode(module.get.validatedMode(settings.startMode));
return settings.onShow.apply($container, arguments);
};
var on = module.setting('on');
var options = $.extend({}, settings.popupOptions, {
popup: $container,
movePopup: !settings.context,
on: on,
hoverable: on === 'hover',
closable: on === 'click',
onShow: onShow,
onVisible: onVisible,
onHide: settings.onHide,
onHidden: onHidden,
});
module.popup(options);
},
inline: function () {
if ($activator.length > 0 && !settings.inline) {
return;
}
settings.inline = true;
$container = $('<div/>').addClass(className.calendar).appendTo($module);
if ($input.length === 0) {
$container.attr('tabindex', '0');
}
},
input: function () {
if (settings.touchReadonly && $input.length > 0 && isTouch) {
$input.prop('readonly', true);
}
module.check.disabled();
},
date: function () {
var date;
if (settings.initialDate) {
date = parser.date(settings.initialDate, settings);
} else if ($module.data(metadata.date) !== undefined) {
date = parser.date($module.data(metadata.date), settings);
} else if ($input.length > 0) {
date = parser.date($input.val(), settings);
}
module.set.date(date, settings.formatInput, false);
module.set.mode(module.get.mode(), false);
},
},
trigger: {
change: function () {
var
inputElement = $input[0]
;
if (inputElement) {
var events = document.createEvent('HTMLEvents');
module.verbose('Triggering native change event');
events.initEvent('change', true, false);
inputElement.dispatchEvent(events);
}
},
},
create: {
calendar: function () {
var
i,
r,
c,
p,
row,
cell,
pageGrid
;
var
mode = module.get.mode(),
today = new Date(),
date = module.get.date(),
focusDate = module.get.focusDate(),
display = module.helper.dateInRange(focusDate || date || parser.date(settings.initialDate, settings) || today)
;
if (!focusDate) {
focusDate = display;
module.set.focusDate(focusDate, false, false);
}
var
isYear = mode === 'year',
isMonth = mode === 'month',
isDay = mode === 'day',
isHour = mode === 'hour',
isMinute = mode === 'minute',
isTimeOnly = settings.type === 'time'
;
var multiMonth = Math.max(settings.multiMonth, 1);
var monthOffset = !isDay ? 0 : module.get.monthOffset();
var
minute = display.getMinutes(),
hour = display.getHours(),
day = display.getDate(),
startMonth = display.getMonth() + monthOffset,
year = display.getFullYear()
;
var columns = isDay
? (settings.showWeekNumbers ? 8 : 7)
: (isHour ? 4 : timeGap.column);
var rows = isDay || isHour ? 6 : timeGap.row;
var pages = isDay ? multiMonth : 1;
var container = $container;
var tooltipPosition = container.hasClass('left') ? 'right center' : 'left center';
container.empty();
if (pages > 1) {
pageGrid = $('<div/>').addClass(className.grid).appendTo(container);
}
for (p = 0; p < pages; p++) {
if (pages > 1) {
var pageColumn = $('<div/>').addClass(className.column).appendTo(pageGrid);
container = pageColumn;
}
var month = startMonth + p;
var firstMonthDayColumn = (new Date(year, month, 1).getDay() - (settings.firstDayOfWeek % 7) + 7) % 7;
if (!settings.constantHeight && isDay) {
var requiredCells = new Date(year, month + 1, 0).getDate() + firstMonthDayColumn;
rows = Math.ceil(requiredCells / 7);
}
var
yearChange = isYear ? 10 : (isMonth ? 1 : 0),
monthChange = isDay ? 1 : 0,
dayChange = isHour || isMinute ? 1 : 0,
prevNextDay = isHour || isMinute ? day : 1,
prevDate = new Date(year - yearChange, month - monthChange, prevNextDay - dayChange, hour),
nextDate = new Date(year + yearChange, month + monthChange, prevNextDay + dayChange, hour),
prevLast = isYear
? new Date(Math.ceil(year / 10) * 10 - 9, 0, 0)
: (isMonth
? new Date(year, 0, 0)
: (isDay // eslint-disable-line unicorn/no-nested-ternary
? new Date(year, month, 0)
: new Date(year, month, day, -1))),
nextFirst = isYear
? new Date(Math.ceil(year / 10) * 10 + 1, 0, 1)
: (isMonth
? new Date(year + 1, 0, 1)
: (isDay // eslint-disable-line unicorn/no-nested-ternary
? new Date(year, month + 1, 1)
: new Date(year, month, day + 1)))
;
var tempMode = mode;
if (isDay && settings.showWeekNumbers) {
tempMode += ' andweek';
}
var table = $('<table/>').addClass(className.table).addClass(tempMode).addClass(numberText[columns] + ' column')
.appendTo(container);
if (isInverted) {
table.addClass(className.inverted);
}
var textColumns = columns;
// no header for time-only mode
if (!isTimeOnly) {
var thead = $('<thead/>').appendTo(table);
row = $('<tr/>').appendTo(thead);
cell = $('<th/>').attr('colspan', '' + columns).appendTo(row);
var headerDate = isYear || isMonth
? new Date(year, 0, 1)
: (isDay
? new Date(year, month, 1)
: new Date(year, month, day, hour, minute));
var headerText = $('<span/>').addClass(className.link).appendTo(cell);
headerText.text(module.helper.dateFormat(formatter[mode + 'Header'], headerDate));
var newMode = isMonth
? (settings.disableYear ? 'day' : 'year')
: (isDay
? (settings.disableMonth ? 'year' : 'month') // eslint-disable-line unicorn/no-nested-ternary
: 'day');
headerText.data(metadata.mode, newMode);
if (p === 0) {
var prev = $('<span/>').addClass(className.prev).appendTo(cell);
prev.data(metadata.focusDate, prevDate);
prev.toggleClass(className.disabledCell, !module.helper.isDateInRange(prevLast, mode));
$('<i/>').addClass(className.prevIcon).appendTo(prev);
}
if (p === pages - 1) {
var next = $('<span/>').addClass(className.next).appendTo(cell);
next.data(metadata.focusDate, nextDate);
next.toggleClass(className.disabledCell, !module.helper.isDateInRange(nextFirst, mode));
$('<i/>').addClass(className.nextIcon).appendTo(next);
}
if (isDay) {
row = $('<tr/>').appendTo(thead);
if (settings.showWeekNumbers) {
cell = $('<th/>').appendTo(row);
cell.text(settings.text.weekNo);
cell.addClass(className.weekCell);
textColumns--;
}
for (i = 0; i < textColumns; i++) {
cell = $('<th/>').appendTo(row);
cell.text(formatter.dayColumnHeader((i + settings.firstDayOfWeek) % 7, settings));
}
}
}
var tbody = $('<tbody/>').appendTo(table);
i = isYear
? Math.ceil(year / 10) * 10 - 9
: (isDay ? 1 - firstMonthDayColumn : 0);
for (r = 0; r < rows; r++) {
row = $('<tr/>').appendTo(tbody);
if (isDay && settings.showWeekNumbers) {
cell = $('<th/>').appendTo(row);
cell.text(module.get.weekOfYear(year, month, i + 1 - settings.firstDayOfWeek));
cell.addClass(className.weekCell);
}
for (c = 0; c < textColumns; c++, i++) {
var cellDate = isYear
? new Date(i, month, 1, hour, minute)
: (isMonth
? new Date(year, i, 1, hour, minute)
: (isDay // eslint-disable-line unicorn/no-nested-ternary
? new Date(year, month, i, hour, minute)
: (isHour
? new Date(year, month, day, i)
: new Date(year, month, day, hour, i * settings.minTimeGap))));
var cellText = isYear
? i
: (isMonth
? settings.text.monthsShort[i]
: (isDay // eslint-disable-line unicorn/no-nested-ternary
? cellDate.getDate()
: module.helper.dateFormat(formatter.cellTime, cellDate)));
cell = $('<td/>').addClass(className.cell).appendTo(row);
cell.text(cellText);
cell.data(metadata.date, cellDate);
var adjacent = isDay && cellDate.getMonth() !== ((month + 12) % 12);
var disabled = (!settings.selectAdjacentDays && adjacent) || !module.helper.isDateInRange(cellDate, mode) || settings.isDisabled(cellDate, mode) || module.helper.isDisabled(cellDate, mode) || !module.helper.isEnabled(cellDate, mode);
var eventDate;
if (disabled) {
var disabledDate = module.helper.findDayAsObject(cellDate, mode, settings.disabledDates);
if (disabledDate !== null && disabledDate[metadata.message]) {
cell.attr('data-tooltip', disabledDate[metadata.message]);
cell.attr('data-position', disabledDate[metadata.position] || tooltipPosition);
if (disabledDate[metadata.inverted] || (isInverted && disabledDate[metadata.inverted] === undefined)) {
cell.attr('data-inverted', '');
}
if (disabledDate[metadata.variation]) {
cell.attr('data-variation', disabledDate[metadata.variation]);
}
}
if (mode === 'hour') {
var disabledHour = module.helper.findHourAsObject(cellDate, mode, settings.disabledHours);
if (disabledHour !== null && disabledHour[metadata.message]) {
cell.attr('data-tooltip', disabledHour[metadata.message]);
cell.attr('data-position', disabledHour[metadata.position] || tooltipPosition);
if (disabledHour[metadata.inverted] || (isInverted && disabledHour[metadata.inverted] === undefined)) {
cell.attr('data-inverted', '');
}
if (disabledHour[metadata.variation]) {
cell.attr('data-variation', disabledHour[metadata.variation]);
}
}
}
} else {
eventDate = module.helper.findDayAsObject(cellDate, mode, settings.eventDates);
if (eventDate !== null) {
cell.addClass(eventDate[metadata.class] || settings.eventClass);
if (eventDate[metadata.message]) {
cell.attr('data-tooltip', eventDate[metadata.message]);
cell.attr('data-position', eventDate[metadata.position] || tooltipPosition);
if (eventDate[metadata.inverted] || (isInverted && eventDate[metadata.inverted] === undefined)) {
cell.attr('data-inverted', '');
}
if (eventDate[metadata.variation]) {
cell.attr('data-variation', eventDate[metadata.variation]);
}
}
}
}
var active = module.helper.dateEqual(cellDate, date, mode);
var isToday = module.helper.dateEqual(cellDate, today, mode);
cell.toggleClass(className.adjacentCell, adjacent && !eventDate);
cell.toggleClass(className.disabledCell, disabled);
cell.toggleClass(className.activeCell, active && !(adjacent && disabled));
if (!isHour && !isMinute) {
cell.toggleClass(className.todayCell, !adjacent && isToday);
}
// Allow for external modifications of each cell
var cellOptions = {
mode: mode,
adjacent: adjacent,
disabled: disabled,
active: active,
today: isToday,
};
formatter.cell(cell, cellDate, cellOptions);
if (module.helper.dateEqual(cellDate, focusDate, mode)) {
// ensure that the focus date is exactly equal to the cell date
// so that, if selected, the correct value is set
module.set.focusDate(cellDate, false, false);
}
}
}
if (settings.today) {
var todayRow = $('<tr/>').appendTo(tbody);
var todayButton = $('<td/>').attr('colspan', '' + columns).addClass(className.today).appendTo(todayRow);
todayButton.text(formatter.today(settings));
todayButton.data(metadata.date, today);
}
module.update.focus(false, table);
if (settings.inline) {
module.refreshTooltips();
}
}
},
},
update: {
focus: function (updateRange, container) {
container = container || $container;
var mode = module.get.mode();
var date = module.get.date();
var focusDate = module.get.focusDate();
var startDate = module.get.startDate();
var endDate = module.get.endDate();
var rangeDate = (updateRange ? focusDate : null) || date || (!isTouch ? focusDate : null);
container.find('td').each(function () {
var $cell = $(this);
var cellDate = $cell.data(metadata.date);
if (!cellDate) {
return;
}
var disabled = $cell.hasClass(className.disabledCell);
var active = $cell.hasClass(className.activeCell);
var adjacent = $cell.hasClass(className.adjacentCell);
var focused = module.helper.dateEqual(cellDate, focusDate, mode);
var inRange = !rangeDate
? false
: (!!startDate && module.helper.isDateInRange(cellDate, mode, startDate, rangeDate))
|| (!!endDate && module.helper.isDateInRange(cellDate, mode, rangeDate, endDate));
$cell.toggleClass(className.focusCell, focused && (!isTouch || isTouchDown) && (!adjacent || (settings.selectAdjacentDays && adjacent)) && !disabled);
if (module.helper.isTodayButton($cell)) {
return;
}
$cell.toggleClass(className.rangeCell, inRange && !active && !disabled);
});
},
},
refresh: function () {
module.create.calendar();
},
refreshTooltips: function () {
var winWidth = $(window).width();
$container.find('td[data-position]').each(function () {
var $cell = $(this);
var tooltipWidth = window.getComputedStyle($cell[0], '::after').width.replace(/[^\d.]/g, '');
var tooltipPosition = $cell.attr('data-position');
// use a fallback width of 250 (calendar width) for IE/Edge (which return "auto")
var calcPosition = (winWidth - $cell.width() - (parseInt(tooltipWidth, 10) || 250)) > $cell.offset().left ? 'right' : 'left';
if (tooltipPosition.indexOf(calcPosition) === -1) {
$cell.attr('data-position', tooltipPosition.replace(/(left|right)/, calcPosition));
}
});
},
bind: {
events: function () {
module.debug('Binding events');
$container.on('mousedown' + eventNamespace, module.event.mousedown);
$container.on('touchstart' + eventNamespace, module.event.mousedown);
$container.on('mouseup' + eventNamespace, module.event.mouseup);
$container.on('touchend' + eventNamespace, module.event.mouseup);
$container.on('mouseover' + eventNamespace, module.event.mouseover);
if ($input.length > 0) {
$input.on('input' + eventNamespace, module.event.inputChange);
$input.on('focus' + eventNamespace, module.event.inputFocus);
$input.on('blur' + eventNamespace, module.event.inputBlur);
$input.on('keydown' + eventNamespace, module.event.keydown);
} else {
$container.on('keydown' + eventNamespace, module.event.keydown);
}
},
},
unbind: {
events: function () {
module.debug('Unbinding events');
$container.off(eventNamespace);
if ($input.length > 0) {
$input.off(eventNamespace);
}
},
},
event: {
mouseover: function (event) {
var target = $(event.target);
var date = target.data(metadata.date);
var mousedown = event.buttons === 1;
if (date) {
module.set.focusDate(date, false, true, mousedown);
}
},
mousedown: function (event) {
if ($input.length > 0) {
// prevent the mousedown on the calendar causing the input to lose focus
event.preventDefault();
}
isTouchDown = event.type.indexOf('touch') >= 0;
var target = $(event.target);
var date = target.data(metadata.date);
if (date) {
module.set.focusDate(date, false, true, true);
}
},
mouseup: function (event) {
// ensure input has focus so that it receives keydown events for calendar navigation
module.focus();
event.preventDefault();
event.stopPropagation();
isTouchDown = false;
var target = $(event.target);
if (target.hasClass('disabled')) {
return;
}
var parent = target.parent();
if (parent.data(metadata.date) || parent.data(metadata.focusDate) || parent.data(metadata.mode)) {
// clicked on a child element, switch to parent (used when clicking directly on prev/next <i> icon element)
target = parent;
}
var date = target.data(metadata.date);
var focusDate = target.data(metadata.focusDate);
var mode = target.data(metadata.mode);
if (date && settings.onSelect.call(element, date, module.get.mode()) !== false) {
var forceSet = target.hasClass(className.today);
module.selectDate(date, forceSet);
} else if (focusDate) {
module.set.focusDate(focusDate);
} else if (mode) {
module.set.mode(mode);
}
},
keydown: function (event) {
var keyCode = event.which;
if (keyCode === 9) {
// tab
module.popup('hide');
}
if (module.popup('is visible')) {
var mode = module.get.mode();
switch (keyCode) {
// arrow keys
case 37:
case 38:
case 39:
case 40: {
var bigIncrement = mode === 'day'
? 7
: (mode === 'hour'
? 4
: (mode === 'minute' ? timeGap.column : 3)); // eslint-disable-line unicorn/no-nested-ternary
var increment = keyCode === 37
? -1
: (keyCode === 38
? -bigIncrement
: (keyCode === 39 ? 1 : bigIncrement)); // eslint-disable-line unicorn/no-nested-ternary
increment *= mode === 'minute' ? settings.minTimeGap : 1;
var focusDate = module.get.focusDate() || module.get.date() || new Date();
var year = focusDate.getFullYear() + (mode === 'year' ? increment : 0);
var month = focusDate.getMonth() + (mode === 'month' ? increment : 0);
var day = focusDate.getDate() + (mode === 'day' ? increment : 0);
var hour = focusDate.getHours() + (mode === 'hour' ? increment : 0);
var minute = focusDate.getMinutes() + (mode === 'minute' ? increment : 0);
var newFocusDate = new Date(year, month, day, hour, minute);
if (settings.type === 'time') {
newFocusDate = module.helper.mergeDateTime(focusDate, newFocusDate);
}
if (module.helper.isDateInRange(newFocusDate, mode)) {
module.set.focusDate(newFocusDate);
}
break;
}
// enter key
case 13: {
var date = module.get.focusDate();
if (date && !settings.isDisabled(date, mode) && !module.helper.isDisabled(date, mode) && module.helper.isEnabled(date, mode)) {
if (settings.onSelect.call(element, date, module.get.mode()) !== false) {
module.selectDate(date);
}
}
// disable form submission:
event.preventDefault();
event.stopPropagation();
break;
}
// escape key
case 27: {
module.popup('hide');
event.stopPropagation();
break;
}
}
}
if (keyCode === 38 || keyCode === 40) {
// arrow-up || arrow-down
event.preventDefault(); // don't scroll
module.popup('show');
}
},
inputChange: function () {
var val = $input.val();
var date = parser.date(val, settings);
module.set.date(date, false);
},
inputFocus: function () {
$container.addClass(className.active);
},
inputBlur: function () {
$container.removeClass(className.active);
if (settings.formatInput) {
var date = module.get.date();
var text = module.helper.dateFormat(formatter[settings.type], date);
$input.val(text);
}
if (selectionComplete) {
module.trigger.change();
selectionComplete = false;
}
},
class: {
mutation: function (mutations) {
mutations.forEach(function (mutation) {
if (mutation.attributeName === 'class') {
module.check.disabled();
}
});
},
},
},
observeChanges: function () {
if ('MutationObserver' in window) {
classObserver = new MutationObserver(module.event.class.mutation);
module.debug('Setting up mutation observer', classObserver);
module.observe.class();
}
},
disconnect: {
classObserver: function () {
if ($input.length > 0 && classObserver) {
classObserver.disconnect();
}
},
},
observe: {
class: function () {
if ($input.length > 0 && classObserver) {
classObserver.observe($module[0], {
attributes: true,
});
}
},
},
is: {
disabled: function () {
return $module.hasClass(className.disabled);
},
},
check: {
disabled: function () {
$input.attr('tabindex', module.is.disabled() ? -1 : 0);
},
},
get: {
weekOfYear: function (weekYear, weekMonth, weekDay) {
// adapted from http://www.merlyn.demon.co.uk/weekcalc.htm
var ms1d = 24 * 3600 * 1000,
ms7d = 7 * ms1d,
DC3 = Date.UTC(weekYear, weekMonth, weekDay + 3) / ms1d, // an absolute day number
AWN = Math.floor(DC3 / 7), // an absolute week number
Wyr = new Date(AWN * ms7d).getUTCFullYear()
;
return AWN - Math.floor(Date.UTC(Wyr, 0, 7) / ms7d) + 1;
},
formattedDate: function (format, date) {
return module.helper.dateFormat(format || formatter[settings.type], date || module.get.date());
},
date: function () {
return module.helper.sanitiseDate($module.data(metadata.date)) || null;
},
inputDate: function () {
return $input.val();
},
focusDate: function () {
return $module.data(metadata.focusDate) || null;
},
startDate: function () {
var startModule = module.get.calendarModule(settings.startCalendar);
return (startModule ? startModule.get.date() : $module.data(metadata.startDate)) || null;
},
endDate: function () {
var endModule = module.get.calendarModule(settings.endCalendar);
return (endModule ? endModule.get.date() : $module.data(metadata.endDate)) || null;
},
minDate: function () {
return $module.data(metadata.minDate) || null;
},
maxDate: function () {
return $module.data(metadata.maxDate) || null;
},
monthOffset: function () {
return $module.data(metadata.monthOffset) || settings.monthOffset || 0;
},
mode: function () {
// only returns valid modes for the current settings
var mode = $module.data(metadata.mode) || settings.startMode;
return module.get.validatedMode(mode);
},
validatedMode: function (mode) {
var validModes = module.get.validModes();
if ($.inArray(mode, validModes) >= 0) {
return mode;
}
return settings.type === 'time'
? 'hour'
: (settings.type === 'month'
? 'month'
: (settings.type === 'year' ? 'year' : 'day')); // eslint-disable-line unicorn/no-nested-ternary
},
type: function () {
return $module.data(metadata.type) || settings.type;
},
validModes: function () {
var validModes = [];
if (settings.type !== 'time') {
if (!settings.disableYear || settings.type === 'year') {
validModes.push('year');
}
if (!(settings.disableMonth || settings.type === 'year') || settings.type === 'month') {
validModes.push('month');
}
if (settings.type.indexOf('date') >= 0) {
validModes.push('day');
}
}
if (settings.type.indexOf('time') >= 0) {
validModes.push('hour');
if (!settings.disableMinute) {
validModes.push('minute');
}
}
return validModes;
},
isTouch: function () {
try {
document.createEvent('TouchEvent');
return true;
} catch (e) {
return false;
}
},
calendarModule: function (selector) {
if (!selector) {
return null;
}
if (!(selector instanceof $)) {
selector = $document.find(selector).first();
}
// assume range related calendars are using the same namespace
return selector.data(moduleNamespace);
},
},
set: {
date: function (date, updateInput, fireChange) {
updateInput = updateInput !== false;
fireChange = fireChange !== false;
date = module.helper.sanitiseDate(date);
date = module.helper.dateInRange(date);
var mode = module.get.mode();
var text = module.helper.dateFormat(formatter[settings.type], date);
if (fireChange && settings.onBeforeChange.call(element, date, text, mode) === false) {
return false;
}
module.set.focusDate(date);
if (settings.isDisabled(date, mode)) {
return false;
}
var endDate = module.get.endDate();
if (!!endDate && !!date && date > endDate) {
// selected date is greater than end date in range, so clear end date
module.set.endDate();
}
module.set.dataKeyValue(metadata.date, date);
if (updateInput && $input.length > 0) {
$input.val(text);
}
if (fireChange) {
settings.onChange.call(element, date, text, mode);
}
},
startDate: function (date, refreshCalendar) {
date = module.helper.sanitiseDate(date);
var startModule = module.get.calendarModule(settings.startCalendar);
if (startModule) {
startModule.set.date(date);
}
module.set.dataKeyValue(metadata.startDate, date, refreshCalendar);
},
endDate: function (date, refreshCalendar) {
date = module.helper.sanitiseDate(date);
var endModule = module.get.calendarModule(settings.endCalendar);
if (endModule) {
endModule.set.date(date);
}
module.set.dataKeyValue(metadata.endDate, date, refreshCalendar);
},
focusDate: function (date, refreshCalendar, updateFocus, updateRange) {
date = module.helper.sanitiseDate(date);
date = module.helper.dateInRange(date);
var isDay = module.get.mode() === 'day';
var oldFocusDate = module.get.focusDate();
if (isDay && date && oldFocusDate) {
var yearDelta = date.getFullYear() - oldFocusDate.getFullYear();
var monthDelta = yearDelta * 12 + date.getMonth() - oldFocusDate.getMonth();
if (monthDelta) {