Example 1 (single snippet):
var text = Granite.Util.patchText("{0} has signed in.", "Jack");
Result 1:
Jack has signed in.
*
Example 2 (multiple snippets):
var text = "{0} {1} has signed in from {2}.";
text = Granite.Util.patchText(text, ["Jack", "McFarland", "10.0.0.99"]);
Result 2:
Jack McFarland has signed in from 10.0.0.99.
* @static
* @param {String} text The text
* @param {String/String[]} snippets The text(s) replacing
* {n}
* @return {String} The patched text
*/
patchText: function(text, snippets) {
if (snippets) {
if (!$.isArray(snippets)) {
text = text.replace("{0}", snippets);
} else {
for (var i=0; i < snippets.length; i++) {
text = text.replace(("{" + i + "}"), snippets[i]);
}
}
}
return text;
},
/**
* Returns the top most accessible window. Check {@link setIFrameMode} to avoid security exception message
* on WebKit browsers if this method is called in an iFrame included in a window from different domain.
* @static
* @return {Window} The top window
*/
getTopWindow: function() {
var win = window;
if( this.iFrameTopWindow ) {
return this.iFrameTopWindow;
}
try {
// try to access parent
// win.parent.location.href throws an exception if not authorized (e.g. different location in a portlet)
while(win.parent && win !== win.parent && win.parent.location.href) {
win = win.parent;
}
} catch( error) {}
return win;
},
/**
* Allows to define if Granite.Util is running in an iFrame and parent window is in another domain
* (and optionally define what would be the top window in that case.
* This is necessary to use {@link getTopWindow} in a iFrame on WebKit based browsers because
* {@link getTopWindow} iterates on parent windows to find the top one which triggers a security exception
* if one parent window is in a different domain. Exception cannot be caught but is not breaking the JS
* execution.
* @param {Object} topWindow (optional) The iFrame top window. Must be running on the same host to avoid
* security exception. Defaults to window.
*/
setIFrameMode: function(topWindow) {
this.iFrameTopWindow = topWindow || window;
},
/**
* Applies default properties if inexistent inzo the base object.
* Child objects are merged recursively.
* REMARK:
* - objects are recursively merged
* - simple type obejct properties are copied over the base
* - arrays are cloned and override the base (no value merging)
*
* @static
* @param {Object} base object
* @param {Object} pass objects to be copied onto the base
* @return {Object} The base object with defaults
*/
applyDefaults: function() {
var override, base = arguments[0] || {};
for (var i=1; i < arguments.length; i++) {
override = arguments[i];
for (var name in override) {
var value = override[name];
if (override.hasOwnProperty(name) && value) {
if (typeof value === "object" && !(value instanceof Array)) {
// nested object
base[name] = self.applyDefaults(base[name], value);
} else if (value instanceof Array) {
//override array
base[name] = value.slice(0);
} else {
// simple type
base[name] = value;
}
}
}
}
return base;
},
/**
* Get keycode from event
* @param event Event
* @returns {Number} Keycode
*/
getKeyCode: function(event) {
return event.keyCode ? event.keyCode : event.which;
}
};
return self;
}());
}(Granite, jQuery));
/*
* ADOBE CONFIDENTIAL
*
* Copyright 2012 Adobe Systems Incorporated
* All Rights Reserved.
*
* NOTICE: All information contained herein is, and remains
* the property of Adobe Systems Incorporated and its suppliers,
* if any. The intellectual and technical concepts contained
* herein are proprietary to Adobe Systems Incorporated and its
* suppliers and may be covered by U.S. and Foreign Patents,
* patents in process, and are protected by trade secret or copyright law.
* Dissemination of this information or reproduction of this material
* is strictly forbidden unless prior written permission is obtained
* from Adobe Systems Incorporated.
*
*/
(function (Granite, util, sling, $) {
/**
* A helper class providing a set of HTTP-related utilities.
* @static
* @singleton
* @class Granite.HTTP
*/
Granite.HTTP = (function() {
/**
* The context path used on the server.
* May only be set by {@link #detectContextPath}.
* @private
* @type String
*/
var contextPath = null,
/**
* The regular expression to detect the context path used
* on the server using the URL of this script.
* @private
* @final
* @type RegExp
*/
SCRIPT_URL_REGEXP = /^(?:http|https):\/\/[^\/]+(\/.*)\/(?:etc(\/.*)*\/clientlibs|libs(\/.*)*\/clientlibs|apps(\/.*)*\/clientlibs).*\.js(\?.*)?$/,
/**
* The regular expression to detect unescaped special characters in a path.
* @private
* @final
* @type RegExp
*/
ENCODE_PATH_REGEXP = /[^1\w-\.!~\*''\(\)\/%;:@&=\$,]/,
/**
* Indicates after a session timeout if a refresh has already been triggered
* in order to avoid multiple alerts.
* @private
* @type String
*/
loginRedirected = false,
self = {};
/**
* Returns the scheme and authority (user, hostname, port) part of
* the specified URL or an empty string if the URL does not include
* that part.
* @static
* @param {String} url The URL
* @return {String} The scheme and authority part
*/
self.getSchemeAndAuthority = function (url) {
var end;
try {
if (url.indexOf("://") == -1) return ""; // e.g. url was /en.html
end = url.indexOf("/", url.indexOf("://") + 3);
return (end == -1) ?
url : // e.g. url was http://www.day.com
url.substring(0, end); // e.g. url was http://www.day.com/en.html
}
catch (e) {
return "";
}
};
/**
* Returns the context path used on the server.
* @static
* @return {String} The context path
*/
self.getContextPath = function () {
return contextPath;
};
/**
* Detects the context path used on the server.
* @private
* @static
*/
self.detectContextPath = function () {
try {
if (window.CQURLInfo) {
contextPath = CQURLInfo.contextPath || "";
} else {
var scripts = document.getElementsByTagName("script");
for (var i = 0; i < scripts.length; i++) {
// in IE the first script is not the expected widgets js: loop
// until it is found
var result = SCRIPT_URL_REGEXP.exec(scripts[i].src);
if (result) {
contextPath = result[1];
return;
}
}
contextPath = "";
}
} catch (e) {
}
};
/**
* Makes sure the specified relative URL starts with the context path
* used on the server. If an absolute URL is passed, it will be returned
* as-is.
* @static
* @param {String} url The URL
* @return {String} The externalized URL
*/
self.externalize = function (url) {
try {
if (url.indexOf("/") == 0 && contextPath &&
url.indexOf(contextPath + "/") != 0) {
url = contextPath + url;
}
}
catch (e) {
}
return url;
};
/**
* Removes scheme, authority and context path from the specified
* absolute URL if it has the same scheme and authority as the
* specified document (or the current one). If a relative URL is passed,
* the context path will be stripped if present.
* @static
* @param {String} url The URL
* @param {String} doc (optional) The document
* @return {String} The internalized URL
*/
self.internalize = function (url, doc) {
if (url.charAt(0) == '/') {
if (contextPath === url) {
return '';
}
else if (contextPath && url.indexOf(contextPath + "/") == 0) {
return url.substring(contextPath.length);
} else {
return url;
}
}
if (!doc) doc = document;
var docHost = self.getSchemeAndAuthority(doc.location.href);
var urlHost = self.getSchemeAndAuthority(url);
if (docHost == urlHost) {
return url.substring(urlHost.length + (contextPath ? contextPath.length : 0));
}
else {
return url;
}
};
/**
* Removes all parts but the path from the specified URL.
*
Examples:
/x/y.sel.html?param=abc => /x/y
*
http://www.day.com/foo/bar.html => /foo/bar
* @static
* @param {String} url The URL, may be empty. If empty window.location.href is taken.
* @return {String} The path
*/
self.getPath = function (url) {
if (!url) {
if (window.CQURLInfo && CQURLInfo.requestPath) {
return CQURLInfo.requestPath;
} else {
url = window.location.pathname;
}
} else {
url = self.removeParameters(url);
url = self.removeAnchor(url);
}
url = self.internalize(url);
var i = url.indexOf(".", url.lastIndexOf("/"));
if (i != -1) {
url = url.substring(0, i);
}
return url;
};
/**
* Removes the anchor from the specified URL.
* @static
* @param {String} url The URL
* @return {String} The URL without anchor
*/
self.removeAnchor = function (url) {
if (url.indexOf("#") != -1) {
return url.substring(0, url.indexOf("#"));
}
return url;
};
/**
* Removes all parameter from the specified URL.
* @static
* @param {String} url The URL
* @return {String} The URL without parameters
*/
self.removeParameters = function (url) {
if (url.indexOf("?") != -1) {
return url.substring(0, url.indexOf("?"));
}
return url;
};
/**
* Encodes the path of the specified URL if it is not already encoded.
* Path means the part of the URL before the first question mark or
* hash sign.
* See {@link #encodePath} for details about the encoding.
* Sample:
* /x/y+z.png?path=/x/y+z >> /x/y%2Bz.png?path=x/y+z
* Note that the sample would not work because the "+" in the request
* parameter would be interpreted as a space. Parameters must be encoded
* separately.
* @param {String} url The URL to encoded
* @return {String} The encoded URL
*/
self.encodePathOfURI = function (url) {
var parts, delim;
if (url.indexOf("?") != -1) {
parts = url.split("?");
delim = "?";
}
else if (url.indexOf("#") != -1) {
parts = url.split("#");
delim = "#";
}
else {
parts = [url];
}
if (ENCODE_PATH_REGEXP.test(parts[0])) {
parts[0] = self.encodePath(parts[0]);
}
return parts.join(delim);
};
/**
* Encodes the specified path using encodeURI. Additionally +,
* # and ? are encoded.
* The following characters are not encoded:
* 0-9 a-z A-Z
* - _ . ! ~ * ( )
* / : @ & =
* @param {String} path The path to encode
* @return {String} The encoded path
*/
self.encodePath = function (path) {
// ensure IPV6 address square brackets are not encoded - see bug #34844
path = encodeURI(path).replace(/%5B/g, '[').replace(/%5D/g, ']');
path = path.replace(/\+/g, "%2B");
path = path.replace(/\?/g, "%3F");
path = path.replace(/;/g, "%3B");
path = path.replace(/#/g, "%23");
path = path.replace(/=/g, "%3D");
path = path.replace(/\$/g, "%24");
path = path.replace(/,/g, "%2C");
path = path.replace(/['']/g, "%27");
path = path.replace(/[""]/g, "%22");
return path;
};
/**
* Returns if the redirect to the login page has already been triggered.
* @return {Boolean}
*/
self.handleLoginRedirect = function () {
if (!loginRedirected) {
loginRedirected = true;
alert(Granite.I18n.get("Your request could not be completed because you have been signed out."));
var l = util.getTopWindow().document.location;
l.href = self.externalize(sling.LOGIN_URL) +
"?resource=" + encodeURIComponent(l.pathname + l.search + l.hash);
}
};
/**
* Gets the XHR hooked URL if called in a portlet context
* @param {String} url The URL to get
* @param {String} method The method to use to retrieve the XHR hooked URL
* @param {Object} params The parameters
* @return {String} The XHR hooked URL if available, the provided URL otherwise
*/
self.getXhrHook = function (url, method, params) {
method = method || "GET";
if (window.G_XHR_HOOK && $.isFunction(G_XHR_HOOK)) {
var p = {
"url": url,
"method": method
};
if (params) {
p["params"] = params;
}
return G_XHR_HOOK(p);
}
return null;
};
/**
* Evaluates and returns the body of the specified response object.
* Alternatively, a URL can be specified, in which case it will be
* requested using a synchornous {@link #get} in order to acquire
* the response object.
* @static
* @param {Object/String} response The response object or URL
* @return {Object} The evaluated response body
* @since 5.3
*/
self.eval = function(response) {
if (typeof response != "object") {
response = $.ajax({
url: response,
type: 'get',
async: false
});
}
try {
// support responseText for backward compatibility (pre 5.3)
return eval("(" + (response.body ? response.body :
response.responseText) + ")");
} catch (e) {
}
return null;
};
return self;
}());
}(Granite, Granite.Util, Granite.Sling, jQuery));
/*
* ADOBE CONFIDENTIAL
*
* Copyright 2012 Adobe Systems Incorporated
* All Rights Reserved.
*
* NOTICE: All information contained herein is, and remains
* the property of Adobe Systems Incorporated and its suppliers,
* if any. The intellectual and technical concepts contained
* herein are proprietary to Adobe Systems Incorporated and its
* suppliers and may be covered by U.S. and Foreign Patents,
* patents in process, and are protected by trade secret or copyright law.
* Dissemination of this information or reproduction of this material
* is strictly forbidden unless prior written permission is obtained
* from Adobe Systems Incorporated.
*
*/
(function (document, Granite, util, http, $) {
/**
* A helper class providing a set of utilities related to internationalization (i18n).
*
* Locale Priorities
*
The locale is read based on the following priorities:
*
* - manually specified locale
*
document.documentElement.lang
* Granite.I18n.LOCALE_DEFAULT
*
*
* Dictionary Priorities
*
The dictionary URL is read based on the following priorities:
*
* - manually specified URL (
urlPrefixurlSuffix)
* data-i18n-dictionary-src attribute at <html> element, which has the type of URI Template string
* - The URL resolved from default
urlPrefix and urlSuffix
*
*
* URI Template of data-i18n-dictionary-src
*
It expects the variable named locale, which will be fetched from the locale (based on priorities above).
* E.g. <html lang="en" data-i18n-dictionary-src="/libs/cq/i18n/dict.{+locale}.json">.
*
* @static
* @singleton
* @class Granite.I18n
*/
Granite.I18n = (function() {
/**
* The map where the dictionaries are stored under their locale.
* @private
* @type Object
*/
var dicts = {},
/**
* The prefix for the URL used to request dictionaries from the server.
* @private
* @type String
*/
urlPrefix = "/libs/cq/i18n/dict.",
/**
* The suffix for the URL used to request dictionaries from the server.
* @private
* @type String
*/
urlSuffix = ".json",
/**
* The manually specified locale as a String or a function that returns the locale as a string.
* @private
* @static
* @type String
*/
manualLocale = undefined,
/**
* If the current locale represents pseudo translations.
* In that case the dictionary is expected to provide just a special
* translation pattern to automatically convert all original strings.
*/
pseudoTranslations = false,
languages = null,
self = {},
/**
* Indicates if the dictionary parameters are specified manually.
*/
manualDictionary = false,
getDictionaryUrl = function(locale) {
if (manualDictionary) {
return urlPrefix + locale + urlSuffix;
}
var dictionarySrc = $("html").attr("data-i18n-dictionary-src");
if (!dictionarySrc) {
return urlPrefix + locale + urlSuffix;
}
// dictionarySrc is a URITemplate
// Use simple string replacement for now; for more complicated scenario, please use Granite.URITemplate
return dictionarySrc.replace("{locale}", encodeURIComponent(locale)).replace("{+locale}", locale);
};
/**
* The default locale (en).
* @static
* @final
* @type String
*/
self.LOCALE_DEFAULT = "en";
/**
* Language code for pseudo translations.
* @static
* @final
* @type String
*/
self.PSEUDO_LANGUAGE = "zz";
/**
* Dictionary key for pseudo translation pattern.
* @static
* @final
* @type String
*/
self.PSEUDO_PATTERN_KEY = "_pseudoPattern_";
/**
* Initializes I18n with the given config options:
*
* - locale: the current locale (defaults to "en")
*
- urlPrefix: the prefix for the URL used to request dictionaries from
* the server (defaults to "/libs/cq/i18n/dict.")
*
- urlSuffix: the suffix for the URL used to request dictionaries from
* the server (defaults to ".json")
*
* Sample config. The dictioniary would be requested from
* "/apps/i18n/dict.fr.json":
{
"locale": "fr",
"urlPrefix": "/apps/i18n/dict.",
"urlSuffix": ".json"
}
* @param {Object} config The config
*/
self.init = function (config) {
config = config || {};
this.setLocale(config.locale);
this.setUrlPrefix(config.urlPrefix);
this.setUrlSuffix(config.urlSuffix);
};
/**
* Sets the current locale.
* @static
* @param {String/Function} locale The locale or a function that returns the locale as a string
*/
self.setLocale = function (locale) {
if (!locale) return;
manualLocale = locale;
};
/**
* Returns the current locale based on the priorities.
* @static
* @return {String} The locale
*/
self.getLocale = function () {
if (typeof manualLocale === "function") {
// execute function first time only and store result in currentLocale
manualLocale = manualLocale();
}
return manualLocale || document.documentElement.lang || self.LOCALE_DEFAULT;
};
/**
* Sets the prefix for the URL used to request dictionaries from
* the server. The locale and URL suffix will be appended.
* @static
* @param {String} prefix The URL prefix
*/
self.setUrlPrefix = function (prefix) {
if (!prefix) return;
urlPrefix = prefix;
manualDictionary = true;
};
/**
* Sets the suffix for the URL used to request dictionaries from
* the server. It will be appended to the URL prefix and locale.
* @static
* @param {String} suffix The URL suffix
*/
self.setUrlSuffix = function (suffix) {
if (!suffix) return;
urlSuffix = suffix;
manualDictionary = true;
};
/**
* Returns the dictionary for the specified locale. This method
* will request the dictionary using the URL prefix, the locale,
* and the URL suffix. If no locale is specified, the current
* locale is used.
* @static
* @param {String} locale (optional) The locale
* @return {Object} The dictionary
*/
self.getDictionary = function (locale) {
locale = locale || self.getLocale();
if (!dicts[locale]) {
pseudoTranslations = (locale.indexOf(self.PSEUDO_LANGUAGE) == 0);
try {
var response = $.ajax(getDictionaryUrl(locale), {
async: false,
dataType: "json"
});
dicts[locale] = JSON.parse(response.responseText);
} catch (e) {}
if (!dicts[locale]) {
dicts[locale] = {};
}
}
return dicts[locale];
};
/**
* Translates the specified text into the current language.
* @static
* @param {String} text The text to translate
* @param {String[]} snippets The snippets replacing {n} (optional)
* @param {String} note A hint for translators (optional)
* @return {String} The translated text
*/
self.get = function (text, snippets, note) {
var dict, newText, lookupText;
dict = self.getDictionary();
// note that pseudoTranslations is initialized in the getDictionary() call above
lookupText = pseudoTranslations ? self.PSEUDO_PATTERN_KEY :
note ? text + " ((" + note + "))" :
text;
if (dict) {
newText = dict[lookupText];
}
if (!newText) {
newText = text;
}
if (pseudoTranslations) {
newText = newText.replace("{string}", text).replace("{comment}", note ? note : "");
}
return util.patchText(newText, snippets);
};
/**
* Translates the specified text into the current language. Use this
* method to translate String variables, e.g. data from the server.
* @static
* @param {String} text The text to translate
* @param {String} note A hint for translators (optional)
* @return {String} The translated text
*/
self.getVar = function (text, note) {
if (!text) {
return null;
}
return self.get(text, null, note);
};
/**
* Returns the available languages, including a "title" property with a display name:
* for instance "German" for "de" or "German (Switzerland)" for "de_ch".
* @static
* @return {Object} An object with language codes as keys and an object with "title",
* "language", "country" and "defaultCountry" members.
*/
self.getLanguages = function () {
if (!languages) {
try {
// use overlay servlet so customers can define /apps/wcm/core/resources/languages
var json = http.eval("/libs/wcm/core/resources/languages.overlay.infinity.json"); // TODO: broken!!!
$.each(json, function(name, lang) {
lang.title = self.getVar(lang.language);
if (lang.title && lang.country && lang.country != "*") {
lang.title += " ("+self.getVar(lang.country)+")";
}
});
languages = json;
} catch (e) {
languages = {};
}
}
return languages;
};
/**
* Parses a language code string such as "de_CH" and returns an object with
* language and country extracted. The delimiter can be "_" or "-".
* @static
* @param {String} langCode a language code such as "de" or "de_CH" or "de-ch"
* @return {Object} an object with "code" ("de_CH"), "language" ("de") and "country" ("CH")
* (or null if langCode was null)
*/
self.parseLocale = function (langCode) {
if (!langCode) {
return null;
}
var pos = langCode.indexOf("_");
if (pos < 0) {
pos = langCode.indexOf("-");
}
var language, country;
if (pos < 0) {
language = langCode;
country = null;
} else {
language = langCode.substring(0, pos);
country = langCode.substring(pos + 1);
}
return {
code: langCode,
language: language,
country: country
};
};
return self;
}());
}(document, Granite, Granite.Util, Granite.HTTP, jQuery));
/*
*
* ADOBE CONFIDENTIAL
* __________________
*
* Copyright 2012 Adobe Systems Incorporated
* All Rights Reserved.
*
* NOTICE: All information contained herein is, and remains
* the property of Adobe Systems Incorporated and its suppliers,
* if any. The intellectual and technical concepts contained
* herein are proprietary to Adobe Systems Incorporated and its
* suppliers and are protected by trade secret or copyright law.
* Dissemination of this information or reproduction of this material
* is strictly forbidden unless prior written permission is obtained
* from Adobe Systems Incorporated.
*
*/
/**
* Implements the "Adobe Dynamic Touch Indicator" that tracks touch events and displays a visual indicator for
* screen sharing and presentation purposes.
*
* To enable it call Granite.TouchIndicator.init() e.g. on document ready:
*
Granite.$(document).ready(function() {
Granite.TouchIndicator.init();
});
*
* AdobePatentID="2631US01"
*/
(function (Granite, $) {
var touchIndicator = function() {
var CSS = {
"visibility": "hidden",
"position": "absolute", // fixed would be better, but flickers on ipad while scrolling
"width": "30px",
"height": "30px",
"-webkit-border-radius": "20px",
"border-radius": "20px",
"border": "5px solid orange",
"-webkit-user-select": "none",
"user-select": "none",
"opacity": "0.5",
"z-index": "2000",
"pointer-events": "none"
};
var used = {};
var unused = [];
return {
debugWithMouse: false,
init: function() {
var self = this;
$(document).on("touchstart.touchindicator touchmove.touchindicator touchend.touchindicator", function(e) {
var touches = e.originalEvent.touches;
self.update(touches);
return true;
});
if (this.debugWithMouse) {
$(document).on("mousemove.touchindicator", function(e){
e.identifer = "fake";
self.update([e]);
return true;
});
}
},
update: function(touches) {
// go over all touch events present in the array
var retained = {};
for (var i = 0; i").css(CSS);
$("body").append(indicator);
}
}
retained[id] = indicator;
indicator.offset({
left: touch.pageX - 20,
top: touch.pageY - 20
});
indicator.css("visibility", "visible");
}
// now hide all unused ones and stuff them in the unused array
for (id in used) {
if (used.hasOwnProperty(id) && !retained[id]) {
indicator = used[id];
indicator.css("visibility", "hidden");
unused.push(indicator);
}
}
used = retained;
}
}
};
Granite.TouchIndicator = new touchIndicator();
}(Granite, jQuery));
/*
* ADOBE CONFIDENTIAL
*
* Copyright 2012 Adobe Systems Incorporated
* All Rights Reserved.
*
* NOTICE: All information contained herein is, and remains
* the property of Adobe Systems Incorporated and its suppliers,
* if any. The intellectual and technical concepts contained
* herein are proprietary to Adobe Systems Incorporated and its
* suppliers and may be covered by U.S. and Foreign Patents,
* patents in process, and are protected by trade secret or copyright law.
* Dissemination of this information or reproduction of this material
* is strictly forbidden unless prior written permission is obtained
* from Adobe Systems Incorporated.
*
*/
(function (Granite, util, http, $) {
/**
* A tool to determine whether any opt-out cookie is set and whether a given cookie name
* is white-listed. The opt-out and white-list cookie names are determined by a server
* side configuration (com.adobe.granite.security.commons.OptOutService) and provided to
* this tool by an optionally included component (/libs/granite/security/components/optout)
* which provides a global JSON object named GraniteOptOutConfig.
*
* @static
* @singleton
* @class Granite.OptOutUtil
*/
Granite.OptOutUtil = (function () {
var self = {};
/**
* Contains the names of cookies the presence of which indicates the user has opted out.
* @private
* @type Array
*/
var optOutCookieNames = [];
/**
* Contains the names of cookies which may still be set in spite of the user having opted out.
* @private
* @type Array
*/
var whitelistedCookieNames = [];
/**
* Initializes this tool with an opt-out configuration. The following options are supported:
*
* - cookieNames: an array of cookie names representing opt-out cookies. Defaults to empty.
*
- whitelistCookieNames: an array of cookies representing white-listed cookies. Defaults to empty.
*
* Sample config:
*
*
* {
* "cookieNames":["omniture_optout","cq-opt-out"],
* "whitelistCookieNames":["someAppCookie", "anotherImportantAppCookie"]
* }
*
*
* @param config The opt-out configuration
*/
self.init = function (config) {
if (config) {
optOutCookieNames = config.cookieNames
? config.cookieNames : optOutCookieNames;
whitelistedCookieNames = config.whitelistCookieNames
? config.whitelistCookieNames : whitelistedCookieNames;
}
};
/**
* Returns the array of configured cookie names representing opt-out cookies.
* @static
* @return {Array} The cookie names
*/
self.getCookieNames = function () {
return optOutCookieNames;
};
/**
* Returns the array of configured cookie names representing white-listed cookies.
* @static
* @return {Array} The cookie names
*/
self.getWhitelistCookieNames = function () {
return whitelistedCookieNames;
};
/**
* Determines whether the user (browser) has elected to opt-out. This is indicated by the presence of
* one of the cookies retrieved through #getCookieNames().
* @return {Boolean} True if an opt-cookie was found in the browser's cookies.
*/
self.isOptedOut = function () {
var browserCookies = document.cookie.split(";");
for (var i = 0; i < browserCookies.length; i++) {
var cookie = browserCookies[i];
var cookieName = cookie.split("=")[0].trim();
if ($.inArray(cookieName, self.getCookieNames()) > -1) {
return true;
}
}
return false;
};
/**
* Determines whether the given cookieName may be used to set a cookie. This is the case
* if either opt-out is inactive (#isOptedOut() == false) or it is active and the give cookie name was
* found in the white-list (#getWhitelistCookieNames()).
* @param cookieName The name of the cookie to check.
* @return {Boolean} True if a cookie of this name may be used with respect to the opt-out status.
*/
self.maySetCookie = function (cookieName) {
return !(self.isOptedOut() && $.inArray(cookieName, self.getWhitelistCookieNames()) === -1);
};
return self;
}());
}(Granite, Granite.Util, Granite.HTTP, jQuery));
/*
* ADOBE CONFIDENTIAL
*
* Copyright 2012 Adobe Systems Incorporated
* All Rights Reserved.
*
* NOTICE: All information contained herein is, and remains
* the property of Adobe Systems Incorporated and its suppliers,
* if any. The intellectual and technical concepts contained
* herein are proprietary to Adobe Systems Incorporated and its
* suppliers and may be covered by U.S. and Foreign Patents,
* patents in process, and are protected by trade secret or copyright law.
* Dissemination of this information or reproduction of this material
* is strictly forbidden unless prior written permission is obtained
* from Adobe Systems Incorporated.
*
*/
//------------------------------------------------------------------------------
// Initialize the Granite utils library
Granite.OptOutUtil.init(window.GraniteOptOutConfig);
Granite.HTTP.detectContextPath();
/*!
* JavaScript Cookie v2.2.1
* https://github.com/js-cookie/js-cookie
*
* Copyright 2006, 2015 Klaus Hartl & Fagner Brack
* Released under the MIT license
*/
;(function (factory) {
var registeredInModuleLoader;
if (typeof define === 'function' && define.amd) {
define(factory);
registeredInModuleLoader = true;
}
if (typeof exports === 'object') {
module.exports = factory();
registeredInModuleLoader = true;
}
if (!registeredInModuleLoader) {
var OldCookies = window.Cookies;
var api = window.Cookies = factory();
api.noConflict = function () {
window.Cookies = OldCookies;
return api;
};
}
}(function () {
function extend () {
var i = 0;
var result = {};
for (; i < arguments.length; i++) {
var attributes = arguments[ i ];
for (var key in attributes) {
result[key] = attributes[key];
}
}
return result;
}
function decode (s) {
return s.replace(/(%[0-9A-Z]{2})+/g, decodeURIComponent);
}
function init (converter) {
function api() {}
function set (key, value, attributes) {
if (typeof document === 'undefined') {
return;
}
attributes = extend({
path: '/'
}, api.defaults, attributes);
if (typeof attributes.expires === 'number') {
attributes.expires = new Date(new Date() * 1 + attributes.expires * 864e+5);
}
// We're using "expires" because "max-age" is not supported by IE
attributes.expires = attributes.expires ? attributes.expires.toUTCString() : '';
try {
var result = JSON.stringify(value);
if (/^[\{\[]/.test(result)) {
value = result;
}
} catch (e) {}
value = converter.write ?
converter.write(value, key) :
encodeURIComponent(String(value))
.replace(/%(23|24|26|2B|3A|3C|3E|3D|2F|3F|40|5B|5D|5E|60|7B|7D|7C)/g, decodeURIComponent);
key = encodeURIComponent(String(key))
.replace(/%(23|24|26|2B|5E|60|7C)/g, decodeURIComponent)
.replace(/[\(\)]/g, escape);
var stringifiedAttributes = '';
for (var attributeName in attributes) {
if (!attributes[attributeName]) {
continue;
}
stringifiedAttributes += '; ' + attributeName;
if (attributes[attributeName] === true) {
continue;
}
// Considers RFC 6265 section 5.2:
// ...
// 3. If the remaining unparsed-attributes contains a %x3B (";")
// character:
// Consume the characters of the unparsed-attributes up to,
// not including, the first %x3B (";") character.
// ...
stringifiedAttributes += '=' + attributes[attributeName].split(';')[0];
}
return (document.cookie = key + '=' + value + stringifiedAttributes);
}
function get (key, json) {
if (typeof document === 'undefined') {
return;
}
var jar = {};
// To prevent the for loop in the first place assign an empty array
// in case there are no cookies at all.
var cookies = document.cookie ? document.cookie.split('; ') : [];
var i = 0;
for (; i < cookies.length; i++) {
var parts = cookies[i].split('=');
var cookie = parts.slice(1).join('=');
if (!json && cookie.charAt(0) === '"') {
cookie = cookie.slice(1, -1);
}
try {
var name = decode(parts[0]);
cookie = (converter.read || converter)(cookie, name) ||
decode(cookie);
if (json) {
try {
cookie = JSON.parse(cookie);
} catch (e) {}
}
jar[name] = cookie;
if (key === name) {
break;
}
} catch (e) {}
}
return key ? jar[key] : jar;
}
api.set = set;
api.get = function (key) {
return get(key, false /* read as raw */);
};
api.getJSON = function (key) {
return get(key, true /* read as json */);
};
api.remove = function (key, attributes) {
set(key, '', extend(attributes, {
expires: -1
}));
};
api.defaults = {};
api.withConverter = init;
return api;
}
return init(function () {});
}));
(function () {
// Returns a function, that, as long as it continues to be invoked, will not
// be triggered. The function will be called after it stops being called for
// N milliseconds. If `immediate` is passed, trigger the function on the
// leading edge, instead of the trailing.
function debounce(func, wait, immediate) {
var timeout;
return function() {
var context = this, args = arguments;
var later = function() {
timeout = null;
if (!immediate) func.apply(context, args);
}
var callNow = immediate && !timeout;
clearTimeout(timeout);
timeout = setTimeout(later, wait);
if (callNow) func.apply(context, args);
}
}
window.PAN_Clean_Util.debounce = debounce
})();
/* -----------------------------------------------
* @fileOverview Kickass library to create and place poppers near their reference elements.
* @version 1.16.1
* @license
* Copyright (c) 2016 Federico Zivolo and contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
(function (global, factory) {
typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
typeof define === 'function' && define.amd ? define(factory) :
(global.Popper = factory());
}(this, (function () { 'use strict';
var isBrowser = typeof window !== 'undefined' && typeof document !== 'undefined' && typeof navigator !== 'undefined';
var timeoutDuration = function () {
var longerTimeoutBrowsers = ['Edge', 'Trident', 'Firefox'];
for (var i = 0; i < longerTimeoutBrowsers.length; i += 1) {
if (isBrowser && navigator.userAgent.indexOf(longerTimeoutBrowsers[i]) >= 0) {
return 1;
}
}
return 0;
}();
function microtaskDebounce(fn) {
var called = false;
return function () {
if (called) {
return;
}
called = true;
window.Promise.resolve().then(function () {
called = false;
fn();
});
};
}
function taskDebounce(fn) {
var scheduled = false;
return function () {
if (!scheduled) {
scheduled = true;
setTimeout(function () {
scheduled = false;
fn();
}, timeoutDuration);
}
};
}
var supportsMicroTasks = isBrowser && window.Promise;
/**
* Create a debounced version of a method, that's asynchronously deferred
* but called in the minimum time possible.
*
* @method
* @memberof Popper.Utils
* @argument {Function} fn
* @returns {Function}
*/
var debounce = supportsMicroTasks ? microtaskDebounce : taskDebounce;
/**
* Check if the given variable is a function
* @method
* @memberof Popper.Utils
* @argument {Any} functionToCheck - variable to check
* @returns {Boolean} answer to: is a function?
*/
function isFunction(functionToCheck) {
var getType = {};
return functionToCheck && getType.toString.call(functionToCheck) === '[object Function]';
}
/**
* Get CSS computed property of the given element
* @method
* @memberof Popper.Utils
* @argument {Eement} element
* @argument {String} property
*/
function getStyleComputedProperty(element, property) {
if (element.nodeType !== 1) {
return [];
}
// NOTE: 1 DOM access here
var window = element.ownerDocument.defaultView;
var css = window.getComputedStyle(element, null);
return property ? css[property] : css;
}
/**
* Returns the parentNode or the host of the element
* @method
* @memberof Popper.Utils
* @argument {Element} element
* @returns {Element} parent
*/
function getParentNode(element) {
if (element.nodeName === 'HTML') {
return element;
}
return element.parentNode || element.host;
}
/**
* Returns the scrolling parent of the given element
* @method
* @memberof Popper.Utils
* @argument {Element} element
* @returns {Element} scroll parent
*/
function getScrollParent(element) {
// Return body, `getScroll` will take care to get the correct `scrollTop` from it
if (!element) {
return document.body;
}
switch (element.nodeName) {
case 'HTML':
case 'BODY':
return element.ownerDocument.body;
case '#document':
return element.body;
}
// Firefox want us to check `-x` and `-y` variations as well
var _getStyleComputedProp = getStyleComputedProperty(element),
overflow = _getStyleComputedProp.overflow,
overflowX = _getStyleComputedProp.overflowX,
overflowY = _getStyleComputedProp.overflowY;
if (/(auto|scroll|overlay)/.test(overflow + overflowY + overflowX)) {
return element;
}
return getScrollParent(getParentNode(element));
}
/**
* Returns the reference node of the reference object, or the reference object itself.
* @method
* @memberof Popper.Utils
* @param {Element|Object} reference - the reference element (the popper will be relative to this)
* @returns {Element} parent
*/
function getReferenceNode(reference) {
return reference && reference.referenceNode ? reference.referenceNode : reference;
}
var isIE11 = isBrowser && !!(window.MSInputMethodContext && document.documentMode);
var isIE10 = isBrowser && /MSIE 10/.test(navigator.userAgent);
/**
* Determines if the browser is Internet Explorer
* @method
* @memberof Popper.Utils
* @param {Number} version to check
* @returns {Boolean} isIE
*/
function isIE(version) {
if (version === 11) {
return isIE11;
}
if (version === 10) {
return isIE10;
}
return isIE11 || isIE10;
}
/**
* Returns the offset parent of the given element
* @method
* @memberof Popper.Utils
* @argument {Element} element
* @returns {Element} offset parent
*/
function getOffsetParent(element) {
if (!element) {
return document.documentElement;
}
var noOffsetParent = isIE(10) ? document.body : null;
// NOTE: 1 DOM access here
var offsetParent = element.offsetParent || null;
// Skip hidden elements which don't have an offsetParent
while (offsetParent === noOffsetParent && element.nextElementSibling) {
offsetParent = (element = element.nextElementSibling).offsetParent;
}
var nodeName = offsetParent && offsetParent.nodeName;
if (!nodeName || nodeName === 'BODY' || nodeName === 'HTML') {
return element ? element.ownerDocument.documentElement : document.documentElement;
}
// .offsetParent will return the closest TH, TD or TABLE in case
// no offsetParent is present, I hate this job...
if (['TH', 'TD', 'TABLE'].indexOf(offsetParent.nodeName) !== -1 && getStyleComputedProperty(offsetParent, 'position') === 'static') {
return getOffsetParent(offsetParent);
}
return offsetParent;
}
function isOffsetContainer(element) {
var nodeName = element.nodeName;
if (nodeName === 'BODY') {
return false;
}
return nodeName === 'HTML' || getOffsetParent(element.firstElementChild) === element;
}
/**
* Finds the root node (document, shadowDOM root) of the given element
* @method
* @memberof Popper.Utils
* @argument {Element} node
* @returns {Element} root node
*/
function getRoot(node) {
if (node.parentNode !== null) {
return getRoot(node.parentNode);
}
return node;
}
/**
* Finds the offset parent common to the two provided nodes
* @method
* @memberof Popper.Utils
* @argument {Element} element1
* @argument {Element} element2
* @returns {Element} common offset parent
*/
function findCommonOffsetParent(element1, element2) {
// This check is needed to avoid errors in case one of the elements isn't defined for any reason
if (!element1 || !element1.nodeType || !element2 || !element2.nodeType) {
return document.documentElement;
}
// Here we make sure to give as "start" the element that comes first in the DOM
var order = element1.compareDocumentPosition(element2) & Node.DOCUMENT_POSITION_FOLLOWING;
var start = order ? element1 : element2;
var end = order ? element2 : element1;
// Get common ancestor container
var range = document.createRange();
range.setStart(start, 0);
range.setEnd(end, 0);
var commonAncestorContainer = range.commonAncestorContainer;
// Both nodes are inside #document
if (element1 !== commonAncestorContainer && element2 !== commonAncestorContainer || start.contains(end)) {
if (isOffsetContainer(commonAncestorContainer)) {
return commonAncestorContainer;
}
return getOffsetParent(commonAncestorContainer);
}
// one of the nodes is inside shadowDOM, find which one
var element1root = getRoot(element1);
if (element1root.host) {
return findCommonOffsetParent(element1root.host, element2);
} else {
return findCommonOffsetParent(element1, getRoot(element2).host);
}
}
/**
* Gets the scroll value of the given element in the given side (top and left)
* @method
* @memberof Popper.Utils
* @argument {Element} element
* @argument {String} side `top` or `left`
* @returns {number} amount of scrolled pixels
*/
function getScroll(element) {
var side = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 'top';
var upperSide = side === 'top' ? 'scrollTop' : 'scrollLeft';
var nodeName = element.nodeName;
if (nodeName === 'BODY' || nodeName === 'HTML') {
var html = element.ownerDocument.documentElement;
var scrollingElement = element.ownerDocument.scrollingElement || html;
return scrollingElement[upperSide];
}
return element[upperSide];
}
/*
* Sum or subtract the element scroll values (left and top) from a given rect object
* @method
* @memberof Popper.Utils
* @param {Object} rect - Rect object you want to change
* @param {HTMLElement} element - The element from the function reads the scroll values
* @param {Boolean} subtract - set to true if you want to subtract the scroll values
* @return {Object} rect - The modifier rect object
*/
function includeScroll(rect, element) {
var subtract = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : false;
var scrollTop = getScroll(element, 'top');
var scrollLeft = getScroll(element, 'left');
var modifier = subtract ? -1 : 1;
rect.top += scrollTop * modifier;
rect.bottom += scrollTop * modifier;
rect.left += scrollLeft * modifier;
rect.right += scrollLeft * modifier;
return rect;
}
/*
* Helper to detect borders of a given element
* @method
* @memberof Popper.Utils
* @param {CSSStyleDeclaration} styles
* Result of `getStyleComputedProperty` on the given element
* @param {String} axis - `x` or `y`
* @return {number} borders - The borders size of the given axis
*/
function getBordersSize(styles, axis) {
var sideA = axis === 'x' ? 'Left' : 'Top';
var sideB = sideA === 'Left' ? 'Right' : 'Bottom';
return parseFloat(styles['border' + sideA + 'Width']) + parseFloat(styles['border' + sideB + 'Width']);
}
function getSize(axis, body, html, computedStyle) {
return Math.max(body['offset' + axis], body['scroll' + axis], html['client' + axis], html['offset' + axis], html['scroll' + axis], isIE(10) ? parseInt(html['offset' + axis]) + parseInt(computedStyle['margin' + (axis === 'Height' ? 'Top' : 'Left')]) + parseInt(computedStyle['margin' + (axis === 'Height' ? 'Bottom' : 'Right')]) : 0);
}
function getWindowSizes(document) {
var body = document.body;
var html = document.documentElement;
var computedStyle = isIE(10) && getComputedStyle(html);
return {
height: getSize('Height', body, html, computedStyle),
width: getSize('Width', body, html, computedStyle)
};
}
var classCallCheck = function (instance, Constructor) {
if (!(instance instanceof Constructor)) {
throw new TypeError("Cannot call a class as a function");
}
};
var createClass = function () {
function defineProperties(target, props) {
for (var i = 0; i < props.length; i++) {
var descriptor = props[i];
descriptor.enumerable = descriptor.enumerable || false;
descriptor.configurable = true;
if ("value" in descriptor) descriptor.writable = true;
Object.defineProperty(target, descriptor.key, descriptor);
}
}
return function (Constructor, protoProps, staticProps) {
if (protoProps) defineProperties(Constructor.prototype, protoProps);
if (staticProps) defineProperties(Constructor, staticProps);
return Constructor;
};
}();
var defineProperty = function (obj, key, value) {
if (key in obj) {
Object.defineProperty(obj, key, {
value: value,
enumerable: true,
configurable: true,
writable: true
});
} else {
obj[key] = value;
}
return obj;
};
var _extends = Object.assign || function (target) {
for (var i = 1; i < arguments.length; i++) {
var source = arguments[i];
for (var key in source) {
if (Object.prototype.hasOwnProperty.call(source, key)) {
target[key] = source[key];
}
}
}
return target;
};
/**
* Given element offsets, generate an output similar to getBoundingClientRect
* @method
* @memberof Popper.Utils
* @argument {Object} offsets
* @returns {Object} ClientRect like output
*/
function getClientRect(offsets) {
return _extends({}, offsets, {
right: offsets.left + offsets.width,
bottom: offsets.top + offsets.height
});
}
/**
* Get bounding client rect of given element
* @method
* @memberof Popper.Utils
* @param {HTMLElement} element
* @return {Object} client rect
*/
function getBoundingClientRect(element) {
var rect = {};
// IE10 10 FIX: Please, don't ask, the element isn't
// considered in DOM in some circumstances...
// This isn't reproducible in IE10 compatibility mode of IE11
try {
if (isIE(10)) {
rect = element.getBoundingClientRect();
var scrollTop = getScroll(element, 'top');
var scrollLeft = getScroll(element, 'left');
rect.top += scrollTop;
rect.left += scrollLeft;
rect.bottom += scrollTop;
rect.right += scrollLeft;
} else {
rect = element.getBoundingClientRect();
}
} catch (e) {}
var result = {
left: rect.left,
top: rect.top,
width: rect.right - rect.left,
height: rect.bottom - rect.top
};
// subtract scrollbar size from sizes
var sizes = element.nodeName === 'HTML' ? getWindowSizes(element.ownerDocument) : {};
var width = sizes.width || element.clientWidth || result.width;
var height = sizes.height || element.clientHeight || result.height;
var horizScrollbar = element.offsetWidth - width;
var vertScrollbar = element.offsetHeight - height;
// if an hypothetical scrollbar is detected, we must be sure it's not a `border`
// we make this check conditional for performance reasons
if (horizScrollbar || vertScrollbar) {
var styles = getStyleComputedProperty(element);
horizScrollbar -= getBordersSize(styles, 'x');
vertScrollbar -= getBordersSize(styles, 'y');
result.width -= horizScrollbar;
result.height -= vertScrollbar;
}
return getClientRect(result);
}
function getOffsetRectRelativeToArbitraryNode(children, parent) {
var fixedPosition = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : false;
var isIE10 = isIE(10);
var isHTML = parent.nodeName === 'HTML';
var childrenRect = getBoundingClientRect(children);
var parentRect = getBoundingClientRect(parent);
var scrollParent = getScrollParent(children);
var styles = getStyleComputedProperty(parent);
var borderTopWidth = parseFloat(styles.borderTopWidth);
var borderLeftWidth = parseFloat(styles.borderLeftWidth);
// In cases where the parent is fixed, we must ignore negative scroll in offset calc
if (fixedPosition && isHTML) {
parentRect.top = Math.max(parentRect.top, 0);
parentRect.left = Math.max(parentRect.left, 0);
}
var offsets = getClientRect({
top: childrenRect.top - parentRect.top - borderTopWidth,
left: childrenRect.left - parentRect.left - borderLeftWidth,
width: childrenRect.width,
height: childrenRect.height
});
offsets.marginTop = 0;
offsets.marginLeft = 0;
// Subtract margins of documentElement in case it's being used as parent
// we do this only on HTML because it's the only element that behaves
// differently when margins are applied to it. The margins are included in
// the box of the documentElement, in the other cases not.
if (!isIE10 && isHTML) {
var marginTop = parseFloat(styles.marginTop);
var marginLeft = parseFloat(styles.marginLeft);
offsets.top -= borderTopWidth - marginTop;
offsets.bottom -= borderTopWidth - marginTop;
offsets.left -= borderLeftWidth - marginLeft;
offsets.right -= borderLeftWidth - marginLeft;
// Attach marginTop and marginLeft because in some circumstances we may need them
offsets.marginTop = marginTop;
offsets.marginLeft = marginLeft;
}
if (isIE10 && !fixedPosition ? parent.contains(scrollParent) : parent === scrollParent && scrollParent.nodeName !== 'BODY') {
offsets = includeScroll(offsets, parent);
}
return offsets;
}
function getViewportOffsetRectRelativeToArtbitraryNode(element) {
var excludeScroll = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false;
var html = element.ownerDocument.documentElement;
var relativeOffset = getOffsetRectRelativeToArbitraryNode(element, html);
var width = Math.max(html.clientWidth, window.innerWidth || 0);
var height = Math.max(html.clientHeight, window.innerHeight || 0);
var scrollTop = !excludeScroll ? getScroll(html) : 0;
var scrollLeft = !excludeScroll ? getScroll(html, 'left') : 0;
var offset = {
top: scrollTop - relativeOffset.top + relativeOffset.marginTop,
left: scrollLeft - relativeOffset.left + relativeOffset.marginLeft,
width: width,
height: height
};
return getClientRect(offset);
}
/**
* Check if the given element is fixed or is inside a fixed parent
* @method
* @memberof Popper.Utils
* @argument {Element} element
* @argument {Element} customContainer
* @returns {Boolean} answer to "isFixed?"
*/
function isFixed(element) {
var nodeName = element.nodeName;
if (nodeName === 'BODY' || nodeName === 'HTML') {
return false;
}
if (getStyleComputedProperty(element, 'position') === 'fixed') {
return true;
}
var parentNode = getParentNode(element);
if (!parentNode) {
return false;
}
return isFixed(parentNode);
}
/**
* Finds the first parent of an element that has a transformed property defined
* @method
* @memberof Popper.Utils
* @argument {Element} element
* @returns {Element} first transformed parent or documentElement
*/
function getFixedPositionOffsetParent(element) {
// This check is needed to avoid errors in case one of the elements isn't defined for any reason
if (!element || !element.parentElement || isIE()) {
return document.documentElement;
}
var el = element.parentElement;
while (el && getStyleComputedProperty(el, 'transform') === 'none') {
el = el.parentElement;
}
return el || document.documentElement;
}
/**
* Computed the boundaries limits and return them
* @method
* @memberof Popper.Utils
* @param {HTMLElement} popper
* @param {HTMLElement} reference
* @param {number} padding
* @param {HTMLElement} boundariesElement - Element used to define the boundaries
* @param {Boolean} fixedPosition - Is in fixed position mode
* @returns {Object} Coordinates of the boundaries
*/
function getBoundaries(popper, reference, padding, boundariesElement) {
var fixedPosition = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : false;
// NOTE: 1 DOM access here
var boundaries = { top: 0, left: 0 };
var offsetParent = fixedPosition ? getFixedPositionOffsetParent(popper) : findCommonOffsetParent(popper, getReferenceNode(reference));
// Handle viewport case
if (boundariesElement === 'viewport') {
boundaries = getViewportOffsetRectRelativeToArtbitraryNode(offsetParent, fixedPosition);
} else {
// Handle other cases based on DOM element used as boundaries
var boundariesNode = void 0;
if (boundariesElement === 'scrollParent') {
boundariesNode = getScrollParent(getParentNode(reference));
if (boundariesNode.nodeName === 'BODY') {
boundariesNode = popper.ownerDocument.documentElement;
}
} else if (boundariesElement === 'window') {
boundariesNode = popper.ownerDocument.documentElement;
} else {
boundariesNode = boundariesElement;
}
var offsets = getOffsetRectRelativeToArbitraryNode(boundariesNode, offsetParent, fixedPosition);
// In case of HTML, we need a different computation
if (boundariesNode.nodeName === 'HTML' && !isFixed(offsetParent)) {
var _getWindowSizes = getWindowSizes(popper.ownerDocument),
height = _getWindowSizes.height,
width = _getWindowSizes.width;
boundaries.top += offsets.top - offsets.marginTop;
boundaries.bottom = height + offsets.top;
boundaries.left += offsets.left - offsets.marginLeft;
boundaries.right = width + offsets.left;
} else {
// for all the other DOM elements, this one is good
boundaries = offsets;
}
}
// Add paddings
padding = padding || 0;
var isPaddingNumber = typeof padding === 'number';
boundaries.left += isPaddingNumber ? padding : padding.left || 0;
boundaries.top += isPaddingNumber ? padding : padding.top || 0;
boundaries.right -= isPaddingNumber ? padding : padding.right || 0;
boundaries.bottom -= isPaddingNumber ? padding : padding.bottom || 0;
return boundaries;
}
function getArea(_ref) {
var width = _ref.width,
height = _ref.height;
return width * height;
}
/**
* Utility used to transform the `auto` placement to the placement with more
* available space.
* @method
* @memberof Popper.Utils
* @argument {Object} data - The data object generated by update method
* @argument {Object} options - Modifiers configuration and options
* @returns {Object} The data object, properly modified
*/
function computeAutoPlacement(placement, refRect, popper, reference, boundariesElement) {
var padding = arguments.length > 5 && arguments[5] !== undefined ? arguments[5] : 0;
if (placement.indexOf('auto') === -1) {
return placement;
}
var boundaries = getBoundaries(popper, reference, padding, boundariesElement);
var rects = {
top: {
width: boundaries.width,
height: refRect.top - boundaries.top
},
right: {
width: boundaries.right - refRect.right,
height: boundaries.height
},
bottom: {
width: boundaries.width,
height: boundaries.bottom - refRect.bottom
},
left: {
width: refRect.left - boundaries.left,
height: boundaries.height
}
};
var sortedAreas = Object.keys(rects).map(function (key) {
return _extends({
key: key
}, rects[key], {
area: getArea(rects[key])
});
}).sort(function (a, b) {
return b.area - a.area;
});
var filteredAreas = sortedAreas.filter(function (_ref2) {
var width = _ref2.width,
height = _ref2.height;
return width >= popper.clientWidth && height >= popper.clientHeight;
});
var computedPlacement = filteredAreas.length > 0 ? filteredAreas[0].key : sortedAreas[0].key;
var variation = placement.split('-')[1];
return computedPlacement + (variation ? '-' + variation : '');
}
/**
* Get offsets to the reference element
* @method
* @memberof Popper.Utils
* @param {Object} state
* @param {Element} popper - the popper element
* @param {Element} reference - the reference element (the popper will be relative to this)
* @param {Element} fixedPosition - is in fixed position mode
* @returns {Object} An object containing the offsets which will be applied to the popper
*/
function getReferenceOffsets(state, popper, reference) {
var fixedPosition = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : null;
var commonOffsetParent = fixedPosition ? getFixedPositionOffsetParent(popper) : findCommonOffsetParent(popper, getReferenceNode(reference));
return getOffsetRectRelativeToArbitraryNode(reference, commonOffsetParent, fixedPosition);
}
/**
* Get the outer sizes of the given element (offset size + margins)
* @method
* @memberof Popper.Utils
* @argument {Element} element
* @returns {Object} object containing width and height properties
*/
function getOuterSizes(element) {
var window = element.ownerDocument.defaultView;
var styles = window.getComputedStyle(element);
var x = parseFloat(styles.marginTop || 0) + parseFloat(styles.marginBottom || 0);
var y = parseFloat(styles.marginLeft || 0) + parseFloat(styles.marginRight || 0);
var result = {
width: element.offsetWidth + y,
height: element.offsetHeight + x
};
return result;
}
/**
* Get the opposite placement of the given one
* @method
* @memberof Popper.Utils
* @argument {String} placement
* @returns {String} flipped placement
*/
function getOppositePlacement(placement) {
var hash = { left: 'right', right: 'left', bottom: 'top', top: 'bottom' };
return placement.replace(/left|right|bottom|top/g, function (matched) {
return hash[matched];
});
}
/**
* Get offsets to the popper
* @method
* @memberof Popper.Utils
* @param {Object} position - CSS position the Popper will get applied
* @param {HTMLElement} popper - the popper element
* @param {Object} referenceOffsets - the reference offsets (the popper will be relative to this)
* @param {String} placement - one of the valid placement options
* @returns {Object} popperOffsets - An object containing the offsets which will be applied to the popper
*/
function getPopperOffsets(popper, referenceOffsets, placement) {
placement = placement.split('-')[0];
// Get popper node sizes
var popperRect = getOuterSizes(popper);
// Add position, width and height to our offsets object
var popperOffsets = {
width: popperRect.width,
height: popperRect.height
};
// depending by the popper placement we have to compute its offsets slightly differently
var isHoriz = ['right', 'left'].indexOf(placement) !== -1;
var mainSide = isHoriz ? 'top' : 'left';
var secondarySide = isHoriz ? 'left' : 'top';
var measurement = isHoriz ? 'height' : 'width';
var secondaryMeasurement = !isHoriz ? 'height' : 'width';
popperOffsets[mainSide] = referenceOffsets[mainSide] + referenceOffsets[measurement] / 2 - popperRect[measurement] / 2;
if (placement === secondarySide) {
popperOffsets[secondarySide] = referenceOffsets[secondarySide] - popperRect[secondaryMeasurement];
} else {
popperOffsets[secondarySide] = referenceOffsets[getOppositePlacement(secondarySide)];
}
return popperOffsets;
}
/**
* Mimics the `find` method of Array
* @method
* @memberof Popper.Utils
* @argument {Array} arr
* @argument prop
* @argument value
* @returns index or -1
*/
function find(arr, check) {
// use native find if supported
if (Array.prototype.find) {
return arr.find(check);
}
// use `filter` to obtain the same behavior of `find`
return arr.filter(check)[0];
}
/**
* Return the index of the matching object
* @method
* @memberof Popper.Utils
* @argument {Array} arr
* @argument prop
* @argument value
* @returns index or -1
*/
function findIndex(arr, prop, value) {
// use native findIndex if supported
if (Array.prototype.findIndex) {
return arr.findIndex(function (cur) {
return cur[prop] === value;
});
}
// use `find` + `indexOf` if `findIndex` isn't supported
var match = find(arr, function (obj) {
return obj[prop] === value;
});
return arr.indexOf(match);
}
/**
* Loop trough the list of modifiers and run them in order,
* each of them will then edit the data object.
* @method
* @memberof Popper.Utils
* @param {dataObject} data
* @param {Array} modifiers
* @param {String} ends - Optional modifier name used as stopper
* @returns {dataObject}
*/
function runModifiers(modifiers, data, ends) {
var modifiersToRun = ends === undefined ? modifiers : modifiers.slice(0, findIndex(modifiers, 'name', ends));
modifiersToRun.forEach(function (modifier) {
if (modifier['function']) {
// eslint-disable-line dot-notation
console.warn('`modifier.function` is deprecated, use `modifier.fn`!');
}
var fn = modifier['function'] || modifier.fn; // eslint-disable-line dot-notation
if (modifier.enabled && isFunction(fn)) {
// Add properties to offsets to make them a complete clientRect object
// we do this before each modifier to make sure the previous one doesn't
// mess with these values
data.offsets.popper = getClientRect(data.offsets.popper);
data.offsets.reference = getClientRect(data.offsets.reference);
data = fn(data, modifier);
}
});
return data;
}
/**
* Updates the position of the popper, computing the new offsets and applying
* the new style.
* Prefer `scheduleUpdate` over `update` because of performance reasons.
* @method
* @memberof Popper
*/
function update() {
// if popper is destroyed, don't perform any further update
if (this.state.isDestroyed) {
return;
}
var data = {
instance: this,
styles: {},
arrowStyles: {},
attributes: {},
flipped: false,
offsets: {}
};
// compute reference element offsets
data.offsets.reference = getReferenceOffsets(this.state, this.popper, this.reference, this.options.positionFixed);
// compute auto placement, store placement inside the data object,
// modifiers will be able to edit `placement` if needed
// and refer to originalPlacement to know the original value
data.placement = computeAutoPlacement(this.options.placement, data.offsets.reference, this.popper, this.reference, this.options.modifiers.flip.boundariesElement, this.options.modifiers.flip.padding);
// store the computed placement inside `originalPlacement`
data.originalPlacement = data.placement;
data.positionFixed = this.options.positionFixed;
// compute the popper offsets
data.offsets.popper = getPopperOffsets(this.popper, data.offsets.reference, data.placement);
data.offsets.popper.position = this.options.positionFixed ? 'fixed' : 'absolute';
// run the modifiers
data = runModifiers(this.modifiers, data);
// the first `update` will call `onCreate` callback
// the other ones will call `onUpdate` callback
if (!this.state.isCreated) {
this.state.isCreated = true;
this.options.onCreate(data);
} else {
this.options.onUpdate(data);
}
}
/**
* Helper used to know if the given modifier is enabled.
* @method
* @memberof Popper.Utils
* @returns {Boolean}
*/
function isModifierEnabled(modifiers, modifierName) {
return modifiers.some(function (_ref) {
var name = _ref.name,
enabled = _ref.enabled;
return enabled && name === modifierName;
});
}
/**
* Get the prefixed supported property name
* @method
* @memberof Popper.Utils
* @argument {String} property (camelCase)
* @returns {String} prefixed property (camelCase or PascalCase, depending on the vendor prefix)
*/
function getSupportedPropertyName(property) {
var prefixes = [false, 'ms', 'Webkit', 'Moz', 'O'];
var upperProp = property.charAt(0).toUpperCase() + property.slice(1);
for (var i = 0; i < prefixes.length; i++) {
var prefix = prefixes[i];
var toCheck = prefix ? '' + prefix + upperProp : property;
if (typeof document.body.style[toCheck] !== 'undefined') {
return toCheck;
}
}
return null;
}
/**
* Destroys the popper.
* @method
* @memberof Popper
*/
function destroy() {
this.state.isDestroyed = true;
// touch DOM only if `applyStyle` modifier is enabled
if (isModifierEnabled(this.modifiers, 'applyStyle')) {
this.popper.removeAttribute('x-placement');
this.popper.style.position = '';
this.popper.style.top = '';
this.popper.style.left = '';
this.popper.style.right = '';
this.popper.style.bottom = '';
this.popper.style.willChange = '';
this.popper.style[getSupportedPropertyName('transform')] = '';
}
this.disableEventListeners();
// remove the popper if user explicitly asked for the deletion on destroy
// do not use `remove` because IE11 doesn't support it
if (this.options.removeOnDestroy) {
this.popper.parentNode.removeChild(this.popper);
}
return this;
}
/**
* Get the window associated with the element
* @argument {Element} element
* @returns {Window}
*/
function getWindow(element) {
var ownerDocument = element.ownerDocument;
return ownerDocument ? ownerDocument.defaultView : window;
}
function attachToScrollParents(scrollParent, event, callback, scrollParents) {
var isBody = scrollParent.nodeName === 'BODY';
var target = isBody ? scrollParent.ownerDocument.defaultView : scrollParent;
target.addEventListener(event, callback, { passive: true });
if (!isBody) {
attachToScrollParents(getScrollParent(target.parentNode), event, callback, scrollParents);
}
scrollParents.push(target);
}
/**
* Setup needed event listeners used to update the popper position
* @method
* @memberof Popper.Utils
* @private
*/
function setupEventListeners(reference, options, state, updateBound) {
// Resize event listener on window
state.updateBound = updateBound;
getWindow(reference).addEventListener('resize', state.updateBound, { passive: true });
// Scroll event listener on scroll parents
var scrollElement = getScrollParent(reference);
attachToScrollParents(scrollElement, 'scroll', state.updateBound, state.scrollParents);
state.scrollElement = scrollElement;
state.eventsEnabled = true;
return state;
}
/**
* It will add resize/scroll events and start recalculating
* position of the popper element when they are triggered.
* @method
* @memberof Popper
*/
function enableEventListeners() {
if (!this.state.eventsEnabled) {
this.state = setupEventListeners(this.reference, this.options, this.state, this.scheduleUpdate);
}
}
/**
* Remove event listeners used to update the popper position
* @method
* @memberof Popper.Utils
* @private
*/
function removeEventListeners(reference, state) {
// Remove resize event listener on window
getWindow(reference).removeEventListener('resize', state.updateBound);
// Remove scroll event listener on scroll parents
state.scrollParents.forEach(function (target) {
target.removeEventListener('scroll', state.updateBound);
});
// Reset state
state.updateBound = null;
state.scrollParents = [];
state.scrollElement = null;
state.eventsEnabled = false;
return state;
}
/**
* It will remove resize/scroll events and won't recalculate popper position
* when they are triggered. It also won't trigger `onUpdate` callback anymore,
* unless you call `update` method manually.
* @method
* @memberof Popper
*/
function disableEventListeners() {
if (this.state.eventsEnabled) {
cancelAnimationFrame(this.scheduleUpdate);
this.state = removeEventListeners(this.reference, this.state);
}
}
/**
* Tells if a given input is a number
* @method
* @memberof Popper.Utils
* @param {*} input to check
* @return {Boolean}
*/
function isNumeric(n) {
return n !== '' && !isNaN(parseFloat(n)) && isFinite(n);
}
/**
* Set the style to the given popper
* @method
* @memberof Popper.Utils
* @argument {Element} element - Element to apply the style to
* @argument {Object} styles
* Object with a list of properties and values which will be applied to the element
*/
function setStyles(element, styles) {
Object.keys(styles).forEach(function (prop) {
var unit = '';
// add unit if the value is numeric and is one of the following
if (['width', 'height', 'top', 'right', 'bottom', 'left'].indexOf(prop) !== -1 && isNumeric(styles[prop])) {
unit = 'px';
}
element.style[prop] = styles[prop] + unit;
});
}
/**
* Set the attributes to the given popper
* @method
* @memberof Popper.Utils
* @argument {Element} element - Element to apply the attributes to
* @argument {Object} styles
* Object with a list of properties and values which will be applied to the element
*/
function setAttributes(element, attributes) {
Object.keys(attributes).forEach(function (prop) {
var value = attributes[prop];
if (value !== false) {
element.setAttribute(prop, attributes[prop]);
} else {
element.removeAttribute(prop);
}
});
}
/**
* @function
* @memberof Modifiers
* @argument {Object} data - The data object generated by `update` method
* @argument {Object} data.styles - List of style properties - values to apply to popper element
* @argument {Object} data.attributes - List of attribute properties - values to apply to popper element
* @argument {Object} options - Modifiers configuration and options
* @returns {Object} The same data object
*/
function applyStyle(data) {
// any property present in `data.styles` will be applied to the popper,
// in this way we can make the 3rd party modifiers add custom styles to it
// Be aware, modifiers could override the properties defined in the previous
// lines of this modifier!
setStyles(data.instance.popper, data.styles);
// any property present in `data.attributes` will be applied to the popper,
// they will be set as HTML attributes of the element
setAttributes(data.instance.popper, data.attributes);
// if arrowElement is defined and arrowStyles has some properties
if (data.arrowElement && Object.keys(data.arrowStyles).length) {
setStyles(data.arrowElement, data.arrowStyles);
}
return data;
}
/**
* Set the x-placement attribute before everything else because it could be used
* to add margins to the popper margins needs to be calculated to get the
* correct popper offsets.
* @method
* @memberof Popper.modifiers
* @param {HTMLElement} reference - The reference element used to position the popper
* @param {HTMLElement} popper - The HTML element used as popper
* @param {Object} options - Popper.js options
*/
function applyStyleOnLoad(reference, popper, options, modifierOptions, state) {
// compute reference element offsets
var referenceOffsets = getReferenceOffsets(state, popper, reference, options.positionFixed);
// compute auto placement, store placement inside the data object,
// modifiers will be able to edit `placement` if needed
// and refer to originalPlacement to know the original value
var placement = computeAutoPlacement(options.placement, referenceOffsets, popper, reference, options.modifiers.flip.boundariesElement, options.modifiers.flip.padding);
popper.setAttribute('x-placement', placement);
// Apply `position` to popper before anything else because
// without the position applied we can't guarantee correct computations
setStyles(popper, { position: options.positionFixed ? 'fixed' : 'absolute' });
return options;
}
/**
* @function
* @memberof Popper.Utils
* @argument {Object} data - The data object generated by `update` method
* @argument {Boolean} shouldRound - If the offsets should be rounded at all
* @returns {Object} The popper's position offsets rounded
*
* The tale of pixel-perfect positioning. It's still not 100% perfect, but as
* good as it can be within reason.
* Discussion here: https://github.com/FezVrasta/popper.js/pull/715
*
* Low DPI screens cause a popper to be blurry if not using full pixels (Safari
* as well on High DPI screens).
*
* Firefox prefers no rounding for positioning and does not have blurriness on
* high DPI screens.
*
* Only horizontal placement and left/right values need to be considered.
*/
function getRoundedOffsets(data, shouldRound) {
var _data$offsets = data.offsets,
popper = _data$offsets.popper,
reference = _data$offsets.reference;
var round = Math.round,
floor = Math.floor;
var noRound = function noRound(v) {
return v;
};
var referenceWidth = round(reference.width);
var popperWidth = round(popper.width);
var isVertical = ['left', 'right'].indexOf(data.placement) !== -1;
var isVariation = data.placement.indexOf('-') !== -1;
var sameWidthParity = referenceWidth % 2 === popperWidth % 2;
var bothOddWidth = referenceWidth % 2 === 1 && popperWidth % 2 === 1;
var horizontalToInteger = !shouldRound ? noRound : isVertical || isVariation || sameWidthParity ? round : floor;
var verticalToInteger = !shouldRound ? noRound : round;
return {
left: horizontalToInteger(bothOddWidth && !isVariation && shouldRound ? popper.left - 1 : popper.left),
top: verticalToInteger(popper.top),
bottom: verticalToInteger(popper.bottom),
right: horizontalToInteger(popper.right)
};
}
var isFirefox = isBrowser && /Firefox/i.test(navigator.userAgent);
/**
* @function
* @memberof Modifiers
* @argument {Object} data - The data object generated by `update` method
* @argument {Object} options - Modifiers configuration and options
* @returns {Object} The data object, properly modified
*/
function computeStyle(data, options) {
var x = options.x,
y = options.y;
var popper = data.offsets.popper;
// Remove this legacy support in Popper.js v2
var legacyGpuAccelerationOption = find(data.instance.modifiers, function (modifier) {
return modifier.name === 'applyStyle';
}).gpuAcceleration;
if (legacyGpuAccelerationOption !== undefined) {
console.warn('WARNING: `gpuAcceleration` option moved to `computeStyle` modifier and will not be supported in future versions of Popper.js!');
}
var gpuAcceleration = legacyGpuAccelerationOption !== undefined ? legacyGpuAccelerationOption : options.gpuAcceleration;
var offsetParent = getOffsetParent(data.instance.popper);
var offsetParentRect = getBoundingClientRect(offsetParent);
// Styles
var styles = {
position: popper.position
};
var offsets = getRoundedOffsets(data, window.devicePixelRatio < 2 || !isFirefox);
var sideA = x === 'bottom' ? 'top' : 'bottom';
var sideB = y === 'right' ? 'left' : 'right';
// if gpuAcceleration is set to `true` and transform is supported,
// we use `translate3d` to apply the position to the popper we
// automatically use the supported prefixed version if needed
var prefixedProperty = getSupportedPropertyName('transform');
// now, let's make a step back and look at this code closely (wtf?)
// If the content of the popper grows once it's been positioned, it
// may happen that the popper gets misplaced because of the new content
// overflowing its reference element
// To avoid this problem, we provide two options (x and y), which allow
// the consumer to define the offset origin.
// If we position a popper on top of a reference element, we can set
// `x` to `top` to make the popper grow towards its top instead of
// its bottom.
var left = void 0,
top = void 0;
if (sideA === 'bottom') {
// when offsetParent is the positioning is relative to the bottom of the screen (excluding the scrollbar)
// and not the bottom of the html element
if (offsetParent.nodeName === 'HTML') {
top = -offsetParent.clientHeight + offsets.bottom;
} else {
top = -offsetParentRect.height + offsets.bottom;
}
} else {
top = offsets.top;
}
if (sideB === 'right') {
if (offsetParent.nodeName === 'HTML') {
left = -offsetParent.clientWidth + offsets.right;
} else {
left = -offsetParentRect.width + offsets.right;
}
} else {
left = offsets.left;
}
if (gpuAcceleration && prefixedProperty) {
styles[prefixedProperty] = 'translate3d(' + left + 'px, ' + top + 'px, 0)';
styles[sideA] = 0;
styles[sideB] = 0;
styles.willChange = 'transform';
} else {
// othwerise, we use the standard `top`, `left`, `bottom` and `right` properties
var invertTop = sideA === 'bottom' ? -1 : 1;
var invertLeft = sideB === 'right' ? -1 : 1;
styles[sideA] = top * invertTop;
styles[sideB] = left * invertLeft;
styles.willChange = sideA + ', ' + sideB;
}
// Attributes
var attributes = {
'x-placement': data.placement
};
// Update `data` attributes, styles and arrowStyles
data.attributes = _extends({}, attributes, data.attributes);
data.styles = _extends({}, styles, data.styles);
data.arrowStyles = _extends({}, data.offsets.arrow, data.arrowStyles);
return data;
}
/**
* Helper used to know if the given modifier depends from another one.
* It checks if the needed modifier is listed and enabled.
* @method
* @memberof Popper.Utils
* @param {Array} modifiers - list of modifiers
* @param {String} requestingName - name of requesting modifier
* @param {String} requestedName - name of requested modifier
* @returns {Boolean}
*/
function isModifierRequired(modifiers, requestingName, requestedName) {
var requesting = find(modifiers, function (_ref) {
var name = _ref.name;
return name === requestingName;
});
var isRequired = !!requesting && modifiers.some(function (modifier) {
return modifier.name === requestedName && modifier.enabled && modifier.order < requesting.order;
});
if (!isRequired) {
var _requesting = '`' + requestingName + '`';
var requested = '`' + requestedName + '`';
console.warn(requested + ' modifier is required by ' + _requesting + ' modifier in order to work, be sure to include it before ' + _requesting + '!');
}
return isRequired;
}
/**
* @function
* @memberof Modifiers
* @argument {Object} data - The data object generated by update method
* @argument {Object} options - Modifiers configuration and options
* @returns {Object} The data object, properly modified
*/
function arrow(data, options) {
var _data$offsets$arrow;
// arrow depends on keepTogether in order to work
if (!isModifierRequired(data.instance.modifiers, 'arrow', 'keepTogether')) {
return data;
}
var arrowElement = options.element;
// if arrowElement is a string, suppose it's a CSS selector
if (typeof arrowElement === 'string') {
arrowElement = data.instance.popper.querySelector(arrowElement);
// if arrowElement is not found, don't run the modifier
if (!arrowElement) {
return data;
}
} else {
// if the arrowElement isn't a query selector we must check that the
// provided DOM node is child of its popper node
if (!data.instance.popper.contains(arrowElement)) {
console.warn('WARNING: `arrow.element` must be child of its popper element!');
return data;
}
}
var placement = data.placement.split('-')[0];
var _data$offsets = data.offsets,
popper = _data$offsets.popper,
reference = _data$offsets.reference;
var isVertical = ['left', 'right'].indexOf(placement) !== -1;
var len = isVertical ? 'height' : 'width';
var sideCapitalized = isVertical ? 'Top' : 'Left';
var side = sideCapitalized.toLowerCase();
var altSide = isVertical ? 'left' : 'top';
var opSide = isVertical ? 'bottom' : 'right';
var arrowElementSize = getOuterSizes(arrowElement)[len];
//
// extends keepTogether behavior making sure the popper and its
// reference have enough pixels in conjunction
//
// top/left side
if (reference[opSide] - arrowElementSize < popper[side]) {
data.offsets.popper[side] -= popper[side] - (reference[opSide] - arrowElementSize);
}
// bottom/right side
if (reference[side] + arrowElementSize > popper[opSide]) {
data.offsets.popper[side] += reference[side] + arrowElementSize - popper[opSide];
}
data.offsets.popper = getClientRect(data.offsets.popper);
// compute center of the popper
var center = reference[side] + reference[len] / 2 - arrowElementSize / 2;
// Compute the sideValue using the updated popper offsets
// take popper margin in account because we don't have this info available
var css = getStyleComputedProperty(data.instance.popper);
var popperMarginSide = parseFloat(css['margin' + sideCapitalized]);
var popperBorderSide = parseFloat(css['border' + sideCapitalized + 'Width']);
var sideValue = center - data.offsets.popper[side] - popperMarginSide - popperBorderSide;
// prevent arrowElement from being placed not contiguously to its popper
sideValue = Math.max(Math.min(popper[len] - arrowElementSize, sideValue), 0);
data.arrowElement = arrowElement;
data.offsets.arrow = (_data$offsets$arrow = {}, defineProperty(_data$offsets$arrow, side, Math.round(sideValue)), defineProperty(_data$offsets$arrow, altSide, ''), _data$offsets$arrow);
return data;
}
/**
* Get the opposite placement variation of the given one
* @method
* @memberof Popper.Utils
* @argument {String} placement variation
* @returns {String} flipped placement variation
*/
function getOppositeVariation(variation) {
if (variation === 'end') {
return 'start';
} else if (variation === 'start') {
return 'end';
}
return variation;
}
/**
* List of accepted placements to use as values of the `placement` option.
* Valid placements are:
* - `auto`
* - `top`
* - `right`
* - `bottom`
* - `left`
*
* Each placement can have a variation from this list:
* - `-start`
* - `-end`
*
* Variations are interpreted easily if you think of them as the left to right
* written languages. Horizontally (`top` and `bottom`), `start` is left and `end`
* is right.
* Vertically (`left` and `right`), `start` is top and `end` is bottom.
*
* Some valid examples are:
* - `top-end` (on top of reference, right aligned)
* - `right-start` (on right of reference, top aligned)
* - `bottom` (on bottom, centered)
* - `auto-end` (on the side with more space available, alignment depends by placement)
*
* @static
* @type {Array}
* @enum {String}
* @readonly
* @method placements
* @memberof Popper
*/
var placements = ['auto-start', 'auto', 'auto-end', 'top-start', 'top', 'top-end', 'right-start', 'right', 'right-end', 'bottom-end', 'bottom', 'bottom-start', 'left-end', 'left', 'left-start'];
// Get rid of `auto` `auto-start` and `auto-end`
var validPlacements = placements.slice(3);
/**
* Given an initial placement, returns all the subsequent placements
* clockwise (or counter-clockwise).
*
* @method
* @memberof Popper.Utils
* @argument {String} placement - A valid placement (it accepts variations)
* @argument {Boolean} counter - Set to true to walk the placements counterclockwise
* @returns {Array} placements including their variations
*/
function clockwise(placement) {
var counter = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false;
var index = validPlacements.indexOf(placement);
var arr = validPlacements.slice(index + 1).concat(validPlacements.slice(0, index));
return counter ? arr.reverse() : arr;
}
var BEHAVIORS = {
FLIP: 'flip',
CLOCKWISE: 'clockwise',
COUNTERCLOCKWISE: 'counterclockwise'
};
/**
* @function
* @memberof Modifiers
* @argument {Object} data - The data object generated by update method
* @argument {Object} options - Modifiers configuration and options
* @returns {Object} The data object, properly modified
*/
function flip(data, options) {
// if `inner` modifier is enabled, we can't use the `flip` modifier
if (isModifierEnabled(data.instance.modifiers, 'inner')) {
return data;
}
if (data.flipped && data.placement === data.originalPlacement) {
// seems like flip is trying to loop, probably there's not enough space on any of the flippable sides
return data;
}
var boundaries = getBoundaries(data.instance.popper, data.instance.reference, options.padding, options.boundariesElement, data.positionFixed);
var placement = data.placement.split('-')[0];
var placementOpposite = getOppositePlacement(placement);
var variation = data.placement.split('-')[1] || '';
var flipOrder = [];
switch (options.behavior) {
case BEHAVIORS.FLIP:
flipOrder = [placement, placementOpposite];
break;
case BEHAVIORS.CLOCKWISE:
flipOrder = clockwise(placement);
break;
case BEHAVIORS.COUNTERCLOCKWISE:
flipOrder = clockwise(placement, true);
break;
default:
flipOrder = options.behavior;
}
flipOrder.forEach(function (step, index) {
if (placement !== step || flipOrder.length === index + 1) {
return data;
}
placement = data.placement.split('-')[0];
placementOpposite = getOppositePlacement(placement);
var popperOffsets = data.offsets.popper;
var refOffsets = data.offsets.reference;
// using floor because the reference offsets may contain decimals we are not going to consider here
var floor = Math.floor;
var overlapsRef = placement === 'left' && floor(popperOffsets.right) > floor(refOffsets.left) || placement === 'right' && floor(popperOffsets.left) < floor(refOffsets.right) || placement === 'top' && floor(popperOffsets.bottom) > floor(refOffsets.top) || placement === 'bottom' && floor(popperOffsets.top) < floor(refOffsets.bottom);
var overflowsLeft = floor(popperOffsets.left) < floor(boundaries.left);
var overflowsRight = floor(popperOffsets.right) > floor(boundaries.right);
var overflowsTop = floor(popperOffsets.top) < floor(boundaries.top);
var overflowsBottom = floor(popperOffsets.bottom) > floor(boundaries.bottom);
var overflowsBoundaries = placement === 'left' && overflowsLeft || placement === 'right' && overflowsRight || placement === 'top' && overflowsTop || placement === 'bottom' && overflowsBottom;
// flip the variation if required
var isVertical = ['top', 'bottom'].indexOf(placement) !== -1;
// flips variation if reference element overflows boundaries
var flippedVariationByRef = !!options.flipVariations && (isVertical && variation === 'start' && overflowsLeft || isVertical && variation === 'end' && overflowsRight || !isVertical && variation === 'start' && overflowsTop || !isVertical && variation === 'end' && overflowsBottom);
// flips variation if popper content overflows boundaries
var flippedVariationByContent = !!options.flipVariationsByContent && (isVertical && variation === 'start' && overflowsRight || isVertical && variation === 'end' && overflowsLeft || !isVertical && variation === 'start' && overflowsBottom || !isVertical && variation === 'end' && overflowsTop);
var flippedVariation = flippedVariationByRef || flippedVariationByContent;
if (overlapsRef || overflowsBoundaries || flippedVariation) {
// this boolean to detect any flip loop
data.flipped = true;
if (overlapsRef || overflowsBoundaries) {
placement = flipOrder[index + 1];
}
if (flippedVariation) {
variation = getOppositeVariation(variation);
}
data.placement = placement + (variation ? '-' + variation : '');
// this object contains `position`, we want to preserve it along with
// any additional property we may add in the future
data.offsets.popper = _extends({}, data.offsets.popper, getPopperOffsets(data.instance.popper, data.offsets.reference, data.placement));
data = runModifiers(data.instance.modifiers, data, 'flip');
}
});
return data;
}
/**
* @function
* @memberof Modifiers
* @argument {Object} data - The data object generated by update method
* @argument {Object} options - Modifiers configuration and options
* @returns {Object} The data object, properly modified
*/
function keepTogether(data) {
var _data$offsets = data.offsets,
popper = _data$offsets.popper,
reference = _data$offsets.reference;
var placement = data.placement.split('-')[0];
var floor = Math.floor;
var isVertical = ['top', 'bottom'].indexOf(placement) !== -1;
var side = isVertical ? 'right' : 'bottom';
var opSide = isVertical ? 'left' : 'top';
var measurement = isVertical ? 'width' : 'height';
if (popper[side] < floor(reference[opSide])) {
data.offsets.popper[opSide] = floor(reference[opSide]) - popper[measurement];
}
if (popper[opSide] > floor(reference[side])) {
data.offsets.popper[opSide] = floor(reference[side]);
}
return data;
}
/**
* Converts a string containing value + unit into a px value number
* @function
* @memberof {modifiers~offset}
* @private
* @argument {String} str - Value + unit string
* @argument {String} measurement - `height` or `width`
* @argument {Object} popperOffsets
* @argument {Object} referenceOffsets
* @returns {Number|String}
* Value in pixels, or original string if no values were extracted
*/
function toValue(str, measurement, popperOffsets, referenceOffsets) {
// separate value from unit
var split = str.match(/((?:\-|\+)?\d*\.?\d*)(.*)/);
var value = +split[1];
var unit = split[2];
// If it's not a number it's an operator, I guess
if (!value) {
return str;
}
if (unit.indexOf('%') === 0) {
var element = void 0;
switch (unit) {
case '%p':
element = popperOffsets;
break;
case '%':
case '%r':
default:
element = referenceOffsets;
}
var rect = getClientRect(element);
return rect[measurement] / 100 * value;
} else if (unit === 'vh' || unit === 'vw') {
// if is a vh or vw, we calculate the size based on the viewport
var size = void 0;
if (unit === 'vh') {
size = Math.max(document.documentElement.clientHeight, window.innerHeight || 0);
} else {
size = Math.max(document.documentElement.clientWidth, window.innerWidth || 0);
}
return size / 100 * value;
} else {
// if is an explicit pixel unit, we get rid of the unit and keep the value
// if is an implicit unit, it's px, and we return just the value
return value;
}
}
/**
* Parse an `offset` string to extrapolate `x` and `y` numeric offsets.
* @function
* @memberof {modifiers~offset}
* @private
* @argument {String} offset
* @argument {Object} popperOffsets
* @argument {Object} referenceOffsets
* @argument {String} basePlacement
* @returns {Array} a two cells array with x and y offsets in numbers
*/
function parseOffset(offset, popperOffsets, referenceOffsets, basePlacement) {
var offsets = [0, 0];
// Use height if placement is left or right and index is 0 otherwise use width
// in this way the first offset will use an axis and the second one
// will use the other one
var useHeight = ['right', 'left'].indexOf(basePlacement) !== -1;
// Split the offset string to obtain a list of values and operands
// The regex addresses values with the plus or minus sign in front (+10, -20, etc)
var fragments = offset.split(/(\+|\-)/).map(function (frag) {
return frag.trim();
});
// Detect if the offset string contains a pair of values or a single one
// they could be separated by comma or space
var divider = fragments.indexOf(find(fragments, function (frag) {
return frag.search(/,|\s/) !== -1;
}));
if (fragments[divider] && fragments[divider].indexOf(',') === -1) {
console.warn('Offsets separated by white space(s) are deprecated, use a comma (,) instead.');
}
// If divider is found, we divide the list of values and operands to divide
// them by ofset X and Y.
var splitRegex = /\s*,\s*|\s+/;
var ops = divider !== -1 ? [fragments.slice(0, divider).concat([fragments[divider].split(splitRegex)[0]]), [fragments[divider].split(splitRegex)[1]].concat(fragments.slice(divider + 1))] : [fragments];
// Convert the values with units to absolute pixels to allow our computations
ops = ops.map(function (op, index) {
// Most of the units rely on the orientation of the popper
var measurement = (index === 1 ? !useHeight : useHeight) ? 'height' : 'width';
var mergeWithPrevious = false;
return op
// This aggregates any `+` or `-` sign that aren't considered operators
// e.g.: 10 + +5 => [10, +, +5]
.reduce(function (a, b) {
if (a[a.length - 1] === '' && ['+', '-'].indexOf(b) !== -1) {
a[a.length - 1] = b;
mergeWithPrevious = true;
return a;
} else if (mergeWithPrevious) {
a[a.length - 1] += b;
mergeWithPrevious = false;
return a;
} else {
return a.concat(b);
}
}, [])
// Here we convert the string values into number values (in px)
.map(function (str) {
return toValue(str, measurement, popperOffsets, referenceOffsets);
});
});
// Loop trough the offsets arrays and execute the operations
ops.forEach(function (op, index) {
op.forEach(function (frag, index2) {
if (isNumeric(frag)) {
offsets[index] += frag * (op[index2 - 1] === '-' ? -1 : 1);
}
});
});
return offsets;
}
/**
* @function
* @memberof Modifiers
* @argument {Object} data - The data object generated by update method
* @argument {Object} options - Modifiers configuration and options
* @argument {Number|String} options.offset=0
* The offset value as described in the modifier description
* @returns {Object} The data object, properly modified
*/
function offset(data, _ref) {
var offset = _ref.offset;
var placement = data.placement,
_data$offsets = data.offsets,
popper = _data$offsets.popper,
reference = _data$offsets.reference;
var basePlacement = placement.split('-')[0];
var offsets = void 0;
if (isNumeric(+offset)) {
offsets = [+offset, 0];
} else {
offsets = parseOffset(offset, popper, reference, basePlacement);
}
if (basePlacement === 'left') {
popper.top += offsets[0];
popper.left -= offsets[1];
} else if (basePlacement === 'right') {
popper.top += offsets[0];
popper.left += offsets[1];
} else if (basePlacement === 'top') {
popper.left += offsets[0];
popper.top -= offsets[1];
} else if (basePlacement === 'bottom') {
popper.left += offsets[0];
popper.top += offsets[1];
}
data.popper = popper;
return data;
}
/**
* @function
* @memberof Modifiers
* @argument {Object} data - The data object generated by `update` method
* @argument {Object} options - Modifiers configuration and options
* @returns {Object} The data object, properly modified
*/
function preventOverflow(data, options) {
var boundariesElement = options.boundariesElement || getOffsetParent(data.instance.popper);
// If offsetParent is the reference element, we really want to
// go one step up and use the next offsetParent as reference to
// avoid to make this modifier completely useless and look like broken
if (data.instance.reference === boundariesElement) {
boundariesElement = getOffsetParent(boundariesElement);
}
// NOTE: DOM access here
// resets the popper's position so that the document size can be calculated excluding
// the size of the popper element itself
var transformProp = getSupportedPropertyName('transform');
var popperStyles = data.instance.popper.style; // assignment to help minification
var top = popperStyles.top,
left = popperStyles.left,
transform = popperStyles[transformProp];
popperStyles.top = '';
popperStyles.left = '';
popperStyles[transformProp] = '';
var boundaries = getBoundaries(data.instance.popper, data.instance.reference, options.padding, boundariesElement, data.positionFixed);
// NOTE: DOM access here
// restores the original style properties after the offsets have been computed
popperStyles.top = top;
popperStyles.left = left;
popperStyles[transformProp] = transform;
options.boundaries = boundaries;
var order = options.priority;
var popper = data.offsets.popper;
var check = {
primary: function primary(placement) {
var value = popper[placement];
if (popper[placement] < boundaries[placement] && !options.escapeWithReference) {
value = Math.max(popper[placement], boundaries[placement]);
}
return defineProperty({}, placement, value);
},
secondary: function secondary(placement) {
var mainSide = placement === 'right' ? 'left' : 'top';
var value = popper[mainSide];
if (popper[placement] > boundaries[placement] && !options.escapeWithReference) {
value = Math.min(popper[mainSide], boundaries[placement] - (placement === 'right' ? popper.width : popper.height));
}
return defineProperty({}, mainSide, value);
}
};
order.forEach(function (placement) {
var side = ['left', 'top'].indexOf(placement) !== -1 ? 'primary' : 'secondary';
popper = _extends({}, popper, check[side](placement));
});
data.offsets.popper = popper;
return data;
}
/**
* @function
* @memberof Modifiers
* @argument {Object} data - The data object generated by `update` method
* @argument {Object} options - Modifiers configuration and options
* @returns {Object} The data object, properly modified
*/
function shift(data) {
var placement = data.placement;
var basePlacement = placement.split('-')[0];
var shiftvariation = placement.split('-')[1];
// if shift shiftvariation is specified, run the modifier
if (shiftvariation) {
var _data$offsets = data.offsets,
reference = _data$offsets.reference,
popper = _data$offsets.popper;
var isVertical = ['bottom', 'top'].indexOf(basePlacement) !== -1;
var side = isVertical ? 'left' : 'top';
var measurement = isVertical ? 'width' : 'height';
var shiftOffsets = {
start: defineProperty({}, side, reference[side]),
end: defineProperty({}, side, reference[side] + reference[measurement] - popper[measurement])
};
data.offsets.popper = _extends({}, popper, shiftOffsets[shiftvariation]);
}
return data;
}
/**
* @function
* @memberof Modifiers
* @argument {Object} data - The data object generated by update method
* @argument {Object} options - Modifiers configuration and options
* @returns {Object} The data object, properly modified
*/
function hide(data) {
if (!isModifierRequired(data.instance.modifiers, 'hide', 'preventOverflow')) {
return data;
}
var refRect = data.offsets.reference;
var bound = find(data.instance.modifiers, function (modifier) {
return modifier.name === 'preventOverflow';
}).boundaries;
if (refRect.bottom < bound.top || refRect.left > bound.right || refRect.top > bound.bottom || refRect.right < bound.left) {
// Avoid unnecessary DOM access if visibility hasn't changed
if (data.hide === true) {
return data;
}
data.hide = true;
data.attributes['x-out-of-boundaries'] = '';
} else {
// Avoid unnecessary DOM access if visibility hasn't changed
if (data.hide === false) {
return data;
}
data.hide = false;
data.attributes['x-out-of-boundaries'] = false;
}
return data;
}
/**
* @function
* @memberof Modifiers
* @argument {Object} data - The data object generated by `update` method
* @argument {Object} options - Modifiers configuration and options
* @returns {Object} The data object, properly modified
*/
function inner(data) {
var placement = data.placement;
var basePlacement = placement.split('-')[0];
var _data$offsets = data.offsets,
popper = _data$offsets.popper,
reference = _data$offsets.reference;
var isHoriz = ['left', 'right'].indexOf(basePlacement) !== -1;
var subtractLength = ['top', 'left'].indexOf(basePlacement) === -1;
popper[isHoriz ? 'left' : 'top'] = reference[basePlacement] - (subtractLength ? popper[isHoriz ? 'width' : 'height'] : 0);
data.placement = getOppositePlacement(placement);
data.offsets.popper = getClientRect(popper);
return data;
}
/**
* Modifier function, each modifier can have a function of this type assigned
* to its `fn` property.
* These functions will be called on each update, this means that you must
* make sure they are performant enough to avoid performance bottlenecks.
*
* @function ModifierFn
* @argument {dataObject} data - The data object generated by `update` method
* @argument {Object} options - Modifiers configuration and options
* @returns {dataObject} The data object, properly modified
*/
/**
* Modifiers are plugins used to alter the behavior of your poppers.
* Popper.js uses a set of 9 modifiers to provide all the basic functionalities
* needed by the library.
*
* Usually you don't want to override the `order`, `fn` and `onLoad` props.
* All the other properties are configurations that could be tweaked.
* @namespace modifiers
*/
var modifiers = {
/**
* Modifier used to shift the popper on the start or end of its reference
* element.
* It will read the variation of the `placement` property.
* It can be one either `-end` or `-start`.
* @memberof modifiers
* @inner
*/
shift: {
/** @prop {number} order=100 - Index used to define the order of execution */
order: 100,
/** @prop {Boolean} enabled=true - Whether the modifier is enabled or not */
enabled: true,
/** @prop {ModifierFn} */
fn: shift
},
/**
* The `offset` modifier can shift your popper on both its axis.
*
* It accepts the following units:
* - `px` or unit-less, interpreted as pixels
* - `%` or `%r`, percentage relative to the length of the reference element
* - `%p`, percentage relative to the length of the popper element
* - `vw`, CSS viewport width unit
* - `vh`, CSS viewport height unit
*
* For length is intended the main axis relative to the placement of the popper.
* This means that if the placement is `top` or `bottom`, the length will be the
* `width`. In case of `left` or `right`, it will be the `height`.
*
* You can provide a single value (as `Number` or `String`), or a pair of values
* as `String` divided by a comma or one (or more) white spaces.
* The latter is a deprecated method because it leads to confusion and will be
* removed in v2.
* Additionally, it accepts additions and subtractions between different units.
* Note that multiplications and divisions aren't supported.
*
* Valid examples are:
* ```
* 10
* '10%'
* '10, 10'
* '10%, 10'
* '10 + 10%'
* '10 - 5vh + 3%'
* '-10px + 5vh, 5px - 6%'
* ```
* > **NB**: If you desire to apply offsets to your poppers in a way that may make them overlap
* > with their reference element, unfortunately, you will have to disable the `flip` modifier.
* > You can read more on this at this [issue](https://github.com/FezVrasta/popper.js/issues/373).
*
* @memberof modifiers
* @inner
*/
offset: {
/** @prop {number} order=200 - Index used to define the order of execution */
order: 200,
/** @prop {Boolean} enabled=true - Whether the modifier is enabled or not */
enabled: true,
/** @prop {ModifierFn} */
fn: offset,
/** @prop {Number|String} offset=0
* The offset value as described in the modifier description
*/
offset: 0
},
/**
* Modifier used to prevent the popper from being positioned outside the boundary.
*
* A scenario exists where the reference itself is not within the boundaries.
* We can say it has "escaped the boundaries" — or just "escaped".
* In this case we need to decide whether the popper should either:
*
* - detach from the reference and remain "trapped" in the boundaries, or
* - if it should ignore the boundary and "escape with its reference"
*
* When `escapeWithReference` is set to`true` and reference is completely
* outside its boundaries, the popper will overflow (or completely leave)
* the boundaries in order to remain attached to the edge of the reference.
*
* @memberof modifiers
* @inner
*/
preventOverflow: {
/** @prop {number} order=300 - Index used to define the order of execution */
order: 300,
/** @prop {Boolean} enabled=true - Whether the modifier is enabled or not */
enabled: true,
/** @prop {ModifierFn} */
fn: preventOverflow,
/**
* @prop {Array} [priority=['left','right','top','bottom']]
* Popper will try to prevent overflow following these priorities by default,
* then, it could overflow on the left and on top of the `boundariesElement`
*/
priority: ['left', 'right', 'top', 'bottom'],
/**
* @prop {number} padding=5
* Amount of pixel used to define a minimum distance between the boundaries
* and the popper. This makes sure the popper always has a little padding
* between the edges of its container
*/
padding: 5,
/**
* @prop {String|HTMLElement} boundariesElement='scrollParent'
* Boundaries used by the modifier. Can be `scrollParent`, `window`,
* `viewport` or any DOM element.
*/
boundariesElement: 'scrollParent'
},
/**
* Modifier used to make sure the reference and its popper stay near each other
* without leaving any gap between the two. Especially useful when the arrow is
* enabled and you want to ensure that it points to its reference element.
* It cares only about the first axis. You can still have poppers with margin
* between the popper and its reference element.
* @memberof modifiers
* @inner
*/
keepTogether: {
/** @prop {number} order=400 - Index used to define the order of execution */
order: 400,
/** @prop {Boolean} enabled=true - Whether the modifier is enabled or not */
enabled: true,
/** @prop {ModifierFn} */
fn: keepTogether
},
/**
* This modifier is used to move the `arrowElement` of the popper to make
* sure it is positioned between the reference element and its popper element.
* It will read the outer size of the `arrowElement` node to detect how many
* pixels of conjunction are needed.
*
* It has no effect if no `arrowElement` is provided.
* @memberof modifiers
* @inner
*/
arrow: {
/** @prop {number} order=500 - Index used to define the order of execution */
order: 500,
/** @prop {Boolean} enabled=true - Whether the modifier is enabled or not */
enabled: true,
/** @prop {ModifierFn} */
fn: arrow,
/** @prop {String|HTMLElement} element='[x-arrow]' - Selector or node used as arrow */
element: '[x-arrow]'
},
/**
* Modifier used to flip the popper's placement when it starts to overlap its
* reference element.
*
* Requires the `preventOverflow` modifier before it in order to work.
*
* **NOTE:** this modifier will interrupt the current update cycle and will
* restart it if it detects the need to flip the placement.
* @memberof modifiers
* @inner
*/
flip: {
/** @prop {number} order=600 - Index used to define the order of execution */
order: 600,
/** @prop {Boolean} enabled=true - Whether the modifier is enabled or not */
enabled: true,
/** @prop {ModifierFn} */
fn: flip,
/**
* @prop {String|Array} behavior='flip'
* The behavior used to change the popper's placement. It can be one of
* `flip`, `clockwise`, `counterclockwise` or an array with a list of valid
* placements (with optional variations)
*/
behavior: 'flip',
/**
* @prop {number} padding=5
* The popper will flip if it hits the edges of the `boundariesElement`
*/
padding: 5,
/**
* @prop {String|HTMLElement} boundariesElement='viewport'
* The element which will define the boundaries of the popper position.
* The popper will never be placed outside of the defined boundaries
* (except if `keepTogether` is enabled)
*/
boundariesElement: 'viewport',
/**
* @prop {Boolean} flipVariations=false
* The popper will switch placement variation between `-start` and `-end` when
* the reference element overlaps its boundaries.
*
* The original placement should have a set variation.
*/
flipVariations: false,
/**
* @prop {Boolean} flipVariationsByContent=false
* The popper will switch placement variation between `-start` and `-end` when
* the popper element overlaps its reference boundaries.
*
* The original placement should have a set variation.
*/
flipVariationsByContent: false
},
/**
* Modifier used to make the popper flow toward the inner of the reference element.
* By default, when this modifier is disabled, the popper will be placed outside
* the reference element.
* @memberof modifiers
* @inner
*/
inner: {
/** @prop {number} order=700 - Index used to define the order of execution */
order: 700,
/** @prop {Boolean} enabled=false - Whether the modifier is enabled or not */
enabled: false,
/** @prop {ModifierFn} */
fn: inner
},
/**
* Modifier used to hide the popper when its reference element is outside of the
* popper boundaries. It will set a `x-out-of-boundaries` attribute which can
* be used to hide with a CSS selector the popper when its reference is
* out of boundaries.
*
* Requires the `preventOverflow` modifier before it in order to work.
* @memberof modifiers
* @inner
*/
hide: {
/** @prop {number} order=800 - Index used to define the order of execution */
order: 800,
/** @prop {Boolean} enabled=true - Whether the modifier is enabled or not */
enabled: true,
/** @prop {ModifierFn} */
fn: hide
},
/**
* Computes the style that will be applied to the popper element to gets
* properly positioned.
*
* Note that this modifier will not touch the DOM, it just prepares the styles
* so that `applyStyle` modifier can apply it. This separation is useful
* in case you need to replace `applyStyle` with a custom implementation.
*
* This modifier has `850` as `order` value to maintain backward compatibility
* with previous versions of Popper.js. Expect the modifiers ordering method
* to change in future major versions of the library.
*
* @memberof modifiers
* @inner
*/
computeStyle: {
/** @prop {number} order=850 - Index used to define the order of execution */
order: 850,
/** @prop {Boolean} enabled=true - Whether the modifier is enabled or not */
enabled: true,
/** @prop {ModifierFn} */
fn: computeStyle,
/**
* @prop {Boolean} gpuAcceleration=true
* If true, it uses the CSS 3D transformation to position the popper.
* Otherwise, it will use the `top` and `left` properties
*/
gpuAcceleration: true,
/**
* @prop {string} [x='bottom']
* Where to anchor the X axis (`bottom` or `top`). AKA X offset origin.
* Change this if your popper should grow in a direction different from `bottom`
*/
x: 'bottom',
/**
* @prop {string} [x='left']
* Where to anchor the Y axis (`left` or `right`). AKA Y offset origin.
* Change this if your popper should grow in a direction different from `right`
*/
y: 'right'
},
/**
* Applies the computed styles to the popper element.
*
* All the DOM manipulations are limited to this modifier. This is useful in case
* you want to integrate Popper.js inside a framework or view library and you
* want to delegate all the DOM manipulations to it.
*
* Note that if you disable this modifier, you must make sure the popper element
* has its position set to `absolute` before Popper.js can do its work!
*
* Just disable this modifier and define your own to achieve the desired effect.
*
* @memberof modifiers
* @inner
*/
applyStyle: {
/** @prop {number} order=900 - Index used to define the order of execution */
order: 900,
/** @prop {Boolean} enabled=true - Whether the modifier is enabled or not */
enabled: true,
/** @prop {ModifierFn} */
fn: applyStyle,
/** @prop {Function} */
onLoad: applyStyleOnLoad,
/**
* @deprecated since version 1.10.0, the property moved to `computeStyle` modifier
* @prop {Boolean} gpuAcceleration=true
* If true, it uses the CSS 3D transformation to position the popper.
* Otherwise, it will use the `top` and `left` properties
*/
gpuAcceleration: undefined
}
};
/**
* The `dataObject` is an object containing all the information used by Popper.js.
* This object is passed to modifiers and to the `onCreate` and `onUpdate` callbacks.
* @name dataObject
* @property {Object} data.instance The Popper.js instance
* @property {String} data.placement Placement applied to popper
* @property {String} data.originalPlacement Placement originally defined on init
* @property {Boolean} data.flipped True if popper has been flipped by flip modifier
* @property {Boolean} data.hide True if the reference element is out of boundaries, useful to know when to hide the popper
* @property {HTMLElement} data.arrowElement Node used as arrow by arrow modifier
* @property {Object} data.styles Any CSS property defined here will be applied to the popper. It expects the JavaScript nomenclature (eg. `marginBottom`)
* @property {Object} data.arrowStyles Any CSS property defined here will be applied to the popper arrow. It expects the JavaScript nomenclature (eg. `marginBottom`)
* @property {Object} data.boundaries Offsets of the popper boundaries
* @property {Object} data.offsets The measurements of popper, reference and arrow elements
* @property {Object} data.offsets.popper `top`, `left`, `width`, `height` values
* @property {Object} data.offsets.reference `top`, `left`, `width`, `height` values
* @property {Object} data.offsets.arrow] `top` and `left` offsets, only one of them will be different from 0
*/
/**
* Default options provided to Popper.js constructor.
* These can be overridden using the `options` argument of Popper.js.
* To override an option, simply pass an object with the same
* structure of the `options` object, as the 3rd argument. For example:
* ```
* new Popper(ref, pop, {
* modifiers: {
* preventOverflow: { enabled: false }
* }
* })
* ```
* @type {Object}
* @static
* @memberof Popper
*/
var Defaults = {
/**
* Popper's placement.
* @prop {Popper.placements} placement='bottom'
*/
placement: 'bottom',
/**
* Set this to true if you want popper to position it self in 'fixed' mode
* @prop {Boolean} positionFixed=false
*/
positionFixed: false,
/**
* Whether events (resize, scroll) are initially enabled.
* @prop {Boolean} eventsEnabled=true
*/
eventsEnabled: true,
/**
* Set to true if you want to automatically remove the popper when
* you call the `destroy` method.
* @prop {Boolean} removeOnDestroy=false
*/
removeOnDestroy: false,
/**
* Callback called when the popper is created.
* By default, it is set to no-op.
* Access Popper.js instance with `data.instance`.
* @prop {onCreate}
*/
onCreate: function onCreate() {},
/**
* Callback called when the popper is updated. This callback is not called
* on the initialization/creation of the popper, but only on subsequent
* updates.
* By default, it is set to no-op.
* Access Popper.js instance with `data.instance`.
* @prop {onUpdate}
*/
onUpdate: function onUpdate() {},
/**
* List of modifiers used to modify the offsets before they are applied to the popper.
* They provide most of the functionalities of Popper.js.
* @prop {modifiers}
*/
modifiers: modifiers
};
/**
* @callback onCreate
* @param {dataObject} data
*/
/**
* @callback onUpdate
* @param {dataObject} data
*/
// Utils
// Methods
var Popper = function () {
/**
* Creates a new Popper.js instance.
* @class Popper
* @param {Element|referenceObject} reference - The reference element used to position the popper
* @param {Element} popper - The HTML / XML element used as the popper
* @param {Object} options - Your custom options to override the ones defined in [Defaults](#defaults)
* @return {Object} instance - The generated Popper.js instance
*/
function Popper(reference, popper) {
var _this = this;
var options = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};
classCallCheck(this, Popper);
this.scheduleUpdate = function () {
return requestAnimationFrame(_this.update);
};
// make update() debounced, so that it only runs at most once-per-tick
this.update = debounce(this.update.bind(this));
// with {} we create a new object with the options inside it
this.options = _extends({}, Popper.Defaults, options);
// init state
this.state = {
isDestroyed: false,
isCreated: false,
scrollParents: []
};
// get reference and popper elements (allow jQuery wrappers)
this.reference = reference && reference.jquery ? reference[0] : reference;
this.popper = popper && popper.jquery ? popper[0] : popper;
// Deep merge modifiers options
this.options.modifiers = {};
Object.keys(_extends({}, Popper.Defaults.modifiers, options.modifiers)).forEach(function (name) {
_this.options.modifiers[name] = _extends({}, Popper.Defaults.modifiers[name] || {}, options.modifiers ? options.modifiers[name] : {});
});
// Refactoring modifiers' list (Object => Array)
this.modifiers = Object.keys(this.options.modifiers).map(function (name) {
return _extends({
name: name
}, _this.options.modifiers[name]);
})
// sort the modifiers by order
.sort(function (a, b) {
return a.order - b.order;
});
// modifiers have the ability to execute arbitrary code when Popper.js get inited
// such code is executed in the same order of its modifier
// they could add new properties to their options configuration
// BE AWARE: don't add options to `options.modifiers.name` but to `modifierOptions`!
this.modifiers.forEach(function (modifierOptions) {
if (modifierOptions.enabled && isFunction(modifierOptions.onLoad)) {
modifierOptions.onLoad(_this.reference, _this.popper, _this.options, modifierOptions, _this.state);
}
});
// fire the first update to position the popper in the right place
this.update();
var eventsEnabled = this.options.eventsEnabled;
if (eventsEnabled) {
// setup event listeners, they will take care of update the position in specific situations
this.enableEventListeners();
}
this.state.eventsEnabled = eventsEnabled;
}
// We can't use class properties because they don't get listed in the
// class prototype and break stuff like Sinon stubs
createClass(Popper, [{
key: 'update',
value: function update$$1() {
return update.call(this);
}
}, {
key: 'destroy',
value: function destroy$$1() {
return destroy.call(this);
}
}, {
key: 'enableEventListeners',
value: function enableEventListeners$$1() {
return enableEventListeners.call(this);
}
}, {
key: 'disableEventListeners',
value: function disableEventListeners$$1() {
return disableEventListeners.call(this);
}
/**
* Schedules an update. It will run on the next UI update available.
* @method scheduleUpdate
* @memberof Popper
*/
/**
* Collection of utilities useful when writing custom modifiers.
* Starting from version 1.7, this method is available only if you
* include `popper-utils.js` before `popper.js`.
*
* **DEPRECATION**: This way to access PopperUtils is deprecated
* and will be removed in v2! Use the PopperUtils module directly instead.
* Due to the high instability of the methods contained in Utils, we can't
* guarantee them to follow semver. Use them at your own risk!
* @static
* @private
* @type {Object}
* @deprecated since version 1.8
* @member Utils
* @memberof Popper
*/
}]);
return Popper;
}();
/**
* The `referenceObject` is an object that provides an interface compatible with Popper.js
* and lets you use it as replacement of a real DOM node.
* You can use this method to position a popper relatively to a set of coordinates
* in case you don't have a DOM node to use as reference.
*
* ```
* new Popper(referenceObject, popperNode);
* ```
*
* NB: This feature isn't supported in Internet Explorer 10.
* @name referenceObject
* @property {Function} data.getBoundingClientRect
* A function that returns a set of coordinates compatible with the native `getBoundingClientRect` method.
* @property {number} data.clientWidth
* An ES6 getter that will return the width of the virtual reference element.
* @property {number} data.clientHeight
* An ES6 getter that will return the height of the virtual reference element.
*/
Popper.Utils = (typeof window !== 'undefined' ? window : global).PopperUtils;
Popper.placements = placements;
Popper.Defaults = Defaults;
return Popper;
})));
//# sourceMappingURL=popper.js.map
/**
* --------------------------------------------------------------------------
* Bootstrap (v4.4.0): index.js
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
* --------------------------------------------------------------------------
*/
(function ($) {
if (typeof $ === 'undefined') {
throw new TypeError('Bootstrap\'s JavaScript requires jQuery. jQuery must be included before Bootstrap\'s JavaScript.');
}
var version = $.fn.jquery.split(' ')[0].split('.');
var minMajor = 1;
var ltMajor = 2;
var minMinor = 9;
var minPatch = 1;
var maxMajor = 4;
if (version[0] < ltMajor && version[1] < minMinor || version[0] === minMajor && version[1] === minMinor && version[2] < minPatch || version[0] >= maxMajor) {
throw new Error('Bootstrap\'s JavaScript requires at least jQuery v1.9.1 but less than v4.0.0');
}
})($);
//# sourceMappingURL=index.js.map
/*!
* Bootstrap util.js v4.5.2 (https://getbootstrap.com/)
* Copyright 2011-2020 The Bootstrap Authors (https://github.com/twbs/bootstrap/graphs/contributors)
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
*/
(function (global, factory) {
typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory(require('jquery')) :
typeof define === 'function' && define.amd ? define(['jquery'], factory) :
(global = typeof globalThis !== 'undefined' ? globalThis : global || self, global.Util = factory(global.jQuery));
}(this, (function ($) { 'use strict';
$ = $ && Object.prototype.hasOwnProperty.call($, 'default') ? $['default'] : $;
/**
* --------------------------------------------------------------------------
* Bootstrap (v4.5.2): util.js
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
* --------------------------------------------------------------------------
*/
/**
* ------------------------------------------------------------------------
* Private TransitionEnd Helpers
* ------------------------------------------------------------------------
*/
var TRANSITION_END = 'transitionend';
var MAX_UID = 1000000;
var MILLISECONDS_MULTIPLIER = 1000; // Shoutout AngusCroll (https://goo.gl/pxwQGp)
function toType(obj) {
if (obj === null || typeof obj === 'undefined') {
return "" + obj;
}
return {}.toString.call(obj).match(/\s([a-z]+)/i)[1].toLowerCase();
}
function getSpecialTransitionEndEvent() {
return {
bindType: TRANSITION_END,
delegateType: TRANSITION_END,
handle: function handle(event) {
if ($(event.target).is(this)) {
return event.handleObj.handler.apply(this, arguments); // eslint-disable-line prefer-rest-params
}
return undefined;
}
};
}
function transitionEndEmulator(duration) {
var _this = this;
var called = false;
$(this).one(Util.TRANSITION_END, function () {
called = true;
});
setTimeout(function () {
if (!called) {
Util.triggerTransitionEnd(_this);
}
}, duration);
return this;
}
function setTransitionEndSupport() {
$.fn.emulateTransitionEnd = transitionEndEmulator;
$.event.special[Util.TRANSITION_END] = getSpecialTransitionEndEvent();
}
/**
* --------------------------------------------------------------------------
* Public Util Api
* --------------------------------------------------------------------------
*/
var Util = {
TRANSITION_END: 'bsTransitionEnd',
getUID: function getUID(prefix) {
do {
// eslint-disable-next-line no-bitwise
prefix += ~~(Math.random() * MAX_UID); // "~~" acts like a faster Math.floor() here
} while (document.getElementById(prefix));
return prefix;
},
getSelectorFromElement: function getSelectorFromElement(element) {
var selector = element.getAttribute('data-target');
if (!selector || selector === '#') {
var hrefAttr = element.getAttribute('href');
selector = hrefAttr && hrefAttr !== '#' ? hrefAttr.trim() : '';
}
try {
return document.querySelector(selector) ? selector : null;
} catch (err) {
return null;
}
},
getTransitionDurationFromElement: function getTransitionDurationFromElement(element) {
if (!element) {
return 0;
} // Get transition-duration of the element
var transitionDuration = $(element).css('transition-duration');
var transitionDelay = $(element).css('transition-delay');
var floatTransitionDuration = parseFloat(transitionDuration);
var floatTransitionDelay = parseFloat(transitionDelay); // Return 0 if element or transition duration is not found
if (!floatTransitionDuration && !floatTransitionDelay) {
return 0;
} // If multiple durations are defined, take the first
transitionDuration = transitionDuration.split(',')[0];
transitionDelay = transitionDelay.split(',')[0];
return (parseFloat(transitionDuration) + parseFloat(transitionDelay)) * MILLISECONDS_MULTIPLIER;
},
reflow: function reflow(element) {
return element.offsetHeight;
},
triggerTransitionEnd: function triggerTransitionEnd(element) {
$(element).trigger(TRANSITION_END);
},
// TODO: Remove in v5
supportsTransitionEnd: function supportsTransitionEnd() {
return Boolean(TRANSITION_END);
},
isElement: function isElement(obj) {
return (obj[0] || obj).nodeType;
},
typeCheckConfig: function typeCheckConfig(componentName, config, configTypes) {
for (var property in configTypes) {
if (Object.prototype.hasOwnProperty.call(configTypes, property)) {
var expectedTypes = configTypes[property];
var value = config[property];
var valueType = value && Util.isElement(value) ? 'element' : toType(value);
if (!new RegExp(expectedTypes).test(valueType)) {
throw new Error(componentName.toUpperCase() + ": " + ("Option \"" + property + "\" provided type \"" + valueType + "\" ") + ("but expected type \"" + expectedTypes + "\"."));
}
}
}
},
findShadowRoot: function findShadowRoot(element) {
if (!document.documentElement.attachShadow) {
return null;
} // Can find the shadow root otherwise it'll return the document
if (typeof element.getRootNode === 'function') {
var root = element.getRootNode();
return root instanceof ShadowRoot ? root : null;
}
if (element instanceof ShadowRoot) {
return element;
} // when we don't find a shadow root
if (!element.parentNode) {
return null;
}
return Util.findShadowRoot(element.parentNode);
},
jQueryDetection: function jQueryDetection() {
if (typeof $ === 'undefined') {
throw new TypeError('Bootstrap\'s JavaScript requires jQuery. jQuery must be included before Bootstrap\'s JavaScript.');
}
var version = $.fn.jquery.split(' ')[0].split('.');
var minMajor = 1;
var ltMajor = 2;
var minMinor = 9;
var minPatch = 1;
var maxMajor = 4;
if (version[0] < ltMajor && version[1] < minMinor || version[0] === minMajor && version[1] === minMinor && version[2] < minPatch || version[0] >= maxMajor) {
throw new Error('Bootstrap\'s JavaScript requires at least jQuery v1.9.1 but less than v4.0.0');
}
}
};
Util.jQueryDetection();
setTransitionEndSupport();
return Util;
})));
//# sourceMappingURL=util.js.map
/*!
* Bootstrap tab.js v4.5.2 (https://getbootstrap.com/)
* Copyright 2011-2020 The Bootstrap Authors (https://github.com/twbs/bootstrap/graphs/contributors)
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
*/
(function (global, factory) {
typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory(require('jquery'), require('./util.js')) :
typeof define === 'function' && define.amd ? define(['jquery', './util.js'], factory) :
(global = typeof globalThis !== 'undefined' ? globalThis : global || self, global.Tab = factory(global.jQuery, global.Util));
}(this, (function ($, Util) { 'use strict';
$ = $ && Object.prototype.hasOwnProperty.call($, 'default') ? $['default'] : $;
Util = Util && Object.prototype.hasOwnProperty.call(Util, 'default') ? Util['default'] : Util;
function _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } }
function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); return Constructor; }
/**
* ------------------------------------------------------------------------
* Constants
* ------------------------------------------------------------------------
*/
var NAME = 'tab';
var VERSION = '4.5.2';
var DATA_KEY = 'bs.tab';
var EVENT_KEY = "." + DATA_KEY;
var DATA_API_KEY = '.data-api';
var JQUERY_NO_CONFLICT = $.fn[NAME];
var EVENT_HIDE = "hide" + EVENT_KEY;
var EVENT_HIDDEN = "hidden" + EVENT_KEY;
var EVENT_SHOW = "show" + EVENT_KEY;
var EVENT_SHOWN = "shown" + EVENT_KEY;
var EVENT_CLICK_DATA_API = "click" + EVENT_KEY + DATA_API_KEY;
var CLASS_NAME_DROPDOWN_MENU = 'dropdown-menu';
var CLASS_NAME_ACTIVE = 'active';
var CLASS_NAME_DISABLED = 'disabled';
var CLASS_NAME_FADE = 'fade';
var CLASS_NAME_SHOW = 'show';
var SELECTOR_DROPDOWN = '.dropdown';
var SELECTOR_NAV_LIST_GROUP = '.nav, .list-group';
var SELECTOR_ACTIVE = '.active';
var SELECTOR_ACTIVE_UL = '> li > .active';
var SELECTOR_DATA_TOGGLE = '[data-toggle="tab"], [data-toggle="pill"], [data-toggle="list"]';
var SELECTOR_DROPDOWN_TOGGLE = '.dropdown-toggle';
var SELECTOR_DROPDOWN_ACTIVE_CHILD = '> .dropdown-menu .active';
/**
* ------------------------------------------------------------------------
* Class Definition
* ------------------------------------------------------------------------
*/
var Tab = /*#__PURE__*/function () {
function Tab(element) {
this._element = element;
} // Getters
var _proto = Tab.prototype;
// Public
_proto.show = function show() {
var _this = this;
if (this._element.parentNode && this._element.parentNode.nodeType === Node.ELEMENT_NODE && $(this._element).hasClass(CLASS_NAME_ACTIVE) || $(this._element).hasClass(CLASS_NAME_DISABLED)) {
return;
}
var target;
var previous;
var listElement = $(this._element).closest(SELECTOR_NAV_LIST_GROUP)[0];
var selector = Util.getSelectorFromElement(this._element);
if (listElement) {
var itemSelector = listElement.nodeName === 'UL' || listElement.nodeName === 'OL' ? SELECTOR_ACTIVE_UL : SELECTOR_ACTIVE;
previous = $.makeArray($(listElement).find(itemSelector));
previous = previous[previous.length - 1];
}
var hideEvent = $.Event(EVENT_HIDE, {
relatedTarget: this._element
});
var showEvent = $.Event(EVENT_SHOW, {
relatedTarget: previous
});
if (previous) {
$(previous).trigger(hideEvent);
}
$(this._element).trigger(showEvent);
if (showEvent.isDefaultPrevented() || hideEvent.isDefaultPrevented()) {
return;
}
if (selector) {
target = document.querySelector(selector);
}
this._activate(this._element, listElement);
var complete = function complete() {
var hiddenEvent = $.Event(EVENT_HIDDEN, {
relatedTarget: _this._element
});
var shownEvent = $.Event(EVENT_SHOWN, {
relatedTarget: previous
});
$(previous).trigger(hiddenEvent);
$(_this._element).trigger(shownEvent);
};
if (target) {
this._activate(target, target.parentNode, complete);
} else {
complete();
}
};
_proto.dispose = function dispose() {
$.removeData(this._element, DATA_KEY);
this._element = null;
} // Private
;
_proto._activate = function _activate(element, container, callback) {
var _this2 = this;
var activeElements = container && (container.nodeName === 'UL' || container.nodeName === 'OL') ? $(container).find(SELECTOR_ACTIVE_UL) : $(container).children(SELECTOR_ACTIVE);
var active = activeElements[0];
var isTransitioning = callback && active && $(active).hasClass(CLASS_NAME_FADE);
var complete = function complete() {
return _this2._transitionComplete(element, active, callback);
};
if (active && isTransitioning) {
var transitionDuration = Util.getTransitionDurationFromElement(active);
$(active).removeClass(CLASS_NAME_SHOW).one(Util.TRANSITION_END, complete).emulateTransitionEnd(transitionDuration);
} else {
complete();
}
};
_proto._transitionComplete = function _transitionComplete(element, active, callback) {
if (active) {
$(active).removeClass(CLASS_NAME_ACTIVE);
var dropdownChild = $(active.parentNode).find(SELECTOR_DROPDOWN_ACTIVE_CHILD)[0];
if (dropdownChild) {
$(dropdownChild).removeClass(CLASS_NAME_ACTIVE);
}
if (active.getAttribute('role') === 'tab') {
active.setAttribute('aria-selected', false);
}
}
$(element).addClass(CLASS_NAME_ACTIVE);
if (element.getAttribute('role') === 'tab') {
element.setAttribute('aria-selected', true);
}
Util.reflow(element);
if (element.classList.contains(CLASS_NAME_FADE)) {
element.classList.add(CLASS_NAME_SHOW);
}
if (element.parentNode && $(element.parentNode).hasClass(CLASS_NAME_DROPDOWN_MENU)) {
var dropdownElement = $(element).closest(SELECTOR_DROPDOWN)[0];
if (dropdownElement) {
var dropdownToggleList = [].slice.call(dropdownElement.querySelectorAll(SELECTOR_DROPDOWN_TOGGLE));
$(dropdownToggleList).addClass(CLASS_NAME_ACTIVE);
}
element.setAttribute('aria-expanded', true);
}
if (callback) {
callback();
}
} // Static
;
Tab._jQueryInterface = function _jQueryInterface(config) {
return this.each(function () {
var $this = $(this);
var data = $this.data(DATA_KEY);
if (!data) {
data = new Tab(this);
$this.data(DATA_KEY, data);
}
if (typeof config === 'string') {
if (typeof data[config] === 'undefined') {
throw new TypeError("No method named \"" + config + "\"");
}
data[config]();
}
});
};
_createClass(Tab, null, [{
key: "VERSION",
get: function get() {
return VERSION;
}
}]);
return Tab;
}();
/**
* ------------------------------------------------------------------------
* Data Api implementation
* ------------------------------------------------------------------------
*/
$(document).on(EVENT_CLICK_DATA_API, SELECTOR_DATA_TOGGLE, function (event) {
event.preventDefault();
Tab._jQueryInterface.call($(this), 'show');
});
/**
* ------------------------------------------------------------------------
* jQuery
* ------------------------------------------------------------------------
*/
$.fn[NAME] = Tab._jQueryInterface;
$.fn[NAME].Constructor = Tab;
$.fn[NAME].noConflict = function () {
$.fn[NAME] = JQUERY_NO_CONFLICT;
return Tab._jQueryInterface;
};
return Tab;
})));
//# sourceMappingURL=tab.js.map
/*!
* Bootstrap collapse.js v4.5.2 (https://getbootstrap.com/)
* Copyright 2011-2020 The Bootstrap Authors (https://github.com/twbs/bootstrap/graphs/contributors)
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
*/
(function (global, factory) {
typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory(require('jquery'), require('./util.js')) :
typeof define === 'function' && define.amd ? define(['jquery', './util.js'], factory) :
(global = typeof globalThis !== 'undefined' ? globalThis : global || self, global.Collapse = factory(global.jQuery, global.Util));
}(this, (function ($, Util) { 'use strict';
$ = $ && Object.prototype.hasOwnProperty.call($, 'default') ? $['default'] : $;
Util = Util && Object.prototype.hasOwnProperty.call(Util, 'default') ? Util['default'] : Util;
function _extends() { _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends.apply(this, arguments); }
function _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } }
function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); return Constructor; }
/**
* ------------------------------------------------------------------------
* Constants
* ------------------------------------------------------------------------
*/
var NAME = 'collapse';
var VERSION = '4.5.2';
var DATA_KEY = 'bs.collapse';
var EVENT_KEY = "." + DATA_KEY;
var DATA_API_KEY = '.data-api';
var JQUERY_NO_CONFLICT = $.fn[NAME];
var Default = {
toggle: true,
parent: ''
};
var DefaultType = {
toggle: 'boolean',
parent: '(string|element)'
};
var EVENT_SHOW = "show" + EVENT_KEY;
var EVENT_SHOWN = "shown" + EVENT_KEY;
var EVENT_HIDE = "hide" + EVENT_KEY;
var EVENT_HIDDEN = "hidden" + EVENT_KEY;
var EVENT_CLICK_DATA_API = "click" + EVENT_KEY + DATA_API_KEY;
var CLASS_NAME_SHOW = 'show';
var CLASS_NAME_COLLAPSE = 'collapse';
var CLASS_NAME_COLLAPSING = 'collapsing';
var CLASS_NAME_COLLAPSED = 'collapsed';
var DIMENSION_WIDTH = 'width';
var DIMENSION_HEIGHT = 'height';
var SELECTOR_ACTIVES = '.show, .collapsing';
var SELECTOR_DATA_TOGGLE = '[data-toggle="collapse"]';
/**
* ------------------------------------------------------------------------
* Class Definition
* ------------------------------------------------------------------------
*/
var Collapse = /*#__PURE__*/function () {
function Collapse(element, config) {
this._isTransitioning = false;
this._element = element;
this._config = this._getConfig(config);
this._triggerArray = [].slice.call(document.querySelectorAll("[data-toggle=\"collapse\"][href=\"#" + element.id + "\"]," + ("[data-toggle=\"collapse\"][data-target=\"#" + element.id + "\"]")));
var toggleList = [].slice.call(document.querySelectorAll(SELECTOR_DATA_TOGGLE));
for (var i = 0, len = toggleList.length; i < len; i++) {
var elem = toggleList[i];
var selector = Util.getSelectorFromElement(elem);
var filterElement = [].slice.call(document.querySelectorAll(selector)).filter(function (foundElem) {
return foundElem === element;
});
if (selector !== null && filterElement.length > 0) {
this._selector = selector;
this._triggerArray.push(elem);
}
}
this._parent = this._config.parent ? this._getParent() : null;
if (!this._config.parent) {
this._addAriaAndCollapsedClass(this._element, this._triggerArray);
}
if (this._config.toggle) {
this.toggle();
}
} // Getters
var _proto = Collapse.prototype;
// Public
_proto.toggle = function toggle() {
if ($(this._element).hasClass(CLASS_NAME_SHOW)) {
this.hide();
} else {
this.show();
}
};
_proto.show = function show() {
var _this = this;
if (this._isTransitioning || $(this._element).hasClass(CLASS_NAME_SHOW)) {
return;
}
var actives;
var activesData;
if (this._parent) {
actives = [].slice.call(this._parent.querySelectorAll(SELECTOR_ACTIVES)).filter(function (elem) {
if (typeof _this._config.parent === 'string') {
return elem.getAttribute('data-parent') === _this._config.parent;
}
return elem.classList.contains(CLASS_NAME_COLLAPSE);
});
if (actives.length === 0) {
actives = null;
}
}
if (actives) {
activesData = $(actives).not(this._selector).data(DATA_KEY);
if (activesData && activesData._isTransitioning) {
return;
}
}
var startEvent = $.Event(EVENT_SHOW);
$(this._element).trigger(startEvent);
if (startEvent.isDefaultPrevented()) {
return;
}
if (actives) {
Collapse._jQueryInterface.call($(actives).not(this._selector), 'hide');
if (!activesData) {
$(actives).data(DATA_KEY, null);
}
}
var dimension = this._getDimension();
$(this._element).removeClass(CLASS_NAME_COLLAPSE).addClass(CLASS_NAME_COLLAPSING);
this._element.style[dimension] = 0;
if (this._triggerArray.length) {
$(this._triggerArray).removeClass(CLASS_NAME_COLLAPSED).attr('aria-expanded', true);
}
this.setTransitioning(true);
var complete = function complete() {
$(_this._element).removeClass(CLASS_NAME_COLLAPSING).addClass(CLASS_NAME_COLLAPSE + " " + CLASS_NAME_SHOW);
_this._element.style[dimension] = '';
_this.setTransitioning(false);
$(_this._element).trigger(EVENT_SHOWN);
};
var capitalizedDimension = dimension[0].toUpperCase() + dimension.slice(1);
var scrollSize = "scroll" + capitalizedDimension;
var transitionDuration = Util.getTransitionDurationFromElement(this._element);
$(this._element).one(Util.TRANSITION_END, complete).emulateTransitionEnd(transitionDuration);
this._element.style[dimension] = this._element[scrollSize] + "px";
};
_proto.hide = function hide() {
var _this2 = this;
if (this._isTransitioning || !$(this._element).hasClass(CLASS_NAME_SHOW)) {
return;
}
var startEvent = $.Event(EVENT_HIDE);
$(this._element).trigger(startEvent);
if (startEvent.isDefaultPrevented()) {
return;
}
var dimension = this._getDimension();
this._element.style[dimension] = this._element.getBoundingClientRect()[dimension] + "px";
Util.reflow(this._element);
$(this._element).addClass(CLASS_NAME_COLLAPSING).removeClass(CLASS_NAME_COLLAPSE + " " + CLASS_NAME_SHOW);
var triggerArrayLength = this._triggerArray.length;
if (triggerArrayLength > 0) {
for (var i = 0; i < triggerArrayLength; i++) {
var trigger = this._triggerArray[i];
var selector = Util.getSelectorFromElement(trigger);
if (selector !== null) {
var $elem = $([].slice.call(document.querySelectorAll(selector)));
if (!$elem.hasClass(CLASS_NAME_SHOW)) {
$(trigger).addClass(CLASS_NAME_COLLAPSED).attr('aria-expanded', false);
}
}
}
}
this.setTransitioning(true);
var complete = function complete() {
_this2.setTransitioning(false);
$(_this2._element).removeClass(CLASS_NAME_COLLAPSING).addClass(CLASS_NAME_COLLAPSE).trigger(EVENT_HIDDEN);
};
this._element.style[dimension] = '';
var transitionDuration = Util.getTransitionDurationFromElement(this._element);
$(this._element).one(Util.TRANSITION_END, complete).emulateTransitionEnd(transitionDuration);
};
_proto.setTransitioning = function setTransitioning(isTransitioning) {
this._isTransitioning = isTransitioning;
};
_proto.dispose = function dispose() {
$.removeData(this._element, DATA_KEY);
this._config = null;
this._parent = null;
this._element = null;
this._triggerArray = null;
this._isTransitioning = null;
} // Private
;
_proto._getConfig = function _getConfig(config) {
config = _extends({}, Default, config);
config.toggle = Boolean(config.toggle); // Coerce string values
Util.typeCheckConfig(NAME, config, DefaultType);
return config;
};
_proto._getDimension = function _getDimension() {
var hasWidth = $(this._element).hasClass(DIMENSION_WIDTH);
return hasWidth ? DIMENSION_WIDTH : DIMENSION_HEIGHT;
};
_proto._getParent = function _getParent() {
var _this3 = this;
var parent;
if (Util.isElement(this._config.parent)) {
parent = this._config.parent; // It's a jQuery object
if (typeof this._config.parent.jquery !== 'undefined') {
parent = this._config.parent[0];
}
} else {
parent = document.querySelector(this._config.parent);
}
var selector = "[data-toggle=\"collapse\"][data-parent=\"" + this._config.parent + "\"]";
var children = [].slice.call(parent.querySelectorAll(selector));
$(children).each(function (i, element) {
_this3._addAriaAndCollapsedClass(Collapse._getTargetFromElement(element), [element]);
});
return parent;
};
_proto._addAriaAndCollapsedClass = function _addAriaAndCollapsedClass(element, triggerArray) {
var isOpen = $(element).hasClass(CLASS_NAME_SHOW);
if (triggerArray.length) {
$(triggerArray).toggleClass(CLASS_NAME_COLLAPSED, !isOpen).attr('aria-expanded', isOpen);
}
} // Static
;
Collapse._getTargetFromElement = function _getTargetFromElement(element) {
var selector = Util.getSelectorFromElement(element);
return selector ? document.querySelector(selector) : null;
};
Collapse._jQueryInterface = function _jQueryInterface(config) {
return this.each(function () {
var $this = $(this);
var data = $this.data(DATA_KEY);
var _config = _extends({}, Default, $this.data(), typeof config === 'object' && config ? config : {});
if (!data && _config.toggle && typeof config === 'string' && /show|hide/.test(config)) {
_config.toggle = false;
}
if (!data) {
data = new Collapse(this, _config);
$this.data(DATA_KEY, data);
}
if (typeof config === 'string') {
if (typeof data[config] === 'undefined') {
throw new TypeError("No method named \"" + config + "\"");
}
data[config]();
}
});
};
_createClass(Collapse, null, [{
key: "VERSION",
get: function get() {
return VERSION;
}
}, {
key: "Default",
get: function get() {
return Default;
}
}]);
return Collapse;
}();
/**
* ------------------------------------------------------------------------
* Data Api implementation
* ------------------------------------------------------------------------
*/
$(document).on(EVENT_CLICK_DATA_API, SELECTOR_DATA_TOGGLE, function (event) {
// preventDefault only for elements (which change the URL) not inside the collapsible element
if (event.currentTarget.tagName === 'A') {
event.preventDefault();
}
var $trigger = $(this);
var selector = Util.getSelectorFromElement(this);
var selectors = [].slice.call(document.querySelectorAll(selector));
$(selectors).each(function () {
var $target = $(this);
var data = $target.data(DATA_KEY);
var config = data ? 'toggle' : $trigger.data();
Collapse._jQueryInterface.call($target, config);
});
});
/**
* ------------------------------------------------------------------------
* jQuery
* ------------------------------------------------------------------------
*/
$.fn[NAME] = Collapse._jQueryInterface;
$.fn[NAME].Constructor = Collapse;
$.fn[NAME].noConflict = function () {
$.fn[NAME] = JQUERY_NO_CONFLICT;
return Collapse._jQueryInterface;
};
return Collapse;
})));
//# sourceMappingURL=collapse.js.map
/*!
* Bootstrap dropdown.js v4.5.2 (https://getbootstrap.com/)
* Copyright 2011-2020 The Bootstrap Authors (https://github.com/twbs/bootstrap/graphs/contributors)
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
*/
(function (global, factory) {
typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory(require('jquery'), require('popper.js'), require('./util.js')) :
typeof define === 'function' && define.amd ? define(['jquery', 'popper.js', './util.js'], factory) :
(global = typeof globalThis !== 'undefined' ? globalThis : global || self, global.Dropdown = factory(global.jQuery, global.Popper, global.Util));
}(this, (function ($, Popper, Util) { 'use strict';
$ = $ && Object.prototype.hasOwnProperty.call($, 'default') ? $['default'] : $;
Popper = Popper && Object.prototype.hasOwnProperty.call(Popper, 'default') ? Popper['default'] : Popper;
Util = Util && Object.prototype.hasOwnProperty.call(Util, 'default') ? Util['default'] : Util;
function _extends() { _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends.apply(this, arguments); }
function _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } }
function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); return Constructor; }
/**
* ------------------------------------------------------------------------
* Constants
* ------------------------------------------------------------------------
*/
var NAME = 'dropdown';
var VERSION = '4.5.2';
var DATA_KEY = 'bs.dropdown';
var EVENT_KEY = "." + DATA_KEY;
var DATA_API_KEY = '.data-api';
var JQUERY_NO_CONFLICT = $.fn[NAME];
var ESCAPE_KEYCODE = 27; // KeyboardEvent.which value for Escape (Esc) key
var SPACE_KEYCODE = 32; // KeyboardEvent.which value for space key
var TAB_KEYCODE = 9; // KeyboardEvent.which value for tab key
var ARROW_UP_KEYCODE = 38; // KeyboardEvent.which value for up arrow key
var ARROW_DOWN_KEYCODE = 40; // KeyboardEvent.which value for down arrow key
var RIGHT_MOUSE_BUTTON_WHICH = 3; // MouseEvent.which value for the right button (assuming a right-handed mouse)
var REGEXP_KEYDOWN = new RegExp(ARROW_UP_KEYCODE + "|" + ARROW_DOWN_KEYCODE + "|" + ESCAPE_KEYCODE);
var EVENT_HIDE = "hide" + EVENT_KEY;
var EVENT_HIDDEN = "hidden" + EVENT_KEY;
var EVENT_SHOW = "show" + EVENT_KEY;
var EVENT_SHOWN = "shown" + EVENT_KEY;
var EVENT_CLICK = "click" + EVENT_KEY;
var EVENT_CLICK_DATA_API = "click" + EVENT_KEY + DATA_API_KEY;
var EVENT_KEYDOWN_DATA_API = "keydown" + EVENT_KEY + DATA_API_KEY;
var EVENT_KEYUP_DATA_API = "keyup" + EVENT_KEY + DATA_API_KEY;
var CLASS_NAME_DISABLED = 'disabled';
var CLASS_NAME_SHOW = 'show';
var CLASS_NAME_DROPUP = 'dropup';
var CLASS_NAME_DROPRIGHT = 'dropright';
var CLASS_NAME_DROPLEFT = 'dropleft';
var CLASS_NAME_MENURIGHT = 'dropdown-menu-right';
var CLASS_NAME_POSITION_STATIC = 'position-static';
var SELECTOR_DATA_TOGGLE = '[data-toggle="dropdown"]';
var SELECTOR_FORM_CHILD = '.dropdown form';
var SELECTOR_MENU = '.dropdown-menu';
var SELECTOR_NAVBAR_NAV = '.navbar-nav';
var SELECTOR_VISIBLE_ITEMS = '.dropdown-menu .dropdown-item:not(.disabled):not(:disabled)';
var PLACEMENT_TOP = 'top-start';
var PLACEMENT_TOPEND = 'top-end';
var PLACEMENT_BOTTOM = 'bottom-start';
var PLACEMENT_BOTTOMEND = 'bottom-end';
var PLACEMENT_RIGHT = 'right-start';
var PLACEMENT_LEFT = 'left-start';
var Default = {
offset: 0,
flip: true,
boundary: 'scrollParent',
reference: 'toggle',
display: 'dynamic',
popperConfig: null
};
var DefaultType = {
offset: '(number|string|function)',
flip: 'boolean',
boundary: '(string|element)',
reference: '(string|element)',
display: 'string',
popperConfig: '(null|object)'
};
/**
* ------------------------------------------------------------------------
* Class Definition
* ------------------------------------------------------------------------
*/
var Dropdown = /*#__PURE__*/function () {
function Dropdown(element, config) {
this._element = element;
this._popper = null;
this._config = this._getConfig(config);
this._menu = this._getMenuElement();
this._inNavbar = this._detectNavbar();
this._addEventListeners();
} // Getters
var _proto = Dropdown.prototype;
// Public
_proto.toggle = function toggle() {
if (this._element.disabled || $(this._element).hasClass(CLASS_NAME_DISABLED)) {
return;
}
var isActive = $(this._menu).hasClass(CLASS_NAME_SHOW);
Dropdown._clearMenus();
if (isActive) {
return;
}
this.show(true);
};
_proto.show = function show(usePopper) {
if (usePopper === void 0) {
usePopper = false;
}
if (this._element.disabled || $(this._element).hasClass(CLASS_NAME_DISABLED) || $(this._menu).hasClass(CLASS_NAME_SHOW)) {
return;
}
var relatedTarget = {
relatedTarget: this._element
};
var showEvent = $.Event(EVENT_SHOW, relatedTarget);
var parent = Dropdown._getParentFromElement(this._element);
$(parent).trigger(showEvent);
if (showEvent.isDefaultPrevented()) {
return;
} // Disable totally Popper.js for Dropdown in Navbar
if (!this._inNavbar && usePopper) {
/**
* Check for Popper dependency
* Popper - https://popper.js.org
*/
if (typeof Popper === 'undefined') {
throw new TypeError('Bootstrap\'s dropdowns require Popper.js (https://popper.js.org/)');
}
var referenceElement = this._element;
if (this._config.reference === 'parent') {
referenceElement = parent;
} else if (Util.isElement(this._config.reference)) {
referenceElement = this._config.reference; // Check if it's jQuery element
if (typeof this._config.reference.jquery !== 'undefined') {
referenceElement = this._config.reference[0];
}
} // If boundary is not `scrollParent`, then set position to `static`
// to allow the menu to "escape" the scroll parent's boundaries
// https://github.com/twbs/bootstrap/issues/24251
if (this._config.boundary !== 'scrollParent') {
$(parent).addClass(CLASS_NAME_POSITION_STATIC);
}
this._popper = new Popper(referenceElement, this._menu, this._getPopperConfig());
} // If this is a touch-enabled device we add extra
// empty mouseover listeners to the body's immediate children;
// only needed because of broken event delegation on iOS
// https://www.quirksmode.org/blog/archives/2014/02/mouse_event_bub.html
if ('ontouchstart' in document.documentElement && $(parent).closest(SELECTOR_NAVBAR_NAV).length === 0) {
$(document.body).children().on('mouseover', null, $.noop);
}
this._element.focus();
this._element.setAttribute('aria-expanded', true);
$(this._menu).toggleClass(CLASS_NAME_SHOW);
$(parent).toggleClass(CLASS_NAME_SHOW).trigger($.Event(EVENT_SHOWN, relatedTarget));
};
_proto.hide = function hide() {
if (this._element.disabled || $(this._element).hasClass(CLASS_NAME_DISABLED) || !$(this._menu).hasClass(CLASS_NAME_SHOW)) {
return;
}
var relatedTarget = {
relatedTarget: this._element
};
var hideEvent = $.Event(EVENT_HIDE, relatedTarget);
var parent = Dropdown._getParentFromElement(this._element);
$(parent).trigger(hideEvent);
if (hideEvent.isDefaultPrevented()) {
return;
}
if (this._popper) {
this._popper.destroy();
}
$(this._menu).toggleClass(CLASS_NAME_SHOW);
$(parent).toggleClass(CLASS_NAME_SHOW).trigger($.Event(EVENT_HIDDEN, relatedTarget));
};
_proto.dispose = function dispose() {
$.removeData(this._element, DATA_KEY);
$(this._element).off(EVENT_KEY);
this._element = null;
this._menu = null;
if (this._popper !== null) {
this._popper.destroy();
this._popper = null;
}
};
_proto.update = function update() {
this._inNavbar = this._detectNavbar();
if (this._popper !== null) {
this._popper.scheduleUpdate();
}
} // Private
;
_proto._addEventListeners = function _addEventListeners() {
var _this = this;
$(this._element).on(EVENT_CLICK, function (event) {
event.preventDefault();
event.stopPropagation();
_this.toggle();
});
};
_proto._getConfig = function _getConfig(config) {
config = _extends({}, this.constructor.Default, $(this._element).data(), config);
Util.typeCheckConfig(NAME, config, this.constructor.DefaultType);
return config;
};
_proto._getMenuElement = function _getMenuElement() {
if (!this._menu) {
var parent = Dropdown._getParentFromElement(this._element);
if (parent) {
this._menu = parent.querySelector(SELECTOR_MENU);
}
}
return this._menu;
};
_proto._getPlacement = function _getPlacement() {
var $parentDropdown = $(this._element.parentNode);
var placement = PLACEMENT_BOTTOM; // Handle dropup
if ($parentDropdown.hasClass(CLASS_NAME_DROPUP)) {
placement = $(this._menu).hasClass(CLASS_NAME_MENURIGHT) ? PLACEMENT_TOPEND : PLACEMENT_TOP;
} else if ($parentDropdown.hasClass(CLASS_NAME_DROPRIGHT)) {
placement = PLACEMENT_RIGHT;
} else if ($parentDropdown.hasClass(CLASS_NAME_DROPLEFT)) {
placement = PLACEMENT_LEFT;
} else if ($(this._menu).hasClass(CLASS_NAME_MENURIGHT)) {
placement = PLACEMENT_BOTTOMEND;
}
return placement;
};
_proto._detectNavbar = function _detectNavbar() {
return $(this._element).closest('.navbar').length > 0;
};
_proto._getOffset = function _getOffset() {
var _this2 = this;
var offset = {};
if (typeof this._config.offset === 'function') {
offset.fn = function (data) {
data.offsets = _extends({}, data.offsets, _this2._config.offset(data.offsets, _this2._element) || {});
return data;
};
} else {
offset.offset = this._config.offset;
}
return offset;
};
_proto._getPopperConfig = function _getPopperConfig() {
var popperConfig = {
placement: this._getPlacement(),
modifiers: {
offset: this._getOffset(),
flip: {
enabled: this._config.flip
},
preventOverflow: {
boundariesElement: this._config.boundary
}
}
}; // Disable Popper.js if we have a static display
if (this._config.display === 'static') {
popperConfig.modifiers.applyStyle = {
enabled: false
};
}
return _extends({}, popperConfig, this._config.popperConfig);
} // Static
;
Dropdown._jQueryInterface = function _jQueryInterface(config) {
return this.each(function () {
var data = $(this).data(DATA_KEY);
var _config = typeof config === 'object' ? config : null;
if (!data) {
data = new Dropdown(this, _config);
$(this).data(DATA_KEY, data);
}
if (typeof config === 'string') {
if (typeof data[config] === 'undefined') {
throw new TypeError("No method named \"" + config + "\"");
}
data[config]();
}
});
};
Dropdown._clearMenus = function _clearMenus(event) {
if (event && (event.which === RIGHT_MOUSE_BUTTON_WHICH || event.type === 'keyup' && event.which !== TAB_KEYCODE)) {
return;
}
var toggles = [].slice.call(document.querySelectorAll(SELECTOR_DATA_TOGGLE));
for (var i = 0, len = toggles.length; i < len; i++) {
var parent = Dropdown._getParentFromElement(toggles[i]);
var context = $(toggles[i]).data(DATA_KEY);
var relatedTarget = {
relatedTarget: toggles[i]
};
if (event && event.type === 'click') {
relatedTarget.clickEvent = event;
}
if (!context) {
continue;
}
var dropdownMenu = context._menu;
if (!$(parent).hasClass(CLASS_NAME_SHOW)) {
continue;
}
if (event && (event.type === 'click' && /input|textarea/i.test(event.target.tagName) || event.type === 'keyup' && event.which === TAB_KEYCODE) && $.contains(parent, event.target)) {
continue;
}
var hideEvent = $.Event(EVENT_HIDE, relatedTarget);
$(parent).trigger(hideEvent);
if (hideEvent.isDefaultPrevented()) {
continue;
} // If this is a touch-enabled device we remove the extra
// empty mouseover listeners we added for iOS support
if ('ontouchstart' in document.documentElement) {
$(document.body).children().off('mouseover', null, $.noop);
}
toggles[i].setAttribute('aria-expanded', 'false');
if (context._popper) {
context._popper.destroy();
}
$(dropdownMenu).removeClass(CLASS_NAME_SHOW);
$(parent).removeClass(CLASS_NAME_SHOW).trigger($.Event(EVENT_HIDDEN, relatedTarget));
}
};
Dropdown._getParentFromElement = function _getParentFromElement(element) {
var parent;
var selector = Util.getSelectorFromElement(element);
if (selector) {
parent = document.querySelector(selector);
}
return parent || element.parentNode;
} // eslint-disable-next-line complexity
;
Dropdown._dataApiKeydownHandler = function _dataApiKeydownHandler(event) {
// If not input/textarea:
// - And not a key in REGEXP_KEYDOWN => not a dropdown command
// If input/textarea:
// - If space key => not a dropdown command
// - If key is other than escape
// - If key is not up or down => not a dropdown command
// - If trigger inside the menu => not a dropdown command
if (/input|textarea/i.test(event.target.tagName) ? event.which === SPACE_KEYCODE || event.which !== ESCAPE_KEYCODE && (event.which !== ARROW_DOWN_KEYCODE && event.which !== ARROW_UP_KEYCODE || $(event.target).closest(SELECTOR_MENU).length) : !REGEXP_KEYDOWN.test(event.which)) {
return;
}
if (this.disabled || $(this).hasClass(CLASS_NAME_DISABLED)) {
return;
}
var parent = Dropdown._getParentFromElement(this);
var isActive = $(parent).hasClass(CLASS_NAME_SHOW);
if (!isActive && event.which === ESCAPE_KEYCODE) {
return;
}
event.preventDefault();
event.stopPropagation();
if (!isActive || isActive && (event.which === ESCAPE_KEYCODE || event.which === SPACE_KEYCODE)) {
if (event.which === ESCAPE_KEYCODE) {
$(parent.querySelector(SELECTOR_DATA_TOGGLE)).trigger('focus');
}
$(this).trigger('click');
return;
}
var items = [].slice.call(parent.querySelectorAll(SELECTOR_VISIBLE_ITEMS)).filter(function (item) {
return $(item).is(':visible');
});
if (items.length === 0) {
return;
}
var index = items.indexOf(event.target);
if (event.which === ARROW_UP_KEYCODE && index > 0) {
// Up
index--;
}
if (event.which === ARROW_DOWN_KEYCODE && index < items.length - 1) {
// Down
index++;
}
if (index < 0) {
index = 0;
}
items[index].focus();
};
_createClass(Dropdown, null, [{
key: "VERSION",
get: function get() {
return VERSION;
}
}, {
key: "Default",
get: function get() {
return Default;
}
}, {
key: "DefaultType",
get: function get() {
return DefaultType;
}
}]);
return Dropdown;
}();
/**
* ------------------------------------------------------------------------
* Data Api implementation
* ------------------------------------------------------------------------
*/
$(document).on(EVENT_KEYDOWN_DATA_API, SELECTOR_DATA_TOGGLE, Dropdown._dataApiKeydownHandler).on(EVENT_KEYDOWN_DATA_API, SELECTOR_MENU, Dropdown._dataApiKeydownHandler).on(EVENT_CLICK_DATA_API + " " + EVENT_KEYUP_DATA_API, Dropdown._clearMenus).on(EVENT_CLICK_DATA_API, SELECTOR_DATA_TOGGLE, function (event) {
event.preventDefault();
event.stopPropagation();
Dropdown._jQueryInterface.call($(this), 'toggle');
}).on(EVENT_CLICK_DATA_API, SELECTOR_FORM_CHILD, function (e) {
e.stopPropagation();
});
/**
* ------------------------------------------------------------------------
* jQuery
* ------------------------------------------------------------------------
*/
$.fn[NAME] = Dropdown._jQueryInterface;
$.fn[NAME].Constructor = Dropdown;
$.fn[NAME].noConflict = function () {
$.fn[NAME] = JQUERY_NO_CONFLICT;
return Dropdown._jQueryInterface;
};
return Dropdown;
})));
//# sourceMappingURL=dropdown.js.map
/*!
* Bootstrap tooltip.js v4.5.2 (https://getbootstrap.com/)
* Copyright 2011-2020 The Bootstrap Authors (https://github.com/twbs/bootstrap/graphs/contributors)
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
*/
(function (global, factory) {
typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory(require('jquery'), require('popper.js'), require('./util.js')) :
typeof define === 'function' && define.amd ? define(['jquery', 'popper.js', './util.js'], factory) :
(global = typeof globalThis !== 'undefined' ? globalThis : global || self, global.Tooltip = factory(global.jQuery, global.Popper, global.Util));
}(this, (function ($, Popper, Util) { 'use strict';
$ = $ && Object.prototype.hasOwnProperty.call($, 'default') ? $['default'] : $;
Popper = Popper && Object.prototype.hasOwnProperty.call(Popper, 'default') ? Popper['default'] : Popper;
Util = Util && Object.prototype.hasOwnProperty.call(Util, 'default') ? Util['default'] : Util;
/**
* --------------------------------------------------------------------------
* Bootstrap (v4.5.2): tools/sanitizer.js
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
* --------------------------------------------------------------------------
*/
var uriAttrs = ['background', 'cite', 'href', 'itemtype', 'longdesc', 'poster', 'src', 'xlink:href'];
var ARIA_ATTRIBUTE_PATTERN = /^aria-[\w-]*$/i;
var DefaultWhitelist = {
// Global attributes allowed on any supplied element below.
'*': ['class', 'dir', 'id', 'lang', 'role', ARIA_ATTRIBUTE_PATTERN],
a: ['target', 'href', 'title', 'rel'],
area: [],
b: [],
br: [],
col: [],
code: [],
div: [],
em: [],
hr: [],
h1: [],
h2: [],
h3: [],
h4: [],
h5: [],
h6: [],
i: [],
img: ['src', 'srcset', 'alt', 'title', 'width', 'height'],
li: [],
ol: [],
p: [],
pre: [],
s: [],
small: [],
span: [],
sub: [],
sup: [],
strong: [],
u: [],
ul: []
};
/**
* A pattern that recognizes a commonly useful subset of URLs that are safe.
*
* Shoutout to Angular 7 https://github.com/angular/angular/blob/7.2.4/packages/core/src/sanitization/url_sanitizer.ts
*/
var SAFE_URL_PATTERN = /^(?:(?:https?|mailto|ftp|tel|file):|[^#&/:?]*(?:[#/?]|$))/gi;
/**
* A pattern that matches safe data URLs. Only matches image, video and audio types.
*
* Shoutout to Angular 7 https://github.com/angular/angular/blob/7.2.4/packages/core/src/sanitization/url_sanitizer.ts
*/
var DATA_URL_PATTERN = /^data:(?:image\/(?:bmp|gif|jpeg|jpg|png|tiff|webp)|video\/(?:mpeg|mp4|ogg|webm)|audio\/(?:mp3|oga|ogg|opus));base64,[\d+/a-z]+=*$/i;
function allowedAttribute(attr, allowedAttributeList) {
var attrName = attr.nodeName.toLowerCase();
if (allowedAttributeList.indexOf(attrName) !== -1) {
if (uriAttrs.indexOf(attrName) !== -1) {
return Boolean(attr.nodeValue.match(SAFE_URL_PATTERN) || attr.nodeValue.match(DATA_URL_PATTERN));
}
return true;
}
var regExp = allowedAttributeList.filter(function (attrRegex) {
return attrRegex instanceof RegExp;
}); // Check if a regular expression validates the attribute.
for (var i = 0, len = regExp.length; i < len; i++) {
if (attrName.match(regExp[i])) {
return true;
}
}
return false;
}
function sanitizeHtml(unsafeHtml, whiteList, sanitizeFn) {
if (unsafeHtml.length === 0) {
return unsafeHtml;
}
if (sanitizeFn && typeof sanitizeFn === 'function') {
return sanitizeFn(unsafeHtml);
}
var domParser = new window.DOMParser();
var createdDocument = domParser.parseFromString(unsafeHtml, 'text/html');
var whitelistKeys = Object.keys(whiteList);
var elements = [].slice.call(createdDocument.body.querySelectorAll('*'));
var _loop = function _loop(i, len) {
var el = elements[i];
var elName = el.nodeName.toLowerCase();
if (whitelistKeys.indexOf(el.nodeName.toLowerCase()) === -1) {
el.parentNode.removeChild(el);
return "continue";
}
var attributeList = [].slice.call(el.attributes);
var whitelistedAttributes = [].concat(whiteList['*'] || [], whiteList[elName] || []);
attributeList.forEach(function (attr) {
if (!allowedAttribute(attr, whitelistedAttributes)) {
el.removeAttribute(attr.nodeName);
}
});
};
for (var i = 0, len = elements.length; i < len; i++) {
var _ret = _loop(i);
if (_ret === "continue") continue;
}
return createdDocument.body.innerHTML;
}
function _extends() { _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends.apply(this, arguments); }
function _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } }
function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); return Constructor; }
/**
* ------------------------------------------------------------------------
* Constants
* ------------------------------------------------------------------------
*/
var NAME = 'tooltip';
var VERSION = '4.5.2';
var DATA_KEY = 'bs.tooltip';
var EVENT_KEY = "." + DATA_KEY;
var JQUERY_NO_CONFLICT = $.fn[NAME];
var CLASS_PREFIX = 'bs-tooltip';
var BSCLS_PREFIX_REGEX = new RegExp("(^|\\s)" + CLASS_PREFIX + "\\S+", 'g');
var DISALLOWED_ATTRIBUTES = ['sanitize', 'whiteList', 'sanitizeFn'];
var DefaultType = {
animation: 'boolean',
template: 'string',
title: '(string|element|function)',
trigger: 'string',
delay: '(number|object)',
html: 'boolean',
selector: '(string|boolean)',
placement: '(string|function)',
offset: '(number|string|function)',
container: '(string|element|boolean)',
fallbackPlacement: '(string|array)',
boundary: '(string|element)',
sanitize: 'boolean',
sanitizeFn: '(null|function)',
whiteList: 'object',
popperConfig: '(null|object)'
};
var AttachmentMap = {
AUTO: 'auto',
TOP: 'top',
RIGHT: 'right',
BOTTOM: 'bottom',
LEFT: 'left'
};
var Default = {
animation: true,
template: '' + '' + '',
trigger: 'hover focus',
title: '',
delay: 0,
html: false,
selector: false,
placement: 'top',
offset: 0,
container: false,
fallbackPlacement: 'flip',
boundary: 'scrollParent',
sanitize: true,
sanitizeFn: null,
whiteList: DefaultWhitelist,
popperConfig: null
};
var HOVER_STATE_SHOW = 'show';
var HOVER_STATE_OUT = 'out';
var Event = {
HIDE: "hide" + EVENT_KEY,
HIDDEN: "hidden" + EVENT_KEY,
SHOW: "show" + EVENT_KEY,
SHOWN: "shown" + EVENT_KEY,
INSERTED: "inserted" + EVENT_KEY,
CLICK: "click" + EVENT_KEY,
FOCUSIN: "focusin" + EVENT_KEY,
FOCUSOUT: "focusout" + EVENT_KEY,
MOUSEENTER: "mouseenter" + EVENT_KEY,
MOUSELEAVE: "mouseleave" + EVENT_KEY
};
var CLASS_NAME_FADE = 'fade';
var CLASS_NAME_SHOW = 'show';
var SELECTOR_TOOLTIP_INNER = '.tooltip-inner';
var SELECTOR_ARROW = '.arrow';
var TRIGGER_HOVER = 'hover';
var TRIGGER_FOCUS = 'focus';
var TRIGGER_CLICK = 'click';
var TRIGGER_MANUAL = 'manual';
/**
* ------------------------------------------------------------------------
* Class Definition
* ------------------------------------------------------------------------
*/
var Tooltip = /*#__PURE__*/function () {
function Tooltip(element, config) {
if (typeof Popper === 'undefined') {
throw new TypeError('Bootstrap\'s tooltips require Popper.js (https://popper.js.org/)');
} // private
this._isEnabled = true;
this._timeout = 0;
this._hoverState = '';
this._activeTrigger = {};
this._popper = null; // Protected
this.element = element;
this.config = this._getConfig(config);
this.tip = null;
this._setListeners();
} // Getters
var _proto = Tooltip.prototype;
// Public
_proto.enable = function enable() {
this._isEnabled = true;
};
_proto.disable = function disable() {
this._isEnabled = false;
};
_proto.toggleEnabled = function toggleEnabled() {
this._isEnabled = !this._isEnabled;
};
_proto.toggle = function toggle(event) {
if (!this._isEnabled) {
return;
}
if (event) {
var dataKey = this.constructor.DATA_KEY;
var context = $(event.currentTarget).data(dataKey);
if (!context) {
context = new this.constructor(event.currentTarget, this._getDelegateConfig());
$(event.currentTarget).data(dataKey, context);
}
context._activeTrigger.click = !context._activeTrigger.click;
if (context._isWithActiveTrigger()) {
context._enter(null, context);
} else {
context._leave(null, context);
}
} else {
if ($(this.getTipElement()).hasClass(CLASS_NAME_SHOW)) {
this._leave(null, this);
return;
}
this._enter(null, this);
}
};
_proto.dispose = function dispose() {
clearTimeout(this._timeout);
$.removeData(this.element, this.constructor.DATA_KEY);
$(this.element).off(this.constructor.EVENT_KEY);
$(this.element).closest('.modal').off('hide.bs.modal', this._hideModalHandler);
if (this.tip) {
$(this.tip).remove();
}
this._isEnabled = null;
this._timeout = null;
this._hoverState = null;
this._activeTrigger = null;
if (this._popper) {
this._popper.destroy();
}
this._popper = null;
this.element = null;
this.config = null;
this.tip = null;
};
_proto.show = function show() {
var _this = this;
if ($(this.element).css('display') === 'none') {
throw new Error('Please use show on visible elements');
}
var showEvent = $.Event(this.constructor.Event.SHOW);
if (this.isWithContent() && this._isEnabled) {
$(this.element).trigger(showEvent);
var shadowRoot = Util.findShadowRoot(this.element);
var isInTheDom = $.contains(shadowRoot !== null ? shadowRoot : this.element.ownerDocument.documentElement, this.element);
if (showEvent.isDefaultPrevented() || !isInTheDom) {
return;
}
var tip = this.getTipElement();
var tipId = Util.getUID(this.constructor.NAME);
tip.setAttribute('id', tipId);
this.element.setAttribute('aria-describedby', tipId);
this.setContent();
if (this.config.animation) {
$(tip).addClass(CLASS_NAME_FADE);
}
var placement = typeof this.config.placement === 'function' ? this.config.placement.call(this, tip, this.element) : this.config.placement;
var attachment = this._getAttachment(placement);
this.addAttachmentClass(attachment);
var container = this._getContainer();
$(tip).data(this.constructor.DATA_KEY, this);
if (!$.contains(this.element.ownerDocument.documentElement, this.tip)) {
$(tip).appendTo(container);
}
$(this.element).trigger(this.constructor.Event.INSERTED);
this._popper = new Popper(this.element, tip, this._getPopperConfig(attachment));
$(tip).addClass(CLASS_NAME_SHOW); // If this is a touch-enabled device we add extra
// empty mouseover listeners to the body's immediate children;
// only needed because of broken event delegation on iOS
// https://www.quirksmode.org/blog/archives/2014/02/mouse_event_bub.html
if ('ontouchstart' in document.documentElement) {
$(document.body).children().on('mouseover', null, $.noop);
}
var complete = function complete() {
if (_this.config.animation) {
_this._fixTransition();
}
var prevHoverState = _this._hoverState;
_this._hoverState = null;
$(_this.element).trigger(_this.constructor.Event.SHOWN);
if (prevHoverState === HOVER_STATE_OUT) {
_this._leave(null, _this);
}
};
if ($(this.tip).hasClass(CLASS_NAME_FADE)) {
var transitionDuration = Util.getTransitionDurationFromElement(this.tip);
$(this.tip).one(Util.TRANSITION_END, complete).emulateTransitionEnd(transitionDuration);
} else {
complete();
}
}
};
_proto.hide = function hide(callback) {
var _this2 = this;
var tip = this.getTipElement();
var hideEvent = $.Event(this.constructor.Event.HIDE);
var complete = function complete() {
if (_this2._hoverState !== HOVER_STATE_SHOW && tip.parentNode) {
tip.parentNode.removeChild(tip);
}
_this2._cleanTipClass();
_this2.element.removeAttribute('aria-describedby');
$(_this2.element).trigger(_this2.constructor.Event.HIDDEN);
if (_this2._popper !== null) {
_this2._popper.destroy();
}
if (callback) {
callback();
}
};
$(this.element).trigger(hideEvent);
if (hideEvent.isDefaultPrevented()) {
return;
}
$(tip).removeClass(CLASS_NAME_SHOW); // If this is a touch-enabled device we remove the extra
// empty mouseover listeners we added for iOS support
if ('ontouchstart' in document.documentElement) {
$(document.body).children().off('mouseover', null, $.noop);
}
this._activeTrigger[TRIGGER_CLICK] = false;
this._activeTrigger[TRIGGER_FOCUS] = false;
this._activeTrigger[TRIGGER_HOVER] = false;
if ($(this.tip).hasClass(CLASS_NAME_FADE)) {
var transitionDuration = Util.getTransitionDurationFromElement(tip);
$(tip).one(Util.TRANSITION_END, complete).emulateTransitionEnd(transitionDuration);
} else {
complete();
}
this._hoverState = '';
};
_proto.update = function update() {
if (this._popper !== null) {
this._popper.scheduleUpdate();
}
} // Protected
;
_proto.isWithContent = function isWithContent() {
return Boolean(this.getTitle());
};
_proto.addAttachmentClass = function addAttachmentClass(attachment) {
$(this.getTipElement()).addClass(CLASS_PREFIX + "-" + attachment);
};
_proto.getTipElement = function getTipElement() {
this.tip = this.tip || $(this.config.template)[0];
return this.tip;
};
_proto.setContent = function setContent() {
var tip = this.getTipElement();
this.setElementContent($(tip.querySelectorAll(SELECTOR_TOOLTIP_INNER)), this.getTitle());
$(tip).removeClass(CLASS_NAME_FADE + " " + CLASS_NAME_SHOW);
};
_proto.setElementContent = function setElementContent($element, content) {
if (typeof content === 'object' && (content.nodeType || content.jquery)) {
// Content is a DOM node or a jQuery
if (this.config.html) {
if (!$(content).parent().is($element)) {
$element.empty().append(content);
}
} else {
$element.text($(content).text());
}
return;
}
if (this.config.html) {
if (this.config.sanitize) {
content = sanitizeHtml(content, this.config.whiteList, this.config.sanitizeFn);
}
$element.html(content);
} else {
$element.text(content);
}
};
_proto.getTitle = function getTitle() {
var title = this.element.getAttribute('data-original-title');
if (!title) {
title = typeof this.config.title === 'function' ? this.config.title.call(this.element) : this.config.title;
}
return title;
} // Private
;
_proto._getPopperConfig = function _getPopperConfig(attachment) {
var _this3 = this;
var defaultBsConfig = {
placement: attachment,
modifiers: {
offset: this._getOffset(),
flip: {
behavior: this.config.fallbackPlacement
},
arrow: {
element: SELECTOR_ARROW
},
preventOverflow: {
boundariesElement: this.config.boundary
}
},
onCreate: function onCreate(data) {
if (data.originalPlacement !== data.placement) {
_this3._handlePopperPlacementChange(data);
}
},
onUpdate: function onUpdate(data) {
return _this3._handlePopperPlacementChange(data);
}
};
return _extends({}, defaultBsConfig, this.config.popperConfig);
};
_proto._getOffset = function _getOffset() {
var _this4 = this;
var offset = {};
if (typeof this.config.offset === 'function') {
offset.fn = function (data) {
data.offsets = _extends({}, data.offsets, _this4.config.offset(data.offsets, _this4.element) || {});
return data;
};
} else {
offset.offset = this.config.offset;
}
return offset;
};
_proto._getContainer = function _getContainer() {
if (this.config.container === false) {
return document.body;
}
if (Util.isElement(this.config.container)) {
return $(this.config.container);
}
return $(document).find(this.config.container);
};
_proto._getAttachment = function _getAttachment(placement) {
return AttachmentMap[placement.toUpperCase()];
};
_proto._setListeners = function _setListeners() {
var _this5 = this;
var triggers = this.config.trigger.split(' ');
triggers.forEach(function (trigger) {
if (trigger === 'click') {
$(_this5.element).on(_this5.constructor.Event.CLICK, _this5.config.selector, function (event) {
return _this5.toggle(event);
});
} else if (trigger !== TRIGGER_MANUAL) {
var eventIn = trigger === TRIGGER_HOVER ? _this5.constructor.Event.MOUSEENTER : _this5.constructor.Event.FOCUSIN;
var eventOut = trigger === TRIGGER_HOVER ? _this5.constructor.Event.MOUSELEAVE : _this5.constructor.Event.FOCUSOUT;
$(_this5.element).on(eventIn, _this5.config.selector, function (event) {
return _this5._enter(event);
}).on(eventOut, _this5.config.selector, function (event) {
return _this5._leave(event);
});
}
});
this._hideModalHandler = function () {
if (_this5.element) {
_this5.hide();
}
};
$(this.element).closest('.modal').on('hide.bs.modal', this._hideModalHandler);
if (this.config.selector) {
this.config = _extends({}, this.config, {
trigger: 'manual',
selector: ''
});
} else {
this._fixTitle();
}
};
_proto._fixTitle = function _fixTitle() {
var titleType = typeof this.element.getAttribute('data-original-title');
if (this.element.getAttribute('title') || titleType !== 'string') {
this.element.setAttribute('data-original-title', this.element.getAttribute('title') || '');
this.element.setAttribute('title', '');
}
};
_proto._enter = function _enter(event, context) {
var dataKey = this.constructor.DATA_KEY;
context = context || $(event.currentTarget).data(dataKey);
if (!context) {
context = new this.constructor(event.currentTarget, this._getDelegateConfig());
$(event.currentTarget).data(dataKey, context);
}
if (event) {
context._activeTrigger[event.type === 'focusin' ? TRIGGER_FOCUS : TRIGGER_HOVER] = true;
}
if ($(context.getTipElement()).hasClass(CLASS_NAME_SHOW) || context._hoverState === HOVER_STATE_SHOW) {
context._hoverState = HOVER_STATE_SHOW;
return;
}
clearTimeout(context._timeout);
context._hoverState = HOVER_STATE_SHOW;
if (!context.config.delay || !context.config.delay.show) {
context.show();
return;
}
context._timeout = setTimeout(function () {
if (context._hoverState === HOVER_STATE_SHOW) {
context.show();
}
}, context.config.delay.show);
};
_proto._leave = function _leave(event, context) {
var dataKey = this.constructor.DATA_KEY;
context = context || $(event.currentTarget).data(dataKey);
if (!context) {
context = new this.constructor(event.currentTarget, this._getDelegateConfig());
$(event.currentTarget).data(dataKey, context);
}
if (event) {
context._activeTrigger[event.type === 'focusout' ? TRIGGER_FOCUS : TRIGGER_HOVER] = false;
}
if (context._isWithActiveTrigger()) {
return;
}
clearTimeout(context._timeout);
context._hoverState = HOVER_STATE_OUT;
if (!context.config.delay || !context.config.delay.hide) {
context.hide();
return;
}
context._timeout = setTimeout(function () {
if (context._hoverState === HOVER_STATE_OUT) {
context.hide();
}
}, context.config.delay.hide);
};
_proto._isWithActiveTrigger = function _isWithActiveTrigger() {
for (var trigger in this._activeTrigger) {
if (this._activeTrigger[trigger]) {
return true;
}
}
return false;
};
_proto._getConfig = function _getConfig(config) {
var dataAttributes = $(this.element).data();
Object.keys(dataAttributes).forEach(function (dataAttr) {
if (DISALLOWED_ATTRIBUTES.indexOf(dataAttr) !== -1) {
delete dataAttributes[dataAttr];
}
});
config = _extends({}, this.constructor.Default, dataAttributes, typeof config === 'object' && config ? config : {});
if (typeof config.delay === 'number') {
config.delay = {
show: config.delay,
hide: config.delay
};
}
if (typeof config.title === 'number') {
config.title = config.title.toString();
}
if (typeof config.content === 'number') {
config.content = config.content.toString();
}
Util.typeCheckConfig(NAME, config, this.constructor.DefaultType);
if (config.sanitize) {
config.template = sanitizeHtml(config.template, config.whiteList, config.sanitizeFn);
}
return config;
};
_proto._getDelegateConfig = function _getDelegateConfig() {
var config = {};
if (this.config) {
for (var key in this.config) {
if (this.constructor.Default[key] !== this.config[key]) {
config[key] = this.config[key];
}
}
}
return config;
};
_proto._cleanTipClass = function _cleanTipClass() {
var $tip = $(this.getTipElement());
var tabClass = $tip.attr('class').match(BSCLS_PREFIX_REGEX);
if (tabClass !== null && tabClass.length) {
$tip.removeClass(tabClass.join(''));
}
};
_proto._handlePopperPlacementChange = function _handlePopperPlacementChange(popperData) {
this.tip = popperData.instance.popper;
this._cleanTipClass();
this.addAttachmentClass(this._getAttachment(popperData.placement));
};
_proto._fixTransition = function _fixTransition() {
var tip = this.getTipElement();
var initConfigAnimation = this.config.animation;
if (tip.getAttribute('x-placement') !== null) {
return;
}
$(tip).removeClass(CLASS_NAME_FADE);
this.config.animation = false;
this.hide();
this.show();
this.config.animation = initConfigAnimation;
} // Static
;
Tooltip._jQueryInterface = function _jQueryInterface(config) {
return this.each(function () {
var data = $(this).data(DATA_KEY);
var _config = typeof config === 'object' && config;
if (!data && /dispose|hide/.test(config)) {
return;
}
if (!data) {
data = new Tooltip(this, _config);
$(this).data(DATA_KEY, data);
}
if (typeof config === 'string') {
if (typeof data[config] === 'undefined') {
throw new TypeError("No method named \"" + config + "\"");
}
data[config]();
}
});
};
_createClass(Tooltip, null, [{
key: "VERSION",
get: function get() {
return VERSION;
}
}, {
key: "Default",
get: function get() {
return Default;
}
}, {
key: "NAME",
get: function get() {
return NAME;
}
}, {
key: "DATA_KEY",
get: function get() {
return DATA_KEY;
}
}, {
key: "Event",
get: function get() {
return Event;
}
}, {
key: "EVENT_KEY",
get: function get() {
return EVENT_KEY;
}
}, {
key: "DefaultType",
get: function get() {
return DefaultType;
}
}]);
return Tooltip;
}();
/**
* ------------------------------------------------------------------------
* jQuery
* ------------------------------------------------------------------------
*/
$.fn[NAME] = Tooltip._jQueryInterface;
$.fn[NAME].Constructor = Tooltip;
$.fn[NAME].noConflict = function () {
$.fn[NAME] = JQUERY_NO_CONFLICT;
return Tooltip._jQueryInterface;
};
return Tooltip;
})));
//# sourceMappingURL=tooltip.js.map
/* @preserve
_____ __ _ __ _
/ ___// /(_)___/ /___ ____ (_)___
/ (_ // // // _ // -_)/ __/_ / /(_-<
\___//_//_/ \_,_/ \__//_/ (_)__/ //___/
|___/
Version: 1.7.4
Author: Nick Piscitelli (pickykneee)
Website: https://nickpiscitelli.com
Documentation: http://nickpiscitelli.github.io/Glider.js
License: MIT License
Release Date: October 25th, 2018
*/
/* global define */
(function (factory) {
typeof define === 'function' && define.amd
? define(factory)
: typeof exports === 'object'
? (module.exports = factory())
: factory()
})(function () {
('use strict') // eslint-disable-line no-unused-expressions
/* globals window:true */
var _window = typeof window !== 'undefined' ? window : this
var Glider = (_window.Glider = function (element, settings) {
var _ = this
if (element._glider) return element._glider
_.ele = element
_.ele.classList.add('glider')
// expose glider object to its DOM element
_.ele._glider = _
// merge user setting with defaults
_.opt = Object.assign(
{},
{
slidesToScroll: 1,
slidesToShow: 1,
resizeLock: true,
duration: 0.5,
autoplay: false,
animateDots: false,
animateDirection: 'X',
scrollbar: false,
trackOffset: true,
// easeInQuad
easing: function (x, t, b, c, d) {
return c * (t /= d) * t + b
}
},
settings
)
// set defaults
_.animate_id = _.page = _.slide = 0
_.arrows = {}
// preserve original options to
// extend breakpoint settings
_._opt = _.opt
if (_.opt.skipTrack) {
// first and only child is the track
_.track = _.ele.children[0]
} else {
// create track and wrap slides
_.track = document.createElement('div')
_.ele.appendChild(_.track)
while (_.ele.children.length !== 1) {
_.track.appendChild(_.ele.children[0])
}
}
_.track.classList.add('glider-track')
// start glider
_.init()
// set events
_.resize = _.init.bind(_, true)
_.event(_.ele, 'add', {
scroll: _.updateControls.bind(_)
})
_.event(_window, 'add', {
resize: _.resize
})
})
var gliderPrototype = Glider.prototype
gliderPrototype.init = function (refresh, paging) {
var _ = this
var width = 0
var height = 0
_.slides = []
for (var i = 0; i < _.track.children.length; ++i) {
var slide = _.track.children[i]
var style = window.getComputedStyle(slide)
if(style.display !== 'none'){
slide.classList.add('glider-slide')
slide.setAttribute('data-gslide', i)
_.slides.push(slide)
}
}
_.containerWidth = _.ele.clientWidth
var breakpointChanged = _.settingsBreakpoint()
if (!paging) paging = breakpointChanged
if (
_.opt.slidesToShow === 'auto' ||
typeof _.opt._autoSlide !== 'undefined'
) {
var slideCount = _.containerWidth / _.opt.itemWidth
_.opt._autoSlide = _.opt.slidesToShow = _.opt.exactWidth
? slideCount
: Math.max(1, Math.floor(slideCount))
}
if (_.opt.slidesToScroll === 'auto') {
_.opt.slidesToScroll = Math.floor(_.opt.slidesToShow)
}
_.itemWidth = _.opt.exactWidth
? _.opt.itemWidth
: _.containerWidth / _.opt.slidesToShow;
// set slide dimensions
[].forEach.call(_.slides, function (__) {
__.style.height = 'auto'
__.style.width = _.itemWidth + 'px'
width += _.itemWidth
height = Math.max(__.offsetHeight, height)
})
// if fractional slides, add offset to end of track
var trackOffset = 0
if (_.opt.trackOffset) {
if( !Number.isInteger(_.opt.slidesToShow) ){
var slideFraction = (_.opt.slidesToShow - Math.floor(_.opt.slidesToShow)).toPrecision(3)
trackOffset = slideFraction * _.itemWidth
}
}
_.track.style.width = width + trackOffset + 'px'
_.trackWidth = width
_.isDrag = false
_.preventClick = false
_.opt.resizeLock && _.scrollTo(_.slide * _.itemWidth, 0)
if (_.opt.scrollbar) {
// for now we only need a fake scrollbar on ios (iphone and ipad)
if(_.iosTest() ){
_.ele.parentElement.classList.add('fake-scrollbar')
var scrollbarEl = document.createElement('div')
scrollbarEl.classList.add('scrollbar')
_.ele.parentElement.appendChild(scrollbarEl)
_.opt.dots = scrollbarEl
} else {
_.ele.parentElement.classList.add('scrollbar')
}
}
if (_.opt.autoplay) {
// set default autplay properties if they have not been defined
_.opt.autoplay = Object.assign(
{
delay: 5000,
disableOnInteraction: true,
repeat: true
},
_.opt.autoplay
)
_.autoPlayDelay = _.opt.autoplay.delay
}
if (breakpointChanged || paging) {
_.bindArrows()
_.buildDots()
_.bindDrag()
}
_.updateControls()
_.emit(refresh ? 'refresh' : 'loaded')
}
gliderPrototype.iosTest = function () {
return [
'iPad Simulator',
'iPhone Simulator',
'iPod Simulator',
'iPad',
'iPhone',
'iPod'
].indexOf(navigator.platform) !== -1
// iPad on iOS 13 detection
|| (navigator.userAgent.indexOf('Mac') !== -1 && 'ontouchend' in document)
}
gliderPrototype.prevSlide = null
gliderPrototype.nextSlide = null
gliderPrototype.autoPlayDirection = 'forward' // forward or reverse
gliderPrototype.autoPlayTimer = 0
gliderPrototype.autoPlayDelay = 0
gliderPrototype.autoPlayTimeElapsed = 0
gliderPrototype.resetAutoPlay = function () {
var _ = this
// reset timer states
if(_.autoPlayTimer){
clearTimeout(_.autoPlayTimer)
_.autoPlayTimer = null
}
if(_.opt.animateDots && typeof _.dots !== 'undefined' && _.dots !== null){
// reset dots styles and progress state
for (var dotIndex = 0; dotIndex < _.dots.childElementCount; dotIndex++) {
var dotEl = _.dots.children[dotIndex].firstElementChild.firstElementChild
dotEl.style.transitionDuration = '0s'
dotEl.style.transform = ''
}
_.autoPlayDelay = _.opt.autoplay.delay
_.autoPlayTimeElapsed = 0
}
}
gliderPrototype.autoPlay = function () {
var _ = this
_.resetAutoPlay()
if(_.autoPlayDirection === 'forward') {
var nextIndex = _.slide + 1
if(_.opt.animateDots && typeof _.dots !== 'undefined' && _.dots !== null){
var activeDot = _.dots.children[_.slide].firstElementChild.firstElementChild
activeDot.style.transitionDuration = ( _.autoPlayDelay / 1000).toFixed(3) + 's'
activeDot.style.transform = 'translate'+ _.opt.animateDirection +'(100%)'
}
_.autoPlayTimer = setTimeout(function(){
if (nextIndex >= _.track.childElementCount) {
if (!_.opt.autoplay.repeat) {
_.resetAutoPlay()
} else {
nextIndex = 0
_.scrollItem(nextIndex)
}
} else {
_.scrollItem(nextIndex)
}
}, _.autoPlayDelay)
}
if(_.autoPlayDirection === 'reverse') {
var nextIndex = _.slide - 1
if(_.opt.animateDots && typeof _.dots !== 'undefined' && _.dots !== null){
var activeDot = _.dots.children[_.slide].firstElementChild.firstElementChild
activeDot.style.transitionDuration = ( _.autoPlayDelay / 1000).toFixed(3) + 's'
activeDot.style.transform = 'translate'+ _.opt.animateDirection +'(100%)'
}
_.autoPlayTimer = setTimeout(function(){
if (nextIndex <= -1) {
if (!_.opt.autoplay.repeat) {
_.resetAutoPlay()
} else {
nextIndex = (_.track.childElementCount - 1)
_.scrollItem(nextIndex)
}
} else {
_.scrollItem(nextIndex)
}
}, _.autoPlayDelay)
}
}
gliderPrototype.bindDrag = function () {
var _ = this
_.mouse = _.mouse || _.handleMouse.bind(_)
var mouseenter = function (e) {
if(_.opt.autoplay && _.opt.autoplay.disableOnInteraction){
_.pauseAutoplay()
}
}
var mouseleave = function (e) {
if(_.opt.autoplay && _.opt.autoplay.disableOnInteraction){
_.resumeAutoplay()
}
// mouseup used to be the handler for mouseleave, so just run it at the end of our custom function
// mouseup default function
_.mouseDown = undefined
_.ele.classList.remove('drag')
if (_.isDrag) {
_.preventClick = true
}
_.isDrag = false
// end mouseup default function
}
var mouseup = function () {
// handle mouseup and touchend
_.mouseDown = undefined
_.ele.classList.remove('drag')
if (_.isDrag) {
_.preventClick = true
if (_.opt.scrollLock) {
_.scrollItem(_.getCurrentSlide())
}
}
_.isDrag = false
}
var mousedown = function (e) {
// handle mousedown and touchstart
if(_.opt.autoplay){
_.resetAutoPlay()
}
e.preventDefault()
e.stopPropagation()
_.mouseDown = e.clientX
_.ele.classList.add('drag')
}
var mouseclick = function (e) {
if (_.preventClick) {
e.preventDefault()
e.stopPropagation()
}
_.preventClick = false
}
// TO DO: touch end not working as expected on ios
// var touchend = function (e) {
// if (_.opt.scrollLock) {
// _.scrollItem(_.getCurrentSlide())
// }
// }
var events = {
mouseenter: mouseenter,
mouseleave: mouseleave,
mouseup: mouseup,
mousedown: mousedown,
mousemove: _.mouse,
// touchend: touchend,
click: mouseclick
}
// refactor: ie11 does not support second param passed to toggle:
// _.ele.classList.toggle('draggable', _.opt.draggable === true)
if( _.opt.draggable === true) {
_.ele.classList.add('draggable')
} else {
_.ele.classList.remove('draggable')
}
_.event(_.ele, 'remove', events)
if (_.opt.draggable) _.event(_.ele, 'add', events)
}
gliderPrototype.buildDots = function () {
var _ = this
if (!_.opt.dots) {
if (_.dots) _.dots.innerHTML = ''
return
}
if (typeof _.opt.dots === 'string') {
_.dots = document.querySelector(_.opt.dots)
} else _.dots = _.opt.dots
if (!_.dots) return
_.dots.innerHTML = ''
_.dots.classList.add('glider-dots')
if(_.opt.animateDots && _.opt.autoplay){
_.dots.classList.add('animate-glider-dots')
}
for (var i = 0; i < Math.ceil(_.slides.length / _.opt.slidesToShow); ++i) {
var dot = document.createElement('button')
dot.dataset.index = i
dot.setAttribute('aria-label', 'Page ' + (i + 1))
dot.setAttribute('role', 'tab')
dot.className = 'glider-dot ' + (i ? '' : 'active')
if(_.opt.animateDots && _.opt.autoplay){
dot.innerHTML = ''
}
_.event(dot, 'add', {
click: _.scrollItem.bind(_, i, true)
})
_.dots.appendChild(dot)
}
}
gliderPrototype.bindArrows = function () {
var _ = this
if (!_.opt.arrows) {
Object.keys(_.arrows).forEach(function (direction) {
var element = _.arrows[direction]
_.event(element, 'remove', { click: element._func })
})
return
}
['prev', 'next'].forEach(function (direction) {
var arrow = _.opt.arrows[direction]
if (arrow) {
if (typeof arrow === 'string') arrow = document.querySelector(arrow)
if (arrow) {
arrow._func = arrow._func || _.scrollItem.bind(_, direction)
_.event(arrow, 'remove', {
click: arrow._func
})
_.event(arrow, 'add', {
click: arrow._func
})
_.arrows[direction] = arrow
}
}
})
}
gliderPrototype.updateControls = function (event) {
var _ = this
if (event && !_.opt.scrollPropagate) {
event.stopPropagation()
}
var disableArrows = _.containerWidth >= _.trackWidth
if (!_.opt.rewind) {
if (_.arrows.prev) {
// refactor: ie11 does not support second param passed to toggle:
// _.arrows.prev.classList.toggle('disabled', _.ele.scrollLeft <= 0 || disableArrows)
if(_.ele.scrollLeft <= 0 || disableArrows){
_.arrows.prev.classList.add('disabled')
} else {
_.arrows.prev.classList.remove('disabled')
}
_.arrows.prev.classList.contains('disabled')
? _.arrows.prev.setAttribute('aria-disabled', true)
: _.arrows.prev.setAttribute('aria-disabled', false)
}
if (_.arrows.next) {
// refactor: ie11 does not support second param passed to toggle:
// _.arrows.next.classList.toggle('disabled', Math.ceil(_.ele.scrollLeft + _.containerWidth) >=Math.floor(_.trackWidth) || disableArrows)
if(Math.ceil(_.ele.scrollLeft + _.containerWidth) >= Math.floor(_.trackWidth) || disableArrows) {
_.arrows.next.classList.add('disabled')
} else {
_.arrows.next.classList.remove('disabled')
}
_.arrows.next.classList.contains('disabled')
? _.arrows.next.setAttribute('aria-disabled', true)
: _.arrows.next.setAttribute('aria-disabled', false)
}
}
_.slide = Math.round(_.ele.scrollLeft / _.itemWidth)
_.page = Math.round(_.ele.scrollLeft / _.containerWidth)
var middle = _.slide + Math.floor(Math.floor(_.opt.slidesToShow) / 2)
var extraMiddle = Math.floor(_.opt.slidesToShow) % 2 ? 0 : middle + 1
if (Math.floor(_.opt.slidesToShow) === 1) {
extraMiddle = 0
}
// the last page may be less than one half of a normal page width so
// the page is rounded down. when at the end, force the page to turn
if (_.ele.scrollLeft + _.containerWidth >= Math.floor(_.trackWidth)) {
_.page = _.dots ? _.dots.children.length - 1 : 0
}
[].forEach.call(_.slides, function (slide, index) {
var slideClasses = slide.classList
var wasVisible = slideClasses.contains('visible')
var start = _.ele.scrollLeft
var end = _.ele.scrollLeft + _.containerWidth
var itemStart = _.itemWidth * index
var itemEnd = itemStart + _.itemWidth;
[].forEach.call(slideClasses, function (className) {
/^left|right/.test(className) && slideClasses.remove(className)
})
// refactor: ie11 does not support second param passed to toggle:
// slideClasses.toggle('active', _.slide === index)
if(_.slide === index){
slideClasses.add('active')
} else {
slideClasses.remove('active')
}
if (middle === index || (extraMiddle && extraMiddle === index)) {
slideClasses.add('center')
} else {
slideClasses.remove('center')
slideClasses.add(
[
index < middle ? 'left' : 'right',
Math.abs(index - (index < middle ? middle : extraMiddle || middle))
].join('-')
)
}
var isVisible =
Math.ceil(itemStart) >= Math.floor(start) &&
Math.floor(itemEnd) <= Math.ceil(end)
// refactor: ie11 does not support second param passed to toggle:
// slideClasses.toggle('visible', isVisible)
if (isVisible) {
slideClasses.add('visible')
} else {
slideClasses.remove('visible')
}
if (isVisible !== wasVisible) {
if(isVisible) {
_.emit('slide-visible', {
slide: index,
prev: _.prevSlide
})
} else {
_.emit('slide-hidden', {
slide: index,
next: _.nextSlide
})
}
}
})
if (_.dots) {
[].forEach.call(_.dots.children, function (dot, index) {
// refactor: ie11 does not support second param passed to toggle:
//dot.classList.toggle('active', _.page === index)
if(_.page === index){
dot.classList.add('active')
} else {
dot.classList.remove('active')
}
})
}
// original scroll lock implementation
if (event && _.opt.scrollLock) {
clearTimeout(_.scrollLock)
_.scrollLock = setTimeout(function () {
clearTimeout(_.scrollLock)
// dont attempt to scroll less than a pixel fraction - causes looping
if (Math.abs(_.ele.scrollLeft / _.itemWidth - _.slide) > 0.02) {
if (!_.mouseDown) {
// Only scroll if not at the end (#94)
if (_.trackWidth > _.containerWidth + _.ele.scrollLeft) {
_.scrollItem(_.getCurrentSlide())
}
}
}
}, _.opt.scrollLockDelay || 250)
}
}
gliderPrototype.getCurrentSlide = function () {
var _ = this
return _.round(_.ele.scrollLeft / _.itemWidth)
}
gliderPrototype.scrollItem = function (slide, dot, e) {
if (e) e.preventDefault()
var _ = this
/*
Updates "nextSlide" property.
Currently only used by "glider-slide-hidden" event which only provides the current slide index before transitioning to next slide.
This allows "glider-slide-hidden" event to also provide the next slide index.
Previously we used the "glider-slide-visible" event because it provides the new slide index, unfortunatly it proivdes the value after the transition completes.
We need to know this value before the transition begins.
*/
var lastIndex = _.slides.length - 1
if(slide === 'next') {
_.nextSlide = (_.slide !== lastIndex ? (_.slide + 1) : 0)
} else if (slide === 'prev') {
_.nextSlide = (_.slide !== 0 ? (_.slide - 1) : lastIndex)
} else {
_.nextSlide = slide
}
/*
Updates "prevSlide" property.
Currently only used by "glider-slide-visible" event which only provides the current slide index after the transitioning to next slide completes.
This allows "glider-slide-visible" event to also provide the previous slide index.
*/
_.prevSlide = _.slide
var originalSlide = slide
++_.animate_id
if (dot === true) {
slide = slide * _.containerWidth
slide = Math.round(slide / _.itemWidth) * _.itemWidth
} else {
if (typeof slide === 'string') {
var backwards = slide === 'prev'
// use precise location if fractional slides are on
if (_.opt.slidesToScroll % 1 || _.opt.slidesToShow % 1) {
slide = _.getCurrentSlide()
} else {
slide = _.slide
}
if (backwards) slide -= _.opt.slidesToScroll
else slide += _.opt.slidesToScroll
}
if (_.opt.rewind) {
var scrollLeft = _.ele.scrollLeft
slide =
backwards && !scrollLeft
? _.slides.length
: !backwards &&
scrollLeft + _.containerWidth >= Math.floor(_.trackWidth)
? 0
: slide
}
slide = Math.max(Math.min(slide, _.slides.length), 0)
_.slide = slide
slide = _.itemWidth * slide
}
_.scrollTo(
slide,
_.opt.duration * Math.abs(_.ele.scrollLeft - slide),
function () {
_.updateControls()
_.emit('animated', {
value: originalSlide,
type:
typeof originalSlide === 'string' ? 'arrow' : dot ? 'dot' : 'slide'
})
}
)
return false
}
gliderPrototype.settingsBreakpoint = function () {
var _ = this
var resp = _._opt.responsive
if (resp) {
// Sort the breakpoints in mobile first order
resp.sort(function (a, b) {
return b.breakpoint - a.breakpoint
})
for (var i = 0; i < resp.length; ++i) {
var size = resp[i]
if (_window.innerWidth >= size.breakpoint) {
if (_.breakpoint !== size.breakpoint) {
_.opt = Object.assign({}, _._opt, size.settings)
_.breakpoint = size.breakpoint
return true
}
return false
}
}
}
// set back to defaults in case they were overriden
var breakpointChanged = _.breakpoint !== 0
_.opt = Object.assign({}, _._opt)
_.breakpoint = 0
return breakpointChanged
}
gliderPrototype.scrollTo = function (scrollTarget, scrollDuration, callback) {
var _ = this
var start = new Date().getTime()
var animateIndex = _.animate_id
function animate () {
var now = new Date().getTime() - start
_.ele.scrollLeft =
_.ele.scrollLeft +
(scrollTarget - _.ele.scrollLeft) *
_.opt.easing(0, now, 0, 1, scrollDuration)
if (now < scrollDuration && animateIndex === _.animate_id) {
_window.requestAnimationFrame(animate)
} else {
_.ele.scrollLeft = scrollTarget
callback && callback.call(_)
// after transition to next slide
// start autoplay for new slide
if (_.opt.autoplay) {
_.autoPlay()
}
}
}
_window.requestAnimationFrame(animate)
}
gliderPrototype.removeItem = function (index) {
var _ = this
if (_.slides.length) {
_.track.removeChild(_.slides[index])
_.refresh(true)
_.emit('remove')
}
}
gliderPrototype.addItem = function (ele) {
var _ = this
_.track.appendChild(ele)
_.refresh(true)
_.emit('add')
}
gliderPrototype.handleMouse = function (e) {
// handles mousemove and touchmove
var _ = this
if (_.mouseDown) {
_.isDrag = true
_.ele.scrollLeft +=
(_.mouseDown - e.clientX) * (_.opt.dragVelocity || 3.3)
_.mouseDown = e.clientX
}
}
// used to round to the nearest 0.XX fraction
gliderPrototype.round = function (double) {
var _ = this
var step = _.opt.slidesToScroll % 1 || 1
var inv = 1.0 / step
return Math.round(double * inv) / inv
}
gliderPrototype.refresh = function (paging) {
var _ = this
_.init(true, paging)
}
gliderPrototype.setOption = function (opt, global) {
var _ = this
if (_.breakpoint && !global) {
_._opt.responsive.forEach(function (v) {
if (v.breakpoint === _.breakpoint) {
v.settings = Object.assign({}, v.settings, opt)
}
})
} else {
_._opt = Object.assign({}, _._opt, opt)
}
_.breakpoint = 0
_.settingsBreakpoint()
}
gliderPrototype.destroy = function () {
var _ = this
var replace = _.ele.cloneNode(true)
var clear = function (ele) {
ele.removeAttribute('style');
[].forEach.call(ele.classList, function (className) {
/^glider/.test(className) && ele.classList.remove(className)
})
}
// remove track
replace.children[0].outerHTML = replace.children[0].innerHTML
clear(replace);
[].forEach.call(replace.getElementsByTagName('*'), clear)
_.ele.parentNode.replaceChild(replace, _.ele)
_.event(_window, 'remove', {
resize: _.resize
})
_.emit('destroy')
}
gliderPrototype.emit = function (name, arg) {
var _ = this
var e = new _window.CustomEvent('glider-' + name, {
bubbles: !_.opt.eventPropagate,
detail: arg
})
_.ele.dispatchEvent(e)
}
gliderPrototype.event = function (ele, type, args) {
var eventHandler = ele[type + 'EventListener'].bind(ele)
Object.keys(args).forEach(function (k) {
eventHandler(k, args[k])
})
}
gliderPrototype.pauseAutoplay = function () {
var _ = this
clearTimeout(_.autoPlayTimer)
_.autoPlayTimer = null
if(_.opt.animateDots && typeof _.dots !== 'undefined' && _.dots !== null){
var activeDot = _.dots.children[_.slide].firstElementChild.firstElementChild
var compStyle = window.getComputedStyle(activeDot).getPropertyValue('transform')
var transformVal
// ie11 cant use DOMMatrixReadOnly
if(typeof DOMMatrixReadOnly !== 'undefined'){
var matrix = new DOMMatrixReadOnly(compStyle)
if(_.opt.animateDirection === 'Y') {
transformVal = matrix.m42
} else {
transformVal = matrix.m41
}
} else {
var init = compStyle.indexOf('(')
var fin = compStyle.indexOf(')')
var str = compStyle.substr( (init + 1), (fin - init - 1))
var fauxMatrix = str.split(',')
if(_.opt.animateDirection === 'Y') {
transformVal = Number(fauxMatrix[5])
} else {
transformVal = Number(fauxMatrix[4])
}
}
// stop and pause dot progress
var totalProgressDistance
if(_.opt.animateDirection === 'Y') {
totalProgressDistance = activeDot.offsetHeight
} else {
totalProgressDistance = activeDot.offsetWidth
}
var progressVal = (transformVal / totalProgressDistance).toFixed(5)
_.autoPlayTimeElapsed = progressVal
activeDot.style.transitionDuration = '0s'
activeDot.style.transform = 'translate'+ _.opt.animateDirection +'(' + (progressVal * 100) + '%)'
}
}
gliderPrototype.resumeAutoplay = function () {
var _ = this
if(_.opt.animateDots && typeof _.dots !== 'undefined' && _.dots !== null){
var activeDot = _.dots.children[_.slide].firstElementChild.firstElementChild
activeDot.style.transitionDuration = ( _.autoPlayDelay / 1000).toFixed(3) + 's'
activeDot.style.transform = 'translate'+ _.opt.animateDirection +'(100%)'
_.autoPlayDelay = Number(_.opt.autoplay.delay - (_.autoPlayTimeElapsed * _.opt.autoplay.delay)).toFixed(3)
}
// TO DO: only clear and start autoPlay if dot is not progressing
clearTimeout(_.autoPlayTimer)
_.autoPlayTimer = null
_.autoPlay()
}
return Glider
});
/*!
* Tempus Dominus v6.2.10 (https://getdatepicker.com/)
* Copyright 2013-2022 Jonathan Peterson
* Licensed under MIT (https://github.com/Eonasdan/tempus-dominus/blob/master/LICENSE)
*/
(function (global, factory) {
typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) :
typeof define === 'function' && define.amd ? define(['exports'], factory) :
(global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory(global.tempusDominus = {}));
})(this, (function (exports) { 'use strict';
exports.Unit = void 0;
(function (Unit) {
Unit["seconds"] = "seconds";
Unit["minutes"] = "minutes";
Unit["hours"] = "hours";
Unit["date"] = "date";
Unit["month"] = "month";
Unit["year"] = "year";
})(exports.Unit || (exports.Unit = {}));
const twoDigitTemplate = {
month: '2-digit',
day: '2-digit',
year: 'numeric',
hour: '2-digit',
minute: '2-digit',
second: '2-digit',
};
const getFormatByUnit = (unit) => {
switch (unit) {
case 'date':
return { dateStyle: 'short' };
case 'month':
return {
month: 'numeric',
year: 'numeric',
};
case 'year':
return { year: 'numeric' };
}
};
const guessHourCycle = (locale) => {
if (!locale)
return 'h12';
// noinspection SpellCheckingInspection
const template = {
hour: '2-digit',
minute: '2-digit',
numberingSystem: 'latn',
};
const dt = new DateTime().setLocale(locale);
dt.hours = 0;
const start = dt.parts(undefined, template).hour;
//midnight is 12 so en-US style 12 AM
if (start === '12')
return 'h12';
//midnight is 24 is from 00-24
if (start === '24')
return 'h24';
dt.hours = 23;
const end = dt.parts(undefined, template).hour;
//if midnight is 00 and hour 23 is 11 then
if (start === '00' && end === '11')
return 'h11';
if (start === '00' && end === '23')
return 'h23';
console.warn(`couldn't determine hour cycle for ${locale}. start: ${start}. end: ${end}`);
return undefined;
};
/**
* For the most part this object behaves exactly the same way
* as the native Date object with a little extra spice.
*/
class DateTime extends Date {
constructor() {
super(...arguments);
/**
* Used with Intl.DateTimeFormat
*/
this.locale = 'default';
this.nonLeapLadder = [
0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334,
];
this.leapLadder = [0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335];
}
/**
* Chainable way to set the {@link locale}
* @param value
*/
setLocale(value) {
this.locale = value;
return this;
}
/**
* Converts a plain JS date object to a DateTime object.
* Doing this allows access to format, etc.
* @param date
* @param locale
*/
static convert(date, locale = 'default') {
if (!date)
throw new Error(`A date is required`);
return new DateTime(date.getFullYear(), date.getMonth(), date.getDate(), date.getHours(), date.getMinutes(), date.getSeconds(), date.getMilliseconds()).setLocale(locale);
}
/**
* Attempts to create a DateTime from a string. A customDateFormat is required for non US dates.
* @param input
* @param localization
*/
//eslint-disable-next-line @typescript-eslint/no-unused-vars
static fromString(input, localization) {
return new DateTime(input);
}
/**
* Native date manipulations are not pure functions. This function creates a duplicate of the DateTime object.
*/
get clone() {
return new DateTime(this.year, this.month, this.date, this.hours, this.minutes, this.seconds, this.getMilliseconds()).setLocale(this.locale);
}
/**
* Sets the current date to the start of the {@link unit} provided
* Example: Consider a date of "April 30, 2021, 11:45:32.984 AM" => new DateTime(2021, 3, 30, 11, 45, 32, 984).startOf('month')
* would return April 1, 2021, 12:00:00.000 AM (midnight)
* @param unit
* @param startOfTheWeek Allows for the changing the start of the week.
*/
startOf(unit, startOfTheWeek = 0) {
if (this[unit] === undefined)
throw new Error(`Unit '${unit}' is not valid`);
switch (unit) {
case 'seconds':
this.setMilliseconds(0);
break;
case 'minutes':
this.setSeconds(0, 0);
break;
case 'hours':
this.setMinutes(0, 0, 0);
break;
case 'date':
this.setHours(0, 0, 0, 0);
break;
case 'weekDay': {
this.startOf(exports.Unit.date);
if (this.weekDay === startOfTheWeek)
break;
let goBack = this.weekDay;
if (startOfTheWeek !== 0 && this.weekDay === 0)
goBack = 8 - startOfTheWeek;
this.manipulate(startOfTheWeek - goBack, exports.Unit.date);
break;
}
case 'month':
this.startOf(exports.Unit.date);
this.setDate(1);
break;
case 'year':
this.startOf(exports.Unit.date);
this.setMonth(0, 1);
break;
}
return this;
}
/**
* Sets the current date to the end of the {@link unit} provided
* Example: Consider a date of "April 30, 2021, 11:45:32.984 AM" => new DateTime(2021, 3, 30, 11, 45, 32, 984).endOf('month')
* would return April 30, 2021, 11:59:59.999 PM
* @param unit
* @param startOfTheWeek
*/
endOf(unit, startOfTheWeek = 0) {
if (this[unit] === undefined)
throw new Error(`Unit '${unit}' is not valid`);
switch (unit) {
case 'seconds':
this.setMilliseconds(999);
break;
case 'minutes':
this.setSeconds(59, 999);
break;
case 'hours':
this.setMinutes(59, 59, 999);
break;
case 'date':
this.setHours(23, 59, 59, 999);
break;
case 'weekDay': {
this.endOf(exports.Unit.date);
const endOfWeek = 6 + startOfTheWeek;
if (this.weekDay === endOfWeek)
break;
this.manipulate(endOfWeek - this.weekDay, exports.Unit.date);
break;
}
case 'month':
this.endOf(exports.Unit.date);
this.manipulate(1, exports.Unit.month);
this.setDate(0);
break;
case 'year':
this.endOf(exports.Unit.date);
this.setMonth(11, 31);
break;
}
return this;
}
/**
* Change a {@link unit} value. Value can be positive or negative
* Example: Consider a date of "April 30, 2021, 11:45:32.984 AM" => new DateTime(2021, 3, 30, 11, 45, 32, 984).manipulate(1, 'month')
* would return May 30, 2021, 11:45:32.984 AM
* @param value A positive or negative number
* @param unit
*/
manipulate(value, unit) {
if (this[unit] === undefined)
throw new Error(`Unit '${unit}' is not valid`);
this[unit] += value;
return this;
}
/**
* Returns a string format.
* See https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/DateTimeFormat
* for valid templates and locale objects
* @param template An object. Uses browser defaults otherwise.
* @param locale Can be a string or an array of strings. Uses browser defaults otherwise.
*/
format(template, locale = this.locale) {
return new Intl.DateTimeFormat(locale, template).format(this);
}
/**
* Return true if {@link compare} is before this date
* @param compare The Date/DateTime to compare
* @param unit If provided, uses {@link startOf} for
* comparision.
*/
isBefore(compare, unit) {
if (!unit)
return this.valueOf() < compare.valueOf();
if (this[unit] === undefined)
throw new Error(`Unit '${unit}' is not valid`);
return (this.clone.startOf(unit).valueOf() < compare.clone.startOf(unit).valueOf());
}
/**
* Return true if {@link compare} is after this date
* @param compare The Date/DateTime to compare
* @param unit If provided, uses {@link startOf} for
* comparision.
*/
isAfter(compare, unit) {
if (!unit)
return this.valueOf() > compare.valueOf();
if (this[unit] === undefined)
throw new Error(`Unit '${unit}' is not valid`);
return (this.clone.startOf(unit).valueOf() > compare.clone.startOf(unit).valueOf());
}
/**
* Return true if {@link compare} is same this date
* @param compare The Date/DateTime to compare
* @param unit If provided, uses {@link startOf} for
* comparision.
*/
isSame(compare, unit) {
if (!unit)
return this.valueOf() === compare.valueOf();
if (this[unit] === undefined)
throw new Error(`Unit '${unit}' is not valid`);
compare = DateTime.convert(compare);
return (this.clone.startOf(unit).valueOf() === compare.startOf(unit).valueOf());
}
/**
* Check if this is between two other DateTimes, optionally looking at unit scale. The match is exclusive.
* @param left
* @param right
* @param unit.
* @param inclusivity. A [ indicates inclusion of a value. A ( indicates exclusion.
* If the inclusivity parameter is used, both indicators must be passed.
*/
isBetween(left, right, unit, inclusivity = '()') {
if (unit && this[unit] === undefined)
throw new Error(`Unit '${unit}' is not valid`);
const leftInclusivity = inclusivity[0] === '(';
const rightInclusivity = inclusivity[1] === ')';
return (((leftInclusivity
? this.isAfter(left, unit)
: !this.isBefore(left, unit)) &&
(rightInclusivity
? this.isBefore(right, unit)
: !this.isAfter(right, unit))) ||
((leftInclusivity
? this.isBefore(left, unit)
: !this.isAfter(left, unit)) &&
(rightInclusivity
? this.isAfter(right, unit)
: !this.isBefore(right, unit))));
}
/**
* Returns flattened object of the date. Does not include literals
* @param locale
* @param template
*/
parts(locale = this.locale, template = { dateStyle: 'full', timeStyle: 'long' }) {
const parts = {};
new Intl.DateTimeFormat(locale, template)
.formatToParts(this)
.filter((x) => x.type !== 'literal')
.forEach((x) => (parts[x.type] = x.value));
return parts;
}
/**
* Shortcut to Date.getSeconds()
*/
get seconds() {
return this.getSeconds();
}
/**
* Shortcut to Date.setSeconds()
*/
set seconds(value) {
this.setSeconds(value);
}
/**
* Returns two digit hours
*/
get secondsFormatted() {
return this.parts(undefined, twoDigitTemplate).second;
}
/**
* Shortcut to Date.getMinutes()
*/
get minutes() {
return this.getMinutes();
}
/**
* Shortcut to Date.setMinutes()
*/
set minutes(value) {
this.setMinutes(value);
}
/**
* Returns two digit minutes
*/
get minutesFormatted() {
return this.parts(undefined, twoDigitTemplate).minute;
}
/**
* Shortcut to Date.getHours()
*/
get hours() {
return this.getHours();
}
/**
* Shortcut to Date.setHours()
*/
set hours(value) {
this.setHours(value);
}
getHoursFormatted(hourCycle = 'h12') {
return this.parts(undefined, { ...twoDigitTemplate, hourCycle: hourCycle })
.hour;
}
/**
* Get the meridiem of the date. E.g. AM or PM.
* If the {@link locale} provides a "dayPeriod" then this will be returned,
* otherwise it will return AM or PM.
* @param locale
*/
meridiem(locale = this.locale) {
return new Intl.DateTimeFormat(locale, {
hour: 'numeric',
hour12: true,
})
.formatToParts(this)
.find((p) => p.type === 'dayPeriod')?.value;
}
/**
* Shortcut to Date.getDate()
*/
get date() {
return this.getDate();
}
/**
* Shortcut to Date.setDate()
*/
set date(value) {
this.setDate(value);
}
/**
* Return two digit date
*/
get dateFormatted() {
return this.parts(undefined, twoDigitTemplate).day;
}
/**
* Shortcut to Date.getDay()
*/
get weekDay() {
return this.getDay();
}
/**
* Shortcut to Date.getMonth()
*/
get month() {
return this.getMonth();
}
/**
* Shortcut to Date.setMonth()
*/
set month(value) {
const targetMonth = new Date(this.year, value + 1);
targetMonth.setDate(0);
const endOfMonth = targetMonth.getDate();
if (this.date > endOfMonth) {
this.date = endOfMonth;
}
this.setMonth(value);
}
/**
* Return two digit, human expected month. E.g. January = 1, December = 12
*/
get monthFormatted() {
return this.parts(undefined, twoDigitTemplate).month;
}
/**
* Shortcut to Date.getFullYear()
*/
get year() {
return this.getFullYear();
}
/**
* Shortcut to Date.setFullYear()
*/
set year(value) {
this.setFullYear(value);
}
// borrowed a bunch of stuff from Luxon
/**
* Gets the week of the year
*/
get week() {
const ordinal = this.computeOrdinal(), weekday = this.getUTCDay();
let weekNumber = Math.floor((ordinal - weekday + 10) / 7);
if (weekNumber < 1) {
weekNumber = this.weeksInWeekYear(this.year - 1);
}
else if (weekNumber > this.weeksInWeekYear(this.year)) {
weekNumber = 1;
}
return weekNumber;
}
weeksInWeekYear(weekYear) {
const p1 = (weekYear +
Math.floor(weekYear / 4) -
Math.floor(weekYear / 100) +
Math.floor(weekYear / 400)) %
7, last = weekYear - 1, p2 = (last +
Math.floor(last / 4) -
Math.floor(last / 100) +
Math.floor(last / 400)) %
7;
return p1 === 4 || p2 === 3 ? 53 : 52;
}
get isLeapYear() {
return (this.year % 4 === 0 && (this.year % 100 !== 0 || this.year % 400 === 0));
}
computeOrdinal() {
return (this.date +
(this.isLeapYear ? this.leapLadder : this.nonLeapLadder)[this.month]);
}
}
class TdError extends Error {
}
class ErrorMessages {
constructor() {
this.base = 'TD:';
//#endregion
//#region used with notify.error
/**
* Used with an Error Event type if the user selects a date that
* fails restriction validation.
*/
this.failedToSetInvalidDate = 'Failed to set invalid date';
/**
* Used with an Error Event type when a user changes the value of the
* input field directly, and does not provide a valid date.
*/
this.failedToParseInput = 'Failed parse input field';
//#endregion
}
//#region out to console
/**
* Throws an error indicating that a key in the options object is invalid.
* @param optionName
*/
unexpectedOption(optionName) {
const error = new TdError(`${this.base} Unexpected option: ${optionName} does not match a known option.`);
error.code = 1;
throw error;
}
/**
* Throws an error indicating that one more keys in the options object is invalid.
* @param optionName
*/
unexpectedOptions(optionName) {
const error = new TdError(`${this.base}: ${optionName.join(', ')}`);
error.code = 1;
throw error;
}
/**
* Throws an error when an option is provide an unsupported value.
* For example a value of 'cheese' for toolbarPlacement which only supports
* 'top', 'bottom', 'default'.
* @param optionName
* @param badValue
* @param validOptions
*/
unexpectedOptionValue(optionName, badValue, validOptions) {
const error = new TdError(`${this.base} Unexpected option value: ${optionName} does not accept a value of "${badValue}". Valid values are: ${validOptions.join(', ')}`);
error.code = 2;
throw error;
}
/**
* Throws an error when an option value is the wrong type.
* For example a string value was provided to multipleDates which only
* supports true or false.
* @param optionName
* @param badType
* @param expectedType
*/
typeMismatch(optionName, badType, expectedType) {
const error = new TdError(`${this.base} Mismatch types: ${optionName} has a type of ${badType} instead of the required ${expectedType}`);
error.code = 3;
throw error;
}
/**
* Throws an error when an option value is outside of the expected range.
* For example restrictions.daysOfWeekDisabled excepts a value between 0 and 6.
* @param optionName
* @param lower
* @param upper
*/
numbersOutOfRange(optionName, lower, upper) {
const error = new TdError(`${this.base} ${optionName} expected an array of number between ${lower} and ${upper}.`);
error.code = 4;
throw error;
}
/**
* Throws an error when a value for a date options couldn't be parsed. Either
* the option was an invalid string or an invalid Date object.
* @param optionName
* @param date
* @param soft If true, logs a warning instead of an error.
*/
//eslint-disable-next-line @typescript-eslint/no-explicit-any
failedToParseDate(optionName, date, soft = false) {
const error = new TdError(`${this.base} Could not correctly parse "${date}" to a date for ${optionName}.`);
error.code = 5;
if (!soft)
throw error;
console.warn(error);
}
/**
* Throws when an element to attach to was not provided in the constructor.
*/
mustProvideElement() {
const error = new TdError(`${this.base} No element was provided.`);
error.code = 6;
throw error;
}
/**
* Throws if providing an array for the events to subscribe method doesn't have
* the same number of callbacks. E.g., subscribe([1,2], [1])
*/
subscribeMismatch() {
const error = new TdError(`${this.base} The subscribed events does not match the number of callbacks`);
error.code = 7;
throw error;
}
/**
* Throws if the configuration has conflicting rules e.g. minDate is after maxDate
*/
conflictingConfiguration(message) {
const error = new TdError(`${this.base} A configuration value conflicts with another rule. ${message}`);
error.code = 8;
throw error;
}
/**
* customDateFormat errors
*/
customDateFormatError(message) {
const error = new TdError(`${this.base} customDateFormat: ${message}`);
error.code = 9;
throw error;
}
/**
* Logs a warning if a date option value is provided as a string, instead of
* a date/datetime object.
*/
dateString() {
console.warn(`${this.base} Using a string for date options is not recommended unless you specify an ISO string or use the customDateFormat plugin.`);
}
deprecatedWarning(message, remediation) {
console.warn(`${this.base} Warning ${message} is deprecated and will be removed in a future version. ${remediation}`);
}
throwError(message) {
const error = new TdError(`${this.base} ${message}`);
error.code = 9;
throw error;
}
}
// this is not the way I want this to stay but nested classes seemed to blown up once its compiled.
const NAME = 'tempus-dominus', dataKey = 'td';
/**
* Events
*/
class Events {
constructor() {
this.key = `.${dataKey}`;
/**
* Change event. Fired when the user selects a date.
* See also EventTypes.ChangeEvent
*/
this.change = `change${this.key}`;
/**
* Emit when the view changes for example from month view to the year view.
* See also EventTypes.ViewUpdateEvent
*/
this.update = `update${this.key}`;
/**
* Emits when a selected date or value from the input field fails to meet the provided validation rules.
* See also EventTypes.FailEvent
*/
this.error = `error${this.key}`;
/**
* Show event
* @event Events#show
*/
this.show = `show${this.key}`;
/**
* Hide event
* @event Events#hide
*/
this.hide = `hide${this.key}`;
// blur and focus are used in the jQuery provider but are otherwise unused.
// keyup/down will be used later for keybinding options
this.blur = `blur${this.key}`;
this.focus = `focus${this.key}`;
this.keyup = `keyup${this.key}`;
this.keydown = `keydown${this.key}`;
}
}
class Css {
constructor() {
/**
* The outer element for the widget.
*/
this.widget = `${NAME}-widget`;
/**
* Hold the previous, next and switcher divs
*/
this.calendarHeader = 'calendar-header';
/**
* The element for the action to change the calendar view. E.g. month -> year.
*/
this.switch = 'picker-switch';
/**
* The elements for all the toolbar options
*/
this.toolbar = 'toolbar';
/**
* Disables the hover and rounding affect.
*/
this.noHighlight = 'no-highlight';
/**
* Applied to the widget element when the side by side option is in use.
*/
this.sideBySide = 'timepicker-sbs';
/**
* The element for the action to change the calendar view, e.g. August -> July
*/
this.previous = 'previous';
/**
* The element for the action to change the calendar view, e.g. August -> September
*/
this.next = 'next';
/**
* Applied to any action that would violate any restriction options. ALso applied
* to an input field if the disabled function is called.
*/
this.disabled = 'disabled';
/**
* Applied to any date that is less than requested view,
* e.g. the last day of the previous month.
*/
this.old = 'old';
/**
* Applied to any date that is greater than of requested view,
* e.g. the last day of the previous month.
*/
this.new = 'new';
/**
* Applied to any date that is currently selected.
*/
this.active = 'active';
//#region date element
/**
* The outer element for the calendar view.
*/
this.dateContainer = 'date-container';
/**
* The outer element for the decades view.
*/
this.decadesContainer = `${this.dateContainer}-decades`;
/**
* Applied to elements within the decades container, e.g. 2020, 2030
*/
this.decade = 'decade';
/**
* The outer element for the years view.
*/
this.yearsContainer = `${this.dateContainer}-years`;
/**
* Applied to elements within the years container, e.g. 2021, 2021
*/
this.year = 'year';
/**
* The outer element for the month view.
*/
this.monthsContainer = `${this.dateContainer}-months`;
/**
* Applied to elements within the month container, e.g. January, February
*/
this.month = 'month';
/**
* The outer element for the calendar view.
*/
this.daysContainer = `${this.dateContainer}-days`;
/**
* Applied to elements within the day container, e.g. 1, 2..31
*/
this.day = 'day';
/**
* If display.calendarWeeks is enabled, a column displaying the week of year
* is shown. This class is applied to each cell in that column.
*/
this.calendarWeeks = 'cw';
/**
* Applied to the first row of the calendar view, e.g. Sunday, Monday
*/
this.dayOfTheWeek = 'dow';
/**
* Applied to the current date on the calendar view.
*/
this.today = 'today';
/**
* Applied to the locale's weekend dates on the calendar view, e.g. Sunday, Saturday
*/
this.weekend = 'weekend';
//#endregion
//#region time element
/**
* The outer element for all time related elements.
*/
this.timeContainer = 'time-container';
/**
* Applied the separator columns between time elements, e.g. hour *:* minute *:* second
*/
this.separator = 'separator';
/**
* The outer element for the clock view.
*/
this.clockContainer = `${this.timeContainer}-clock`;
/**
* The outer element for the hours selection view.
*/
this.hourContainer = `${this.timeContainer}-hour`;
/**
* The outer element for the minutes selection view.
*/
this.minuteContainer = `${this.timeContainer}-minute`;
/**
* The outer element for the seconds selection view.
*/
this.secondContainer = `${this.timeContainer}-second`;
/**
* Applied to each element in the hours selection view.
*/
this.hour = 'hour';
/**
* Applied to each element in the minutes selection view.
*/
this.minute = 'minute';
/**
* Applied to each element in the seconds selection view.
*/
this.second = 'second';
/**
* Applied AM/PM toggle button.
*/
this.toggleMeridiem = 'toggleMeridiem';
//#endregion
//#region collapse
/**
* Applied the element of the current view mode, e.g. calendar or clock.
*/
this.show = 'show';
/**
* Applied to the currently showing view mode during a transition
* between calendar and clock views
*/
this.collapsing = 'td-collapsing';
/**
* Applied to the currently hidden view mode.
*/
this.collapse = 'td-collapse';
//#endregion
/**
* Applied to the widget when the option display.inline is enabled.
*/
this.inline = 'inline';
/**
* Applied to the widget when the option display.theme is light.
*/
this.lightTheme = 'light';
/**
* Applied to the widget when the option display.theme is dark.
*/
this.darkTheme = 'dark';
/**
* Used for detecting if the system color preference is dark mode
*/
this.isDarkPreferredQuery = '(prefers-color-scheme: dark)';
}
}
class Namespace {
}
Namespace.NAME = NAME;
// noinspection JSUnusedGlobalSymbols
Namespace.dataKey = dataKey;
Namespace.events = new Events();
Namespace.css = new Css();
Namespace.errorMessages = new ErrorMessages();
class ServiceLocator {
constructor() {
this.cache = new Map();
}
locate(identifier) {
const service = this.cache.get(identifier);
if (service)
return service;
const value = new identifier();
this.cache.set(identifier, value);
return value;
}
}
const setupServiceLocator = () => {
serviceLocator = new ServiceLocator();
};
let serviceLocator;
const CalendarModes = [
{
name: 'calendar',
className: Namespace.css.daysContainer,
unit: exports.Unit.month,
step: 1,
},
{
name: 'months',
className: Namespace.css.monthsContainer,
unit: exports.Unit.year,
step: 1,
},
{
name: 'years',
className: Namespace.css.yearsContainer,
unit: exports.Unit.year,
step: 10,
},
{
name: 'decades',
className: Namespace.css.decadesContainer,
unit: exports.Unit.year,
step: 100,
},
];
class OptionsStore {
constructor() {
this._currentCalendarViewMode = 0;
this._viewDate = new DateTime();
this.minimumCalendarViewMode = 0;
this.currentView = 'calendar';
}
get currentCalendarViewMode() {
return this._currentCalendarViewMode;
}
set currentCalendarViewMode(value) {
this._currentCalendarViewMode = value;
this.currentView = CalendarModes[value].name;
}
get viewDate() {
return this._viewDate;
}
set viewDate(v) {
this._viewDate = v;
if (this.options)
this.options.viewDate = v;
}
/**
* When switching back to the calendar from the clock,
* this sets currentView to the correct calendar view.
*/
refreshCurrentView() {
this.currentView = CalendarModes[this.currentCalendarViewMode].name;
}
get isTwelveHour() {
return ['h12', 'h11'].includes(this.options.localization.hourCycle);
}
}
/**
* Main class for date validation rules based on the options provided.
*/
class Validation {
constructor() {
this.optionsStore = serviceLocator.locate(OptionsStore);
}
/**
* Checks to see if the target date is valid based on the rules provided in the options.
* Granularity can be provided to check portions of the date instead of the whole.
* @param targetDate
* @param granularity
*/
isValid(targetDate, granularity) {
if (granularity !== exports.Unit.month &&
this.optionsStore.options.restrictions.disabledDates.length > 0 &&
this._isInDisabledDates(targetDate)) {
return false;
}
if (granularity !== exports.Unit.month &&
this.optionsStore.options.restrictions.enabledDates.length > 0 &&
!this._isInEnabledDates(targetDate)) {
return false;
}
if (granularity !== exports.Unit.month &&
granularity !== exports.Unit.year &&
this.optionsStore.options.restrictions.daysOfWeekDisabled?.length > 0 &&
this.optionsStore.options.restrictions.daysOfWeekDisabled.indexOf(targetDate.weekDay) !== -1) {
return false;
}
if (this.optionsStore.options.restrictions.minDate &&
targetDate.isBefore(this.optionsStore.options.restrictions.minDate, granularity)) {
return false;
}
if (this.optionsStore.options.restrictions.maxDate &&
targetDate.isAfter(this.optionsStore.options.restrictions.maxDate, granularity)) {
return false;
}
if (granularity === exports.Unit.hours ||
granularity === exports.Unit.minutes ||
granularity === exports.Unit.seconds) {
if (this.optionsStore.options.restrictions.disabledHours.length > 0 &&
this._isInDisabledHours(targetDate)) {
return false;
}
if (this.optionsStore.options.restrictions.enabledHours.length > 0 &&
!this._isInEnabledHours(targetDate)) {
return false;
}
if (this.optionsStore.options.restrictions.disabledTimeIntervals.length > 0) {
for (const disabledTimeIntervals of this.optionsStore.options
.restrictions.disabledTimeIntervals) {
if (targetDate.isBetween(disabledTimeIntervals.from, disabledTimeIntervals.to))
return false;
}
}
}
return true;
}
/**
* Checks to see if the disabledDates option is in use and returns true (meaning invalid)
* if the `testDate` is with in the array. Granularity is by date.
* @param testDate
* @private
*/
_isInDisabledDates(testDate) {
if (!this.optionsStore.options.restrictions.disabledDates ||
this.optionsStore.options.restrictions.disabledDates.length === 0)
return false;
return this.optionsStore.options.restrictions.disabledDates.find((x) => x.isSame(testDate, exports.Unit.date));
}
/**
* Checks to see if the enabledDates option is in use and returns true (meaning valid)
* if the `testDate` is with in the array. Granularity is by date.
* @param testDate
* @private
*/
_isInEnabledDates(testDate) {
if (!this.optionsStore.options.restrictions.enabledDates ||
this.optionsStore.options.restrictions.enabledDates.length === 0)
return true;
return this.optionsStore.options.restrictions.enabledDates.find((x) => x.isSame(testDate, exports.Unit.date));
}
/**
* Checks to see if the disabledHours option is in use and returns true (meaning invalid)
* if the `testDate` is with in the array. Granularity is by hours.
* @param testDate
* @private
*/
_isInDisabledHours(testDate) {
if (!this.optionsStore.options.restrictions.disabledHours ||
this.optionsStore.options.restrictions.disabledHours.length === 0)
return false;
const formattedDate = testDate.hours;
return this.optionsStore.options.restrictions.disabledHours.find((x) => x === formattedDate);
}
/**
* Checks to see if the enabledHours option is in use and returns true (meaning valid)
* if the `testDate` is with in the array. Granularity is by hours.
* @param testDate
* @private
*/
_isInEnabledHours(testDate) {
if (!this.optionsStore.options.restrictions.enabledHours ||
this.optionsStore.options.restrictions.enabledHours.length === 0)
return true;
const formattedDate = testDate.hours;
return this.optionsStore.options.restrictions.enabledHours.find((x) => x === formattedDate);
}
}
class EventEmitter {
constructor() {
this.subscribers = [];
}
subscribe(callback) {
this.subscribers.push(callback);
return this.unsubscribe.bind(this, this.subscribers.length - 1);
}
unsubscribe(index) {
this.subscribers.splice(index, 1);
}
emit(value) {
this.subscribers.forEach((callback) => {
callback(value);
});
}
destroy() {
this.subscribers = null;
this.subscribers = [];
}
}
class EventEmitters {
constructor() {
this.triggerEvent = new EventEmitter();
this.viewUpdate = new EventEmitter();
this.updateDisplay = new EventEmitter();
this.action = new EventEmitter(); //eslint-disable-line @typescript-eslint/no-explicit-any
this.updateViewDate = new EventEmitter();
}
destroy() {
this.triggerEvent.destroy();
this.viewUpdate.destroy();
this.updateDisplay.destroy();
this.action.destroy();
this.updateViewDate.destroy();
}
}
const DefaultOptions = {
restrictions: {
minDate: undefined,
maxDate: undefined,
disabledDates: [],
enabledDates: [],
daysOfWeekDisabled: [],
disabledTimeIntervals: [],
disabledHours: [],
enabledHours: [],
},
display: {
icons: {
type: 'icons',
time: 'fa-solid fa-clock',
date: 'fa-solid fa-calendar',
up: 'fa-solid fa-arrow-up',
down: 'fa-solid fa-arrow-down',
previous: 'fa-solid fa-chevron-left',
next: 'fa-solid fa-chevron-right',
today: 'fa-solid fa-calendar-check',
clear: 'fa-solid fa-trash',
close: 'fa-solid fa-xmark',
},
sideBySide: false,
calendarWeeks: false,
viewMode: 'calendar',
toolbarPlacement: 'bottom',
keepOpen: false,
buttons: {
today: false,
clear: false,
close: false,
},
components: {
calendar: true,
date: true,
month: true,
year: true,
decades: true,
clock: true,
hours: true,
minutes: true,
seconds: false,
useTwentyfourHour: undefined,
},
inline: false,
theme: 'auto',
},
stepping: 1,
useCurrent: true,
defaultDate: undefined,
localization: {
today: 'Go to today',
clear: 'Clear selection',
close: 'Close the picker',
selectMonth: 'Select Month',
previousMonth: 'Previous Month',
nextMonth: 'Next Month',
selectYear: 'Select Year',
previousYear: 'Previous Year',
nextYear: 'Next Year',
selectDecade: 'Select Decade',
previousDecade: 'Previous Decade',
nextDecade: 'Next Decade',
previousCentury: 'Previous Century',
nextCentury: 'Next Century',
pickHour: 'Pick Hour',
incrementHour: 'Increment Hour',
decrementHour: 'Decrement Hour',
pickMinute: 'Pick Minute',
incrementMinute: 'Increment Minute',
decrementMinute: 'Decrement Minute',
pickSecond: 'Pick Second',
incrementSecond: 'Increment Second',
decrementSecond: 'Decrement Second',
toggleMeridiem: 'Toggle Meridiem',
selectTime: 'Select Time',
selectDate: 'Select Date',
dayViewHeaderFormat: { month: 'long', year: '2-digit' },
locale: 'default',
hourCycle: undefined,
startOfTheWeek: 0,
/**
* This is only used with the customDateFormat plugin
*/
dateFormats: {
LTS: 'h:mm:ss T',
LT: 'h:mm T',
L: 'MM/dd/yyyy',
LL: 'MMMM d, yyyy',
LLL: 'MMMM d, yyyy h:mm T',
LLLL: 'dddd, MMMM d, yyyy h:mm T',
},
/**
* This is only used with the customDateFormat plugin
*/
ordinal: (n) => n,
/**
* This is only used with the customDateFormat plugin
*/
format: 'L LT',
},
keepInvalid: false,
debug: false,
allowInputToggle: false,
viewDate: new DateTime(),
multipleDates: false,
multipleDatesSeparator: '; ',
promptTimeOnDateChange: false,
promptTimeOnDateChangeTransitionDelay: 200,
meta: {},
container: undefined,
};
/**
* Attempts to prove `d` is a DateTime or Date or can be converted into one.
* @param d If a string will attempt creating a date from it.
* @param localization object containing locale and format settings. Only used with the custom formats
* @private
*/
function tryConvertToDateTime(d, localization) {
if (d.constructor.name === DateTime.name)
return d;
if (d.constructor.name === Date.name) {
return DateTime.convert(d);
}
if (typeof d === typeof '') {
const dateTime = DateTime.fromString(d, localization);
if (JSON.stringify(dateTime) === 'null') {
return null;
}
return dateTime;
}
return null;
}
/**
* Attempts to convert `d` to a DateTime object
* @param d value to convert
* @param optionName Provides text to error messages e.g. disabledDates
* @param localization object containing locale and format settings. Only used with the custom formats
*/
function convertToDateTime(d, optionName, localization) {
if (typeof d === typeof '' && optionName !== 'input') {
Namespace.errorMessages.dateString();
}
const converted = tryConvertToDateTime(d, localization);
if (!converted) {
Namespace.errorMessages.failedToParseDate(optionName, d, optionName === 'input');
}
return converted;
}
/**
* Type checks that `value` is an array of Date or DateTime
* @param optionName Provides text to error messages e.g. disabledDates
* @param value Option value
* @param providedType Used to provide text to error messages
* @param localization
*/
function typeCheckDateArray(optionName, value, //eslint-disable-line @typescript-eslint/no-explicit-any
providedType, localization) {
if (!Array.isArray(value)) {
Namespace.errorMessages.typeMismatch(optionName, providedType, 'array of DateTime or Date');
}
for (let i = 0; i < value.length; i++) {
const d = value[i];
const dateTime = convertToDateTime(d, optionName, localization);
if (!dateTime) {
Namespace.errorMessages.typeMismatch(optionName, typeof d, 'DateTime or Date');
}
dateTime.setLocale(localization?.locale ?? 'default');
value[i] = dateTime;
}
}
/**
* Type checks that `value` is an array of numbers
* @param optionName Provides text to error messages e.g. disabledDates
* @param value Option value
* @param providedType Used to provide text to error messages
*/
function typeCheckNumberArray(optionName, value, //eslint-disable-line @typescript-eslint/no-explicit-any
providedType) {
if (!Array.isArray(value) || value.some((x) => typeof x !== typeof 0)) {
Namespace.errorMessages.typeMismatch(optionName, providedType, 'array of numbers');
}
}
function mandatoryDate(key) {
return ({ value, providedType, localization }) => {
const dateTime = convertToDateTime(value, key, localization);
if (dateTime !== undefined) {
dateTime.setLocale(localization.locale);
return dateTime;
}
Namespace.errorMessages.typeMismatch(key, providedType, 'DateTime or Date');
};
}
function optionalDate(key) {
const mandatory = mandatoryDate(key);
return (args) => {
if (args.value === undefined) {
return args.value;
}
return mandatory(args);
};
}
function numbersInRange(key, lower, upper) {
return ({ value, providedType }) => {
if (value === undefined) {
return [];
}
typeCheckNumberArray(key, value, providedType);
if (value.some((x) => x < lower || x > upper))
Namespace.errorMessages.numbersOutOfRange(key, lower, upper);
return value;
};
}
function validHourRange(key) {
return numbersInRange(key, 0, 23);
}
function validDateArray(key) {
return ({ value, providedType, localization }) => {
if (value === undefined) {
return [];
}
typeCheckDateArray(key, value, providedType, localization);
return value;
};
}
function validKeyOption(keyOptions) {
return ({ value, path }) => {
if (!keyOptions.includes(value))
Namespace.errorMessages.unexpectedOptionValue(path.substring(1), value, keyOptions);
return value;
};
}
const optionProcessors = Object.freeze({
defaultDate: mandatoryDate('defaultDate'),
viewDate: mandatoryDate('viewDate'),
minDate: optionalDate('restrictions.minDate'),
maxDate: optionalDate('restrictions.maxDate'),
disabledHours: validHourRange('restrictions.disabledHours'),
enabledHours: validHourRange('restrictions.enabledHours'),
disabledDates: validDateArray('restrictions.disabledDates'),
enabledDates: validDateArray('restrictions.enabledDates'),
daysOfWeekDisabled: numbersInRange('restrictions.daysOfWeekDisabled', 0, 6),
disabledTimeIntervals: ({ key, value, providedType, localization }) => {
if (value === undefined) {
return [];
}
if (!Array.isArray(value)) {
Namespace.errorMessages.typeMismatch(key, providedType, 'array of { from: DateTime|Date, to: DateTime|Date }');
}
const valueObject = value; //eslint-disable-line @typescript-eslint/no-explicit-any
for (let i = 0; i < valueObject.length; i++) {
Object.keys(valueObject[i]).forEach((vk) => {
const subOptionName = `${key}[${i}].${vk}`;
const d = valueObject[i][vk];
const dateTime = convertToDateTime(d, subOptionName, localization);
if (!dateTime) {
Namespace.errorMessages.typeMismatch(subOptionName, typeof d, 'DateTime or Date');
}
dateTime.setLocale(localization.locale);
valueObject[i][vk] = dateTime;
});
}
return valueObject;
},
toolbarPlacement: validKeyOption(['top', 'bottom', 'default']),
type: validKeyOption(['icons', 'sprites']),
viewMode: validKeyOption([
'clock',
'calendar',
'months',
'years',
'decades',
]),
theme: validKeyOption(['light', 'dark', 'auto']),
meta: ({ value }) => value,
dayViewHeaderFormat: ({ value }) => value,
container: ({ value, path }) => {
if (value &&
!(value instanceof HTMLElement ||
value instanceof Element ||
value?.appendChild)) {
Namespace.errorMessages.typeMismatch(path.substring(1), typeof value, 'HTMLElement');
}
return value;
},
useTwentyfourHour: ({ value, path, providedType, defaultType }) => {
Namespace.errorMessages.deprecatedWarning('useTwentyfourHour', 'Please use "options.localization.hourCycle" instead');
if (value === undefined || providedType === 'boolean')
return value;
Namespace.errorMessages.typeMismatch(path, providedType, defaultType);
},
hourCycle: validKeyOption(['h11', 'h12', 'h23', 'h24']),
});
const defaultProcessor = ({ value, defaultType, providedType, path, }) => {
switch (defaultType) {
case 'boolean':
return value === 'true' || value === true;
case 'number':
return +value;
case 'string':
return value.toString();
case 'object':
return {};
case 'function':
return value;
default:
Namespace.errorMessages.typeMismatch(path, providedType, defaultType);
}
};
function processKey(args) {
return (optionProcessors[args.key] || defaultProcessor)(args);
}
class OptionConverter {
static deepCopy(input) {
const o = {};
Object.keys(input).forEach((key) => {
const inputElement = input[key];
if (inputElement instanceof DateTime) {
o[key] = inputElement.clone;
return;
}
else if (inputElement instanceof Date) {
o[key] = new Date(inputElement.valueOf());
return;
}
o[key] = inputElement;
if (typeof inputElement !== 'object' ||
inputElement instanceof HTMLElement ||
inputElement instanceof Element)
return;
if (!Array.isArray(inputElement)) {
o[key] = OptionConverter.deepCopy(inputElement);
}
});
return o;
}
/**
* Finds value out of an object based on a string, period delimited, path
* @param paths
* @param obj
*/
static objectPath(paths, obj) {
if (paths.charAt(0) === '.')
paths = paths.slice(1);
if (!paths)
return obj;
return paths
.split('.')
.reduce((value, key) => OptionConverter.isValue(value) || OptionConverter.isValue(value[key])
? value[key]
: undefined, obj);
}
/**
* The spread operator caused sub keys to be missing after merging.
* This is to fix that issue by using spread on the child objects first.
* Also handles complex options like disabledDates
* @param provided An option from new providedOptions
* @param copyTo Destination object. This was added to prevent reference copies
* @param path
* @param localization
*/
static spread(provided, copyTo, path = '', localization) {
const defaultOptions = OptionConverter.objectPath(path, DefaultOptions);
const unsupportedOptions = Object.keys(provided).filter((x) => !Object.keys(defaultOptions).includes(x));
if (unsupportedOptions.length > 0) {
const flattenedOptions = OptionConverter.getFlattenDefaultOptions();
const errors = unsupportedOptions.map((x) => {
let error = `"${path}.${x}" in not a known option.`;
const didYouMean = flattenedOptions.find((y) => y.includes(x));
if (didYouMean)
error += ` Did you mean "${didYouMean}"?`;
return error;
});
Namespace.errorMessages.unexpectedOptions(errors);
}
Object.keys(provided)
.filter((key) => key !== '__proto__' && key !== 'constructor')
.forEach((key) => {
path += `.${key}`;
if (path.charAt(0) === '.')
path = path.slice(1);
const defaultOptionValue = defaultOptions[key];
const providedType = typeof provided[key];
const defaultType = typeof defaultOptionValue;
const value = provided[key];
if (value === undefined || value === null) {
copyTo[key] = value;
path = path.substring(0, path.lastIndexOf(`.${key}`));
return;
}
if (typeof defaultOptionValue === 'object' &&
!Array.isArray(provided[key]) &&
!(defaultOptionValue instanceof Date ||
OptionConverter.ignoreProperties.includes(key))) {
OptionConverter.spread(provided[key], copyTo[key], path, localization);
}
else {
copyTo[key] = OptionConverter.processKey(key, value, providedType, defaultType, path, localization);
}
path = path.substring(0, path.lastIndexOf(`.${key}`));
});
}
static processKey(key, value, //eslint-disable-line @typescript-eslint/no-explicit-any
providedType, defaultType, path, localization) {
return processKey({
key,
value,
providedType,
defaultType,
path,
localization,
});
}
static _mergeOptions(providedOptions, mergeTo) {
const newConfig = OptionConverter.deepCopy(mergeTo);
//see if the options specify a locale
const localization = mergeTo.localization?.locale !== 'default'
? mergeTo.localization
: providedOptions?.localization || DefaultOptions.localization;
OptionConverter.spread(providedOptions, newConfig, '', localization);
return newConfig;
}
static _dataToOptions(element, options) {
const eData = JSON.parse(JSON.stringify(element.dataset));
if (eData?.tdTargetInput)
delete eData.tdTargetInput;
if (eData?.tdTargetToggle)
delete eData.tdTargetToggle;
if (!eData ||
Object.keys(eData).length === 0 ||
eData.constructor !== DOMStringMap)
return options;
const dataOptions = {};
// because dataset returns camelCase including the 'td' key the option
// key won't align
const objectToNormalized = (object) => {
const lowered = {};
Object.keys(object).forEach((x) => {
lowered[x.toLowerCase()] = x;
});
return lowered;
};
const rabbitHole = (split, index, optionSubgroup, value) => {
// first round = display { ... }
const normalizedOptions = objectToNormalized(optionSubgroup);
const keyOption = normalizedOptions[split[index].toLowerCase()];
const internalObject = {};
if (keyOption === undefined)
return internalObject;
// if this is another object, continue down the rabbit hole
if (optionSubgroup[keyOption].constructor === Object) {
index++;
internalObject[keyOption] = rabbitHole(split, index, optionSubgroup[keyOption], value);
}
else {
internalObject[keyOption] = value;
}
return internalObject;
};
const optionsLower = objectToNormalized(options);
Object.keys(eData)
.filter((x) => x.startsWith(Namespace.dataKey))
.map((x) => x.substring(2))
.forEach((key) => {
let keyOption = optionsLower[key.toLowerCase()];
// dataset merges dashes to camelCase... yay
// i.e. key = display_components_seconds
if (key.includes('_')) {
// [display, components, seconds]
const split = key.split('_');
// display
keyOption = optionsLower[split[0].toLowerCase()];
if (keyOption !== undefined &&
options[keyOption].constructor === Object) {
dataOptions[keyOption] = rabbitHole(split, 1, options[keyOption], eData[`td${key}`]);
}
}
// or key = multipleDate
else if (keyOption !== undefined) {
dataOptions[keyOption] = eData[`td${key}`];
}
});
return this._mergeOptions(dataOptions, options);
}
/**
* Attempts to prove `d` is a DateTime or Date or can be converted into one.
* @param d If a string will attempt creating a date from it.
* @param localization object containing locale and format settings. Only used with the custom formats
* @private
*/
static _dateTypeCheck(d, //eslint-disable-line @typescript-eslint/no-explicit-any
localization) {
return tryConvertToDateTime(d, localization);
}
/**
* Type checks that `value` is an array of Date or DateTime
* @param optionName Provides text to error messages e.g. disabledDates
* @param value Option value
* @param providedType Used to provide text to error messages
* @param localization
*/
static _typeCheckDateArray(optionName, value, providedType, localization) {
return typeCheckDateArray(optionName, value, providedType, localization);
}
/**
* Type checks that `value` is an array of numbers
* @param optionName Provides text to error messages e.g. disabledDates
* @param value Option value
* @param providedType Used to provide text to error messages
*/
static _typeCheckNumberArray(optionName, value, providedType) {
return typeCheckNumberArray(optionName, value, providedType);
}
/**
* Attempts to convert `d` to a DateTime object
* @param d value to convert
* @param optionName Provides text to error messages e.g. disabledDates
* @param localization object containing locale and format settings. Only used with the custom formats
*/
static dateConversion(d, //eslint-disable-line @typescript-eslint/no-explicit-any
optionName, localization) {
return convertToDateTime(d, optionName, localization);
}
static getFlattenDefaultOptions() {
if (this._flattenDefaults)
return this._flattenDefaults;
const deepKeys = (t, pre = []) => {
if (Array.isArray(t))
return [];
if (Object(t) === t) {
return Object.entries(t).flatMap(([k, v]) => deepKeys(v, [...pre, k]));
}
else {
return pre.join('.');
}
};
this._flattenDefaults = deepKeys(DefaultOptions);
return this._flattenDefaults;
}
/**
* Some options conflict like min/max date. Verify that these kinds of options
* are set correctly.
* @param config
*/
static _validateConflicts(config) {
if (config.display.sideBySide &&
(!config.display.components.clock ||
!(config.display.components.hours ||
config.display.components.minutes ||
config.display.components.seconds))) {
Namespace.errorMessages.conflictingConfiguration('Cannot use side by side mode without the clock components');
}
if (config.restrictions.minDate && config.restrictions.maxDate) {
if (config.restrictions.minDate.isAfter(config.restrictions.maxDate)) {
Namespace.errorMessages.conflictingConfiguration('minDate is after maxDate');
}
if (config.restrictions.maxDate.isBefore(config.restrictions.minDate)) {
Namespace.errorMessages.conflictingConfiguration('maxDate is before minDate');
}
}
}
}
OptionConverter.ignoreProperties = [
'meta',
'dayViewHeaderFormat',
'container',
'dateForms',
'ordinal',
];
OptionConverter.isValue = (a) => a != null; // everything except undefined + null
class Dates {
constructor() {
this._dates = [];
this.optionsStore = serviceLocator.locate(OptionsStore);
this.validation = serviceLocator.locate(Validation);
this._eventEmitters = serviceLocator.locate(EventEmitters);
}
/**
* Returns the array of selected dates
*/
get picked() {
return this._dates;
}
/**
* Returns the last picked value.
*/
get lastPicked() {
return this._dates[this.lastPickedIndex];
}
/**
* Returns the length of picked dates -1 or 0 if none are selected.
*/
get lastPickedIndex() {
if (this._dates.length === 0)
return 0;
return this._dates.length - 1;
}
/**
* Formats a DateTime object to a string. Used when setting the input value.
* @param date
*/
formatInput(date) {
const components = this.optionsStore.options.display.components;
if (!date)
return '';
return date.format({
year: components.calendar && components.year ? 'numeric' : undefined,
month: components.calendar && components.month ? '2-digit' : undefined,
day: components.calendar && components.date ? '2-digit' : undefined,
hour: components.clock && components.hours ? '2-digit' : undefined,
minute: components.clock && components.minutes ? '2-digit' : undefined,
second: components.clock && components.seconds ? '2-digit' : undefined,
hourCycle: this.optionsStore.options.localization.hourCycle,
});
}
/**
* parse the value into a DateTime object.
* this can be overwritten to supply your own parsing.
*/
//eslint-disable-next-line @typescript-eslint/no-explicit-any
parseInput(value) {
return OptionConverter.dateConversion(value, 'input', this.optionsStore.options.localization);
}
/**
* Tries to convert the provided value to a DateTime object.
* If value is null|undefined then clear the value of the provided index (or 0).
* @param value Value to convert or null|undefined
* @param index When using multidates this is the index in the array
*/
//eslint-disable-next-line @typescript-eslint/no-explicit-any
setFromInput(value, index) {
if (!value) {
this.setValue(undefined, index);
return;
}
const converted = this.parseInput(value);
if (converted) {
converted.setLocale(this.optionsStore.options.localization.locale);
this.setValue(converted, index);
}
}
/**
* Adds a new DateTime to selected dates array
* @param date
*/
add(date) {
this._dates.push(date);
}
/**
* Returns true if the `targetDate` is part of the selected dates array.
* If `unit` is provided then a granularity to that unit will be used.
* @param targetDate
* @param unit
*/
isPicked(targetDate, unit) {
if (!unit)
return this._dates.find((x) => x === targetDate) !== undefined;
const format = getFormatByUnit(unit);
const innerDateFormatted = targetDate.format(format);
return (this._dates
.map((x) => x.format(format))
.find((x) => x === innerDateFormatted) !== undefined);
}
/**
* Returns the index at which `targetDate` is in the array.
* This is used for updating or removing a date when multi-date is used
* If `unit` is provided then a granularity to that unit will be used.
* @param targetDate
* @param unit
*/
pickedIndex(targetDate, unit) {
if (!unit)
return this._dates.indexOf(targetDate);
const format = getFormatByUnit(unit);
const innerDateFormatted = targetDate.format(format);
return this._dates.map((x) => x.format(format)).indexOf(innerDateFormatted);
}
/**
* Clears all selected dates.
*/
clear() {
this.optionsStore.unset = true;
this._eventEmitters.triggerEvent.emit({
type: Namespace.events.change,
date: undefined,
oldDate: this.lastPicked,
isClear: true,
isValid: true,
});
this._dates = [];
}
/**
* Find the "book end" years given a `year` and a `factor`
* @param factor e.g. 100 for decades
* @param year e.g. 2021
*/
static getStartEndYear(factor, year) {
const step = factor / 10, startYear = Math.floor(year / factor) * factor, endYear = startYear + step * 9, focusValue = Math.floor(year / step) * step;
return [startYear, endYear, focusValue];
}
updateInput(target) {
if (!this.optionsStore.input)
return;
let newValue = this.formatInput(target);
if (this.optionsStore.options.multipleDates) {
newValue = this._dates
.map((d) => this.formatInput(d))
.join(this.optionsStore.options.multipleDatesSeparator);
}
if (this.optionsStore.input.value != newValue)
this.optionsStore.input.value = newValue;
}
/**
* Attempts to either clear or set the `target` date at `index`.
* If the `target` is null then the date will be cleared.
* If multi-date is being used then it will be removed from the array.
* If `target` is valid and multi-date is used then if `index` is
* provided the date at that index will be replaced, otherwise it is appended.
* @param target
* @param index
*/
setValue(target, index) {
const noIndex = typeof index === 'undefined', isClear = !target && noIndex;
let oldDate = this.optionsStore.unset ? null : this._dates[index];
if (!oldDate && !this.optionsStore.unset && noIndex && isClear) {
oldDate = this.lastPicked;
}
if (target && oldDate?.isSame(target)) {
this.updateInput(target);
return;
}
// case of calling setValue(null)
if (!target) {
if (!this.optionsStore.options.multipleDates ||
this._dates.length === 1 ||
isClear) {
this.optionsStore.unset = true;
this._dates = [];
}
else {
this._dates.splice(index, 1);
}
this.updateInput();
this._eventEmitters.triggerEvent.emit({
type: Namespace.events.change,
date: undefined,
oldDate,
isClear,
isValid: true,
});
this._eventEmitters.updateDisplay.emit('all');
return;
}
index = index || 0;
target = target.clone;
// minute stepping is being used, force the minute to the closest value
if (this.optionsStore.options.stepping !== 1) {
target.minutes =
Math.round(target.minutes / this.optionsStore.options.stepping) *
this.optionsStore.options.stepping;
target.seconds = 0;
}
if (this.validation.isValid(target)) {
this._dates[index] = target;
this._eventEmitters.updateViewDate.emit(target.clone);
this.updateInput(target);
this.optionsStore.unset = false;
this._eventEmitters.updateDisplay.emit('all');
this._eventEmitters.triggerEvent.emit({
type: Namespace.events.change,
date: target,
oldDate,
isClear,
isValid: true,
});
return;
}
if (this.optionsStore.options.keepInvalid) {
this._dates[index] = target;
this._eventEmitters.updateViewDate.emit(target.clone);
this.updateInput(target);
this._eventEmitters.triggerEvent.emit({
type: Namespace.events.change,
date: target,
oldDate,
isClear,
isValid: false,
});
}
this._eventEmitters.triggerEvent.emit({
type: Namespace.events.error,
reason: Namespace.errorMessages.failedToSetInvalidDate,
date: target,
oldDate,
});
}
}
var ActionTypes;
(function (ActionTypes) {
ActionTypes["next"] = "next";
ActionTypes["previous"] = "previous";
ActionTypes["changeCalendarView"] = "changeCalendarView";
ActionTypes["selectMonth"] = "selectMonth";
ActionTypes["selectYear"] = "selectYear";
ActionTypes["selectDecade"] = "selectDecade";
ActionTypes["selectDay"] = "selectDay";
ActionTypes["selectHour"] = "selectHour";
ActionTypes["selectMinute"] = "selectMinute";
ActionTypes["selectSecond"] = "selectSecond";
ActionTypes["incrementHours"] = "incrementHours";
ActionTypes["incrementMinutes"] = "incrementMinutes";
ActionTypes["incrementSeconds"] = "incrementSeconds";
ActionTypes["decrementHours"] = "decrementHours";
ActionTypes["decrementMinutes"] = "decrementMinutes";
ActionTypes["decrementSeconds"] = "decrementSeconds";
ActionTypes["toggleMeridiem"] = "toggleMeridiem";
ActionTypes["togglePicker"] = "togglePicker";
ActionTypes["showClock"] = "showClock";
ActionTypes["showHours"] = "showHours";
ActionTypes["showMinutes"] = "showMinutes";
ActionTypes["showSeconds"] = "showSeconds";
ActionTypes["clear"] = "clear";
ActionTypes["close"] = "close";
ActionTypes["today"] = "today";
})(ActionTypes || (ActionTypes = {}));
var ActionTypes$1 = ActionTypes;
/**
* Creates and updates the grid for `date`
*/
class DateDisplay {
constructor() {
this.optionsStore = serviceLocator.locate(OptionsStore);
this.dates = serviceLocator.locate(Dates);
this.validation = serviceLocator.locate(Validation);
}
/**
* Build the container html for the display
* @private
*/
getPicker() {
const container = document.createElement('div');
container.classList.add(Namespace.css.daysContainer);
container.append(...this._daysOfTheWeek());
if (this.optionsStore.options.display.calendarWeeks) {
const div = document.createElement('div');
div.classList.add(Namespace.css.calendarWeeks, Namespace.css.noHighlight);
container.appendChild(div);
}
for (let i = 0; i < 42; i++) {
if (i !== 0 && i % 7 === 0) {
if (this.optionsStore.options.display.calendarWeeks) {
const div = document.createElement('div');
div.classList.add(Namespace.css.calendarWeeks, Namespace.css.noHighlight);
container.appendChild(div);
}
}
const div = document.createElement('div');
div.setAttribute('data-action', ActionTypes$1.selectDay);
container.appendChild(div);
}
return container;
}
/**
* Populates the grid and updates enabled states
* @private
*/
_update(widget, paint) {
const container = widget.getElementsByClassName(Namespace.css.daysContainer)[0];
if (this.optionsStore.currentView === 'calendar') {
const [previous, switcher, next] = container.parentElement
.getElementsByClassName(Namespace.css.calendarHeader)[0]
.getElementsByTagName('div');
switcher.setAttribute(Namespace.css.daysContainer, this.optionsStore.viewDate.format(this.optionsStore.options.localization.dayViewHeaderFormat));
this.optionsStore.options.display.components.month
? switcher.classList.remove(Namespace.css.disabled)
: switcher.classList.add(Namespace.css.disabled);
this.validation.isValid(this.optionsStore.viewDate.clone.manipulate(-1, exports.Unit.month), exports.Unit.month)
? previous.classList.remove(Namespace.css.disabled)
: previous.classList.add(Namespace.css.disabled);
this.validation.isValid(this.optionsStore.viewDate.clone.manipulate(1, exports.Unit.month), exports.Unit.month)
? next.classList.remove(Namespace.css.disabled)
: next.classList.add(Namespace.css.disabled);
}
const innerDate = this.optionsStore.viewDate.clone
.startOf(exports.Unit.month)
.startOf('weekDay', this.optionsStore.options.localization.startOfTheWeek)
.manipulate(12, exports.Unit.hours);
container
.querySelectorAll(`[data-action="${ActionTypes$1.selectDay}"], .${Namespace.css.calendarWeeks}`)
.forEach((containerClone) => {
if (this.optionsStore.options.display.calendarWeeks &&
containerClone.classList.contains(Namespace.css.calendarWeeks)) {
if (containerClone.innerText === '#')
return;
containerClone.innerText = `${innerDate.week}`;
return;
}
const classes = [];
classes.push(Namespace.css.day);
if (innerDate.isBefore(this.optionsStore.viewDate, exports.Unit.month)) {
classes.push(Namespace.css.old);
}
if (innerDate.isAfter(this.optionsStore.viewDate, exports.Unit.month)) {
classes.push(Namespace.css.new);
}
if (!this.optionsStore.unset &&
this.dates.isPicked(innerDate, exports.Unit.date)) {
classes.push(Namespace.css.active);
}
if (!this.validation.isValid(innerDate, exports.Unit.date)) {
classes.push(Namespace.css.disabled);
}
if (innerDate.isSame(new DateTime(), exports.Unit.date)) {
classes.push(Namespace.css.today);
}
if (innerDate.weekDay === 0 || innerDate.weekDay === 6) {
classes.push(Namespace.css.weekend);
}
paint(exports.Unit.date, innerDate, classes, containerClone);
containerClone.classList.remove(...containerClone.classList);
containerClone.classList.add(...classes);
containerClone.setAttribute('data-value', `${innerDate.year}-${innerDate.monthFormatted}-${innerDate.dateFormatted}`);
containerClone.setAttribute('data-day', `${innerDate.date}`);
containerClone.innerText = innerDate.format({ day: 'numeric' });
innerDate.manipulate(1, exports.Unit.date);
});
}
/***
* Generates an html row that contains the days of the week.
* @private
*/
_daysOfTheWeek() {
const innerDate = this.optionsStore.viewDate.clone
.startOf('weekDay', this.optionsStore.options.localization.startOfTheWeek)
.startOf(exports.Unit.date);
const row = [];
document.createElement('div');
if (this.optionsStore.options.display.calendarWeeks) {
const htmlDivElement = document.createElement('div');
htmlDivElement.classList.add(Namespace.css.calendarWeeks, Namespace.css.noHighlight);
htmlDivElement.innerText = '#';
row.push(htmlDivElement);
}
for (let i = 0; i < 7; i++) {
const htmlDivElement = document.createElement('div');
htmlDivElement.classList.add(Namespace.css.dayOfTheWeek, Namespace.css.noHighlight);
htmlDivElement.innerText = innerDate.format({ weekday: 'short' });
innerDate.manipulate(1, exports.Unit.date);
row.push(htmlDivElement);
}
return row;
}
}
/**
* Creates and updates the grid for `month`
*/
class MonthDisplay {
constructor() {
this.optionsStore = serviceLocator.locate(OptionsStore);
this.dates = serviceLocator.locate(Dates);
this.validation = serviceLocator.locate(Validation);
}
/**
* Build the container html for the display
* @private
*/
getPicker() {
const container = document.createElement('div');
container.classList.add(Namespace.css.monthsContainer);
for (let i = 0; i < 12; i++) {
const div = document.createElement('div');
div.setAttribute('data-action', ActionTypes$1.selectMonth);
container.appendChild(div);
}
return container;
}
/**
* Populates the grid and updates enabled states
* @private
*/
_update(widget, paint) {
const container = widget.getElementsByClassName(Namespace.css.monthsContainer)[0];
if (this.optionsStore.currentView === 'months') {
const [previous, switcher, next] = container.parentElement
.getElementsByClassName(Namespace.css.calendarHeader)[0]
.getElementsByTagName('div');
switcher.setAttribute(Namespace.css.monthsContainer, this.optionsStore.viewDate.format({ year: 'numeric' }));
this.optionsStore.options.display.components.year
? switcher.classList.remove(Namespace.css.disabled)
: switcher.classList.add(Namespace.css.disabled);
this.validation.isValid(this.optionsStore.viewDate.clone.manipulate(-1, exports.Unit.year), exports.Unit.year)
? previous.classList.remove(Namespace.css.disabled)
: previous.classList.add(Namespace.css.disabled);
this.validation.isValid(this.optionsStore.viewDate.clone.manipulate(1, exports.Unit.year), exports.Unit.year)
? next.classList.remove(Namespace.css.disabled)
: next.classList.add(Namespace.css.disabled);
}
const innerDate = this.optionsStore.viewDate.clone.startOf(exports.Unit.year);
container
.querySelectorAll(`[data-action="${ActionTypes$1.selectMonth}"]`)
.forEach((containerClone, index) => {
const classes = [];
classes.push(Namespace.css.month);
if (!this.optionsStore.unset &&
this.dates.isPicked(innerDate, exports.Unit.month)) {
classes.push(Namespace.css.active);
}
if (!this.validation.isValid(innerDate, exports.Unit.month)) {
classes.push(Namespace.css.disabled);
}
paint(exports.Unit.month, innerDate, classes, containerClone);
containerClone.classList.remove(...containerClone.classList);
containerClone.classList.add(...classes);
containerClone.setAttribute('data-value', `${index}`);
containerClone.innerText = `${innerDate.format({ month: 'short' })}`;
innerDate.manipulate(1, exports.Unit.month);
});
}
}
/**
* Creates and updates the grid for `year`
*/
class YearDisplay {
constructor() {
this.optionsStore = serviceLocator.locate(OptionsStore);
this.dates = serviceLocator.locate(Dates);
this.validation = serviceLocator.locate(Validation);
}
/**
* Build the container html for the display
* @private
*/
getPicker() {
const container = document.createElement('div');
container.classList.add(Namespace.css.yearsContainer);
for (let i = 0; i < 12; i++) {
const div = document.createElement('div');
div.setAttribute('data-action', ActionTypes$1.selectYear);
container.appendChild(div);
}
return container;
}
/**
* Populates the grid and updates enabled states
* @private
*/
_update(widget, paint) {
this._startYear = this.optionsStore.viewDate.clone.manipulate(-1, exports.Unit.year);
this._endYear = this.optionsStore.viewDate.clone.manipulate(10, exports.Unit.year);
const container = widget.getElementsByClassName(Namespace.css.yearsContainer)[0];
if (this.optionsStore.currentView === 'years') {
const [previous, switcher, next] = container.parentElement
.getElementsByClassName(Namespace.css.calendarHeader)[0]
.getElementsByTagName('div');
switcher.setAttribute(Namespace.css.yearsContainer, `${this._startYear.format({ year: 'numeric' })}-${this._endYear.format({
year: 'numeric',
})}`);
this.optionsStore.options.display.components.decades
? switcher.classList.remove(Namespace.css.disabled)
: switcher.classList.add(Namespace.css.disabled);
this.validation.isValid(this._startYear, exports.Unit.year)
? previous.classList.remove(Namespace.css.disabled)
: previous.classList.add(Namespace.css.disabled);
this.validation.isValid(this._endYear, exports.Unit.year)
? next.classList.remove(Namespace.css.disabled)
: next.classList.add(Namespace.css.disabled);
}
const innerDate = this.optionsStore.viewDate.clone
.startOf(exports.Unit.year)
.manipulate(-1, exports.Unit.year);
container
.querySelectorAll(`[data-action="${ActionTypes$1.selectYear}"]`)
.forEach((containerClone) => {
const classes = [];
classes.push(Namespace.css.year);
if (!this.optionsStore.unset &&
this.dates.isPicked(innerDate, exports.Unit.year)) {
classes.push(Namespace.css.active);
}
if (!this.validation.isValid(innerDate, exports.Unit.year)) {
classes.push(Namespace.css.disabled);
}
paint(exports.Unit.year, innerDate, classes, containerClone);
containerClone.classList.remove(...containerClone.classList);
containerClone.classList.add(...classes);
containerClone.setAttribute('data-value', `${innerDate.year}`);
containerClone.innerText = innerDate.format({ year: 'numeric' });
innerDate.manipulate(1, exports.Unit.year);
});
}
}
/**
* Creates and updates the grid for `seconds`
*/
class DecadeDisplay {
constructor() {
this.optionsStore = serviceLocator.locate(OptionsStore);
this.dates = serviceLocator.locate(Dates);
this.validation = serviceLocator.locate(Validation);
}
/**
* Build the container html for the display
* @private
*/
getPicker() {
const container = document.createElement('div');
container.classList.add(Namespace.css.decadesContainer);
for (let i = 0; i < 12; i++) {
const div = document.createElement('div');
div.setAttribute('data-action', ActionTypes$1.selectDecade);
container.appendChild(div);
}
return container;
}
/**
* Populates the grid and updates enabled states
* @private
*/
_update(widget, paint) {
const [start, end] = Dates.getStartEndYear(100, this.optionsStore.viewDate.year);
this._startDecade = this.optionsStore.viewDate.clone.startOf(exports.Unit.year);
this._startDecade.year = start;
this._endDecade = this.optionsStore.viewDate.clone.startOf(exports.Unit.year);
this._endDecade.year = end;
const container = widget.getElementsByClassName(Namespace.css.decadesContainer)[0];
const [previous, switcher, next] = container.parentElement
.getElementsByClassName(Namespace.css.calendarHeader)[0]
.getElementsByTagName('div');
if (this.optionsStore.currentView === 'decades') {
switcher.setAttribute(Namespace.css.decadesContainer, `${this._startDecade.format({
year: 'numeric',
})}-${this._endDecade.format({ year: 'numeric' })}`);
this.validation.isValid(this._startDecade, exports.Unit.year)
? previous.classList.remove(Namespace.css.disabled)
: previous.classList.add(Namespace.css.disabled);
this.validation.isValid(this._endDecade, exports.Unit.year)
? next.classList.remove(Namespace.css.disabled)
: next.classList.add(Namespace.css.disabled);
}
const pickedYears = this.dates.picked.map((x) => x.year);
container
.querySelectorAll(`[data-action="${ActionTypes$1.selectDecade}"]`)
.forEach((containerClone, index) => {
if (index === 0) {
containerClone.classList.add(Namespace.css.old);
if (this._startDecade.year - 10 < 0) {
containerClone.textContent = ' ';
previous.classList.add(Namespace.css.disabled);
containerClone.classList.add(Namespace.css.disabled);
containerClone.setAttribute('data-value', '');
return;
}
else {
containerClone.innerText = this._startDecade.clone
.manipulate(-10, exports.Unit.year)
.format({ year: 'numeric' });
containerClone.setAttribute('data-value', `${this._startDecade.year}`);
return;
}
}
const classes = [];
classes.push(Namespace.css.decade);
const startDecadeYear = this._startDecade.year;
const endDecadeYear = this._startDecade.year + 9;
if (!this.optionsStore.unset &&
pickedYears.filter((x) => x >= startDecadeYear && x <= endDecadeYear)
.length > 0) {
classes.push(Namespace.css.active);
}
paint('decade', this._startDecade, classes, containerClone);
containerClone.classList.remove(...containerClone.classList);
containerClone.classList.add(...classes);
containerClone.setAttribute('data-value', `${this._startDecade.year}`);
containerClone.innerText = `${this._startDecade.format({
year: 'numeric',
})}`;
this._startDecade.manipulate(10, exports.Unit.year);
});
}
}
/**
* Creates the clock display
*/
class TimeDisplay {
constructor() {
this._gridColumns = '';
this.optionsStore = serviceLocator.locate(OptionsStore);
this.dates = serviceLocator.locate(Dates);
this.validation = serviceLocator.locate(Validation);
}
/**
* Build the container html for the clock display
* @private
*/
getPicker(iconTag) {
const container = document.createElement('div');
container.classList.add(Namespace.css.clockContainer);
container.append(...this._grid(iconTag));
return container;
}
/**
* Populates the various elements with in the clock display
* like the current hour and if the manipulation icons are enabled.
* @private
*/
_update(widget) {
const timesDiv = (widget.getElementsByClassName(Namespace.css.clockContainer)[0]);
const lastPicked = (this.dates.lastPicked || this.optionsStore.viewDate)
.clone;
timesDiv
.querySelectorAll('.disabled')
.forEach((element) => element.classList.remove(Namespace.css.disabled));
if (this.optionsStore.options.display.components.hours) {
if (!this.validation.isValid(this.optionsStore.viewDate.clone.manipulate(1, exports.Unit.hours), exports.Unit.hours)) {
timesDiv
.querySelector(`[data-action=${ActionTypes$1.incrementHours}]`)
.classList.add(Namespace.css.disabled);
}
if (!this.validation.isValid(this.optionsStore.viewDate.clone.manipulate(-1, exports.Unit.hours), exports.Unit.hours)) {
timesDiv
.querySelector(`[data-action=${ActionTypes$1.decrementHours}]`)
.classList.add(Namespace.css.disabled);
}
timesDiv.querySelector(`[data-time-component=${exports.Unit.hours}]`).innerText = lastPicked.getHoursFormatted(this.optionsStore.options.localization.hourCycle);
}
if (this.optionsStore.options.display.components.minutes) {
if (!this.validation.isValid(this.optionsStore.viewDate.clone.manipulate(1, exports.Unit.minutes), exports.Unit.minutes)) {
timesDiv
.querySelector(`[data-action=${ActionTypes$1.incrementMinutes}]`)
.classList.add(Namespace.css.disabled);
}
if (!this.validation.isValid(this.optionsStore.viewDate.clone.manipulate(-1, exports.Unit.minutes), exports.Unit.minutes)) {
timesDiv
.querySelector(`[data-action=${ActionTypes$1.decrementMinutes}]`)
.classList.add(Namespace.css.disabled);
}
timesDiv.querySelector(`[data-time-component=${exports.Unit.minutes}]`).innerText = lastPicked.minutesFormatted;
}
if (this.optionsStore.options.display.components.seconds) {
if (!this.validation.isValid(this.optionsStore.viewDate.clone.manipulate(1, exports.Unit.seconds), exports.Unit.seconds)) {
timesDiv
.querySelector(`[data-action=${ActionTypes$1.incrementSeconds}]`)
.classList.add(Namespace.css.disabled);
}
if (!this.validation.isValid(this.optionsStore.viewDate.clone.manipulate(-1, exports.Unit.seconds), exports.Unit.seconds)) {
timesDiv
.querySelector(`[data-action=${ActionTypes$1.decrementSeconds}]`)
.classList.add(Namespace.css.disabled);
}
timesDiv.querySelector(`[data-time-component=${exports.Unit.seconds}]`).innerText = lastPicked.secondsFormatted;
}
if (this.optionsStore.isTwelveHour) {
const toggle = timesDiv.querySelector(`[data-action=${ActionTypes$1.toggleMeridiem}]`);
toggle.innerText = lastPicked.meridiem();
if (!this.validation.isValid(lastPicked.clone.manipulate(lastPicked.hours >= 12 ? -12 : 12, exports.Unit.hours))) {
toggle.classList.add(Namespace.css.disabled);
}
else {
toggle.classList.remove(Namespace.css.disabled);
}
}
timesDiv.style.gridTemplateAreas = `"${this._gridColumns}"`;
}
/**
* Creates the table for the clock display depending on what options are selected.
* @private
*/
_grid(iconTag) {
this._gridColumns = '';
const top = [], middle = [], bottom = [], separator = document.createElement('div'), upIcon = iconTag(this.optionsStore.options.display.icons.up), downIcon = iconTag(this.optionsStore.options.display.icons.down);
separator.classList.add(Namespace.css.separator, Namespace.css.noHighlight);
const separatorColon = separator.cloneNode(true);
separatorColon.innerHTML = ':';
const getSeparator = (colon = false) => {
return colon
? separatorColon.cloneNode(true)
: separator.cloneNode(true);
};
if (this.optionsStore.options.display.components.hours) {
let divElement = document.createElement('div');
divElement.setAttribute('title', this.optionsStore.options.localization.incrementHour);
divElement.setAttribute('data-action', ActionTypes$1.incrementHours);
divElement.appendChild(upIcon.cloneNode(true));
top.push(divElement);
divElement = document.createElement('div');
divElement.setAttribute('title', this.optionsStore.options.localization.pickHour);
divElement.setAttribute('data-action', ActionTypes$1.showHours);
divElement.setAttribute('data-time-component', exports.Unit.hours);
middle.push(divElement);
divElement = document.createElement('div');
divElement.setAttribute('title', this.optionsStore.options.localization.decrementHour);
divElement.setAttribute('data-action', ActionTypes$1.decrementHours);
divElement.appendChild(downIcon.cloneNode(true));
bottom.push(divElement);
this._gridColumns += 'a';
}
if (this.optionsStore.options.display.components.minutes) {
this._gridColumns += ' a';
if (this.optionsStore.options.display.components.hours) {
top.push(getSeparator());
middle.push(getSeparator(true));
bottom.push(getSeparator());
this._gridColumns += ' a';
}
let divElement = document.createElement('div');
divElement.setAttribute('title', this.optionsStore.options.localization.incrementMinute);
divElement.setAttribute('data-action', ActionTypes$1.incrementMinutes);
divElement.appendChild(upIcon.cloneNode(true));
top.push(divElement);
divElement = document.createElement('div');
divElement.setAttribute('title', this.optionsStore.options.localization.pickMinute);
divElement.setAttribute('data-action', ActionTypes$1.showMinutes);
divElement.setAttribute('data-time-component', exports.Unit.minutes);
middle.push(divElement);
divElement = document.createElement('div');
divElement.setAttribute('title', this.optionsStore.options.localization.decrementMinute);
divElement.setAttribute('data-action', ActionTypes$1.decrementMinutes);
divElement.appendChild(downIcon.cloneNode(true));
bottom.push(divElement);
}
if (this.optionsStore.options.display.components.seconds) {
this._gridColumns += ' a';
if (this.optionsStore.options.display.components.minutes) {
top.push(getSeparator());
middle.push(getSeparator(true));
bottom.push(getSeparator());
this._gridColumns += ' a';
}
let divElement = document.createElement('div');
divElement.setAttribute('title', this.optionsStore.options.localization.incrementSecond);
divElement.setAttribute('data-action', ActionTypes$1.incrementSeconds);
divElement.appendChild(upIcon.cloneNode(true));
top.push(divElement);
divElement = document.createElement('div');
divElement.setAttribute('title', this.optionsStore.options.localization.pickSecond);
divElement.setAttribute('data-action', ActionTypes$1.showSeconds);
divElement.setAttribute('data-time-component', exports.Unit.seconds);
middle.push(divElement);
divElement = document.createElement('div');
divElement.setAttribute('title', this.optionsStore.options.localization.decrementSecond);
divElement.setAttribute('data-action', ActionTypes$1.decrementSeconds);
divElement.appendChild(downIcon.cloneNode(true));
bottom.push(divElement);
}
if (this.optionsStore.isTwelveHour) {
this._gridColumns += ' a';
let divElement = getSeparator();
top.push(divElement);
const button = document.createElement('button');
button.setAttribute('title', this.optionsStore.options.localization.toggleMeridiem);
button.setAttribute('data-action', ActionTypes$1.toggleMeridiem);
button.setAttribute('tabindex', '-1');
if (Namespace.css.toggleMeridiem.includes(',')) {
//todo move this to paint function?
button.classList.add(...Namespace.css.toggleMeridiem.split(','));
}
else
button.classList.add(Namespace.css.toggleMeridiem);
divElement = document.createElement('div');
divElement.classList.add(Namespace.css.noHighlight);
divElement.appendChild(button);
middle.push(divElement);
divElement = getSeparator();
bottom.push(divElement);
}
this._gridColumns = this._gridColumns.trim();
return [...top, ...middle, ...bottom];
}
}
/**
* Creates and updates the grid for `hours`
*/
class HourDisplay {
constructor() {
this.optionsStore = serviceLocator.locate(OptionsStore);
this.validation = serviceLocator.locate(Validation);
}
/**
* Build the container html for the display
* @private
*/
getPicker() {
const container = document.createElement('div');
container.classList.add(Namespace.css.hourContainer);
for (let i = 0; i < (this.optionsStore.isTwelveHour ? 12 : 24); i++) {
const div = document.createElement('div');
div.setAttribute('data-action', ActionTypes$1.selectHour);
container.appendChild(div);
}
return container;
}
/**
* Populates the grid and updates enabled states
* @private
*/
_update(widget, paint) {
const container = widget.getElementsByClassName(Namespace.css.hourContainer)[0];
const innerDate = this.optionsStore.viewDate.clone.startOf(exports.Unit.date);
container
.querySelectorAll(`[data-action="${ActionTypes$1.selectHour}"]`)
.forEach((containerClone) => {
const classes = [];
classes.push(Namespace.css.hour);
if (!this.validation.isValid(innerDate, exports.Unit.hours)) {
classes.push(Namespace.css.disabled);
}
paint(exports.Unit.hours, innerDate, classes, containerClone);
containerClone.classList.remove(...containerClone.classList);
containerClone.classList.add(...classes);
containerClone.setAttribute('data-value', `${innerDate.hours}`);
containerClone.innerText = innerDate.getHoursFormatted(this.optionsStore.options.localization.hourCycle);
innerDate.manipulate(1, exports.Unit.hours);
});
}
}
/**
* Creates and updates the grid for `minutes`
*/
class MinuteDisplay {
constructor() {
this.optionsStore = serviceLocator.locate(OptionsStore);
this.validation = serviceLocator.locate(Validation);
}
/**
* Build the container html for the display
* @private
*/
getPicker() {
const container = document.createElement('div');
container.classList.add(Namespace.css.minuteContainer);
const step = this.optionsStore.options.stepping === 1
? 5
: this.optionsStore.options.stepping;
for (let i = 0; i < 60 / step; i++) {
const div = document.createElement('div');
div.setAttribute('data-action', ActionTypes$1.selectMinute);
container.appendChild(div);
}
return container;
}
/**
* Populates the grid and updates enabled states
* @private
*/
_update(widget, paint) {
const container = widget.getElementsByClassName(Namespace.css.minuteContainer)[0];
const innerDate = this.optionsStore.viewDate.clone.startOf(exports.Unit.hours);
const step = this.optionsStore.options.stepping === 1
? 5
: this.optionsStore.options.stepping;
container
.querySelectorAll(`[data-action="${ActionTypes$1.selectMinute}"]`)
.forEach((containerClone) => {
const classes = [];
classes.push(Namespace.css.minute);
if (!this.validation.isValid(innerDate, exports.Unit.minutes)) {
classes.push(Namespace.css.disabled);
}
paint(exports.Unit.minutes, innerDate, classes, containerClone);
containerClone.classList.remove(...containerClone.classList);
containerClone.classList.add(...classes);
containerClone.setAttribute('data-value', `${innerDate.minutes}`);
containerClone.innerText = innerDate.minutesFormatted;
innerDate.manipulate(step, exports.Unit.minutes);
});
}
}
/**
* Creates and updates the grid for `seconds`
*/
class secondDisplay {
constructor() {
this.optionsStore = serviceLocator.locate(OptionsStore);
this.validation = serviceLocator.locate(Validation);
}
/**
* Build the container html for the display
* @private
*/
getPicker() {
const container = document.createElement('div');
container.classList.add(Namespace.css.secondContainer);
for (let i = 0; i < 12; i++) {
const div = document.createElement('div');
div.setAttribute('data-action', ActionTypes$1.selectSecond);
container.appendChild(div);
}
return container;
}
/**
* Populates the grid and updates enabled states
* @private
*/
_update(widget, paint) {
const container = widget.getElementsByClassName(Namespace.css.secondContainer)[0];
const innerDate = this.optionsStore.viewDate.clone.startOf(exports.Unit.minutes);
container
.querySelectorAll(`[data-action="${ActionTypes$1.selectSecond}"]`)
.forEach((containerClone) => {
const classes = [];
classes.push(Namespace.css.second);
if (!this.validation.isValid(innerDate, exports.Unit.seconds)) {
classes.push(Namespace.css.disabled);
}
paint(exports.Unit.seconds, innerDate, classes, containerClone);
containerClone.classList.remove(...containerClone.classList);
containerClone.classList.add(...classes);
containerClone.setAttribute('data-value', `${innerDate.seconds}`);
containerClone.innerText = innerDate.secondsFormatted;
innerDate.manipulate(5, exports.Unit.seconds);
});
}
}
/**
* Provides a collapse functionality to the view changes
*/
class Collapse {
/**
* Flips the show/hide state of `target`
* @param target html element to affect.
*/
static toggle(target) {
if (target.classList.contains(Namespace.css.show)) {
this.hide(target);
}
else {
this.show(target);
}
}
/**
* Skips any animation or timeouts and immediately set the element to show.
* @param target
*/
static showImmediately(target) {
target.classList.remove(Namespace.css.collapsing);
target.classList.add(Namespace.css.collapse, Namespace.css.show);
target.style.height = '';
}
/**
* If `target` is not already showing, then show after the animation.
* @param target
*/
static show(target) {
if (target.classList.contains(Namespace.css.collapsing) ||
target.classList.contains(Namespace.css.show))
return;
const complete = () => {
Collapse.showImmediately(target);
};
target.style.height = '0';
target.classList.remove(Namespace.css.collapse);
target.classList.add(Namespace.css.collapsing);
//eslint-disable-next-line @typescript-eslint/no-unused-vars
setTimeout(complete, this.getTransitionDurationFromElement(target));
target.style.height = `${target.scrollHeight}px`;
}
/**
* Skips any animation or timeouts and immediately set the element to hide.
* @param target
*/
static hideImmediately(target) {
if (!target)
return;
target.classList.remove(Namespace.css.collapsing, Namespace.css.show);
target.classList.add(Namespace.css.collapse);
}
/**
* If `target` is not already hidden, then hide after the animation.
* @param target HTML Element
*/
static hide(target) {
if (target.classList.contains(Namespace.css.collapsing) ||
!target.classList.contains(Namespace.css.show))
return;
const complete = () => {
Collapse.hideImmediately(target);
};
target.style.height = `${target.getBoundingClientRect()['height']}px`;
const reflow = (element) => element.offsetHeight;
reflow(target);
target.classList.remove(Namespace.css.collapse, Namespace.css.show);
target.classList.add(Namespace.css.collapsing);
target.style.height = '';
//eslint-disable-next-line @typescript-eslint/no-unused-vars
setTimeout(complete, this.getTransitionDurationFromElement(target));
}
}
/**
* Gets the transition duration from the `element` by getting css properties
* `transition-duration` and `transition-delay`
* @param element HTML Element
*/
Collapse.getTransitionDurationFromElement = (element) => {
if (!element) {
return 0;
}
// Get transition-duration of the element
let { transitionDuration, transitionDelay } = window.getComputedStyle(element);
const floatTransitionDuration = Number.parseFloat(transitionDuration);
const floatTransitionDelay = Number.parseFloat(transitionDelay);
// Return 0 if element or transition duration is not found
if (!floatTransitionDuration && !floatTransitionDelay) {
return 0;
}
// If multiple durations are defined, take the first
transitionDuration = transitionDuration.split(',')[0];
transitionDelay = transitionDelay.split(',')[0];
return ((Number.parseFloat(transitionDuration) +
Number.parseFloat(transitionDelay)) *
1000);
};
/**
* Main class for all things display related.
*/
class Display {
constructor() {
this._isVisible = false;
/**
* A document click event to hide the widget if click is outside
* @private
* @param e MouseEvent
*/
this._documentClickEvent = (e) => {
if (this.optionsStore.options.debug || window.debug)
return; //eslint-disable-line @typescript-eslint/no-explicit-any
if (this._isVisible &&
!e.composedPath().includes(this.widget) && // click inside the widget
!e.composedPath()?.includes(this.optionsStore.element) // click on the element
) {
this.hide();
}
};
/**
* Click event for any action like selecting a date
* @param e MouseEvent
* @private
*/
this._actionsClickEvent = (e) => {
this._eventEmitters.action.emit({ e: e });
};
this.optionsStore = serviceLocator.locate(OptionsStore);
this.validation = serviceLocator.locate(Validation);
this.dates = serviceLocator.locate(Dates);
this.dateDisplay = serviceLocator.locate(DateDisplay);
this.monthDisplay = serviceLocator.locate(MonthDisplay);
this.yearDisplay = serviceLocator.locate(YearDisplay);
this.decadeDisplay = serviceLocator.locate(DecadeDisplay);
this.timeDisplay = serviceLocator.locate(TimeDisplay);
this.hourDisplay = serviceLocator.locate(HourDisplay);
this.minuteDisplay = serviceLocator.locate(MinuteDisplay);
this.secondDisplay = serviceLocator.locate(secondDisplay);
this._eventEmitters = serviceLocator.locate(EventEmitters);
this._widget = undefined;
this._eventEmitters.updateDisplay.subscribe((result) => {
this._update(result);
});
}
/**
* Returns the widget body or undefined
* @private
*/
get widget() {
return this._widget;
}
/**
* Returns this visible state of the picker (shown)
*/
get isVisible() {
return this._isVisible;
}
/**
* Updates the table for a particular unit. Used when an option as changed or
* whenever the class list might need to be refreshed.
* @param unit
* @private
*/
_update(unit) {
if (!this.widget)
return;
//todo do I want some kind of error catching or other guards here?
switch (unit) {
case exports.Unit.seconds:
this.secondDisplay._update(this.widget, this.paint);
break;
case exports.Unit.minutes:
this.minuteDisplay._update(this.widget, this.paint);
break;
case exports.Unit.hours:
this.hourDisplay._update(this.widget, this.paint);
break;
case exports.Unit.date:
this.dateDisplay._update(this.widget, this.paint);
break;
case exports.Unit.month:
this.monthDisplay._update(this.widget, this.paint);
break;
case exports.Unit.year:
this.yearDisplay._update(this.widget, this.paint);
break;
case 'clock':
if (!this._hasTime)
break;
this.timeDisplay._update(this.widget);
this._update(exports.Unit.hours);
this._update(exports.Unit.minutes);
this._update(exports.Unit.seconds);
break;
case 'calendar':
this._update(exports.Unit.date);
this._update(exports.Unit.year);
this._update(exports.Unit.month);
this.decadeDisplay._update(this.widget, this.paint);
this._updateCalendarHeader();
break;
case 'all':
if (this._hasTime) {
this._update('clock');
}
if (this._hasDate) {
this._update('calendar');
}
}
}
// noinspection JSUnusedLocalSymbols
/**
* Allows developers to add/remove classes from an element.
* @param _unit
* @param _date
* @param _classes
* @param _element
*/
/* eslint-disable @typescript-eslint/no-unused-vars */
paint(_unit, _date, _classes, _element) {
// implemented in plugin
}
/* eslint-enable @typescript-eslint/no-unused-vars */
/**
* Shows the picker and creates a Popper instance if needed.
* Add document click event to hide when clicking outside the picker.
* fires Events#show
*/
show() {
if (this.widget == undefined) {
if (this.dates.picked.length == 0) {
if (this.optionsStore.options.useCurrent &&
!this.optionsStore.options.defaultDate) {
const date = new DateTime().setLocale(this.optionsStore.options.localization.locale);
if (!this.optionsStore.options.keepInvalid) {
let tries = 0;
let direction = 1;
if (this.optionsStore.options.restrictions.maxDate?.isBefore(date)) {
direction = -1;
}
while (!this.validation.isValid(date)) {
date.manipulate(direction, exports.Unit.date);
if (tries > 31)
break;
tries++;
}
}
this.dates.setValue(date);
}
if (this.optionsStore.options.defaultDate) {
this.dates.setValue(this.optionsStore.options.defaultDate);
}
}
this._buildWidget();
this._updateTheme();
// If modeView is only clock
const onlyClock = this._hasTime && !this._hasDate;
// reset the view to the clock if there's no date components
if (onlyClock) {
this.optionsStore.currentView = 'clock';
this._eventEmitters.action.emit({
e: null,
action: ActionTypes$1.showClock,
});
}
// otherwise return to the calendar view
else if (!this.optionsStore.currentCalendarViewMode) {
this.optionsStore.currentCalendarViewMode =
this.optionsStore.minimumCalendarViewMode;
}
if (!onlyClock &&
this.optionsStore.options.display.viewMode !== 'clock') {
if (this._hasTime) {
if (!this.optionsStore.options.display.sideBySide) {
Collapse.hideImmediately(this.widget.querySelector(`div.${Namespace.css.timeContainer}`));
}
else {
Collapse.show(this.widget.querySelector(`div.${Namespace.css.timeContainer}`));
}
}
Collapse.show(this.widget.querySelector(`div.${Namespace.css.dateContainer}`));
}
if (this._hasDate) {
this._showMode();
}
if (!this.optionsStore.options.display.inline) {
// If needed to change the parent container
const container = this.optionsStore.options?.container || document.body;
container.appendChild(this.widget);
this.createPopup(this.optionsStore.element, this.widget, {
modifiers: [{ name: 'eventListeners', enabled: true }],
//#2400
placement: document.documentElement.dir === 'rtl'
? 'bottom-end'
: 'bottom-start',
}).then();
}
else {
this.optionsStore.element.appendChild(this.widget);
}
if (this.optionsStore.options.display.viewMode == 'clock') {
this._eventEmitters.action.emit({
e: null,
action: ActionTypes$1.showClock,
});
}
this.widget
.querySelectorAll('[data-action]')
.forEach((element) => element.addEventListener('click', this._actionsClickEvent));
// show the clock when using sideBySide
if (this._hasTime && this.optionsStore.options.display.sideBySide) {
this.timeDisplay._update(this.widget);
this.widget.getElementsByClassName(Namespace.css.clockContainer)[0].style.display = 'grid';
}
}
this.widget.classList.add(Namespace.css.show);
if (!this.optionsStore.options.display.inline) {
this.updatePopup();
document.addEventListener('click', this._documentClickEvent);
}
this._eventEmitters.triggerEvent.emit({ type: Namespace.events.show });
this._isVisible = true;
}
async createPopup(element, widget,
//eslint-disable-next-line @typescript-eslint/no-explicit-any
options) {
let createPopperFunction;
//eslint-disable-next-line @typescript-eslint/no-explicit-any
if (window?.Popper) {
//eslint-disable-next-line @typescript-eslint/no-explicit-any
createPopperFunction = window?.Popper?.createPopper;
}
else {
const { createPopper } = await import('@popperjs/core');
createPopperFunction = createPopper;
}
if (createPopperFunction) {
this._popperInstance = createPopperFunction(element, widget, options);
}
}
updatePopup() {
this._popperInstance?.update();
}
/**
* Changes the calendar view mode. E.g. month <-> year
* @param direction -/+ number to move currentViewMode
* @private
*/
_showMode(direction) {
if (!this.widget) {
return;
}
if (direction) {
const max = Math.max(this.optionsStore.minimumCalendarViewMode, Math.min(3, this.optionsStore.currentCalendarViewMode + direction));
if (this.optionsStore.currentCalendarViewMode == max)
return;
this.optionsStore.currentCalendarViewMode = max;
}
this.widget
.querySelectorAll(`.${Namespace.css.dateContainer} > div:not(.${Namespace.css.calendarHeader}), .${Namespace.css.timeContainer} > div:not(.${Namespace.css.clockContainer})`)
.forEach((e) => (e.style.display = 'none'));
const datePickerMode = CalendarModes[this.optionsStore.currentCalendarViewMode];
const picker = this.widget.querySelector(`.${datePickerMode.className}`);
switch (datePickerMode.className) {
case Namespace.css.decadesContainer:
this.decadeDisplay._update(this.widget, this.paint);
break;
case Namespace.css.yearsContainer:
this.yearDisplay._update(this.widget, this.paint);
break;
case Namespace.css.monthsContainer:
this.monthDisplay._update(this.widget, this.paint);
break;
case Namespace.css.daysContainer:
this.dateDisplay._update(this.widget, this.paint);
break;
}
picker.style.display = 'grid';
if (this.optionsStore.options.display.sideBySide)
(this.widget.querySelectorAll(`.${Namespace.css.clockContainer}`)[0]).style.display = 'grid';
this._updateCalendarHeader();
this._eventEmitters.viewUpdate.emit();
}
/**
* Changes the theme. E.g. light, dark or auto
* @param theme the theme name
* @private
*/
_updateTheme(theme) {
if (!this.widget) {
return;
}
if (theme) {
if (this.optionsStore.options.display.theme === theme)
return;
this.optionsStore.options.display.theme = theme;
}
this.widget.classList.remove('light', 'dark');
this.widget.classList.add(this._getThemeClass());
if (this.optionsStore.options.display.theme === 'auto') {
window
.matchMedia(Namespace.css.isDarkPreferredQuery)
.addEventListener('change', () => this._updateTheme());
}
else {
window
.matchMedia(Namespace.css.isDarkPreferredQuery)
.removeEventListener('change', () => this._updateTheme());
}
}
_getThemeClass() {
const currentTheme = this.optionsStore.options.display.theme || 'auto';
const isDarkMode = window.matchMedia &&
window.matchMedia(Namespace.css.isDarkPreferredQuery).matches;
switch (currentTheme) {
case 'light':
return Namespace.css.lightTheme;
case 'dark':
return Namespace.css.darkTheme;
case 'auto':
return isDarkMode ? Namespace.css.darkTheme : Namespace.css.lightTheme;
}
}
_updateCalendarHeader() {
if (!this._hasDate)
return;
const showing = [
...this.widget.querySelector(`.${Namespace.css.dateContainer} div[style*="display: grid"]`).classList,
].find((x) => x.startsWith(Namespace.css.dateContainer));
const [previous, switcher, next] = this.widget
.getElementsByClassName(Namespace.css.calendarHeader)[0]
.getElementsByTagName('div');
switch (showing) {
case Namespace.css.decadesContainer:
previous.setAttribute('title', this.optionsStore.options.localization.previousCentury);
switcher.setAttribute('title', '');
next.setAttribute('title', this.optionsStore.options.localization.nextCentury);
break;
case Namespace.css.yearsContainer:
previous.setAttribute('title', this.optionsStore.options.localization.previousDecade);
switcher.setAttribute('title', this.optionsStore.options.localization.selectDecade);
next.setAttribute('title', this.optionsStore.options.localization.nextDecade);
break;
case Namespace.css.monthsContainer:
previous.setAttribute('title', this.optionsStore.options.localization.previousYear);
switcher.setAttribute('title', this.optionsStore.options.localization.selectYear);
next.setAttribute('title', this.optionsStore.options.localization.nextYear);
break;
case Namespace.css.daysContainer:
previous.setAttribute('title', this.optionsStore.options.localization.previousMonth);
switcher.setAttribute('title', this.optionsStore.options.localization.selectMonth);
next.setAttribute('title', this.optionsStore.options.localization.nextMonth);
switcher.setAttribute(showing, this.optionsStore.viewDate.format(this.optionsStore.options.localization.dayViewHeaderFormat));
break;
}
switcher.innerText = switcher.getAttribute(showing);
}
/**
* Hides the picker if needed.
* Remove document click event to hide when clicking outside the picker.
* fires Events#hide
*/
hide() {
if (!this.widget || !this._isVisible)
return;
this.widget.classList.remove(Namespace.css.show);
if (this._isVisible) {
this._eventEmitters.triggerEvent.emit({
type: Namespace.events.hide,
date: this.optionsStore.unset
? null
: this.dates.lastPicked
? this.dates.lastPicked.clone
: void 0,
});
this._isVisible = false;
}
document.removeEventListener('click', this._documentClickEvent);
}
/**
* Toggles the picker's open state. Fires a show/hide event depending.
*/
toggle() {
return this._isVisible ? this.hide() : this.show();
}
/**
* Removes document and data-action click listener and reset the widget
* @private
*/
_dispose() {
document.removeEventListener('click', this._documentClickEvent);
if (!this.widget)
return;
this.widget
.querySelectorAll('[data-action]')
.forEach((element) => element.removeEventListener('click', this._actionsClickEvent));
this.widget.parentNode.removeChild(this.widget);
this._widget = undefined;
}
/**
* Builds the widgets html template.
* @private
*/
_buildWidget() {
const template = document.createElement('div');
template.classList.add(Namespace.css.widget);
const dateView = document.createElement('div');
dateView.classList.add(Namespace.css.dateContainer);
dateView.append(this.getHeadTemplate(), this.decadeDisplay.getPicker(), this.yearDisplay.getPicker(), this.monthDisplay.getPicker(), this.dateDisplay.getPicker());
const timeView = document.createElement('div');
timeView.classList.add(Namespace.css.timeContainer);
timeView.appendChild(this.timeDisplay.getPicker(this._iconTag.bind(this)));
timeView.appendChild(this.hourDisplay.getPicker());
timeView.appendChild(this.minuteDisplay.getPicker());
timeView.appendChild(this.secondDisplay.getPicker());
const toolbar = document.createElement('div');
toolbar.classList.add(Namespace.css.toolbar);
toolbar.append(...this.getToolbarElements());
if (this.optionsStore.options.display.inline) {
template.classList.add(Namespace.css.inline);
}
if (this.optionsStore.options.display.calendarWeeks) {
template.classList.add('calendarWeeks');
}
if (this.optionsStore.options.display.sideBySide &&
this._hasDate &&
this._hasTime) {
template.classList.add(Namespace.css.sideBySide);
if (this.optionsStore.options.display.toolbarPlacement === 'top') {
template.appendChild(toolbar);
}
const row = document.createElement('div');
row.classList.add('td-row');
dateView.classList.add('td-half');
timeView.classList.add('td-half');
row.appendChild(dateView);
row.appendChild(timeView);
template.appendChild(row);
if (this.optionsStore.options.display.toolbarPlacement === 'bottom') {
template.appendChild(toolbar);
}
this._widget = template;
return;
}
if (this.optionsStore.options.display.toolbarPlacement === 'top') {
template.appendChild(toolbar);
}
if (this._hasDate) {
if (this._hasTime) {
dateView.classList.add(Namespace.css.collapse);
if (this.optionsStore.options.display.viewMode !== 'clock')
dateView.classList.add(Namespace.css.show);
}
template.appendChild(dateView);
}
if (this._hasTime) {
if (this._hasDate) {
timeView.classList.add(Namespace.css.collapse);
if (this.optionsStore.options.display.viewMode === 'clock')
timeView.classList.add(Namespace.css.show);
}
template.appendChild(timeView);
}
if (this.optionsStore.options.display.toolbarPlacement === 'bottom') {
template.appendChild(toolbar);
}
const arrow = document.createElement('div');
arrow.classList.add('arrow');
arrow.setAttribute('data-popper-arrow', '');
template.appendChild(arrow);
this._widget = template;
}
/**
* Returns true if the hours, minutes, or seconds component is turned on
*/
get _hasTime() {
return (this.optionsStore.options.display.components.clock &&
(this.optionsStore.options.display.components.hours ||
this.optionsStore.options.display.components.minutes ||
this.optionsStore.options.display.components.seconds));
}
/**
* Returns true if the year, month, or date component is turned on
*/
get _hasDate() {
return (this.optionsStore.options.display.components.calendar &&
(this.optionsStore.options.display.components.year ||
this.optionsStore.options.display.components.month ||
this.optionsStore.options.display.components.date));
}
/**
* Get the toolbar html based on options like buttons.today
* @private
*/
getToolbarElements() {
const toolbar = [];
if (this.optionsStore.options.display.buttons.today) {
const div = document.createElement('div');
div.setAttribute('data-action', ActionTypes$1.today);
div.setAttribute('title', this.optionsStore.options.localization.today);
div.appendChild(this._iconTag(this.optionsStore.options.display.icons.today));
toolbar.push(div);
}
if (!this.optionsStore.options.display.sideBySide &&
this._hasDate &&
this._hasTime) {
let title, icon;
if (this.optionsStore.options.display.viewMode === 'clock') {
title = this.optionsStore.options.localization.selectDate;
icon = this.optionsStore.options.display.icons.date;
}
else {
title = this.optionsStore.options.localization.selectTime;
icon = this.optionsStore.options.display.icons.time;
}
const div = document.createElement('div');
div.setAttribute('data-action', ActionTypes$1.togglePicker);
div.setAttribute('title', title);
div.appendChild(this._iconTag(icon));
toolbar.push(div);
}
if (this.optionsStore.options.display.buttons.clear) {
const div = document.createElement('div');
div.setAttribute('data-action', ActionTypes$1.clear);
div.setAttribute('title', this.optionsStore.options.localization.clear);
div.appendChild(this._iconTag(this.optionsStore.options.display.icons.clear));
toolbar.push(div);
}
if (this.optionsStore.options.display.buttons.close) {
const div = document.createElement('div');
div.setAttribute('data-action', ActionTypes$1.close);
div.setAttribute('title', this.optionsStore.options.localization.close);
div.appendChild(this._iconTag(this.optionsStore.options.display.icons.close));
toolbar.push(div);
}
return toolbar;
}
/***
* Builds the base header template with next and previous icons
* @private
*/
getHeadTemplate() {
const calendarHeader = document.createElement('div');
calendarHeader.classList.add(Namespace.css.calendarHeader);
const previous = document.createElement('div');
previous.classList.add(Namespace.css.previous);
previous.setAttribute('data-action', ActionTypes$1.previous);
previous.appendChild(this._iconTag(this.optionsStore.options.display.icons.previous));
const switcher = document.createElement('div');
switcher.classList.add(Namespace.css.switch);
switcher.setAttribute('data-action', ActionTypes$1.changeCalendarView);
const next = document.createElement('div');
next.classList.add(Namespace.css.next);
next.setAttribute('data-action', ActionTypes$1.next);
next.appendChild(this._iconTag(this.optionsStore.options.display.icons.next));
calendarHeader.append(previous, switcher, next);
return calendarHeader;
}
/**
* Builds an icon tag as either an ``
* or with icons.type is `sprites` then a svg tag instead
* @param iconClass
* @private
*/
_iconTag(iconClass) {
if (this.optionsStore.options.display.icons.type === 'sprites') {
const svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg');
const icon = document.createElementNS('http://www.w3.org/2000/svg', 'use');
icon.setAttribute('xlink:href', iconClass); // Deprecated. Included for backward compatibility
icon.setAttribute('href', iconClass);
svg.appendChild(icon);
return svg;
}
const icon = document.createElement('i');
icon.classList.add(...iconClass.split(' '));
return icon;
}
/**
* Causes the widget to get rebuilt on next show. If the picker is already open
* then hide and reshow it.
* @private
*/
_rebuild() {
const wasVisible = this._isVisible;
this._dispose();
if (wasVisible)
this.show();
}
}
/**
*
*/
class Actions {
constructor() {
this.optionsStore = serviceLocator.locate(OptionsStore);
this.dates = serviceLocator.locate(Dates);
this.validation = serviceLocator.locate(Validation);
this.display = serviceLocator.locate(Display);
this._eventEmitters = serviceLocator.locate(EventEmitters);
this._eventEmitters.action.subscribe((result) => {
this.do(result.e, result.action);
});
}
/**
* Performs the selected `action`. See ActionTypes
* @param e This is normally a click event
* @param action If not provided, then look for a [data-action]
*/
//eslint-disable-next-line @typescript-eslint/no-explicit-any
do(e, action) {
const currentTarget = e?.currentTarget;
if (currentTarget?.classList?.contains(Namespace.css.disabled))
return false;
action = action || currentTarget?.dataset?.action;
const lastPicked = (this.dates.lastPicked || this.optionsStore.viewDate)
.clone;
switch (action) {
case ActionTypes$1.next:
case ActionTypes$1.previous:
this.handleNextPrevious(action);
break;
case ActionTypes$1.changeCalendarView:
this.display._showMode(1);
this.display._updateCalendarHeader();
break;
case ActionTypes$1.selectMonth:
case ActionTypes$1.selectYear:
case ActionTypes$1.selectDecade:
this.handleSelectCalendarMode(action, currentTarget);
break;
case ActionTypes$1.selectDay:
this.handleSelectDay(currentTarget);
break;
case ActionTypes$1.selectHour: {
let hour = +currentTarget.dataset.value;
if (lastPicked.hours >= 12 && this.optionsStore.isTwelveHour)
hour += 12;
lastPicked.hours = hour;
this.dates.setValue(lastPicked, this.dates.lastPickedIndex);
this.hideOrClock(e);
break;
}
case ActionTypes$1.selectMinute: {
lastPicked.minutes = +currentTarget.dataset.value;
this.dates.setValue(lastPicked, this.dates.lastPickedIndex);
this.hideOrClock(e);
break;
}
case ActionTypes$1.selectSecond: {
lastPicked.seconds = +currentTarget.dataset.value;
this.dates.setValue(lastPicked, this.dates.lastPickedIndex);
this.hideOrClock(e);
break;
}
case ActionTypes$1.incrementHours:
this.manipulateAndSet(lastPicked, exports.Unit.hours);
break;
case ActionTypes$1.incrementMinutes:
this.manipulateAndSet(lastPicked, exports.Unit.minutes, this.optionsStore.options.stepping);
break;
case ActionTypes$1.incrementSeconds:
this.manipulateAndSet(lastPicked, exports.Unit.seconds);
break;
case ActionTypes$1.decrementHours:
this.manipulateAndSet(lastPicked, exports.Unit.hours, -1);
break;
case ActionTypes$1.decrementMinutes:
this.manipulateAndSet(lastPicked, exports.Unit.minutes, this.optionsStore.options.stepping * -1);
break;
case ActionTypes$1.decrementSeconds:
this.manipulateAndSet(lastPicked, exports.Unit.seconds, -1);
break;
case ActionTypes$1.toggleMeridiem:
this.manipulateAndSet(lastPicked, exports.Unit.hours, this.dates.lastPicked.hours >= 12 ? -12 : 12);
break;
case ActionTypes$1.togglePicker:
this.handleToggle(currentTarget);
break;
case ActionTypes$1.showClock:
case ActionTypes$1.showHours:
case ActionTypes$1.showMinutes:
case ActionTypes$1.showSeconds:
//make sure the clock is actually displaying
if (!this.optionsStore.options.display.sideBySide &&
this.optionsStore.currentView !== 'clock') {
//hide calendar
Collapse.hideImmediately(this.display.widget.querySelector(`div.${Namespace.css.dateContainer}`));
//show clock
Collapse.showImmediately(this.display.widget.querySelector(`div.${Namespace.css.timeContainer}`));
}
this.handleShowClockContainers(action);
break;
case ActionTypes$1.clear:
this.dates.setValue(null);
this.display._updateCalendarHeader();
break;
case ActionTypes$1.close:
this.display.hide();
break;
case ActionTypes$1.today: {
const today = new DateTime().setLocale(this.optionsStore.options.localization.locale);
this._eventEmitters.updateViewDate.emit(today);
//todo this this really a good idea?
if (this.validation.isValid(today, exports.Unit.date))
this.dates.setValue(today, this.dates.lastPickedIndex);
break;
}
}
}
handleShowClockContainers(action) {
if (!this.display._hasTime) {
Namespace.errorMessages.throwError('Cannot show clock containers when time is disabled.');
return;
}
this.optionsStore.currentView = 'clock';
this.display.widget
.querySelectorAll(`.${Namespace.css.timeContainer} > div`)
.forEach((htmlElement) => (htmlElement.style.display = 'none'));
let classToUse = '';
switch (action) {
case ActionTypes$1.showClock:
classToUse = Namespace.css.clockContainer;
this.display._update('clock');
break;
case ActionTypes$1.showHours:
classToUse = Namespace.css.hourContainer;
this.display._update(exports.Unit.hours);
break;
case ActionTypes$1.showMinutes:
classToUse = Namespace.css.minuteContainer;
this.display._update(exports.Unit.minutes);
break;
case ActionTypes$1.showSeconds:
classToUse = Namespace.css.secondContainer;
this.display._update(exports.Unit.seconds);
break;
}
(this.display.widget.getElementsByClassName(classToUse)[0]).style.display = 'grid';
}
handleNextPrevious(action) {
const { unit, step } = CalendarModes[this.optionsStore.currentCalendarViewMode];
if (action === ActionTypes$1.next)
this.optionsStore.viewDate.manipulate(step, unit);
else
this.optionsStore.viewDate.manipulate(step * -1, unit);
this._eventEmitters.viewUpdate.emit();
this.display._showMode();
}
/**
* After setting the value it will either show the clock or hide the widget.
* @param e
*/
hideOrClock(e) {
if (!this.optionsStore.isTwelveHour &&
!this.optionsStore.options.display.components.minutes &&
!this.optionsStore.options.display.keepOpen &&
!this.optionsStore.options.display.inline) {
this.display.hide();
}
else {
this.do(e, ActionTypes$1.showClock);
}
}
/**
* Common function to manipulate {@link lastPicked} by `unit`.
* @param lastPicked
* @param unit
* @param value Value to change by
*/
manipulateAndSet(lastPicked, unit, value = 1) {
const newDate = lastPicked.manipulate(value, unit);
if (this.validation.isValid(newDate, unit)) {
this.dates.setValue(newDate, this.dates.lastPickedIndex);
}
}
handleSelectCalendarMode(action,
//eslint-disable-next-line @typescript-eslint/no-explicit-any
currentTarget) {
const value = +currentTarget.dataset.value;
switch (action) {
case ActionTypes$1.selectMonth:
this.optionsStore.viewDate.month = value;
break;
case ActionTypes$1.selectYear:
case ActionTypes$1.selectDecade:
this.optionsStore.viewDate.year = value;
break;
}
if (this.optionsStore.currentCalendarViewMode ===
this.optionsStore.minimumCalendarViewMode) {
this.dates.setValue(this.optionsStore.viewDate, this.dates.lastPickedIndex);
if (!this.optionsStore.options.display.inline) {
this.display.hide();
}
}
else {
this.display._showMode(-1);
}
}
//eslint-disable-next-line @typescript-eslint/no-explicit-any
handleToggle(currentTarget) {
if (currentTarget.getAttribute('title') ===
this.optionsStore.options.localization.selectDate) {
currentTarget.setAttribute('title', this.optionsStore.options.localization.selectTime);
currentTarget.innerHTML = this.display._iconTag(this.optionsStore.options.display.icons.time).outerHTML;
this.display._updateCalendarHeader();
this.optionsStore.refreshCurrentView();
}
else {
currentTarget.setAttribute('title', this.optionsStore.options.localization.selectDate);
currentTarget.innerHTML = this.display._iconTag(this.optionsStore.options.display.icons.date).outerHTML;
if (this.display._hasTime) {
this.handleShowClockContainers(ActionTypes$1.showClock);
this.display._update('clock');
}
}
this.display.widget
.querySelectorAll(`.${Namespace.css.dateContainer}, .${Namespace.css.timeContainer}`)
.forEach((htmlElement) => Collapse.toggle(htmlElement));
this._eventEmitters.viewUpdate.emit();
}
//eslint-disable-next-line @typescript-eslint/no-explicit-any
handleSelectDay(currentTarget) {
const day = this.optionsStore.viewDate.clone;
if (currentTarget.classList.contains(Namespace.css.old)) {
day.manipulate(-1, exports.Unit.month);
}
if (currentTarget.classList.contains(Namespace.css.new)) {
day.manipulate(1, exports.Unit.month);
}
day.date = +currentTarget.dataset.day;
let index = 0;
if (this.optionsStore.options.multipleDates) {
index = this.dates.pickedIndex(day, exports.Unit.date);
if (index !== -1) {
this.dates.setValue(null, index); //deselect multi-date
}
else {
this.dates.setValue(day, this.dates.lastPickedIndex + 1);
}
}
else {
this.dates.setValue(day, this.dates.lastPickedIndex);
}
if (!this.display._hasTime &&
!this.optionsStore.options.display.keepOpen &&
!this.optionsStore.options.display.inline &&
!this.optionsStore.options.multipleDates) {
this.display.hide();
}
}
}
/**
* A robust and powerful date/time picker component.
*/
class TempusDominus {
constructor(element, options = {}) {
//eslint-disable-next-line @typescript-eslint/no-explicit-any
this._subscribers = {};
this._isDisabled = false;
/**
* Event for when the input field changes. This is a class level method so there's
* something for the remove listener function.
* @private
*/
//eslint-disable-next-line @typescript-eslint/no-explicit-any
this._inputChangeEvent = (event) => {
const internallyTriggered = event?.detail;
if (internallyTriggered)
return;
const setViewDate = () => {
if (this.dates.lastPicked)
this.optionsStore.viewDate = this.dates.lastPicked.clone;
};
const value = this.optionsStore.input.value;
if (this.optionsStore.options.multipleDates) {
try {
const valueSplit = value.split(this.optionsStore.options.multipleDatesSeparator);
for (let i = 0; i < valueSplit.length; i++) {
this.dates.setFromInput(valueSplit[i], i);
}
setViewDate();
}
catch {
console.warn('TD: Something went wrong trying to set the multipleDates values from the input field.');
}
}
else {
this.dates.setFromInput(value, 0);
setViewDate();
}
};
/**
* Event for when the toggle is clicked. This is a class level method so there's
* something for the remove listener function.
* @private
*/
this._toggleClickEvent = () => {
if (this.optionsStore.element?.disabled ||
this.optionsStore.input?.disabled)
return;
this.toggle();
};
setupServiceLocator();
this._eventEmitters = serviceLocator.locate(EventEmitters);
this.optionsStore = serviceLocator.locate(OptionsStore);
this.display = serviceLocator.locate(Display);
this.dates = serviceLocator.locate(Dates);
this.actions = serviceLocator.locate(Actions);
if (!element) {
Namespace.errorMessages.mustProvideElement();
}
this.optionsStore.element = element;
this._initializeOptions(options, DefaultOptions, true);
this.optionsStore.viewDate.setLocale(this.optionsStore.options.localization.locale);
this.optionsStore.unset = true;
this._initializeInput();
this._initializeToggle();
if (this.optionsStore.options.display.inline)
this.display.show();
this._eventEmitters.triggerEvent.subscribe((e) => {
this._triggerEvent(e);
});
this._eventEmitters.viewUpdate.subscribe(() => {
this._viewUpdate();
});
this._eventEmitters.updateViewDate.subscribe((dateTime) => {
this.viewDate = dateTime;
});
}
get viewDate() {
return this.optionsStore.viewDate;
}
set viewDate(value) {
this.optionsStore.viewDate = value;
this.optionsStore.viewDate.setLocale(this.optionsStore.options.localization.locale);
this.display._update(this.optionsStore.currentView === 'clock' ? 'clock' : 'calendar');
}
// noinspection JSUnusedGlobalSymbols
/**
* Update the picker options. If `reset` is provide `options` will be merged with DefaultOptions instead.
* @param options
* @param reset
* @public
*/
updateOptions(options, reset = false) {
if (reset)
this._initializeOptions(options, DefaultOptions);
else
this._initializeOptions(options, this.optionsStore.options);
this.display._rebuild();
}
// noinspection JSUnusedGlobalSymbols
/**
* Toggles the picker open or closed. If the picker is disabled, nothing will happen.
* @public
*/
toggle() {
if (this._isDisabled)
return;
this.display.toggle();
}
// noinspection JSUnusedGlobalSymbols
/**
* Shows the picker unless the picker is disabled.
* @public
*/
show() {
if (this._isDisabled)
return;
this.display.show();
}
// noinspection JSUnusedGlobalSymbols
/**
* Hides the picker unless the picker is disabled.
* @public
*/
hide() {
this.display.hide();
}
// noinspection JSUnusedGlobalSymbols
/**
* Disables the picker and the target input field.
* @public
*/
disable() {
this._isDisabled = true;
// todo this might be undesired. If a dev disables the input field to
// only allow using the picker, this will break that.
this.optionsStore.input?.setAttribute('disabled', 'disabled');
this.display.hide();
}
// noinspection JSUnusedGlobalSymbols
/**
* Enables the picker and the target input field.
* @public
*/
enable() {
this._isDisabled = false;
this.optionsStore.input?.removeAttribute('disabled');
}
// noinspection JSUnusedGlobalSymbols
/**
* Clears all the selected dates
* @public
*/
clear() {
this.optionsStore.input.value = '';
this.dates.clear();
}
// noinspection JSUnusedGlobalSymbols
/**
* Allows for a direct subscription to picker events, without having to use addEventListener on the element.
* @param eventTypes See Namespace.Events
* @param callbacks Function to call when event is triggered
* @public
*/
subscribe(eventTypes, callbacks //eslint-disable-line @typescript-eslint/no-explicit-any
) {
if (typeof eventTypes === 'string') {
eventTypes = [eventTypes];
}
let callBackArray; //eslint-disable-line @typescript-eslint/no-explicit-any
if (!Array.isArray(callbacks)) {
callBackArray = [callbacks];
}
else {
callBackArray = callbacks;
}
if (eventTypes.length !== callBackArray.length) {
Namespace.errorMessages.subscribeMismatch();
}
const returnArray = [];
for (let i = 0; i < eventTypes.length; i++) {
const eventType = eventTypes[i];
if (!Array.isArray(this._subscribers[eventType])) {
this._subscribers[eventType] = [];
}
this._subscribers[eventType].push(callBackArray[i]);
returnArray.push({
unsubscribe: this._unsubscribe.bind(this, eventType, this._subscribers[eventType].length - 1),
});
if (eventTypes.length === 1) {
return returnArray[0];
}
}
return returnArray;
}
// noinspection JSUnusedGlobalSymbols
/**
* Hides the picker and removes event listeners
*/
dispose() {
this.display.hide();
// this will clear the document click event listener
this.display._dispose();
this._eventEmitters.destroy();
this.optionsStore.input?.removeEventListener('change', this._inputChangeEvent);
if (this.optionsStore.options.allowInputToggle) {
this.optionsStore.input?.removeEventListener('click', this._toggleClickEvent);
}
this._toggle?.removeEventListener('click', this._toggleClickEvent);
this._subscribers = {};
}
/**
* Updates the options to use the provided language.
* THe language file must be loaded first.
* @param language
*/
locale(language) {
const asked = loadedLocales[language];
if (!asked)
return;
this.updateOptions({
localization: asked,
});
}
/**
* Triggers an event like ChangeEvent when the picker has updated the value
* of a selected date.
* @param event Accepts a BaseEvent object.
* @private
*/
_triggerEvent(event) {
event.viewMode = this.optionsStore.currentView;
const isChangeEvent = event.type === Namespace.events.change;
if (isChangeEvent) {
const { date, oldDate, isClear } = event;
if ((date && oldDate && date.isSame(oldDate)) ||
(!isClear && !date && !oldDate)) {
return;
}
this._handleAfterChangeEvent(event);
this.optionsStore.input?.dispatchEvent(
//eslint-disable-next-line @typescript-eslint/no-explicit-any
new CustomEvent(event.type, { detail: event }));
this.optionsStore.input?.dispatchEvent(
//eslint-disable-next-line @typescript-eslint/no-explicit-any
new CustomEvent('change', { detail: event }));
}
this.optionsStore.element.dispatchEvent(
//eslint-disable-next-line @typescript-eslint/no-explicit-any
new CustomEvent(event.type, { detail: event }));
//eslint-disable-next-line @typescript-eslint/no-explicit-any
if (window.jQuery) {
//eslint-disable-next-line @typescript-eslint/no-explicit-any
const $ = window.jQuery;
if (isChangeEvent && this.optionsStore.input) {
$(this.optionsStore.input).trigger(event);
}
else {
$(this.optionsStore.element).trigger(event);
}
}
this._publish(event);
}
_publish(event) {
// return if event is not subscribed
if (!Array.isArray(this._subscribers[event.type])) {
return;
}
// Trigger callback for each subscriber
this._subscribers[event.type].forEach((callback) => {
callback(event);
});
}
/**
* Fires a ViewUpdate event when, for example, the month view is changed.
* @private
*/
_viewUpdate() {
this._triggerEvent({
type: Namespace.events.update,
viewDate: this.optionsStore.viewDate.clone,
});
}
_unsubscribe(eventName, index) {
this._subscribers[eventName].splice(index, 1);
}
/**
* Merges two Option objects together and validates options type
* @param config new Options
* @param mergeTo Options to merge into
* @param includeDataset When true, the elements data-td attributes will be included in the
* @private
*/
_initializeOptions(config, mergeTo, includeDataset = false) {
let newConfig = OptionConverter.deepCopy(config);
newConfig = OptionConverter._mergeOptions(newConfig, mergeTo);
if (includeDataset)
newConfig = OptionConverter._dataToOptions(this.optionsStore.element, newConfig);
OptionConverter._validateConflicts(newConfig);
newConfig.viewDate = newConfig.viewDate.setLocale(newConfig.localization.locale);
if (!this.optionsStore.viewDate.isSame(newConfig.viewDate)) {
this.optionsStore.viewDate = newConfig.viewDate;
}
/**
* Sets the minimum view allowed by the picker. For example the case of only
* allowing year and month to be selected but not date.
*/
if (newConfig.display.components.year) {
this.optionsStore.minimumCalendarViewMode = 2;
}
if (newConfig.display.components.month) {
this.optionsStore.minimumCalendarViewMode = 1;
}
if (newConfig.display.components.date) {
this.optionsStore.minimumCalendarViewMode = 0;
}
this.optionsStore.currentCalendarViewMode = Math.max(this.optionsStore.minimumCalendarViewMode, this.optionsStore.currentCalendarViewMode);
// Update view mode if needed
if (CalendarModes[this.optionsStore.currentCalendarViewMode].name !==
newConfig.display.viewMode) {
this.optionsStore.currentCalendarViewMode = Math.max(CalendarModes.findIndex((x) => x.name === newConfig.display.viewMode), this.optionsStore.minimumCalendarViewMode);
}
if (this.display?.isVisible) {
this.display._update('all');
}
if (newConfig.display.components.useTwentyfourHour &&
newConfig.localization.hourCycle === undefined)
newConfig.localization.hourCycle = 'h24';
else if (newConfig.localization.hourCycle === undefined) {
newConfig.localization.hourCycle = guessHourCycle(newConfig.localization.locale);
}
this.optionsStore.options = newConfig;
}
/**
* Checks if an input field is being used, attempts to locate one and sets an
* event listener if found.
* @private
*/
_initializeInput() {
if (this.optionsStore.element.tagName == 'INPUT') {
this.optionsStore.input = this.optionsStore.element;
}
else {
const query = this.optionsStore.element.dataset.tdTargetInput;
if (query == undefined || query == 'nearest') {
this.optionsStore.input =
this.optionsStore.element.querySelector('input');
}
else {
this.optionsStore.input =
this.optionsStore.element.querySelector(query);
}
}
if (!this.optionsStore.input)
return;
if (!this.optionsStore.input.value && this.optionsStore.options.defaultDate)
this.optionsStore.input.value = this.dates.formatInput(this.optionsStore.options.defaultDate);
this.optionsStore.input.addEventListener('change', this._inputChangeEvent);
if (this.optionsStore.options.allowInputToggle) {
this.optionsStore.input.addEventListener('click', this._toggleClickEvent);
}
if (this.optionsStore.input.value) {
this._inputChangeEvent();
}
}
/**
* Attempts to locate a toggle for the picker and sets an event listener
* @private
*/
_initializeToggle() {
if (this.optionsStore.options.display.inline)
return;
let query = this.optionsStore.element.dataset.tdTargetToggle;
if (query == 'nearest') {
query = '[data-td-toggle="datetimepicker"]';
}
this._toggle =
query == undefined
? this.optionsStore.element
: this.optionsStore.element.querySelector(query);
this._toggle.addEventListener('click', this._toggleClickEvent);
}
/**
* If the option is enabled this will render the clock view after a date pick.
* @param e change event
* @private
*/
_handleAfterChangeEvent(e) {
if (
// options is disabled
!this.optionsStore.options.promptTimeOnDateChange ||
this.optionsStore.options.display.inline ||
this.optionsStore.options.display.sideBySide ||
// time is disabled
!this.display._hasTime ||
// clock component is already showing
this.display.widget
?.getElementsByClassName(Namespace.css.show)[0]
.classList.contains(Namespace.css.timeContainer))
return;
// First time ever. If useCurrent option is set to true (default), do nothing
// because the first date is selected automatically.
// or date didn't change (time did) or date changed because time did.
if ((!e.oldDate && this.optionsStore.options.useCurrent) ||
(e.oldDate && e.date?.isSame(e.oldDate))) {
return;
}
clearTimeout(this._currentPromptTimeTimeout);
this._currentPromptTimeTimeout = setTimeout(() => {
if (this.display.widget) {
this._eventEmitters.action.emit({
e: {
currentTarget: this.display.widget.querySelector(`.${Namespace.css.switch}`),
},
action: ActionTypes$1.togglePicker,
});
}
}, this.optionsStore.options.promptTimeOnDateChangeTransitionDelay);
}
}
/**
* Whenever a locale is loaded via a plugin then store it here based on the
* locale name. E.g. loadedLocales['ru']
*/
const loadedLocales = {};
// noinspection JSUnusedGlobalSymbols
/**
* Called from a locale plugin.
* @param l locale object for localization options
*/
const loadLocale = (l) => {
if (loadedLocales[l.name])
return;
loadedLocales[l.name] = l.localization;
};
/**
* A sets the global localization options to the provided locale name.
* `loadLocale` MUST be called first.
* @param l
*/
const locale = (l) => {
const asked = loadedLocales[l];
if (!asked)
return;
DefaultOptions.localization = asked;
};
// noinspection JSUnusedGlobalSymbols
/**
* Called from a plugin to extend or override picker defaults.
* @param plugin
* @param option
*/
const extend = function (plugin, option = undefined) {
if (!plugin)
return tempusDominus;
if (!plugin.installed) {
// install plugin only once
plugin(option, { TempusDominus, Dates, Display, DateTime, Namespace }, tempusDominus);
plugin.installed = true;
}
return tempusDominus;
};
const version = '6.2.10';
const tempusDominus = {
TempusDominus,
extend,
loadLocale,
locale,
Namespace,
DefaultOptions,
DateTime,
Unit: exports.Unit,
version,
};
exports.DateTime = DateTime;
exports.DefaultOptions = DefaultOptions;
exports.Namespace = Namespace;
exports.TempusDominus = TempusDominus;
exports.extend = extend;
exports.loadLocale = loadLocale;
exports.locale = locale;
exports.version = version;
Object.defineProperty(exports, '__esModule', { value: true });
}));
//# sourceMappingURL=tempus-dominus.js.map
/*!
Waypoints - 4.0.1
Copyright © 2011-2016 Caleb Troughton
Licensed under the MIT license.
https://github.com/imakewebthings/waypoints/blob/master/licenses.txt
*/
(function() {
'use strict'
var keyCounter = 0
var allWaypoints = {}
/* http://imakewebthings.com/waypoints/api/waypoint */
function Waypoint(options) {
if (!options) {
throw new Error('No options passed to Waypoint constructor')
}
if (!options.element) {
throw new Error('No element option passed to Waypoint constructor')
}
if (!options.handler) {
throw new Error('No handler option passed to Waypoint constructor')
}
this.key = 'waypoint-' + keyCounter
this.options = Waypoint.Adapter.extend({}, Waypoint.defaults, options)
this.element = this.options.element
this.adapter = new Waypoint.Adapter(this.element)
this.callback = options.handler
this.axis = this.options.horizontal ? 'horizontal' : 'vertical'
this.enabled = this.options.enabled
this.triggerPoint = null
this.group = Waypoint.Group.findOrCreate({
name: this.options.group,
axis: this.axis
})
this.context = Waypoint.Context.findOrCreateByElement(this.options.context)
if (Waypoint.offsetAliases[this.options.offset]) {
this.options.offset = Waypoint.offsetAliases[this.options.offset]
}
this.group.add(this)
this.context.add(this)
allWaypoints[this.key] = this
keyCounter += 1
}
/* Private */
Waypoint.prototype.queueTrigger = function(direction) {
this.group.queueTrigger(this, direction)
}
/* Private */
Waypoint.prototype.trigger = function(args) {
if (!this.enabled) {
return
}
if (this.callback) {
this.callback.apply(this, args)
}
}
/* Public */
/* http://imakewebthings.com/waypoints/api/destroy */
Waypoint.prototype.destroy = function() {
this.context.remove(this)
this.group.remove(this)
delete allWaypoints[this.key]
}
/* Public */
/* http://imakewebthings.com/waypoints/api/disable */
Waypoint.prototype.disable = function() {
this.enabled = false
return this
}
/* Public */
/* http://imakewebthings.com/waypoints/api/enable */
Waypoint.prototype.enable = function() {
this.context.refresh()
this.enabled = true
return this
}
/* Public */
/* http://imakewebthings.com/waypoints/api/next */
Waypoint.prototype.next = function() {
return this.group.next(this)
}
/* Public */
/* http://imakewebthings.com/waypoints/api/previous */
Waypoint.prototype.previous = function() {
return this.group.previous(this)
}
/* Private */
Waypoint.invokeAll = function(method) {
var allWaypointsArray = []
for (var waypointKey in allWaypoints) {
allWaypointsArray.push(allWaypoints[waypointKey])
}
for (var i = 0, end = allWaypointsArray.length; i < end; i++) {
allWaypointsArray[i][method]()
}
}
/* Public */
/* http://imakewebthings.com/waypoints/api/destroy-all */
Waypoint.destroyAll = function() {
Waypoint.invokeAll('destroy')
}
/* Public */
/* http://imakewebthings.com/waypoints/api/disable-all */
Waypoint.disableAll = function() {
Waypoint.invokeAll('disable')
}
/* Public */
/* http://imakewebthings.com/waypoints/api/enable-all */
Waypoint.enableAll = function() {
Waypoint.Context.refreshAll()
for (var waypointKey in allWaypoints) {
allWaypoints[waypointKey].enabled = true
}
return this
}
/* Public */
/* http://imakewebthings.com/waypoints/api/refresh-all */
Waypoint.refreshAll = function() {
Waypoint.Context.refreshAll()
}
/* Public */
/* http://imakewebthings.com/waypoints/api/viewport-height */
Waypoint.viewportHeight = function() {
return window.innerHeight || document.documentElement.clientHeight
}
/* Public */
/* http://imakewebthings.com/waypoints/api/viewport-width */
Waypoint.viewportWidth = function() {
return document.documentElement.clientWidth
}
Waypoint.adapters = []
Waypoint.defaults = {
context: window,
continuous: true,
enabled: true,
group: 'default',
horizontal: false,
offset: 0
}
Waypoint.offsetAliases = {
'bottom-in-view': function() {
return this.context.innerHeight() - this.adapter.outerHeight()
},
'right-in-view': function() {
return this.context.innerWidth() - this.adapter.outerWidth()
}
}
window.Waypoint = Waypoint
}())
;(function() {
'use strict'
function requestAnimationFrameShim(callback) {
window.setTimeout(callback, 1000 / 60)
}
var keyCounter = 0
var contexts = {}
var Waypoint = window.Waypoint
var oldWindowLoad = window.onload
/* http://imakewebthings.com/waypoints/api/context */
function Context(element) {
this.element = element
this.Adapter = Waypoint.Adapter
this.adapter = new this.Adapter(element)
this.key = 'waypoint-context-' + keyCounter
this.didScroll = false
this.didResize = false
this.oldScroll = {
x: this.adapter.scrollLeft(),
y: this.adapter.scrollTop()
}
this.waypoints = {
vertical: {},
horizontal: {}
}
element.waypointContextKey = this.key
contexts[element.waypointContextKey] = this
keyCounter += 1
if (!Waypoint.windowContext) {
Waypoint.windowContext = true
Waypoint.windowContext = new Context(window)
}
this.createThrottledScrollHandler()
this.createThrottledResizeHandler()
}
/* Private */
Context.prototype.add = function(waypoint) {
var axis = waypoint.options.horizontal ? 'horizontal' : 'vertical'
this.waypoints[axis][waypoint.key] = waypoint
this.refresh()
}
/* Private */
Context.prototype.checkEmpty = function() {
var horizontalEmpty = this.Adapter.isEmptyObject(this.waypoints.horizontal)
var verticalEmpty = this.Adapter.isEmptyObject(this.waypoints.vertical)
var isWindow = this.element == this.element.window
if (horizontalEmpty && verticalEmpty && !isWindow) {
this.adapter.off('.waypoints')
delete contexts[this.key]
}
}
/* Private */
Context.prototype.createThrottledResizeHandler = function() {
var self = this
function resizeHandler() {
self.handleResize()
self.didResize = false
}
this.adapter.on('resize.waypoints', function() {
if (!self.didResize) {
self.didResize = true
Waypoint.requestAnimationFrame(resizeHandler)
}
})
}
/* Private */
Context.prototype.createThrottledScrollHandler = function() {
var self = this
function scrollHandler() {
self.handleScroll()
self.didScroll = false
}
this.adapter.on('scroll.waypoints', function() {
if (!self.didScroll || Waypoint.isTouch) {
self.didScroll = true
Waypoint.requestAnimationFrame(scrollHandler)
}
})
}
/* Private */
Context.prototype.handleResize = function() {
Waypoint.Context.refreshAll()
}
/* Private */
Context.prototype.handleScroll = function() {
var triggeredGroups = {}
var axes = {
horizontal: {
newScroll: this.adapter.scrollLeft(),
oldScroll: this.oldScroll.x,
forward: 'right',
backward: 'left'
},
vertical: {
newScroll: this.adapter.scrollTop(),
oldScroll: this.oldScroll.y,
forward: 'down',
backward: 'up'
}
}
for (var axisKey in axes) {
var axis = axes[axisKey]
var isForward = axis.newScroll > axis.oldScroll
var direction = isForward ? axis.forward : axis.backward
for (var waypointKey in this.waypoints[axisKey]) {
var waypoint = this.waypoints[axisKey][waypointKey]
if (waypoint.triggerPoint === null) {
continue
}
var wasBeforeTriggerPoint = axis.oldScroll < waypoint.triggerPoint
var nowAfterTriggerPoint = axis.newScroll >= waypoint.triggerPoint
var crossedForward = wasBeforeTriggerPoint && nowAfterTriggerPoint
var crossedBackward = !wasBeforeTriggerPoint && !nowAfterTriggerPoint
if (crossedForward || crossedBackward) {
waypoint.queueTrigger(direction)
triggeredGroups[waypoint.group.id] = waypoint.group
}
}
}
for (var groupKey in triggeredGroups) {
triggeredGroups[groupKey].flushTriggers()
}
this.oldScroll = {
x: axes.horizontal.newScroll,
y: axes.vertical.newScroll
}
}
/* Private */
Context.prototype.innerHeight = function() {
/*eslint-disable eqeqeq */
if (this.element == this.element.window) {
return Waypoint.viewportHeight()
}
/*eslint-enable eqeqeq */
return this.adapter.innerHeight()
}
/* Private */
Context.prototype.remove = function(waypoint) {
delete this.waypoints[waypoint.axis][waypoint.key]
this.checkEmpty()
}
/* Private */
Context.prototype.innerWidth = function() {
/*eslint-disable eqeqeq */
if (this.element == this.element.window) {
return Waypoint.viewportWidth()
}
/*eslint-enable eqeqeq */
return this.adapter.innerWidth()
}
/* Public */
/* http://imakewebthings.com/waypoints/api/context-destroy */
Context.prototype.destroy = function() {
var allWaypoints = []
for (var axis in this.waypoints) {
for (var waypointKey in this.waypoints[axis]) {
allWaypoints.push(this.waypoints[axis][waypointKey])
}
}
for (var i = 0, end = allWaypoints.length; i < end; i++) {
allWaypoints[i].destroy()
}
}
/* Public */
/* http://imakewebthings.com/waypoints/api/context-refresh */
Context.prototype.refresh = function() {
/*eslint-disable eqeqeq */
var isWindow = this.element == this.element.window
/*eslint-enable eqeqeq */
var contextOffset = isWindow ? undefined : this.adapter.offset()
var triggeredGroups = {}
var axes
this.handleScroll()
axes = {
horizontal: {
contextOffset: isWindow ? 0 : contextOffset.left,
contextScroll: isWindow ? 0 : this.oldScroll.x,
contextDimension: this.innerWidth(),
oldScroll: this.oldScroll.x,
forward: 'right',
backward: 'left',
offsetProp: 'left'
},
vertical: {
contextOffset: isWindow ? 0 : contextOffset.top,
contextScroll: isWindow ? 0 : this.oldScroll.y,
contextDimension: this.innerHeight(),
oldScroll: this.oldScroll.y,
forward: 'down',
backward: 'up',
offsetProp: 'top'
}
}
for (var axisKey in axes) {
var axis = axes[axisKey]
for (var waypointKey in this.waypoints[axisKey]) {
var waypoint = this.waypoints[axisKey][waypointKey]
var adjustment = waypoint.options.offset
var oldTriggerPoint = waypoint.triggerPoint
var elementOffset = 0
var freshWaypoint = oldTriggerPoint == null
var contextModifier, wasBeforeScroll, nowAfterScroll
var triggeredBackward, triggeredForward
if (waypoint.element !== waypoint.element.window) {
elementOffset = waypoint.adapter.offset()[axis.offsetProp]
}
if (typeof adjustment === 'function') {
adjustment = adjustment.apply(waypoint)
}
else if (typeof adjustment === 'string') {
adjustment = parseFloat(adjustment)
if (waypoint.options.offset.indexOf('%') > - 1) {
adjustment = Math.ceil(axis.contextDimension * adjustment / 100)
}
}
contextModifier = axis.contextScroll - axis.contextOffset
waypoint.triggerPoint = Math.floor(elementOffset + contextModifier - adjustment)
wasBeforeScroll = oldTriggerPoint < axis.oldScroll
nowAfterScroll = waypoint.triggerPoint >= axis.oldScroll
triggeredBackward = wasBeforeScroll && nowAfterScroll
triggeredForward = !wasBeforeScroll && !nowAfterScroll
if (!freshWaypoint && triggeredBackward) {
waypoint.queueTrigger(axis.backward)
triggeredGroups[waypoint.group.id] = waypoint.group
}
else if (!freshWaypoint && triggeredForward) {
waypoint.queueTrigger(axis.forward)
triggeredGroups[waypoint.group.id] = waypoint.group
}
else if (freshWaypoint && axis.oldScroll >= waypoint.triggerPoint) {
waypoint.queueTrigger(axis.forward)
triggeredGroups[waypoint.group.id] = waypoint.group
}
}
}
Waypoint.requestAnimationFrame(function() {
for (var groupKey in triggeredGroups) {
triggeredGroups[groupKey].flushTriggers()
}
})
return this
}
/* Private */
Context.findOrCreateByElement = function(element) {
return Context.findByElement(element) || new Context(element)
}
/* Private */
Context.refreshAll = function() {
for (var contextId in contexts) {
contexts[contextId].refresh()
}
}
/* Public */
/* http://imakewebthings.com/waypoints/api/context-find-by-element */
Context.findByElement = function(element) {
return contexts[element.waypointContextKey]
}
window.onload = function() {
if (oldWindowLoad) {
oldWindowLoad()
}
Context.refreshAll()
}
Waypoint.requestAnimationFrame = function(callback) {
var requestFn = window.requestAnimationFrame ||
window.mozRequestAnimationFrame ||
window.webkitRequestAnimationFrame ||
requestAnimationFrameShim
requestFn.call(window, callback)
}
Waypoint.Context = Context
}())
;(function() {
'use strict'
function byTriggerPoint(a, b) {
return a.triggerPoint - b.triggerPoint
}
function byReverseTriggerPoint(a, b) {
return b.triggerPoint - a.triggerPoint
}
var groups = {
vertical: {},
horizontal: {}
}
var Waypoint = window.Waypoint
/* http://imakewebthings.com/waypoints/api/group */
function Group(options) {
this.name = options.name
this.axis = options.axis
this.id = this.name + '-' + this.axis
this.waypoints = []
this.clearTriggerQueues()
groups[this.axis][this.name] = this
}
/* Private */
Group.prototype.add = function(waypoint) {
this.waypoints.push(waypoint)
}
/* Private */
Group.prototype.clearTriggerQueues = function() {
this.triggerQueues = {
up: [],
down: [],
left: [],
right: []
}
}
/* Private */
Group.prototype.flushTriggers = function() {
for (var direction in this.triggerQueues) {
var waypoints = this.triggerQueues[direction]
var reverse = direction === 'up' || direction === 'left'
waypoints.sort(reverse ? byReverseTriggerPoint : byTriggerPoint)
for (var i = 0, end = waypoints.length; i < end; i += 1) {
var waypoint = waypoints[i]
if (waypoint.options.continuous || i === waypoints.length - 1) {
waypoint.trigger([direction])
}
}
}
this.clearTriggerQueues()
}
/* Private */
Group.prototype.next = function(waypoint) {
this.waypoints.sort(byTriggerPoint)
var index = Waypoint.Adapter.inArray(waypoint, this.waypoints)
var isLast = index === this.waypoints.length - 1
return isLast ? null : this.waypoints[index + 1]
}
/* Private */
Group.prototype.previous = function(waypoint) {
this.waypoints.sort(byTriggerPoint)
var index = Waypoint.Adapter.inArray(waypoint, this.waypoints)
return index ? this.waypoints[index - 1] : null
}
/* Private */
Group.prototype.queueTrigger = function(waypoint, direction) {
this.triggerQueues[direction].push(waypoint)
}
/* Private */
Group.prototype.remove = function(waypoint) {
var index = Waypoint.Adapter.inArray(waypoint, this.waypoints)
if (index > -1) {
this.waypoints.splice(index, 1)
}
}
/* Public */
/* http://imakewebthings.com/waypoints/api/first */
Group.prototype.first = function() {
return this.waypoints[0]
}
/* Public */
/* http://imakewebthings.com/waypoints/api/last */
Group.prototype.last = function() {
return this.waypoints[this.waypoints.length - 1]
}
/* Private */
Group.findOrCreate = function(options) {
return groups[options.axis][options.name] || new Group(options)
}
Waypoint.Group = Group
}())
;(function() {
'use strict'
var $ = window.jQuery
var Waypoint = window.Waypoint
function JQueryAdapter(element) {
this.$element = $(element)
}
$.each([
'innerHeight',
'innerWidth',
'off',
'offset',
'on',
'outerHeight',
'outerWidth',
'scrollLeft',
'scrollTop'
], function(i, method) {
JQueryAdapter.prototype[method] = function() {
var args = Array.prototype.slice.call(arguments)
return this.$element[method].apply(this.$element, args)
}
})
$.each([
'extend',
'inArray',
'isEmptyObject'
], function(i, method) {
JQueryAdapter[method] = $[method]
})
Waypoint.adapters.push({
name: 'jquery',
Adapter: JQueryAdapter
})
Waypoint.Adapter = JQueryAdapter
}())
;(function() {
'use strict'
var Waypoint = window.Waypoint
function createExtension(framework) {
return function() {
var waypoints = []
var overrides = arguments[0]
// framework.isFunction(arguments[0])
// Change the isFunction as it's causing jQuery migrate warnings.
if (typeof arguments[0] === "function") {
overrides = framework.extend({}, arguments[1])
overrides.handler = arguments[0]
}
this.each(function() {
var options = framework.extend({}, overrides, {
element: this
})
if (typeof options.context === 'string') {
options.context = framework(this).closest(options.context)[0]
}
waypoints.push(new Waypoint(options))
})
return waypoints
}
}
if (window.jQuery) {
window.jQuery.fn.waypoint = createExtension(window.jQuery)
}
if (window.Zepto) {
window.Zepto.fn.waypoint = createExtension(window.Zepto)
}
}())
;
/*! Lity - v2.4.1 - 2020-04-26
* http://sorgalla.com/lity/
* Copyright (c) 2015-2020 Jan Sorgalla; Licensed MIT */
(function(window, factory) {
if (typeof define === 'function' && define.amd) {
define(['jquery'], function($) {
return factory(window, $);
});
} else if (typeof module === 'object' && typeof module.exports === 'object') {
module.exports = factory(window, require('jquery'));
} else {
window.lity = factory(window, window.jQuery || window.Zepto);
}
}(typeof window !== "undefined" ? window : this, function(window, $) {
'use strict';
var document = window.document;
var _win = $(window);
var _deferred = $.Deferred;
var _html = $('html');
var _instances = [];
var _attrAriaHidden = 'aria-hidden';
var _dataAriaHidden = 'lity-' + _attrAriaHidden;
var _focusableElementsSelector = 'a[href],area[href],input:not([disabled]),select:not([disabled]),textarea:not([disabled]),button:not([disabled]),iframe,object,embed,[contenteditable],[tabindex]:not([tabindex^="-"])';
var _defaultOptions = {
esc: true,
handler: null,
handlers: {
image: imageHandler,
inline: inlineHandler,
youtube: youtubeHandler,
vimeo: vimeoHandler,
googlemaps: googlemapsHandler,
facebookvideo: facebookvideoHandler,
iframe: iframeHandler
},
template: '