modal.js 26 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888
  1. /*!
  2. * Bootstrap modal.js v5.0.2 (https://getbootstrap.com/)
  3. * Copyright 2011-2021 The Bootstrap Authors (https://github.com/twbs/bootstrap/graphs/contributors)
  4. * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
  5. */
  6. (function (global, factory) {
  7. typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory(require('./dom/selector-engine.js'), require('./dom/event-handler.js'), require('./dom/manipulator.js'), require('./base-component.js')) :
  8. typeof define === 'function' && define.amd ? define(['./dom/selector-engine', './dom/event-handler', './dom/manipulator', './base-component'], factory) :
  9. (global = typeof globalThis !== 'undefined' ? globalThis : global || self, global.Modal = factory(global.SelectorEngine, global.EventHandler, global.Manipulator, global.Base));
  10. }(this, (function (SelectorEngine, EventHandler, Manipulator, BaseComponent) { 'use strict';
  11. function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; }
  12. var SelectorEngine__default = /*#__PURE__*/_interopDefaultLegacy(SelectorEngine);
  13. var EventHandler__default = /*#__PURE__*/_interopDefaultLegacy(EventHandler);
  14. var Manipulator__default = /*#__PURE__*/_interopDefaultLegacy(Manipulator);
  15. var BaseComponent__default = /*#__PURE__*/_interopDefaultLegacy(BaseComponent);
  16. const MILLISECONDS_MULTIPLIER = 1000;
  17. const TRANSITION_END = 'transitionend'; // Shoutout AngusCroll (https://goo.gl/pxwQGp)
  18. const toType = obj => {
  19. if (obj === null || obj === undefined) {
  20. return `${obj}`;
  21. }
  22. return {}.toString.call(obj).match(/\s([a-z]+)/i)[1].toLowerCase();
  23. };
  24. const getSelector = element => {
  25. let selector = element.getAttribute('data-bs-target');
  26. if (!selector || selector === '#') {
  27. let hrefAttr = element.getAttribute('href'); // The only valid content that could double as a selector are IDs or classes,
  28. // so everything starting with `#` or `.`. If a "real" URL is used as the selector,
  29. // `document.querySelector` will rightfully complain it is invalid.
  30. // See https://github.com/twbs/bootstrap/issues/32273
  31. if (!hrefAttr || !hrefAttr.includes('#') && !hrefAttr.startsWith('.')) {
  32. return null;
  33. } // Just in case some CMS puts out a full URL with the anchor appended
  34. if (hrefAttr.includes('#') && !hrefAttr.startsWith('#')) {
  35. hrefAttr = `#${hrefAttr.split('#')[1]}`;
  36. }
  37. selector = hrefAttr && hrefAttr !== '#' ? hrefAttr.trim() : null;
  38. }
  39. return selector;
  40. };
  41. const getElementFromSelector = element => {
  42. const selector = getSelector(element);
  43. return selector ? document.querySelector(selector) : null;
  44. };
  45. const getTransitionDurationFromElement = element => {
  46. if (!element) {
  47. return 0;
  48. } // Get transition-duration of the element
  49. let {
  50. transitionDuration,
  51. transitionDelay
  52. } = window.getComputedStyle(element);
  53. const floatTransitionDuration = Number.parseFloat(transitionDuration);
  54. const floatTransitionDelay = Number.parseFloat(transitionDelay); // Return 0 if element or transition duration is not found
  55. if (!floatTransitionDuration && !floatTransitionDelay) {
  56. return 0;
  57. } // If multiple durations are defined, take the first
  58. transitionDuration = transitionDuration.split(',')[0];
  59. transitionDelay = transitionDelay.split(',')[0];
  60. return (Number.parseFloat(transitionDuration) + Number.parseFloat(transitionDelay)) * MILLISECONDS_MULTIPLIER;
  61. };
  62. const triggerTransitionEnd = element => {
  63. element.dispatchEvent(new Event(TRANSITION_END));
  64. };
  65. const isElement = obj => {
  66. if (!obj || typeof obj !== 'object') {
  67. return false;
  68. }
  69. if (typeof obj.jquery !== 'undefined') {
  70. obj = obj[0];
  71. }
  72. return typeof obj.nodeType !== 'undefined';
  73. };
  74. const getElement = obj => {
  75. if (isElement(obj)) {
  76. // it's a jQuery object or a node element
  77. return obj.jquery ? obj[0] : obj;
  78. }
  79. if (typeof obj === 'string' && obj.length > 0) {
  80. return SelectorEngine__default['default'].findOne(obj);
  81. }
  82. return null;
  83. };
  84. const typeCheckConfig = (componentName, config, configTypes) => {
  85. Object.keys(configTypes).forEach(property => {
  86. const expectedTypes = configTypes[property];
  87. const value = config[property];
  88. const valueType = value && isElement(value) ? 'element' : toType(value);
  89. if (!new RegExp(expectedTypes).test(valueType)) {
  90. throw new TypeError(`${componentName.toUpperCase()}: Option "${property}" provided type "${valueType}" but expected type "${expectedTypes}".`);
  91. }
  92. });
  93. };
  94. const isVisible = element => {
  95. if (!isElement(element) || element.getClientRects().length === 0) {
  96. return false;
  97. }
  98. return getComputedStyle(element).getPropertyValue('visibility') === 'visible';
  99. };
  100. const reflow = element => element.offsetHeight;
  101. const getjQuery = () => {
  102. const {
  103. jQuery
  104. } = window;
  105. if (jQuery && !document.body.hasAttribute('data-bs-no-jquery')) {
  106. return jQuery;
  107. }
  108. return null;
  109. };
  110. const DOMContentLoadedCallbacks = [];
  111. const onDOMContentLoaded = callback => {
  112. if (document.readyState === 'loading') {
  113. // add listener on the first call when the document is in loading state
  114. if (!DOMContentLoadedCallbacks.length) {
  115. document.addEventListener('DOMContentLoaded', () => {
  116. DOMContentLoadedCallbacks.forEach(callback => callback());
  117. });
  118. }
  119. DOMContentLoadedCallbacks.push(callback);
  120. } else {
  121. callback();
  122. }
  123. };
  124. const isRTL = () => document.documentElement.dir === 'rtl';
  125. const defineJQueryPlugin = plugin => {
  126. onDOMContentLoaded(() => {
  127. const $ = getjQuery();
  128. /* istanbul ignore if */
  129. if ($) {
  130. const name = plugin.NAME;
  131. const JQUERY_NO_CONFLICT = $.fn[name];
  132. $.fn[name] = plugin.jQueryInterface;
  133. $.fn[name].Constructor = plugin;
  134. $.fn[name].noConflict = () => {
  135. $.fn[name] = JQUERY_NO_CONFLICT;
  136. return plugin.jQueryInterface;
  137. };
  138. }
  139. });
  140. };
  141. const execute = callback => {
  142. if (typeof callback === 'function') {
  143. callback();
  144. }
  145. };
  146. const executeAfterTransition = (callback, transitionElement, waitForTransition = true) => {
  147. if (!waitForTransition) {
  148. execute(callback);
  149. return;
  150. }
  151. const durationPadding = 5;
  152. const emulatedDuration = getTransitionDurationFromElement(transitionElement) + durationPadding;
  153. let called = false;
  154. const handler = ({
  155. target
  156. }) => {
  157. if (target !== transitionElement) {
  158. return;
  159. }
  160. called = true;
  161. transitionElement.removeEventListener(TRANSITION_END, handler);
  162. execute(callback);
  163. };
  164. transitionElement.addEventListener(TRANSITION_END, handler);
  165. setTimeout(() => {
  166. if (!called) {
  167. triggerTransitionEnd(transitionElement);
  168. }
  169. }, emulatedDuration);
  170. };
  171. /**
  172. * --------------------------------------------------------------------------
  173. * Bootstrap (v5.0.2): util/scrollBar.js
  174. * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
  175. * --------------------------------------------------------------------------
  176. */
  177. const SELECTOR_FIXED_CONTENT = '.fixed-top, .fixed-bottom, .is-fixed, .sticky-top';
  178. const SELECTOR_STICKY_CONTENT = '.sticky-top';
  179. class ScrollBarHelper {
  180. constructor() {
  181. this._element = document.body;
  182. }
  183. getWidth() {
  184. // https://developer.mozilla.org/en-US/docs/Web/API/Window/innerWidth#usage_notes
  185. const documentWidth = document.documentElement.clientWidth;
  186. return Math.abs(window.innerWidth - documentWidth);
  187. }
  188. hide() {
  189. const width = this.getWidth();
  190. this._disableOverFlow(); // give padding to element to balance the hidden scrollbar width
  191. this._setElementAttributes(this._element, 'paddingRight', calculatedValue => calculatedValue + width); // trick: We adjust positive paddingRight and negative marginRight to sticky-top elements to keep showing fullwidth
  192. this._setElementAttributes(SELECTOR_FIXED_CONTENT, 'paddingRight', calculatedValue => calculatedValue + width);
  193. this._setElementAttributes(SELECTOR_STICKY_CONTENT, 'marginRight', calculatedValue => calculatedValue - width);
  194. }
  195. _disableOverFlow() {
  196. this._saveInitialAttribute(this._element, 'overflow');
  197. this._element.style.overflow = 'hidden';
  198. }
  199. _setElementAttributes(selector, styleProp, callback) {
  200. const scrollbarWidth = this.getWidth();
  201. const manipulationCallBack = element => {
  202. if (element !== this._element && window.innerWidth > element.clientWidth + scrollbarWidth) {
  203. return;
  204. }
  205. this._saveInitialAttribute(element, styleProp);
  206. const calculatedValue = window.getComputedStyle(element)[styleProp];
  207. element.style[styleProp] = `${callback(Number.parseFloat(calculatedValue))}px`;
  208. };
  209. this._applyManipulationCallback(selector, manipulationCallBack);
  210. }
  211. reset() {
  212. this._resetElementAttributes(this._element, 'overflow');
  213. this._resetElementAttributes(this._element, 'paddingRight');
  214. this._resetElementAttributes(SELECTOR_FIXED_CONTENT, 'paddingRight');
  215. this._resetElementAttributes(SELECTOR_STICKY_CONTENT, 'marginRight');
  216. }
  217. _saveInitialAttribute(element, styleProp) {
  218. const actualValue = element.style[styleProp];
  219. if (actualValue) {
  220. Manipulator__default['default'].setDataAttribute(element, styleProp, actualValue);
  221. }
  222. }
  223. _resetElementAttributes(selector, styleProp) {
  224. const manipulationCallBack = element => {
  225. const value = Manipulator__default['default'].getDataAttribute(element, styleProp);
  226. if (typeof value === 'undefined') {
  227. element.style.removeProperty(styleProp);
  228. } else {
  229. Manipulator__default['default'].removeDataAttribute(element, styleProp);
  230. element.style[styleProp] = value;
  231. }
  232. };
  233. this._applyManipulationCallback(selector, manipulationCallBack);
  234. }
  235. _applyManipulationCallback(selector, callBack) {
  236. if (isElement(selector)) {
  237. callBack(selector);
  238. } else {
  239. SelectorEngine__default['default'].find(selector, this._element).forEach(callBack);
  240. }
  241. }
  242. isOverflowing() {
  243. return this.getWidth() > 0;
  244. }
  245. }
  246. /**
  247. * --------------------------------------------------------------------------
  248. * Bootstrap (v5.0.2): util/backdrop.js
  249. * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
  250. * --------------------------------------------------------------------------
  251. */
  252. const Default$1 = {
  253. isVisible: true,
  254. // if false, we use the backdrop helper without adding any element to the dom
  255. isAnimated: false,
  256. rootElement: 'body',
  257. // give the choice to place backdrop under different elements
  258. clickCallback: null
  259. };
  260. const DefaultType$1 = {
  261. isVisible: 'boolean',
  262. isAnimated: 'boolean',
  263. rootElement: '(element|string)',
  264. clickCallback: '(function|null)'
  265. };
  266. const NAME$1 = 'backdrop';
  267. const CLASS_NAME_BACKDROP = 'modal-backdrop';
  268. const CLASS_NAME_FADE$1 = 'fade';
  269. const CLASS_NAME_SHOW$1 = 'show';
  270. const EVENT_MOUSEDOWN = `mousedown.bs.${NAME$1}`;
  271. class Backdrop {
  272. constructor(config) {
  273. this._config = this._getConfig(config);
  274. this._isAppended = false;
  275. this._element = null;
  276. }
  277. show(callback) {
  278. if (!this._config.isVisible) {
  279. execute(callback);
  280. return;
  281. }
  282. this._append();
  283. if (this._config.isAnimated) {
  284. reflow(this._getElement());
  285. }
  286. this._getElement().classList.add(CLASS_NAME_SHOW$1);
  287. this._emulateAnimation(() => {
  288. execute(callback);
  289. });
  290. }
  291. hide(callback) {
  292. if (!this._config.isVisible) {
  293. execute(callback);
  294. return;
  295. }
  296. this._getElement().classList.remove(CLASS_NAME_SHOW$1);
  297. this._emulateAnimation(() => {
  298. this.dispose();
  299. execute(callback);
  300. });
  301. } // Private
  302. _getElement() {
  303. if (!this._element) {
  304. const backdrop = document.createElement('div');
  305. backdrop.className = CLASS_NAME_BACKDROP;
  306. if (this._config.isAnimated) {
  307. backdrop.classList.add(CLASS_NAME_FADE$1);
  308. }
  309. this._element = backdrop;
  310. }
  311. return this._element;
  312. }
  313. _getConfig(config) {
  314. config = { ...Default$1,
  315. ...(typeof config === 'object' ? config : {})
  316. }; // use getElement() with the default "body" to get a fresh Element on each instantiation
  317. config.rootElement = getElement(config.rootElement);
  318. typeCheckConfig(NAME$1, config, DefaultType$1);
  319. return config;
  320. }
  321. _append() {
  322. if (this._isAppended) {
  323. return;
  324. }
  325. this._config.rootElement.appendChild(this._getElement());
  326. EventHandler__default['default'].on(this._getElement(), EVENT_MOUSEDOWN, () => {
  327. execute(this._config.clickCallback);
  328. });
  329. this._isAppended = true;
  330. }
  331. dispose() {
  332. if (!this._isAppended) {
  333. return;
  334. }
  335. EventHandler__default['default'].off(this._element, EVENT_MOUSEDOWN);
  336. this._element.remove();
  337. this._isAppended = false;
  338. }
  339. _emulateAnimation(callback) {
  340. executeAfterTransition(callback, this._getElement(), this._config.isAnimated);
  341. }
  342. }
  343. /**
  344. * --------------------------------------------------------------------------
  345. * Bootstrap (v5.0.2): modal.js
  346. * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
  347. * --------------------------------------------------------------------------
  348. */
  349. /**
  350. * ------------------------------------------------------------------------
  351. * Constants
  352. * ------------------------------------------------------------------------
  353. */
  354. const NAME = 'modal';
  355. const DATA_KEY = 'bs.modal';
  356. const EVENT_KEY = `.${DATA_KEY}`;
  357. const DATA_API_KEY = '.data-api';
  358. const ESCAPE_KEY = 'Escape';
  359. const Default = {
  360. backdrop: true,
  361. keyboard: true,
  362. focus: true
  363. };
  364. const DefaultType = {
  365. backdrop: '(boolean|string)',
  366. keyboard: 'boolean',
  367. focus: 'boolean'
  368. };
  369. const EVENT_HIDE = `hide${EVENT_KEY}`;
  370. const EVENT_HIDE_PREVENTED = `hidePrevented${EVENT_KEY}`;
  371. const EVENT_HIDDEN = `hidden${EVENT_KEY}`;
  372. const EVENT_SHOW = `show${EVENT_KEY}`;
  373. const EVENT_SHOWN = `shown${EVENT_KEY}`;
  374. const EVENT_FOCUSIN = `focusin${EVENT_KEY}`;
  375. const EVENT_RESIZE = `resize${EVENT_KEY}`;
  376. const EVENT_CLICK_DISMISS = `click.dismiss${EVENT_KEY}`;
  377. const EVENT_KEYDOWN_DISMISS = `keydown.dismiss${EVENT_KEY}`;
  378. const EVENT_MOUSEUP_DISMISS = `mouseup.dismiss${EVENT_KEY}`;
  379. const EVENT_MOUSEDOWN_DISMISS = `mousedown.dismiss${EVENT_KEY}`;
  380. const EVENT_CLICK_DATA_API = `click${EVENT_KEY}${DATA_API_KEY}`;
  381. const CLASS_NAME_OPEN = 'modal-open';
  382. const CLASS_NAME_FADE = 'fade';
  383. const CLASS_NAME_SHOW = 'show';
  384. const CLASS_NAME_STATIC = 'modal-static';
  385. const SELECTOR_DIALOG = '.modal-dialog';
  386. const SELECTOR_MODAL_BODY = '.modal-body';
  387. const SELECTOR_DATA_TOGGLE = '[data-bs-toggle="modal"]';
  388. const SELECTOR_DATA_DISMISS = '[data-bs-dismiss="modal"]';
  389. /**
  390. * ------------------------------------------------------------------------
  391. * Class Definition
  392. * ------------------------------------------------------------------------
  393. */
  394. class Modal extends BaseComponent__default['default'] {
  395. constructor(element, config) {
  396. super(element);
  397. this._config = this._getConfig(config);
  398. this._dialog = SelectorEngine__default['default'].findOne(SELECTOR_DIALOG, this._element);
  399. this._backdrop = this._initializeBackDrop();
  400. this._isShown = false;
  401. this._ignoreBackdropClick = false;
  402. this._isTransitioning = false;
  403. this._scrollBar = new ScrollBarHelper();
  404. } // Getters
  405. static get Default() {
  406. return Default;
  407. }
  408. static get NAME() {
  409. return NAME;
  410. } // Public
  411. toggle(relatedTarget) {
  412. return this._isShown ? this.hide() : this.show(relatedTarget);
  413. }
  414. show(relatedTarget) {
  415. if (this._isShown || this._isTransitioning) {
  416. return;
  417. }
  418. const showEvent = EventHandler__default['default'].trigger(this._element, EVENT_SHOW, {
  419. relatedTarget
  420. });
  421. if (showEvent.defaultPrevented) {
  422. return;
  423. }
  424. this._isShown = true;
  425. if (this._isAnimated()) {
  426. this._isTransitioning = true;
  427. }
  428. this._scrollBar.hide();
  429. document.body.classList.add(CLASS_NAME_OPEN);
  430. this._adjustDialog();
  431. this._setEscapeEvent();
  432. this._setResizeEvent();
  433. EventHandler__default['default'].on(this._element, EVENT_CLICK_DISMISS, SELECTOR_DATA_DISMISS, event => this.hide(event));
  434. EventHandler__default['default'].on(this._dialog, EVENT_MOUSEDOWN_DISMISS, () => {
  435. EventHandler__default['default'].one(this._element, EVENT_MOUSEUP_DISMISS, event => {
  436. if (event.target === this._element) {
  437. this._ignoreBackdropClick = true;
  438. }
  439. });
  440. });
  441. this._showBackdrop(() => this._showElement(relatedTarget));
  442. }
  443. hide(event) {
  444. if (event && ['A', 'AREA'].includes(event.target.tagName)) {
  445. event.preventDefault();
  446. }
  447. if (!this._isShown || this._isTransitioning) {
  448. return;
  449. }
  450. const hideEvent = EventHandler__default['default'].trigger(this._element, EVENT_HIDE);
  451. if (hideEvent.defaultPrevented) {
  452. return;
  453. }
  454. this._isShown = false;
  455. const isAnimated = this._isAnimated();
  456. if (isAnimated) {
  457. this._isTransitioning = true;
  458. }
  459. this._setEscapeEvent();
  460. this._setResizeEvent();
  461. EventHandler__default['default'].off(document, EVENT_FOCUSIN);
  462. this._element.classList.remove(CLASS_NAME_SHOW);
  463. EventHandler__default['default'].off(this._element, EVENT_CLICK_DISMISS);
  464. EventHandler__default['default'].off(this._dialog, EVENT_MOUSEDOWN_DISMISS);
  465. this._queueCallback(() => this._hideModal(), this._element, isAnimated);
  466. }
  467. dispose() {
  468. [window, this._dialog].forEach(htmlElement => EventHandler__default['default'].off(htmlElement, EVENT_KEY));
  469. this._backdrop.dispose();
  470. super.dispose();
  471. /**
  472. * `document` has 2 events `EVENT_FOCUSIN` and `EVENT_CLICK_DATA_API`
  473. * Do not move `document` in `htmlElements` array
  474. * It will remove `EVENT_CLICK_DATA_API` event that should remain
  475. */
  476. EventHandler__default['default'].off(document, EVENT_FOCUSIN);
  477. }
  478. handleUpdate() {
  479. this._adjustDialog();
  480. } // Private
  481. _initializeBackDrop() {
  482. return new Backdrop({
  483. isVisible: Boolean(this._config.backdrop),
  484. // 'static' option will be translated to true, and booleans will keep their value
  485. isAnimated: this._isAnimated()
  486. });
  487. }
  488. _getConfig(config) {
  489. config = { ...Default,
  490. ...Manipulator__default['default'].getDataAttributes(this._element),
  491. ...(typeof config === 'object' ? config : {})
  492. };
  493. typeCheckConfig(NAME, config, DefaultType);
  494. return config;
  495. }
  496. _showElement(relatedTarget) {
  497. const isAnimated = this._isAnimated();
  498. const modalBody = SelectorEngine__default['default'].findOne(SELECTOR_MODAL_BODY, this._dialog);
  499. if (!this._element.parentNode || this._element.parentNode.nodeType !== Node.ELEMENT_NODE) {
  500. // Don't move modal's DOM position
  501. document.body.appendChild(this._element);
  502. }
  503. this._element.style.display = 'block';
  504. this._element.removeAttribute('aria-hidden');
  505. this._element.setAttribute('aria-modal', true);
  506. this._element.setAttribute('role', 'dialog');
  507. this._element.scrollTop = 0;
  508. if (modalBody) {
  509. modalBody.scrollTop = 0;
  510. }
  511. if (isAnimated) {
  512. reflow(this._element);
  513. }
  514. this._element.classList.add(CLASS_NAME_SHOW);
  515. if (this._config.focus) {
  516. this._enforceFocus();
  517. }
  518. const transitionComplete = () => {
  519. if (this._config.focus) {
  520. this._element.focus();
  521. }
  522. this._isTransitioning = false;
  523. EventHandler__default['default'].trigger(this._element, EVENT_SHOWN, {
  524. relatedTarget
  525. });
  526. };
  527. this._queueCallback(transitionComplete, this._dialog, isAnimated);
  528. }
  529. _enforceFocus() {
  530. EventHandler__default['default'].off(document, EVENT_FOCUSIN); // guard against infinite focus loop
  531. EventHandler__default['default'].on(document, EVENT_FOCUSIN, event => {
  532. if (document !== event.target && this._element !== event.target && !this._element.contains(event.target)) {
  533. this._element.focus();
  534. }
  535. });
  536. }
  537. _setEscapeEvent() {
  538. if (this._isShown) {
  539. EventHandler__default['default'].on(this._element, EVENT_KEYDOWN_DISMISS, event => {
  540. if (this._config.keyboard && event.key === ESCAPE_KEY) {
  541. event.preventDefault();
  542. this.hide();
  543. } else if (!this._config.keyboard && event.key === ESCAPE_KEY) {
  544. this._triggerBackdropTransition();
  545. }
  546. });
  547. } else {
  548. EventHandler__default['default'].off(this._element, EVENT_KEYDOWN_DISMISS);
  549. }
  550. }
  551. _setResizeEvent() {
  552. if (this._isShown) {
  553. EventHandler__default['default'].on(window, EVENT_RESIZE, () => this._adjustDialog());
  554. } else {
  555. EventHandler__default['default'].off(window, EVENT_RESIZE);
  556. }
  557. }
  558. _hideModal() {
  559. this._element.style.display = 'none';
  560. this._element.setAttribute('aria-hidden', true);
  561. this._element.removeAttribute('aria-modal');
  562. this._element.removeAttribute('role');
  563. this._isTransitioning = false;
  564. this._backdrop.hide(() => {
  565. document.body.classList.remove(CLASS_NAME_OPEN);
  566. this._resetAdjustments();
  567. this._scrollBar.reset();
  568. EventHandler__default['default'].trigger(this._element, EVENT_HIDDEN);
  569. });
  570. }
  571. _showBackdrop(callback) {
  572. EventHandler__default['default'].on(this._element, EVENT_CLICK_DISMISS, event => {
  573. if (this._ignoreBackdropClick) {
  574. this._ignoreBackdropClick = false;
  575. return;
  576. }
  577. if (event.target !== event.currentTarget) {
  578. return;
  579. }
  580. if (this._config.backdrop === true) {
  581. this.hide();
  582. } else if (this._config.backdrop === 'static') {
  583. this._triggerBackdropTransition();
  584. }
  585. });
  586. this._backdrop.show(callback);
  587. }
  588. _isAnimated() {
  589. return this._element.classList.contains(CLASS_NAME_FADE);
  590. }
  591. _triggerBackdropTransition() {
  592. const hideEvent = EventHandler__default['default'].trigger(this._element, EVENT_HIDE_PREVENTED);
  593. if (hideEvent.defaultPrevented) {
  594. return;
  595. }
  596. const {
  597. classList,
  598. scrollHeight,
  599. style
  600. } = this._element;
  601. const isModalOverflowing = scrollHeight > document.documentElement.clientHeight; // return if the following background transition hasn't yet completed
  602. if (!isModalOverflowing && style.overflowY === 'hidden' || classList.contains(CLASS_NAME_STATIC)) {
  603. return;
  604. }
  605. if (!isModalOverflowing) {
  606. style.overflowY = 'hidden';
  607. }
  608. classList.add(CLASS_NAME_STATIC);
  609. this._queueCallback(() => {
  610. classList.remove(CLASS_NAME_STATIC);
  611. if (!isModalOverflowing) {
  612. this._queueCallback(() => {
  613. style.overflowY = '';
  614. }, this._dialog);
  615. }
  616. }, this._dialog);
  617. this._element.focus();
  618. } // ----------------------------------------------------------------------
  619. // the following methods are used to handle overflowing modals
  620. // ----------------------------------------------------------------------
  621. _adjustDialog() {
  622. const isModalOverflowing = this._element.scrollHeight > document.documentElement.clientHeight;
  623. const scrollbarWidth = this._scrollBar.getWidth();
  624. const isBodyOverflowing = scrollbarWidth > 0;
  625. if (!isBodyOverflowing && isModalOverflowing && !isRTL() || isBodyOverflowing && !isModalOverflowing && isRTL()) {
  626. this._element.style.paddingLeft = `${scrollbarWidth}px`;
  627. }
  628. if (isBodyOverflowing && !isModalOverflowing && !isRTL() || !isBodyOverflowing && isModalOverflowing && isRTL()) {
  629. this._element.style.paddingRight = `${scrollbarWidth}px`;
  630. }
  631. }
  632. _resetAdjustments() {
  633. this._element.style.paddingLeft = '';
  634. this._element.style.paddingRight = '';
  635. } // Static
  636. static jQueryInterface(config, relatedTarget) {
  637. return this.each(function () {
  638. const data = Modal.getOrCreateInstance(this, config);
  639. if (typeof config !== 'string') {
  640. return;
  641. }
  642. if (typeof data[config] === 'undefined') {
  643. throw new TypeError(`No method named "${config}"`);
  644. }
  645. data[config](relatedTarget);
  646. });
  647. }
  648. }
  649. /**
  650. * ------------------------------------------------------------------------
  651. * Data Api implementation
  652. * ------------------------------------------------------------------------
  653. */
  654. EventHandler__default['default'].on(document, EVENT_CLICK_DATA_API, SELECTOR_DATA_TOGGLE, function (event) {
  655. const target = getElementFromSelector(this);
  656. if (['A', 'AREA'].includes(this.tagName)) {
  657. event.preventDefault();
  658. }
  659. EventHandler__default['default'].one(target, EVENT_SHOW, showEvent => {
  660. if (showEvent.defaultPrevented) {
  661. // only register focus restorer if modal will actually get shown
  662. return;
  663. }
  664. EventHandler__default['default'].one(target, EVENT_HIDDEN, () => {
  665. if (isVisible(this)) {
  666. this.focus();
  667. }
  668. });
  669. });
  670. const data = Modal.getOrCreateInstance(target);
  671. data.toggle(this);
  672. });
  673. /**
  674. * ------------------------------------------------------------------------
  675. * jQuery
  676. * ------------------------------------------------------------------------
  677. * add .Modal to jQuery only if jQuery is present
  678. */
  679. defineJQueryPlugin(Modal);
  680. return Modal;
  681. })));
  682. //# sourceMappingURL=modal.js.map