PageSourceSearch

https://ed-imli.ru/media/vendor/bootstrap/js/modal.js?5.3.8

js ed-imli.ru collected 2026-09-25 03:59:07 UTC 15,190 bytes, 454 lines download raw bytes

vendor: 9,470 bytes, lines 1-308
1import { E as EventHandler, S as SelectorEngine, i as isVisible, e as enableDismissTrigger, d as defineJQueryPlugin, B as BaseComponent, k as ScrollBarHelper, l as Backdrop, F as FocusTrap, r as reflow, c as isRTL } from './dom.js?5.3.8';
2
3/**
4 * --------------------------------------------------------------------------
5 * Bootstrap modal.js
6 * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
7 * --------------------------------------------------------------------------
8 */
9
10
11/**
12 * Constants
13 */
14
15const NAME = 'modal';
16const DATA_KEY = 'bs.modal';
17const EVENT_KEY = `.${DATA_KEY}`;
18const DATA_API_KEY = '.data-api';
19const ESCAPE_KEY = 'Escape';
20const EVENT_HIDE = `hide${EVENT_KEY}`;
21const EVENT_HIDE_PREVENTED = `hidePrevented${EVENT_KEY}`;
22const EVENT_HIDDEN = `hidden${EVENT_KEY}`;
23const EVENT_SHOW = `show${EVENT_KEY}`;
24const EVENT_SHOWN = `shown${EVENT_KEY}`;
25const EVENT_RESIZE = `resize${EVENT_KEY}`;
26const EVENT_CLICK_DISMISS = `click.dismiss${EVENT_KEY}`;
27const EVENT_MOUSEDOWN_DISMISS = `mousedown.dismiss${EVENT_KEY}`;
28const EVENT_KEYDOWN_DISMISS = `keydown.dismiss${EVENT_KEY}`;
29const EVENT_CLICK_DATA_API = `click${EVENT_KEY}${DATA_API_KEY}`;
30const CLASS_NAME_OPEN = 'modal-open';
31const CLASS_NAME_FADE = 'fade';
32const CLASS_NAME_SHOW = 'show';
33const CLASS_NAME_STATIC = 'modal-static';
34const OPEN_SELECTOR = '.modal.show';
35const SELECTOR_DIALOG = '.modal-dialog';
36const SELECTOR_MODAL_BODY = '.modal-body';
37const SELECTOR_DATA_TOGGLE = '[data-bs-toggle="modal"]';
38const Default = {
39  backdrop: true,
40  focus: true,
41  keyboard: true
42};
43const DefaultType = {
44  backdrop: '(boolean|string)',
45  focus: 'boolean',
46  keyboard: 'boolean'
47};
48
49/**
50 * Class definition
51 */
52
53class Modal extends BaseComponent {
54  constructor(element, config) {
55    super(element, config);
56    this._dialog = SelectorEngine.findOne(SELECTOR_DIALOG, this._element);
57    this._backdrop = this._initializeBackDrop();
58    this._focustrap = this._initializeFocusTrap();
59    this._isShown = false;
60    this._isTransitioning = false;
61    this._scrollBar = new ScrollBarHelper();
62    this._addEventListeners();
63  }
64
65  // Getters
66  static get Default() {
67    return Default;
68  }
69  static get DefaultType() {
70    return DefaultType;
71  }
72  static get NAME() {
73    return NAME;
74  }
75
76  // Public
77  toggle(relatedTarget) {
78    return this._isShown ? this.hide() : this.show(relatedTarget);
79  }
80  show(relatedTarget) {
81    if (this._isShown || this._isTransitioning) {
82      return;
83    }
84    const showEvent = EventHandler.trigger(this._element, EVENT_SHOW, {
85      relatedTarget
86    });
87    if (showEvent.defaultPrevented) {
88      return;
89    }
90    this._isShown = true;
91    this._isTransitioning = true;
92    this._scrollBar.hide();
93    document.body.classList.add(CLASS_NAME_OPEN);
94    this._adjustDialog();
95    this._backdrop.show(() => this._showElement(relatedTarget));
96  }
97  hide() {
98    if (!this._isShown || this._isTransitioning) {
99      return;
100    }
101    const hideEvent = EventHandler.trigger(this._element, EVENT_HIDE);
102    if (hideEvent.defaultPrevented) {
103      return;
104    }
105    this._isShown = false;
106    this._isTransitioning = true;
107    this._focustrap.deactivate();
108    this._element.classList.remove(CLASS_NAME_SHOW);
109    this._queueCallback(() => this._hideModal(), this._element, this._isAnimated());
110  }
111  dispose() {
112    EventHandler.off(window, EVENT_KEY);
113    EventHandler.off(this._dialog, EVENT_KEY);
114    this._backdrop.dispose();
115    this._focustrap.deactivate();
116    super.dispose();
117  }
118  handleUpdate() {
119    this._adjustDialog();
120  }
121
122  // Private
123  _initializeBackDrop() {
124    return new Backdrop({
125      isVisible: Boolean(this._config.backdrop),
126      // 'static' option will be translated to true, and booleans will keep their value,
127      isAnimated: this._isAnimated()
128    });
129  }
130  _initializeFocusTrap() {
131    return new FocusTrap({
132      trapElement: this._element
133    });
134  }
135  _showElement(relatedTarget) {
136    // try to append dynamic modal
137    if (!document.body.contains(this._element)) {
138      document.body.append(this._element);
139    }
140    this._element.style.display = 'block';
141    this._element.removeAttribute('aria-hidden');
142    this._element.setAttribute('aria-modal', true);
143    this._element.setAttribute('role', 'dialog');
144    this._element.scrollTop = 0;
145    const modalBody = SelectorEngine.findOne(SELECTOR_MODAL_BODY, this._dialog);
146    if (modalBody) {
147      modalBody.scrollTop = 0;
148    }
149    reflow(this._element);
150    this._element.classList.add(CLASS_NAME_SHOW);
151    const transitionComplete = () => {
152      if (this._config.focus) {
153        this._focustrap.activate();
154      }
155      this._isTransitioning = false;
156      EventHandler.trigger(this._element, EVENT_SHOWN, {
157        relatedTarget
158      });
159    };
160    this._queueCallback(transitionComplete, this._dialog, this._isAnimated());
161  }
162  _addEventListeners() {
163    EventHandler.on(this._element, EVENT_KEYDOWN_DISMISS, event => {
164      if (event.key !== ESCAPE_KEY) {
165        return;
166      }
167      if (this._config.keyboard) {
168        this.hide();
169        return;
170      }
171      this._triggerBackdropTransition();
172    });
173    EventHandler.on(window, EVENT_RESIZE, () => {
174      if (this._isShown && !this._isTransitioning) {
175        this._adjustDialog();
176      }
177    });
178    EventHandler.on(this._element, EVENT_MOUSEDOWN_DISMISS, event => {
179      // a bad trick to segregate clicks that may start inside dialog but end outside, and avoid listen to scrollbar clicks
180      EventHandler.one(this._element, EVENT_CLICK_DISMISS, event2 => {
181        if (this._element !== event.target || this._element !== event2.target) {
182          return;
183        }
184        if (this._config.backdrop === 'static') {
185          this._triggerBackdropTransition();
186          return;
187        }
188        if (this._config.backdrop) {
189          this.hide();
190        }
191      });
192    });
193  }
194  _hideModal() {
195    this._element.style.display = 'none';
196    this._element.setAttribute('aria-hidden', true);
197    this._element.removeAttribute('aria-modal');
198    this._element.removeAttribute('role');
199    this._isTransitioning = false;
200    this._backdrop.hide(() => {
201      document.body.classList.remove(CLASS_NAME_OPEN);
202      this._resetAdjustments();
203      this._scrollBar.reset();
204      EventHandler.trigger(this._element, EVENT_HIDDEN);
205    });
206  }
207  _isAnimated() {
208    return this._element.classList.contains(CLASS_NAME_FADE);
209  }
210  _triggerBackdropTransition() {
211    const hideEvent = EventHandler.trigger(this._element, EVENT_HIDE_PREVENTED);
212    if (hideEvent.defaultPrevented) {
213      return;
214    }
215    const isModalOverflowing = this._element.scrollHeight > document.documentElement.clientHeight;
216    const initialOverflowY = this._element.style.overflowY;
217    // return if the following background transition hasn't yet completed
218    if (initialOverflowY === 'hidden' || this._element.classList.contains(CLASS_NAME_STATIC)) {
219      return;
220    }
221    if (!isModalOverflowing) {
222      this._element.style.overflowY = 'hidden';
223    }
224    this._element.classList.add(CLASS_NAME_STATIC);
225    this._queueCallback(() => {
226      this._element.classList.remove(CLASS_NAME_STATIC);
227      this._queueCallback(() => {
228        this._element.style.overflowY = initialOverflowY;
229      }, this._dialog);
230    }, this._dialog);
231    this._element.focus();
232  }
233
234  /**
235   * The following methods are used to handle overflowing modals
236   */
237
238  _adjustDialog() {
239    const isModalOverflowing = this._element.scrollHeight > document.documentElement.clientHeight;
240    const scrollbarWidth = this._scrollBar.getWidth();
241    const isBodyOverflowing = scrollbarWidth > 0;
242    if (isBodyOverflowing && !isModalOverflowing) {
243      const property = isRTL() ? 'paddingLeft' : 'paddingRight';
244      this._element.style[property] = `${scrollbarWidth}px`;
245    }
246    if (!isBodyOverflowing && isModalOverflowing) {
247      const property = isRTL() ? 'paddingRight' : 'paddingLeft';
248      this._element.style[property] = `${scrollbarWidth}px`;
249    }
250  }
251  _resetAdjustments() {
252    this._element.style.paddingLeft = '';
253    this._element.style.paddingRight = '';
254  }
255
256  // Static
257  static jQueryInterface(config, relatedTarget) {
258    return this.each(function () {
259      const data = Modal.getOrCreateInstance(this, config);
260      if (typeof config !== 'string') {
261        return;
262      }
263      if (typeof data[config] === 'undefined') {
264        throw new TypeError(`No method named "${config}"`);
265      }
266      data[config](relatedTarget);
267    });
268  }
269}
270
271/**
272 * Data API implementation
273 */
274
275EventHandler.on(document, EVENT_CLICK_DATA_API, SELECTOR_DATA_TOGGLE, function (event) {
276  const target = SelectorEngine.getElementFromSelector(this);
277  if (['A', 'AREA'].includes(this.tagName)) {
278    event.preventDefault();
279  }
280  EventHandler.one(target, EVENT_SHOW, showEvent => {
281    if (showEvent.defaultPrevented) {
282      // only register focus restorer if modal will actually get shown
283      return;
284    }
285    EventHandler.one(target, EVENT_HIDDEN, () => {
286      if (isVisible(this)) {
287        this.focus();
288      }
289    });
290  });
291
292  // avoid conflict when clicking modal toggler while another one is open
293  const alreadyOpen = SelectorEngine.findOne(OPEN_SELECTOR);
294  if (alreadyOpen) {
295    Modal.getInstance(alreadyOpen).hide();
296  }
297  const data = Modal.getOrCreateInstance(target);
298  data.toggle(this);
299});
300enableDismissTrigger(Modal);
301
302/**
303 * jQuery
304 */
305
306defineJQueryPlugin(Modal);
307
308Joomla = Joomla || {};
309Joomla.Modal = Joomla.Modal || {};
310window.bootstrap = window.bootstrap || {};
311window.bootstrap.Modal = Modal;
312const allowed = {
313  iframe: ['src', 'name', 'width', 'height']
314};
315Joomla.initialiseModal = (modal, options) => {
316  if (!(modal instanceof Element)) {
317    return;
318  }
319  new window.bootstrap.Modal(modal, options);
320
321  // Comply with the Joomla API - Bound element.open/close
322  modal.open = () => {
323    window.bootstrap.Modal.getInstance(modal).show(modal);
324  };
325  modal.close = () => {
326    window.bootstrap.Modal.getInstance(modal).hide();
327  };
328
329  // Do some Joomla specific changes
330  modal.addEventListener('show.bs.modal', () => {
331    // Comply with the Joomla API - Set the current Modal ID
332    Joomla.Modal.setCurrent(modal);
333    if (modal.dataset.url) {
334      const modalBody = modal.querySelector('.modal-body');
335      const iframe = modalBody.querySelector('iframe');
336      if (iframe) {
337        const addData = modal.querySelector('joomla-field-mediamore');
338        if (addData) {
339          addData.parentNode.removeChild(addData);
340        }
341        iframe.parentNode.removeChild(iframe);
342      }
343
344      // @todo merge https://github.com/joomla/joomla-cms/pull/20788
345      // Hacks because com_associations and field modals use pure javascript in the url!
346      if (modal.dataset.iframe.indexOf('document.getElementById') > 0) {
347        const iframeTextArr = modal.dataset.iframe.split('+');
348        const idFieldArr = iframeTextArr[1].split('"');
349        let el;
350        idFieldArr[0] = idFieldArr[0].replace(/"/g, '"');
351        if (!document.getElementById(idFieldArr[1])) {
352          const fn = new Function(`return ${idFieldArr[0]}`); // This is UNSAFE!!!!
353          el = fn.call(null);
354        } else {
355          el = document.getElementById(idFieldArr[1]).value;
356        }
357        modalBody.insertAdjacentHTML('afterbegin', Joomla.sanitizeHtml(`${iframeTextArr[0]}${el}${iframeTextArr[2]}`, allowed));
358      } else {
359        modalBody.insertAdjacentHTML('afterbegin', Joomla.sanitizeHtml(modal.dataset.iframe, allowed));
360      }
361    }
362  });
363  modal.addEventListener('shown.bs.modal', () => {
364    const modalBody = modal.querySelector('.modal-body');
365    const modalHeader = modal.querySelector('.modal-header');
366    const modalFooter = modal.querySelector('.modal-footer');
367    let modalHeaderHeight = 0;
368    let modalFooterHeight = 0;
369    let maxModalBodyHeight = 0;
370    let modalBodyPadding = 0;
371    let modalBodyHeightOuter = 0;
372    if (modalBody) {
373      if (modalHeader) {
374        const modalHeaderRects = modalHeader.getBoundingClientRect();
375        modalHeaderHeight = modalHeaderRects.height;
376        modalBodyHeightOuter = modalBody.offsetHeight;
377      }
378      if (modalFooter) {
379        modalFooterHeight = parseFloat(getComputedStyle(modalFooter, null).height.replace('px', ''));
380      }
381      const modalBodyHeight = parseFloat(getComputedStyle(modalBody, null).height.replace('px', ''));
382      const padding = modalBody.offsetTop;
383      const maxModalHeight = parseFloat(getComputedStyle(document.body, null).height.replace('px', '')) - padding * 2;
384      modalBodyPadding = modalBodyHeightOuter - modalBodyHeight;
385      maxModalBodyHeight = maxModalHeight - (modalHeaderHeight + modalFooterHeight + modalBodyPadding);
386    }
387    if (modal.dataset.url) {
388      const iframeEl = modal.querySelector('iframe');
389      const iframeHeight = parseFloat(getComputedStyle(iframeEl, null).height.replace('px', ''));
390      if (iframeHeight > maxModalBodyHeight) {
391        modalBody.style.maxHeight = maxModalBodyHeight;
392        modalBody.style.overflowY = 'auto';
393        iframeEl.style.maxHeight = maxModalBodyHeight - modalBodyPadding;
394      }
395    }
396  });
397  modal.addEventListener('hide.bs.modal', () => {
398    const modalBody = modal.querySelector('.modal-body');
399    modalBody.style.maxHeight = 'initial';
400  });
401  modal.addEventListener('hidden.bs.modal', () => {
402    // Comply with the Joomla API - Remove the current Modal ID
403    Joomla.Modal.setCurrent('');
404  });
405};
406
407/**
408 * Method to invoke a click on button inside an iframe
409 *
410 * @param   {object}  options  Object with the css selector for the parent element of an iframe
411 *                             and the selector of the button in the iframe that will be clicked
412 *                             { iframeSelector: '', buttonSelector: '' }
413 * @returns {boolean}
414 *
415 * @since   4.0.0
416 */
417Joomla.iframeButtonClick = options => {
418  if (!options.iframeSelector || !options.buttonSelector) {
419    throw new Error('Selector is missing');
420  }
421
422  // Backward compatibility for older buttons
423  const old2newBtn = {
424    '#closeBtn': '#closeBtn, #toolbar-cancel>button',
425    '#saveBtn': '#saveBtn, #toolbar-save>button',
426    '#applyBtn': '#applyBtn, #toolbar-apply>button'
427  };
428  const iframe = document.querySelector(`${options.iframeSelector} iframe`);
429  if (iframe) {
430    const selector = old2newBtn[options.buttonSelector] ? old2newBtn[options.buttonSelector] : options.buttonSelector;
431    const button = iframe.contentWindow.document.querySelector(selector);
432    if (button) {
433      button.click();
434    }
435  }
436};
437if (Joomla && Joomla.getOptions) {
438  // Get the elements/configurations from the PHP
439  const modals = Joomla.getOptions('bootstrap.modal');
440  // Initialise the elements
441  if (typeof modals === 'object' && modals !== null) {
442    Object.keys(modals).forEach(modal => {
443      const opt = modals[modal];
444      const options = {
445        backdrop: opt.backdrop ? opt.backdrop : true,
446        keyboard: opt.keyboard ? opt.keyboard : true,
447        focus: opt.focus ? opt.focus : true
448      };
449      document.querySelectorAll(modal).forEach(modalEl => Joomla.initialiseModal(modalEl, options));
450    });
451  }
452}
453
454export { Modal as M };

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.