Project import generated by Copybara.

GitOrigin-RevId: 63746295f1a5ab5a619056791995793d65529e62
diff --git a/node_modules/dialog-polyfill/dist/dialog-polyfill.esm.js b/node_modules/dialog-polyfill/dist/dialog-polyfill.esm.js
new file mode 100644
index 0000000..d866b94
--- /dev/null
+++ b/node_modules/dialog-polyfill/dist/dialog-polyfill.esm.js
@@ -0,0 +1,728 @@
+// nb. This is for IE10 and lower _only_.
+var supportCustomEvent = window.CustomEvent;
+if (!supportCustomEvent || typeof supportCustomEvent === 'object') {
+  supportCustomEvent = function CustomEvent(event, x) {
+    x = x || {};
+    var ev = document.createEvent('CustomEvent');
+    ev.initCustomEvent(event, !!x.bubbles, !!x.cancelable, x.detail || null);
+    return ev;
+  };
+  supportCustomEvent.prototype = window.Event.prototype;
+}
+
+/**
+ * @param {Element} el to check for stacking context
+ * @return {boolean} whether this el or its parents creates a stacking context
+ */
+function createsStackingContext(el) {
+  while (el && el !== document.body) {
+    var s = window.getComputedStyle(el);
+    var invalid = function(k, ok) {
+      return !(s[k] === undefined || s[k] === ok);
+    };
+    
+    if (s.opacity < 1 ||
+        invalid('zIndex', 'auto') ||
+        invalid('transform', 'none') ||
+        invalid('mixBlendMode', 'normal') ||
+        invalid('filter', 'none') ||
+        invalid('perspective', 'none') ||
+        s['isolation'] === 'isolate' ||
+        s.position === 'fixed' ||
+        s.webkitOverflowScrolling === 'touch') {
+      return true;
+    }
+    el = el.parentElement;
+  }
+  return false;
+}
+
+/**
+ * Finds the nearest <dialog> from the passed element.
+ *
+ * @param {Element} el to search from
+ * @return {HTMLDialogElement} dialog found
+ */
+function findNearestDialog(el) {
+  while (el) {
+    if (el.localName === 'dialog') {
+      return /** @type {HTMLDialogElement} */ (el);
+    }
+    el = el.parentElement;
+  }
+  return null;
+}
+
+/**
+ * Blur the specified element, as long as it's not the HTML body element.
+ * This works around an IE9/10 bug - blurring the body causes Windows to
+ * blur the whole application.
+ *
+ * @param {Element} el to blur
+ */
+function safeBlur(el) {
+  if (el && el.blur && el !== document.body) {
+    el.blur();
+  }
+}
+
+/**
+ * @param {!NodeList} nodeList to search
+ * @param {Node} node to find
+ * @return {boolean} whether node is inside nodeList
+ */
+function inNodeList(nodeList, node) {
+  for (var i = 0; i < nodeList.length; ++i) {
+    if (nodeList[i] === node) {
+      return true;
+    }
+  }
+  return false;
+}
+
+/**
+ * @param {HTMLFormElement} el to check
+ * @return {boolean} whether this form has method="dialog"
+ */
+function isFormMethodDialog(el) {
+  if (!el || !el.hasAttribute('method')) {
+    return false;
+  }
+  return el.getAttribute('method').toLowerCase() === 'dialog';
+}
+
+/**
+ * @param {!HTMLDialogElement} dialog to upgrade
+ * @constructor
+ */
+function dialogPolyfillInfo(dialog) {
+  this.dialog_ = dialog;
+  this.replacedStyleTop_ = false;
+  this.openAsModal_ = false;
+
+  // Set a11y role. Browsers that support dialog implicitly know this already.
+  if (!dialog.hasAttribute('role')) {
+    dialog.setAttribute('role', 'dialog');
+  }
+
+  dialog.show = this.show.bind(this);
+  dialog.showModal = this.showModal.bind(this);
+  dialog.close = this.close.bind(this);
+
+  if (!('returnValue' in dialog)) {
+    dialog.returnValue = '';
+  }
+
+  if ('MutationObserver' in window) {
+    var mo = new MutationObserver(this.maybeHideModal.bind(this));
+    mo.observe(dialog, {attributes: true, attributeFilter: ['open']});
+  } else {
+    // IE10 and below support. Note that DOMNodeRemoved etc fire _before_ removal. They also
+    // seem to fire even if the element was removed as part of a parent removal. Use the removed
+    // events to force downgrade (useful if removed/immediately added).
+    var removed = false;
+    var cb = function() {
+      removed ? this.downgradeModal() : this.maybeHideModal();
+      removed = false;
+    }.bind(this);
+    var timeout;
+    var delayModel = function(ev) {
+      if (ev.target !== dialog) { return; }  // not for a child element
+      var cand = 'DOMNodeRemoved';
+      removed |= (ev.type.substr(0, cand.length) === cand);
+      window.clearTimeout(timeout);
+      timeout = window.setTimeout(cb, 0);
+    };
+    ['DOMAttrModified', 'DOMNodeRemoved', 'DOMNodeRemovedFromDocument'].forEach(function(name) {
+      dialog.addEventListener(name, delayModel);
+    });
+  }
+  // Note that the DOM is observed inside DialogManager while any dialog
+  // is being displayed as a modal, to catch modal removal from the DOM.
+
+  Object.defineProperty(dialog, 'open', {
+    set: this.setOpen.bind(this),
+    get: dialog.hasAttribute.bind(dialog, 'open')
+  });
+
+  this.backdrop_ = document.createElement('div');
+  this.backdrop_.className = 'backdrop';
+  this.backdrop_.addEventListener('click', this.backdropClick_.bind(this));
+}
+
+dialogPolyfillInfo.prototype = {
+
+  get dialog() {
+    return this.dialog_;
+  },
+
+  /**
+   * Maybe remove this dialog from the modal top layer. This is called when
+   * a modal dialog may no longer be tenable, e.g., when the dialog is no
+   * longer open or is no longer part of the DOM.
+   */
+  maybeHideModal: function() {
+    if (this.dialog_.hasAttribute('open') && document.body.contains(this.dialog_)) { return; }
+    this.downgradeModal();
+  },
+
+  /**
+   * Remove this dialog from the modal top layer, leaving it as a non-modal.
+   */
+  downgradeModal: function() {
+    if (!this.openAsModal_) { return; }
+    this.openAsModal_ = false;
+    this.dialog_.style.zIndex = '';
+
+    // This won't match the native <dialog> exactly because if the user set top on a centered
+    // polyfill dialog, that top gets thrown away when the dialog is closed. Not sure it's
+    // possible to polyfill this perfectly.
+    if (this.replacedStyleTop_) {
+      this.dialog_.style.top = '';
+      this.replacedStyleTop_ = false;
+    }
+
+    // Clear the backdrop and remove from the manager.
+    this.backdrop_.parentNode && this.backdrop_.parentNode.removeChild(this.backdrop_);
+    dialogPolyfill.dm.removeDialog(this);
+  },
+
+  /**
+   * @param {boolean} value whether to open or close this dialog
+   */
+  setOpen: function(value) {
+    if (value) {
+      this.dialog_.hasAttribute('open') || this.dialog_.setAttribute('open', '');
+    } else {
+      this.dialog_.removeAttribute('open');
+      this.maybeHideModal();  // nb. redundant with MutationObserver
+    }
+  },
+
+  /**
+   * Handles clicks on the fake .backdrop element, redirecting them as if
+   * they were on the dialog itself.
+   *
+   * @param {!Event} e to redirect
+   */
+  backdropClick_: function(e) {
+    if (!this.dialog_.hasAttribute('tabindex')) {
+      // Clicking on the backdrop should move the implicit cursor, even if dialog cannot be
+      // focused. Create a fake thing to focus on. If the backdrop was _before_ the dialog, this
+      // would not be needed - clicks would move the implicit cursor there.
+      var fake = document.createElement('div');
+      this.dialog_.insertBefore(fake, this.dialog_.firstChild);
+      fake.tabIndex = -1;
+      fake.focus();
+      this.dialog_.removeChild(fake);
+    } else {
+      this.dialog_.focus();
+    }
+
+    var redirectedEvent = document.createEvent('MouseEvents');
+    redirectedEvent.initMouseEvent(e.type, e.bubbles, e.cancelable, window,
+        e.detail, e.screenX, e.screenY, e.clientX, e.clientY, e.ctrlKey,
+        e.altKey, e.shiftKey, e.metaKey, e.button, e.relatedTarget);
+    this.dialog_.dispatchEvent(redirectedEvent);
+    e.stopPropagation();
+  },
+
+  /**
+   * Focuses on the first focusable element within the dialog. This will always blur the current
+   * focus, even if nothing within the dialog is found.
+   */
+  focus_: function() {
+    // Find element with `autofocus` attribute, or fall back to the first form/tabindex control.
+    var target = this.dialog_.querySelector('[autofocus]:not([disabled])');
+    if (!target && this.dialog_.tabIndex >= 0) {
+      target = this.dialog_;
+    }
+    if (!target) {
+      // Note that this is 'any focusable area'. This list is probably not exhaustive, but the
+      // alternative involves stepping through and trying to focus everything.
+      var opts = ['button', 'input', 'keygen', 'select', 'textarea'];
+      var query = opts.map(function(el) {
+        return el + ':not([disabled])';
+      });
+      // TODO(samthor): tabindex values that are not numeric are not focusable.
+      query.push('[tabindex]:not([disabled]):not([tabindex=""])');  // tabindex != "", not disabled
+      target = this.dialog_.querySelector(query.join(', '));
+    }
+    safeBlur(document.activeElement);
+    target && target.focus();
+  },
+
+  /**
+   * Sets the zIndex for the backdrop and dialog.
+   *
+   * @param {number} dialogZ
+   * @param {number} backdropZ
+   */
+  updateZIndex: function(dialogZ, backdropZ) {
+    if (dialogZ < backdropZ) {
+      throw new Error('dialogZ should never be < backdropZ');
+    }
+    this.dialog_.style.zIndex = dialogZ;
+    this.backdrop_.style.zIndex = backdropZ;
+  },
+
+  /**
+   * Shows the dialog. If the dialog is already open, this does nothing.
+   */
+  show: function() {
+    if (!this.dialog_.open) {
+      this.setOpen(true);
+      this.focus_();
+    }
+  },
+
+  /**
+   * Show this dialog modally.
+   */
+  showModal: function() {
+    if (this.dialog_.hasAttribute('open')) {
+      throw new Error('Failed to execute \'showModal\' on dialog: The element is already open, and therefore cannot be opened modally.');
+    }
+    if (!document.body.contains(this.dialog_)) {
+      throw new Error('Failed to execute \'showModal\' on dialog: The element is not in a Document.');
+    }
+    if (!dialogPolyfill.dm.pushDialog(this)) {
+      throw new Error('Failed to execute \'showModal\' on dialog: There are too many open modal dialogs.');
+    }
+
+    if (createsStackingContext(this.dialog_.parentElement)) {
+      console.warn('A dialog is being shown inside a stacking context. ' +
+          'This may cause it to be unusable. For more information, see this link: ' +
+          'https://github.com/GoogleChrome/dialog-polyfill/#stacking-context');
+    }
+
+    this.setOpen(true);
+    this.openAsModal_ = true;
+
+    // Optionally center vertically, relative to the current viewport.
+    if (dialogPolyfill.needsCentering(this.dialog_)) {
+      dialogPolyfill.reposition(this.dialog_);
+      this.replacedStyleTop_ = true;
+    } else {
+      this.replacedStyleTop_ = false;
+    }
+
+    // Insert backdrop.
+    this.dialog_.parentNode.insertBefore(this.backdrop_, this.dialog_.nextSibling);
+
+    // Focus on whatever inside the dialog.
+    this.focus_();
+  },
+
+  /**
+   * Closes this HTMLDialogElement. This is optional vs clearing the open
+   * attribute, however this fires a 'close' event.
+   *
+   * @param {string=} opt_returnValue to use as the returnValue
+   */
+  close: function(opt_returnValue) {
+    if (!this.dialog_.hasAttribute('open')) {
+      throw new Error('Failed to execute \'close\' on dialog: The element does not have an \'open\' attribute, and therefore cannot be closed.');
+    }
+    this.setOpen(false);
+
+    // Leave returnValue untouched in case it was set directly on the element
+    if (opt_returnValue !== undefined) {
+      this.dialog_.returnValue = opt_returnValue;
+    }
+
+    // Triggering "close" event for any attached listeners on the <dialog>.
+    var closeEvent = new supportCustomEvent('close', {
+      bubbles: false,
+      cancelable: false
+    });
+    this.dialog_.dispatchEvent(closeEvent);
+  }
+
+};
+
+var dialogPolyfill = {};
+
+dialogPolyfill.reposition = function(element) {
+  var scrollTop = document.body.scrollTop || document.documentElement.scrollTop;
+  var topValue = scrollTop + (window.innerHeight - element.offsetHeight) / 2;
+  element.style.top = Math.max(scrollTop, topValue) + 'px';
+};
+
+dialogPolyfill.isInlinePositionSetByStylesheet = function(element) {
+  for (var i = 0; i < document.styleSheets.length; ++i) {
+    var styleSheet = document.styleSheets[i];
+    var cssRules = null;
+    // Some browsers throw on cssRules.
+    try {
+      cssRules = styleSheet.cssRules;
+    } catch (e) {}
+    if (!cssRules) { continue; }
+    for (var j = 0; j < cssRules.length; ++j) {
+      var rule = cssRules[j];
+      var selectedNodes = null;
+      // Ignore errors on invalid selector texts.
+      try {
+        selectedNodes = document.querySelectorAll(rule.selectorText);
+      } catch(e) {}
+      if (!selectedNodes || !inNodeList(selectedNodes, element)) {
+        continue;
+      }
+      var cssTop = rule.style.getPropertyValue('top');
+      var cssBottom = rule.style.getPropertyValue('bottom');
+      if ((cssTop && cssTop !== 'auto') || (cssBottom && cssBottom !== 'auto')) {
+        return true;
+      }
+    }
+  }
+  return false;
+};
+
+dialogPolyfill.needsCentering = function(dialog) {
+  var computedStyle = window.getComputedStyle(dialog);
+  if (computedStyle.position !== 'absolute') {
+    return false;
+  }
+
+  // We must determine whether the top/bottom specified value is non-auto.  In
+  // WebKit/Blink, checking computedStyle.top == 'auto' is sufficient, but
+  // Firefox returns the used value. So we do this crazy thing instead: check
+  // the inline style and then go through CSS rules.
+  if ((dialog.style.top !== 'auto' && dialog.style.top !== '') ||
+      (dialog.style.bottom !== 'auto' && dialog.style.bottom !== '')) {
+    return false;
+  }
+  return !dialogPolyfill.isInlinePositionSetByStylesheet(dialog);
+};
+
+/**
+ * @param {!Element} element to force upgrade
+ */
+dialogPolyfill.forceRegisterDialog = function(element) {
+  if (window.HTMLDialogElement || element.showModal) {
+    console.warn('This browser already supports <dialog>, the polyfill ' +
+        'may not work correctly', element);
+  }
+  if (element.localName !== 'dialog') {
+    throw new Error('Failed to register dialog: The element is not a dialog.');
+  }
+  new dialogPolyfillInfo(/** @type {!HTMLDialogElement} */ (element));
+};
+
+/**
+ * @param {!Element} element to upgrade, if necessary
+ */
+dialogPolyfill.registerDialog = function(element) {
+  if (!element.showModal) {
+    dialogPolyfill.forceRegisterDialog(element);
+  }
+};
+
+/**
+ * @constructor
+ */
+dialogPolyfill.DialogManager = function() {
+  /** @type {!Array<!dialogPolyfillInfo>} */
+  this.pendingDialogStack = [];
+
+  var checkDOM = this.checkDOM_.bind(this);
+
+  // The overlay is used to simulate how a modal dialog blocks the document.
+  // The blocking dialog is positioned on top of the overlay, and the rest of
+  // the dialogs on the pending dialog stack are positioned below it. In the
+  // actual implementation, the modal dialog stacking is controlled by the
+  // top layer, where z-index has no effect.
+  this.overlay = document.createElement('div');
+  this.overlay.className = '_dialog_overlay';
+  this.overlay.addEventListener('click', function(e) {
+    this.forwardTab_ = undefined;
+    e.stopPropagation();
+    checkDOM([]);  // sanity-check DOM
+  }.bind(this));
+
+  this.handleKey_ = this.handleKey_.bind(this);
+  this.handleFocus_ = this.handleFocus_.bind(this);
+
+  this.zIndexLow_ = 100000;
+  this.zIndexHigh_ = 100000 + 150;
+
+  this.forwardTab_ = undefined;
+
+  if ('MutationObserver' in window) {
+    this.mo_ = new MutationObserver(function(records) {
+      var removed = [];
+      records.forEach(function(rec) {
+        for (var i = 0, c; c = rec.removedNodes[i]; ++i) {
+          if (!(c instanceof Element)) {
+            continue;
+          } else if (c.localName === 'dialog') {
+            removed.push(c);
+          }
+          removed = removed.concat(c.querySelectorAll('dialog'));
+        }
+      });
+      removed.length && checkDOM(removed);
+    });
+  }
+};
+
+/**
+ * Called on the first modal dialog being shown. Adds the overlay and related
+ * handlers.
+ */
+dialogPolyfill.DialogManager.prototype.blockDocument = function() {
+  document.documentElement.addEventListener('focus', this.handleFocus_, true);
+  document.addEventListener('keydown', this.handleKey_);
+  this.mo_ && this.mo_.observe(document, {childList: true, subtree: true});
+};
+
+/**
+ * Called on the first modal dialog being removed, i.e., when no more modal
+ * dialogs are visible.
+ */
+dialogPolyfill.DialogManager.prototype.unblockDocument = function() {
+  document.documentElement.removeEventListener('focus', this.handleFocus_, true);
+  document.removeEventListener('keydown', this.handleKey_);
+  this.mo_ && this.mo_.disconnect();
+};
+
+/**
+ * Updates the stacking of all known dialogs.
+ */
+dialogPolyfill.DialogManager.prototype.updateStacking = function() {
+  var zIndex = this.zIndexHigh_;
+
+  for (var i = 0, dpi; dpi = this.pendingDialogStack[i]; ++i) {
+    dpi.updateZIndex(--zIndex, --zIndex);
+    if (i === 0) {
+      this.overlay.style.zIndex = --zIndex;
+    }
+  }
+
+  // Make the overlay a sibling of the dialog itself.
+  var last = this.pendingDialogStack[0];
+  if (last) {
+    var p = last.dialog.parentNode || document.body;
+    p.appendChild(this.overlay);
+  } else if (this.overlay.parentNode) {
+    this.overlay.parentNode.removeChild(this.overlay);
+  }
+};
+
+/**
+ * @param {Element} candidate to check if contained or is the top-most modal dialog
+ * @return {boolean} whether candidate is contained in top dialog
+ */
+dialogPolyfill.DialogManager.prototype.containedByTopDialog_ = function(candidate) {
+  while (candidate = findNearestDialog(candidate)) {
+    for (var i = 0, dpi; dpi = this.pendingDialogStack[i]; ++i) {
+      if (dpi.dialog === candidate) {
+        return i === 0;  // only valid if top-most
+      }
+    }
+    candidate = candidate.parentElement;
+  }
+  return false;
+};
+
+dialogPolyfill.DialogManager.prototype.handleFocus_ = function(event) {
+  if (this.containedByTopDialog_(event.target)) { return; }
+
+  if (document.activeElement === document.documentElement) { return; }
+
+  event.preventDefault();
+  event.stopPropagation();
+  safeBlur(/** @type {Element} */ (event.target));
+
+  if (this.forwardTab_ === undefined) { return; }  // move focus only from a tab key
+
+  var dpi = this.pendingDialogStack[0];
+  var dialog = dpi.dialog;
+  var position = dialog.compareDocumentPosition(event.target);
+  if (position & Node.DOCUMENT_POSITION_PRECEDING) {
+    if (this.forwardTab_) {
+      // forward
+      dpi.focus_();
+    } else if (event.target !== document.documentElement) {
+      // backwards if we're not already focused on <html>
+      document.documentElement.focus();
+    }
+  }
+
+  return false;
+};
+
+dialogPolyfill.DialogManager.prototype.handleKey_ = function(event) {
+  this.forwardTab_ = undefined;
+  if (event.keyCode === 27) {
+    event.preventDefault();
+    event.stopPropagation();
+    var cancelEvent = new supportCustomEvent('cancel', {
+      bubbles: false,
+      cancelable: true
+    });
+    var dpi = this.pendingDialogStack[0];
+    if (dpi && dpi.dialog.dispatchEvent(cancelEvent)) {
+      dpi.dialog.close();
+    }
+  } else if (event.keyCode === 9) {
+    this.forwardTab_ = !event.shiftKey;
+  }
+};
+
+/**
+ * Finds and downgrades any known modal dialogs that are no longer displayed. Dialogs that are
+ * removed and immediately readded don't stay modal, they become normal.
+ *
+ * @param {!Array<!HTMLDialogElement>} removed that have definitely been removed
+ */
+dialogPolyfill.DialogManager.prototype.checkDOM_ = function(removed) {
+  // This operates on a clone because it may cause it to change. Each change also calls
+  // updateStacking, which only actually needs to happen once. But who removes many modal dialogs
+  // at a time?!
+  var clone = this.pendingDialogStack.slice();
+  clone.forEach(function(dpi) {
+    if (removed.indexOf(dpi.dialog) !== -1) {
+      dpi.downgradeModal();
+    } else {
+      dpi.maybeHideModal();
+    }
+  });
+};
+
+/**
+ * @param {!dialogPolyfillInfo} dpi
+ * @return {boolean} whether the dialog was allowed
+ */
+dialogPolyfill.DialogManager.prototype.pushDialog = function(dpi) {
+  var allowed = (this.zIndexHigh_ - this.zIndexLow_) / 2 - 1;
+  if (this.pendingDialogStack.length >= allowed) {
+    return false;
+  }
+  if (this.pendingDialogStack.unshift(dpi) === 1) {
+    this.blockDocument();
+  }
+  this.updateStacking();
+  return true;
+};
+
+/**
+ * @param {!dialogPolyfillInfo} dpi
+ */
+dialogPolyfill.DialogManager.prototype.removeDialog = function(dpi) {
+  var index = this.pendingDialogStack.indexOf(dpi);
+  if (index === -1) { return; }
+
+  this.pendingDialogStack.splice(index, 1);
+  if (this.pendingDialogStack.length === 0) {
+    this.unblockDocument();
+  }
+  this.updateStacking();
+};
+
+dialogPolyfill.dm = new dialogPolyfill.DialogManager();
+dialogPolyfill.formSubmitter = null;
+dialogPolyfill.useValue = null;
+
+/**
+ * Installs global handlers, such as click listers and native method overrides. These are needed
+ * even if a no dialog is registered, as they deal with <form method="dialog">.
+ */
+if (window.HTMLDialogElement === undefined) {
+
+  /**
+   * If HTMLFormElement translates method="DIALOG" into 'get', then replace the descriptor with
+   * one that returns the correct value.
+   */
+  var testForm = document.createElement('form');
+  testForm.setAttribute('method', 'dialog');
+  if (testForm.method !== 'dialog') {
+    var methodDescriptor = Object.getOwnPropertyDescriptor(HTMLFormElement.prototype, 'method');
+    if (methodDescriptor) {
+      // nb. Some older iOS and older PhantomJS fail to return the descriptor. Don't do anything
+      // and don't bother to update the element.
+      var realGet = methodDescriptor.get;
+      methodDescriptor.get = function() {
+        if (isFormMethodDialog(this)) {
+          return 'dialog';
+        }
+        return realGet.call(this);
+      };
+      var realSet = methodDescriptor.set;
+      methodDescriptor.set = function(v) {
+        if (typeof v === 'string' && v.toLowerCase() === 'dialog') {
+          return this.setAttribute('method', v);
+        }
+        return realSet.call(this, v);
+      };
+      Object.defineProperty(HTMLFormElement.prototype, 'method', methodDescriptor);
+    }
+  }
+
+  /**
+   * Global 'click' handler, to capture the <input type="submit"> or <button> element which has
+   * submitted a <form method="dialog">. Needed as Safari and others don't report this inside
+   * document.activeElement.
+   */
+  document.addEventListener('click', function(ev) {
+    dialogPolyfill.formSubmitter = null;
+    dialogPolyfill.useValue = null;
+    if (ev.defaultPrevented) { return; }  // e.g. a submit which prevents default submission
+
+    var target = /** @type {Element} */ (ev.target);
+    if (!target || !isFormMethodDialog(target.form)) { return; }
+
+    var valid = (target.type === 'submit' && ['button', 'input'].indexOf(target.localName) > -1);
+    if (!valid) {
+      if (!(target.localName === 'input' && target.type === 'image')) { return; }
+      // this is a <input type="image">, which can submit forms
+      dialogPolyfill.useValue = ev.offsetX + ',' + ev.offsetY;
+    }
+
+    var dialog = findNearestDialog(target);
+    if (!dialog) { return; }
+
+    dialogPolyfill.formSubmitter = target;
+
+  }, false);
+
+  /**
+   * Replace the native HTMLFormElement.submit() method, as it won't fire the
+   * submit event and give us a chance to respond.
+   */
+  var nativeFormSubmit = HTMLFormElement.prototype.submit;
+  var replacementFormSubmit = function () {
+    if (!isFormMethodDialog(this)) {
+      return nativeFormSubmit.call(this);
+    }
+    var dialog = findNearestDialog(this);
+    dialog && dialog.close();
+  };
+  HTMLFormElement.prototype.submit = replacementFormSubmit;
+
+  /**
+   * Global form 'dialog' method handler. Closes a dialog correctly on submit
+   * and possibly sets its return value.
+   */
+  document.addEventListener('submit', function(ev) {
+    var form = /** @type {HTMLFormElement} */ (ev.target);
+    if (!isFormMethodDialog(form)) { return; }
+    ev.preventDefault();
+
+    var dialog = findNearestDialog(form);
+    if (!dialog) { return; }
+
+    // Forms can only be submitted via .submit() or a click (?), but anyway: sanity-check that
+    // the submitter is correct before using its value as .returnValue.
+    var s = dialogPolyfill.formSubmitter;
+    if (s && s.form === form) {
+      dialog.close(dialogPolyfill.useValue || s.value);
+    } else {
+      dialog.close();
+    }
+    dialogPolyfill.formSubmitter = null;
+
+  }, true);
+}
+
+export default dialogPolyfill;