@fullcalendar/common
Version:
internal package
1,503 lines (1,481 loc) • 401 kB
JavaScript
/*!
FullCalendar v5.11.4
Docs & License: https://fullcalendar.io/
(c) 2022 Adam Shaw
*/
'use strict';
Object.defineProperty(exports, '__esModule', { value: true });
;
var tslib = require('tslib');
var vdom_cjs = require('./vdom.cjs');
// no public types yet. when there are, export from:
// import {} from './api-type-deps'
var EventSourceApi = /** @class */ (function () {
function EventSourceApi(context, internalEventSource) {
this.context = context;
this.internalEventSource = internalEventSource;
}
EventSourceApi.prototype.remove = function () {
this.context.dispatch({
type: 'REMOVE_EVENT_SOURCE',
sourceId: this.internalEventSource.sourceId,
});
};
EventSourceApi.prototype.refetch = function () {
this.context.dispatch({
type: 'FETCH_EVENT_SOURCES',
sourceIds: [this.internalEventSource.sourceId],
isRefetch: true,
});
};
Object.defineProperty(EventSourceApi.prototype, "id", {
get: function () {
return this.internalEventSource.publicId;
},
enumerable: false,
configurable: true
});
Object.defineProperty(EventSourceApi.prototype, "url", {
get: function () {
return this.internalEventSource.meta.url;
},
enumerable: false,
configurable: true
});
Object.defineProperty(EventSourceApi.prototype, "format", {
get: function () {
return this.internalEventSource.meta.format; // TODO: bad. not guaranteed
},
enumerable: false,
configurable: true
});
return EventSourceApi;
}());
function removeElement(el) {
if (el.parentNode) {
el.parentNode.removeChild(el);
}
}
// Querying
// ----------------------------------------------------------------------------------------------------------------
function elementClosest(el, selector) {
if (el.closest) {
return el.closest(selector);
// really bad fallback for IE
// from https://developer.mozilla.org/en-US/docs/Web/API/Element/closest
}
if (!document.documentElement.contains(el)) {
return null;
}
do {
if (elementMatches(el, selector)) {
return el;
}
el = (el.parentElement || el.parentNode);
} while (el !== null && el.nodeType === 1);
return null;
}
function elementMatches(el, selector) {
var method = el.matches || el.matchesSelector || el.msMatchesSelector;
return method.call(el, selector);
}
// accepts multiple subject els
// returns a real array. good for methods like forEach
// TODO: accept the document
function findElements(container, selector) {
var containers = container instanceof HTMLElement ? [container] : container;
var allMatches = [];
for (var i = 0; i < containers.length; i += 1) {
var matches = containers[i].querySelectorAll(selector);
for (var j = 0; j < matches.length; j += 1) {
allMatches.push(matches[j]);
}
}
return allMatches;
}
// accepts multiple subject els
// only queries direct child elements // TODO: rename to findDirectChildren!
function findDirectChildren(parent, selector) {
var parents = parent instanceof HTMLElement ? [parent] : parent;
var allMatches = [];
for (var i = 0; i < parents.length; i += 1) {
var childNodes = parents[i].children; // only ever elements
for (var j = 0; j < childNodes.length; j += 1) {
var childNode = childNodes[j];
if (!selector || elementMatches(childNode, selector)) {
allMatches.push(childNode);
}
}
}
return allMatches;
}
// Style
// ----------------------------------------------------------------------------------------------------------------
var PIXEL_PROP_RE = /(top|left|right|bottom|width|height)$/i;
function applyStyle(el, props) {
for (var propName in props) {
applyStyleProp(el, propName, props[propName]);
}
}
function applyStyleProp(el, name, val) {
if (val == null) {
el.style[name] = '';
}
else if (typeof val === 'number' && PIXEL_PROP_RE.test(name)) {
el.style[name] = val + "px";
}
else {
el.style[name] = val;
}
}
// Event Handling
// ----------------------------------------------------------------------------------------------------------------
// if intercepting bubbled events at the document/window/body level,
// and want to see originating element (the 'target'), use this util instead
// of `ev.target` because it goes within web-component boundaries.
function getEventTargetViaRoot(ev) {
var _a, _b;
return (_b = (_a = ev.composedPath) === null || _a === void 0 ? void 0 : _a.call(ev)[0]) !== null && _b !== void 0 ? _b : ev.target;
}
// Shadow DOM consuderations
// ----------------------------------------------------------------------------------------------------------------
function getElRoot(el) {
return el.getRootNode ? el.getRootNode() : document;
}
// Unique ID for DOM attribute
var guid$1 = 0;
function getUniqueDomId() {
guid$1 += 1;
return 'fc-dom-' + guid$1;
}
// Stops a mouse/touch event from doing it's native browser action
function preventDefault(ev) {
ev.preventDefault();
}
// Event Delegation
// ----------------------------------------------------------------------------------------------------------------
function buildDelegationHandler(selector, handler) {
return function (ev) {
var matchedChild = elementClosest(ev.target, selector);
if (matchedChild) {
handler.call(matchedChild, ev, matchedChild);
}
};
}
function listenBySelector(container, eventType, selector, handler) {
var attachedHandler = buildDelegationHandler(selector, handler);
container.addEventListener(eventType, attachedHandler);
return function () {
container.removeEventListener(eventType, attachedHandler);
};
}
function listenToHoverBySelector(container, selector, onMouseEnter, onMouseLeave) {
var currentMatchedChild;
return listenBySelector(container, 'mouseover', selector, function (mouseOverEv, matchedChild) {
if (matchedChild !== currentMatchedChild) {
currentMatchedChild = matchedChild;
onMouseEnter(mouseOverEv, matchedChild);
var realOnMouseLeave_1 = function (mouseLeaveEv) {
currentMatchedChild = null;
onMouseLeave(mouseLeaveEv, matchedChild);
matchedChild.removeEventListener('mouseleave', realOnMouseLeave_1);
};
// listen to the next mouseleave, and then unattach
matchedChild.addEventListener('mouseleave', realOnMouseLeave_1);
}
});
}
// Animation
// ----------------------------------------------------------------------------------------------------------------
var transitionEventNames = [
'webkitTransitionEnd',
'otransitionend',
'oTransitionEnd',
'msTransitionEnd',
'transitionend',
];
// triggered only when the next single subsequent transition finishes
function whenTransitionDone(el, callback) {
var realCallback = function (ev) {
callback(ev);
transitionEventNames.forEach(function (eventName) {
el.removeEventListener(eventName, realCallback);
});
};
transitionEventNames.forEach(function (eventName) {
el.addEventListener(eventName, realCallback); // cross-browser way to determine when the transition finishes
});
}
// ARIA workarounds
// ----------------------------------------------------------------------------------------------------------------
function createAriaClickAttrs(handler) {
return tslib.__assign({ onClick: handler }, createAriaKeyboardAttrs(handler));
}
function createAriaKeyboardAttrs(handler) {
return {
tabIndex: 0,
onKeyDown: function (ev) {
if (ev.key === 'Enter' || ev.key === ' ') {
handler(ev);
ev.preventDefault(); // if space, don't scroll down page
}
},
};
}
var guidNumber = 0;
function guid() {
guidNumber += 1;
return String(guidNumber);
}
/* FullCalendar-specific DOM Utilities
----------------------------------------------------------------------------------------------------------------------*/
// Make the mouse cursor express that an event is not allowed in the current area
function disableCursor() {
document.body.classList.add('fc-not-allowed');
}
// Returns the mouse cursor to its original look
function enableCursor() {
document.body.classList.remove('fc-not-allowed');
}
/* Selection
----------------------------------------------------------------------------------------------------------------------*/
function preventSelection(el) {
el.classList.add('fc-unselectable');
el.addEventListener('selectstart', preventDefault);
}
function allowSelection(el) {
el.classList.remove('fc-unselectable');
el.removeEventListener('selectstart', preventDefault);
}
/* Context Menu
----------------------------------------------------------------------------------------------------------------------*/
function preventContextMenu(el) {
el.addEventListener('contextmenu', preventDefault);
}
function allowContextMenu(el) {
el.removeEventListener('contextmenu', preventDefault);
}
function parseFieldSpecs(input) {
var specs = [];
var tokens = [];
var i;
var token;
if (typeof input === 'string') {
tokens = input.split(/\s*,\s*/);
}
else if (typeof input === 'function') {
tokens = [input];
}
else if (Array.isArray(input)) {
tokens = input;
}
for (i = 0; i < tokens.length; i += 1) {
token = tokens[i];
if (typeof token === 'string') {
specs.push(token.charAt(0) === '-' ?
{ field: token.substring(1), order: -1 } :
{ field: token, order: 1 });
}
else if (typeof token === 'function') {
specs.push({ func: token });
}
}
return specs;
}
function compareByFieldSpecs(obj0, obj1, fieldSpecs) {
var i;
var cmp;
for (i = 0; i < fieldSpecs.length; i += 1) {
cmp = compareByFieldSpec(obj0, obj1, fieldSpecs[i]);
if (cmp) {
return cmp;
}
}
return 0;
}
function compareByFieldSpec(obj0, obj1, fieldSpec) {
if (fieldSpec.func) {
return fieldSpec.func(obj0, obj1);
}
return flexibleCompare(obj0[fieldSpec.field], obj1[fieldSpec.field])
* (fieldSpec.order || 1);
}
function flexibleCompare(a, b) {
if (!a && !b) {
return 0;
}
if (b == null) {
return -1;
}
if (a == null) {
return 1;
}
if (typeof a === 'string' || typeof b === 'string') {
return String(a).localeCompare(String(b));
}
return a - b;
}
/* String Utilities
----------------------------------------------------------------------------------------------------------------------*/
function padStart(val, len) {
var s = String(val);
return '000'.substr(0, len - s.length) + s;
}
function formatWithOrdinals(formatter, args, fallbackText) {
if (typeof formatter === 'function') {
return formatter.apply(void 0, args);
}
if (typeof formatter === 'string') { // non-blank string
return args.reduce(function (str, arg, index) { return (str.replace('$' + index, arg || '')); }, formatter);
}
return fallbackText;
}
/* Number Utilities
----------------------------------------------------------------------------------------------------------------------*/
function compareNumbers(a, b) {
return a - b;
}
function isInt(n) {
return n % 1 === 0;
}
/* FC-specific DOM dimension stuff
----------------------------------------------------------------------------------------------------------------------*/
function computeSmallestCellWidth(cellEl) {
var allWidthEl = cellEl.querySelector('.fc-scrollgrid-shrink-frame');
var contentWidthEl = cellEl.querySelector('.fc-scrollgrid-shrink-cushion');
if (!allWidthEl) {
throw new Error('needs fc-scrollgrid-shrink-frame className'); // TODO: use const
}
if (!contentWidthEl) {
throw new Error('needs fc-scrollgrid-shrink-cushion className');
}
return cellEl.getBoundingClientRect().width - allWidthEl.getBoundingClientRect().width + // the cell padding+border
contentWidthEl.getBoundingClientRect().width;
}
var DAY_IDS = ['sun', 'mon', 'tue', 'wed', 'thu', 'fri', 'sat'];
// Adding
function addWeeks(m, n) {
var a = dateToUtcArray(m);
a[2] += n * 7;
return arrayToUtcDate(a);
}
function addDays(m, n) {
var a = dateToUtcArray(m);
a[2] += n;
return arrayToUtcDate(a);
}
function addMs(m, n) {
var a = dateToUtcArray(m);
a[6] += n;
return arrayToUtcDate(a);
}
// Diffing (all return floats)
// TODO: why not use ranges?
function diffWeeks(m0, m1) {
return diffDays(m0, m1) / 7;
}
function diffDays(m0, m1) {
return (m1.valueOf() - m0.valueOf()) / (1000 * 60 * 60 * 24);
}
function diffHours(m0, m1) {
return (m1.valueOf() - m0.valueOf()) / (1000 * 60 * 60);
}
function diffMinutes(m0, m1) {
return (m1.valueOf() - m0.valueOf()) / (1000 * 60);
}
function diffSeconds(m0, m1) {
return (m1.valueOf() - m0.valueOf()) / 1000;
}
function diffDayAndTime(m0, m1) {
var m0day = startOfDay(m0);
var m1day = startOfDay(m1);
return {
years: 0,
months: 0,
days: Math.round(diffDays(m0day, m1day)),
milliseconds: (m1.valueOf() - m1day.valueOf()) - (m0.valueOf() - m0day.valueOf()),
};
}
// Diffing Whole Units
function diffWholeWeeks(m0, m1) {
var d = diffWholeDays(m0, m1);
if (d !== null && d % 7 === 0) {
return d / 7;
}
return null;
}
function diffWholeDays(m0, m1) {
if (timeAsMs(m0) === timeAsMs(m1)) {
return Math.round(diffDays(m0, m1));
}
return null;
}
// Start-Of
function startOfDay(m) {
return arrayToUtcDate([
m.getUTCFullYear(),
m.getUTCMonth(),
m.getUTCDate(),
]);
}
function startOfHour(m) {
return arrayToUtcDate([
m.getUTCFullYear(),
m.getUTCMonth(),
m.getUTCDate(),
m.getUTCHours(),
]);
}
function startOfMinute(m) {
return arrayToUtcDate([
m.getUTCFullYear(),
m.getUTCMonth(),
m.getUTCDate(),
m.getUTCHours(),
m.getUTCMinutes(),
]);
}
function startOfSecond(m) {
return arrayToUtcDate([
m.getUTCFullYear(),
m.getUTCMonth(),
m.getUTCDate(),
m.getUTCHours(),
m.getUTCMinutes(),
m.getUTCSeconds(),
]);
}
// Week Computation
function weekOfYear(marker, dow, doy) {
var y = marker.getUTCFullYear();
var w = weekOfGivenYear(marker, y, dow, doy);
if (w < 1) {
return weekOfGivenYear(marker, y - 1, dow, doy);
}
var nextW = weekOfGivenYear(marker, y + 1, dow, doy);
if (nextW >= 1) {
return Math.min(w, nextW);
}
return w;
}
function weekOfGivenYear(marker, year, dow, doy) {
var firstWeekStart = arrayToUtcDate([year, 0, 1 + firstWeekOffset(year, dow, doy)]);
var dayStart = startOfDay(marker);
var days = Math.round(diffDays(firstWeekStart, dayStart));
return Math.floor(days / 7) + 1; // zero-indexed
}
// start-of-first-week - start-of-year
function firstWeekOffset(year, dow, doy) {
// first-week day -- which january is always in the first week (4 for iso, 1 for other)
var fwd = 7 + dow - doy;
// first-week day local weekday -- which local weekday is fwd
var fwdlw = (7 + arrayToUtcDate([year, 0, fwd]).getUTCDay() - dow) % 7;
return -fwdlw + fwd - 1;
}
// Array Conversion
function dateToLocalArray(date) {
return [
date.getFullYear(),
date.getMonth(),
date.getDate(),
date.getHours(),
date.getMinutes(),
date.getSeconds(),
date.getMilliseconds(),
];
}
function arrayToLocalDate(a) {
return new Date(a[0], a[1] || 0, a[2] == null ? 1 : a[2], // day of month
a[3] || 0, a[4] || 0, a[5] || 0);
}
function dateToUtcArray(date) {
return [
date.getUTCFullYear(),
date.getUTCMonth(),
date.getUTCDate(),
date.getUTCHours(),
date.getUTCMinutes(),
date.getUTCSeconds(),
date.getUTCMilliseconds(),
];
}
function arrayToUtcDate(a) {
// according to web standards (and Safari), a month index is required.
// massage if only given a year.
if (a.length === 1) {
a = a.concat([0]);
}
return new Date(Date.UTC.apply(Date, a));
}
// Other Utils
function isValidDate(m) {
return !isNaN(m.valueOf());
}
function timeAsMs(m) {
return m.getUTCHours() * 1000 * 60 * 60 +
m.getUTCMinutes() * 1000 * 60 +
m.getUTCSeconds() * 1000 +
m.getUTCMilliseconds();
}
function createEventInstance(defId, range, forcedStartTzo, forcedEndTzo) {
return {
instanceId: guid(),
defId: defId,
range: range,
forcedStartTzo: forcedStartTzo == null ? null : forcedStartTzo,
forcedEndTzo: forcedEndTzo == null ? null : forcedEndTzo,
};
}
var hasOwnProperty = Object.prototype.hasOwnProperty;
// Merges an array of objects into a single object.
// The second argument allows for an array of property names who's object values will be merged together.
function mergeProps(propObjs, complexPropsMap) {
var dest = {};
if (complexPropsMap) {
for (var name_1 in complexPropsMap) {
var complexObjs = [];
// collect the trailing object values, stopping when a non-object is discovered
for (var i = propObjs.length - 1; i >= 0; i -= 1) {
var val = propObjs[i][name_1];
if (typeof val === 'object' && val) { // non-null object
complexObjs.unshift(val);
}
else if (val !== undefined) {
dest[name_1] = val; // if there were no objects, this value will be used
break;
}
}
// if the trailing values were objects, use the merged value
if (complexObjs.length) {
dest[name_1] = mergeProps(complexObjs);
}
}
}
// copy values into the destination, going from last to first
for (var i = propObjs.length - 1; i >= 0; i -= 1) {
var props = propObjs[i];
for (var name_2 in props) {
if (!(name_2 in dest)) { // if already assigned by previous props or complex props, don't reassign
dest[name_2] = props[name_2];
}
}
}
return dest;
}
function filterHash(hash, func) {
var filtered = {};
for (var key in hash) {
if (func(hash[key], key)) {
filtered[key] = hash[key];
}
}
return filtered;
}
function mapHash(hash, func) {
var newHash = {};
for (var key in hash) {
newHash[key] = func(hash[key], key);
}
return newHash;
}
function arrayToHash(a) {
var hash = {};
for (var _i = 0, a_1 = a; _i < a_1.length; _i++) {
var item = a_1[_i];
hash[item] = true;
}
return hash;
}
function buildHashFromArray(a, func) {
var hash = {};
for (var i = 0; i < a.length; i += 1) {
var tuple = func(a[i], i);
hash[tuple[0]] = tuple[1];
}
return hash;
}
function hashValuesToArray(obj) {
var a = [];
for (var key in obj) {
a.push(obj[key]);
}
return a;
}
function isPropsEqual(obj0, obj1) {
if (obj0 === obj1) {
return true;
}
for (var key in obj0) {
if (hasOwnProperty.call(obj0, key)) {
if (!(key in obj1)) {
return false;
}
}
}
for (var key in obj1) {
if (hasOwnProperty.call(obj1, key)) {
if (obj0[key] !== obj1[key]) {
return false;
}
}
}
return true;
}
function getUnequalProps(obj0, obj1) {
var keys = [];
for (var key in obj0) {
if (hasOwnProperty.call(obj0, key)) {
if (!(key in obj1)) {
keys.push(key);
}
}
}
for (var key in obj1) {
if (hasOwnProperty.call(obj1, key)) {
if (obj0[key] !== obj1[key]) {
keys.push(key);
}
}
}
return keys;
}
function compareObjs(oldProps, newProps, equalityFuncs) {
if (equalityFuncs === void 0) { equalityFuncs = {}; }
if (oldProps === newProps) {
return true;
}
for (var key in newProps) {
if (key in oldProps && isObjValsEqual(oldProps[key], newProps[key], equalityFuncs[key])) ;
else {
return false;
}
}
// check for props that were omitted in the new
for (var key in oldProps) {
if (!(key in newProps)) {
return false;
}
}
return true;
}
/*
assumed "true" equality for handler names like "onReceiveSomething"
*/
function isObjValsEqual(val0, val1, comparator) {
if (val0 === val1 || comparator === true) {
return true;
}
if (comparator) {
return comparator(val0, val1);
}
return false;
}
function collectFromHash(hash, startIndex, endIndex, step) {
if (startIndex === void 0) { startIndex = 0; }
if (step === void 0) { step = 1; }
var res = [];
if (endIndex == null) {
endIndex = Object.keys(hash).length;
}
for (var i = startIndex; i < endIndex; i += step) {
var val = hash[i];
if (val !== undefined) { // will disregard undefined for sparse arrays
res.push(val);
}
}
return res;
}
function parseRecurring(refined, defaultAllDay, dateEnv, recurringTypes) {
for (var i = 0; i < recurringTypes.length; i += 1) {
var parsed = recurringTypes[i].parse(refined, dateEnv);
if (parsed) {
var allDay = refined.allDay;
if (allDay == null) {
allDay = defaultAllDay;
if (allDay == null) {
allDay = parsed.allDayGuess;
if (allDay == null) {
allDay = false;
}
}
}
return {
allDay: allDay,
duration: parsed.duration,
typeData: parsed.typeData,
typeId: i,
};
}
}
return null;
}
function expandRecurring(eventStore, framingRange, context) {
var dateEnv = context.dateEnv, pluginHooks = context.pluginHooks, options = context.options;
var defs = eventStore.defs, instances = eventStore.instances;
// remove existing recurring instances
// TODO: bad. always expand events as a second step
instances = filterHash(instances, function (instance) { return !defs[instance.defId].recurringDef; });
for (var defId in defs) {
var def = defs[defId];
if (def.recurringDef) {
var duration = def.recurringDef.duration;
if (!duration) {
duration = def.allDay ?
options.defaultAllDayEventDuration :
options.defaultTimedEventDuration;
}
var starts = expandRecurringRanges(def, duration, framingRange, dateEnv, pluginHooks.recurringTypes);
for (var _i = 0, starts_1 = starts; _i < starts_1.length; _i++) {
var start = starts_1[_i];
var instance = createEventInstance(defId, {
start: start,
end: dateEnv.add(start, duration),
});
instances[instance.instanceId] = instance;
}
}
}
return { defs: defs, instances: instances };
}
/*
Event MUST have a recurringDef
*/
function expandRecurringRanges(eventDef, duration, framingRange, dateEnv, recurringTypes) {
var typeDef = recurringTypes[eventDef.recurringDef.typeId];
var markers = typeDef.expand(eventDef.recurringDef.typeData, {
start: dateEnv.subtract(framingRange.start, duration),
end: framingRange.end,
}, dateEnv);
// the recurrence plugins don't guarantee that all-day events are start-of-day, so we have to
if (eventDef.allDay) {
markers = markers.map(startOfDay);
}
return markers;
}
var INTERNAL_UNITS = ['years', 'months', 'days', 'milliseconds'];
var PARSE_RE = /^(-?)(?:(\d+)\.)?(\d+):(\d\d)(?::(\d\d)(?:\.(\d\d\d))?)?/;
// Parsing and Creation
function createDuration(input, unit) {
var _a;
if (typeof input === 'string') {
return parseString(input);
}
if (typeof input === 'object' && input) { // non-null object
return parseObject(input);
}
if (typeof input === 'number') {
return parseObject((_a = {}, _a[unit || 'milliseconds'] = input, _a));
}
return null;
}
function parseString(s) {
var m = PARSE_RE.exec(s);
if (m) {
var sign = m[1] ? -1 : 1;
return {
years: 0,
months: 0,
days: sign * (m[2] ? parseInt(m[2], 10) : 0),
milliseconds: sign * ((m[3] ? parseInt(m[3], 10) : 0) * 60 * 60 * 1000 + // hours
(m[4] ? parseInt(m[4], 10) : 0) * 60 * 1000 + // minutes
(m[5] ? parseInt(m[5], 10) : 0) * 1000 + // seconds
(m[6] ? parseInt(m[6], 10) : 0) // ms
),
};
}
return null;
}
function parseObject(obj) {
var duration = {
years: obj.years || obj.year || 0,
months: obj.months || obj.month || 0,
days: obj.days || obj.day || 0,
milliseconds: (obj.hours || obj.hour || 0) * 60 * 60 * 1000 + // hours
(obj.minutes || obj.minute || 0) * 60 * 1000 + // minutes
(obj.seconds || obj.second || 0) * 1000 + // seconds
(obj.milliseconds || obj.millisecond || obj.ms || 0), // ms
};
var weeks = obj.weeks || obj.week;
if (weeks) {
duration.days += weeks * 7;
duration.specifiedWeeks = true;
}
return duration;
}
// Equality
function durationsEqual(d0, d1) {
return d0.years === d1.years &&
d0.months === d1.months &&
d0.days === d1.days &&
d0.milliseconds === d1.milliseconds;
}
function asCleanDays(dur) {
if (!dur.years && !dur.months && !dur.milliseconds) {
return dur.days;
}
return 0;
}
// Simple Math
function addDurations(d0, d1) {
return {
years: d0.years + d1.years,
months: d0.months + d1.months,
days: d0.days + d1.days,
milliseconds: d0.milliseconds + d1.milliseconds,
};
}
function subtractDurations(d1, d0) {
return {
years: d1.years - d0.years,
months: d1.months - d0.months,
days: d1.days - d0.days,
milliseconds: d1.milliseconds - d0.milliseconds,
};
}
function multiplyDuration(d, n) {
return {
years: d.years * n,
months: d.months * n,
days: d.days * n,
milliseconds: d.milliseconds * n,
};
}
// Conversions
// "Rough" because they are based on average-case Gregorian months/years
function asRoughYears(dur) {
return asRoughDays(dur) / 365;
}
function asRoughMonths(dur) {
return asRoughDays(dur) / 30;
}
function asRoughDays(dur) {
return asRoughMs(dur) / 864e5;
}
function asRoughMinutes(dur) {
return asRoughMs(dur) / (1000 * 60);
}
function asRoughSeconds(dur) {
return asRoughMs(dur) / 1000;
}
function asRoughMs(dur) {
return dur.years * (365 * 864e5) +
dur.months * (30 * 864e5) +
dur.days * 864e5 +
dur.milliseconds;
}
// Advanced Math
function wholeDivideDurations(numerator, denominator) {
var res = null;
for (var i = 0; i < INTERNAL_UNITS.length; i += 1) {
var unit = INTERNAL_UNITS[i];
if (denominator[unit]) {
var localRes = numerator[unit] / denominator[unit];
if (!isInt(localRes) || (res !== null && res !== localRes)) {
return null;
}
res = localRes;
}
else if (numerator[unit]) {
// needs to divide by something but can't!
return null;
}
}
return res;
}
function greatestDurationDenominator(dur) {
var ms = dur.milliseconds;
if (ms) {
if (ms % 1000 !== 0) {
return { unit: 'millisecond', value: ms };
}
if (ms % (1000 * 60) !== 0) {
return { unit: 'second', value: ms / 1000 };
}
if (ms % (1000 * 60 * 60) !== 0) {
return { unit: 'minute', value: ms / (1000 * 60) };
}
if (ms) {
return { unit: 'hour', value: ms / (1000 * 60 * 60) };
}
}
if (dur.days) {
if (dur.specifiedWeeks && dur.days % 7 === 0) {
return { unit: 'week', value: dur.days / 7 };
}
return { unit: 'day', value: dur.days };
}
if (dur.months) {
return { unit: 'month', value: dur.months };
}
if (dur.years) {
return { unit: 'year', value: dur.years };
}
return { unit: 'millisecond', value: 0 };
}
// timeZoneOffset is in minutes
function buildIsoString(marker, timeZoneOffset, stripZeroTime) {
if (stripZeroTime === void 0) { stripZeroTime = false; }
var s = marker.toISOString();
s = s.replace('.000', '');
if (stripZeroTime) {
s = s.replace('T00:00:00Z', '');
}
if (s.length > 10) { // time part wasn't stripped, can add timezone info
if (timeZoneOffset == null) {
s = s.replace('Z', '');
}
else if (timeZoneOffset !== 0) {
s = s.replace('Z', formatTimeZoneOffset(timeZoneOffset, true));
}
// otherwise, its UTC-0 and we want to keep the Z
}
return s;
}
// formats the date, but with no time part
// TODO: somehow merge with buildIsoString and stripZeroTime
// TODO: rename. omit "string"
function formatDayString(marker) {
return marker.toISOString().replace(/T.*$/, '');
}
// TODO: use Date::toISOString and use everything after the T?
function formatIsoTimeString(marker) {
return padStart(marker.getUTCHours(), 2) + ':' +
padStart(marker.getUTCMinutes(), 2) + ':' +
padStart(marker.getUTCSeconds(), 2);
}
function formatTimeZoneOffset(minutes, doIso) {
if (doIso === void 0) { doIso = false; }
var sign = minutes < 0 ? '-' : '+';
var abs = Math.abs(minutes);
var hours = Math.floor(abs / 60);
var mins = Math.round(abs % 60);
if (doIso) {
return sign + padStart(hours, 2) + ":" + padStart(mins, 2);
}
return "GMT" + sign + hours + (mins ? ":" + padStart(mins, 2) : '');
}
// TODO: new util arrayify?
function removeExact(array, exactVal) {
var removeCnt = 0;
var i = 0;
while (i < array.length) {
if (array[i] === exactVal) {
array.splice(i, 1);
removeCnt += 1;
}
else {
i += 1;
}
}
return removeCnt;
}
function isArraysEqual(a0, a1, equalityFunc) {
if (a0 === a1) {
return true;
}
var len = a0.length;
var i;
if (len !== a1.length) { // not array? or not same length?
return false;
}
for (i = 0; i < len; i += 1) {
if (!(equalityFunc ? equalityFunc(a0[i], a1[i]) : a0[i] === a1[i])) {
return false;
}
}
return true;
}
function memoize(workerFunc, resEquality, teardownFunc) {
var currentArgs;
var currentRes;
return function () {
var newArgs = [];
for (var _i = 0; _i < arguments.length; _i++) {
newArgs[_i] = arguments[_i];
}
if (!currentArgs) {
currentRes = workerFunc.apply(this, newArgs);
}
else if (!isArraysEqual(currentArgs, newArgs)) {
if (teardownFunc) {
teardownFunc(currentRes);
}
var res = workerFunc.apply(this, newArgs);
if (!resEquality || !resEquality(res, currentRes)) {
currentRes = res;
}
}
currentArgs = newArgs;
return currentRes;
};
}
function memoizeObjArg(workerFunc, resEquality, teardownFunc) {
var _this = this;
var currentArg;
var currentRes;
return function (newArg) {
if (!currentArg) {
currentRes = workerFunc.call(_this, newArg);
}
else if (!isPropsEqual(currentArg, newArg)) {
if (teardownFunc) {
teardownFunc(currentRes);
}
var res = workerFunc.call(_this, newArg);
if (!resEquality || !resEquality(res, currentRes)) {
currentRes = res;
}
}
currentArg = newArg;
return currentRes;
};
}
function memoizeArraylike(// used at all?
workerFunc, resEquality, teardownFunc) {
var _this = this;
var currentArgSets = [];
var currentResults = [];
return function (newArgSets) {
var currentLen = currentArgSets.length;
var newLen = newArgSets.length;
var i = 0;
for (; i < currentLen; i += 1) {
if (!newArgSets[i]) { // one of the old sets no longer exists
if (teardownFunc) {
teardownFunc(currentResults[i]);
}
}
else if (!isArraysEqual(currentArgSets[i], newArgSets[i])) {
if (teardownFunc) {
teardownFunc(currentResults[i]);
}
var res = workerFunc.apply(_this, newArgSets[i]);
if (!resEquality || !resEquality(res, currentResults[i])) {
currentResults[i] = res;
}
}
}
for (; i < newLen; i += 1) {
currentResults[i] = workerFunc.apply(_this, newArgSets[i]);
}
currentArgSets = newArgSets;
currentResults.splice(newLen); // remove excess
return currentResults;
};
}
function memoizeHashlike(workerFunc, resEquality, teardownFunc) {
var _this = this;
var currentArgHash = {};
var currentResHash = {};
return function (newArgHash) {
var newResHash = {};
for (var key in newArgHash) {
if (!currentResHash[key]) {
newResHash[key] = workerFunc.apply(_this, newArgHash[key]);
}
else if (!isArraysEqual(currentArgHash[key], newArgHash[key])) {
if (teardownFunc) {
teardownFunc(currentResHash[key]);
}
var res = workerFunc.apply(_this, newArgHash[key]);
newResHash[key] = (resEquality && resEquality(res, currentResHash[key]))
? currentResHash[key]
: res;
}
else {
newResHash[key] = currentResHash[key];
}
}
currentArgHash = newArgHash;
currentResHash = newResHash;
return newResHash;
};
}
var EXTENDED_SETTINGS_AND_SEVERITIES = {
week: 3,
separator: 0,
omitZeroMinute: 0,
meridiem: 0,
omitCommas: 0,
};
var STANDARD_DATE_PROP_SEVERITIES = {
timeZoneName: 7,
era: 6,
year: 5,
month: 4,
day: 2,
weekday: 2,
hour: 1,
minute: 1,
second: 1,
};
var MERIDIEM_RE = /\s*([ap])\.?m\.?/i; // eats up leading spaces too
var COMMA_RE = /,/g; // we need re for globalness
var MULTI_SPACE_RE = /\s+/g;
var LTR_RE = /\u200e/g; // control character
var UTC_RE = /UTC|GMT/;
var NativeFormatter = /** @class */ (function () {
function NativeFormatter(formatSettings) {
var standardDateProps = {};
var extendedSettings = {};
var severity = 0;
for (var name_1 in formatSettings) {
if (name_1 in EXTENDED_SETTINGS_AND_SEVERITIES) {
extendedSettings[name_1] = formatSettings[name_1];
severity = Math.max(EXTENDED_SETTINGS_AND_SEVERITIES[name_1], severity);
}
else {
standardDateProps[name_1] = formatSettings[name_1];
if (name_1 in STANDARD_DATE_PROP_SEVERITIES) { // TODO: what about hour12? no severity
severity = Math.max(STANDARD_DATE_PROP_SEVERITIES[name_1], severity);
}
}
}
this.standardDateProps = standardDateProps;
this.extendedSettings = extendedSettings;
this.severity = severity;
this.buildFormattingFunc = memoize(buildFormattingFunc);
}
NativeFormatter.prototype.format = function (date, context) {
return this.buildFormattingFunc(this.standardDateProps, this.extendedSettings, context)(date);
};
NativeFormatter.prototype.formatRange = function (start, end, context, betterDefaultSeparator) {
var _a = this, standardDateProps = _a.standardDateProps, extendedSettings = _a.extendedSettings;
var diffSeverity = computeMarkerDiffSeverity(start.marker, end.marker, context.calendarSystem);
if (!diffSeverity) {
return this.format(start, context);
}
var biggestUnitForPartial = diffSeverity;
if (biggestUnitForPartial > 1 && // the two dates are different in a way that's larger scale than time
(standardDateProps.year === 'numeric' || standardDateProps.year === '2-digit') &&
(standardDateProps.month === 'numeric' || standardDateProps.month === '2-digit') &&
(standardDateProps.day === 'numeric' || standardDateProps.day === '2-digit')) {
biggestUnitForPartial = 1; // make it look like the dates are only different in terms of time
}
var full0 = this.format(start, context);
var full1 = this.format(end, context);
if (full0 === full1) {
return full0;
}
var partialDateProps = computePartialFormattingOptions(standardDateProps, biggestUnitForPartial);
var partialFormattingFunc = buildFormattingFunc(partialDateProps, extendedSettings, context);
var partial0 = partialFormattingFunc(start);
var partial1 = partialFormattingFunc(end);
var insertion = findCommonInsertion(full0, partial0, full1, partial1);
var separator = extendedSettings.separator || betterDefaultSeparator || context.defaultSeparator || '';
if (insertion) {
return insertion.before + partial0 + separator + partial1 + insertion.after;
}
return full0 + separator + full1;
};
NativeFormatter.prototype.getLargestUnit = function () {
switch (this.severity) {
case 7:
case 6:
case 5:
return 'year';
case 4:
return 'month';
case 3:
return 'week';
case 2:
return 'day';
default:
return 'time'; // really?
}
};
return NativeFormatter;
}());
function buildFormattingFunc(standardDateProps, extendedSettings, context) {
var standardDatePropCnt = Object.keys(standardDateProps).length;
if (standardDatePropCnt === 1 && standardDateProps.timeZoneName === 'short') {
return function (date) { return (formatTimeZoneOffset(date.timeZoneOffset)); };
}
if (standardDatePropCnt === 0 && extendedSettings.week) {
return function (date) { return (formatWeekNumber(context.computeWeekNumber(date.marker), context.weekText, context.weekTextLong, context.locale, extendedSettings.week)); };
}
return buildNativeFormattingFunc(standardDateProps, extendedSettings, context);
}
function buildNativeFormattingFunc(standardDateProps, extendedSettings, context) {
standardDateProps = tslib.__assign({}, standardDateProps); // copy
extendedSettings = tslib.__assign({}, extendedSettings); // copy
sanitizeSettings(standardDateProps, extendedSettings);
standardDateProps.timeZone = 'UTC'; // we leverage the only guaranteed timeZone for our UTC markers
var normalFormat = new Intl.DateTimeFormat(context.locale.codes, standardDateProps);
var zeroFormat; // needed?
if (extendedSettings.omitZeroMinute) {
var zeroProps = tslib.__assign({}, standardDateProps);
delete zeroProps.minute; // seconds and ms were already considered in sanitizeSettings
zeroFormat = new Intl.DateTimeFormat(context.locale.codes, zeroProps);
}
return function (date) {
var marker = date.marker;
var format;
if (zeroFormat && !marker.getUTCMinutes()) {
format = zeroFormat;
}
else {
format = normalFormat;
}
var s = format.format(marker);
return postProcess(s, date, standardDateProps, extendedSettings, context);
};
}
function sanitizeSettings(standardDateProps, extendedSettings) {
// deal with a browser inconsistency where formatting the timezone
// requires that the hour/minute be present.
if (standardDateProps.timeZoneName) {
if (!standardDateProps.hour) {
standardDateProps.hour = '2-digit';
}
if (!standardDateProps.minute) {
standardDateProps.minute = '2-digit';
}
}
// only support short timezone names
if (standardDateProps.timeZoneName === 'long') {
standardDateProps.timeZoneName = 'short';
}
// if requesting to display seconds, MUST display minutes
if (extendedSettings.omitZeroMinute && (standardDateProps.second || standardDateProps.millisecond)) {
delete extendedSettings.omitZeroMinute;
}
}
function postProcess(s, date, standardDateProps, extendedSettings, context) {
s = s.replace(LTR_RE, ''); // remove left-to-right control chars. do first. good for other regexes
if (standardDateProps.timeZoneName === 'short') {
s = injectTzoStr(s, (context.timeZone === 'UTC' || date.timeZoneOffset == null) ?
'UTC' : // important to normalize for IE, which does "GMT"
formatTimeZoneOffset(date.timeZoneOffset));
}
if (extendedSettings.omitCommas) {
s = s.replace(COMMA_RE, '').trim();
}
if (extendedSettings.omitZeroMinute) {
s = s.replace(':00', ''); // zeroFormat doesn't always achieve this
}
// ^ do anything that might create adjacent spaces before this point,
// because MERIDIEM_RE likes to eat up loading spaces
if (extendedSettings.meridiem === false) {
s = s.replace(MERIDIEM_RE, '').trim();
}
else if (extendedSettings.meridiem === 'narrow') { // a/p
s = s.replace(MERIDIEM_RE, function (m0, m1) { return m1.toLocaleLowerCase(); });
}
else if (extendedSettings.meridiem === 'short') { // am/pm
s = s.replace(MERIDIEM_RE, function (m0, m1) { return m1.toLocaleLowerCase() + "m"; });
}
else if (extendedSettings.meridiem === 'lowercase') { // other meridiem transformers already converted to lowercase
s = s.replace(MERIDIEM_RE, function (m0) { return m0.toLocaleLowerCase(); });
}
s = s.replace(MULTI_SPACE_RE, ' ');
s = s.trim();
return s;
}
function injectTzoStr(s, tzoStr) {
var replaced = false;
s = s.replace(UTC_RE, function () {
replaced = true;
return tzoStr;
});
// IE11 doesn't include UTC/GMT in the original string, so append to end
if (!replaced) {
s += " " + tzoStr;
}
return s;
}
function formatWeekNumber(num, weekText, weekTextLong, locale, display) {
var parts = [];
if (display === 'long') {
parts.push(weekTextLong);
}
else if (display === 'short' || display === 'narrow') {
parts.push(weekText);
}
if (display === 'long' || display === 'short') {
parts.push(' ');
}
parts.push(locale.simpleNumberFormat.format(num));
if (locale.options.direction === 'rtl') { // TODO: use control characters instead?
parts.reverse();
}
return parts.join('');
}
// Range Formatting Utils
// 0 = exactly the same
// 1 = different by time
// and bigger
function computeMarkerDiffSeverity(d0, d1, ca) {
if (ca.getMarkerYear(d0) !== ca.getMarkerYear(d1)) {
return 5;
}
if (ca.getMarkerMonth(d0) !== ca.getMarkerMonth(d1)) {
return 4;
}
if (ca.getMarkerDay(d0) !== ca.getMarkerDay(d1)) {
return 2;
}
if (timeAsMs(d0) !== timeAsMs(d1)) {
return 1;
}
return 0;
}
function computePartialFormattingOptions(options, biggestUnit) {
var partialOptions = {};
for (var name_2 in options) {
if (!(name_2 in STANDARD_DATE_PROP_SEVERITIES) || // not a date part prop (like timeZone)
STANDARD_DATE_PROP_SEVERITIES[name_2] <= biggestUnit) {
partialOptions[name_2] = options[name_2];
}
}
return partialOptions;
}
function findCommonInsertion(full0, partial0, full1, partial1) {
var i0 = 0;
while (i0 < full0.length) {
var found0 = full0.indexOf(partial0, i0);
if (found0 === -1) {
break;
}
var before0 = full0.substr(0, found0);
i0 = found0 + partial0.length;
var after0 = full0.substr(i0);
var i1 = 0;
while (i1 < full1.length) {
var found1 = full1.indexOf(partial1, i1);
if (found1 === -1) {
break;
}
var before1 = full1.substr(0, found1);
i1 = found1 + partial1.length;
var after1 = full1.substr(i1);
if (before0 === before1 && after0 === after1) {
return {
before: before0,
after: after0,
};
}
}
}
return null;
}
function expandZonedMarker(dateInfo, calendarSystem) {
var a = calendarSystem.markerToArray(dateInfo.marker);
return {
marker: dateInfo.marker,
timeZoneOffset: dateInfo.timeZoneOffset,
array: a,
year: a[0],
month: a[1],
day: a[2],
hour: a[3],
minute: a[4],
second: a[5],
millisecond: a[6],
};
}
function createVerboseFormattingArg(start, end, context, betterDefaultSeparator) {
var startInfo = expandZonedMarker(start, context.calendarSystem);
var endInfo = end ? expandZonedMarker(end, context.calendarSystem) : null;
return {
date: startInfo,
start: startInfo,
end: endInfo,
timeZone: context.timeZone,
localeCodes: context.locale.codes,
defaultSeparator: betterDefaultSeparator || context.defaultSeparator,
};
}
/*
TODO: fix the terminology of "formatter" vs "formatting func"
*/
/*
At the time of instantiation, this object does not know which cmd-formatting system it will use.
It receives this at the time of formatting, as a setting.
*/
var CmdFormatter = /** @class */ (function () {
function CmdFormatter(cmdStr) {
this.cmdStr = cmdStr;
}
CmdFormatter.prototype.format = function (date, context, betterDefaultSeparator) {
return context.cmdFormatter(this.cmdStr, createVerboseFormattingArg(date, null, context, betterDefaultSeparator));
};
CmdFormatter.prototype.formatRange = function (start, end, context, betterDefaultSeparator) {
return context.cmdFormatter(this.cmdStr, createVerboseFormattingArg(start, end, context, betterDefaultSeparator));
};
return CmdFormatter;
}());
var FuncFormatter = /** @class */ (function () {
function FuncFormatter(func) {
this.func = func;
}
FuncFormatter.prototype.format = function (date, context, betterDefaultSeparator) {
return this.func(createVerboseFormattingArg(date, null, context, betterDefaultSeparator));
};
FuncFormatter.prototype.formatRange = function (start, end, context, betterDefaultSeparator) {
return this.func(createVerboseFormattingArg(start, end, context, betterDefaultSeparator));
};
return FuncFormatter;
}());
function createFormatter(input) {
if (typeof input === 'object' && input) { // non-null object
return new NativeFormatter(input);
}
if (typeof input === 'string') {
return new CmdFormatter(input);
}
if (typeof input === 'function') {
return new FuncFormatter(input);
}
return null;
}
// base options
// ------------
var BASE_OPTION_REFINERS = {
navLinkDayClick: identity,
navLinkWeekClick: identity,
duration: createDuration,
bootstrapFontAwesome: identity,
buttonIcons: identity,
customButtons: identity,
defaultAllDayEventDuration: createDuration,
defaultTimedEventDuration: createDuration,
nextDayThreshold: createDuration,
scrollTime: createDuration,
scrollTimeReset: Boolean,
slotMinTime: createDuration,
slotMaxTime: createDuration,
dayPopoverFormat: createFormatter,
slotDuration: createDuration,
snapDuration: createDuration,
headerToolbar: identity,
footerToolbar: identity,
defaultRangeSeparator: String,
titleRangeSeparator: String,
forceEventDuration: Boolean,
dayHeaders: Boolean,
dayHeaderFormat: createFormatter,
dayHeaderClassNames: identity,
dayHeaderContent: identity,
dayHeaderDidMount: identity,
dayHeaderWillUnmount: identity,
dayCellClassNames: identity,
dayCellContent: identity,
dayCellDidMount: identity,
dayCellWillUnmount: identity,
initialView: String,
aspectRatio: Number,
weekends: Boolean,
weekNumberCalculation: identity,
weekNumbers: Boolean,
weekNumberClassNames: identity,
weekNumberContent: identity,
weekNumberDidMount: identity,
weekNumberWillUnmount: identity,
editable: Boolean,
viewClassNames: identity,
viewDidMount: identity,
viewWillUnmount: identity,
nowIndicator: Boolean,
nowIndicatorClassNames: identity,
nowIndicatorContent: identity,