[ Index ]

PHP Cross Reference of Joomla 4.2.2 documentation

title

Body

[close]

/media/vendor/bootstrap/js/ -> modal.js (source)

   1  import { E as EventHandler, c as getElementFromSelector, i as isVisible, S as SelectorEngine, e as enableDismissTrigger, d as defineJQueryPlugin, B as BaseComponent, l as ScrollBarHelper, m as Backdrop, F as FocusTrap, M as Manipulator, a as typeCheckConfig, r as reflow, b as isRTL } from './dom.js?5.1.3';
   2  
   3  /**
   4   * --------------------------------------------------------------------------
   5   * Bootstrap (v5.1.3): modal.js
   6   * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
   7   * --------------------------------------------------------------------------
   8   */
   9  /**
  10   * ------------------------------------------------------------------------
  11   * Constants
  12   * ------------------------------------------------------------------------
  13   */
  14  
  15  const NAME = 'modal';
  16  const DATA_KEY = 'bs.modal';
  17  const EVENT_KEY = `.$DATA_KEY}`;
  18  const DATA_API_KEY = '.data-api';
  19  const ESCAPE_KEY = 'Escape';
  20  const Default = {
  21    backdrop: true,
  22    keyboard: true,
  23    focus: true
  24  };
  25  const DefaultType = {
  26    backdrop: '(boolean|string)',
  27    keyboard: 'boolean',
  28    focus: 'boolean'
  29  };
  30  const EVENT_HIDE = `hide$EVENT_KEY}`;
  31  const EVENT_HIDE_PREVENTED = `hidePrevented$EVENT_KEY}`;
  32  const EVENT_HIDDEN = `hidden$EVENT_KEY}`;
  33  const EVENT_SHOW = `show$EVENT_KEY}`;
  34  const EVENT_SHOWN = `shown$EVENT_KEY}`;
  35  const EVENT_RESIZE = `resize$EVENT_KEY}`;
  36  const EVENT_CLICK_DISMISS = `click.dismiss$EVENT_KEY}`;
  37  const EVENT_KEYDOWN_DISMISS = `keydown.dismiss$EVENT_KEY}`;
  38  const EVENT_MOUSEUP_DISMISS = `mouseup.dismiss$EVENT_KEY}`;
  39  const EVENT_MOUSEDOWN_DISMISS = `mousedown.dismiss$EVENT_KEY}`;
  40  const EVENT_CLICK_DATA_API = `click$EVENT_KEY}$DATA_API_KEY}`;
  41  const CLASS_NAME_OPEN = 'modal-open';
  42  const CLASS_NAME_FADE = 'fade';
  43  const CLASS_NAME_SHOW = 'show';
  44  const CLASS_NAME_STATIC = 'modal-static';
  45  const OPEN_SELECTOR = '.modal.show';
  46  const SELECTOR_DIALOG = '.modal-dialog';
  47  const SELECTOR_MODAL_BODY = '.modal-body';
  48  const SELECTOR_DATA_TOGGLE = '[data-bs-toggle="modal"]';
  49  /**
  50   * ------------------------------------------------------------------------
  51   * Class Definition
  52   * ------------------------------------------------------------------------
  53   */
  54  
  55  class Modal extends BaseComponent {
  56    constructor(element, config) {
  57      super(element);
  58      this._config = this._getConfig(config);
  59      this._dialog = SelectorEngine.findOne(SELECTOR_DIALOG, this._element);
  60      this._backdrop = this._initializeBackDrop();
  61      this._focustrap = this._initializeFocusTrap();
  62      this._isShown = false;
  63      this._ignoreBackdropClick = false;
  64      this._isTransitioning = false;
  65      this._scrollBar = new ScrollBarHelper();
  66    } // Getters
  67  
  68  
  69    static get Default() {
  70      return Default;
  71    }
  72  
  73    static get NAME() {
  74      return NAME;
  75    } // Public
  76  
  77  
  78    toggle(relatedTarget) {
  79      return this._isShown ? this.hide() : this.show(relatedTarget);
  80    }
  81  
  82    show(relatedTarget) {
  83      if (this._isShown || this._isTransitioning) {
  84        return;
  85      }
  86  
  87      const showEvent = EventHandler.trigger(this._element, EVENT_SHOW, {
  88        relatedTarget
  89      });
  90  
  91      if (showEvent.defaultPrevented) {
  92        return;
  93      }
  94  
  95      this._isShown = true;
  96  
  97      if (this._isAnimated()) {
  98        this._isTransitioning = true;
  99      }
 100  
 101      this._scrollBar.hide();
 102  
 103      document.body.classList.add(CLASS_NAME_OPEN);
 104  
 105      this._adjustDialog();
 106  
 107      this._setEscapeEvent();
 108  
 109      this._setResizeEvent();
 110  
 111      EventHandler.on(this._dialog, EVENT_MOUSEDOWN_DISMISS, () => {
 112        EventHandler.one(this._element, EVENT_MOUSEUP_DISMISS, event => {
 113          if (event.target === this._element) {
 114            this._ignoreBackdropClick = true;
 115          }
 116        });
 117      });
 118  
 119      this._showBackdrop(() => this._showElement(relatedTarget));
 120    }
 121  
 122    hide() {
 123      if (!this._isShown || this._isTransitioning) {
 124        return;
 125      }
 126  
 127      const hideEvent = EventHandler.trigger(this._element, EVENT_HIDE);
 128  
 129      if (hideEvent.defaultPrevented) {
 130        return;
 131      }
 132  
 133      this._isShown = false;
 134  
 135      const isAnimated = this._isAnimated();
 136  
 137      if (isAnimated) {
 138        this._isTransitioning = true;
 139      }
 140  
 141      this._setEscapeEvent();
 142  
 143      this._setResizeEvent();
 144  
 145      this._focustrap.deactivate();
 146  
 147      this._element.classList.remove(CLASS_NAME_SHOW);
 148  
 149      EventHandler.off(this._element, EVENT_CLICK_DISMISS);
 150      EventHandler.off(this._dialog, EVENT_MOUSEDOWN_DISMISS);
 151  
 152      this._queueCallback(() => this._hideModal(), this._element, isAnimated);
 153    }
 154  
 155    dispose() {
 156      [window, this._dialog].forEach(htmlElement => EventHandler.off(htmlElement, EVENT_KEY));
 157  
 158      this._backdrop.dispose();
 159  
 160      this._focustrap.deactivate();
 161  
 162      super.dispose();
 163    }
 164  
 165    handleUpdate() {
 166      this._adjustDialog();
 167    } // Private
 168  
 169  
 170    _initializeBackDrop() {
 171      return new Backdrop({
 172        isVisible: Boolean(this._config.backdrop),
 173        // 'static' option will be translated to true, and booleans will keep their value
 174        isAnimated: this._isAnimated()
 175      });
 176    }
 177  
 178    _initializeFocusTrap() {
 179      return new FocusTrap({
 180        trapElement: this._element
 181      });
 182    }
 183  
 184    _getConfig(config) {
 185      config = { ...Default,
 186        ...Manipulator.getDataAttributes(this._element),
 187        ...(typeof config === 'object' ? config : {})
 188      };
 189      typeCheckConfig(NAME, config, DefaultType);
 190      return config;
 191    }
 192  
 193    _showElement(relatedTarget) {
 194      const isAnimated = this._isAnimated();
 195  
 196      const modalBody = SelectorEngine.findOne(SELECTOR_MODAL_BODY, this._dialog);
 197  
 198      if (!this._element.parentNode || this._element.parentNode.nodeType !== Node.ELEMENT_NODE) {
 199        // Don't move modal's DOM position
 200        document.body.append(this._element);
 201      }
 202  
 203      this._element.style.display = 'block';
 204  
 205      this._element.removeAttribute('aria-hidden');
 206  
 207      this._element.setAttribute('aria-modal', true);
 208  
 209      this._element.setAttribute('role', 'dialog');
 210  
 211      this._element.scrollTop = 0;
 212  
 213      if (modalBody) {
 214        modalBody.scrollTop = 0;
 215      }
 216  
 217      if (isAnimated) {
 218        reflow(this._element);
 219      }
 220  
 221      this._element.classList.add(CLASS_NAME_SHOW);
 222  
 223      const transitionComplete = () => {
 224        if (this._config.focus) {
 225          this._focustrap.activate();
 226        }
 227  
 228        this._isTransitioning = false;
 229        EventHandler.trigger(this._element, EVENT_SHOWN, {
 230          relatedTarget
 231        });
 232      };
 233  
 234      this._queueCallback(transitionComplete, this._dialog, isAnimated);
 235    }
 236  
 237    _setEscapeEvent() {
 238      if (this._isShown) {
 239        EventHandler.on(this._element, EVENT_KEYDOWN_DISMISS, event => {
 240          if (this._config.keyboard && event.key === ESCAPE_KEY) {
 241            event.preventDefault();
 242            this.hide();
 243          } else if (!this._config.keyboard && event.key === ESCAPE_KEY) {
 244            this._triggerBackdropTransition();
 245          }
 246        });
 247      } else {
 248        EventHandler.off(this._element, EVENT_KEYDOWN_DISMISS);
 249      }
 250    }
 251  
 252    _setResizeEvent() {
 253      if (this._isShown) {
 254        EventHandler.on(window, EVENT_RESIZE, () => this._adjustDialog());
 255      } else {
 256        EventHandler.off(window, EVENT_RESIZE);
 257      }
 258    }
 259  
 260    _hideModal() {
 261      this._element.style.display = 'none';
 262  
 263      this._element.setAttribute('aria-hidden', true);
 264  
 265      this._element.removeAttribute('aria-modal');
 266  
 267      this._element.removeAttribute('role');
 268  
 269      this._isTransitioning = false;
 270  
 271      this._backdrop.hide(() => {
 272        document.body.classList.remove(CLASS_NAME_OPEN);
 273  
 274        this._resetAdjustments();
 275  
 276        this._scrollBar.reset();
 277  
 278        EventHandler.trigger(this._element, EVENT_HIDDEN);
 279      });
 280    }
 281  
 282    _showBackdrop(callback) {
 283      EventHandler.on(this._element, EVENT_CLICK_DISMISS, event => {
 284        if (this._ignoreBackdropClick) {
 285          this._ignoreBackdropClick = false;
 286          return;
 287        }
 288  
 289        if (event.target !== event.currentTarget) {
 290          return;
 291        }
 292  
 293        if (this._config.backdrop === true) {
 294          this.hide();
 295        } else if (this._config.backdrop === 'static') {
 296          this._triggerBackdropTransition();
 297        }
 298      });
 299  
 300      this._backdrop.show(callback);
 301    }
 302  
 303    _isAnimated() {
 304      return this._element.classList.contains(CLASS_NAME_FADE);
 305    }
 306  
 307    _triggerBackdropTransition() {
 308      const hideEvent = EventHandler.trigger(this._element, EVENT_HIDE_PREVENTED);
 309  
 310      if (hideEvent.defaultPrevented) {
 311        return;
 312      }
 313  
 314      const {
 315        classList,
 316        scrollHeight,
 317        style
 318      } = this._element;
 319      const isModalOverflowing = scrollHeight > document.documentElement.clientHeight; // return if the following background transition hasn't yet completed
 320  
 321      if (!isModalOverflowing && style.overflowY === 'hidden' || classList.contains(CLASS_NAME_STATIC)) {
 322        return;
 323      }
 324  
 325      if (!isModalOverflowing) {
 326        style.overflowY = 'hidden';
 327      }
 328  
 329      classList.add(CLASS_NAME_STATIC);
 330  
 331      this._queueCallback(() => {
 332        classList.remove(CLASS_NAME_STATIC);
 333  
 334        if (!isModalOverflowing) {
 335          this._queueCallback(() => {
 336            style.overflowY = '';
 337          }, this._dialog);
 338        }
 339      }, this._dialog);
 340  
 341      this._element.focus();
 342    } // ----------------------------------------------------------------------
 343    // the following methods are used to handle overflowing modals
 344    // ----------------------------------------------------------------------
 345  
 346  
 347    _adjustDialog() {
 348      const isModalOverflowing = this._element.scrollHeight > document.documentElement.clientHeight;
 349  
 350      const scrollbarWidth = this._scrollBar.getWidth();
 351  
 352      const isBodyOverflowing = scrollbarWidth > 0;
 353  
 354      if (!isBodyOverflowing && isModalOverflowing && !isRTL() || isBodyOverflowing && !isModalOverflowing && isRTL()) {
 355        this._element.style.paddingLeft = `$scrollbarWidth}px`;
 356      }
 357  
 358      if (isBodyOverflowing && !isModalOverflowing && !isRTL() || !isBodyOverflowing && isModalOverflowing && isRTL()) {
 359        this._element.style.paddingRight = `$scrollbarWidth}px`;
 360      }
 361    }
 362  
 363    _resetAdjustments() {
 364      this._element.style.paddingLeft = '';
 365      this._element.style.paddingRight = '';
 366    } // Static
 367  
 368  
 369    static jQueryInterface(config, relatedTarget) {
 370      return this.each(function () {
 371        const data = Modal.getOrCreateInstance(this, config);
 372  
 373        if (typeof config !== 'string') {
 374          return;
 375        }
 376  
 377        if (typeof data[config] === 'undefined') {
 378          throw new TypeError(`No method named "$config}"`);
 379        }
 380  
 381        data[config](relatedTarget);
 382      });
 383    }
 384  
 385  }
 386  /**
 387   * ------------------------------------------------------------------------
 388   * Data Api implementation
 389   * ------------------------------------------------------------------------
 390   */
 391  
 392  
 393  EventHandler.on(document, EVENT_CLICK_DATA_API, SELECTOR_DATA_TOGGLE, function (event) {
 394    const target = getElementFromSelector(this);
 395  
 396    if (['A', 'AREA'].includes(this.tagName)) {
 397      event.preventDefault();
 398    }
 399  
 400    EventHandler.one(target, EVENT_SHOW, showEvent => {
 401      if (showEvent.defaultPrevented) {
 402        // only register focus restorer if modal will actually get shown
 403        return;
 404      }
 405  
 406      EventHandler.one(target, EVENT_HIDDEN, () => {
 407        if (isVisible(this)) {
 408          this.focus();
 409        }
 410      });
 411    }); // avoid conflict when clicking moddal toggler while another one is open
 412  
 413    const allReadyOpen = SelectorEngine.findOne(OPEN_SELECTOR);
 414  
 415    if (allReadyOpen) {
 416      Modal.getInstance(allReadyOpen).hide();
 417    }
 418  
 419    const data = Modal.getOrCreateInstance(target);
 420    data.toggle(this);
 421  });
 422  enableDismissTrigger(Modal);
 423  /**
 424   * ------------------------------------------------------------------------
 425   * jQuery
 426   * ------------------------------------------------------------------------
 427   * add .Modal to jQuery only if jQuery is present
 428   */
 429  
 430  defineJQueryPlugin(Modal);
 431  
 432  Joomla = Joomla || {};
 433  Joomla.Modal = Joomla.Modal || {};
 434  window.bootstrap = window.bootstrap || {};
 435  window.bootstrap.Modal = Modal;
 436  const allowed = {
 437    iframe: ['src', 'name', 'width', 'height']
 438  };
 439  
 440  Joomla.initialiseModal = (modal, options) => {
 441    if (!(modal instanceof Element)) {
 442      return;
 443    } // eslint-disable-next-line no-new
 444  
 445  
 446    new window.bootstrap.Modal(modal, options); // Comply with the Joomla API - Bound element.open/close
 447  
 448    modal.open = () => {
 449      window.bootstrap.Modal.getInstance(modal).show(modal);
 450    };
 451  
 452    modal.close = () => {
 453      window.bootstrap.Modal.getInstance(modal).hide();
 454    }; // Do some Joomla specific changes
 455  
 456  
 457    modal.addEventListener('show.bs.modal', () => {
 458      // Comply with the Joomla API - Set the current Modal ID
 459      Joomla.Modal.setCurrent(modal);
 460  
 461      if (modal.dataset.url) {
 462        const modalBody = modal.querySelector('.modal-body');
 463        const iframe = modalBody.querySelector('iframe');
 464  
 465        if (iframe) {
 466          const addData = modal.querySelector('joomla-field-mediamore');
 467  
 468          if (addData) {
 469            addData.parentNode.removeChild(addData);
 470          }
 471  
 472          iframe.parentNode.removeChild(iframe);
 473        } // @todo merge https://github.com/joomla/joomla-cms/pull/20788
 474        // Hacks because com_associations and field modals use pure javascript in the url!
 475  
 476  
 477        if (modal.dataset.iframe.indexOf('document.getElementById') > 0) {
 478          const iframeTextArr = modal.dataset.iframe.split('+');
 479          const idFieldArr = iframeTextArr[1].split('"');
 480          let el;
 481          idFieldArr[0] = idFieldArr[0].replace(/"/g, '"');
 482  
 483          if (!document.getElementById(idFieldArr[1])) {
 484            // eslint-disable-next-line no-new-func
 485            const fn = new Function(`return $idFieldArr[0]}`); // This is UNSAFE!!!!
 486  
 487            el = fn.call(null);
 488          } else {
 489            el = document.getElementById(idFieldArr[1]).value;
 490          }
 491  
 492          modalBody.insertAdjacentHTML('afterbegin', Joomla.sanitizeHtml(`$iframeTextArr[0]}$el}$iframeTextArr[2]}`, allowed));
 493        } else {
 494          modalBody.insertAdjacentHTML('afterbegin', Joomla.sanitizeHtml(modal.dataset.iframe, allowed));
 495        }
 496      }
 497    });
 498    modal.addEventListener('shown.bs.modal', () => {
 499      const modalBody = modal.querySelector('.modal-body');
 500      const modalHeader = modal.querySelector('.modal-header');
 501      const modalFooter = modal.querySelector('.modal-footer');
 502      let modalHeaderHeight = 0;
 503      let modalFooterHeight = 0;
 504      let maxModalBodyHeight = 0;
 505      let modalBodyPadding = 0;
 506      let modalBodyHeightOuter = 0;
 507  
 508      if (modalBody) {
 509        if (modalHeader) {
 510          const modalHeaderRects = modalHeader.getBoundingClientRect();
 511          modalHeaderHeight = modalHeaderRects.height;
 512          modalBodyHeightOuter = modalBody.offsetHeight;
 513        }
 514  
 515        if (modalFooter) {
 516          modalFooterHeight = parseFloat(getComputedStyle(modalFooter, null).height.replace('px', ''));
 517        }
 518  
 519        const modalBodyHeight = parseFloat(getComputedStyle(modalBody, null).height.replace('px', ''));
 520        const padding = modalBody.offsetTop;
 521        const maxModalHeight = parseFloat(getComputedStyle(document.body, null).height.replace('px', '')) - padding * 2;
 522        modalBodyPadding = modalBodyHeightOuter - modalBodyHeight;
 523        maxModalBodyHeight = maxModalHeight - (modalHeaderHeight + modalFooterHeight + modalBodyPadding);
 524      }
 525  
 526      if (modal.dataset.url) {
 527        const iframeEl = modal.querySelector('iframe');
 528        const iframeHeight = parseFloat(getComputedStyle(iframeEl, null).height.replace('px', ''));
 529  
 530        if (iframeHeight > maxModalBodyHeight) {
 531          modalBody.style.maxHeight = maxModalBodyHeight;
 532          modalBody.style.overflowY = 'auto';
 533          iframeEl.style.maxHeight = maxModalBodyHeight - modalBodyPadding;
 534        }
 535      }
 536    });
 537    modal.addEventListener('hide.bs.modal', () => {
 538      const modalBody = modal.querySelector('.modal-body');
 539      modalBody.style.maxHeight = 'initial';
 540    });
 541    modal.addEventListener('hidden.bs.modal', () => {
 542      // Comply with the Joomla API - Remove the current Modal ID
 543      Joomla.Modal.setCurrent('');
 544    });
 545  };
 546  /**
 547   * Method to invoke a click on button inside an iframe
 548   *
 549   * @param   {object}  options  Object with the css selector for the parent element of an iframe
 550   *                             and the selector of the button in the iframe that will be clicked
 551   *                             { iframeSelector: '', buttonSelector: '' }
 552   * @returns {boolean}
 553   *
 554   * @since   4.0.0
 555   */
 556  
 557  
 558  Joomla.iframeButtonClick = options => {
 559    if (!options.iframeSelector || !options.buttonSelector) {
 560      throw new Error('Selector is missing');
 561    }
 562  
 563    const iframe = document.querySelector(`$options.iframeSelector} iframe`);
 564  
 565    if (iframe) {
 566      const button = iframe.contentWindow.document.querySelector(options.buttonSelector);
 567  
 568      if (button) {
 569        button.click();
 570      }
 571    }
 572  };
 573  
 574  if (Joomla && Joomla.getOptions) {
 575    // Get the elements/configurations from the PHP
 576    const modals = Joomla.getOptions('bootstrap.modal'); // Initialise the elements
 577  
 578    if (typeof modals === 'object' && modals !== null) {
 579      Object.keys(modals).forEach(modal => {
 580        const opt = modals[modal];
 581        const options = {
 582          backdrop: opt.backdrop ? opt.backdrop : true,
 583          keyboard: opt.keyboard ? opt.keyboard : true,
 584          focus: opt.focus ? opt.focus : true
 585        };
 586        Array.from(document.querySelectorAll(modal)).map(modalEl => Joomla.initialiseModal(modalEl, options));
 587      });
 588    }
 589  }
 590  
 591  export { Modal as M };


Generated: Wed Sep 7 05:41:13 2022 Chilli.vc Blog - For Webmaster,Blog-Writer,System Admin and Domainer