PageSourceSearch

https://www.portseattle.org/themes/contrib/bootstrap/js/modal.jquery.ui.bridge.js?tlh9gk

js portseattle.org collected 2026-09-24 08:38:23 UTC 22,205 bytes, 585 lines download raw bytes

1/**
2 * @file
3 * Bootstrap Modals.
4 *
5 * @param {jQuery} $
6 * @param {Drupal} Drupal
7 * @param {Drupal.bootstrap} Bootstrap
8 * @param {Attributes} Attributes
9 * @param {drupalSettings} drupalSettings
10 */
11(function ($, Drupal, Bootstrap, Attributes, drupalSettings) {
12  'use strict';
13
14  /**
15   * Only process this once.
16   */
17  Bootstrap.once('modal.jquery.ui.bridge', function (settings) {
18    // RTL support.
19    var rtl = document.documentElement.getAttribute('dir').toLowerCase() === 'rtl';
20
21    // Override drupal.dialog button classes. This must be done on DOM ready
22    // since core/drupal.dialog technically depends on this file and has not
23    // yet set their default settings.
24    $(function () {
25      drupalSettings.dialog.buttonClass = 'btn';
26      drupalSettings.dialog.buttonPrimaryClass = 'btn-primary';
27    });
28
29    // Create the "dialog" plugin bridge.
30    Bootstrap.Dialog.Bridge = function (options) {
31      var args = Array.prototype.slice.call(arguments);
32      var $element = $(this);
33      var type = options && options.dialogType || $element[0].dialogType || 'modal';
34
35      $element[0].dialogType = type;
36
37      var handler = Bootstrap.Dialog.Handler.get(type);
38
39      // When only options are passed, jQuery UI dialog treats this like a
40      // initialization method. Destroy any existing Bootstrap modal and
41      // recreate it using the contents of the dialog HTML.
42      if (args.length === 1 && typeof options === 'object') {
43        this.each(function () {
44          handler.ensureModalStructure(this, options);
45        });
46
47        // Proxy to the Bootstrap Modal plugin, indicating that this is a
48        // jQuery UI dialog bridge.
49        return handler.invoke(this, {
50          dialogOptions: options,
51          jQueryUiBridge: true
52        });
53      }
54
55      // Otherwise, proxy all arguments to the Bootstrap Modal plugin.
56      var ret;
57      try {
58        ret = handler.invoke.apply(handler, [this].concat(args));
59      }
60      catch (e) {
61        Bootstrap.warn(e);
62      }
63
64      // If just one element and there was a result returned for the option passed,
65      // then return the result. Otherwise, just return the jQuery object.
66      return this.length === 1 && ret !== void 0 ? ret : this;
67    };
68
69    // Assign the jQuery "dialog" plugin to use to the bridge.
70    Bootstrap.createPlugin('dialog', Bootstrap.Dialog.Bridge);
71
72    // Create the "modal" plugin bridge.
73    Bootstrap.Modal.Bridge = function () {
74      var Modal = this;
75
76      return {
77        DEFAULTS: {
78          // By default, this option is disabled. It's only flagged when a modal
79          // was created using $.fn.dialog above.
80          jQueryUiBridge: false
81        },
82        prototype: {
83
84          /**
85           * Handler for $.fn.dialog('close').
86           */
87          close: function () {
88            var _this = this;
89
90            this.hide.apply(this, arguments);
91
92            // For some reason (likely due to the transition event not being
93            // registered properly), the backdrop doesn't always get removed
94            // after the above "hide" method is invoked . Instead, ensure the
95            // backdrop is removed after the transition duration by manually
96            // invoking the internal "hideModal" method shortly thereafter.
97            setTimeout(function () {
98              if (!_this.isShown && _this.$backdrop) {
99                _this.hideModal();
100              }
101            }, (Modal.TRANSITION_DURATION !== void 0 ? Modal.TRANSITION_DURATION : 300) + 10);
102          },
103
104          /**
105           * Creates any necessary buttons from dialog options.
106           */
107          createButtons: function () {
108            var handler = Bootstrap.Dialog.Handler.get(this.$element);
109            this.$footer.find(handler.selectors.buttons).remove();
110
111            // jQuery UI supports both objects and arrays. Unfortunately
112            // developers have misunderstood and abused this by simply placing
113            // the objects that should be in an array inside an object with
114            // arbitrary keys (likely to target specific buttons as a hack).
115            var buttons = this.options.dialogOptions && this.options.dialogOptions.buttons || [];
116            if (!Array.isArray(buttons)) {
117              var array = [];
118              for (var k in buttons) {
119                // Support the proper object values: label => click callback.
120                if (typeof buttons[k] === 'function') {
121                  array.push({
122                    label: k,
123                    click: buttons[k],
124                  });
125                }
126                // Support nested objects, but log a warning.
127                else if (buttons[k].text || buttons[k].label) {
128                  Bootstrap.warn('Malformed jQuery UI dialog button: @key. The button object should be inside an array.', {
129                    '@key': k
130                  });
131                  array.push(buttons[k]);
132                }
133                else {
134                  Bootstrap.unsupported('button', k, buttons[k]);
135                }
136              }
137              buttons = array;
138            }
139
140            if (buttons.length) {
141              var $buttons = $('<div class="modal-buttons"/>').appendTo(this.$footer);
142              for (var i = 0, l = buttons.length; i < l; i++) {
143                var button = buttons[i];
144                var $button = $(Drupal.theme('bootstrapModalDialogButton', button));
145
146                // Invoke the "create" method for jQuery UI buttons.
147                if (typeof button.create === 'function') {
148                  button.create.call($button[0]);
149                }
150
151                // Bind the "click" method for jQuery UI buttons to the modal.
152                if (typeof button.click === 'function') {
153                  $button.on('click', button.click.bind(this.$element));
154                }
155
156                $buttons.append($button);
157              }
158            }
159
160            // Toggle footer visibility based on whether it has child elements.
161            this.$footer[this.$footer.children()[0] ? 'show' : 'hide']();
162          },
163
164          /**
165           * Initializes the Bootstrap Modal.
166           */
167          init: function () {
168            var handler = Bootstrap.Dialog.Handler.get(this.$element);
169            if (!this.$dialog) {
170              this.$dialog = this.$element.find(handler.selectors.dialog);
171            }
172            this.$dialog.addClass('js-drupal-dialog');
173
174            if (!this.$header) {
175              this.$header = this.$dialog.find(handler.selectors.header);
176            }
177            if (!this.$title) {
178              this.$title = this.$dialog.find(handler.selectors.title);
179            }
180            if (!this.$close) {
181              this.$close = this.$header.find(handler.selectors.close);
182            }
183            if (!this.$footer) {
184              this.$footer = this.$dialog.find(handler.selectors.footer);
185            }
186            if (!this.$content) {
187              this.$content = this.$dialog.find(handler.selectors.content);
188            }
189            if (!this.$dialogBody) {
190              this.$dialogBody = this.$dialog.find(handler.selectors.body);
191            }
192
193            // Relay necessary events.
194            if (this.options.jQueryUiBridge) {
195              this.$element.on('hide.bs.modal',   Bootstrap.relayEvent(this.$element, 'dialogbeforeclose', false));
196              this.$element.on('hidden.bs.modal', Bootstrap.relayEvent(this.$element, 'dialogclose', false));
197              this.$element.on('show.bs.modal',   Bootstrap.relayEvent(this.$element, 'dialogcreate', false));
198              this.$element.on('shown.bs.modal',  Bootstrap.relayEvent(this.$element, 'dialogopen', false));
199            }
200
201            // Create a footer if one doesn't exist.
202            // This is necessary in case dialog.ajax.js decides to add buttons.
203            if (!this.$footer[0]) {
204              this.$footer = handler.theme('footer', {}, true).insertAfter(this.$dialogBody);
205            }
206
207            // Map the initial options.
208            $.extend(true, this.options, this.mapDialogOptions(this.options));
209
210            // Update buttons.
211            this.createButtons();
212
213            // Now call the parent init method.
214            this.super();
215
216            // Handle autoResize option (this is a drupal.dialog option).
217            if (this.options.dialogOptions && this.options.dialogOptions.autoResize && this.options.dialogOptions.position) {
218              this.position(this.options.dialogOptions.position);
219            }
220
221            // If show is enabled and currently not shown, show it.
222            if (this.options.jQueryUiBridge && this.options.show && !this.isShown) {
223              this.show();
224            }
225          },
226
227          /**
228           * Handler for $.fn.dialog('instance').
229           */
230          instance: function () {
231            Bootstrap.unsupported('method', 'instance', arguments);
232          },
233
234          /**
235           * Handler for $.fn.dialog('isOpen').
236           */
237          isOpen: function () {
238            return !!this.isShown;
239          },
240
241          /**
242           * Maps dialog options to the modal.
243           *
244           * @param {Object} options
245           *   The options to map.
246           */
247          mapDialogOptions: function (options) {
248            // Retrieve the dialog handler for this type.
249            var handler = Bootstrap.Dialog.Handler.get(this.$element);
250
251            var mappedOptions = {};
252            var dialogOptions = options.dialogOptions || {};
253
254            // Remove any existing dialog options.
255            delete options.dialogOptions;
256
257            // Separate Bootstrap modal options from jQuery UI dialog options.
258            for (var k in options) {
259              if (Modal.DEFAULTS.hasOwnProperty(k)) {
260                mappedOptions[k] = options[k];
261              }
262              else {
263                dialogOptions[k] = options[k];
264              }
265            }
266
267
268            // Handle CSS properties.
269            var cssUnitRegExp = /^([+-]?(?:\d+|\d*\.\d+))([a-z]*|%)?$/;
270            var parseCssUnit = function (value, defaultUnit) {
271              var parts = ('' + value).match(cssUnitRegExp);
272              return parts && parts[1] !== void 0 ? parts[1] + (parts[2] || defaultUnit || 'px') : null;
273            };
274            var styles = {};
275            var cssProperties = ['height', 'maxHeight', 'maxWidth', 'minHeight', 'minWidth', 'width'];
276            for (var i = 0, l = cssProperties.length; i < l; i++) {
277              var prop = cssProperties[i];
278              if (dialogOptions[prop] !== void 0) {
279                var value = parseCssUnit(dialogOptions[prop]);
280                if (value) {
281                  styles[prop] = value;
282
283                  // If there's a defined height of some kind, enforce the modal
284                  // to use flex (on modern browsers). This will ensure that
285                  // the core autoResize calculations don't cause the content
286                  // to overflow.
287                  if (dialogOptions.autoResize && (prop === 'height' || prop === 'maxHeight')) {
288                    styles.display = 'flex';
289                    styles.flexDirection = 'column';
290                    this.$dialogBody.css('overflow', 'scroll');
291                  }
292                }
293              }
294            }
295
296            // Apply mapped CSS styles to the modal-content container.
297            this.$content.css(styles);
298
299            // Handle deprecated "dialogClass" option by merging it with "classes".
300            var classesMap = {
301              'ui-dialog': 'modal-content',
302              'ui-dialog-titlebar': 'modal-header',
303              'ui-dialog-title': 'modal-title',
304              'ui-dialog-titlebar-close': 'close',
305              'ui-dialog-content': 'modal-body',
306              'ui-dialog-buttonpane': 'modal-footer'
307            };
308            if (dialogOptions.dialogClass) {
309              if (dialogOptions.classes === void 0) {
310                dialogOptions.classes = {};
311              }
312              if (dialogOptions.classes['ui-dialog'] === void 0) {
313                dialogOptions.classes['ui-dialog'] = '';
314              }
315              var dialogClass = dialogOptions.classes['ui-dialog'].split(' ');
316              dialogClass.push(dialogOptions.dialogClass);
317              dialogOptions.classes['ui-dialog'] = dialogClass.join(' ');
318              delete dialogOptions.dialogClass;
319            }
320
321            // Add jQuery UI classes to elements in case developers target them
322            // in callbacks.
323            for (k in classesMap) {
324              this.$element.find('.' + classesMap[k]).addClass(k);
325            }
326
327            // Bind events.
328            var events = [
329              'beforeClose', 'close',
330              'create',
331              'drag', 'dragStart', 'dragStop',
332              'focus',
333              'open',
334              'resize', 'resizeStart', 'resizeStop'
335            ];
336            for (i = 0, l = events.length; i < l; i++) {
337              var event = events[i].toLowerCase();
338              if (dialogOptions[event] === void 0 || typeof dialogOptions[event] !== 'function') continue;
339              this.$element.on('dialog' + event, dialogOptions[event]);
340            }
341
342            // Support title attribute on the modal.
343            var title;
344            if ((dialogOptions.title === null || dialogOptions.title === void 0) && (title = this.$element.attr('title'))) {
345              dialogOptions.title = title;
346            }
347
348            // Handle the reset of the options.
349            for (var name in dialogOptions) {
350              if (!dialogOptions.hasOwnProperty(name) || dialogOptions[name] === void 0) continue;
351
352              switch (name) {
353                case 'appendTo':
354                  Bootstrap.unsupported('option', name, dialogOptions.appendTo);
355                  break;
356
357                case 'autoOpen':
358                  mappedOptions.show = dialogOptions.show = !!dialogOptions.autoOpen;
359                  break;
360
361                case 'classes':
362                  if (dialogOptions.classes) {
363                    for (var key in dialogOptions.classes) {
364                      if (dialogOptions.classes.hasOwnProperty(key) && classesMap[key] !== void 0) {
365                        // Run through Attributes to sanitize classes.
366                        var attributes = Attributes.create().addClass(dialogOptions.classes[key]).toPlainObject();
367                        var selector = '.' + classesMap[key];
368                        this.$element.find(selector).addClass(attributes['class']);
369                      }
370                    }
371                  }
372                  break;
373
374                case 'closeOnEscape':
375                  mappedOptions.keyboard = !!dialogOptions.closeOnEscape;
376                  if (!dialogOptions.closeOnEscape && dialogOptions.modal) {
377                    mappedOptions.backdrop = 'static';
378                  }
379                  break;
380
381                case 'closeText':
382                  Bootstrap.unsupported('option', name, dialogOptions.closeText);
383                  break;
384
385                case 'draggable':
386                  this.$content
387                    .draggable({
388                      handle: handler.selectors.header,
389                      drag: Bootstrap.relayEvent(this.$element, 'dialogdrag'),
390                      start: Bootstrap.relayEvent(this.$element, 'dialogdragstart'),
391                      end: Bootstrap.relayEvent(this.$element, 'dialogdragend')
392                    })
393                    .draggable(dialogOptions.draggable ? 'enable' : 'disable');
394                  break;
395
396                case 'hide':
397                  if (dialogOptions.hide === false || dialogOptions.hide === true) {
398                    this.$element[dialogOptions.hide ? 'addClass' : 'removeClass']('fade');
399                    mappedOptions.animation = dialogOptions.hide;
400                  }
401                  else {
402                    Bootstrap.unsupported('option', name + ' (complex animation)', dialogOptions.hide);
403                  }
404                  break;
405
406                case 'modal':
407                  if (!dialogOptions.closeOnEscape && dialogOptions.modal) {
408                    mappedOptions.backdrop = 'static';
409                  }
410                  else {
411                    mappedOptions.backdrop = dialogOptions.modal;
412                  }
413
414                  // If not a modal and no initial position, center it.
415                  if (!dialogOptions.modal && !dialogOptions.position) {
416                    this.position({ my: 'center', of: window });
417                  }
418                  break;
419
420                case 'position':
421                  this.position(dialogOptions.position);
422                  break;
423
424                // Resizable support (must initialize first).
425                case 'resizable':
426                  this.$content
427                    .resizable({
428                      resize: Bootstrap.relayEvent(this.$element, 'dialogresize'),
429                      start: Bootstrap.relayEvent(this.$element, 'dialogresizestart'),
430                      end: Bootstrap.relayEvent(this.$element, 'dialogresizeend')
431                    })
432                    .resizable(dialogOptions.resizable ? 'enable' : 'disable');
433                  break;
434
435                case 'show':
436                  if (dialogOptions.show === false || dialogOptions.show === true) {
437                    this.$element[dialogOptions.show ? 'addClass' : 'removeClass']('fade');
438                    mappedOptions.animation = dialogOptions.show;
439                  }
440                  else {
441                    Bootstrap.unsupported('option', name + ' (complex animation)', dialogOptions.show);
442                  }
443                  break;
444
445                case 'title':
446                  this.$title.text(dialogOptions.title);
447                  break;
448
449              }
450            }
451
452            // Add the supported dialog options to the mapped options.
453            mappedOptions.dialogOptions = dialogOptions;
454
455            return mappedOptions;
456          },
457
458          /**
459           * Handler for $.fn.dialog('moveToTop').
460           */
461          moveToTop: function () {
462            Bootstrap.unsupported('method', 'moveToTop', arguments);
463          },
464
465          /**
466           * Handler for $.fn.dialog('option').
467           */
468          option: function () {
469            var clone = {options: {}};
470
471            // Apply the parent option method to the clone of current options.
472            this.super.apply(clone, arguments);
473
474            // Merge in the cloned mapped options.
475            $.extend(true, this.options, this.mapDialogOptions(clone.options));
476
477            // Update buttons.
478            this.createButtons();
479          },
480
481          position: function(position) {
482            // Reset modal styling.
483            this.$element.css({
484              bottom: 'initial',
485              overflow: 'visible',
486              right: 'initial'
487            });
488
489            // Position the modal.
490            this.$element.position(position);
491          },
492
493          /**
494           * Handler for $.fn.dialog('open').
495           */
496          open: function () {
497            this.show.apply(this, arguments);
498          },
499
500          /**
501           * Handler for $.fn.dialog('widget').
502           */
503          widget: function () {
504            return this.$element;
505          }
506        }
507      };
508    };
509
510    // Extend the Bootstrap Modal plugin constructor class.
511    Bootstrap.extendPlugin('modal', Bootstrap.Modal.Bridge);
512
513    // Register default core dialog type handlers.
514    Bootstrap.Dialog.Handler.register('dialog');
515    Bootstrap.Dialog.Handler.register('modal');
516
517    /**
518     * Extend Drupal theming functions.
519     */
520    $.extend(Drupal.theme, /** @lend Drupal.theme */ {
521
522      /**
523       * Renders a jQuery UI Dialog compatible button element.
524       *
525       * @param {Object} button
526       *   The button object passed in the dialog options.
527       *
528       * @return {String}
529       *   The modal dialog button markup.
530       *
531       * @see http://api.jqueryui.com/dialog/#option-buttons
532       * @see http://api.jqueryui.com/button/
533       */
534      bootstrapModalDialogButton: function (button) {
535        var attributes = Attributes.create();
536
537        var icon = '';
538        var iconPosition = button.iconPosition || 'beginning';
539        iconPosition = (iconPosition === 'end' && !rtl) || (iconPosition === 'beginning' && rtl) ? 'after' : 'before';
540
541        // Handle Bootstrap icons differently.
542        if (button.bootstrapIcon) {
543          icon = Drupal.theme('icon', 'bootstrap', button.icon);
544        }
545        // Otherwise, assume it's a jQuery UI icon.
546        // @todo Map jQuery UI icons to Bootstrap icons?
547        else if (button.icon) {
548          var iconAttributes = Attributes.create()
549            .addClass(['ui-icon', button.icon])
550            .set('aria-hidden', 'true');
551          icon = '<span' + iconAttributes + '></span>';
552        }
553
554        // Label. Note: jQuery UI dialog has an inconsistency where it uses
555        // "text" instead of "label", so both need to be supported.
556        var value = button.label || button.text;
557
558        // Show/hide label.
559        if (icon && ((button.showLabel !== void 0 && !button.showLabel) || (button.text !== void 0 && !button.text))) {
560          value = '<span' + Attributes.create().addClass('sr-only') + '>
560' + value + '</span>';
561        }
562        attributes.set('value', iconPosition === 'before' ? icon + value : value + icon);
563
564        // Handle disabled.
565        attributes[button.disabled ? 'set' :'remove']('disabled', 'disabled');
566
567        if (button.classes) {
568          attributes.addClass(Object.keys(button.classes).map(function(key) { return button.classes[key]; }));
569        }
570        if (button['class']) {
571          attributes.addClass(button['class']);
572        }
573        if (button.primary) {
574          attributes.addClass('btn-primary');
575        }
576
577        return Drupal.theme('button', attributes);
578      }
579
580    });
581
582  });
583
584
585})(window.jQuery, window.Drupal, window.Drupal.bootstrap, window.Attributes, window.drupalSettings);

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.