Project import generated by Copybara.
GitOrigin-RevId: 63746295f1a5ab5a619056791995793d65529e62
diff --git a/node_modules/dialog-polyfill/LICENSE b/node_modules/dialog-polyfill/LICENSE
new file mode 100644
index 0000000..3d0f7d3
--- /dev/null
+++ b/node_modules/dialog-polyfill/LICENSE
@@ -0,0 +1,27 @@
+// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/node_modules/dialog-polyfill/README.md b/node_modules/dialog-polyfill/README.md
new file mode 100644
index 0000000..fa907ec
--- /dev/null
+++ b/node_modules/dialog-polyfill/README.md
@@ -0,0 +1,123 @@
+dialog-polyfill.js is a polyfill for `<dialog>` and `<form method="dialog">`.
+Check out [some demos](http://demo.agektmr.com/dialog/)!
+
+`<dialog>` is an element for a popup box in a web page, including a modal option which will make the rest of the page inert during use.
+This could be useful to block a user's interaction until they give you a response, or to confirm an action.
+See the [HTML spec](https://html.spec.whatwg.org/multipage/forms.html#the-dialog-element).
+
+## Usage
+
+### Installation
+
+You may optionally install via NPM -
+
+ $ npm install dialog-polyfill
+
+
+There are several ways that to include the dialog polyfill:
+
+* include `dialog-polyfill.js` script directly in your HTML, which exposes a global `dialogPolyfill` function.
+* `import` (es modules)
+* `require` (commonjs/node)
+
+
+```javascript
+// direct import (script module, deno)
+import dialogPolyfill from './node_modules/dialog-polyfill/index.js';
+
+// *OR*
+
+// modern es modules with rollup/webpack bundlers, and node via esm module
+import dialogPolyfill from 'dialog-polyfill'
+
+// *OR*
+
+// traditional commonjs/node and browserify bundler
+const dialogPolyfill = require('dialog-polyfill')
+```
+
+
+### Supports
+
+This polyfill works on modern versions of all major browsers. It also supports IE9 and above.
+
+### Steps
+
+1. Include the CSS in the `<head>` of your document, and the Javascript anywhere before referencing `dialogPolyfill`.
+2. Create your dialog elements within the document. See [limitations](#limitations) for more details.
+3. Register the elements using `dialogPolyfill.registerDialog()`, passing it one node at a time. This polyfill won't replace native support.
+4. Use your `<dialog>` elements!
+
+## Script Global Example
+
+```html
+<head>
+ <link rel="stylesheet" type="text/css" href="dist/dialog-polyfill.css" />
+</head>
+<body>
+ <dialog>
+ I'm a dialog!
+ <form method="dialog">
+ <input type="submit" value="Close" />
+ </form>
+ </dialog>
+ <script src="dist/dialog-polyfill.js"></script>
+ <script>
+ var dialog = document.querySelector('dialog');
+ dialogPolyfill.registerDialog(dialog);
+ // Now dialog acts like a native <dialog>.
+ dialog.showModal();
+ </script>
+</body>
+```
+
+### ::backdrop
+
+In native `<dialog>`, the backdrop is a pseudo-element.
+When using the polyfill, the backdrop will be an adjacent element:
+
+```css
+dialog::backdrop { /* native */
+ background-color: green;
+}
+dialog + .backdrop { /* polyfill */
+ background-color: green;
+}
+```
+
+## Limitations
+
+In the polyfill, modal dialogs have limitations-
+
+- They should not be contained by parents that create a stacking context, see below
+- The browser's chrome may not always be accessible via the tab key
+- Changes to the CSS top/bottom values while open aren't retained
+
+### Stacking Context
+
+The major limitation of the polyfill is that dialogs should not have parents that create [a stacking context](https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Positioning/Understanding_z_index/The_stacking_context).
+The easiest way to solve this is to move your `<dialog>` element to be a child of `<body>`.
+
+If this isn't possible you may still be able to use the dialog.
+However, you may want to resolve it for two major reasons-
+
+1. The polyfill can't guarantee that the dialog will be the top-most element of your page
+2. The dialog may be positioned incorrectly as they are positioned as part of the page layout _where they are opened_ (defined by spec), and not at a fixed position in the user's browser.
+
+To position a dialog in the center (regardless of user scroll position or stacking context), you can specify the following CSS-
+
+```css
+dialog {
+ position: fixed;
+ top: 50%;
+ transform: translate(0, -50%);
+}
+```
+
+## Extensions
+
+### Focus
+
+The WAI-ARIA doc suggests returning focus to the previously focused element after a modal dialog is closed.
+However, this is not part of the dialog spec itself.
+See [this snippet](https://gist.github.com/samthor/babe9fad4a65625b301ba482dad284d1) to add this, even to the native `dialog`.
diff --git a/node_modules/dialog-polyfill/dialog-polyfill.css b/node_modules/dialog-polyfill/dialog-polyfill.css
new file mode 100644
index 0000000..6b38bf0
--- /dev/null
+++ b/node_modules/dialog-polyfill/dialog-polyfill.css
@@ -0,0 +1,37 @@
+dialog {
+ position: absolute;
+ left: 0; right: 0;
+ width: -moz-fit-content;
+ width: -webkit-fit-content;
+ width: fit-content;
+ height: -moz-fit-content;
+ height: -webkit-fit-content;
+ height: fit-content;
+ margin: auto;
+ border: solid;
+ padding: 1em;
+ background: white;
+ color: black;
+ display: block;
+}
+
+dialog:not([open]) {
+ display: none;
+}
+
+dialog + .backdrop {
+ position: fixed;
+ top: 0; right: 0; bottom: 0; left: 0;
+ background: rgba(0,0,0,0.1);
+}
+
+._dialog_overlay {
+ position: fixed;
+ top: 0; right: 0; bottom: 0; left: 0;
+}
+
+dialog.fixed {
+ position: fixed;
+ top: 50%;
+ transform: translate(0, -50%);
+}
\ No newline at end of file
diff --git a/node_modules/dialog-polyfill/dist/dialog-polyfill.css b/node_modules/dialog-polyfill/dist/dialog-polyfill.css
new file mode 100644
index 0000000..6b38bf0
--- /dev/null
+++ b/node_modules/dialog-polyfill/dist/dialog-polyfill.css
@@ -0,0 +1,37 @@
+dialog {
+ position: absolute;
+ left: 0; right: 0;
+ width: -moz-fit-content;
+ width: -webkit-fit-content;
+ width: fit-content;
+ height: -moz-fit-content;
+ height: -webkit-fit-content;
+ height: fit-content;
+ margin: auto;
+ border: solid;
+ padding: 1em;
+ background: white;
+ color: black;
+ display: block;
+}
+
+dialog:not([open]) {
+ display: none;
+}
+
+dialog + .backdrop {
+ position: fixed;
+ top: 0; right: 0; bottom: 0; left: 0;
+ background: rgba(0,0,0,0.1);
+}
+
+._dialog_overlay {
+ position: fixed;
+ top: 0; right: 0; bottom: 0; left: 0;
+}
+
+dialog.fixed {
+ position: fixed;
+ top: 50%;
+ transform: translate(0, -50%);
+}
\ No newline at end of file
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;
diff --git a/node_modules/dialog-polyfill/dist/dialog-polyfill.js b/node_modules/dialog-polyfill/dist/dialog-polyfill.js
new file mode 100644
index 0000000..72a4ab9
--- /dev/null
+++ b/node_modules/dialog-polyfill/dist/dialog-polyfill.js
@@ -0,0 +1,736 @@
+(function (global, factory) {
+ typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
+ typeof define === 'function' && define.amd ? define(factory) :
+ (global = global || self, global.dialogPolyfill = factory());
+}(this, function () { 'use strict';
+
+ // 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);
+ }
+
+ return dialogPolyfill;
+
+}));
diff --git a/node_modules/dialog-polyfill/index.js b/node_modules/dialog-polyfill/index.js
new file mode 100644
index 0000000..e4db8da
--- /dev/null
+++ b/node_modules/dialog-polyfill/index.js
@@ -0,0 +1,732 @@
+
+// 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();
+ }
+ } else {
+ // TODO: Focus after the dialog, is ignored.
+ }
+
+ 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;
diff --git a/node_modules/dialog-polyfill/package-lock.json b/node_modules/dialog-polyfill/package-lock.json
new file mode 100644
index 0000000..fcc690e
--- /dev/null
+++ b/node_modules/dialog-polyfill/package-lock.json
@@ -0,0 +1,697 @@
+{
+ "name": "dialog-polyfill",
+ "version": "0.4.10",
+ "lockfileVersion": 1,
+ "requires": true,
+ "dependencies": {
+ "@types/estree": {
+ "version": "0.0.39",
+ "resolved": "https://registry.npmjs.org/@types/estree/-/estree-0.0.39.tgz",
+ "integrity": "sha512-EYNwp3bU+98cpU4lAWYYL7Zz+2gryWH1qbdDTidVd6hkiR6weksdbMadyXKXNPEkQFhXM+hVO9ZygomHXp+AIw==",
+ "dev": true
+ },
+ "@types/node": {
+ "version": "11.9.5",
+ "resolved": "https://registry.npmjs.org/@types/node/-/node-11.9.5.tgz",
+ "integrity": "sha512-vVjM0SVzgaOUpflq4GYBvCpozes8OgIIS5gVXVka+OfK3hvnkC1i93U8WiY2OtNE4XUWyyy/86Kf6e0IHTQw1Q==",
+ "dev": true
+ },
+ "acorn": {
+ "version": "6.1.0",
+ "resolved": "https://registry.npmjs.org/acorn/-/acorn-6.1.0.tgz",
+ "integrity": "sha512-MW/FjM+IvU9CgBzjO3UIPCE2pyEwUsoFl+VGdczOPEdxfGFjuKny/gN54mOuX7Qxmb9Rg9MCn2oKiSUeW+pjrw==",
+ "dev": true
+ },
+ "ansi-styles": {
+ "version": "3.2.1",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz",
+ "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==",
+ "dev": true,
+ "requires": {
+ "color-convert": "^1.9.0"
+ }
+ },
+ "array-filter": {
+ "version": "0.0.1",
+ "resolved": "https://registry.npmjs.org/array-filter/-/array-filter-0.0.1.tgz",
+ "integrity": "sha1-fajPLiZijtcygDWB/SH2fKzS7uw=",
+ "dev": true
+ },
+ "array-map": {
+ "version": "0.0.0",
+ "resolved": "https://registry.npmjs.org/array-map/-/array-map-0.0.0.tgz",
+ "integrity": "sha1-iKK6tz0c97zVwbEYoAP2b2ZfpmI=",
+ "dev": true
+ },
+ "array-reduce": {
+ "version": "0.0.0",
+ "resolved": "https://registry.npmjs.org/array-reduce/-/array-reduce-0.0.0.tgz",
+ "integrity": "sha1-FziZ0//Rx9k4PkR5Ul2+J4yrXys=",
+ "dev": true
+ },
+ "assertion-error": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-1.1.0.tgz",
+ "integrity": "sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==",
+ "dev": true
+ },
+ "balanced-match": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz",
+ "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=",
+ "dev": true
+ },
+ "brace-expansion": {
+ "version": "1.1.11",
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
+ "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
+ "dev": true,
+ "requires": {
+ "balanced-match": "^1.0.0",
+ "concat-map": "0.0.1"
+ }
+ },
+ "browser-stdout": {
+ "version": "1.3.1",
+ "resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.1.tgz",
+ "integrity": "sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==",
+ "dev": true
+ },
+ "chai": {
+ "version": "4.2.0",
+ "resolved": "https://registry.npmjs.org/chai/-/chai-4.2.0.tgz",
+ "integrity": "sha512-XQU3bhBukrOsQCuwZndwGcCVQHyZi53fQ6Ys1Fym7E4olpIqqZZhhoFJoaKVvV17lWQoXYwgWN2nF5crA8J2jw==",
+ "dev": true,
+ "requires": {
+ "assertion-error": "^1.1.0",
+ "check-error": "^1.0.2",
+ "deep-eql": "^3.0.1",
+ "get-func-name": "^2.0.0",
+ "pathval": "^1.1.0",
+ "type-detect": "^4.0.5"
+ }
+ },
+ "chalk": {
+ "version": "2.4.2",
+ "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz",
+ "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==",
+ "dev": true,
+ "requires": {
+ "ansi-styles": "^3.2.1",
+ "escape-string-regexp": "^1.0.5",
+ "supports-color": "^5.3.0"
+ }
+ },
+ "check-error": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/check-error/-/check-error-1.0.2.tgz",
+ "integrity": "sha1-V00xLt2Iu13YkS6Sht1sCu1KrII=",
+ "dev": true
+ },
+ "color-convert": {
+ "version": "1.9.3",
+ "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz",
+ "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==",
+ "dev": true,
+ "requires": {
+ "color-name": "1.1.3"
+ }
+ },
+ "color-name": {
+ "version": "1.1.3",
+ "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz",
+ "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=",
+ "dev": true
+ },
+ "commander": {
+ "version": "2.15.1",
+ "resolved": "https://registry.npmjs.org/commander/-/commander-2.15.1.tgz",
+ "integrity": "sha512-VlfT9F3V0v+jr4yxPc5gg9s62/fIVWsd2Bk2iD435um1NlGMYdVCq+MjcXnhYq2icNOizHr1kK+5TI6H0Hy0ag==",
+ "dev": true
+ },
+ "concat-map": {
+ "version": "0.0.1",
+ "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
+ "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=",
+ "dev": true
+ },
+ "cross-spawn": {
+ "version": "6.0.5",
+ "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz",
+ "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==",
+ "dev": true,
+ "requires": {
+ "nice-try": "^1.0.4",
+ "path-key": "^2.0.1",
+ "semver": "^5.5.0",
+ "shebang-command": "^1.2.0",
+ "which": "^1.2.9"
+ }
+ },
+ "debug": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz",
+ "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==",
+ "dev": true,
+ "requires": {
+ "ms": "2.0.0"
+ }
+ },
+ "deep-eql": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-3.0.1.tgz",
+ "integrity": "sha512-+QeIQyN5ZuO+3Uk5DYh6/1eKO0m0YmJFGNmFHGACpf1ClL1nmlV/p4gNgbl2pJGxgXb4faqo6UE+M5ACEMyVcw==",
+ "dev": true,
+ "requires": {
+ "type-detect": "^4.0.0"
+ }
+ },
+ "define-properties": {
+ "version": "1.1.3",
+ "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz",
+ "integrity": "sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ==",
+ "dev": true,
+ "requires": {
+ "object-keys": "^1.0.12"
+ }
+ },
+ "diff": {
+ "version": "3.5.0",
+ "resolved": "https://registry.npmjs.org/diff/-/diff-3.5.0.tgz",
+ "integrity": "sha512-A46qtFgd+g7pDZinpnwiRJtxbC1hpgf0uzP3iG89scHk0AUC7A1TGxf5OiiOUv/JMZR8GOt8hL900hV0bOy5xA==",
+ "dev": true
+ },
+ "error-ex": {
+ "version": "1.3.2",
+ "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz",
+ "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==",
+ "dev": true,
+ "requires": {
+ "is-arrayish": "^0.2.1"
+ }
+ },
+ "es-abstract": {
+ "version": "1.13.0",
+ "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.13.0.tgz",
+ "integrity": "sha512-vDZfg/ykNxQVwup/8E1BZhVzFfBxs9NqMzGcvIJrqg5k2/5Za2bWo40dK2J1pgLngZ7c+Shh8lwYtLGyrwPutg==",
+ "dev": true,
+ "requires": {
+ "es-to-primitive": "^1.2.0",
+ "function-bind": "^1.1.1",
+ "has": "^1.0.3",
+ "is-callable": "^1.1.4",
+ "is-regex": "^1.0.4",
+ "object-keys": "^1.0.12"
+ }
+ },
+ "es-to-primitive": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.0.tgz",
+ "integrity": "sha512-qZryBOJjV//LaxLTV6UC//WewneB3LcXOL9NP++ozKVXsIIIpm/2c13UDiD9Jp2eThsecw9m3jPqDwTyobcdbg==",
+ "dev": true,
+ "requires": {
+ "is-callable": "^1.1.4",
+ "is-date-object": "^1.0.1",
+ "is-symbol": "^1.0.2"
+ }
+ },
+ "escape-string-regexp": {
+ "version": "1.0.5",
+ "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
+ "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=",
+ "dev": true
+ },
+ "fs.realpath": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
+ "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=",
+ "dev": true
+ },
+ "function-bind": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz",
+ "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==",
+ "dev": true
+ },
+ "get-func-name": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/get-func-name/-/get-func-name-2.0.0.tgz",
+ "integrity": "sha1-6td0q+5y4gQJQzoGY2YCPdaIekE=",
+ "dev": true
+ },
+ "glob": {
+ "version": "7.1.2",
+ "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz",
+ "integrity": "sha512-MJTUg1kjuLeQCJ+ccE4Vpa6kKVXkPYJ2mOCQyUuKLcLQsdrMCpBPUi8qVE6+YuaJkozeA9NusTAw3hLr8Xe5EQ==",
+ "dev": true,
+ "requires": {
+ "fs.realpath": "^1.0.0",
+ "inflight": "^1.0.4",
+ "inherits": "2",
+ "minimatch": "^3.0.4",
+ "once": "^1.3.0",
+ "path-is-absolute": "^1.0.0"
+ }
+ },
+ "graceful-fs": {
+ "version": "4.1.15",
+ "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.15.tgz",
+ "integrity": "sha512-6uHUhOPEBgQ24HM+r6b/QwWfZq+yiFcipKFrOFiBEnWdy5sdzYoi+pJeQaPI5qOLRFqWmAXUPQNsielzdLoecA==",
+ "dev": true
+ },
+ "growl": {
+ "version": "1.10.5",
+ "resolved": "https://registry.npmjs.org/growl/-/growl-1.10.5.tgz",
+ "integrity": "sha512-qBr4OuELkhPenW6goKVXiv47US3clb3/IbuWF9KNKEijAy9oeHxU9IgzjvJhHkUzhaj7rOUD7+YGWqUjLp5oSA==",
+ "dev": true
+ },
+ "has": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz",
+ "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==",
+ "dev": true,
+ "requires": {
+ "function-bind": "^1.1.1"
+ }
+ },
+ "has-flag": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
+ "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=",
+ "dev": true
+ },
+ "has-symbols": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.0.tgz",
+ "integrity": "sha1-uhqPGvKg/DllD1yFA2dwQSIGO0Q=",
+ "dev": true
+ },
+ "he": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/he/-/he-1.1.1.tgz",
+ "integrity": "sha1-k0EP0hsAlzUVH4howvJx80J+I/0=",
+ "dev": true
+ },
+ "hosted-git-info": {
+ "version": "2.7.1",
+ "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.7.1.tgz",
+ "integrity": "sha512-7T/BxH19zbcCTa8XkMlbK5lTo1WtgkFi3GvdWEyNuc4Vex7/9Dqbnpsf4JMydcfj9HCg4zUWFTL3Za6lapg5/w==",
+ "dev": true
+ },
+ "inflight": {
+ "version": "1.0.6",
+ "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
+ "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=",
+ "dev": true,
+ "requires": {
+ "once": "^1.3.0",
+ "wrappy": "1"
+ }
+ },
+ "inherits": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz",
+ "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=",
+ "dev": true
+ },
+ "is-arrayish": {
+ "version": "0.2.1",
+ "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz",
+ "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=",
+ "dev": true
+ },
+ "is-callable": {
+ "version": "1.1.4",
+ "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.1.4.tgz",
+ "integrity": "sha512-r5p9sxJjYnArLjObpjA4xu5EKI3CuKHkJXMhT7kwbpUyIFD1n5PMAsoPvWnvtZiNz7LjkYDRZhd7FlI0eMijEA==",
+ "dev": true
+ },
+ "is-date-object": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.1.tgz",
+ "integrity": "sha1-mqIOtq7rv/d/vTPnTKAbM1gdOhY=",
+ "dev": true
+ },
+ "is-regex": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.0.4.tgz",
+ "integrity": "sha1-VRdIm1RwkbCTDglWVM7SXul+lJE=",
+ "dev": true,
+ "requires": {
+ "has": "^1.0.1"
+ }
+ },
+ "is-symbol": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.2.tgz",
+ "integrity": "sha512-HS8bZ9ox60yCJLH9snBpIwv9pYUAkcuLhSA1oero1UB5y9aiQpRA8y2ex945AOtCZL1lJDeIk3G5LthswI46Lw==",
+ "dev": true,
+ "requires": {
+ "has-symbols": "^1.0.0"
+ }
+ },
+ "isexe": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz",
+ "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=",
+ "dev": true
+ },
+ "json-parse-better-errors": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz",
+ "integrity": "sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==",
+ "dev": true
+ },
+ "jsonify": {
+ "version": "0.0.0",
+ "resolved": "https://registry.npmjs.org/jsonify/-/jsonify-0.0.0.tgz",
+ "integrity": "sha1-LHS27kHZPKUbe1qu6PUDYx0lKnM=",
+ "dev": true
+ },
+ "load-json-file": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-4.0.0.tgz",
+ "integrity": "sha1-L19Fq5HjMhYjT9U62rZo607AmTs=",
+ "dev": true,
+ "requires": {
+ "graceful-fs": "^4.1.2",
+ "parse-json": "^4.0.0",
+ "pify": "^3.0.0",
+ "strip-bom": "^3.0.0"
+ }
+ },
+ "memorystream": {
+ "version": "0.3.1",
+ "resolved": "https://registry.npmjs.org/memorystream/-/memorystream-0.3.1.tgz",
+ "integrity": "sha1-htcJCzDORV1j+64S3aUaR93K+bI=",
+ "dev": true
+ },
+ "minimatch": {
+ "version": "3.0.4",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz",
+ "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==",
+ "dev": true,
+ "requires": {
+ "brace-expansion": "^1.1.7"
+ }
+ },
+ "minimist": {
+ "version": "0.0.8",
+ "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz",
+ "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=",
+ "dev": true
+ },
+ "mkdirp": {
+ "version": "0.5.1",
+ "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz",
+ "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=",
+ "dev": true,
+ "requires": {
+ "minimist": "0.0.8"
+ }
+ },
+ "mocha": {
+ "version": "5.2.0",
+ "resolved": "https://registry.npmjs.org/mocha/-/mocha-5.2.0.tgz",
+ "integrity": "sha512-2IUgKDhc3J7Uug+FxMXuqIyYzH7gJjXECKe/w43IGgQHTSj3InJi+yAA7T24L9bQMRKiUEHxEX37G5JpVUGLcQ==",
+ "dev": true,
+ "requires": {
+ "browser-stdout": "1.3.1",
+ "commander": "2.15.1",
+ "debug": "3.1.0",
+ "diff": "3.5.0",
+ "escape-string-regexp": "1.0.5",
+ "glob": "7.1.2",
+ "growl": "1.10.5",
+ "he": "1.1.1",
+ "minimatch": "3.0.4",
+ "mkdirp": "0.5.1",
+ "supports-color": "5.4.0"
+ }
+ },
+ "ms": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
+ "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=",
+ "dev": true
+ },
+ "nice-try": {
+ "version": "1.0.5",
+ "resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz",
+ "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==",
+ "dev": true
+ },
+ "normalize-package-data": {
+ "version": "2.5.0",
+ "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz",
+ "integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==",
+ "dev": true,
+ "requires": {
+ "hosted-git-info": "^2.1.4",
+ "resolve": "^1.10.0",
+ "semver": "2 || 3 || 4 || 5",
+ "validate-npm-package-license": "^3.0.1"
+ }
+ },
+ "npm-run-all": {
+ "version": "4.1.5",
+ "resolved": "https://registry.npmjs.org/npm-run-all/-/npm-run-all-4.1.5.tgz",
+ "integrity": "sha512-Oo82gJDAVcaMdi3nuoKFavkIHBRVqQ1qvMb+9LHk/cF4P6B2m8aP04hGf7oL6wZ9BuGwX1onlLhpuoofSyoQDQ==",
+ "dev": true,
+ "requires": {
+ "ansi-styles": "^3.2.1",
+ "chalk": "^2.4.1",
+ "cross-spawn": "^6.0.5",
+ "memorystream": "^0.3.1",
+ "minimatch": "^3.0.4",
+ "pidtree": "^0.3.0",
+ "read-pkg": "^3.0.0",
+ "shell-quote": "^1.6.1",
+ "string.prototype.padend": "^3.0.0"
+ }
+ },
+ "object-keys": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.0.tgz",
+ "integrity": "sha512-6OO5X1+2tYkNyNEx6TsCxEqFfRWaqx6EtMiSbGrw8Ob8v9Ne+Hl8rBAgLBZn5wjEz3s/s6U1WXFUFOcxxAwUpg==",
+ "dev": true
+ },
+ "once": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
+ "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=",
+ "dev": true,
+ "requires": {
+ "wrappy": "1"
+ }
+ },
+ "parse-json": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz",
+ "integrity": "sha1-vjX1Qlvh9/bHRxhPmKeIy5lHfuA=",
+ "dev": true,
+ "requires": {
+ "error-ex": "^1.3.1",
+ "json-parse-better-errors": "^1.0.1"
+ }
+ },
+ "path-is-absolute": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
+ "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=",
+ "dev": true
+ },
+ "path-key": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz",
+ "integrity": "sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A=",
+ "dev": true
+ },
+ "path-parse": {
+ "version": "1.0.6",
+ "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.6.tgz",
+ "integrity": "sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==",
+ "dev": true
+ },
+ "path-type": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/path-type/-/path-type-3.0.0.tgz",
+ "integrity": "sha512-T2ZUsdZFHgA3u4e5PfPbjd7HDDpxPnQb5jN0SrDsjNSuVXHJqtwTnWqG0B1jZrgmJ/7lj1EmVIByWt1gxGkWvg==",
+ "dev": true,
+ "requires": {
+ "pify": "^3.0.0"
+ }
+ },
+ "pathval": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/pathval/-/pathval-1.1.0.tgz",
+ "integrity": "sha1-uULm1L3mUwBe9rcTYd74cn0GReA=",
+ "dev": true
+ },
+ "pidtree": {
+ "version": "0.3.0",
+ "resolved": "https://registry.npmjs.org/pidtree/-/pidtree-0.3.0.tgz",
+ "integrity": "sha512-9CT4NFlDcosssyg8KVFltgokyKZIFjoBxw8CTGy+5F38Y1eQWrt8tRayiUOXE+zVKQnYu5BR8JjCtvK3BcnBhg==",
+ "dev": true
+ },
+ "pify": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz",
+ "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=",
+ "dev": true
+ },
+ "read-pkg": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-3.0.0.tgz",
+ "integrity": "sha1-nLxoaXj+5l0WwA4rGcI3/Pbjg4k=",
+ "dev": true,
+ "requires": {
+ "load-json-file": "^4.0.0",
+ "normalize-package-data": "^2.3.2",
+ "path-type": "^3.0.0"
+ }
+ },
+ "resolve": {
+ "version": "1.10.0",
+ "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.10.0.tgz",
+ "integrity": "sha512-3sUr9aq5OfSg2S9pNtPA9hL1FVEAjvfOC4leW0SNf/mpnaakz2a9femSd6LqAww2RaFctwyf1lCqnTHuF1rxDg==",
+ "dev": true,
+ "requires": {
+ "path-parse": "^1.0.6"
+ }
+ },
+ "rollup": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/rollup/-/rollup-1.3.0.tgz",
+ "integrity": "sha512-QiT0wFmu0IzkGgT5LvzRK5hezHcJW8T9MQdvdC+FylrNpsprpz0gTOpcyY9iM6Sda1fD1SatwNutCxcQd3Y/Lg==",
+ "dev": true,
+ "requires": {
+ "@types/estree": "0.0.39",
+ "@types/node": "^11.9.5",
+ "acorn": "^6.1.0"
+ }
+ },
+ "semver": {
+ "version": "5.6.0",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-5.6.0.tgz",
+ "integrity": "sha512-RS9R6R35NYgQn++fkDWaOmqGoj4Ek9gGs+DPxNUZKuwE183xjJroKvyo1IzVFeXvUrvmALy6FWD5xrdJT25gMg==",
+ "dev": true
+ },
+ "shebang-command": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz",
+ "integrity": "sha1-RKrGW2lbAzmJaMOfNj/uXer98eo=",
+ "dev": true,
+ "requires": {
+ "shebang-regex": "^1.0.0"
+ }
+ },
+ "shebang-regex": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz",
+ "integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=",
+ "dev": true
+ },
+ "shell-quote": {
+ "version": "1.6.1",
+ "resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.6.1.tgz",
+ "integrity": "sha1-9HgZSczkAmlxJ0MOo7PFR29IF2c=",
+ "dev": true,
+ "requires": {
+ "array-filter": "~0.0.0",
+ "array-map": "~0.0.0",
+ "array-reduce": "~0.0.0",
+ "jsonify": "~0.0.0"
+ }
+ },
+ "spdx-correct": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.1.0.tgz",
+ "integrity": "sha512-lr2EZCctC2BNR7j7WzJ2FpDznxky1sjfxvvYEyzxNyb6lZXHODmEoJeFu4JupYlkfha1KZpJyoqiJ7pgA1qq8Q==",
+ "dev": true,
+ "requires": {
+ "spdx-expression-parse": "^3.0.0",
+ "spdx-license-ids": "^3.0.0"
+ }
+ },
+ "spdx-exceptions": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.2.0.tgz",
+ "integrity": "sha512-2XQACfElKi9SlVb1CYadKDXvoajPgBVPn/gOQLrTvHdElaVhr7ZEbqJaRnJLVNeaI4cMEAgVCeBMKF6MWRDCRA==",
+ "dev": true
+ },
+ "spdx-expression-parse": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.0.tgz",
+ "integrity": "sha512-Yg6D3XpRD4kkOmTpdgbUiEJFKghJH03fiC1OPll5h/0sO6neh2jqRDVHOQ4o/LMea0tgCkbMgea5ip/e+MkWyg==",
+ "dev": true,
+ "requires": {
+ "spdx-exceptions": "^2.1.0",
+ "spdx-license-ids": "^3.0.0"
+ }
+ },
+ "spdx-license-ids": {
+ "version": "3.0.3",
+ "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.3.tgz",
+ "integrity": "sha512-uBIcIl3Ih6Phe3XHK1NqboJLdGfwr1UN3k6wSD1dZpmPsIkb8AGNbZYJ1fOBk834+Gxy8rpfDxrS6XLEMZMY2g==",
+ "dev": true
+ },
+ "string.prototype.padend": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/string.prototype.padend/-/string.prototype.padend-3.0.0.tgz",
+ "integrity": "sha1-86rvfBcZ8XDF6rHDK/eA2W4h8vA=",
+ "dev": true,
+ "requires": {
+ "define-properties": "^1.1.2",
+ "es-abstract": "^1.4.3",
+ "function-bind": "^1.0.2"
+ }
+ },
+ "strip-bom": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz",
+ "integrity": "sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=",
+ "dev": true
+ },
+ "supports-color": {
+ "version": "5.4.0",
+ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.4.0.tgz",
+ "integrity": "sha512-zjaXglF5nnWpsq470jSv6P9DwPvgLkuapYmfDm3JWOm0vkNTVF2tI4UrN2r6jH1qM/uc/WtxYY1hYoA2dOKj5w==",
+ "dev": true,
+ "requires": {
+ "has-flag": "^3.0.0"
+ }
+ },
+ "type-detect": {
+ "version": "4.0.8",
+ "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz",
+ "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==",
+ "dev": true
+ },
+ "validate-npm-package-license": {
+ "version": "3.0.4",
+ "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz",
+ "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==",
+ "dev": true,
+ "requires": {
+ "spdx-correct": "^3.0.0",
+ "spdx-expression-parse": "^3.0.0"
+ }
+ },
+ "which": {
+ "version": "1.3.1",
+ "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz",
+ "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==",
+ "dev": true,
+ "requires": {
+ "isexe": "^2.0.0"
+ }
+ },
+ "wrappy": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
+ "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=",
+ "dev": true
+ }
+ }
+}
diff --git a/node_modules/dialog-polyfill/package.json b/node_modules/dialog-polyfill/package.json
new file mode 100644
index 0000000..6b3bcd0
--- /dev/null
+++ b/node_modules/dialog-polyfill/package.json
@@ -0,0 +1,59 @@
+{
+ "_from": "dialog-polyfill",
+ "_id": "dialog-polyfill@0.5.0",
+ "_inBundle": false,
+ "_integrity": "sha512-fOj68T8KB6UIsDFmK7zYbmORJMLYkRmtsLM1W6wbCVUWu4Hdcud5bqbvuueTxO84JXtK9HcpCHV9vNwlWUdCIw==",
+ "_location": "/dialog-polyfill",
+ "_phantomChildren": {},
+ "_requested": {
+ "type": "tag",
+ "registry": true,
+ "raw": "dialog-polyfill",
+ "name": "dialog-polyfill",
+ "escapedName": "dialog-polyfill",
+ "rawSpec": "",
+ "saveSpec": null,
+ "fetchSpec": "latest"
+ },
+ "_requiredBy": [
+ "#USER",
+ "/"
+ ],
+ "_resolved": "https://registry.npmjs.org/dialog-polyfill/-/dialog-polyfill-0.5.0.tgz",
+ "_shasum": "bf67bc67abaf538e44fff80f2f71ff132befadd7",
+ "_spec": "dialog-polyfill",
+ "_where": "/var/www/html/hores",
+ "author": {
+ "name": "The Chromium Authors"
+ },
+ "bugs": {
+ "url": "https://github.com/GoogleChrome/dialog-polyfill/issues"
+ },
+ "bundleDependencies": false,
+ "deprecated": false,
+ "description": "Polyfill for the dialog element",
+ "devDependencies": {
+ "chai": "^4.2.0",
+ "mocha": "^6.0.2",
+ "npm-run-all": "^4.1.5",
+ "rollup": "^1.3.0"
+ },
+ "homepage": "https://github.com/GoogleChrome/dialog-polyfill",
+ "license": "BSD",
+ "main": "dist/dialog-polyfill.js",
+ "module": "dist/dialog-polyfill.esm.js",
+ "name": "dialog-polyfill",
+ "repository": {
+ "type": "git",
+ "url": "git+https://github.com/GoogleChrome/dialog-polyfill.git"
+ },
+ "scripts": {
+ "build": "npm-run-all -p build:*",
+ "build:css": "cp dialog-polyfill.css dist/dialog-polyfill.css",
+ "build:esm": "rollup index.js --file dist/dialog-polyfill.esm.js --format esm",
+ "build:umd": "rollup index.js --file dist/dialog-polyfill.js --format umd --name dialogPolyfill",
+ "prepublishOnly": "npm run build",
+ "test": "open test.html"
+ },
+ "version": "0.5.0"
+}
diff --git a/node_modules/dialog-polyfill/suite.js b/node_modules/dialog-polyfill/suite.js
new file mode 100644
index 0000000..943f17d
--- /dev/null
+++ b/node_modules/dialog-polyfill/suite.js
@@ -0,0 +1,790 @@
+/*
+ * Copyright 2015 Google Inc. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+
+void function() {
+
+ /**
+ * Asserts that the displayed dialog is in the center of the screen.
+ *
+ * @param {HTMLDialogElement?} opt_dialog to check, or test default
+ */
+ function checkDialogCenter(opt_dialog) {
+ var d = opt_dialog || dialog;
+ var expectedTop = (window.innerHeight - d.offsetHeight) / 2;
+ var expectedLeft = (window.innerWidth - d.offsetWidth) / 2;
+ var rect = d.getBoundingClientRect();
+ assert.closeTo(rect.top, expectedTop, 1, 'top should be nearby');
+ assert.closeTo(rect.left, expectedLeft, 1, 'left should be nearby');
+ }
+
+ /**
+ * Creates a fake KeyboardEvent.
+ *
+ * @param {number} keyCode to press
+ * @param {string?} opt_type to use, default keydown
+ * @return {!Event} event
+ */
+ function createKeyboardEvent(keyCode, opt_type) {
+ var ev = document.createEvent('Events');
+ ev.initEvent(opt_type || 'keydown', true, true);
+ ev.keyCode = keyCode;
+ ev.which = keyCode;
+ return ev;
+ }
+
+ /**
+ * Cleans up any passed DOM elements.
+ *
+ * @param {!Element} el to clean up
+ * @return {!Element} the same element, for chaining
+ */
+ var cleanup = (function() {
+ var e = [];
+ teardown(function() {
+ e.forEach(function(el) {
+ try {
+ el.close(); // try to close dialogs
+ } catch (e) {}
+ el.parentElement && el.parentElement.removeChild(el);
+ });
+ e = [];
+ });
+
+ return function(el) {
+ e.push(el);
+ return el;
+ };
+ })();
+
+ /**
+ * Creates a dialog for testing that will be cleaned up later.
+ *
+ * @param {string?} opt_content to be used as innerHTML
+ */
+ function createDialog(opt_content) {
+ var dialog = document.createElement('dialog');
+ dialog.innerHTML = opt_content || 'Dialog #' + (cleanup.length);
+ document.body.appendChild(dialog);
+ if (window.location.search == '?force') {
+ dialogPolyfill.forceRegisterDialog(dialog);
+ } else {
+ dialogPolyfill.registerDialog(dialog);
+ }
+ return cleanup(dialog);
+ }
+
+ var dialog; // global dialog for all tests
+ setup(function() {
+ dialog = createDialog('Default Dialog');
+ });
+
+ suite('basic', function() {
+ test('show and close', function() {
+ assert.isFalse(dialog.hasAttribute('open'));
+ dialog.show();
+ assert.isTrue(dialog.hasAttribute('open'));
+ assert.isTrue(dialog.open);
+
+ var returnValue = 1234;
+ dialog.close(returnValue);
+ assert.isFalse(dialog.hasAttribute('open'));
+ assert.equal(dialog.returnValue, returnValue);
+
+ dialog.show();
+ dialog.close();
+ assert.isFalse(dialog.open);
+ assert.equal(dialog.returnValue, returnValue);
+ });
+ test('open property', function() {
+ assert.isFalse(dialog.hasAttribute('open'));
+ dialog.show();
+ assert.isTrue(dialog.hasAttribute('open'));
+ assert.isTrue(dialog.open);
+
+ dialog.open = false;
+ assert.isFalse(dialog.open);
+ assert.isFalse(dialog.hasAttribute('open'),
+ 'open property should clear attribute');
+ assert.throws(dialog.close);
+
+ var overlay = document.querySelector('._dialog_overlay');
+ assert.isNull(overlay);
+ });
+ test('show/showModal interaction', function() {
+ assert.isFalse(dialog.hasAttribute('open'));
+ dialog.show();
+
+ // If the native dialog is being tested, show/showModal are not already
+ // bound, so wrap them in helper methods for throws/doesNotThrow.
+ var show = function() { dialog.show(); };
+ var showModal = function() { dialog.showModal(); };
+
+ assert.doesNotThrow(show);
+ assert.throws(showModal);
+
+ dialog.open = false;
+ assert.doesNotThrow(showModal);
+ assert.doesNotThrow(show); // show after showModal does nothing
+ assert.throws(showModal);
+ // TODO: check dialog is still modal
+
+ assert.isTrue(dialog.open);
+ });
+ test('setAttribute reflects property', function() {
+ dialog.setAttribute('open', '');
+ assert.isTrue(dialog.open, 'attribute opens dialog');
+ });
+ test('changing open to dummy value is ignored', function() {
+ dialog.showModal();
+
+ dialog.setAttribute('open', 'dummy, ignored');
+ assert.isTrue(dialog.open, 'dialog open with dummy open value');
+
+ var overlay = document.querySelector('._dialog_overlay');
+ assert(overlay, 'dialog is still modal');
+ });
+ test('show/showModal outside document', function() {
+ dialog.open = false;
+ dialog.parentNode.removeChild(dialog);
+
+ assert.throws(function() { dialog.showModal(); });
+
+ assert.doesNotThrow(function() { dialog.show(); });
+ assert.isTrue(dialog.open, 'can open non-modal outside document');
+ assert.isFalse(document.body.contains(dialog));
+ });
+ test('has a11y property', function() {
+ assert.equal(dialog.getAttribute('role'), 'dialog', 'role should be dialog');
+ });
+ });
+
+ suite('DOM', function() {
+ setup(function(done) {
+ // DOM tests wait for modal to settle, so MutationOberver doesn't coalesce attr changes
+ dialog.showModal();
+ window.setTimeout(done, 0);
+ });
+ test('DOM direct removal', function(done) {
+ assert.isTrue(dialog.open);
+ assert.isNotNull(document.querySelector('.backdrop'));
+
+ var parentNode = dialog.parentNode;
+ parentNode.removeChild(dialog);
+
+ // DOMNodeRemoved defers its task a frame (since it occurs before removal, not after). This
+ // doesn't effect MutationObserver, just delays the test a frame.
+ window.setTimeout(function() {
+ assert.isNull(document.querySelector('.backdrop'), 'dialog removal should clear modal');
+
+ assert.isTrue(dialog.open, 'removed dialog should still be open');
+ parentNode.appendChild(dialog);
+
+ assert.isTrue(dialog.open, 're-added dialog should still be open');
+ assert.isNull(document.querySelector('.backdrop'), 're-add dialog should not be modal');
+
+ done();
+ }, 0);
+ });
+ test('DOM removal inside other element', function(done) {
+ var div = cleanup(document.createElement('div'));
+ document.body.appendChild(div);
+ div.appendChild(dialog);
+
+ document.body.removeChild(div);
+
+ window.setTimeout(function() {
+ assert.isNull(document.querySelector('.backdrop'), 'dialog removal should clear modal');
+ assert.isTrue(dialog.open, 'removed dialog should still be open');
+ done();
+ }, 0);
+ });
+ test('DOM instant remove/add', function(done) {
+ var div = cleanup(document.createElement('div'));
+ document.body.appendChild(div);
+ dialog.parentNode.removeChild(dialog);
+ div.appendChild(dialog);
+
+ window.setTimeout(function() {
+ assert.isNull(document.querySelector('.backdrop'), 'backdrop should disappear');
+ assert.isTrue(dialog.open);
+ done();
+ }, 0);
+ });
+ });
+
+ suite('position', function() {
+ test('non-modal is not centered', function() {
+ var el = cleanup(document.createElement('div'));
+ dialog.parentNode.insertBefore(el, dialog);
+ var testRect = el.getBoundingClientRect();
+
+ dialog.show();
+ var rect = dialog.getBoundingClientRect();
+
+ assert.equal(rect.top, testRect.top, 'dialog should not be centered');
+ });
+ test('default modal centering', function() {
+ dialog.showModal();
+ checkDialogCenter();
+ assert.ok(dialog.style.top, 'expected top to be set');
+ dialog.close();
+ assert.notOk(dialog.style.top, 'expected top to be cleared');
+ });
+ test('modal respects static position', function() {
+ dialog.style.top = '10px';
+ dialog.showModal();
+
+ var rect = dialog.getBoundingClientRect();
+ assert.equal(rect.top, 10);
+ });
+ test('modal recentering', function() {
+ var pX = document.body.scrollLeft;
+ var pY = document.body.scrollTop;
+ var big = cleanup(document.createElement('div'));
+ big.style.height = '200vh'; // 2x view height
+ document.body.appendChild(big);
+
+ try {
+ var scrollValue = 200; // don't use incredibly large values
+ dialog.showModal();
+ dialog.close();
+
+ window.scrollTo(0, scrollValue);
+ dialog.showModal();
+ checkDialogCenter(); // must be centered, even after scroll
+ var rectAtScroll = dialog.getBoundingClientRect();
+
+ // after scroll, we aren't recentered, check offset
+ window.scrollTo(0, 0);
+ var rect = dialog.getBoundingClientRect();
+ assert.closeTo(rectAtScroll.top + scrollValue, rect.top, 1);
+ } finally {
+ window.scrollTo(pX, pY);
+ }
+ });
+ test('clamped to top of page', function() {
+ var big = cleanup(document.createElement('div'));
+ big.style.height = '200vh'; // 2x view height
+ document.body.appendChild(big);
+ document.documentElement.scrollTop = document.documentElement.scrollHeight / 2;
+
+ dialog.style.height = document.documentElement.scrollHeight + 200 + 'px';
+ dialog.showModal();
+
+ var visibleRect = dialog.getBoundingClientRect();
+ assert.equal(visibleRect.top, 0, 'large dialog should be visible at top of page');
+
+ var style = window.getComputedStyle(dialog);
+ assert.equal(style.top, document.documentElement.scrollTop + 'px',
+ 'large dialog should be absolutely positioned at scroll top');
+ });
+ });
+
+ suite('backdrop', function() {
+ test('backdrop div on modal', function() {
+ dialog.showModal();
+ var foundBackdrop = document.querySelector('.backdrop');
+ assert.isNotNull(foundBackdrop);
+
+ var sibling = dialog.nextElementSibling;
+ assert.strictEqual(foundBackdrop, sibling);
+ });
+ test('no backdrop on non-modal', function() {
+ dialog.show();
+ assert.isNull(document.querySelector('.backdrop'));
+ dialog.close();
+ });
+ test('backdrop click appears as dialog', function() {
+ dialog.showModal();
+ var backdrop = dialog.nextElementSibling;
+
+ var clickFired = 0;
+ var helper = function(ev) {
+ assert.equal(ev.target, dialog);
+ ++clickFired;
+ };
+
+ dialog.addEventListener('click', helper)
+ backdrop.click();
+ assert.equal(clickFired, 1);
+ });
+ test('backdrop click focuses dialog', function() {
+ dialog.showModal();
+ dialog.tabIndex = 0;
+
+ var input = document.createElement('input');
+ input.type = 'text';
+ dialog.appendChild(input);
+
+ // TODO: It would be nice to check `input` instead here, but there's no more reliable ways
+ // to emulate a browser tab event (Firefox, Chrome etc have made it a security violation).
+
+ var backdrop = dialog.nextElementSibling;
+ backdrop.click();
+ assert.equal(document.activeElement, dialog);
+ });
+ });
+
+ suite('form focus', function() {
+ test('non-modal inside modal is focusable', function() {
+ var sub = createDialog();
+ dialog.appendChild(sub);
+
+ var input = document.createElement('input');
+ input.type = 'text';
+ sub.appendChild(input);
+
+ dialog.showModal();
+ sub.show();
+
+ input.focus();
+ assert.equal(input, document.activeElement);
+ });
+ test('clear focus when nothing focusable in modal', function() {
+ var input = cleanup(document.createElement('input'));
+ input.type = 'text';
+ document.body.appendChild(input);
+ input.focus();
+
+ var previous = document.activeElement;
+ dialog.showModal();
+ assert.notEqual(previous, document.activeElement);
+ });
+ test('default focus on modal', function() {
+ var input = cleanup(document.createElement('input'));
+ input.type = 'text';
+ dialog.appendChild(input);
+
+ var anotherInput = cleanup(document.createElement('input'));
+ anotherInput.type = 'text';
+ dialog.appendChild(anotherInput);
+
+ dialog.showModal();
+ assert.equal(document.activeElement, input);
+ });
+ test('default focus on non-modal', function() {
+ var div = cleanup(document.createElement('div'));
+ div.tabIndex = 4;
+ dialog.appendChild(div);
+
+ dialog.show();
+ assert.equal(document.activeElement, div);
+ });
+ test('autofocus element chosen', function() {
+ var input = cleanup(document.createElement('input'));
+ input.type = 'text';
+ dialog.appendChild(input);
+
+ var inputAF = cleanup(document.createElement('input'));
+ inputAF.type = 'text';
+ inputAF.autofocus = true;
+ dialog.appendChild(inputAF);
+
+ dialog.showModal();
+ assert.equal(document.activeElement, inputAF);
+ });
+ test('child modal dialog', function() {
+ dialog.showModal();
+
+ var input = cleanup(document.createElement('input'));
+ input.type = 'text';
+ dialog.appendChild(input);
+ input.focus();
+ assert.equal(document.activeElement, input);
+
+ // NOTE: This is a single sub-test, but all the above tests could be run
+ // again in a sub-context (i.e., dialog within dialog).
+ var child = createDialog();
+ child.showModal();
+ assert.notEqual(document.activeElement, input,
+ 'additional modal dialog should clear parent focus');
+
+ child.close();
+ assert.notEqual(document.activeElement, input,
+ 'parent focus should not be restored');
+ });
+ test('don\'t scroll anything into focus', function() {
+ // https://github.com/GoogleChrome/dialog-polyfill/issues/119
+
+ var div = cleanup(document.createElement('div'));
+ document.body.appendChild(div);
+
+ var inner = document.createElement('div');
+ inner.style.height = '10000px';
+ div.appendChild(inner);
+
+ div.appendChild(dialog);
+
+ var input = cleanup(document.createElement('input'));
+ input.type = 'text';
+ dialog.appendChild(input);
+
+ var prev = document.documentElement.scrollTop;
+ dialog.showModal();
+ assert.equal(document.documentElement.scrollTop, prev);
+ });
+ });
+
+ suite('top layer / inert', function() {
+ test('background focus allowed on non-modal', function() {
+ var input = cleanup(document.createElement('input'));
+ input.type = 'text';
+ document.body.appendChild(input);
+ input.focus();
+
+ dialog.show();
+ assert.notEqual(document.activeElement, input,
+ 'non-modal dialog should clear focus, even with no dialog content');
+
+ document.body.focus();
+ input.focus();
+ assert.equal(document.activeElement, input,
+ 'non-modal should allow background focus');
+ });
+ test('modal disallows background focus', function() {
+ var input = cleanup(document.createElement('input'));
+ input.type = 'text';
+ document.body.appendChild(input);
+
+ dialog.showModal();
+ input.focus();
+
+ if (!document.hasFocus()) {
+ // Browsers won't trigger a focus event if they're not in the
+ // foreground, so we can't intercept it. However, they'll fire one when
+ // restored, before a user can get to any incorrectly focused element.
+ console.warn('background focus test requires document focus');
+ document.documentElement.focus();
+ }
+ assert.notEqual(document.activeElement, input,
+ 'modal should disallow background focus');
+ });
+ test('overlay is a sibling of topmost dialog', function() {
+ var stacking = cleanup(document.createElement('div'));
+ stacking.style.opacity = 0.8; // creates stacking context
+ document.body.appendChild(stacking);
+ stacking.appendChild(dialog);
+ dialog.showModal();
+
+ var overlay = document.querySelector('._dialog_overlay');
+ assert.isNotNull(overlay);
+ assert.equal(overlay.parentNode, dialog.parentNode);
+ });
+ test('overlay is between topmost and remaining dialogs', function() {
+ dialog.showModal();
+
+ var other = cleanup(createDialog());
+ document.body.appendChild(other);
+ other.showModal();
+
+ var overlay = document.querySelector('._dialog_overlay');
+ assert.isNotNull(overlay);
+ assert.equal(overlay.parentNode, other.parentNode);
+
+ assert.isAbove(+other.style.zIndex, +overlay.style.zIndex, 'top-most dialog above overlay');
+ assert.isAbove(+overlay.style.zIndex, +dialog.style.zIndex, 'overlay above other dialogs');
+ });
+ });
+
+ suite('events', function() {
+ test('close event', function() {
+ var closeFired = 0;
+ dialog.addEventListener('close', function() {
+ ++closeFired;
+ });
+
+ dialog.show();
+ assert.equal(closeFired, 0);
+
+ dialog.close();
+ assert.equal(closeFired, 1);
+
+ assert.throws(dialog.close); // can't close already closed dialog
+ assert.equal(closeFired, 1);
+
+ dialog.showModal();
+ dialog.close();
+ assert.equal(closeFired, 2);
+ });
+ test('cancel event', function() {
+ dialog.showModal();
+ dialog.dispatchEvent(createKeyboardEvent(27));
+ assert.isFalse(dialog.open, 'esc should close modal');
+
+ var cancelFired = 0;
+ dialog.addEventListener('cancel', function() {
+ ++cancelFired;
+ });
+ dialog.showModal();
+ dialog.dispatchEvent(createKeyboardEvent(27));
+ assert.equal(cancelFired, 1, 'expected cancel to be fired');
+ assert.isFalse(dialog.open), 'esc should close modal again';
+
+ // Sanity-check that non-modals aren't effected.
+ dialog.show();
+ dialog.dispatchEvent(createKeyboardEvent(27));
+ assert.isTrue(dialog.open, 'esc should only close modal dialog');
+ assert.equal(cancelFired, 1);
+ });
+ test('overlay click is prevented', function() {
+ dialog.showModal();
+
+ var overlay = document.querySelector('._dialog_overlay');
+ assert.isNotNull(overlay);
+
+ var helper = function(ev) {
+ throw Error('body should not be clicked');
+ };
+ try {
+ document.body.addEventListener('click', helper);
+ overlay.click();
+ } finally {
+ document.body.removeEventListener('click', helper);
+ }
+ });
+ });
+
+ suite('form', function() {
+ test('method attribute is translated to property', function() {
+ var form = document.createElement('form');
+ form.method = 'dialog';
+ assert.equal(form.method, 'dialog');
+
+ form.method = 'PoSt';
+ assert.equal(form.method, 'post');
+ assert.equal(form.getAttribute('method'), 'PoSt');
+ });
+ test('dialog method input', function() {
+ var value = 'ExpectedValue' + Math.random();
+
+ var form = document.createElement('form');
+ try {
+ form.method = 'dialog';
+ } catch (e) {
+ // Setting the method directly throws an exception in <=IE9.
+ form.setAttribute('method', 'dialog');
+ }
+ dialog.appendChild(form);
+
+ var input = document.createElement('input');
+ input.type = 'submit';
+ input.value = value;
+ form.appendChild(input);
+
+ var closeCount = 0;
+ dialog.addEventListener('close', function() {
+ ++closeCount;
+ });
+
+ dialog.show();
+ input.focus(); // emulate user focus action
+ input.click();
+
+ assert.isFalse(dialog.open);
+ assert.equal(dialog.returnValue, value);
+ assert.equal(closeCount, 1);
+ });
+ test('dialog with button preventDefault does not trigger submit', function() {
+ var form = document.createElement('form');
+ form.setAttribute('method', 'dialog');
+ dialog.appendChild(form);
+
+ var button = document.createElement('button');
+ button.value = 'does not matter';
+ form.appendChild(button);
+ button.addEventListener('click', function(ev) {
+ ev.preventDefault();
+ });
+
+ dialog.showModal();
+ button.click();
+
+ assert.isTrue(dialog.open, 'dialog should remain open');
+ assert.equal(dialog.returnValue, '');
+ });
+ test('dialog programmatic submit does not change returnValue', function() {
+ var form = document.createElement('form');
+ form.setAttribute('method', 'dialog');
+
+ dialog.returnValue = 'manually set'; // set before appending
+ dialog.appendChild(form);
+
+ dialog.showModal();
+ form.submit();
+ assert.isFalse(dialog.open);
+
+ assert.equal(dialog.returnValue, 'manually set', 'returnValue should not change');
+ });
+ test('dialog method button', function() {
+ var value = 'ExpectedValue' + Math.random();
+
+ var form = document.createElement('form');
+ form.setAttribute('method', 'dialog');
+ dialog.appendChild(form);
+
+ var button = document.createElement('button');
+ button.value = value;
+ form.appendChild(button);
+
+ dialog.showModal();
+ button.focus(); // emulate user focus action
+ button.click();
+
+ assert.isFalse(dialog.open);
+ assert.equal(dialog.returnValue, value);
+
+ // Clear button value, confirm textContent is not used as value.
+ button.value = 'blah blah';
+ button.removeAttribute('value');
+ button.textContent = value;
+ dialog.show();
+ button.focus(); // emulate user focus action
+ button.click();
+
+ assert.equal(dialog.returnValue, button.value,
+ 'don\'t take button textContent as value');
+ });
+ test('boring form inside dialog', function() {
+ var form = document.createElement('form');
+ dialog.appendChild(form); // don't specify method
+ form.addEventListener('submit', function(ev) {
+ ev.preventDefault();
+ });
+
+ var button = document.createElement('button');
+ button.value = 'Moot';
+ form.appendChild(button);
+
+ dialog.showModal();
+ button.focus(); // emulate user focus action
+ button.click();
+
+ assert.isTrue(dialog.open, 'non-dialog form should not close dialog')
+ assert(!dialog.returnValue);
+ });
+ test('type="image" submitter', function() {
+ var form = document.createElement('form');
+ form.setAttribute('method', 'dialog');
+ dialog.appendChild(form);
+ dialog.show();
+
+ var image = document.createElement('input');
+ image.type = 'image';
+ image.src = 'data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==';
+ image.setAttribute('value', 'image should not accept value');
+ form.appendChild(image);
+ image.click();
+
+ assert.notEqual(image.getAttribute('value'), dialog.returnValue);
+ assert.equal(dialog.returnValue, '0,0');
+ });
+ test('form submitter across dialogs', function() {
+ var form1 = document.createElement('form');
+ form1.setAttribute('method', 'dialog');
+ dialog.appendChild(form1);
+
+ var button1 = document.createElement('button');
+ button1.value = 'from form1: first value';
+ form1.appendChild(button1);
+ dialog.showModal();
+
+ var dialog2 = createDialog();
+ dialog2.returnValue = 'dialog2 default close value';
+ var form2 = document.createElement('form');
+ form2.setAttribute('method', 'dialog');
+ dialog2.appendChild(form2);
+ dialog2.showModal();
+
+ button1.click();
+ assert.isFalse(dialog.open);
+
+ // nb. this never fires 'submit' so the .returnValue can't be wrong: is there another way
+ // to submit a form that doesn't involve a click (enter implicitly 'clicks') or submit?
+ form2.submit();
+ assert.isFalse(dialog2.open);
+
+ assert.equal(dialog2.returnValue, 'dialog2 default close value',
+ 'second dialog shouldn\'t reuse formSubmitter');
+ });
+ });
+
+ suite('order', function() {
+ test('non-modal unchanged', function() {
+ var one = createDialog();
+ var two = createDialog();
+
+ one.style.zIndex = 100;
+ two.style.zIndex = 200;
+ one.show();
+ two.show();
+
+ assert.equal(window.getComputedStyle(one).zIndex, 100);
+ assert.equal(window.getComputedStyle(two).zIndex, 200);
+
+ two.close();
+ assert.equal(window.getComputedStyle(two).zIndex, 200);
+ });
+ test('modal stacking order', function() {
+ dialog.showModal();
+
+ // Create incorrectly-named dialogs: front has a lower z-index, and back
+ // has a higher z-index.
+ var front = createDialog();
+ var back = createDialog();
+ front.style.zIndex = 100;
+ back.style.zIndex = 200;
+
+ // Show back first, then front. Thus we expect back to be behind front.
+ back.showModal();
+ front.showModal();
+
+ var zf = +window.getComputedStyle(front).zIndex;
+ var zb = +window.getComputedStyle(back).zIndex;
+ assert.isAbove(zf, zb, 'showModal order dictates z-index');
+
+ var backBackdrop = back.nextElementSibling;
+ var zbb = +window.getComputedStyle(backBackdrop).zIndex;
+ assert.equal(backBackdrop.className, 'backdrop');
+ assert.isBelow(zbb, zb, 'backdrop below dialog');
+
+ var frontBackdrop = front.nextElementSibling;
+ var zfb = +window.getComputedStyle(frontBackdrop).zIndex
+ assert.equal(frontBackdrop.className, 'backdrop');
+ assert.isBelow(zfb, zf,' backdrop below dialog');
+
+ assert.isAbove(zfb, zb, 'front backdrop is above back dialog');
+
+ front.close();
+ assert.notOk(front.style.zIndex, 'modal close should clear zindex');
+ });
+ });
+
+ suite('press tab key', function() {
+ test('tab key', function() {
+ var dialog = createDialog();
+ dialog.showModal();
+
+ document.documentElement.dispatchEvent(createKeyboardEvent(9));
+
+ var ev = document.createEvent('Events');
+ ev.initEvent('focus', true, true);
+ document.documentElement.dispatchEvent(ev);
+
+ dialog.close();
+ });
+ });
+}();
diff --git a/node_modules/dialog-polyfill/test.html b/node_modules/dialog-polyfill/test.html
new file mode 100644
index 0000000..03e9b0b
--- /dev/null
+++ b/node_modules/dialog-polyfill/test.html
@@ -0,0 +1,49 @@
+<!--
+ Copyright 2015 Google Inc. All rights reserved.
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<!DOCTYPE html>
+<meta charset="UTF-8" />
+<link rel="stylesheet" href="node_modules/mocha/mocha.css" />
+<script src="node_modules/mocha/mocha.js"></script>
+<script src="node_modules/chai/chai.js"></script>
+<link rel="stylesheet" type="text/css" href="dist/dialog-polyfill.css" />
+<script src="dist/dialog-polyfill.js"></script>
+<script>
+
+var assert = chai.assert;
+mocha.setup({ ui: 'tdd' });
+
+(function() {
+ var pageError = null;
+
+ window.addEventListener('error', function(event) {
+ pageError = event.filename + ':' + event.lineno + ' ' + event.message;
+ });
+
+ window.addEventListener('load', function() {
+ if (pageError) {
+ suite('page-script-errors', function() {
+ test('no script errors on page', function() {
+ assert.fail(null, null, pageError);
+ });
+ });
+ }
+ mocha.run();
+ });
+})();
+
+</script>
+<div id="mocha"></div>
+<script src="suite.js"></script>
diff --git a/node_modules/dialog-polyfill/tests/actionbar.html b/node_modules/dialog-polyfill/tests/actionbar.html
new file mode 100644
index 0000000..3af6c75
--- /dev/null
+++ b/node_modules/dialog-polyfill/tests/actionbar.html
@@ -0,0 +1,188 @@
+<!DOCTYPE html>
+<html>
+<meta name="viewport" content="initial-scale=1" />
+<meta charset="UTF-8" />
+<head>
+ <script src="../dist/dialog-polyfill.js"></script>
+ <link rel="stylesheet" type="text/css" href="../dist/dialog-polyfill.css">
+ <style>
+
+ dialog.actionbar {
+ position: fixed;
+ border: 0;
+ box-sizing: border-box;
+ width: 100%;
+ bottom: 0;
+ background: #fff;
+ transform: translate(0, 100%);
+ transition: transform 0.25s ease-in-out;
+ margin: 0;
+ padding: 0;
+ box-shadow: 0 0 4px rgba(0, 0, 0, 0.25);
+ }
+ dialog.actionbar.appear {
+ transform: translate(0);
+ }
+
+ dialog.actionbar .holder {
+ display: flex;
+ justify-content: space-around;
+ flex-flow: wrap;
+ }
+ dialog.actionbar .holder button {
+ flex-grow: 1;
+ min-width: 200px;
+ margin: 8px;
+ border: 0;
+ font-family: Roboto, Arial, Sans-Serif;
+ font-size: 23px;
+ line-height: 33px;
+ font-weight: 400;
+ color: #666;
+ border-radius: 2px;
+ background: #ccc;
+ display: block;
+ box-sizing: border-box;
+ border: 2px solid transparent;
+ }
+ dialog.actionbar .holder button:focus {
+ outline: 0;
+ border-color: rgba(0, 0, 0, 0.125);
+ }
+ dialog.actionbar .holder button.cancel {
+ background: #fcc;
+ }
+
+ dialog.actionbar::backdrop {
+ background: rgba(0, 0, 0, 0.25);
+ opacity: 0;
+ transition: opacity 0.25s ease-in-out;
+ }
+ dialog.actionbar + .backdrop {
+ background: rgba(0, 0, 0, 0.25);
+ opacity: 0;
+ transition: opacity 0.25s ease-in-out;
+ }
+ dialog.actionbar.appear::backdrop {
+ opacity: 1;
+ }
+ dialog.actionbar.appear + .backdrop {
+ opacity: 1;
+ }
+
+ </style>
+</head>
+<body>
+
+<p>Builds an action bar from a modal dialog.</p>
+
+<button id="show">Show</button>
+
+<script>
+
+var ActionBar = function() {
+ this.options_ = [];
+ this.cancelButton_ = true;
+ this.dialog_ = null;
+};
+
+ActionBar.prototype.enableCancelButton = function(value) {
+ this.cancelButton_ = vaue;
+};
+
+ActionBar.prototype.addOption = function(name, opt_callback) {
+ this.options_.push({name: name, callback: opt_callback || null});
+};
+
+ActionBar.prototype.show = function(opt_callback) {
+ if (this.dialog_ != null) {
+ throw "Can't show ActionBar, already visible";
+ }
+
+ var dialog = document.createElement('dialog');
+ if (!('showModal' in dialog)) {
+ dialogPolyfill.registerDialog(dialog);
+ }
+ dialog.className = 'actionbar';
+
+ var holder = document.createElement('div');
+ holder.className = 'holder';
+ dialog.appendChild(holder);
+
+ dialog.addEventListener('click', function(ev) {
+ if (ev.target == dialog) {
+ dialog.close();
+ }
+ });
+
+ dialog.addEventListener('close', function() {
+ opt_callback && opt_callback(dialog.returnValue);
+
+ var cloneDialog = dialog.cloneNode(true);
+ if (!('showModal' in cloneDialog)) {
+ dialogPolyfill.registerDialog(cloneDialog);
+ }
+
+ document.body.appendChild(cloneDialog);
+ cloneDialog.showModal();
+ cloneDialog.classList.remove('appear');
+ window.setTimeout(function() {
+ cloneDialog.close(); // TODO: needed until DOM removal is safe
+ cloneDialog.parentNode.removeChild(cloneDialog);
+ }, 250);
+
+ if (dialog.parentNode) {
+ dialog.parentNode.removeChild(dialog);
+ }
+ if (this.dialog_ == dialog) {
+ this.dialog_ = null;
+ }
+ }.bind(this));
+
+ this.options_.forEach(function(option) {
+ var button = document.createElement('button');
+ button.textContent = option.name;
+ button.addEventListener('click', function() {
+ if (option.callback) {
+ window.setTimeout(option.callback, 0);
+ }
+ dialog.close(option.name);
+ });
+ holder.appendChild(button);
+ });
+
+ if (this.cancelButton_) {
+ var cancelButton = document.createElement('button');
+ cancelButton.className = 'cancel';
+ cancelButton.textContent = 'Cancel';
+ cancelButton.addEventListener('click', function() {
+ dialog.close();
+ });
+ holder.appendChild(cancelButton);
+ }
+
+ document.body.appendChild(dialog);
+ dialog.showModal();
+ dialog.classList.add('appear');
+
+ this.dialog_ = dialog;
+};
+
+////// DEMO
+
+show.addEventListener('click', function() {
+ var ab = new ActionBar();
+ ab.addOption('Display');
+ ab.addOption('Options');
+ ab.addOption('Longer Choice');
+ ab.addOption('Alert', function() {
+ alert('you hit Alert');
+ });
+ ab.show(function(choice) {
+ console.info('choice was', choice);
+ });
+});
+
+</script>
+</body>
+</html>
diff --git a/node_modules/dialog-polyfill/tests/backdrop.html b/node_modules/dialog-polyfill/tests/backdrop.html
new file mode 100644
index 0000000..77c6d81
--- /dev/null
+++ b/node_modules/dialog-polyfill/tests/backdrop.html
@@ -0,0 +1,38 @@
+<!DOCTYPE html>
+<html>
+<meta charset='utf-8'>
+<head>
+<script src="../dist/dialog-polyfill.js"></script>
+<link rel="stylesheet" type="text/css" href="../dist/dialog-polyfill.css">
+<style>
+dialog {
+ height: 100px;
+ width: 100px;
+}
+
+dialog + .backdrop {
+ background-color: rgba(0,255,0,0.5);
+}
+</style>
+</head>
+<body>
+<p>Test for backdrop. The test passes if you see a green background behind the
+box.</p>
+<div id="console"></div>
+<dialog></dialog>
+<button id="inert-button">Can't click me</button>
+<script>
+function writeToConsole(s) {
+ var console = document.getElementById('console');
+ var span = document.createElement('span');
+ span.textContent = s;
+ console.appendChild(span);
+ console.appendChild(document.createElement('br'));
+}
+
+var dialog = document.querySelector('dialog');
+dialogPolyfill.registerDialog(dialog);
+dialog.showModal();
+</script>
+</body>
+</html>
diff --git a/node_modules/dialog-polyfill/tests/dialog-centering.html b/node_modules/dialog-polyfill/tests/dialog-centering.html
new file mode 100644
index 0000000..0eaff60
--- /dev/null
+++ b/node_modules/dialog-polyfill/tests/dialog-centering.html
@@ -0,0 +1,41 @@
+<!DOCTYPE html>
+<html>
+<meta charset='utf-8'>
+<head>
+<script src="../dist/dialog-polyfill.js"></script>
+<link rel="stylesheet" type="text/css" href="../dist/dialog-polyfill.css">
+</head>
+<body>
+<p>Test that dialog is centered in the viewport. The test passes if you see a
+box in the center of the screen.</p>
+<div id="console"></div>
+<dialog>Hello</dialog>
+<script>
+function writeToConsole(s) {
+ var console = document.getElementById('console');
+ var span = document.createElement('span');
+ span.textContent = s;
+ console.appendChild(span);
+ console.appendChild(document.createElement('br'));
+}
+
+function checkCentered(dialog) {
+ var expectedTop = (window.innerHeight - dialog.offsetHeight) / 2;
+ var expectedLeft = (window.innerWidth - dialog.offsetWidth) / 2;
+ var rect = dialog.getBoundingClientRect();
+ if (rect.top == expectedTop && rect.left == expectedLeft) {
+ writeToConsole('SUCCESS');
+ } else {
+ writeToConsole('FAIL: expected dialog top,left to be ' +
+ expectedTop + ',' + expectedLeft + ' and was actually ' +
+ rect.top + ',' + rect.left);
+ }
+}
+
+var dialog = document.querySelector('dialog');
+dialogPolyfill.registerDialog(dialog);
+dialog.show();
+checkCentered(dialog);
+</script>
+</body>
+</html>
diff --git a/node_modules/dialog-polyfill/tests/dialog-recentering.html b/node_modules/dialog-polyfill/tests/dialog-recentering.html
new file mode 100644
index 0000000..5d09f21
--- /dev/null
+++ b/node_modules/dialog-polyfill/tests/dialog-recentering.html
@@ -0,0 +1,65 @@
+<!DOCTYPE html>
+<html>
+<meta charset='utf-8'>
+<head>
+<script src="../dist/dialog-polyfill.js"></script>
+<link rel="stylesheet" type="text/css" href="../dist/dialog-polyfill.css">
+<style>
+body {
+ height: 10000px;
+}
+dialog {
+ width: 100px;
+}
+#console {
+ position: fixed;
+}
+#ruler {
+ position: absolute;
+ left: 0;
+ width: 100%;
+}
+</style>
+</head>
+<body>
+<p>Test that dialog is recentered if reopened. The test passes if you see a
+box in the center of the screen.</p>
+<div id="ruler"></div>
+<div id="console"></div>
+<dialog></dialog>
+<script>
+function writeToConsole(s) {
+ var console = document.getElementById('console');
+ var span = document.createElement('span');
+ span.textContent = s;
+ console.appendChild(span);
+ console.appendChild(document.createElement('br'));
+}
+
+function windowWidthMinusScrollbar() {
+ return document.getElementById('ruler').offsetWidth;
+}
+
+function checkCentered(dialog) {
+ var expectedTop = (window.innerHeight - dialog.offsetHeight) / 2;
+ var expectedLeft = (windowWidthMinusScrollbar() - dialog.offsetWidth) / 2;
+ var rect = dialog.getBoundingClientRect();
+ if (rect.top == expectedTop && rect.left == expectedLeft) {
+ writeToConsole('SUCCESS');
+ } else {
+ writeToConsole('FAIL: expected dialog top,left to be ' +
+ expectedTop + ',' + expectedLeft + ' and was actually ' +
+ rect.top + ',' + rect.left);
+ }
+}
+
+var dialog = document.querySelector('dialog');
+dialogPolyfill.registerDialog(dialog);
+dialog.show();
+dialog.close();
+window.scrollTo(0, 500);
+dialog.show();
+checkCentered(dialog);
+</script>
+</body>
+</html>
diff --git a/node_modules/dialog-polyfill/tests/fancy-modal-dialog.html b/node_modules/dialog-polyfill/tests/fancy-modal-dialog.html
new file mode 100644
index 0000000..3e10740
--- /dev/null
+++ b/node_modules/dialog-polyfill/tests/fancy-modal-dialog.html
@@ -0,0 +1,270 @@
+<!DOCTYPE html>
+<html>
+<head>
+<script src="../dist/dialog-polyfill.js"></script>
+<link rel="stylesheet" type="text/css" href="../dist/dialog-polyfill.css">
+<style>
+#close-button {
+ position: absolute;
+ top: 7px;
+ right: 7px;
+ height: 14px;
+ width: 14px;
+ margin: 0;
+ background-image: url('resources/close_dialog.png');
+}
+
+#close-button:hover {
+ background-image: url('resources/close_dialog_hover.png');
+}
+
+dialog {
+/* width: 50%;
+ border-radius: 3px;
+ opacity: 0;
+ background: white;
+ box-shadow: 0 4px 23px 5px rgba(0, 0, 0, 0.2), 0 2px 6px rgba(0,0,0,0.15);
+ color: #333;
+ min-width: 400px;
+ padding: 0;
+ z-index: 100;
+ border: 0;
+ padding: 15px;
+*/}
+
+dialog + .backdrop {
+ position: fixed;
+ top: 0;
+ bottom: 0;
+ left: 0;
+ right: 0;
+}
+
+dialog.no-backdrop + .backdrop {
+ display: none;
+}
+
+.dialog-setting {
+ margin: 30px;
+}
+
+/* keyframes used to pulse the overlay */
+@-webkit-keyframes pulse {
+ 0% {
+ -webkit-transform: scale(1);
+ }
+ 40% {
+ -webkit-transform: scale(1.05);
+ }
+ 60% {
+ -webkit-transform: scale(1.05);
+ }
+ 100% {
+ -webkit-transform: scale(1);
+ }
+}
+
+.pulse {
+ -webkit-animation-duration: 180ms;
+ -webkit-animation-iteration-count: 1;
+ -webkit-animation-name: pulse;
+ -webkit-animation-timing-function: ease-in-out;
+}
+#messages {
+ background-color: red;
+}
+body {
+ font-family: sans-serif;
+}
+
+#open-button {
+ position: fixed;
+ bottom: 0;
+ left: 0;
+ background-color: lightgray;
+ border: 1px solid;
+ margin: 10px;
+ padding: 15px;
+}
+
+#open-button:active {
+ background-color: lime;
+ margin: 9px;
+}
+
+#backdrop {
+ display: none;
+ position: fixed;
+ top:0;
+ right:0;
+ bottom:0;
+ left:0;
+ background: rgba(0,0,0,0.5);
+ z-index: 10;
+}
+.post {
+ margin: 10px;
+ padding: 5px;
+ border: 2px solid;
+}
+
+.post-buttons {
+ margin: 5px;
+ text-align: right;
+}
+.post-button:hover {
+ color: green;
+ font-weight: bold;
+}
+</style>
+<body>
+<div id="messages"></div>
+<dialog id="dialog" class="_dialog_fixed">
+ <h3>Reshare</h3>
+ <input type="text" style="width: 75%" value="I am resharing this."><br>
+ <div class="dialog-setting">
+ <input id="click-outside-to-close" type="checkbox">
+ <label for="click-outside-to-close">Close dialog upon clicking outside</label>
+ </div>
+ <div class="dialog-setting">
+ <input id="enable-backdrop" type="checkbox" checked>
+ <label for="enable-backdrop">Enable ::backdrop</label>
+ </div>
+ <div id="close-button"></div>
+</dialog>
+<div id="post-container"></div>
+<script>
+var dialog = document.getElementById('dialog');
+dialogPolyfill.registerDialog(dialog);
+
+function createPostButton(container, text) {
+ var link = document.createElement('a');
+ link.href = 'javascript:void(0)';
+ link.textContent = text;
+ link.className = 'post-button';
+ container.appendChild(link);
+ var span = document.createElement('span');
+ span.textContent = ' ';
+ container.appendChild(span);
+ return link;
+}
+
+SampleText = 'From this spot I rise not, valiant knight, until your ' +
+ 'courtesy grants me the boon I seek, one that will redound ' +
+ 'to your praise and the benefit of the human race.';
+
+function createPost(container) {
+ var post = document.createElement('div');
+ post.className = 'post';
+ var postContent = document.createElement('div');
+ postContent.className = 'post-content';
+ postContent.textContent = SampleText;
+ post.appendChild(postContent);
+ var postButtons = document.createElement('div');
+ postButtons.className = 'post-buttons';
+ post.appendChild(postButtons);
+ var reshare = createPostButton(postButtons, 'Reshare');
+ reshare.addEventListener('click', openDialog);
+ var reply = createPostButton(postButtons, 'Reply');
+ reply.addEventListener('click', openDialog);
+ createPostButton(postButtons, 'Enjoyed this post');
+
+ container.appendChild(post);
+ return post;
+}
+
+function initPosts() {
+ var container = document.getElementById('post-container');
+ for (var i = 0; i < 25; ++i) {
+ var post = createPost(container);
+ }
+}
+
+function shouldCloseDialogOnClickOutside() {
+ return document.getElementById('click-outside-to-close').checked;
+}
+
+function closeDialog() {
+ if (dialog.open)
+ dialog.close();
+}
+
+function computeCenteredTop(dialog) {
+ dialog.style.transition = '';
+ dialog.showModal();
+ var computedTopPx = window.getComputedStyle(dialog).top;
+ var computedTop = parseInt(computedTopPx.substring(0, computedTopPx.length - 2), 10);
+ dialog.close();
+ return computedTop;
+}
+
+function openDialog() {
+ dialog.style.opacity = 0;
+ dialog.style.transition = 'all 250ms ease';
+
+ dialog.showModal();
+
+ dialog.style.opacity = 1;
+}
+
+function pulseDialog() {
+ if (!dialog.style.webkitAnimation) {
+ return;
+ }
+ // classList isn't supported in IE8, but it's safe to use here as this only
+ // runs inside WebKit/Chrome anyway.
+ dialog.classList.add('pulse');
+ dialog.addEventListener('webkitAnimationEnd', function(e) {
+ dialog.classList.remove('pulse');
+ });
+}
+
+function clickedInDialog(mouseEvent) {
+ var rect = dialog.getBoundingClientRect();
+ return rect.top <= mouseEvent.clientY && mouseEvent.clientY <= rect.top + rect.height
+ && rect.left <= mouseEvent.clientX && mouseEvent.clientX <= rect.left + rect.width;
+}
+
+function handleClickOutsideDialog() {
+ if (!shouldCloseDialogOnClickOutside()) {
+ pulseDialog();
+ return
+ }
+ closeDialog();
+}
+
+document.body.addEventListener('keydown', function(e) {
+ if (e.keyCode == 27)
+ closeDialog();
+});
+
+var enableBackdrop = document.getElementById('enable-backdrop');
+enableBackdrop.addEventListener('change', function(e) {
+ if (this.checked) {
+ dialog.className = '';
+ } else {
+ dialog.className = 'no-backdrop';
+ }
+});
+
+var closeButton = document.getElementById('close-button');
+closeButton.addEventListener('click', function(e) { closeDialog(); });
+
+document.body.addEventListener('click', function(e) {
+ console.info('document body click', e.target);
+ if (!dialog.open)
+ return;
+ if (e.target != document.body)
+ return;
+ handleClickOutsideDialog();
+});
+
+dialog.addEventListener('click', function(e) {
+ if (clickedInDialog(e))
+ return;
+ handleClickOutsideDialog();
+});
+initPosts();
+</script>
+</body>
+</html>
diff --git a/node_modules/dialog-polyfill/tests/form.html b/node_modules/dialog-polyfill/tests/form.html
new file mode 100644
index 0000000..75e1645
--- /dev/null
+++ b/node_modules/dialog-polyfill/tests/form.html
@@ -0,0 +1,48 @@
+<!DOCTYPE html>
+<html>
+<meta charset='utf-8'>
+<head>
+<script src="../dist/dialog-polyfill.js"></script>
+<link rel="stylesheet" type="text/css" href="../dist/dialog-polyfill.css">
+</head>
+<body>
+
+ <p>
+Enter a value and submit the form. The close event will be fired and the <code>returnValue</code> of the dialog will be alerted.
+ </p>
+
+<input type="text" placeholder="Focusable pre" />
+
+<dialog>
+ <form method="dialog">
+ <input type="text" placeholder="Enter value" />
+ <input type="reset" value="Reset" />
+ <input type="submit" value="Stuff" />
+ <input type="submit" value="Done" />
+ <button type="submit" value="Button Value Is Different Than Text">Button Submit</button>
+ <button value="Regular Button Value">Button</button>
+ </form>
+</dialog>
+
+<button id="show">Show Dialog</button>
+
+<input type="text" placeholder="Focusable post" />
+
+
+<script>
+var dialog = document.querySelector('dialog');
+dialogPolyfill.registerDialog(dialog);
+dialog.showModal();
+
+dialog.addEventListener('close', function() {
+ var valueEl = dialog.querySelector('input[type="text"]');
+ alert(dialog.returnValue);
+});
+
+show.addEventListener('click', function() {
+ dialog.showModal();
+});
+
+</script>
+</body>
+</html>
diff --git a/node_modules/dialog-polyfill/tests/modal-dialog-stacking.html b/node_modules/dialog-polyfill/tests/modal-dialog-stacking.html
new file mode 100644
index 0000000..e111ee4
--- /dev/null
+++ b/node_modules/dialog-polyfill/tests/modal-dialog-stacking.html
@@ -0,0 +1,89 @@
+<!DOCTYPE html>
+<html>
+<meta charset='utf-8'>
+<head>
+<script src="../dist/dialog-polyfill.js"></script>
+<link rel="stylesheet" type="text/css" href="../dist/dialog-polyfill.css">
+<style>
+dialog {
+ padding: 0px;
+ border: none;
+ margin: 0px;
+}
+
+#bottom + .backdrop {
+ top: 100px;
+ left: 100px;
+ height: 300px;
+ width: 300px;
+ background-color: rgb(0, 50, 0);
+ z-index: 100; /* z-index has no effect. */
+}
+
+#bottom {
+ top: 125px;
+ left: 125px;
+ height: 250px;
+ width: 250px;
+ background-color: rgb(0, 90, 0);
+}
+
+#middle + .backdrop {
+ top: 150px;
+ left: 150px;
+ height: 200px;
+ width: 200px;
+ background-color: rgb(0, 130, 0);
+ z-index: -100; /* z-index has no effect. */
+}
+
+#middle {
+ top: 175px;
+ left: 175px;
+ height: 150px;
+ width: 150px;
+ background-color: rgb(0, 170, 0);
+}
+
+#top + .backdrop {
+ top: 200px;
+ left: 200px;
+ height: 100px;
+ width: 100px;
+ background-color: rgb(0, 210, 0);
+ z-index: 0; /* z-index has no effect. */
+}
+
+#top {
+ top: 225px;
+ left: 225px;
+ height: 50px;
+ width: 50px;
+ background-color: rgb(0, 255, 0);
+ z-index: -1000; /* z-index has no effect. */
+}
+</style>
+</head>
+<body>
+Test for modal dialog and backdrop stacking order. The test passes if there are
+6 boxes enclosed in each other, becoming increasingly smaller and brighter
+green.
+<dialog id="top"></dialog>
+<dialog id="middle"></dialog>
+<dialog id="bottom"></dialog>
+<script>
+var topDialog = document.getElementById('top');
+dialogPolyfill.registerDialog(topDialog);
+var middleDialog = document.getElementById('middle');
+dialogPolyfill.registerDialog(middleDialog);
+var bottomDialog = document.getElementById('bottom');
+dialogPolyfill.registerDialog(bottomDialog);
+
+topDialog.showModal();
+bottomDialog.showModal();
+topDialog.close(); // Just to shuffle the top layer order around a little.
+middleDialog.showModal();
+topDialog.showModal();
+</script>
+</body>
+</html>
diff --git a/node_modules/dialog-polyfill/tests/modal-dialog.html b/node_modules/dialog-polyfill/tests/modal-dialog.html
new file mode 100644
index 0000000..be98e91
--- /dev/null
+++ b/node_modules/dialog-polyfill/tests/modal-dialog.html
@@ -0,0 +1,62 @@
+<!DOCTYPE html>
+<html tabindex="0">
+<meta charset='utf-8'>
+<head>
+<script src="../dist/dialog-polyfill.js"></script>
+<meta name="viewport" content="width=device-width, user-scalable=no">
+<link rel="stylesheet" type="text/css" href="../dist/dialog-polyfill.css">
+<style>
+ html {
+ border: 4px solid white;
+ }
+ html:focus {
+ border-color: red;
+ }
+dialog {
+ width: 100px;
+}
+</style>
+</head>
+<body>
+<p>Test for modal dialog. The test passes if you can click on "Click me" button,
+but can't click or tab to the "Can't click me!" button</p>
+<div id="console"></div>
+
+<input type="text" placeholder="Test 1" tabindex="1" />
+<input type="text" placeholder="Test 2" tabindex="2" />
+<input type="text" placeholder="Test 2.1" tabindex="2" />
+<input type="text" placeholder="Test 3" tabindex="3" />
+
+<input type="text" placeholder="Before dialog" />
+<dialog>
+ <button id="dialog-button">Click me</button>
+ <input type="text" placeholder="Focus me" />
+ <input type="text" placeholder="Focus me again" />
+</dialog>
+<button id="inert-button">Can't click me</button>
+<input type="text" placeholder="Can't focus me" />
+<script>
+function writeToConsole(s) {
+ var console = document.getElementById('console');
+ var span = document.createElement('span');
+ span.textContent = s;
+ console.appendChild(span);
+ console.appendChild(document.createElement('br'));
+}
+
+var dialog = document.querySelector('dialog');
+dialogPolyfill.registerDialog(dialog);
+dialog.showModal();
+
+var dialogButton = document.getElementById('dialog-button');
+dialogButton.addEventListener('click', function(e) {
+ writeToConsole("SUCCESS: dialog's button was clicked");
+});
+
+var inertButton = document.getElementById('inert-button');
+inertButton.addEventListener('click', function(e) {
+ writeToConsole('FAIL: inert button was clicked');
+});
+</script>
+</body>
+</html>
diff --git a/node_modules/dialog-polyfill/tests/resources/close_dialog.png b/node_modules/dialog-polyfill/tests/resources/close_dialog.png
new file mode 100644
index 0000000..793f60e
--- /dev/null
+++ b/node_modules/dialog-polyfill/tests/resources/close_dialog.png
Binary files differ
diff --git a/node_modules/dialog-polyfill/tests/resources/close_dialog_hover.png b/node_modules/dialog-polyfill/tests/resources/close_dialog_hover.png
new file mode 100644
index 0000000..fe896f9
--- /dev/null
+++ b/node_modules/dialog-polyfill/tests/resources/close_dialog_hover.png
Binary files differ
diff --git a/node_modules/dialog-polyfill/tests/respect-positioned-dialog.html b/node_modules/dialog-polyfill/tests/respect-positioned-dialog.html
new file mode 100644
index 0000000..1272136
--- /dev/null
+++ b/node_modules/dialog-polyfill/tests/respect-positioned-dialog.html
@@ -0,0 +1,59 @@
+<!DOCTYPE html>
+<html>
+<meta charset='utf-8'>
+<head>
+<script src="../dist/dialog-polyfill.js"></script>
+<link rel="stylesheet" type="text/css" href="../dist/dialog-polyfill.css">
+<style>
+dialog {
+ display: none;
+ height: 50px;
+ width: 50px;
+ padding: 0;
+ margin: 0;
+}
+
+dialog.left {
+ top: 100px;
+ left: 100px;
+}
+
+#middle {
+ top: 100px;
+ left: 200px;
+}
+</style>
+</head>
+<body>
+<p>Test that dialogs with an explicit static position don't get auto-centered.
+The test passes if there are three boxes aligned in a single row.</p>
+<div id="console"></div>
+<dialog id="left" class="left"></dialog>
+<dialog id="middle"></dialog>
+<dialog id="right"></dialog>
+<script>
+function writeToConsole(s) {
+ var console = document.getElementById('console');
+ var span = document.createElement('span');
+ span.textContent = s;
+ console.appendChild(span);
+ console.appendChild(document.createElement('br'));
+}
+
+var dialogs = document.querySelectorAll('dialog');
+for (var i = 0; i < dialogs.length; ++i)
+ dialogPolyfill.registerDialog(dialogs[i]);
+
+var leftDialog = document.getElementById('left');
+leftDialog.show();
+
+var middleDialog = document.getElementById('middle');
+middleDialog.show();
+
+var rightDialog = document.getElementById('right');
+rightDialog.style.top = '100px';
+rightDialog.style.left = '300px';
+rightDialog.show();
+</script>
+</body>
+</html>
diff --git a/node_modules/dialog-polyfill/tests/stacking.html b/node_modules/dialog-polyfill/tests/stacking.html
new file mode 100644
index 0000000..54de723
--- /dev/null
+++ b/node_modules/dialog-polyfill/tests/stacking.html
@@ -0,0 +1,41 @@
+<!DOCTYPE html>
+<html>
+<meta charset='utf-8'>
+<head>
+<script src="../dist/dialog-polyfill.js"></script>
+<link rel="stylesheet" type="text/css" href="../dist/dialog-polyfill.css">
+</head>
+<body>
+
+ <p>
+The dialog below is incorrectly positioned within a stacking context.
+It should generate a console warning message, and place the <code>_dialog_overlay</code> in a different place in order to try to work around the problem without moving the dialog itself.
+ </p>
+
+<div style="position: absolute; top: 100px; left: 10vw; width: 80vw; height: 400px; background: blue; opacity: 0.7;">
+
+<dialog>
+ <form method="dialog">
+ <input type="submit" />
+ </form>
+ <p>
+ Help, I'm inside an extra stacking context D:
+ </p>
+</dialog>
+
+</div>
+
+<button id="show">Show Dialog</button>
+
+<script>
+var dialog = document.querySelector('dialog');
+dialogPolyfill.registerDialog(dialog);
+dialog.showModal();
+
+show.addEventListener('click', function() {
+ dialog.showModal();
+});
+
+</script>
+</body>
+</html>