PageSourceSearch

https://misexperience.org.br/wp-content/themes/museuio/assets/js/dialog-polyfill/dialog-polyfill.js?ver=7.1

js misexperience.org.br collected 2026-10-04 19:13:39 UTC 29,167 bytes, 866 lines download raw bytes

vendor: 29,167 bytes, lines 1-866
1(function (global, factory) {
2  typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
3  typeof define === 'function' && define.amd ? define(factory) :
4  (global = global || self, global.dialogPolyfill = factory());
5}(this, function () { 'use strict';
6
7  // nb. This is for IE10 and lower _only_.
8  var supportCustomEvent = window.CustomEvent;
9  if (!supportCustomEvent || typeof supportCustomEvent === 'object') {
10    supportCustomEvent = function CustomEvent(event, x) {
11      x = x || {};
12      var ev = document.createEvent('CustomEvent');
13      ev.initCustomEvent(event, !!x.bubbles, !!x.cancelable, x.detail || null);
14      return ev;
15    };
16    supportCustomEvent.prototype = window.Event.prototype;
17  }
18
19  /**
20   * Dispatches the passed event to both an "on<type>" handler as well as via the
21   * normal dispatch operation. Does not bubble.
22   *
23   * @param {!EventTarget} target
24   * @param {!Event} event
25   * @return {boolean}
26   */
27  function safeDispatchEvent(target, event) {
28    var check = 'on' + event.type.toLowerCase();
29    if (typeof target[check] === 'function') {
30      target[check](event);
31    }
32    return target.dispatchEvent(event);
33  }
34
35  /**
36   * @param {Element} el to check for stacking context
37   * @return {boolean} whether this el or its parents creates a stacking context
38   */
39  function createsStackingContext(el) {
40    while (el && el !== document.body) {
41      var s = window.getComputedStyle(el);
42      var invalid = function(k, ok) {
43        return !(s[k] === undefined || s[k] === ok);
44      };
45
46      if (s.opacity < 1 ||
47          invalid('zIndex', 'auto') ||
48          invalid('transform', 'none') ||
49          invalid('mixBlendMode', 'normal') ||
50          invalid('filter', 'none') ||
51          invalid('perspective', 'none') ||
52          s['isolation'] === 'isolate' ||
53          s.position === 'fixed' ||
54          s.webkitOverflowScrolling === 'touch') {
55        return true;
56      }
57      el = el.parentElement;
58    }
59    return false;
60  }
61
62  /**
63   * Finds the nearest <dialog> from the passed element.
64   *
65   * @param {Element} el to search from
66   * @return {HTMLDialogElement} dialog found
67   */
68  function findNearestDialog(el) {
69    while (el) {
70      if (el.localName === 'dialog') {
71        return /** @type {HTMLDialogElement} */ (el);
72      }
73      if (el.parentElement) {
74        el = el.parentElement;
75      } else if (el.parentNode) {
76        el = el.parentNode.host;
77      } else {
78        el = null;
79      }
80    }
81    return null;
82  }
83
84  /**
85   * Blur the specified element, as long as it's not the HTML body element.
86   * This works around an IE9/10 bug - blurring the body causes Windows to
87   * blur the whole application.
88   *
89   * @param {Element} el to blur
90   */
91  function safeBlur(el) {
92    // Find the actual focused element when the active element is inside a shadow root
93    while (el && el.shadowRoot && el.shadowRoot.activeElement) {
94      el = el.shadowRoot.activeElement;
95    }
96
97    if (el && el.blur && el !== document.body) {
98      el.blur();
99    }
100  }
101
102  /**
103   * @param {!NodeList} nodeList to search
104   * @param {Node} node to find
105   * @return {boolean} whether node is inside nodeList
106   */
107  function inNodeList(nodeList, node) {
108    for (var i = 0; i < nodeList.length; ++i) {
109      if (nodeList[i] === node) {
110        return true;
111      }
112    }
113    return false;
114  }
115
116  /**
117   * @param {HTMLFormElement} el to check
118   * @return {boolean} whether this form has method="dialog"
119   */
120  function isFormMethodDialog(el) {
121    if (!el || !el.hasAttribute('method')) {
122      return false;
123    }
124    return el.getAttribute('method').toLowerCase() === 'dialog';
125  }
126
127  /**
128   * @param {!DocumentFragment|!Element} hostElement
129   * @return {?Element}
130   */
131  function findFocusableElementWithin(hostElement) {
132    // Note that this is 'any focusable area'. This list is probably not exhaustive, but the
133    // alternative involves stepping through and trying to focus everything.
134    var opts = ['button', 'input', 'keygen', 'select', 'textarea'];
135    var query = opts.map(function(el) {
136      return el + ':not([disabled])';
137    });
138    // TODO(samthor): tabindex values that are not numeric are not focusable.
139    query.push('[tabindex]:not([disabled]):not([tabindex=""])');  // tabindex != "", not disabled
140    var target = hostElement.querySelector(query.join(', '));
141
142    if (!target && 'attachShadow' in Element.prototype) {
143      // If we haven't found a focusable target, see if the host element contains an element
144      // which has a shadowRoot.
145      // Recursively search for the first focusable item in shadow roots.
146      var elems = hostElement.querySelectorAll('*');
147      for (var i = 0; i < elems.length; i++) {
148        if (elems[i].tagName && elems[i].shadowRoot) {
149          target = findFocusableElementWithin(elems[i].shadowRoot);
150          if (target) {
151            break;
152          }
153        }
154      }
155    }
156    return target;
157  }
158
159  /**
160   * Determines if an element is attached to the DOM.
161   * @param {Element} element to check
162   * @return {boolean} whether the element is in DOM
163   */
164  function isConnected(element) {
165    return element.isConnected || document.body.contains(element);
166  }
167
168  /**
169   * @param {!Event} event
170   * @return {?Element}
171   */
172  function findFormSubmitter(event) {
173    if (event.submitter) {
174      return event.submitter;
175    }
176
177    var form = event.target;
178    if (!(form instanceof HTMLFormElement)) {
179      return null;
180    }
181
182    var submitter = dialogPolyfill.formSubmitter;
183    if (!submitter) {
184      var target = event.target;
185      var root = ('getRootNode' in target && target.getRootNode() || document);
186      submitter = root.activeElement;
187    }
188
189    if (!submitter || submitter.form !== form) {
190      return null;
191    }
192    return submitter;
193  }
194
195  /**
196   * @param {!Event} event
197   */
198  function maybeHandleSubmit(event) {
199    if (event.defaultPrevented) {
200      return;
201    }
202    var form = /** @type {!HTMLFormElement} */ (event.target);
203
204    // We'd have a value if we clicked on an imagemap.
205    var value = dialogPolyfill.imagemapUseValue;
206    var submitter = findFormSubmitter(event);
207    if (value === null && submitter) {
208      value = submitter.value;
209    }
210
211    // There should always be a dialog as this handler is added specifically on them, but check just
212    // in case.
213    var dialog = findNearestDialog(form);
214    if (!dialog) {
215      return;
216    }
217
218    // Prefer formmethod on the button.
219    var formmethod = submitter && submitter.getAttribute('formmethod') || form.getAttribute('method');
220    if (formmethod !== 'dialog') {
221      return;
222    }
223    event.preventDefault();
224
225    if (value != null) {
226      // nb. we explicitly check against null/undefined
227      dialog.close(value);
228    } else {
229      dialog.close();
230    }
231  }
232
233  /**
234   * @param {!HTMLDialogElement} dialog to upgrade
235   * @constructor
236   */
237  function dialogPolyfillInfo(dialog) {
238    this.dialog_ = dialog;
239    this.replacedStyleTop_ = false;
240    this.openAsModal_ = false;
241
242    // Set a11y role. Browsers that support dialog implicitly know this already.
243    if (!dialog.hasAttribute('role')) {
244      dialog.setAttribute('role', 'dialog');
245    }
246
247    dialog.show = this.show.bind(this);
248    dialog.showModal = this.showModal.bind(this);
249    dialog.close = this.close.bind(this);
250
251    dialog.addEventListener('submit', maybeHandleSubmit, false);
252
253    if (!('returnValue' in dialog)) {
254      dialog.returnValue = '';
255    }
256
257    if ('MutationObserver' in window) {
258      var mo = new MutationObserver(this.maybeHideModal.bind(this));
259      mo.observe(dialog, {attributes: true, attributeFilter: ['open']});
260    } else {
261      // IE10 and below support. Note that DOMNodeRemoved etc fire _before_ removal. They also
262      // seem to fire even if the element was removed as part of a parent removal. Use the removed
263      // events to force downgrade (useful if removed/immediately added).
264      var removed = false;
265      var cb = function() {
266        removed ? this.downgradeModal() : this.maybeHideModal();
267        removed = false;
268      }.bind(this);
269      var timeout;
270      var delayModel = function(ev) {
271        if (ev.target !== dialog) { return; }  // not for a child element
272        var cand = 'DOMNodeRemoved';
273        removed |= (ev.type.substr(0, cand.length) === cand);
274        window.clearTimeout(timeout);
275        timeout = window.setTimeout(cb, 0);
276      };
277      ['DOMAttrModified', 'DOMNodeRemoved', 'DOMNodeRemovedFromDocument'].forEach(function(name) {
278        dialog.addEventListener(name, delayModel);
279      });
280    }
281    // Note that the DOM is observed inside DialogManager while any dialog
282    // is being displayed as a modal, to catch modal removal from the DOM.
283
284    Object.defineProperty(dialog, 'open', {
285      set: this.setOpen.bind(this),
286      get: dialog.hasAttribute.bind(dialog, 'open')
287    });
288
289    this.backdrop_ = document.createElement('div');
290    this.backdrop_.className = 'backdrop';
291    this.backdrop_.addEventListener('mouseup'  , this.backdropMouseEvent_.bind(this));
292    this.backdrop_.addEventListener('mousedown', this.backdropMouseEvent_.bind(this));
293    this.backdrop_.addEventListener('click'    , this.backdropMouseEvent_.bind(this));
294  }
295
296  dialogPolyfillInfo.prototype = /** @type {HTMLDialogElement.prototype} */ ({
297
298    get dialog() {
299      return this.dialog_;
300    },
301
302    /**
303     * Maybe remove this dialog from the modal top layer. This is called when
304     * a modal dialog may no longer be tenable, e.g., when the dialog is no
305     * longer open or is no longer part of the DOM.
306     */
307    maybeHideModal: function() {
308      if (this.dialog_.hasAttribute('open') && isConnected(this.dialog_)) { return; }
309      this.downgradeModal();
310    },
311
312    /**
313     * Remove this dialog from the modal top layer, leaving it as a non-modal.
314     */
315    downgradeModal: function() {
316      if (!this.openAsModal_) { return; }
317      this.openAsModal_ = false;
318      this.dialog_.style.zIndex = '';
319
320      // This won't match the native <dialog> exactly because if the user set top on a centered
321      // polyfill dialog, that top gets thrown away when the dialog is closed. Not sure it's
322      // possible to polyfill this perfectly.
323      if (this.replacedStyleTop_) {
324        this.dialog_.style.top = '';
325        this.replacedStyleTop_ = false;
326      }
327
328      // Clear the backdrop and remove from the manager.
329      this.backdrop_.parentNode && this.backdrop_.parentNode.removeChild(this.backdrop_);
330      dialogPolyfill.dm.removeDialog(this);
331    },
332
333    /**
334     * @param {boolean} value whether to open or close this dialog
335     */
336    setOpen: function(value) {
337      if (value) {
338        this.dialog_.hasAttribute('open') || this.dialog_.setAttribute('open', '');
339      } else {
340        this.dialog_.removeAttribute('open');
341        this.maybeHideModal();  // nb. redundant with MutationObserver
342      }
343    },
344
345    /**
346     * Handles mouse events ('mouseup', 'mousedown', 'click') on the fake .backdrop element, redirecting them as if
347     * they were on the dialog itself.
348     *
349     * @param {!Event} e to redirect
350     */
351    backdropMouseEvent_: function(e) {
352      if (!this.dialog_.hasAttribute('tabindex')) {
353        // Clicking on the backdrop should move the implicit cursor, even if dialog cannot be
354        // focused. Create a fake thing to focus on. If the backdrop was _before_ the dialog, this
355        // would not be needed - clicks would move the implicit cursor there.
356        var fake = document.createElement('div');
357        this.dialog_.insertBefore(fake, this.dialog_.firstChild);
358        fake.tabIndex = -1;
359        fake.focus();
360        this.dialog_.removeChild(fake);
361      } else {
362        this.dialog_.focus();
363      }
364
365      var redirectedEvent = document.createEvent('MouseEvents');
366      redirectedEvent.initMouseEvent(e.type, e.bubbles, e.cancelable, window,
367          e.detail, e.screenX, e.screenY, e.clientX, e.clientY, e.ctrlKey,
368          e.altKey, e.shiftKey, e.metaKey, e.button, e.relatedTarget);
369      this.dialog_.dispatchEvent(redirectedEvent);
370      e.stopPropagation();
371    },
372
373    /**
374     * Focuses on the first focusable element within the dialog. This will always blur the current
375     * focus, even if nothing within the dialog is found.
376     */
377    focus_: function() {
378      // Find element with `autofocus` attribute, or fall back to the first form/tabindex control.
379      var target = this.dialog_.querySelector('[autofocus]:not([disabled])');
380      if (!target && this.dialog_.tabIndex >= 0) {
381        target = this.dialog_;
382      }
383      if (!target) {
384        target = findFocusableElementWithin(this.dialog_);
385      }
386      safeBlur(document.activeElement);
387      target && target.focus();
388    },
389
390    /**
391     * Sets the zIndex for the backdrop and dialog.
392     *
393     * @param {number} dialogZ
394     * @param {number} backdropZ
395     */
396    updateZIndex: function(dialogZ, backdropZ) {
397      if (dialogZ < backdropZ) {
398        throw new Error('dialogZ should never be < backdropZ');
399      }
400      this.dialog_.style.zIndex = dialogZ;
401      this.backdrop_.style.zIndex = backdropZ;
402    },
403
404    /**
405     * Shows the dialog. If the dialog is already open, this does nothing.
406     */
407    show: function() {
408      if (!this.dialog_.open) {
409        this.setOpen(true);
410        this.focus_();
411      }
412    },
413
414    /**
415     * Show this dialog modally.
416     */
417    showModal: function() {
418      if (this.dialog_.hasAttribute('open')) {
419        throw new Error('Failed to execute \'showModal\' on dialog: The element is already open, and therefore cannot be opened modally.');
420      }
421      if (!isConnected(this.dialog_)) {
422        throw new Error('Failed to execute \'showModal\' on dialog: The element is not in a Document.');
423      }
424      if (!dialogPolyfill.dm.pushDialog(this)) {
425        throw new Error('Failed to execute \'showModal\' on dialog: There are too many open modal dialogs.');
426      }
427
428      if (createsStackingContext(this.dialog_.parentElement)) {
429        console.warn('A dialog is being shown inside a stacking context. ' +
430            'This may cause it to be unusable. For more information, see this link: ' +
431            'https://github.com/GoogleChrome/dialog-polyfill/#stacking-context');
432      }
433
434      this.setOpen(true);
435      this.openAsModal_ = true;
436
437      // Optionally center vertically, relative to the current viewport.
438      if (dialogPolyfill.needsCentering(this.dialog_)) {
439        dialogPolyfill.reposition(this.dialog_);
440        this.replacedStyleTop_ = true;
441      } else {
442        this.replacedStyleTop_ = false;
443      }
444
445      // Insert backdrop.
446      this.dialog_.parentNode.insertBefore(this.backdrop_, this.dialog_.nextSibling);
447
448      // Focus on whatever inside the dialog.
449      this.focus_();
450    },
451
452    /**
453     * Closes this HTMLDialogElement. This is optional vs clearing the open
454     * attribute, however this fires a 'close' event.
455     *
456     * @param {string=} opt_returnValue to use as the returnValue
457     */
458    close: function(opt_returnValue) {
459      if (!this.dialog_.hasAttribute('open')) {
460        throw new Error('Failed to execute \'close\' on dialog: The element does not have an \'open\' attribute, and therefore cannot be closed.');
461      }
462      this.setOpen(false);
463
464      // Leave returnValue untouched in case it was set directly on the element
465      if (opt_returnValue !== undefined) {
466        this.dialog_.returnValue = opt_returnValue;
467      }
468
469      // Triggering "close" event for any attached listeners on the <dialog>.
470      var closeEvent = new supportCustomEvent('close', {
471        bubbles: false,
472        cancelable: false
473      });
474      safeDispatchEvent(this.dialog_, closeEvent);
475    }
476
477  });
478
479  var dialogPolyfill = {};
480
481  dialogPolyfill.reposition = function(element) {
482    var scrollTop = document.body.scrollTop || document.documentElement.scrollTop;
483    var topValue = scrollTop + (window.innerHeight - element.offsetHeight) / 2;
484    element.style.top = Math.max(scrollTop, topValue) + 'px';
485  };
486
487  dialogPolyfill.isInlinePositionSetByStylesheet = function(element) {
488    for (var i = 0; i < document.styleSheets.length; ++i) {
489      var styleSheet = document.styleSheets[i];
490      var cssRules = null;
491      // Some browsers throw on cssRules.
492      try {
493        cssRules = styleSheet.cssRules;
494      } catch (e) {}
495      if (!cssRules) { continue; }
496      for (var j = 0; j < cssRules.length; ++j) {
497        var rule = cssRules[j];
498        var selectedNodes = null;
499        // Ignore errors on invalid selector texts.
500        try {
501          selectedNodes = document.querySelectorAll(rule.selectorText);
502        } catch(e) {}
503        if (!selectedNodes || !inNodeList(selectedNodes, element)) {
504          continue;
505        }
506        var cssTop = rule.style.getPropertyValue('top');
507        var cssBottom = rule.style.getPropertyValue('bottom');
508        if ((cssTop && cssTop !== 'auto') || (cssBottom && cssBottom !== 'auto')) {
509          return true;
510        }
511      }
512    }
513    return false;
514  };
515
516  dialogPolyfill.needsCentering = function(dialog) {
517    var computedStyle = window.getComputedStyle(dialog);
518    if (computedStyle.position !== 'absolute') {
519      return false;
520    }
521
522    // We must determine whether the top/bottom specified value is non-auto.  In
523    // WebKit/Blink, checking computedStyle.top == 'auto' is sufficient, but
524    // Firefox returns the used value. So we do this crazy thing instead: check
525    // the inline style and then go through CSS rules.
526    if ((dialog.style.top !== 'auto' && dialog.style.top !== '') ||
527        (dialog.style.bottom !== 'auto' && dialog.style.bottom !== '')) {
528      return false;
529    }
530    return !dialogPolyfill.isInlinePositionSetByStylesheet(dialog);
531  };
532
533  /**
534   * @param {!Element} element to force upgrade
535   */
536  dialogPolyfill.forceRegisterDialog = function(element) {
537    if (window.HTMLDialogElement || element.showModal) {
538      console.warn('This browser already supports <dialog>, the polyfill ' +
539          'may not work correctly', element);
540    }
541    if (element.localName !== 'dialog') {
542      throw new Error('Failed to register dialog: The element is not a dialog.');
543    }
544    new dialogPolyfillInfo(/** @type {!HTMLDialogElement} */ (element));
545  };
546
547  /**
548   * @param {!Element} element to upgrade, if necessary
549   */
550  dialogPolyfill.registerDialog = function(element) {
551    if (!element.showModal) {
552      dialogPolyfill.forceRegisterDialog(element);
553    }
554  };
555
556  /**
557   * @constructor
558   */
559  dialogPolyfill.DialogManager = function() {
560    /** @type {!Array<!dialogPolyfillInfo>} */
561    this.pendingDialogStack = [];
562
563    var checkDOM = this.checkDOM_.bind(this);
564
565    // The overlay is used to simulate how a modal dialog blocks the document.
566    // The blocking dialog is positioned on top of the overlay, and the rest of
567    // the dialogs on the pending dialog stack are positioned below it. In the
568    // actual implementation, the modal dialog stacking is controlled by the
569    // top layer, where z-index has no effect.
570    this.overlay = document.createElement('div');
571    this.overlay.className = '_dialog_overlay';
572    this.overlay.addEventListener('click', function(e) {
573      this.forwardTab_ = undefined;
574      e.stopPropagation();
575      checkDOM([]);  // sanity-check DOM
576    }.bind(this));
577
578    this.handleKey_ = this.handleKey_.bind(this);
579    this.handleFocus_ = this.handleFocus_.bind(this);
580
581    this.zIndexLow_ = 100000;
582    this.zIndexHigh_ = 100000 + 150;
583
584    this.forwardTab_ = undefined;
585
586    if ('MutationObserver' in window) {
587      this.mo_ = new MutationObserver(function(records) {
588        var removed = [];
589        records.forEach(function(rec) {
590          for (var i = 0, c; c = rec.removedNodes[i]; ++i) {
591            if (!(c instanceof Element)) {
592              continue;
593            } else if (c.localName === 'dialog') {
594              removed.push(c);
595            }
596            removed = removed.concat(c.querySelectorAll('dialog'));
597          }
598        });
599        removed.length && checkDOM(removed);
600      });
601    }
602  };
603
604  /**
605   * Called on the first modal dialog being shown. Adds the overlay and related
606   * handlers.
607   */
608  dialogPolyfill.DialogManager.prototype.blockDocument = function() {
609    document.documentElement.addEventListener('focus', this.handleFocus_, true);
610    document.addEventListener('keydown', this.handleKey_);
611    this.mo_ && this.mo_.observe(document, {childList: true, subtree: true});
612  };
613
614  /**
615   * Called on the first modal dialog being removed, i.e., when no more modal
616   * dialogs are visible.
617   */
618  dialogPolyfill.DialogManager.prototype.unblockDocument = function() {
619    document.documentElement.removeEventListener('focus', this.handleFocus_, true);
620    document.removeEventListener('keydown', this.handleKey_);
621    this.mo_ && this.mo_.disconnect();
622  };
623
624  /**
625   * Updates the stacking of all known dialogs.
626   */
627  dialogPolyfill.DialogManager.prototype.updateStacking = function() {
628    var zIndex = this.zIndexHigh_;
629
630    for (var i = 0, dpi; dpi = this.pendingDialogStack[i]; ++i) {
631      dpi.updateZIndex(--zIndex, --zIndex);
632      if (i === 0) {
633        this.overlay.style.zIndex = --zIndex;
634      }
635    }
636
637    // Make the overlay a sibling of the dialog itself.
638    var last = this.pendingDialogStack[0];
639    if (last) {
640      var p = last.dialog.parentNode || document.body;
641      p.appendChild(this.overlay);
642    } else if (this.overlay.parentNode) {
643      this.overlay.parentNode.removeChild(this.overlay);
644    }
645  };
646
647  /**
648   * @param {Element} candidate to check if contained or is the top-most modal dialog
649   * @return {boolean} whether candidate is contained in top dialog
650   */
651  dialogPolyfill.DialogManager.prototype.containedByTopDialog_ = function(candidate) {
652    while (candidate = findNearestDialog(candidate)) {
653      for (var i = 0, dpi; dpi = this.pendingDialogStack[i]; ++i) {
654        if (dpi.dialog === candidate) {
655          return i === 0;  // only valid if top-most
656        }
657      }
658      candidate = candidate.parentElement;
659    }
660    return false;
661  };
662
663  dialogPolyfill.DialogManager.prototype.handleFocus_ = function(event) {
664    var target = event.composedPath ? event.composedPath()[0] : event.target;
665
666    if (this.containedByTopDialog_(target)) { return; }
667
668    if (document.activeElement === document.documentElement) { return; }
669
670    event.preventDefault();
671    event.stopPropagation();
672    safeBlur(/** @type {Element} */ (target));
673
674    if (this.forwardTab_ === undefined) { return; }  // move focus only from a tab key
675
676    var dpi = this.pendingDialogStack[0];
677    var dialog = dpi.dialog;
678    var position = dialog.compareDocumentPosition(target);
679    if (position & Node.DOCUMENT_POSITION_PRECEDING) {
680      if (this.forwardTab_) {
681        // forward
682        dpi.focus_();
683      } else if (target !== document.documentElement) {
684        // backwards if we're not already focused on <html>
685        document.documentElement.focus();
686      }
687    }
688
689    return false;
690  };
691
692  dialogPolyfill.DialogManager.prototype.handleKey_ = function(event) {
693    this.forwardTab_ = undefined;
694    if (event.keyCode === 27) {
695      event.preventDefault();
696      event.stopPropagation();
697      var cancelEvent = new supportCustomEvent('cancel', {
698        bubbles: false,
699        cancelable: true
700      });
701      var dpi = this.pendingDialogStack[0];
702      if (dpi && safeDispatchEvent(dpi.dialog, cancelEvent)) {
703        dpi.dialog.close();
704      }
705    } else if (event.keyCode === 9) {
706      this.forwardTab_ = !event.shiftKey;
707    }
708  };
709
710  /**
711   * Finds and downgrades any known modal dialogs that are no longer displayed. Dialogs that are
712   * removed and immediately readded don't stay modal, they become normal.
713   *
714   * @param {!Array<!HTMLDialogElement>} removed that have definitely been removed
715   */
716  dialogPolyfill.DialogManager.prototype.checkDOM_ = function(removed) {
717    // This operates on a clone because it may cause it to change. Each change also calls
718    // updateStacking, which only actually needs to happen once. But who removes many modal dialogs
719    // at a time?!
720    var clone = this.pendingDialogStack.slice();
721    clone.forEach(function(dpi) {
722      if (removed.indexOf(dpi.dialog) !== -1) {
723        dpi.downgradeModal();
724      } else {
725        dpi.maybeHideModal();
726      }
727    });
728  };
729
730  /**
731   * @param {!dialogPolyfillInfo} dpi
732   * @return {boolean} whether the dialog was allowed
733   */
734  dialogPolyfill.DialogManager.prototype.pushDialog = function(dpi) {
735    var allowed = (this.zIndexHigh_ - this.zIndexLow_) / 2 - 1;
736    if (this.pendingDialogStack.length >= allowed) {
737      return false;
738    }
739    if (this.pendingDialogStack.unshift(dpi) === 1) {
740      this.blockDocument();
741    }
742    this.updateStacking();
743    return true;
744  };
745
746  /**
747   * @param {!dialogPolyfillInfo} dpi
748   */
749  dialogPolyfill.DialogManager.prototype.removeDialog = function(dpi) {
750    var index = this.pendingDialogStack.indexOf(dpi);
751    if (index === -1) { return; }
752
753    this.pendingDialogStack.splice(index, 1);
754    if (this.pendingDialogStack.length === 0) {
755      this.unblockDocument();
756    }
757    this.updateStacking();
758  };
759
760  dialogPolyfill.dm = new dialogPolyfill.DialogManager();
761  dialogPolyfill.formSubmitter = null;
762  dialogPolyfill.imagemapUseValue = null;
763
764  /**
765   * Installs global handlers, such as click listers and native method overrides. These are needed
766   * even if a no dialog is registered, as they deal with <form method="dialog">.
767   */
768  if (window.HTMLDialogElement === undefined) {
769
770    /**
771     * If HTMLFormElement translates method="DIALOG" into 'get', then replace the descriptor with
772     * one that returns the correct value.
773     */
774    var testForm = document.createElement('form');
775    testForm.setAttribute('method', 'dialog');
776    if (testForm.method !== 'dialog') {
777      var methodDescriptor = Object.getOwnPropertyDescriptor(HTMLFormElement.prototype, 'method');
778      if (methodDescriptor) {
779        // nb. Some older iOS and older PhantomJS fail to return the descriptor. Don't do anything
780        // and don't bother to update the element.
781        var realGet = methodDescriptor.get;
782        methodDescriptor.get = function() {
783          if (isFormMethodDialog(this)) {
784            return 'dialog';
785          }
786          return realGet.call(this);
787        };
788        var realSet = methodDescriptor.set;
789        /** @this {HTMLElement} */
790        methodDescriptor.set = function(v) {
791          if (typeof v === 'string' && v.toLowerCase() === 'dialog') {
792            return this.setAttribute('method', v);
793          }
794          return realSet.call(this, v);
795        };
796        Object.defineProperty(HTMLFormElement.prototype, 'method', methodDescriptor);
797      }
798    }
799
800    /**
801     * Global 'click' handler, to capture the <input type="submit"> or <button> element which has
802     * submitted a <form method="dialog">. Needed as Safari and others don't report this inside
803     * document.activeElement.
804     */
805    document.addEventListener('click', function(ev) {
806      dialogPolyfill.formSubmitter = null;
807      dialogPolyfill.imagemapUseValue = null;
808      if (ev.defaultPrevented) { return; }  // e.g. a submit which prevents default submission
809
810      var target = /** @type {Element} */ (ev.target);
811      if ('composedPath' in ev) {
812        var path = ev.composedPath();
813        target = path.shift() || target;
814      }
815      if (!target || !isFormMethodDialog(target.form)) { return; }
816
817      var valid = (target.type === 'submit' && ['button', 'input'].indexOf(target.localName) > -1);
818      if (!valid) {
819        if (!(target.localName === 'input' && target.type === 'image')) { return; }
820        // this is a <input type="image">, which can submit forms
821        dialogPolyfill.imagemapUseValue = ev.offsetX + ',' + ev.offsetY;
822      }
823
824      var dialog = findNearestDialog(target);
825      if (!dialog) { return; }
826
827      dialogPolyfill.formSubmitter = target;
828
829    }, false);
830
831    /**
832     * Global 'submit' handler. This handles submits of `method="dialog"` which are invalid, i.e.,
833     * outside a dialog. They get prevented.
834     */
835    document.addEventListener('submit', function(ev) {
836      var form = ev.target;
837      var dialog = findNearestDialog(form);
838      if (dialog) {
839        return;  // ignore, handle there
840      }
841
842      var submitter = findFormSubmitter(ev);
843      var formmethod = submitter && submitter.getAttribute('formmethod') || form.getAttribute('method');
844      if (formmethod === 'dialog') {
845        ev.preventDefault();
846      }
847    });
848
849    /**
850     * Replace the native HTMLFormElement.submit() method, as it won't fire the
851     * submit event and give us a chance to respond.
852     */
853    var nativeFormSubmit = HTMLFormElement.prototype.submit;
854    var replacementFormSubmit = function () {
855      if (!isFormMethodDialog(this)) {
856        return nativeFormSubmit.call(this);
857      }
858      var dialog = findNearestDialog(this);
859      dialog && dialog.close();
860    };
861    HTMLFormElement.prototype.submit = replacementFormSubmit;
862  }
863
864  return dialogPolyfill;
865
866}));

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.