PageSourceSearch

https://www.hipoisoncenter.org/themes/contrib/bootstrap/js/modal.js?tjnbs4

js hipoisoncenter.org collected 2026-10-04 11:39:51 UTC 18,983 bytes, 595 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 */
10(function ($, Drupal, Bootstrap, Attributes) {
11  'use strict';
12
13  /**
14   * Document jQuery object.
15   *
16   * @type {jQuery}
17   */
18  var $document = $(document);
19
20  /**
21   * Finds the first available and visible focusable input element.
22   *
23   * This is abstracted from the main code below so sub-themes can override
24   * this method to return their own element if desired.
25   *
26   * @param {Modal} modal
27   *   The Bootstrap modal instance.
28   *
29   * @return {jQuery}
30   *   A jQuery object containing the element that should be focused. Note: if
31   *   this object contains multiple elements, only the first visible one will
32   *   be used.
33   */
34  Bootstrap.modalFindFocusableElement = function (modal) {
35    return modal.$dialogBody.find(':input,:button,.btn').not('.visually-hidden,.sr-only');
36  };
37
38  $document.on('shown.bs.modal', function (e) {
39    var $modal = $(e.target);
40    var modal = $modal.data('bs.modal');
41
42    // Focus the first input element found.
43    if (modal && modal.options.focusInput) {
44      var $focusable = Bootstrap.modalFindFocusableElement(modal);
45      if ($focusable && $focusable[0]) {
46        var $input = $focusable.filter(':visible:first').focus();
47
48        // Select text if input is text.
49        if (modal.options.selectText && $input.is(':text')) {
50          $input[0].setSelectionRange(0, $input[0].value.length)
51        }
52      }
53      else if (modal.$close.is(':visible')) {
54        modal.$close.focus();
55      }
56    }
57  });
58
59  /**
60   * Only process this once.
61   */
62  Bootstrap.once('modal', function (settings) {
63
64    /**
65     * Replace the Bootstrap Modal jQuery plugin definition.
66     *
67     * This adds a little bit of functionality so it works better with Drupal.
68     */
69    Bootstrap.replacePlugin('modal', function () {
70      var BootstrapModal = this;
71
72      // Override the Modal constructor.
73      Bootstrap.Modal = function (element, options) {
74        this.$body               = $(document.body);
75        this.$element            = $(element);
76        this.$dialog             = this.$element.find('.modal-dialog');
77        this.$header             = this.$dialog.find('.modal-header');
78        this.$title              = this.$dialog.find('.modal-title');
79        this.$close              = this.$header.find('.close');
80        this.$footer             = this.$dialog.find('.modal-footer');
81        this.$content            = this.$dialog.find('.modal-content');
82        this.$dialogBody         = this.$dialog.find('.modal-body');
83        this.$backdrop           = null;
84        this.isShown             = null;
85        this.originalBodyPad     = null;
86        this.scrollbarWidth      = 0;
87        this.ignoreBackdropClick = false;
88        this.options             = this.mapDialogOptions(options);
89      };
90
91      // Extend defaults to take into account for theme settings.
92      Bootstrap.Modal.DEFAULTS = $.extend({}, BootstrapModal.DEFAULTS, {
93        animation: !!settings.modal_animation,
94        backdrop: settings.modal_backdrop === 'static' ? 'static' : !!settings.modal_backdrop,
95        focusInput: !!settings.modal_focus_input,
96        selectText: !!settings.modal_select_text,
97        keyboard: !!settings.modal_keyboard,
98        remote: null,
99        show: !!settings.modal_show,
100        size: settings.modal_size
101      });
102
103      // Copy over the original prototype methods.
104      Bootstrap.Modal.prototype = BootstrapModal.prototype;
105
106      /**
107       * Handler for $.fn.modal('destroy').
108       */
109      Bootstrap.Modal.prototype.destroy = function () {
110        this.hide();
111        Drupal.detachBehaviors(this.$element[0]);
112        this.$element.removeData('bs.modal').remove();
113      };
114
115      /**
116       * Initialize the modal.
117       */
118      Bootstrap.Modal.prototype.init = function () {
119        if (this.options.remote) {
120          this.$content.load(this.options.remote, $.proxy(function () {
121            this.$element.trigger('loaded.bs.modal');
122          }, this));
123        }
124      };
125
126      /**
127       * Map dialog options.
128       *
129       * Note: this is primarily for use in modal.jquery.ui.bridge.js.
130       *
131       * @param {Object} options
132       *   The passed options.
133       */
134      Bootstrap.Modal.prototype.mapDialogOptions = function (options) {
135        return options || {};
136      }
137
138      // Modal jQuery Plugin Definition.
139      var Plugin = function () {
140        // Extract the arguments.
141        var args = Array.prototype.slice.call(arguments);
142        var method = args[0];
143        var options = args[1] || {};
144        var relatedTarget = args[2] || null;
145        // Move arguments down if no method was passed.
146        if ($.isPlainObject(method)) {
147          relatedTarget = options || null;
148          options = method;
149          method = null;
150        }
151        var ret = void 0;
152        this.each(function () {
153          var $this   = $(this);
154          var data    = $this.data('bs.modal');
155          var initialize = false;
156
157          // Immediately return if there's no instance to invoke a valid method.
158          var showMethods = ['open', 'show', 'toggle'];
159          if (!data && method && showMethods.indexOf(method) === -1) {
160            return;
161          }
162
163          options = Bootstrap.normalizeObject($.extend({}, Bootstrap.Modal.DEFAULTS, data && data.options, $this.data(), options));
164          delete options['bs.modal'];
165
166          if (!data) {
167            $this.data('bs.modal', (data = new Bootstrap.Modal(this, options)));
168            initialize = true;
169          }
170
171          // Initialize the modal.
172          if (initialize || (!method && !args.length)) {
173            data.init();
174          }
175
176          // Explicit method passed.
177          if (method) {
178            if (typeof data[method] === 'function') {
179              try {
180                ret = data[method].apply(data, args.slice(1));
181              }
182              catch (e) {
183                Drupal.throwError(e);
184              }
185            }
186            else {
187              Bootstrap.unsupported('method', method);
188            }
189          }
190          // No method, set options and open if necessary.
191          else {
192            data.option(options);
193            if (options.show && !data.isShown) {
194              data.show(relatedTarget);
195            }
196          }
197        });
198
199        // If just one element and there was a result returned for the option passed,
200        // then return the result. Otherwise, just return the jQuery object.
201        return this.length === 1 && ret !== void 0 ? ret : this;
202      };
203
204      // Replace the plugin constructor with the new Modal constructor.
205      Plugin.Constructor = Bootstrap.Modal;
206
207      // Replace the data API so that it calls $.fn.modal rather than Plugin.
208      // This allows sub-themes to replace the jQuery Plugin if they like with
209      // out having to redo all this boilerplate.
210      $document
211        .off('click.bs.modal.data-api')
212        .on('click.bs.modal.data-api', '[data-toggle="modal"]', function (e) {
213          var $this   = $(this);
214          var href    = $this.attr('href');
215          var target  = $this.attr('data-target') || (href && href.replace(/.*(?=#[^\s]+$)/, '')); // strip for ie7
216          var $target = $document.find(target);
217          var options  = $target.data('bs.modal') ? 'toggle' : $.extend({ remote: !/#/.test(href) && href }, $target.data(), $this.data());
218
219          if ($this.is('a')) e.preventDefault();
220
221          $target.one('show.bs.modal', function (showEvent) {
222            // Only register focus restorer if modal will actually get shown.
223            if (showEvent.isDefaultPrevented()) return;
224            $target.one('hidden.bs.modal', function () {
225              $this.is(':visible') && $this.trigger('focus');
226            });
227          });
228          $target.modal(options, this);
229        });
230
231      return Plugin;
232    });
233
234    /**
235     * Extend Drupal theming functions.
236     */
237    $.extend(Drupal.theme, /** @lend Drupal.theme */ {
238      /**
239       * Theme function for a Bootstrap Modal.
240       *
241       * @param {Object} [variables]
242       *   An object containing key/value pairs of variables.
243       *
244       * @return {string}
245       *   The HTML for the modal.
246       */
247      bootstrapModal: function (variables) {
248        var output = '';
249        var settings = drupalSettings.bootstrap || {};
250        var defaults = {
251          attributes: {
252            class: ['modal'],
253            tabindex: -1,
254            role: 'dialog'
255          },
256          body: '',
257          closeButton: true,
258          description: {
259            attributes: {
260              class: ['help-block']
261            },
262            content: null,
263            position: 'before'
264          },
265          footer: '',
266          id: 'drupal-modal',
267          size: settings.modal_size ? settings.modal_size : '',
268          title: {
269            attributes: {
270              class: ['modal-title']
271            },
272            content: Drupal.t('Loading...'),
273            html: false,
274            tag: 'h4'
275          }
276        };
277        variables = $.extend(true, {}, defaults, variables);
278
279        var attributes = Attributes.create(defaults.attributes).merge(variables.attributes);
280        attributes.set('id', attributes.get('id', variables.id));
281
282        if (settings.modal_animation) {
283          attributes.addClass('fade');
284        }
285
286        // Build the modal wrapper.
287        output += '<div' + attributes + '>';
288
289        // Build the modal-dialog wrapper.
290        output += Drupal.theme('bootstrapModalDialog', _.omit(variables, 'attributes'));
291
292        // Close the modal wrapper.
293        output += '</div>';
294
295        // Return the constructed modal.
296        return output;
297      },
298
299      /**
300       * Theme function for a Bootstrap Modal dialog markup.
301       *
302       * @param {Object} [variables]
303       *   An object containing key/value pairs of variables.
304       *
305       * @return {string}
306       *   The HTML for the modal close button.
307       */
308      bootstrapModalDialog: function (variables) {
309        var output = '';
310
311        var defaults = {
312          attributes: {
313            class: ['modal-dialog'],
314            role: 'document'
315          },
316          id: 'drupal-modal'
317        };
318        variables = $.extend(true, {}, defaults, variables);
319
320        var attributes = Attributes.create(defaults.attributes).merge(variables.attributes);
321        attributes.set('id', attributes.get('id', variables.id + '--dialog'));
322
323        if (variables.size) {
324          attributes.addClass(variables.size);
325        }
326        output += '<div' + attributes + '>';
327
328        // Build the modal-content wrapper.
329        output += Drupal.theme('bootstrapModalContent', _.omit(variables, 'attributes'));
330
331        // Close the modal-dialog wrapper.
332        output += '</div>';
333        return output;
334      },
335
336      /**
337       * Theme function for a Bootstrap Modal content markup.
338       *
339       * @param {Object} [variables]
340       *   An object containing key/value pairs of variables.
341       *
342       * @return {string}
343       *   The HTML for the modal close button.
344       */
345      bootstrapModalContent: function (variables) {
346        var output = '';
347
348        var defaults = {
349          attributes: {
350            class: ['modal-content']
351          },
352          id: 'drupal-modal'
353        };
354        variables = $.extend(true, {}, defaults, variables);
355
356        var attributes = Attributes.create(defaults.attributes).merge(variables.attributes);
357        attributes.set('id', attributes.get('id', variables.id + '--content'));
358
359        // Build the modal-content wrapper.
360        output += '<div' + attributes + '>';
361        variables = _.omit(variables, 'attributes');
362
363        // Build the header wrapper and title.
364        output += Drupal.theme('bootstrapModalHeader', variables);
365
366        // Build the body.
367        output += Drupal.theme('bootstrapModalBody', variables);
368
369        // Build the footer.
370        output += Drupal.theme('bootstrapModalFooter', variables);
371
372        // Close the modal-content wrapper.
373        output += '</div>';
374
375        return output;
376      },
377
378      /**
379       * Theme function for a Bootstrap Modal body markup.
380       *
381       * @param {Object} [variables]
382       *   An object containing key/value pairs of variables.
383       *
384       * @return {string}
385       *   The HTML for the modal close button.
386       */
387      bootstrapModalBody: function (variables) {
388        var output = '';
389
390        var defaults = {
391          attributes: {
392            class: ['modal-body']
393          },
394          body: '',
395          description: {
396            attributes: {
397              class: ['help-block']
398            },
399            content: null,
400            position: 'before'
401          },
402          id: 'drupal-modal'
403        };
404        variables = $.extend(true, {}, defaults, variables);
405
406        var attributes = Attributes.create(defaults.attributes).merge(variables.attributes);
407        attributes.set('id', attributes.get('id', variables.id + '--body'));
408
409        output += '<div' + attributes + '>';
410
411        if (typeof variables.description === 'string') {
412          variables.description = $.extend({}, defaults.description, { content: variables.description });
413        }
414
415        var description = variables.description;
416        description.attributes = Attributes.create(defaults.description.attributes).merge(description.attributes);
417
418        if (description.content && description.position === 'invisible') {
419          description.attributes.addClass('sr-only');
420        }
421
422        if (description.content && description.position === 'before') {
423          output += '<p' + description.attributes + '>' + description.content + '</p>';
424        }
425
426        output += variables.body;
427
428        if (description.content && (description.position === 'after' || description.position === 'invisible')) {
429          output += '<p' + description.attributes + '>' + description.content + '</p>';
430        }
431
432        output += '</div>';
433
434        return output;
435      },
436
437      /**
438       * Theme function for a Bootstrap Modal close button.
439       *
440       * @param {Object} [variables]
441       *   An object containing key/value pairs of variables.
442       *
443       * @return {string}
444       *   The HTML for the modal close button.
445       */
446      bootstrapModalClose: function (variables) {
447        var defaults = {
448          attributes: {
449            'aria-label': Drupal.t('Close'),
450            class: ['close'],
451            'data-dismiss': 'modal',
452            type: 'button'
453          }
454        };
455        variables = $.extend(true, {}, defaults, variables);
456        var attributes = Attributes.create(defaults.attributes).merge(variables.attributes);
457        return '<button' + attributes + '><span aria-hidden="true">&times;</span></button>';
458      },
459
460      /**
461       * Theme function for a Bootstrap Modal footer.
462       *
463       * @param {Object} [variables]
464       *   An object containing key/value pairs of variables.
465       * @param {boolean} [force]
466       *   Flag to force rendering the footer, regardless if there's content.
467       *
468       * @return {string}
469       *   The HTML for the modal footer.
470       */
471      bootstrapModalFooter: function (variables, force) {
472        var output = '';
473        var defaults = {
474          attributes: {
475            class: ['modal-footer']
476          },
477          footer: '',
478          id: 'drupal-modal'
479        };
480
481        variables = $.extend(true, {}, defaults, variables);
482
483        if (force || variables.footer) {
484          var attributes = Attributes.create(defaults.attributes).merge(variables.attributes);
485          attributes.set('id', attributes.get('id', variables.id + '--footer'));
486          output += '<div' + attributes + '>';
487          output += variables.footer;
488          output += '</div>';
489        }
490
491        return output;
492      },
493
494      /**
495       * Theme function for a Bootstrap Modal header.
496       *
497       * @param {Object} [variables]
498       *   An object containing key/value pairs of variables.
499       *
500       * @return {string}
501       *   The HTML for the modal header.
502       */
503      bootstrapModalHeader: function (variables) {
504        var output = '';
505
506        var defaults = {
507          attributes: {
508            class: ['modal-header']
509          },
510          closeButton: true,
511          id: 'drupal-modal',
512          title: {
513            attributes: {
514              class: ['modal-title']
515            },
516            content: Drupal.t('Loading...'),
517            html: false,
518            tag: 'h4'
519          }
520        };
521        variables = $.extend(true, {}, defaults, variables);
522
523        if (typeof variables.title === 'string') {
524          variables.title = $.extend({}, defaults.title, { content: variables.title });
525        }
526
527        var title = Drupal.theme('bootstrapModalTitle', variables.title);
528        if (title) {
529          var attributes = Attributes.create(defaults.attributes).merge(variables.attributes);
530          attributes.set('id', attributes.get('id', variables.id + '--header'));
531
532          output += '<div' + attributes + '>';
533
534          if (variables.closeButton) {
535            output += Drupal.theme('bootstrapModalClose', _.omit(variables, 'attributes'));
536          }
537
538          output += title;
539
540          output += '</div>';
541        }
542
543        return output;
544      },
545
546      /**
547       * Theme function for a Bootstrap Modal title.
548       *
549       * @param {Object} [variables]
550       *   An object containing key/value pairs of variables.
551       *
552       * @return {string}
553       *   The HTML for the modal title.
554       */
555      bootstrapModalTitle: function (variables) {
556        var output = '';
557
558        var defaults = {
559          attributes: {
560            class: ['modal-title']
561          },
562          closeButton: true,
563          id: 'drupal-modal',
564          content: Drupal.t('Loading...'),
565          html: false,
566          tag: 'h4'
567        };
568
569        if (typeof variables === 'string') {
570          variables = $.extend({}, defaults, { content: title });
571        }
572
573        variables = $.extend(true, {}, defaults, variables);
574
575        var attributes = Attributes.create(defaults.attributes).merge(variables.attributes);
576        attributes.set('id', attributes.get('id', variables.id + '--title'));
577
578        output += '<' + Drupal.checkPlain(variables.tag) + Attributes.create(defaults.attributes).merge(variables.attributes) + '>';
579
580        if (variables.closeButton) {
581          output += Drupal.theme('bootstrapModalClose', _.omit(variables, 'attributes'));
582        }
583
584        output += (variables.html ? variables.content : Drupal.checkPlain(variables.content));
585
586        output += '</' + Drupal.checkPlain(variables.tag) + '>';
587
588        return output;
589      }
590
591    })
592
593  });
594
595})(window.jQuery, window.Drupal, window.Drupal.bootstrap, window.Attributes);

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.