PageSourceSearch

https://mix.my/modules/contrib/mercury_editor/build/js/dialog.drupal.js?tluj44

js mix.my collected 2026-10-02 14:07:46 UTC 16,768 bytes, 458 lines download raw bytes

1(function () {
2  'use strict';
3
4  (function ($, Drupal, drupalSettings) {
5    // TODO: This does not seem to be used, but keeping for reference.
6    // drupalSettings.mercuryDialog = {
7    //   autoOpen: true,
8    //   autoResize: false,
9    //   dialogClass: '',
10    //   buttonClass: 'button',
11    //   buttonPrimaryClass: 'button--primary',
12    // };
13
14    function dispatchDialogEvent(eventType, dialog, element, settings) {
15      // Use the jQuery if the DrupalDialogEvent is not defined for BC.
16      if (typeof DrupalDialogEvent === 'undefined') {
17        $(window).trigger(`dialog:${eventType}`, [dialog, $(element), settings]);
18      } else {
19        const event = new DrupalDialogEvent(eventType, dialog, settings || {});
20        element.dispatchEvent(event);
21      }
22    }
23
24    Drupal.mercuryDialog = function (element, options) {
25      var undef;
26      var $element = $(element);
27      // Override the jQuery dialog method to return the element, preventing
28      // uncaught errors from core jQuery dialog being thrown.
29      $element.dialog = () => $element;
30      var dialogElement;
31      var dialog = {
32        open: false,
33        returnValue: undef,
34      };
35
36      /**
37       * ResizeObserver callback that runs when the shadow dialog element resizes.
38       *
39       * @param {ResizeObserverEntry[]} entries
40       */
41      function onDockResize(entries) {
42        for (const entry of entries) {
43          const element = entry.target;
44          const dockDirection = element.getAttribute('data-dock');
45          if (!element.open || !dockDirection || dockDirection == 'none') {
46            return;
47          }
48
49          // Dispatch custom event with resize data
50          const resizeEvent = new CustomEvent('mercury:dockResize', {
51            detail: {
52              width: entry.contentRect.width,
53              height: entry.contentRect.height
54            },
55            bubbles: true
56          });
57
58          dialogElement.setAttribute('width', entry.contentRect.width);
59          dialogElement.dispatchEvent(resizeEvent);
60        }
61      }
62
63      const dockObserver = new ResizeObserver(onDockResize);
64
65      /**
66       * MutationObserver callback that will start the resize observer when the dialog opens.
67       *
68       * @param {MutationRecord[]} entries
69       */
70      function onDialogMutate(records) {
71        // @todo: We may only want to attach this if the dialog is transitioning to an open state.
72        dockObserver.observe(dialogElement.shadowRoot.querySelector('dialog'));
73      }
74
75      const dialogMutationObserver = new MutationObserver(onDialogMutate);
76
77      /**
78       * Converts a numeric only value to a px based CSS value.
79       * @param {*} value
80       * @returns {string}
81       *   A CSS length value.
82       */
83      function getCSSLength(value) {
84        if (typeof value === 'undefined' || !/^\d+$/.test(value)) {
85          return value;
86        }
87        return `${value}px`;
88      }
89
90      /**
91       * Apply options to the <mercury-dialog> element.
92       *
93       * @param {Element} dialogElement
94       * @param {Object} options
95       */
96      function applyOptions(dialogElement, options) {
97        const attributeOptions = [
98          'title',
99          'modal',
100          'dock',
101          'push',
102          'resizable',
103          'moveable',
104        ];
105
106        // Set these options as HTML attributes on the element.
107        attributeOptions.forEach(option => {
108          if (typeof options[option] !== 'undefined') {
109            dialogElement.setAttribute(option, options[option]);
110          }
111        });
112
113        if (options.dialogClass) {
114          dialogElement.classList.add(...options.dialogClass.split(' '));
115        }
116
117        if (dialogElement.id === 'me-edit-screen') {
118          // Options for the main Mercury Editor tray.
119          let isTrayCollapsed = localStorage.getItem('mercury-dialog-dock-collapsed') === 'true';
120          let savedWidth = localStorage.getItem('mercury-dialog-dock-width');
121          let savedHeight = localStorage.getItem('mercury-dialog-dock-height');
122
123          if (!isTrayCollapsed) {
124            let dialogWidth = savedWidth || options.width;
125            let dialogHeight = savedHeight || options.height;
126
127            if (dialogWidth) {
128              dialogElement.setAttribute('width', dialogWidth);
129              document.documentElement.style.setProperty('--me-dialog-dock-width', getCSSLength(dialogWidth));
130            }
131
132            if (dialogHeight) {
133              dialogElement.setAttribute('height', dialogHeight);
134              document.documentElement.style.setProperty('--me-dialog-dock-height', getCSSLength(dialogHeight));
135            }
136          }
137          else {
138            dialogElement.setAttribute('width', '10');
139            document.documentElement.style.setProperty('--me-dialog-dock-width', '10px');
140          }
141        }
142        else {
143          // Options for all other dialogs.
144          if (options.width) {
145            dialogElement.setAttribute('width', options.width);
146            document.documentElement.style.setProperty('--me-dialog-width', getCSSLength(options.width));
147          }
148
149          if (options.height) {
150            dialogElement.setAttribute('height', options.height);
151            document.documentElement.style.setProperty('--me-dialog-height', getCSSLength(options.height));
152          }
153        }
154
155        // TODO: Determine if we need to persist the width and height of docked dialogs.
156        // if (options.dock) {
157        //   if (savedWidth && ['right', 'left'].includes(options.dock)) {
158        //     dialogElement.setAttribute('width', savedWidth);
159        //   }
160        //   if (savedHeight && ['top', 'bottom'].includes(options.dock)) {
161        //     dialogElement.setAttribute('height', savedHeight);
162        //   }
163        // }
164
165        if (options.drupalAutoButtons && !options.buttons) {
166          options.buttons = Drupal.behaviors.mercuryDialog.prepareDialogButtons($(dialogElement));
167        }
168
169        if (options.buttons && options.buttons.length) {
170          _createButtonPane(options.buttons);
171        }
172
173        return dialogElement;
174      }
175
176      /**
177       * Determines which element to append the dialog to. Defaults to the <body>
178       * @returns Element
179       */
180      function _appendTo() {
181        var element = options.appendTo;
182        if (element && (element.jquery || element.nodeType)) {
183          return $(element);
184        }
185        return $(document).find(element || "body").eq(0);
186      }
187      /**
188       * Create a button pane similar to jQuery UI dialog.
189       */
190      function _createButtonPane(buttons) {
191        const existing = dialogElement.querySelector('.me-dialog__buttonpane');
192        if (existing) {
193          existing.remove();
194        }
195        const uiDialogButtonPane = document.createElement('div');
196        uiDialogButtonPane.setAttribute('slot', 'footer');
197        uiDialogButtonPane.classList.add('me-dialog__buttonpane');
198        dialogElement.appendChild(uiDialogButtonPane);
199        if ($.isEmptyObject(buttons) || (Array.isArray(buttons) && !buttons.length)) {
200          return;
201        }
202        buttons.forEach((props) => {
203          const button = document.createElement('button');
204          button.classList = props.class;
205          button.classList.add('button');
206          button.appendChild(document.createTextNode(props.text));
207          button.addEventListener('click', props.click);
208          uiDialogButtonPane.appendChild(button);
209        });
210      }
211      function init(settings) {
212        // Wrap the element in a <mercury-dialog> if it isn't already.
213        if (element.tagName !== 'MERCURY-DIALOG') {
214          const wrapper = $('<mercury-dialog>').append($element).appendTo(_appendTo());
215          dialogElement = wrapper[0];
216        } else {
217          dialogElement = element;
218        }
219        applyOptions(dialogElement, settings);
220      }
221
222      /**
223       * Initializes and opens a dialog element.
224       * @param {Object} settings
225       *   Dialog settings mimicing jQuery UI for compatability.
226       */
227      function openDialog(settings) {
228        settings = { ...drupalSettings.dialog, ...drupalSettings.mercuryEditor, ...options, ...settings };
229        dispatchDialogEvent('beforecreate', dialog, $element.get(0), settings);
230        init(settings);
231        dialogElement[settings.modal ? 'showModal' : 'show']();
232        // Set autoResize to false to prevent Drupal core's jQuery dialog from
233        // attemping to resize, which would throw an error.
234        const originalResizeSetting = settings.autoResize;
235        settings.autoResize = false;
236        dispatchDialogEvent('aftercreate', dialog, $element.get(0), settings);
237        settings.autoResize = originalResizeSetting;
238        // Add a mutation observer to the dialog element.
239        dialogMutationObserver.observe(dialogElement, { childList: true, attributes: true });
240        dialogElement.addEventListener('close', function () {
241          closeDialog();
242        });
243      }
244
245      function closeDialog(value) {
246        dispatchDialogEvent('beforeclose', dialog, $element.get(0));
247        // Stop observing height and width changes.
248        dockObserver.disconnect();
249        dialogMutationObserver.disconnect();
250        Drupal.detachBehaviors(element, null, 'unload');
251        element.close();
252        dialog.returnValue = value;
253        dispatchDialogEvent('afterclose', dialog, $element.get(0));
254        $element.remove();
255      }
256
257      dialog.show = function () {
258        openDialog({
259          modal: false,
260        });
261      };
262
263      dialog.showModal = function () {
264        openDialog({
265          modal: true,
266        });
267      };
268
269      dialog.applyOptions = function (options) {
270        init(options);
271      };
272
273      dialog.close = closeDialog;
274      return dialog;
275    };
276
277    Drupal.behaviors.mercuryDialog = {
278      attach: function (context, settings) {
279        // Provide a known 'drupal-mercury-dialog' DOM element for Drupal-based modal
280        // dialogs. Non-modal dialogs are responsible for creating their own
281        // elements, since there can be multiple non-modal dialogs at a time.
282
283        if (!$('#drupal-mercury-dialog').length) {
284          // Add 'ui-front' jQuery UI class so jQuery UI widgets like autocomplete
285          // sit on top of dialogs. For more information see
286          // http://api.jqueryui.com/theming/stacking-elements/.
287          // @todo: .ui-front just sets a z-index of 100 which is not high enough
288          // to overlay Gin's Toolbar.
289          $('<mercury-dialog id="drupal-mercury-dialog"></mercury-dialog>')
290            .appendTo('body');
291        }
292
293        // Special behaviors specific when attaching content within a dialog.
294        // These behaviors usually fire after a validation error inside a dialog.
295        const $dialog = $(context).closest('mercury-dialog');
296        if ($dialog.length) {
297          $dialog.trigger('dialogButtonsChange');
298        }
299
300      },
301
302      prepareDialogButtons: function prepareDialogButtons($dialog) {
303        var buttons = [];
304        var $buttons = $dialog.find('.form-actions').last().find('input[type=submit], a.button, a.action-link');
305        $buttons.each(function () {
306          var $originalButton = $(this).css({
307            display: 'none'
308          });
309          buttons.push({
310            text: $originalButton.html() || $originalButton.attr('value'),
311            class: $originalButton.attr('class'),
312            click: function click(e) {
313              if ($originalButton.is('a')) {
314                $originalButton[0].click();
315              } else {
316                $originalButton.trigger('mousedown').trigger('mouseup').trigger('click');
317                e.preventDefault();
318              }
319            }
320          });
321        });
322        return buttons;
323      }
324    };
325
326    // Moves Layout Paragraphs form buttons into the dialog button pane.
327    function moveFormButtonsToDialog(event, dialog, $dialog) {
328      if ($dialog[0].tagName !== 'MERCURY-DIALOG') {
329        return;
330      }
331      if ($dialog.attr('id').indexOf('lpb-dialog-') === 0) {
332        const buttons = Drupal.behaviors.mercuryDialog.prepareDialogButtons($dialog);
333        if (buttons.length) {
334          Drupal.mercuryDialog($dialog[0]).applyOptions({ buttons: buttons });
335        }
336      }
337    }
338    $(window).on('dialog:aftercreate', moveFormButtonsToDialog);
339
340    /**
341     * ResizeObserver callback that resizes the parent iframe based on
342     * the height of the child document html element.
343     *
344     * @param {ResizeObserverEntry[]} entries
345     */
346    function onBodyResize(iframe) {
347      return (entries) => {
348        // Resize the parent iframe based on the html's border box height.
349        if (iframe && entries.length) {
350          iframe.style.height = `${entries[0].borderBoxSize[0].blockSize + 1}px`;
351          iframe.style.width = `${entries[0].borderBoxSize[0].inlineSize + 1}px`;
352        }
353      }
354    }
355
356    /**
357     * Set a max-width on the iframe's <body> to match the dialog's
358     * max-width to prevent horizontal scrolling.
359     *
360     * @param {HTMLIFrameElement} iframe
361     *   The iframe element within a mercury dialog.
362     * @param {HTMLBodyElement} framedBody
363     *   The body element within the iframe.
364     */
365    function setFrameBodyMaxWidth(iframe, framedBody) {
366      const dialogStyles = window.getComputedStyle(iframe.closest('mercur
366y-dialog').shadowRoot.querySelector('dialog'));
367      const dialogMainStyles = window.getComputedStyle(iframe.closest('mercury-dialog').shadowRoot.querySelector('main'));
368      framedBody.style.maxWidth = `calc(${dialogStyles.getPropertyValue('max-width')} - ${dialogMainStyles.getPropertyValue('padding-left')} - ${dialogMainStyles.getPropertyValue('padding-right')} - 2px)`;
369    }
370
371    /**
372     * Resizes an iFrame to match the height of its inner <body> element.
373     * @param {HTMLIFrameElement} iframe
374     */
375    function resizeIframe(iframe) {
376      const framedBody = iframe.contentWindow.document.body;
377      framedBody.style.width = 'max-content';
378      framedBody.style.height = 'fit-content';
379
380      setFrameBodyMaxWidth(iframe, framedBody);
381
382      // Observe changes to the iframe's inner <body> dimensions.
383      new ResizeObserver(onBodyResize(iframe)).observe(framedBody, { box: 'border-box' });
384    }
385
386    function updateIframeSize(event, dialog, $dialog) {
387      if ($dialog[0].tagName !== 'MERCURY-DIALOG') {
388        return;
389      }
390
391      const iframe = $dialog[0].querySelector('iframe');
392      if (!iframe) {
393        return;
394      }
395
396      $dialog[0].style.setProperty('--me-dialog-height-default', 'fit-content');
397
398      iframe.onload = function () {
399        resizeIframe(iframe);
400        setFrameBodyMaxWidth(iframe, framedBody);
401      };
402
403      const framedBody = iframe?.contentWindow?.document?.body;
404      if (framedBody) {
405        window.addEventListener('resize', function () {
406          setFrameBodyMaxWidth(iframe, framedBody);
407        });
408      }
409    }
410    $(window).on('dialog:aftercreate', updateIframeSize);
411
412    // Store open modals.
413    const modalStack = [];
414
415    // The following event handlers are used to manage the modal stack.
416    // Since native dialog['modal'] elements live in the browser's top-level,
417    // we need to make sure any jQuery ui modals that are opended from within
418    // a mercury-dialog element get nested within the top-level modal.
419    // Otherwise the jQuery dialog will be obscured by the mercury-dialog modal.
420    // See https://developer.chrome.com/blog/what-is-the-top-layer/
421    function addModalToStack(event, dialog, $dialog) {
422      if ($dialog[0].tagName !== 'MERCURY-DIALOG') {
423        return;
424      }
425      if ($dialog[0].hasAttribute('modal') && $dialog[0].getAttribute('modal') !== 'false') {
426        modalStack.push($dialog);
427      }
428    }
429    $(window).on('dialog:aftercreate', addModalToStack);
430
431    function removeModalFromStack(event, dialog, $dialog) {
432      if ($dialog[0].tagName !== 'MERCURY-DIALOG') {
433        return;
434      }
435      if ($dialog[0].hasAttribute('modal') && $dialog[0].getAttribute('modal') !== 'false') {
436        const index = modalStack.indexOf($dialog);
437        if (index > -1) {
438          modalStack.splice(index, 1);
439        }
440      }
441    }
442    $(window).on('dialog:beforeclose', removeModalFromStack);
443
444    function nestDialogInModal(event, dialog, $dialog) {
445      if ($dialog[0].tagName !== 'MERCURY-DIALOG') {
446        return;
447      }
448      if (modalStack.length > 0) {
449        const $parent = $dialog.parent('.ui-dialog');
450        const $overlay = $parent.next('.ui-widget-overlay');
451        modalStack.slice(-1)[0].append([$parent, $overlay]);
452      }
453    }
454    $(window).on('dialog:aftercreate', nestDialogInModal);
455
456  })(jQuery, Drupal, drupalSettings);
457
458})();

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.