UNPKG

fomantic-ui

Version:

Fomantic empowers designers and developers by creating a shared vocabulary for UI.

948 lines (865 loc) • 101 kB
/*! * # 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) {