brut-js 0.14.0 → 0.15.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/specs/Toast.spec.js +34 -0
- package/specs/public/js/bundle.js +59 -4
- package/specs/public/js/bundle.js.map +3 -3
- package/src/I18nTranslation.js +3 -0
- package/src/Message.js +9 -3
- package/src/RichString.js +4 -1
- package/src/Toast.js +102 -0
- package/src/index.js +3 -0
package/package.json
CHANGED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { withHTML } from "./SpecHelper.js"
|
|
2
|
+
|
|
3
|
+
describe("<brut-toast>", () => {
|
|
4
|
+
withHTML(`
|
|
5
|
+
<div>
|
|
6
|
+
<brut-i18n-translation key="toast.saved" value="Save successful"></brut-i18n-translation>
|
|
7
|
+
<brut-toast show-warnings>
|
|
8
|
+
<div>
|
|
9
|
+
<output><span>Message here</span></output>
|
|
10
|
+
<button>Close</button>
|
|
11
|
+
</div>
|
|
12
|
+
<button>Close</button>
|
|
13
|
+
</brut-toast>
|
|
14
|
+
</div>
|
|
15
|
+
`).test("setting the key shows the message, then it's removed when closed", ({window,document,assert}) => {
|
|
16
|
+
|
|
17
|
+
const toast = document.querySelector("brut-toast")
|
|
18
|
+
assert(toast, "brut-toast element should be present")
|
|
19
|
+
|
|
20
|
+
toast.setAttribute("key", "toast.saved")
|
|
21
|
+
const output = toast.querySelector("output")
|
|
22
|
+
assert.equal(output.textContent.trim(), "Save successful")
|
|
23
|
+
const message = output.querySelector("brut-message")
|
|
24
|
+
assert(message, "brut-message should be present")
|
|
25
|
+
console.log(message.outerHTML)
|
|
26
|
+
assert.equal(message.getAttribute("role"), "status")
|
|
27
|
+
assert.equal(message.getAttribute("aria-live"), "polite")
|
|
28
|
+
assert.equal(message.getAttribute("aria-atomic"), "true")
|
|
29
|
+
|
|
30
|
+
const button = toast.querySelector("button")
|
|
31
|
+
button.click()
|
|
32
|
+
assert.equal(toast.getAttribute("key"), null)
|
|
33
|
+
})
|
|
34
|
+
})
|
|
@@ -80,10 +80,13 @@
|
|
|
80
80
|
*
|
|
81
81
|
* @param {null|undefined|String|RichString} possiblyDefinedStringOrRichString - if `null`, `undefined`, or otherwise falsey, this method returns `null`. If a String, returns a new `RichString` wrapping it. If a `RichString`, returns the `RichString` unchanged.
|
|
82
82
|
*/
|
|
83
|
-
static fromString(possiblyDefinedStringOrRichString) {
|
|
83
|
+
static fromString(possiblyDefinedStringOrRichString, { allowBlank = false } = {}) {
|
|
84
84
|
if (possiblyDefinedStringOrRichString instanceof _RichString) {
|
|
85
85
|
return possiblyDefinedStringOrRichString;
|
|
86
86
|
}
|
|
87
|
+
if (allowBlank && possiblyDefinedStringOrRichString === "") {
|
|
88
|
+
return new _RichString("");
|
|
89
|
+
}
|
|
87
90
|
if (!possiblyDefinedStringOrRichString) {
|
|
88
91
|
return null;
|
|
89
92
|
}
|
|
@@ -349,6 +352,9 @@
|
|
|
349
352
|
* }
|
|
350
353
|
*/
|
|
351
354
|
translation(interpolatedValues) {
|
|
355
|
+
if (!this.#value) {
|
|
356
|
+
this.logger.warn("No value attribute for key '%s', so translation will be blank", this.#key);
|
|
357
|
+
}
|
|
352
358
|
return this.#value.replaceAll(/%\{([^}%]+)\}/g, (match, key) => {
|
|
353
359
|
if (interpolatedValues[key]) {
|
|
354
360
|
return interpolatedValues[key];
|
|
@@ -1291,10 +1297,16 @@
|
|
|
1291
1297
|
"show-warnings",
|
|
1292
1298
|
"key"
|
|
1293
1299
|
];
|
|
1300
|
+
/*
|
|
1301
|
+
* Creates a new `<brut-message>` element with the given attributes.
|
|
1302
|
+
*/
|
|
1294
1303
|
static createElement(document2, attributes) {
|
|
1295
1304
|
const element = document2.createElement(_Message.tagName);
|
|
1296
|
-
|
|
1297
|
-
|
|
1305
|
+
Object.entries(attributes).forEach(([name, value]) => {
|
|
1306
|
+
if (value !== null && value !== void 0) {
|
|
1307
|
+
element.setAttribute(name, value);
|
|
1308
|
+
}
|
|
1309
|
+
});
|
|
1298
1310
|
return element;
|
|
1299
1311
|
}
|
|
1300
1312
|
#key = null;
|
|
@@ -1312,7 +1324,7 @@
|
|
|
1312
1324
|
this.logger.info("Could not find translation based on selector '%s'", selector);
|
|
1313
1325
|
return;
|
|
1314
1326
|
}
|
|
1315
|
-
this.textContent = RichString_default.fromString(translation.translation()).capitalize().toString();
|
|
1327
|
+
this.textContent = RichString_default.fromString(translation.translation(), { allowBlank: true }).capitalize().toString();
|
|
1316
1328
|
}
|
|
1317
1329
|
};
|
|
1318
1330
|
var Message_default = Message;
|
|
@@ -1402,6 +1414,48 @@
|
|
|
1402
1414
|
};
|
|
1403
1415
|
var Tabs_default = Tabs;
|
|
1404
1416
|
|
|
1417
|
+
// src/Toast.js
|
|
1418
|
+
var Toast = class extends BaseCustomElement_default {
|
|
1419
|
+
static tagName = "brut-toast";
|
|
1420
|
+
static observedAttributes = [
|
|
1421
|
+
"show-warnings",
|
|
1422
|
+
"key"
|
|
1423
|
+
];
|
|
1424
|
+
#key = null;
|
|
1425
|
+
#closeListener = (event) => {
|
|
1426
|
+
event.preventDefault();
|
|
1427
|
+
this.removeAttribute("key");
|
|
1428
|
+
};
|
|
1429
|
+
keyChangedCallback({ newValue }) {
|
|
1430
|
+
this.#key = RichString_default.fromString(newValue);
|
|
1431
|
+
}
|
|
1432
|
+
update() {
|
|
1433
|
+
const closeButton = this.querySelector("button");
|
|
1434
|
+
if (closeButton) {
|
|
1435
|
+
closeButton.addEventListener("click", this.#closeListener);
|
|
1436
|
+
}
|
|
1437
|
+
if (!this.#key) {
|
|
1438
|
+
return;
|
|
1439
|
+
}
|
|
1440
|
+
const output = this.querySelector("output");
|
|
1441
|
+
if (!output) {
|
|
1442
|
+
this.logger.warn("No <output> element found, so toast will not be displayed");
|
|
1443
|
+
return;
|
|
1444
|
+
}
|
|
1445
|
+
const messageNode = Message_default.createElement(document, {
|
|
1446
|
+
"key": this.#key,
|
|
1447
|
+
"role": "status",
|
|
1448
|
+
"aria-live": "polite",
|
|
1449
|
+
"aria-atomic": "true"
|
|
1450
|
+
});
|
|
1451
|
+
output.replaceChildren(messageNode);
|
|
1452
|
+
this.style.animation = "none";
|
|
1453
|
+
this.offsetWidth;
|
|
1454
|
+
this.style.animation = "";
|
|
1455
|
+
}
|
|
1456
|
+
};
|
|
1457
|
+
var Toast_default = Toast;
|
|
1458
|
+
|
|
1405
1459
|
// src/Tracing.js
|
|
1406
1460
|
var Tracing = class extends BaseCustomElement_default {
|
|
1407
1461
|
static tagName = "brut-tracing";
|
|
@@ -1595,6 +1649,7 @@
|
|
|
1595
1649
|
AjaxSubmit_default,
|
|
1596
1650
|
ConstraintViolationMessage_default,
|
|
1597
1651
|
Tabs_default,
|
|
1652
|
+
Toast_default,
|
|
1598
1653
|
LocaleDetection_default,
|
|
1599
1654
|
Autosubmit_default,
|
|
1600
1655
|
Tracing_default
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
|
-
"sources": ["../../../src/Logger.js", "../../../src/RichString.js", "../../../src/BaseCustomElement.js", "../../../src/I18nTranslation.js", "../../../src/ConstraintViolationMessage.js", "../../../src/ConstraintViolationMessages.js", "../../../src/AjaxSubmit.js", "../../../src/Autosubmit.js", "../../../src/ConfirmationDialog.js", "../../../src/ConfirmSubmit.js", "../../../src/CopyToClipboard.js", "../../../src/Form.js", "../../../src/LocaleDetection.js", "../../../src/Message.js", "../../../src/Tabs.js", "../../../src/Tracing.js", "../../../src/index.js", "../../../src/appForTestingOnly.js"],
|
|
4
|
-
"sourcesContent": ["/**\n * Abstract interface for logging information from a component.\n * This is intended to allow prefixed messages to be optionally shown\n * in the console to help debug.\n *\n * @see BufferedLogger\n * @see PrefixedLogger\n * @see BaseCustomElement#logger\n */\nclass Logger {\n /** Create a logger for the given prefix.\n *\n * @param {string|false} stringOrFalse - if false,returns a {@link BufferedLogger}. Otherwise, returns a {@link PrefixedLogger} using the param's value as the prefix.\n *\n * @returns {Logger}\n */\n static forPrefix(stringOrFalse) {\n if (!stringOrFalse) {\n return new BufferedLogger()\n }\n else {\n return new PrefixedLogger(stringOrFalse)\n }\n }\n\n /** Subclasses must implement this.\n *\n * @param {string} level - 'info' or 'warn' to indicate the logging level\n * @param {...*} args - args to pass directly to console.log\n */\n log() {\n throw `Subclass must implement`\n }\n\n /** Log an informational bit of information */\n info(...args) { this.log(\"info\",...args) }\n /** Log a warning */\n warn(...args) { this.log(\"warn\",...args) }\n}\n\n/** Logger that buffers, but does not print, its logged messages.\n * The reason it buffers them is to allow custom elements to retroatively log\n * information captured before warnings were turned on.\n */\nclass BufferedLogger extends Logger {\n constructor() {\n super()\n this.messages = []\n }\n log(...args) {\n this.messages.push(args)\n }\n}\n\n/** Log information to the JavaScript console.\n*/\nclass PrefixedLogger extends Logger {\n /** Create a PrefixedLogger.\n *\n * @param {string|true} prefixOrTrue - if true, uses the prefix `\"debug\"`, otherwise uses the param as the prefix to all\n * messages output.\n */\n constructor(prefixOrTrue) {\n super()\n this.prefix = prefixOrTrue === true ? \"debug\" : prefixOrTrue\n }\n\n /** Dumps hte contents of a {@link BufferedLogger} to this logger's output.\n *\n * @param {BufferedLogger} bufferedLogger - a logger with pent-up messages, waiting to be logged.\n */\n dump(bufferedLogger) {\n if (bufferedLogger instanceof BufferedLogger) {\n bufferedLogger.messages.forEach( (args) => {\n this.log(...args)\n })\n }\n }\n\n log(level,...args) {\n if (typeof(args[0]) === \"string\") {\n const message = `[prefix:${this.prefix}]:${args[0]}`\n console[level](message,...(args.slice(1)))\n }\n else {\n console[level](this.prefix,...args)\n }\n }\n}\nexport default Logger\n", "/** A wrapper around a string that provides useful utility functions\n * not present in the standard library.\n *\n * @example\n *\n * const string = RichString.fromString(element.textContent)\n * element.textContent = string.humanize().toString()\n */\nclass RichString {\n /** Prefer this over the constructor, as this will\n * wrap `possiblyDefinedStringOrRichString` only if necessary\n * as well as handle `null`.\n *\n * @param {null|undefined|String|RichString} possiblyDefinedStringOrRichString - if `null`, `undefined`, or otherwise falsey, this method returns `null`. If a String, returns a new `RichString` wrapping it. If a `RichString`, returns the `RichString` unchanged.\n */\n static fromString(possiblyDefinedStringOrRichString) {\n if (possiblyDefinedStringOrRichString instanceof RichString) {\n return possiblyDefinedStringOrRichString\n }\n if (!possiblyDefinedStringOrRichString) {\n return null\n }\n return new RichString(String(possiblyDefinedStringOrRichString))\n }\n\n /** Prefer `fromString` */\n constructor(string) {\n if (typeof string !== \"string\") {\n throw `You may only construct a RichString with a String, not a ${typeof string}`\n }\n this.string = string\n }\n\n /** Returns a `RichString` with the string capitalized. */\n capitalize() {\n return new RichString(this.string.charAt(0).toUpperCase() + this.string.slice(1))\n }\n\n /** Returns a `RichString` with the string un-capitalized. */\n decapitalize() {\n return new RichString(this.string.charAt(0).toLowerCase() + this.string.slice(1))\n }\n\n /** Returns a `RichString` with the string converted from snake or kebab case into camel case. */\n camelize() {\n // Taken from camelize npm module\n return RichString.fromString(this.string.replace(/[_.-](\\w|$)/g, function (_, x) {\n return x.toUpperCase()\n }))\n }\n\n /** Returns a 'humanized' `RichString`, which is basically a de-camelized version with the first letter\n * capitalized.\n */\n humanize() {\n return this.decamlize({spacer: \" \"}).capitalize()\n }\n\n /** Returns a `RichString` with the string converted from camel case to snake or kebab case.\n *\n * @param {Object} parameters\n * @param {string} parameters.spacer [\"_\"] - a string to use when joining words together.\n *\n */\n decamlize({spacer=\"_\"} = {}) {\n // Taken from decamelize NPM module\n\n // Checking the second character is done later on. Therefore process shorter strings here.\n if (this.string.length < 2) {\n return new RichString(this.string.toLowerCase())\n }\n\n const replacement = `$1${spacer}$2`\n\n // Split lowercase sequences followed by uppercase character.\n // `dataForUSACounties` \u2192 `data_For_USACounties`\n // `myURLstring \u2192 `my_URLstring`\n const decamelized = this.string.replace(\n /([\\p{Lowercase_Letter}\\d])(\\p{Uppercase_Letter})/gu,\n replacement,\n )\n\n // Split multiple uppercase characters followed by one or more lowercase characters.\n // `my_URLstring` \u2192 `my_ur_lstring`\n return new RichString(decamelized.\n replace(\n /(\\p{Uppercase_Letter})(\\p{Uppercase_Letter}\\p{Lowercase_Letter}+)/gu,\n replacement,\n ).\n toLowerCase()\n )\n }\n\n /** Return the underlying String value */\n toString() { return this.string }\n\n /** Return the underlying String value or null if the string is blank */\n toStringOrNull() {\n if (this.isBlank()) {\n return null\n }\n else {\n return this.string\n }\n }\n\n /* Returns true if this string has only whitespace in it */\n isBlank() {\n return this.string.trim() == \"\"\n }\n\n}\nexport default RichString\n", "import Logger from \"./Logger\"\nimport RichString from \"./RichString\"\n\n/** Base class for Custom Elements that provides a few quality-of-life enhancements.\n * \n * Custom elements that use this base class instead of `HTMLElement` get the following features:\n *\n * * `connectedCallback` and `attributeChangedCallback` call into a central `update` method where the \n * class can centralilize its logic\n * * Instead of implementing `attributeChangedCallback` and checking the name, per-attribute\n * callbacks can be implemented that are called when an observed attribute is changed. See {@link\n * attributeChangedCallback}.\n * * Support for defining the element by declaring a tag name\n * * Opt-in debugging support to allow verbose logging of mistaken use of the element that can be turned\n * off for production use.\n *\n * How to use this class:\n *\n * 1. Your custom element should extend this class via `extends BaseCustomElement`\n * 2. Create a static property called `tagName` that will be your element's tag name. Remember that all tag names must have a dash in them.\n * 3. Create a static property called `observedAttributes` that is an array of attribute names your element supports. This is part of the HTML spec and not specific to this base class.\n * 4. If you include the attribute `show-warnings` in your list of `observedAttributes`, you will have enhanced debugging abilities.\n * 5. For each attribute *other* than `show-warnings`, implement a callback to receive notifications on the attribute's changes. See {@link attributeChangedCallback} for more info.\n * 6. Implement `update` to execute whatever logic the component needs. `update` will be called multiple times and thus should be relatively idempotent. Specifically, it will be called after any attribute has changed, and it will be called as part of the standard `connectedCallback`.\n * 7. To use your component, call the static {@link define} method.\n *\n * Debugging\n *\n * Custom Elements have to work under a variety of degenerate cirucmstances. Further, if you are building \n * elements that wrap and enhance conventional elements, it can be easy to make a mistake, for example intending\n * to wrap a `FORM`, but wrapping only an `INPUT`.\n *\n * To help debug these situations, you are encouraged to use `this.logger.warn(...)` to emit warnings when\n * potentially incorrect use of your component is detected. By default, these warnings will not be shown. This\n * provides your users with a drama-free console. During development, however, you can add the `show-warnings`\n * attribute to your element. If that is set, warnings *are* shown in the console.\n *\n * `show-warnings` can be given a value, in which case that value if used to prefix all warnings the element emits.\n * This can be useful to know which use of an element is causing problems. If you don't give any value\n * to `show-warnings`, the element's `id` will be used as the prefix. If the element has no `id`, you will\n * still see warnings, but without a prefix. This could make it hard to know where the warnings are coming from.\n *\n * @example\n * // Replaces all span elements inside the component with\n * // an upper-cased value of the attribute 'some-attribute'\n * class MyComponent extends BaseCustomElement {\n * static tagName = \"my-component\"\n * static observedAttributes = [\n * \"show-warnings\",\n * \"some-attribute\",\n * ]\n *\n * someAttributeChangedCallback({newValue}) {\n * this.someAttribute = newValue ? newValue.toUpperCase() : null\n * }\n *\n * update() {\n * const spans = this.querySelectorAll(\"span\")\n * if (spans.length == 0) {\n * this.logger.warn(\"Did not find any <span> elements - element won't do anything\")\n * }\n * spans.forEach( (element) => {\n * element.textContent = this.someAttribute\n * })\n * }\n * }\n * docment.addEventListener(\"DOMContentLoaded\", () => {\n * MyComponent.define()\n * })\n *\n * // Then, in your HTML\n * <my-component some-attribute=\"hello there\">\n * <span></span>\n * <div></div>\n * <span></span>\n * </my-component>\n *\n * // The browser will effectively produce this HTML:\n * <my-component some-attribute=\"hello there\">\n * <span>HELLO THERE</span>\n * <div></div>\n * <span>HELLO THERE</span>\n * </my-component>\n *\n * // If JavaScript (or browser dev tools) changed some-attribute\n * // to be \"goodbye then\", the markup will change to look like so:\n * <my-component some-attribute=\"goodby then\">\n * <span>GOODBYE THEN</span>\n * <div></div>\n * <span>GOODBYE THEN</span>\n * </my-component>\n */\nclass BaseCustomElement extends HTMLElement {\n\n /** A {@link Logger} you can use to write warning messages. By default, these\n * messages are not shown in the console. If you put `show-warnings` as an attribute on your\n * element, warnings sent to this logger *are* shown.\n */\n logger = Logger.forPrefix(null)\n\n #_connectedCallbackCalled = false\n #_disconnectedCallbackCalled = false\n\n constructor() {\n super()\n this.logger = Logger.forPrefix(null)\n }\n\n /** You must call this to define the custom element. This is bascially\n * a wrapper around `customElements.define`. It is recommended that you call \n * this inside a `DOMContentLoaded` event, or after the page's HTML has been processed.\n * \n * @see external:CustomElementRegistry\n */\n static define() {\n if (!this.tagName) {\n throw `To use BaseCustomElement, you must define the static member tagName to return your custom tag's name`\n }\n customElements.define(this.tagName, this)\n }\n\n showWarningsChangedCallback({oldValue,newValue}) {\n let oldLogger\n if (!oldValue && newValue) {\n oldLogger = this.logger\n }\n let prefix = newValue == \"\" ? this.id : newValue\n if (!prefix) {\n prefix = \"UNKNOWN COMPONENT\"\n }\n this.logger = Logger.forPrefix(prefix)\n if (oldLogger) {\n this.logger.dump(oldLogger)\n }\n }\n\n /**\n * Overrides the standard callback to allow subclasses to have a slightly easier API when responding\n * to attribute changes. You can override this to use the custom element callback directly. Note that if\n * you do, `show-warnings` will not have any affect and you probably don't need to bother using\n * this class as your base class.\n *\n * This method will locate a per-attribute method and call that.\n * Attribute names are assumed to be in kebab-case and are translated to camelCase to create a method name.\n * That method is `\u00ABattributeInCamelCase\u00BBChangedCallback`, so if your attribute is `hex-code`,\n * a method named `hexCodeChangedCallback` in invoked. If no such method is defined, a\n * warning is logged in the console, regardless of the `show-warnings` attribute.\n *\n * The method is invoked with `{oldValue,newValue,newValueAsBoolean}` - i.e. an object and not positional parameters. This \n * means your implementation can omit any parameters it doesn't care about. `newValueAsBoolean` is not part of\n * the custom element spec, but is provided as an unambiguous way to know if a boolean attribute was set or not. This is\n * because if the value is set, it is likely to be the empty string, which is considered false by JavaScript. Cool.\n *\n * The return value of the method is ignored.\n *\n * After your method is called, if there is a method named `update`, it is called with no arguments.\n *\n * What this allows you to do is separate how you manage your element's attributes from how your logic\n * is managed. For complex elements that take a lot of attributes, this can simplify your element's code without straying too far from the spec.\n *\n * @example\n *\n * // If your element accepts the attribute `warning-message` that will be trimmed of whitespace\n * // then placed into all `H1` tags inside the element, you can manage that like so:\n * class MyElement extends BaseCustomElement {\n * static tagName = \"my-element\"\n * static observedAttributes = [\n * \"warning-message\",\n * ]\n *\n * // called by attributeChangedCallback when warning-message's value changes\n * warningMessageChangedCallback({newValue}) {\n * this.warningMessage = (newValue || \"\").trim()\n * }\n *\n * // called after attributeChangedCallback calls warningMessageChangedCallback\n * update() {\n * this.querySelectorAll(\"h1\").forEach( (e) => e.textContent = this.warningMessage )\n * }\n * }\n *\n *\n * @see {@link https://developer.mozilla.org/en-US/docs/Web/API/Web_components/Using_custom_elements}\n *\n */\n attributeChangedCallback(name,oldValue,newValue) {\n const callbackName = `${new RichString(name).camelize()}ChangedCallback`\n if (this[callbackName]) {\n const newValueAsBoolean = newValue !== null\n this[callbackName]({oldValue,newValue,newValueAsBoolean})\n }\n else if (this.constructor.observedAttributes.indexOf(name) != -1) {\n console.warn(\"Observing %s but no method named %s was found to handle it\",name,callbackName)\n }\n this.__update()\n }\n \n /** Overrides the custom element callback to set internal flags allowing you to know if your\n * element has been disconnected. When an element is disconnected, `update` is not called.\n *\n * If you want to add your own logic during disconnection, override {@link onDisconnected}.\n *\n * @see {@link https://developer.mozilla.org/en-US/docs/Web/API/Web_components/Using_custom_elements}\n */\n disconnectedCallback() {\n this.#_disconnectedCallbackCalled = true\n this.onDisconnected()\n }\n\n /** Override this to add logic when `disconnectedCallback` is called by the browser. This will\n * not be called if you overrode `disconnectedCallback`.\n */\n onDisconnected() {}\n\n /** Overrides the custom element callback to set internal flags allowing you to know if your\n * element has been connected. `update` is still called for elements that have not yet connected, however\n * in practice your element will be connected before any codepath that calls `update` is called.\n *\n * To add logic when your element is connected, override {@link onConnected}\n *\n * @see {@link https://developer.mozilla.org/en-US/docs/Web/API/Web_components/Using_custom_elements}\n * @see BaseCustomElement#connectedCallbackCalled\n */\n connectedCallback() {\n this.#_connectedCallbackCalled = true\n this.onConnected()\n this.__update()\n }\n\n /** Override this to add logic when `connectedCallback` is called by the browser. This will\n * not be called if you overrode `connectedCallback`\n */\n onConnected() {}\n\n /** Returns true if this element is connected and the connected callback has been called.\n * This is different from `Node#isConnected`, which can return true before `connectedCallback` is called.\n */\n get connectedCallbackCalled() { return !!this.#_connectedCallbackCalled }\n\n /** Override this to perform whatever logic your element must perform.\n * Because changes to your element's attributes can happen at any time and in any order,\n * you will want to consolidate all logic into one method\u2014this one. You will also\n * want to make sure that this method is idempotent and fault-tolerant. It will be called multiple times.\n *\n * It is called by {@link BaseCustomElement#attributeChangedCallback|attributeChangedCallback} and {@link BaseCustomElement#connectedCallback|connectedCallback}, however\n * it will *not* be called after the elment has been disconnected.\n *\n * That means that any event listeners, rendering, content manipulation, or other behavior should happen hear\n * and it *must* be idempotent. In particular, any event listeners you attach must be done with care. Using\n * anonymous functions could result in duplicate listeners.\n */\n update() {}\n\n __update() {\n if (this.#_disconnectedCallbackCalled) {\n return\n }\n this.update()\n }\n}\nexport default BaseCustomElement\n", "import BaseCustomElement from \"./BaseCustomElement\"\n\n/** Manages a translation based on a key and value, with the value potentially having interpolation.\n *\n * This is intended to be server-rendered with the subset of keys the server manages that are relevant\n * to the front-end. Any other code on the page can then locate an element with the desired key and\n * call `translation` to get the human-readable key. It is assumed that the server would render\n * in the language required by the visitor, so there is no need to first select by locale.\n *\n * @property {string} key - an i18n key, presumably dot-delimited, however it can be any valid attribute value.\n * @property {string} value - the value of the key, in the browser's locale. It may contain placeholders for interpolation using `%{\u00ABplaceholder\u00BB}` syntax.\n *\n * @example\n * <brut-i18n-translation key=\"greeting\" value=\"Hello %{username}\"></brut-i18n-translation>\n *\n * @customElement brut-i18n-translation\n */\nclass I18nTranslation extends BaseCustomElement {\n static tagName = \"brut-i18n-translation\"\n\n static observedAttributes = [\n \"show-warnings\",\n \"key\",\n \"value\",\n ]\n\n #key = null\n #value = \"\"\n\n keyChangedCallback({newValue}) {\n this.#key = newValue\n }\n\n valueChangedCallback({newValue}) {\n this.#value = newValue ? String(newValue) : \"\"\n }\n\n /**\n * Called by other JavaScript to get the translated string.\n * @param {Object} interpolatedValues - Object where the keys are placeholders in the string for interpolation and the values are\n * the values to replace. Placeholders not in the translated value are ignored. Missing placeholders won't cause an error, but the\n * placeholder will be present verbatim in the translated string.\n *\n * @example\n * const element = document.querySeletor(\"brut-i18n-translation[key='greeting']\")\n * if (element) {\n * const translation = element.translation({ username: \"Pat\" })\n * alert(translation) // Shows 'Hello Pat'\n * }\n */\n translation(interpolatedValues) {\n return this.#value.replaceAll(/%\\{([^}%]+)\\}/g, (match,key) => {\n if (interpolatedValues[key]) {\n return interpolatedValues[key]\n }\n return match\n })\n }\n\n}\nexport default I18nTranslation\n", "import BaseCustomElement from \"./BaseCustomElement\"\nimport RichString from \"./RichString\"\nimport I18nTranslation from \"./I18nTranslation\"\n\n/** Like {@link Message} but specific to constraint violations of input fields. This accepts the name\n * of an input field via `input-name`, which can be used to locate the field's localized name.\n *\n * Here is how the field's name is determined:\n *\n * 1. It will look for a `<brut-i18n-translation>` element with the `key` `cv.cs.fieldNames.\u00ABinput-name\u00BB`.\n * 2. If that's not found, it will attempt to use \"this field\" by locating a `<brut-i18n-translation>` element with the `key`\n * `cv.this_field` (the underscore being what is used on Brut's server side).\n * 3. If that is not found, it will use the literaly string \"this field\" and emit a console warning.\n *\n * @property {string} key - the i18n translation key to use. It must map to the `key` of a `<brut-i18n-translation>` on the page or\n * the element will not render any text.\n * @property {string} input-name - the name of the input, used to insert into the message, e.g. \"Title is required\".\n * @property {boolean} server-generated if true, this indicates the element's HTML was generated on the server.\n * This means that your CSS can target it for display in all cases. If this is not present,\n * you may want to avoid showing this element if the form has not been submitted yet.\n * Does not affect behavior.\n * @property {boolean} server-side if true, this indicates the element contains constraint violation messages\n * from the server. Does not affect behavior.\n * @property {boolean} client-side if true, this indicates the element contains constraint violation messages\n * from the client, however they may have been generated from the server, since the server may\n * re-evaluate the client-side constraints. Does not affect behavior of this tag.\n *\n * @see I18nTranslation\n * @see ConstraintViolationMessages\n * @see Message\n *\n * @customElement brut-cv\n */\nclass ConstraintViolationMessage extends BaseCustomElement {\n static tagName = \"brut-cv\"\n\n static observedAttributes = [\n \"show-warnings\",\n \"key\",\n \"input-name\",\n \"server-side\",\n \"client-side\",\n \"server-generated\",\n ]\n\n static createElement(document,attributes) {\n const element = document.createElement(ConstraintViolationMessage.tagName)\n element.setAttribute(\"key\",this.i18nKey(\"cs\", attributes.key))\n element.setAttribute(\"input-name\",attributes[\"input-name\"])\n element.setAttribute(\"client-side\",\"\")\n if (Object.hasOwn(attributes,\"show-warnings\")) {\n element.setAttribute(\"show-warnings\",attributes[\"show-warnings\"])\n }\n return element\n }\n\n static markServerSide(element) {\n if (element.tagName.toLowerCase() == this.tagName) {\n element.setAttribute(\"server-side\", true)\n }\n }\n\n static clientSideSelector() {\n return `${this.tagName}:not([server-side])`\n }\n\n static serverSideSelector() {\n return `${this.tagName}[server-side]`\n }\n\n /** Returns the I18N key used for front-end constraint violations. This is useful\n * if you need to construct a key and want to follow Brut's conventions on how they\n * are managed.\n *\n * @param {...String} keyPath - parts of the path of the key after the namespace that Brut manages.\n */\n static i18nKey(...keyPath) {\n const path = [ \"cv\" ]\n return path.concat(keyPath).join(\".\")\n }\n\n #key = null\n #inputNameKey = null\n #thisFieldKey = this.#i18nKey(\"this_field\")\n\n keyChangedCallback({newValue}) {\n this.#key = newValue\n }\n\n inputNameChangedCallback({newValue}) {\n this.#inputNameKey = this.#i18nKey(\"cs\", \"fieldNames\", newValue)\n }\n\n serverSideChangedCallback({newValueAsBoolean}) {\n // attribute listed for documentation purposes only\n }\n clientSideChangedCallback({newValueAsBoolean}) {\n // attribute listed for documentation purposes only\n }\n serverGeneratedChangedCallback({newValueAsBoolean}) {\n // attribute listed for documentation purposes only\n }\n\n update() {\n if (!this.#key) {\n this.logger.info(\"No key attribute, so can't do anything\")\n return\n }\n\n const selector = `${I18nTranslation.tagName}[key='${this.#key}']`\n const translation = document.querySelector(selector)\n if (!translation) {\n this.logger.info(\"Could not find translation based on selector '%s'\",selector)\n return\n }\n\n const fieldNameSelector = `${I18nTranslation.tagName}[key='${this.#inputNameKey}']`\n const thisFieldSelector = `${I18nTranslation.tagName}[key='${this.#thisFieldKey}']`\n\n let fieldNameTranslation = document.querySelector(fieldNameSelector)\n if (!fieldNameTranslation) {\n this.logger.info(\"Could not find translation for input/field name based on selector '%s'. Will try 'this field' fallback\",fieldNameSelector)\n fieldNameTranslation = document.querySelector(thisFieldSelector)\n if (!fieldNameTranslation) {\n this.logger.info(\"Could not find translation for 'this field' fallback key, based on selector '%s'\",thisFieldSelector)\n }\n }\n\n const fieldName = fieldNameTranslation ? fieldNameTranslation.translation() : \"this field\"\n this.textContent = RichString.fromString(translation.translation({ field: fieldName })).capitalize().toString()\n }\n\n /** Helper that calls the static version */\n #i18nKey(...keyPath) {\n return this.constructor.i18nKey(...keyPath)\n }\n\n\n}\nexport default ConstraintViolationMessage\n", "import BaseCustomElement from \"./BaseCustomElement\"\nimport RichString from \"./RichString\"\nimport ConstraintViolationMessage from \"./ConstraintViolationMessage\"\n\n/**\n * Custom element to translate keys from {@link external:ValidityState} into \n * actual messges for a human. This works by inserting `<brut-cv>` elements\n * as children, where the key represents the particular errors present in the `ValidityState` passed\n * to `createMessages`. Note that this will not insert an element for `customError`, since there can't be\n * any sort of general error message to correspond to that.\n *\n * @property {string} input-name if set, this indicates this element contains constraint violation messages\n * for the input with this name inside the form this element is in. Currently doesn't affect\n * this element's behavior, however AjaxSubmit will use it to locate where it \n * should insert server-side errors.\n *\n * @see Form\n * @see ConstraintViolationMessage\n * @see AjaxSubmit\n *\n * @customElement brut-cv-messages\n */\nclass ConstraintViolationMessages extends BaseCustomElement {\n static tagName = \"brut-cv-messages\"\n\n static observedAttributes = [\n \"show-warnings\",\n \"input-name\",\n ]\n\n inputNameChangedCallback({newValue}) {\n // attribute listed for documentation purposes only\n }\n\n /** \n * Creates error messages based on the passed `ValidityState` and input name.\n *\n * This should be called as part of a Form validation event to provide a customized UX for\n * the error messages, beyond what the browser would do by default. The keys used are the same\n * as the attributes of a `ValidityState`, so for example, a range underflow would mean that `validity.rangeUnderflow` would return\n * true. Thus, a `<brut-cv>` would be created with `key=\"cv.cs.rangeUnderflow\"`.\n *\n * The `cv.cs` is hard-coded to be consistent with Brut's server-side translation management.\n *\n * @param {ValidityState} validityState - the return from an element's `validity` when it's found to have constraint violations.\n * @param {String} inputName - the element's `name`.\n */\n createMessages({validityState,inputName}) {\n const errors = this.#VALIDITY_STATE_ATTRIBUTES.filter( (attribute) => validityState[attribute] )\n this.clearClientSideMessages()\n errors.forEach( (key) => {\n const options = {\n key: key,\n \"input-name\": inputName,\n }\n const showWarnings = this.getAttribute(\"show-warnings\")\n if (showWarnings != null) {\n options[\"show-warnings\"] = showWarnings\n }\n const element = ConstraintViolationMessage.createElement(document,options)\n this.appendChild(element)\n })\n }\n\n /**\n * Clear any client-side messages previously inserted by another element.\n * This is useful to remove potentially out-of-date messages to replace with up-to-date ones.\n */\n clearClientSideMessages() {\n this.querySelectorAll(ConstraintViolationMessage.clientSideSelector()).forEach( (element) => {\n this.removeChild(element)\n })\n }\n\n /**\n * Clear any server-side messages previously inserted by another element or rendered from the server.\n * This is useful to remove potentially out-of-date messages to replace with up-to-date ones.\n */\n clearServerSideMessages() {\n this.querySelectorAll(ConstraintViolationMessage.serverSideSelector()).forEach( (element) => {\n this.removeChild(element)\n })\n }\n\n #VALIDITY_STATE_ATTRIBUTES = [\n \"badInput\",\n \"patternMismatch\",\n \"rangeOverflow\",\n \"rangeUnderflow\",\n \"stepMismatch\",\n \"tooLong\",\n \"tooShort\",\n \"typeMismatch\",\n \"valueMissing\",\n // customError omitted, since it makes no sense as a general error key to look up\n ]\n}\nexport default ConstraintViolationMessages\n", "import BaseCustomElement from \"./BaseCustomElement\"\nimport ConstraintViolationMessages from \"./ConstraintViolationMessages\"\nimport ConstraintViolationMessage from \"./ConstraintViolationMessage\"\n\n/** Wraps a `<BUTTON>` assumed to be inside a form to indicate that, when clicked, it should submit\n * the form it's a part of via AJAX. It accounts for network failures and timeouts.\n *\n * The general flow is as follows:\n *\n * 1. When the button is clicked, the form's validity is checked. If it's not valid, nothing happens.\n * 2. If the form is valid, this element will be given the `requesting` attribute.\n * 3. The request will be initiated, set to abort after `request-timeout` ms as well\n * as to abort on an externally-provided AbortSignal (see below).\n * The data submitted will be the contents of `new FormData(form)`, along with the \n * name/value of the button that was clicked, if it has a name and value.\n * 4. If the request returns OK:\n * - `requesting` will be removed and `submitted` will be added.\n * - `submitted` will be removed after `submitted-lifetime` ms.\n * - the `brut:submitok` event will be fired with the response text, **parsed as HTML**, as `event.detail`.\n * 5. If the request returned a 422:\n * - If you have set `no-server-side-error-parsing`, the results will be included in the \n * detail field of the `brut:submitinvalid` event.\n * - If you have NOT set `no-server-side-error-parsing`, the response is parsed as\n * errors to be inserted into the DOM. See below for how that works. In this case,\n * `brut:submitinvalid`'s detail bill be null.\n * 6. If the request returns not OK and not 422:\n * - if it has been `request-timeout` ms or more since the button was first clicked, the operation is aborted (see below).\n * - if it has been less than `request-timeout` ms and the HTTP status code was 5xx, the operation is retried.\n * - otherwise, the operation is aborted.\n * 7. If fetch throws an error, the operation is aborted.\n *\n * ## 422 Responses\n *\n * For a 422 response (where `no-server-side-error-parsing` is *not* set),\n * this element assumes the response is `text/html` and contains one or more `<brut-cv>`\n * elements. These elements will be inserted into the proper `<brut-cv-messages>` element, as follows:\n *\n * 1. The `input-name` is examined.\n * 2. A `<brut-cv-messages input-name=\"\u00ABinput-name\u00BB\">` is located\n * 3. The containing form is located\n * 4. The input element(s) are located inside that form, based on `input-name`.\n * 5. The `<brut-cv-messages>` are cleared of any element with attribute `server-side`\n * 6. The messages from the server are inserted, with the attribute `server-side` added if it's not there.\n * 7. The input is set as having a custom validity\n * 8. validity is reported\n * 9. The first input located is scrolled into view\n * 10. If the input is modified after this all happens, custom validity is cleared\n *\n * For the server you are contacting, this element has a few requirements:\n *\n * - If everything is OK/the operation did what it was intended to do:\n * - the server will respond with a 2xx\n * - the response body, if it contains anything, be `text/html` (this is provided in the event detail)\n * - If there are server-side constraint violations.\n * - the server will return 422\n * - the response body will be `text/html`\n * - the response body will contain one or more `<brut-cv>` elements\n *\n * ## Aborting the `fetch` Request\n *\n * By default, the call to `fetch` will be aborted after the value of `request-timeout` ms (default is 5,000).\n * When this happens, the **form is submitted through the browser** without Ajax. This is currently\n * not configurable.\n *\n * You *can* set an additional `AbortSignal` by setting the `abortSignal` property. When this is done, then\n * either a timeout or your custom abort will abort the request and **submit the form through the browser**.\n *\n * For your custom abort signal, you can prevent browser submission by providing a reason\n * with the value `AjaxSubmit.doNotSubmitThroughBrowser`. This can be useful when you are debouncing\n * requests. See the examples. You will also find it useful to set `log-request-errors` on the element\n * so you can see what is happening.\n *\n * @property {boolean} no-server-side-error-parsing - if set, the response body for a 422 will not be parsed and inserted into the DOM. Instead, the body will be part of the detail of the `brut:submitinvalid` event.\n * @property {number} request-timeout - number of ms that the entire operation is expected to complete within. Default is 5000\n * @property {number} submitted-lifetime - number of ms that \"submitted\" should remain on the element after the form has completed. Default is 2000\n * @property {boolean} requesting - boolean attribute that indicates the request has been made, but not yet returned. Don't set this yourself outside of development. It will be set and removed by this element.\n * @property {boolean} submitted - boolean attribute that indicates the form has been successfully submitted. Don't set this yourselr outside of develoment. It will be set and removed by this element.\n * @property {boolean} log-request-errors - if set, logging related to request error handling will appear in the console. It will also\n * cause any form submission to be delayed by 2s to allow you to read the console.\n *\n * @fires brut:submitok Fired when the AJAX request initated by this returns OK and all processing has completed. The detail will include the *parsed document* of the HTML returned in the response.\n * @fires brut:submitinvalid Fired when the AJAX request initated by this returns a 422 and all logic around managing the reponse has completed. The detail will be null unless `no-server-side-error-parsing` is set, in which case it will be the parsed document of the HTML returned in the response.\n *\n * @example <caption>Typical use</caption>\n * <form action=\"/widgets\" method=\"post\">\n * <input type=\"text\" name=\"name\">\n *\n * <brut-ajax-submit>\n * <button name=\"button\" value=\"save\">Save</button>\n * </brut-ajax-submit>\n * <brut-ajax-submit>\n * <button name=\"button\" value=\"analyze\">Analyze</button>\n * </brut-ajax-submit>\n * </form>\n * <!-- When \"Save\" is clicked, \"name\" will have the value from the text field,\n * and \"button\" will have the value \"save\".\n * When \"Analyze\" is clicked, \"name\" will have the value from the text\n * field, and \"button\" will have the value \"analyze\". -->\n *\n * @example <caption>Using a custom abort signal</caption>\n * const ajaxSubmit = document.querySelector(\"brut-ajax-submit\")\n * const controller = new AbortController()\n * ajaxSubmit.abortSignal = controller.signal\n * // later, when you want to abort the request\n * controller.abort(AjaxSubmit.doNotSubmitThroughBrowser)\n *\n * @customelement brut-ajax-submit\n */\nclass AjaxSubmit extends BaseCustomElement {\n static tagName = \"brut-ajax-submit\"\n static observedAttributes = [\n \"show-warnings\",\n \"requesting\",\n \"submitted\",\n \"submitted-lifetime\",\n \"request-timeout\",\n \"max-retry-attempts\",\n \"log-request-errors\",\n \"no-server-side-error-parsing\",\n ]\n\n /* Use this when calling abort() to indicate that the form \n * should be sub submitted through the browser.\n */\n static doNotSubmitThroughBrowser = \"doNotSubmitThroughBrowser\"\n\n #requestErrorLogger = () => {}\n #formSubmitDelay = 0\n #submittedLifetime = 2000\n #requestTimeout = 5000\n #maxRetryAttempts = 25\n #serverSideErrorParsing = true\n #abortSignal = null\n\n /* Set an additional abort signal to be used when\n * the form is submitted via Ajax. This allows you to \n * control the fetch request beyond the built-in timeout.\n *\n * @param {AbortSignal} value the AbortSignal to add to the fetch request.\n */\n set abortSignal(value) {\n this.#abortSignal = value\n }\n\n constructor() {\n super()\n this.domParser = new DOMParser()\n }\n\n submittedLifetimeChangedCallback({newValue}) {\n const newValueAsInt = parseInt(newValue)\n if (isNaN(newValueAsInt)) {\n throw `submitted-lifetime must be a number, not '${newValue}'`\n }\n this.#submittedLifetime = newValueAsInt\n }\n\n noServerSideErrorParsingChangedCallback({newValueAsBoolean}) {\n this.#serverSideErrorParsing = !newValueAsBoolean\n }\n\n maxRetryAttemptsChangedCallback({newValue}) {\n const num = parseInt(newValue)\n if (isNaN(num)) {\n this.logger.warn(`max-retry-attempts '${newValue}' is not a number. Using 1 as a fallback`)\n this.#maxRetryAttempts = 1\n }\n else {\n this.#maxRetryAttempts = num\n }\n }\n\n requestTimeoutChangedCallback({newValue}) {\n const newValueAsInt = parseInt(newValue)\n if (isNaN(newValueAsInt)) {\n throw `request-timeout must be a number, not '${newValue}'`\n }\n this.#requestTimeout = newValueAsInt\n }\n\n submittedChangedCallback({newValueAsBoolean}) {\n // no op\n }\n\n requestingChangedCallback({newValueAsBoolean}) {\n if (this.#button()) {\n if (newValueAsBoolean) {\n this.#button().setAttribute(\"disabled\",true)\n }\n else {\n this.#button().removeAttribute(\"disabled\",true)\n }\n }\n }\n\n logRequestErrorsChangedCallback({newValueAsBoolean}) {\n if (newValueAsBoolean) {\n this.#requestErrorLogger = console.warn\n this.#formSubmitDelay = 2000\n }\n else {\n this.#requestErrorLogger = () => {}\n this.#formSubmitDelay = 0\n }\n }\n\n update() {\n const button = this.#button()\n if (!button)\n {\n this.logger.info(\"Could not find a <button> to attach behavior to\")\n return\n }\n const form = button.form\n if (!form) {\n this.logger.info(\"%o did not have a form associated with it - cannot attach behavior\",button)\n return\n }\n button.form.addEventListener(\"submit\",this.#formSubmitted)\n }\n\n\n #formSubmitted = (event) => {\n const submitter = event.submitter\n if (submitter == this.#button()) {\n event.preventDefault()\n const now = Date.now()\n this.#submitForm(event.target, event.submitter, now, 0)\n }\n }\n\n #submitForm(form, submitter, firstSubmittedAt, numAttempts) {\n\n const headers = new Headers()\n headers.append(\"X-Requested-With\",\"XMLHttpRequest\")\n headers.append(\"Content-Type\",\"application/x-www-form-urlencoded\")\n\n const formData = new FormData(form)\n if (submitter && submitter.name) {\n formData.append(submitter.name, submitter.value)\n }\n const urlSearchParams = new URLSearchParams(formData)\n\n const signals = [\n AbortSignal.timeout(this.#requestTimeout),\n ]\n if (this.#abortSignal) {\n signals.push(this.#abortSignal)\n }\n const abortSignals = AbortSignal.any(signals)\n\n let url = form.action\n let body = null\n\n if (form.method.toLowerCase() == \"get\") {\n const sep = url.includes(\"?\") ? \"&\" : \"?\"\n url += sep + urlSearchParams.toString()\n }\n else {\n body = urlSearchParams\n }\n\n const request = new Request(\n url,\n {\n headers: headers,\n method: form.method,\n body: body,\n signal: abortSignals,\n }\n )\n\n\n if (numAttempts > this.#maxRetryAttempts) {\n this.#requestErrorLogger(\"%d attempts. Giving up\",numAttempts)\n this.#submitFormThroughBrowser(form)\n return\n }\n this.setAttribute(\"requesting\", true)\n fetch(request).then( (response) => {\n if (response.ok) {\n this.removeAttribute(\"requesting\")\n this.setAttribute(\"submitted\",true)\n\n setTimeout( () => this.removeAttribute(\"submitted\"), this.#submittedLifetime )\n response.text().then( (text) => {\n const parsedDocument = this.domParser.parseFromString(text,\"text/html\")\n this.dispatchEvent(new CustomEvent(\"brut:submitok\", { detail: parsedDocument }))\n })\n }\n else {\n\n let retry = false // if true, we retry the request via ajax\n let resubmit = false // if true, and we aren't retrying, we submit the\n // form the old fashioned way\n\n if ( (Date.now() - firstSubmittedAt) > this.#requestTimeout) {\n this.#requestErrorLogger(\"Since initial button press %d, it's taken more than %d ms to get a response.\",firstSubmittedAt,this.#requestTimeout)\n retry = false\n resubmit = true\n }\n else {\n const status = parseInt(response.status)\n if (isNaN(status)) {\n this.#requestErrorLogger(\"Got unparseable status: %d\",response.status)\n retry = false\n }\n else if (status >= 500) {\n this.#requestErrorLogger(\"Got a %d, maybe retry will fix\", status)\n retry = true\n }\n else {\n retry = false\n if (status == 422) {\n this.#handleConstraintViolations(response)\n }\n }\n }\n if (retry) {\n this.#requestErrorLogger(\"Trying again (attempt %d)\",numAttempts +1)\n setTimeout( () => this.#submitForm(form, submitter, firstSubmittedAt, numAttempts + 1), numAttempts * 10)\n }\n else if (resubmit) {\n this.#requestErrorLogger(\"'retry' was marked false, but resubmit is 'true', so submitting through browser\")\n this.#submitFormThroughBrowser(form)\n this.removeAttribute(\"requesting\")\n }\n }\n }).catch( (error) => {\n if (error == AjaxSubmit.doNotSubmitThroughBrowser) {\n this.#requestErrorLogger(\"Error indicates we should not submit through browser: %o\",error)\n }\n else {\n this.#requestErrorLogger(\"Got %o, which cannot be retried\",error)\n this.#submitFormThroughBrowser(form)\n }\n })\n }\n\n #button = () => {\n let button = this.querySelector(\"button\")\n if (!button) {\n button = this.querySelector(\"input[type='submit']\")\n }\n return button\n }\n\n\n #submitFormThroughBrowser(form) {\n form.removeEventListener(\"submit\",this.#formSubmitted)\n if (this.#formSubmitDelay > 0) {\n console.log(\"Form submission has been delayed by %d ms in order to allow examining the log\",this.#formSubmitDelay)\n setTimeout( () => form.requestSubmit(this.#button()), this.#formSubmitDelay)\n }\n else {\n form.requestSubmit(this.#button())\n }\n\n }\n\n #handleConstraintViolations(response) {\n let resubmit = false\n response.text().then( (text) => {\n const parsedDocument = this.domParser.parseFromString(text,\"text/html\")\n let event\n if (this.#serverSideErrorParsing) {\n event = new CustomEvent(\"brut:submitinvalid\")\n }\n else {\n event = new CustomEvent(\"brut:submitinvalid\", { detail: parsedDocument })\n }\n this.dispatchEvent(event)\n if (this.#serverSideErrorParsing) {\n const constraintViolationNodes = parsedDocument.querySelectorAll(ConstraintViolationMessage.tagName)\n try {\n const inputsToMessages = ErrorMessagesForInput.mapInputsToErrorMessages(\n constraintViolationNodes,\n this.#requestErrorLogger\n )\n\n let inputToScrollToAfterReportingValidity\n for (const [inputName, {input, messagesElement, errorMessages}] of Object.entries(inputsToMessages)) {\n if (!inputToScrollToAfterReportingValidity) {\n inputToScrollToAfterReportingValidity = input\n }\n messagesElement.clearServerSideMessages()\n errorMessages.forEach( (element) => {\n ConstraintViolationMessage.markServerSide(element)\n messagesElement.appendChild(element) \n })\n this.#setCustomValidityThatClearsOnChange(input,errorMessages)\n }\n\n if (inputToScrollToAfterReportingValidity) {\n inputToScrollToAfterReportingValidity.scrollIntoView()\n }\n resubmit = false\n this.removeAttribute(\"requesting\")\n }\n catch (e) {\n this.#requestErrorLogger(\"While parsing %s, got %s\", text, e)\n resubmit = true\n }\n if (resubmit) {\n this.#submitFormThroughBrowser(form)\n }\n }\n })\n }\n\n #setCustomValidityThatClearsOnChange(input,errorMessages) {\n input.setCustomValidity(errorMessages[0].textContent)\n input.reportValidity()\n input.addEventListener(\"change\", () => input.setCustomValidity(\"\") )\n }\n}\n\nclass ErrorMessagesForInput {\n static mapInputsToErrorMessages(errorMessages,requestErrorLogger) {\n const errorMessagesForInputs = Array.from(errorMessages).map( (element) => {\n return new ErrorMessagesForInput({\n element: element,\n inputName: element.getAttribute(\"input-name\"),\n document: document,\n })\n })\n\n const inputsToMessages = {}\n\n errorMessagesForInputs.forEach( (errorMessagesForInput) => {\n if (errorMessagesForInput.allElementsFound()) {\n\n if (!inputsToMessages[errorMessagesForInput.inputName]) {\n inputsToMessages[errorMessagesForInput.inputName] = {\n input: errorMessagesForInput.input,\n messagesElement: errorMessagesForInput.messagesElement,\n errorMessages: []\n }\n }\n\n inputsToMessages[errorMessagesForInput.inputName].errorMessages.push(\n errorMessagesForInput.element\n )\n }\n else {\n requestErrorLogger(\n \"Server message %o could not be shown to the user: %s\",\n errorMessagesForInput.element,\n errorMessagesForInput.reasonNotAllElementsFound()\n )\n }\n })\n\n return inputsToMessages\n }\n\n constructor({element,inputName,document}) {\n this.element = element\n this.inputName = inputName\n\n if (this.inputName) {\n const selector = `${ConstraintViolationMessages.tagName}[input-name='${this.inputName}']`\n this.messagesElement = document.querySelector(selector)\n if (this.messagesElement) {\n this.closestForm = this.messagesElement.closest(\"form\")\n }\n if (this.inputName && this.closestForm) {\n this.input = this.closestForm.elements.namedItem(this.inputName)\n }\n }\n }\n\n allElementsFound() {\n return !!this.input\n }\n\n reasonNotAllElementsFound() {\n let reason\n if (this.inputName) {\n if (this.messagesElement) {\n if (this.closestForm) {\n reason = `Form did not contain an input named ${this.inputName}`\n }\n else {\n reason = `Could not find a form that contained the ${ConstraintViolationMessages.tagName} element` \n }\n }\n else {\n reason = `Could not find a ${ConstraintViolationMessages.tagName} element for ${this.inputName}`\n }\n }\n else {\n reason = \"server message was missing an input-name\"\n }\n return reason\n }\n}\n\nexport default AjaxSubmit\n", "import BaseCustomElement from \"./BaseCustomElement\"\n\n/** When the value of the wrapped form element(s) fire(s) a change event, the form is submitted.\n * This will only work if it is inside a form *and* the form elements it contains are part of the form.\n * That means if your input/textarea/select uses the `form` attribute, this custom element has no effect.\n *\n * @example\n *\n * <form>\n * <brut-autosubmit>\n * <!-- when a selection is made, the form is submitted -->\n * <select name=\"status\">\n * <option value=\"draft\">Draft</option>\n * <option value=\"ready\">Ready</option>\n * <option value=\"published\">Published</option>\n * </select>\n * </brut-autosubmit>\n * <!-- when the value is changed, form is NOT submitted -->\n * <input type=\"text\" value=\"notes\">\n * <button>Save</button>\n * </form>\n *\n * @customElement brut-autosubmit\n */\nclass Autosubmit extends BaseCustomElement {\n static tagName = \"brut-autosubmit\"\n\n static observedAttributes = [\n \"show-warnings\",\n ]\n\n #submitForm = (event) => {\n const form = this.closest(\"form\")\n if (!form) {\n this.logger.info(\"No longer a form containing this element\")\n return\n }\n if (event.target.form != form) {\n this.logger.info(\"Event target %o's form is not the form that contains this element\",event.target)\n return\n }\n form.requestSubmit()\n }\n\n update() {\n const form = this.closest(\"form\")\n if (!form) {\n this.logger.info(\"No form containing this element - nothing to autosubmit\")\n return\n }\n const inputs = Array.from(this.querySelectorAll(\"input, textarea, select\")).filter( (element) => {\n return element.form == form\n })\n if (inputs.length == 0) {\n this.logger.info(\"No input, textarea, or select inside this element belongs to the form containing this element\")\n return\n }\n inputs.forEach( (input) => {\n input.addEventListener(\"change\", this.#submitForm)\n })\n }\n}\nexport default Autosubmit\n", "import BaseCustomElement from \"./BaseCustomElement\"\nimport RichString from \"./RichString\"\n\n/**\n * Enhances a `DIALOG` element to allow it to be used for confirmation. Mostly useful\n * with {@link Confirm}.\n *\n * This element must:\n *\n * * Wrap a `<DIALOG>` element\n * * Contain an `<H1>` where the message will go\n * * Have a `<BUTTON>` with value `ok` that will be considered the confirmation button.\n * * Have a `<BUTTON>` with value `cancel` that will be considered the denial button.\n *\n * Omitting these will cause this element to not work properly. Set `show-warnings` to see\n * warnings on this.\n *\n * @property {string} message - the message to use to ask for confirmation\n * @property {string} confirm-label - the label to use for the \"OK\" or \"Confirm\" button\n *\n * @example <caption>Minimal Example</caption>\n * <brut-confirmation-dialog message=\"This cannot be undone\" confirm-label=\"DOIT\">\n * <dialog>\n * <h1></h1>\n * <button value=\"ok\"></button>\n * <button value=\"cancel\">Nevermind</button>\n * </dialog>\n * </brut-confirmation-dialog>\n *\n * @customElement brut-confirmation-dialog\n */\nclass ConfirmationDialog extends BaseCustomElement {\n static tagName = \"brut-confirmation-dialog\"\n static observedAttributes = [\n \"message\",\n \"confirm-label\",\n \"show-warnings\"\n ]\n\n #onClose = () => {}\n #message = new RichString(\"\")\n #confirmLabel = new RichString(\"OK\")\n\n constructor() {\n super()\n this.okListener = (event) => {\n this.#closeDialog()\n this.#onClose(true)\n }\n this.cancelListener = (event) => {\n this.#closeDialog()\n this.#onClose(false)\n }\n }\n\n messageChangedCallback({newValue}) {\n this.#message = RichString.fromString(newValue)\n }\n\n confirmLabelChangedCallback({newValue}) {\n this.#confirmLabel = RichString.fromString(newValue)\n }\n\n /**\n * Call this to show the dialog. When the dialog is closed, `onClose` is called with the result.\n *\n * @param {function} onClose - a function called with either `true` or `false`, if the dialog was confirmed or \n * denied, respectively.\n *\n * @example\n * dialog.showModal( (confirmed) => {\n * if (confirmed) {\n * form.submit()\n * }\n * else {\n * // do nothing\n * }\n * })\n */\n showModal(onClose) {\n const dialog = this.#dialog\n if (dialog) {\n this.#onClose = onClose || (() => {})\n dialog.showModal()\n }\n else {\n this.logger.warn(\"No <dialog> found to show\")\n }\n }\n\n get #dialog() {\n return this.querySelector(\"dialog\")\n }\n\n #closeDialog() {\n const dialog = this.#dialog\n if (dialog) {\n dialog.close()\n }\n }\n\n update() {\n const dialog = this.#dialog\n if (!dialog) {\n this.logger.warn(\"Could not find a <dialog> - this custom element won't do anything\")\n return\n }\n this.#setMessage(dialog)\n this.#setupButtons()\n }\n\n #setMessage(dialog) {\n const h1 = dialog.querySelector(\"h1\")\n if (h1) {\n if (this.#message.isBlank()) {\n h1.textContent = null\n }\n else {\n h1.textContent = this.#message.toString()\n }\n }\n else {\n this.logger.warn(\"Dialog had no <h1>, so nowhere to put the message\")\n }\n }\n\n #setupButtons() {\n const okButton = this.querySelector(\"button[value='ok']\")\n const cancelButton = this.querySelector(\"button[value='cancel']\")\n\n if (!okButton || !cancelButton) {\n if (!okButton) { this.logger.warn(\"no <button value='ok'> which is required for this dialog to work\") }\n if (!cancelButton) { this.logger.warn(\"no <button value='cancel'> which is required for this dialog to work\") }\n return\n }\n\n okButton.textContent = this.#confirmLabel\n\n okButton.addEventListener(\"click\" , this.okListener)\n cancelButton.addEventListener(\"click\" , this.cancelListener)\n }\n}\nexport default ConfirmationDialog\n", "import BaseCustomElement from \"./BaseCustomElement\"\nimport RichString from \"./RichString\"\nimport ConfirmationDialog from \"./ConfirmationDialog\"\n\n/** Confirms form submissions with the user before allowing the form to be submitted. This is applied\n * to buttons, not forms, to allow for finer control over the behavior.\n *\n * * This only works for `button` or `input type=submit`\n * * The elements must have a form, either by being inside a form or having\n * a `form` attribute.\n * * If the form is not valid, it will not show the confirmation dialog.\n *\n *\n * By default, this will use {@link external:Window#confirm}. if \"OK\" is pressed,\n * the button click goes through and the form would be submitted. If \"Cancel\" is pressed,\n * the event is prevented.\n *\n * If there is a `brut-confirmation-dialog` on the page, this component can use that, possibly\n * with help from the `dialog` attribute as followed:\n *\n * * If `dialog` is set:\n * - If that id is on a `<brut-confirmation-dialog>` that is used.\n * - If not, `window.confirm` is used.\n * * If `dialog` is not set:\n * - If there is exactly one `<brut-confirmation-dialog>` on the page, this is used.\n * - If there is more than one, or no `<brut-confirmation-dialog>`s, `window.confirm` is used.\n *\n * If the wrong dialog or notification method is happening, set `show-warnings` on the element, and it will\n * print out why it's doing what it's doing.\n *\n * @see ConfirmationDialog\n *\n * @property {string} message - the message to show that asks for confirmation. It should be written such that\n * \"OK\" is grammatically correct for confirmation and \"Cancel\" is for aborting.\n * @property {string} dialog - optional ID of the `brut-confirmation-dialog` to use instead of `window.confirm`.\n * If there is no such dialog or the id references the wrong element type,\n * `window.confirm` will be used. Setting `show-warnings` will generate a warning for this.\n *\n * @customElement brut-confirm-submit\n */\nclass ConfirmSubmit extends BaseCustomElement {\n static tagName = \"brut-confirm-submit\"\n\n static observedAttributes = [\n \"message\",\n \"dialog\",\n \"show-warnings\",\n ]\n\n #message = new RichString(\"\")\n #confirming = false\n #dialogId = null\n\n messageChangedCallback({newValue}) {\n this.#message = new RichString(newValue || \"\")\n }\n\n dialogChangedCallback({newValue}) {\n this.#dialogId = RichString.fromString(newValue)\n }\n\n constructor() {\n super()\n this.onClick = (event) => {\n if (this.#confirming) {\n this.#confirming = false\n return\n }\n if (this.#message.isBlank()) {\n this.logger.warn(\"No message provided, so cannot confirm\")\n return\n }\n const form = event.currentTarget.form\n\n if (!form) {\n this.logger.warn(\"Element was not part of a form, so cannot confirm submission\")\n return\n }\n\n if (!form.checkValidity()) {\n return\n }\n\n const dialog = this.#findDialog()\n if (dialog) {\n event.preventDefault()\n dialog.setAttribute(\"message\",this.#message.toString())\n const buttonLabel = event.target.getAttribute(\"aria-label\") || event.target.textContent\n dialog.setAttribute(\"confirm-label\",buttonLabel)\n this.#confirming = true\n dialog.showModal((confirm) => {\n if (confirm) {\n event.target.click()\n }\n else {\n this.#confirming = false\n }\n })\n }\n else {\n const result = window.confirm(this.#message)\n if (!result) {\n event.preventDefault()\n }\n }\n }\n }\n\n #findDialog() {\n if (this.#dialogId) {\n const dialog = document.getElementById(this.#dialogId)\n if (dialog) {\n if (dialog.tagName.toLowerCase() != ConfirmationDialog.tagName) {\n throw `${this.#dialogId} is the id of a '${dialog.tagName}', not '${ConfirmationDialog.tagName}'`\n }\n return dialog\n }\n this.logger.warn(`No dialog with id ${this.#dialogId} - using window.confirm as a fallback`)\n return null\n }\n const dialogs = document.querySelectorAll(ConfirmationDialog.tagName)\n if (dialogs.length == 1) {\n return dialogs[0]\n }\n if (dialogs.length == 0) {\n this.logger.warn(`No '${ConfirmationDialog.tagName}' found in document - using window.confirm as a fallback`)\n return null\n }\n throw `Found ${dialogs.length} '${ConfirmationDialog.tagName}' elements. Not sure which to use. Remove all but one or specify the 'dialog' attribute on this element to specify which one to use`\n }\n\n update() {\n this.querySelectorAll(\"button\").forEach( (button) => button.addEventListener(\"click\", this.onClick) )\n this.querySelectorAll(\"input[type=submit]\").forEach( (button) => button.addEventListener(\"click\", this.onClick) )\n }\n}\nexport default ConfirmSubmit\n", "import BaseCustomElement from \"./BaseCustomElement\"\n\n/** Wraps a `<BUTTON>` that will copy text from another element into the system clipboard. It will set various attributes on itself\n * to allow styling the states of the process.\n *\n * Overall, the flow is as follows:\n *\n * 1. When the button is clicked, its default is prevented, and the element with the id of the `element` attribute is located.\n * 2. If found, this element gets the `copying` attribute set.\n * 3. If the copy completes successfully:\n * a. `copying` is removed\n * b. `copied` is set\n * c. `copied` is scheduled for removal in 2000ms or the number of milliseconds in the `copied-lifetime` attribute.\n * 4. If the copy failed:\n * a. `copying` is removed\n * b. `errored` is set\n * c. The `brut:copyfailed` event is fired. It's detail contains a `text:` value with the text that was attempted to be copied.\n *\n * The intention is to use these attributes to style whatever UX you want.\n *\n * @property {string} element - ID of the element whose `textContent` is what will be copied to the clipboard.\n * @property {number} copied-lifetime - number of milliseconds to wait before clearing the `copied` attribute after a successful copy.\n * @property {boolean} copying - Set after a copy is initiated, but before it completes\n * @property {boolean} copied - Set after a copy is completed successfully\n * @property {boolean} errored - Set after a copy is fails\n *\n * @fires brut:copyfailed Fired when the copy fails to complete\n *\n * @example\n * <pre><code id=\"code\">dx/exec bin/setup</code></pre>\n * <brut-copy-to-clipboard element=\"code\">\n * <button>Copy</button>\n * </brut-copy-to-clipboard>\n *\n * @customElement brut-copy-to-clipboard\n */\nclass CopyToClipboard extends BaseCustomElement {\n static tagName = \"brut-copy-to-clipboard\"\n\n static observedAttributes = [\n \"element\",\n \"copied-lifetime\",\n ]\n\n #elementId = null\n #copiedLifetime = 2000\n\n #copyCode = (event) => {\n event.preventDefault()\n\n const element = document.getElementById(this.#elementId)\n\n if (!element) {\n this.logger.info(\"No element with id %s, so nothing to copy\",this.#elementId)\n return\n }\n this.setAttribute(\"copying\",true)\n\n const text = element.textContent\n\n navigator.clipboard.writeText(text).then( () => {\n this.setAttribute(\"copied\", true)\n }).catch( (e) => {\n this.setAttribute(\"errored\", true)\n this.dispatchEvent(new CustomEvent(\"brut:copyfailed\", { detail: { text: text }}))\n }).finally( () => {\n this.removeAttribute(\"copying\")\n setTimeout( () => this.removeAttribute(\"copied\"), 2000)\n })\n }\n\n update() {\n const button = this.querySelector(\"button\")\n if (button) {\n button.addEventListener(\"click\", this.#copyCode)\n }\n else {\n this.logger.info(\"There is no button, so no way to initiate a copy\")\n }\n }\n\n elementChangedCallback({newValue}) {\n this.#elementId = newValue\n }\n\n copiedLifetimeChangedCallback({newValue}) {\n const newValueAsInt = parseInt(newValue)\n if (!isNaN(newValueAsInt)) {\n this.#copiedLifetime = newValueAsInt\n }\n else {\n this.logger.info(\"Value '%s' for copied-lifetime is not a number. Ignoring it\",newValue)\n }\n }\n}\nexport default CopyToClipboard\n", "import BaseCustomElement from \"./BaseCustomElement\"\nimport RichString from \"./RichString\"\nimport AjaxSubmit from \"./AjaxSubmit\"\nimport ConstraintViolationMessages from \"./ConstraintViolationMessages\"\n\n/** A web component that enhances a form it contains to make constraint validations\n * easier to manage and control.\n *\n * This provides two main features:\n *\n * * While the `:user-invalid` selector allows you to target inputs that have been interacted\n * with (thus avoiding issues when using `:invalid`), this still creates the experience of a\n * user tabbing off of a control and getting an error message. If, instead, you only\n * want to show these errors when a submit has been attempted, this element will\n * set `submitted-invalid` on itself when that happens, thus allowing you to target invalid\n * fields only after a submission attempt.\n * * You may wish to control the messaging of client-side constraint violations\n * beyond what the browser gives you. Assuming you have generated a `<brut-cv-messages input-name=\"\u00ABinput name\u00BB\"></brut-cv-messasges>`, it will be populated with `<brut-cv>` elements for each client-side constraint violation, based on the {@link external:ValidityState} of the control.\n *\n * @fires brut:invalid Fired when any element is found to be invalid\n * @fires brut:valid Fired when no element is found to be invalid. This should be reliable to know\n * when constraint violations have cleared.\n *\n * @example <caption>Basic Structure Required</caption>\n * <brut-form>\n * <form ...>\n * <label>\n * <input type=\"text\" required name=\"username\">\n * <brut-cv-messages input-name=\"username\">\n * </brut-cv-messages>\n * </label>\n * <div> <!-- container need not be a label -->\n * <input type=\"text\" required minlength=\"4\" name=\"alias\">\n * <brut-cv-messages input-name=\"alias\">\n * </brut-cv-messages>\n * </div>\n * <button>Submit</button>\n * </form>\n * </brut-form>\n * <!-- after a submit of this form, the HTML will effectively be as follows -->\n * <brut-form submitted-invalid>\n * <form ...>\n * <label>\n * <input type=\"text\" required name=\"username\">\n * <brut-cv-messages input-name=\"username\">\n * <brut-cv>This field is required</brut-cv>\n * </brut-cv-messages>\n * </label>\n * <div> <!-- container need not be a label -->\n * <input type=\"text\" required minlength=\"4\" name=\"alias\">\n * <brut-cv-messages input-name=\"alias\">\n * <brut-cv>This field is required</brut-cv>\n * </brut-cv-messages>\n * </div>\n * <button>Submit</button>\n * </form>\n * </brut-form>\n *\n * @property {boolean} submitted-invalid - set by this element when the form is submitted. Does not trigger any behavior and can be used in CSS.\n * @see ConstraintViolationMessages\n *\n * @customElement brut-form\n */\nclass Form extends BaseCustomElement {\n static tagName = \"brut-form\"\n static observedAttributes = [\n \"submitted-invalid\",\n \"show-warnings\",\n ]\n\n #markFormSubmittedInvalid = (event) => {\n this.setAttribute(\"submitted-invalid\",\"\")\n }\n #updateValidity = (event) => {\n this.#updateErrorMessages(event)\n }\n #sendValid = () => {\n this.dispatchEvent(new CustomEvent(\"brut:valid\"))\n }\n #sendInvalid = () => {\n this.dispatchEvent(new CustomEvent(\"brut:invalid\"))\n }\n\n submittedInvalidChangedCallback() {}\n\n update() {\n const forms = this.querySelectorAll(\"form\")\n if (forms.length == 0) {\n this.logger.warn(\"Didn't find any forms. Ignoring\")\n return\n }\n forms.forEach( (form) => {\n Array.from(form.elements).forEach( (formElement) => {\n formElement.addEventListener(\"invalid\", this.#updateValidity)\n formElement.addEventListener(\"invalid\", this.#markFormSubmittedInvalid)\n formElement.addEventListener(\"input\", this.#updateValidity)\n })\n form.querySelectorAll(AjaxSubmit.tagName).forEach( (ajaxSubmits) => {\n ajaxSubmits.addEventListener(\"brut:submitok\", this.#sendValid)\n ajaxSubmits.addEventListener(\"brut:submitinvalid\", this.#sendInvalid)\n })\n })\n }\n\n #updateErrorMessages(event) {\n const element = event.target\n let constraintViolationMessages = []\n if (element.name && element.form) {\n const selector = `${ConstraintViolationMessages.tagName}[input-name='${element.name}']`\n constraintViolationMessages = element.form.querySelectorAll(selector)\n if (constraintViolationMessages.length == 0) {\n this.logger.warn(`Did not find any elements matching ${selector}, so no error messages will be shown`)\n }\n }\n else {\n if (element.name) {\n this.logger.warn(\"Element has a name (%s), but is not associated with any form.\", element.name)\n }\n else {\n this.logger.warn(\"Element has a form, but has no name, which means we cannot locate %s by input-name\", ConstraintViolationMessages.tagName)\n }\n }\n if (constraintViolationMessages.length == 0) {\n return\n }\n let anyErrors = false\n constraintViolationMessages.forEach( (errorLabel) => {\n if (element.validity.valid) {\n errorLabel.clearClientSideMessages()\n }\n else {\n anyErrors = true\n errorLabel.createMessages({\n validityState: element.validity,\n inputName: element.name\n })\n }\n })\n if (anyErrors) {\n this.#sendInvalid()\n }\n else {\n this.#sendValid()\n }\n }\n}\nexport default Form\n", "import BaseCustomElement from \"./BaseCustomElement\"\n\n/**\n * Send the locale and timezone from the browser to a configured endpoint on the server. This allows\n * the server to have access to a reasonable guess as to the website visitor's locale/timezone.\n *\n * Note that this will not contact the server if both `locale-from-server` and `timezone-from-server` are\n * set. Further note that this will only contact the server once per page load, unless `url` is changed.\n *\n * @property {String} locale-from-server - omit this if the server doesn't know the visitor's locale. If both this and `timezone-from-server` are set, the server will not be contacted.\n * @property {String} timezone-from-server - omit this if the server doesn't know the visitor's timezone. If both this and `locale-from-server` are set, the server will not be contacted.\n * @property {URL} url - the url to send information to on the server.\n * @property {number} timeout-before-ping-ms - MS to wait until this element contacts the server. A value of 0 will contact the server immediately. The default is 1,000, meaning this element will wait 1 second before contacting the server.\n *\n * @example <caption>When no information about the visitor is known</caption>\n * <brut-locale-detection url=\"__brut/locale-detection\"></brut-locale-detection>\n *\n * @example <caption>When all information about the visitor is known</caption>\n * <brut-locale-detection\n * url=\"__brut/locale-detection\"\n * locale-from-server=\"en-US\"\n * timezone-from-server=\"America/New_York\">\n * </brut-locale-detection>\n *\n * @customElement brut-locale-detection\n */\nclass LocaleDetection extends BaseCustomElement {\n static tagName = \"brut-locale-detection\"\n\n static observedAttributes = [\n \"locale-from-server\",\n \"timezone-from-server\",\n \"url\",\n \"timeout-before-ping-ms\",\n \"show-warnings\",\n ]\n\n #localeFromServer = null\n #timezoneFromServer = null\n #reportingURL = null\n #timeoutBeforePing = 1000\n #serverContacted = false\n\n localeFromServerChangedCallback({newValue}) {\n this.#localeFromServer = newValue\n }\n\n timezoneFromServerChangedCallback({newValue}) {\n this.#timezoneFromServer = newValue\n }\n\n urlChangedCallback({newValue}) {\n if (this.#serverContacted) {\n this.#serverContacted = false\n }\n this.#reportingURL = newValue\n }\n\n timeoutBeforePingMsChangedCallback({newValue}) {\n this.#timeoutBeforePing = newValue\n }\n\n update() {\n if (this.#timeoutBeforePing == 0) {\n this.#pingServerWithLocaleInfo()\n }\n else {\n setTimeout(this.#pingServerWithLocaleInfo.bind(this), this.#timeoutBeforePing)\n }\n }\n\n #pingServerWithLocaleInfo() {\n if (!this.#reportingURL) {\n this.logger.info(\"no url= set, so nowhere to report to\")\n return\n }\n if (this.#localeFromServer && this.#timezoneFromServer) {\n this.logger.info(\"locale and timezone both set, not contacting server\")\n return\n }\n\n if (this.#serverContacted) {\n this.logger.info(\"server has already been contacted at the given url, not doing it again\")\n return\n }\n this.#serverContacted = true\n\n const formatOptions = Intl.DateTimeFormat().resolvedOptions()\n const request = new Request(\n this.#reportingURL,\n {\n headers: {\n \"Content-Type\": \"application/json\",\n },\n method: \"POST\",\n body: JSON.stringify({\n locale: formatOptions.locale,\n timeZone: formatOptions.timeZone,\n }),\n }\n )\n\n window.fetch(request).then( (response) => {\n if (response.ok) {\n this.logger.info(\"Server gave us the OK\") \n }\n else {\n console.warn(response)\n }\n }).catch( (e) => {\n console.warn(e)\n })\n }\n\n\n}\nexport default LocaleDetection\n", "import BaseCustomElement from \"./BaseCustomElement\"\nimport RichString from \"./RichString\"\nimport I18nTranslation from \"./I18nTranslation\"\n\n/** Renders a translated message for a given key, handling all the needed interpolation based\n * on the existence of `<brut-i18n-translation>` elements on the page.\n *\n * When the `key` attribute has a value, this element will locate the `<brut-i18-translation>` element and call `translate`. Note that\n * interpolation is not supported.\n *\n * @property {string} key - the i18n translation key to use. It must map to the `key` of a `<brut-i18n-translation>` on the page or\n * the element will not render any text.\n *\n * @see I18nTranslation\n * @see ConstraintViolationMessage\n *\n * @customElement brut-message\n */\nclass Message extends BaseCustomElement {\n static tagName = \"brut-message\"\n static observedAttributes = [\n \"show-warnings\",\n \"key\",\n ]\n\n static createElement(document,attributes) {\n const element = document.createElement(Message.tagName)\n element.setAttribute(\"key\",attributes.key)\n element.setAttribute(\"show-warnings\",attributes[\"show-warnings\"])\n return element\n }\n\n #key = null\n\n keyChangedCallback({newValue}) {\n this.#key = newValue\n }\n\n update() {\n if (!this.#key) {\n this.logger.info(\"No key attribute, so can't do anything\")\n return\n }\n\n const selector = `${I18nTranslation.tagName}[key='${this.#key}']`\n const translation = document.querySelector(selector)\n if (!translation) {\n this.logger.info(\"Could not find translation based on selector '%s'\",selector)\n return\n }\n\n this.textContent = RichString.fromString(translation.translation()).capitalize().toString()\n }\n}\n\nexport default Message\n", "import BaseCustomElement from \"./BaseCustomElement\"\n\n/** Implements an in-page tab selector. It's intended to wrap a set of `<a>` or `<button>` elements\n * that represent the tabs of a tabbed UI, as defined by ARIA roles. \n *\n * Each direct child must be an `<a>` or a `<button>`, though `<a>` is recommended.\n * Any other elements are ignored. Each `<a>` or `<button>`\n * (herafter referred to as \"tab\") must have the correct ARIA attributes:\n *\n * * `role=\"tab\"`\n * * `aria-selected` as true or false, depending on what tab is selected when the page is first rendered. This\n * custom element will ensure this value is updated as different tabs are selected.\n * * `tabindex` should be 0 if selected, -1 otherwise. This custom element will ensure this value is updated as\n * different tabs are selected.\n * * `aria-controls` to the ID or list of IDs of panels that should be shown when this tab is selected.\n * * `id` to allow the `tab-panel` to refer back to this tab.\n *\n * This custom element will set click listeners on all tabs and, when clicked, hide all panels referred to by\n * every tab (by setting the `hidden` attribute), then show only those panels referred to by the clicked\n * tab. You can use CSS to style everything the way you like it.\n *\n * @property {boolean} tab-selection-pushes-and-restores-state if set, this custom element will use the \n * history API to manage state. When a tab\n * implemented by an `<a>` with an `href` is\n * clicked, that `href` will be pushed into \n * the state. When the back button is hit,\n * this will select the previous tab as selected.\n * Note that this will conflict with anything else\n * on the page that manipulates state, so only\n * set this if your UI is a \"full page tab\"\n * style UI.\n *\n * @fires Tabs#brut:tabselected whenever the tab selection has changed\n * @example\n * <brut-tabs>\n * <a role=\"tab\" aria-selected=\"true\" tabindex=\"0\" aria-controls=\"inbox-panel\" id=\"inbox-tab\"\n * href=\"?tab=inbox\">Inbox</a>\n * <a role=\"tab\" aria-selected=\"false\" tabindex=\"-1\" aria-controls=\"drafts-panel\" id=\"drafts-tab\"\n * href=\"?tab=drafts\">Drafts</a>\n * <a role=\"tab\" aria-selected=\"false\" tabindex=\"-1\" aria-controls=\"spam-panel\" id=\"spam-tab\"\n * href=\"?tab=spam\">Spam</a>\n * </brut-tabs>\n * <section role=\"tabpanel\" tabindex=\"0\" id=\"inbox-panel\">\n * <h3>Inbox</h3>\n * </section>\n * <section role=\"tabpanel\" tabindex=\"0\" id=\"drafts-panel\" hidden>\n * <h3>Drafts</h3>\n * </section>\n * <section role=\"tabpanel\" tabindex=\"0\" id=\"spam-panel\" hidden>\n * <h3>Spam</h3>\n * </section>\n * <!-- if a user clicks on 'Drafts', the DOM will be updated to look\n * effectively like so: -->\n * <brut-tabs>\n * <a role=\"tab\" aria-selected=\"false\" tabindex=\"-1\" aria-controls=\"inbox-panel\" id=\"inbox-tab\"\n * href=\"?tab=inbox\">Inbox</a>\n * <a role=\"tab\" aria-selected=\"true\" tabindex=\"0\" aria-controls=\"drafts-panel\" id=\"drafts-tab\"\n * href=\"?tab=drafts\">Drafts</a>\n * <a role=\"tab\" aria-selected=\"false\" tabindex=\"-1\" aria-controls=\"spam-panel\" id=\"spam-tab\"\n * href=\"?tab=spam\">Spam</a>\n * </brut-tabs>\n * <section role=\"tabpanel\" tabindex=\"0\" id=\"inbox-panel\" hidden>\n * <h3>Inbox</h3>\n * </section>\n * <section role=\"tabpanel\" tabindex=\"-1\" id=\"drafts-panel\">\n * <h3>Drafts</h3>\n * </section>\n * <section role=\"tabpanel\" tabindex=\"-1\" id=\"spam-panel\" hidden>\n * <h3>Spam</h3>\n * </section>\n *\n * @customElement brut-tabs\n */\nclass Tabs extends BaseCustomElement {\n static tagName = \"brut-tabs\"\n static observedAttributes = [\n \"tab-selection-pushes-and-restores-state\",\n \"show-warnings\",\n ]\n\n tabSelectionPushesAndRestoresStateChangedCallback({newValue,oldValue}) {\n this.#pushAndRestoreTabState = newValue != null\n }\n\n update() {\n this.#tabs().forEach( (tab) => {\n tab.addEventListener(\"click\", this.#tabClicked)\n })\n }\n\n #pushAndRestoreTabState = false\n\n #tabClicked = (event) => {\n event.preventDefault()\n this.#setTabAsSelected(event.target)\n event.preventDefault()\n }\n\n #reloadTab = (event) => {\n const tab = document.getElementById(event.state.tabId)\n if (tab) {\n this.#setTabAsSelected(tab, { skipPushState: true })\n }\n }\n\n #setTabAsSelected(selectedTab, { skipPushState = false } = {}) {\n this.#tabs().forEach( (tab) => {\n const tabPanels = []\n const ariaControls = tab.getAttribute(\"aria-controls\")\n if (ariaControls) {\n ariaControls.split(/\\s+/).forEach( (id) => {\n const panel = document.getElementById(id)\n if (panel) {\n tabPanels.push(panel)\n }\n else {\n this.logger.warn(\"Tab %o references panel with id %s, but no such element exists with that id\",tab,id)\n }\n })\n }\n if (tab == selectedTab) {\n tab.setAttribute(\"aria-selected\",true)\n tab.setAttribute(\"tabindex\",\"0\")\n tabPanels.forEach( (panel) => panel.removeAttribute(\"hidden\") )\n if (this.#pushAndRestoreTabState && !skipPushState) {\n let href = tab.getAttribute(\"href\") || \"\"\n if (href.startsWith(\"?\")) {\n let hrefQueryString = href.slice(1)\n const anchorIndex = hrefQueryString.indexOf(\"#\")\n if (anchorIndex != -1) {\n hrefQueryString = hrefQueryString.slice(-1 * (hrefQueryString.length - anchorIndex - 1))\n }\n const currentQuery = new URLSearchParams(window.location.search)\n const hrefQuery = new URLSearchParams(hrefQueryString)\n hrefQuery.forEach( (value,key) => {\n currentQuery.set(key,value)\n })\n href = \"?\" + currentQuery.toString() + (anchorIndex == -1 ? \"\" : hrefQueryString.slice(anchorIndex))\n }\n window.history.pushState({ tabId: tab.id },\"\",href)\n window.addEventListener(\"popstate\", this.#reloadTab)\n }\n this.dispatchEvent(new CustomEvent(\"brut:tabselected\", { tabId: tab.id }))\n }\n else {\n tab.setAttribute(\"aria-selected\",false)\n tab.setAttribute(\"tabindex\",\"-1\")\n tabPanels.forEach( (panel) => panel.setAttribute(\"hidden\", true) )\n }\n })\n }\n\n\n #tabs() {\n const tabs = []\n this.querySelectorAll(\"[role=tab]\").forEach( (tab) => {\n if ( (tab.tagName.toLowerCase() == \"a\") || (tab.tagName.toLowerCase() == \"button\") ) {\n tabs.push(tab)\n }\n else {\n this.logger.warn(\"An element with tag %s was assigned role=tab, and %s doesn't work that way. Use an <a> or a <button>\",tab.tagName,this.constructor.name)\n }\n })\n return tabs\n }\n\n}\nexport default Tabs\n", "import { BaseCustomElement } from \"brut-js\"\n\n/** Sends performance data to an endpoint in a Brut-powered app that is expected to save it as an Open Telemetry span.\n * Uses the W3C-recommended headers \"traceparent\" and \"tracestate\" to do this.\n *\n * ### Supported Metrics\n *\n * Currently, this will attempt to send \"navigation\", \"largest-contentful-paint\", and \"first-contentful-paint\" back to the server.\n * Not all browsers support these, so this element will send back as many as it can. It will wait for all supported metrics to be\n * received before contacting the server. It will attempt to do this exactly once.\n *\n * ### Use\n *\n * To use this element, your page must have a `<meta>` element that contains the value for \"traceparent\". It is expected that your\n * server will include this in server-generatd HTML. The Brut's `Brut::FrontEnd::Components::Traceparent` component will handle this\n * for you. The value for \"traceparent\" is key to connecting the browser metrics to the back-end request that generated the page.\n *\n * The element also requires a `url` attribute to know where to send the data. By default, Brut is listening in\n * `/__brut/instrumentation`. See the example.\n *\n * ### Durations vs Timestamps\n *\n * The performance API produces durations since an origin timestamp. Open Telemetry wants timestamps. In theory,\n * `Performance.timeOrigin` is provided by the browser as a reference time when the page started doing anything.\n * In practice, this value is incorrect on Firefox, so the element records a timestamp when it is created.\n *\n * When the data is merged back to the server span, the specific timestamps will not exactly match reality, however the durations will\n * be accurate. Note that even if `Performance.timeOrigin` was correct, clock drift between client and server would make\n * the timestamps inaccurate anyway.\n *\n * ### Encoding\n *\n * The spec for the \"tracestate\" header leaves open how the data is to be encoded. It supports multiple vendors using a key/value\n * pair:\n *\n * tracestate: honeycomb=\u00ABencoded data\u00BB,newrelic=\u00ABencoded data\u00BB\n *\n * This element uses the vendor name \"brut\". The data is a Base64-encoded JSON blob containing the data. \n *\n * tracestate: brut=\u00ABBase64 encoded JSON\u00BB\n *\n * The values captured and format of the JSON map closely to Open Telemetry's browser instrumentation format.\n * Of course, this element is many magnitudes smaller in size than Open Telemetry's, which is why it exists at all\n *\n * @example\n * <!DOCTYPE html>\n * <html>\n * <head>\n * <meta name=\"traceparent\" content=\"293874293749237439843294\">\n * <brut-tracing url=\"/__brut/instrumentation\"></brut-tracing>\n * <!-- ... -->\n * </head>\n * <body>\n * <!-- ... -->\n * </body>\n * </html>\n *\n * @property {string} url - the url where the trace information is to be sent.\n *\n * @see {@link https://www.w3.org/TR/trace-context/}\n * @see external:Performance\n *\n * @customElement brut-tracing\n */\nclass Tracing extends BaseCustomElement {\n static tagName = \"brut-tracing\"\n\n static observedAttributes = [\n \"url\",\n \"show-warnings\",\n ]\n\n\n #url = null\n #sent = {}\n #payload = {}\n #timeOrigin = null\n #supportedTypes = []\n\n #performanceObserver = new PerformanceObserver( (entries) => {\n const navigation = entries.getEntriesByType(\"navigation\")[0]\n if (navigation && navigation.loadEventEnd != 0 && !this.#payload.navigation) {\n this.#payload.navigation = this.#parseNavigation(navigation)\n }\n const largestContentfulPaint = entries.getEntriesByType(\"largest-contentful-paint\")\n if (largestContentfulPaint.length > 0 && !this.#payload[\"largest-contentful-paint\"]) {\n this.#payload[\"largest-contentful-paint\"] = this.#parseLargestContentfulPaint(largestContentfulPaint)\n }\n const paint = entries.getEntriesByName(\"first-contentful-paint\", \"paint\")[0]\n if (paint && !this.#payload.paint) {\n this.#payload.paint = this.#parseFirstContentfulPaint(paint)\n }\n\n if ( this.#supportedTypes.every( (type) => this.#payload[type] ) ) {\n this.#sendSpans()\n this.#payload = {}\n }\n })\n\n constructor() {\n super()\n this.#timeOrigin = Date.now()\n this.#supportedTypes = [\n \"navigation\",\n \"largest-contentful-paint\",\n \"paint\",\n ].filter( (type) => {\n return PerformanceObserver.supportedEntryTypes.includes(type)\n })\n }\n\n urlChangedCallback({newValue}) {\n this.#url = newValue\n }\n\n update() {\n this.#supportedTypes.forEach( (type) => {\n this.#performanceObserver.observe({type: type, buffered: true})\n })\n }\n\n #sendSpans() {\n const headers = this.#initializerHeadersIfCanContinue()\n if (!headers) {\n return\n }\n const span = this.#payload.navigation\n\n if (this.#payload.paint) {\n span.events.push({\n name: this.#payload.paint.name,\n timestamp: this.#timeOrigin + this.#payload.paint.startTime\n })\n }\n if (this.#payload[\"largest-contentful-paint\"]) {\n this.#payload[\"largest-contentful-paint\"].forEach( (event) => {\n span.events.push({\n name: event.name,\n timestamp: this.#timeOrigin + event.startTime,\n attributes: {\n \"element.tag\": event.element?.tagName,\n \"element.class\": event.element?.className,\n }\n })\n })\n }\n\n this.#sent[this.#url] = true\n headers.append(\"tracestate\",`brut=${window.btoa(JSON.stringify(span))}`)\n const request = new Request(\n this.#url,\n {\n headers: headers,\n method: \"GET\",\n }\n )\n fetch(request).then( (response) => {\n if (!response.ok) {\n console.warn(\"Problem sending instrumentation: %s/%s\", response.status,response.statusText)\n }\n }).catch( (error) => {\n console.warn(\"Problem sending instrumentation: %o\", error)\n })\n }\n\n #parseNavigation(navigation) {\n const documentFetch = {\n name: \"browser.documentFetch\",\n start_timestamp: navigation.fetchStart + this.#timeOrigin,\n end_timestamp: navigation.responseEnd + this.#timeOrigin,\n attributes: {\n \"http.url\": navigation.name,\n },\n }\n const events = [\n \"fetchStart\",\n \"unloadEventStart\",\n \"unloadEventEnd\",\n \"domInteractive\",\n \"domInteractive\",\n \"domContentLoadedEventStart\",\n \"domContentLoadedEventEnd\",\n \"domComplete\",\n \"loadEventStart\",\n \"loadEventEnd\",\n ]\n\n return {\n name: \"browser.documentLoad\",\n start_timestamp: navigation.fetchStart + this.#timeOrigin,\n end_timestamp: navigation.loadEventEnd + this.#timeOrigin,\n attributes: {\n \"http.url\": navigation.name,\n \"http.user_agent\": window.navigator.userAgent,\n },\n events: events.map( (eventName) => {\n return {\n name: eventName,\n timestamp: this.#timeOrigin + navigation[eventName],\n }\n }),\n spans: [\n documentFetch\n ]\n }\n }\n\n #parseFirstContentfulPaint(paint) {\n return {\n name: \"browser.first-contentful-paint\",\n startTime: paint.startTime,\n }\n }\n\n #parseLargestContentfulPaint(largestContentfulPaint) {\n return largestContentfulPaint.map( (entry) => {\n return {\n name: \"browser.largest-contentful-paint\",\n startTime: entry.startTime,\n element: entry.element,\n }\n })\n }\n\n #initializerHeadersIfCanContinue() {\n if (!this.#url) {\n this.logger.info(\"No url set, no traces will be reported\")\n return\n }\n const $traceparent = document.querySelector(\"meta[name='traceparent']\")\n if (!$traceparent) {\n this.logger.info(\"No <meta name='traceparent' ...> in the document, no traces can be reported\")\n return\n }\n if (this.#sent[this.#url]) {\n this.logger.info(\"Already sent to %s\", this.#url)\n return\n }\n const traceparent = $traceparent.getAttribute(\"content\")\n if (!traceparent) {\n this.logger.info(\"%o had no value for the content attribute, no traces can be reported\",$traceparent)\n return\n }\n return new Headers({ traceparent })\n }\n}\nexport default Tracing\n", "import BaseCustomElement from \"./BaseCustomElement\"\nimport AjaxSubmit from \"./AjaxSubmit\"\nimport Autosubmit from \"./Autosubmit\"\nimport ConfirmSubmit from \"./ConfirmSubmit\"\nimport ConfirmationDialog from \"./ConfirmationDialog\"\nimport ConstraintViolationMessage from \"./ConstraintViolationMessage\"\nimport ConstraintViolationMessages from \"./ConstraintViolationMessages\"\nimport CopyToClipboard from \"./CopyToClipboard\"\nimport Form from \"./Form\"\nimport I18nTranslation from \"./I18nTranslation\"\nimport LocaleDetection from \"./LocaleDetection\"\nimport Message from \"./Message\"\nimport RichString from \"./RichString\"\nimport Tabs from \"./Tabs\"\nimport Tracing from \"./Tracing\"\n\n/**\n * This is the code for a test case. It may return a {@link external:Promise} if there is async behavior that must\n * be waited-on to properly assert behavior.\n *\n * @callback testCodeCallback\n *\n * @param {Object} objects - objects passed into your test that you may need.\n * @param {Window} objects.window - Access to the top-level window object. Note that this provided by JSDOM and is not exactly like the `Window` you'd get in your browser.\n * @param {Document} objects.document - Access to the top-level document object. Note that this provided by JSDOM and is not exactly like the `Document` you'd get in your browser.\n * @param {Object} objects.assert - The NodeJS assert object that you should use to assert behavior.\n * @param {Object} objects.fetchRequests - An array of `Request` instances given to `fetch`. This will be updated as `fetch` is\n * called and can be useful to assert the contents of what was requested via `fetch`.\n *\n * @example\n * test(\"some test\", ({document,assert}) => {\n * const element = document.querySelector(\"div\")\n * assert(div.getAttribute(\"data-foo\") != null)\n * })\n *\n * @example\n * test(\"some other test\", ({document,window,assert}) => {\n * const element = document.querySelector(\"div\")\n * assert.equal(window.history.state[\"foo\"], \"bar\")\n * })\n */\n\n/**\n * @external Performance\n * @see {@link https://developer.mozilla.org/en-US/docs/Web/API/Performance|Performance API}\n */\n\n/**\n * @external Promise\n * @see {@link https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise|Promise}\n */\n/**\n * @external fetch\n * @see {@link https://developer.mozilla.org/en-US/docs/Web/API/Window/fetch|fetch}\n */\n\n/**\n * @external ValidityState\n * @see {@link https://developer.mozilla.org/en-US/docs/Web/API/ValidityState|ValidityState}\n */\n\n/**\n * The standard `CustomElementRegistry`\n *\n * @external CustomElementRegistry\n * @see {@link https://developer.mozilla.org/en-US/docs/Web/API/CustomElementRegistry|CustomElementRegistry}\n */\n\n/**\n * @external Window\n * @see {@link https://developer.mozilla.org/en-US/docs/Web/API/Window/|Window}\n */\n\n/** \n * @method confirm\n * @memberof external:Window#\n * @see {@link https://developer.mozilla.org/en-US/docs/Web/API/Window/confirm|confirm}\n */\n\n/**\n * Class that can be used to automatically define all of brut's custom\n * elements.\n */\nclass BrutCustomElements {\n static elementClasses = []\n static define() {\n this.elementClasses.forEach( (e) => {\n e.define() \n })\n }\n static addElementClasses(...classes) {\n this.elementClasses.push(...classes)\n }\n}\n\nBrutCustomElements.addElementClasses(\n // Ordering is important here - TBD how to make sure these are created in order\n I18nTranslation,\n CopyToClipboard,\n Message,\n ConfirmSubmit,\n ConfirmationDialog,\n ConstraintViolationMessages,\n Form,\n AjaxSubmit,\n ConstraintViolationMessage,\n Tabs,\n LocaleDetection,\n Autosubmit,\n Tracing,\n)\n\nexport {\n AjaxSubmit,\n Autosubmit,\n BaseCustomElement,\n BrutCustomElements,\n ConfirmSubmit,\n ConfirmationDialog,\n ConstraintViolationMessage,\n ConstraintViolationMessages,\n Form,\n I18nTranslation,\n LocaleDetection,\n Message,\n RichString,\n Tabs,\n Tracing\n}\n\n", "import * as BrutJS from \"./index.js\"\ndocument.addEventListener(\"DOMContentLoaded\", () => {\n BrutJS.BrutCustomElements.define()\n if (!HTMLDialogElement.prototype.showModal) {\n HTMLDialogElement.prototype.showModal = function() {\n this.open = true\n }\n }\n if (!HTMLDialogElement.prototype.close) {\n HTMLDialogElement.prototype.close = function(returnValue) {\n this.open = false\n this.returnValue = returnValue\n }\n }\n})\n"],
|
|
5
|
-
"mappings": "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",
|
|
3
|
+
"sources": ["../../../src/Logger.js", "../../../src/RichString.js", "../../../src/BaseCustomElement.js", "../../../src/I18nTranslation.js", "../../../src/ConstraintViolationMessage.js", "../../../src/ConstraintViolationMessages.js", "../../../src/AjaxSubmit.js", "../../../src/Autosubmit.js", "../../../src/ConfirmationDialog.js", "../../../src/ConfirmSubmit.js", "../../../src/CopyToClipboard.js", "../../../src/Form.js", "../../../src/LocaleDetection.js", "../../../src/Message.js", "../../../src/Tabs.js", "../../../src/Toast.js", "../../../src/Tracing.js", "../../../src/index.js", "../../../src/appForTestingOnly.js"],
|
|
4
|
+
"sourcesContent": ["/**\n * Abstract interface for logging information from a component.\n * This is intended to allow prefixed messages to be optionally shown\n * in the console to help debug.\n *\n * @see BufferedLogger\n * @see PrefixedLogger\n * @see BaseCustomElement#logger\n */\nclass Logger {\n /** Create a logger for the given prefix.\n *\n * @param {string|false} stringOrFalse - if false,returns a {@link BufferedLogger}. Otherwise, returns a {@link PrefixedLogger} using the param's value as the prefix.\n *\n * @returns {Logger}\n */\n static forPrefix(stringOrFalse) {\n if (!stringOrFalse) {\n return new BufferedLogger()\n }\n else {\n return new PrefixedLogger(stringOrFalse)\n }\n }\n\n /** Subclasses must implement this.\n *\n * @param {string} level - 'info' or 'warn' to indicate the logging level\n * @param {...*} args - args to pass directly to console.log\n */\n log() {\n throw `Subclass must implement`\n }\n\n /** Log an informational bit of information */\n info(...args) { this.log(\"info\",...args) }\n /** Log a warning */\n warn(...args) { this.log(\"warn\",...args) }\n}\n\n/** Logger that buffers, but does not print, its logged messages.\n * The reason it buffers them is to allow custom elements to retroatively log\n * information captured before warnings were turned on.\n */\nclass BufferedLogger extends Logger {\n constructor() {\n super()\n this.messages = []\n }\n log(...args) {\n this.messages.push(args)\n }\n}\n\n/** Log information to the JavaScript console.\n*/\nclass PrefixedLogger extends Logger {\n /** Create a PrefixedLogger.\n *\n * @param {string|true} prefixOrTrue - if true, uses the prefix `\"debug\"`, otherwise uses the param as the prefix to all\n * messages output.\n */\n constructor(prefixOrTrue) {\n super()\n this.prefix = prefixOrTrue === true ? \"debug\" : prefixOrTrue\n }\n\n /** Dumps hte contents of a {@link BufferedLogger} to this logger's output.\n *\n * @param {BufferedLogger} bufferedLogger - a logger with pent-up messages, waiting to be logged.\n */\n dump(bufferedLogger) {\n if (bufferedLogger instanceof BufferedLogger) {\n bufferedLogger.messages.forEach( (args) => {\n this.log(...args)\n })\n }\n }\n\n log(level,...args) {\n if (typeof(args[0]) === \"string\") {\n const message = `[prefix:${this.prefix}]:${args[0]}`\n console[level](message,...(args.slice(1)))\n }\n else {\n console[level](this.prefix,...args)\n }\n }\n}\nexport default Logger\n", "/** A wrapper around a string that provides useful utility functions\n * not present in the standard library.\n *\n * @example\n *\n * const string = RichString.fromString(element.textContent)\n * element.textContent = string.humanize().toString()\n */\nclass RichString {\n /** Prefer this over the constructor, as this will\n * wrap `possiblyDefinedStringOrRichString` only if necessary\n * as well as handle `null`.\n *\n * @param {null|undefined|String|RichString} possiblyDefinedStringOrRichString - if `null`, `undefined`, or otherwise falsey, this method returns `null`. If a String, returns a new `RichString` wrapping it. If a `RichString`, returns the `RichString` unchanged.\n */\n static fromString(possiblyDefinedStringOrRichString, {allowBlank=false} = {}) {\n if (possiblyDefinedStringOrRichString instanceof RichString) {\n return possiblyDefinedStringOrRichString\n }\n if (allowBlank && possiblyDefinedStringOrRichString === \"\") {\n return new RichString(\"\")\n }\n if (!possiblyDefinedStringOrRichString) {\n return null\n }\n return new RichString(String(possiblyDefinedStringOrRichString))\n }\n\n /** Prefer `fromString` */\n constructor(string) {\n if (typeof string !== \"string\") {\n throw `You may only construct a RichString with a String, not a ${typeof string}`\n }\n this.string = string\n }\n\n /** Returns a `RichString` with the string capitalized. */\n capitalize() {\n return new RichString(this.string.charAt(0).toUpperCase() + this.string.slice(1))\n }\n\n /** Returns a `RichString` with the string un-capitalized. */\n decapitalize() {\n return new RichString(this.string.charAt(0).toLowerCase() + this.string.slice(1))\n }\n\n /** Returns a `RichString` with the string converted from snake or kebab case into camel case. */\n camelize() {\n // Taken from camelize npm module\n return RichString.fromString(this.string.replace(/[_.-](\\w|$)/g, function (_, x) {\n return x.toUpperCase()\n }))\n }\n\n /** Returns a 'humanized' `RichString`, which is basically a de-camelized version with the first letter\n * capitalized.\n */\n humanize() {\n return this.decamlize({spacer: \" \"}).capitalize()\n }\n\n /** Returns a `RichString` with the string converted from camel case to snake or kebab case.\n *\n * @param {Object} parameters\n * @param {string} parameters.spacer [\"_\"] - a string to use when joining words together.\n *\n */\n decamlize({spacer=\"_\"} = {}) {\n // Taken from decamelize NPM module\n\n // Checking the second character is done later on. Therefore process shorter strings here.\n if (this.string.length < 2) {\n return new RichString(this.string.toLowerCase())\n }\n\n const replacement = `$1${spacer}$2`\n\n // Split lowercase sequences followed by uppercase character.\n // `dataForUSACounties` \u2192 `data_For_USACounties`\n // `myURLstring \u2192 `my_URLstring`\n const decamelized = this.string.replace(\n /([\\p{Lowercase_Letter}\\d])(\\p{Uppercase_Letter})/gu,\n replacement,\n )\n\n // Split multiple uppercase characters followed by one or more lowercase characters.\n // `my_URLstring` \u2192 `my_ur_lstring`\n return new RichString(decamelized.\n replace(\n /(\\p{Uppercase_Letter})(\\p{Uppercase_Letter}\\p{Lowercase_Letter}+)/gu,\n replacement,\n ).\n toLowerCase()\n )\n }\n\n /** Return the underlying String value */\n toString() { return this.string }\n\n /** Return the underlying String value or null if the string is blank */\n toStringOrNull() {\n if (this.isBlank()) {\n return null\n }\n else {\n return this.string\n }\n }\n\n /* Returns true if this string has only whitespace in it */\n isBlank() {\n return this.string.trim() == \"\"\n }\n\n}\nexport default RichString\n", "import Logger from \"./Logger\"\nimport RichString from \"./RichString\"\n\n/** Base class for Custom Elements that provides a few quality-of-life enhancements.\n * \n * Custom elements that use this base class instead of `HTMLElement` get the following features:\n *\n * * `connectedCallback` and `attributeChangedCallback` call into a central `update` method where the \n * class can centralilize its logic\n * * Instead of implementing `attributeChangedCallback` and checking the name, per-attribute\n * callbacks can be implemented that are called when an observed attribute is changed. See {@link\n * attributeChangedCallback}.\n * * Support for defining the element by declaring a tag name\n * * Opt-in debugging support to allow verbose logging of mistaken use of the element that can be turned\n * off for production use.\n *\n * How to use this class:\n *\n * 1. Your custom element should extend this class via `extends BaseCustomElement`\n * 2. Create a static property called `tagName` that will be your element's tag name. Remember that all tag names must have a dash in them.\n * 3. Create a static property called `observedAttributes` that is an array of attribute names your element supports. This is part of the HTML spec and not specific to this base class.\n * 4. If you include the attribute `show-warnings` in your list of `observedAttributes`, you will have enhanced debugging abilities.\n * 5. For each attribute *other* than `show-warnings`, implement a callback to receive notifications on the attribute's changes. See {@link attributeChangedCallback} for more info.\n * 6. Implement `update` to execute whatever logic the component needs. `update` will be called multiple times and thus should be relatively idempotent. Specifically, it will be called after any attribute has changed, and it will be called as part of the standard `connectedCallback`.\n * 7. To use your component, call the static {@link define} method.\n *\n * Debugging\n *\n * Custom Elements have to work under a variety of degenerate cirucmstances. Further, if you are building \n * elements that wrap and enhance conventional elements, it can be easy to make a mistake, for example intending\n * to wrap a `FORM`, but wrapping only an `INPUT`.\n *\n * To help debug these situations, you are encouraged to use `this.logger.warn(...)` to emit warnings when\n * potentially incorrect use of your component is detected. By default, these warnings will not be shown. This\n * provides your users with a drama-free console. During development, however, you can add the `show-warnings`\n * attribute to your element. If that is set, warnings *are* shown in the console.\n *\n * `show-warnings` can be given a value, in which case that value if used to prefix all warnings the element emits.\n * This can be useful to know which use of an element is causing problems. If you don't give any value\n * to `show-warnings`, the element's `id` will be used as the prefix. If the element has no `id`, you will\n * still see warnings, but without a prefix. This could make it hard to know where the warnings are coming from.\n *\n * @example\n * // Replaces all span elements inside the component with\n * // an upper-cased value of the attribute 'some-attribute'\n * class MyComponent extends BaseCustomElement {\n * static tagName = \"my-component\"\n * static observedAttributes = [\n * \"show-warnings\",\n * \"some-attribute\",\n * ]\n *\n * someAttributeChangedCallback({newValue}) {\n * this.someAttribute = newValue ? newValue.toUpperCase() : null\n * }\n *\n * update() {\n * const spans = this.querySelectorAll(\"span\")\n * if (spans.length == 0) {\n * this.logger.warn(\"Did not find any <span> elements - element won't do anything\")\n * }\n * spans.forEach( (element) => {\n * element.textContent = this.someAttribute\n * })\n * }\n * }\n * docment.addEventListener(\"DOMContentLoaded\", () => {\n * MyComponent.define()\n * })\n *\n * // Then, in your HTML\n * <my-component some-attribute=\"hello there\">\n * <span></span>\n * <div></div>\n * <span></span>\n * </my-component>\n *\n * // The browser will effectively produce this HTML:\n * <my-component some-attribute=\"hello there\">\n * <span>HELLO THERE</span>\n * <div></div>\n * <span>HELLO THERE</span>\n * </my-component>\n *\n * // If JavaScript (or browser dev tools) changed some-attribute\n * // to be \"goodbye then\", the markup will change to look like so:\n * <my-component some-attribute=\"goodby then\">\n * <span>GOODBYE THEN</span>\n * <div></div>\n * <span>GOODBYE THEN</span>\n * </my-component>\n */\nclass BaseCustomElement extends HTMLElement {\n\n /** A {@link Logger} you can use to write warning messages. By default, these\n * messages are not shown in the console. If you put `show-warnings` as an attribute on your\n * element, warnings sent to this logger *are* shown.\n */\n logger = Logger.forPrefix(null)\n\n #_connectedCallbackCalled = false\n #_disconnectedCallbackCalled = false\n\n constructor() {\n super()\n this.logger = Logger.forPrefix(null)\n }\n\n /** You must call this to define the custom element. This is bascially\n * a wrapper around `customElements.define`. It is recommended that you call \n * this inside a `DOMContentLoaded` event, or after the page's HTML has been processed.\n * \n * @see external:CustomElementRegistry\n */\n static define() {\n if (!this.tagName) {\n throw `To use BaseCustomElement, you must define the static member tagName to return your custom tag's name`\n }\n customElements.define(this.tagName, this)\n }\n\n showWarningsChangedCallback({oldValue,newValue}) {\n let oldLogger\n if (!oldValue && newValue) {\n oldLogger = this.logger\n }\n let prefix = newValue == \"\" ? this.id : newValue\n if (!prefix) {\n prefix = \"UNKNOWN COMPONENT\"\n }\n this.logger = Logger.forPrefix(prefix)\n if (oldLogger) {\n this.logger.dump(oldLogger)\n }\n }\n\n /**\n * Overrides the standard callback to allow subclasses to have a slightly easier API when responding\n * to attribute changes. You can override this to use the custom element callback directly. Note that if\n * you do, `show-warnings` will not have any affect and you probably don't need to bother using\n * this class as your base class.\n *\n * This method will locate a per-attribute method and call that.\n * Attribute names are assumed to be in kebab-case and are translated to camelCase to create a method name.\n * That method is `\u00ABattributeInCamelCase\u00BBChangedCallback`, so if your attribute is `hex-code`,\n * a method named `hexCodeChangedCallback` in invoked. If no such method is defined, a\n * warning is logged in the console, regardless of the `show-warnings` attribute.\n *\n * The method is invoked with `{oldValue,newValue,newValueAsBoolean}` - i.e. an object and not positional parameters. This \n * means your implementation can omit any parameters it doesn't care about. `newValueAsBoolean` is not part of\n * the custom element spec, but is provided as an unambiguous way to know if a boolean attribute was set or not. This is\n * because if the value is set, it is likely to be the empty string, which is considered false by JavaScript. Cool.\n *\n * The return value of the method is ignored.\n *\n * After your method is called, if there is a method named `update`, it is called with no arguments.\n *\n * What this allows you to do is separate how you manage your element's attributes from how your logic\n * is managed. For complex elements that take a lot of attributes, this can simplify your element's code without straying too far from the spec.\n *\n * @example\n *\n * // If your element accepts the attribute `warning-message` that will be trimmed of whitespace\n * // then placed into all `H1` tags inside the element, you can manage that like so:\n * class MyElement extends BaseCustomElement {\n * static tagName = \"my-element\"\n * static observedAttributes = [\n * \"warning-message\",\n * ]\n *\n * // called by attributeChangedCallback when warning-message's value changes\n * warningMessageChangedCallback({newValue}) {\n * this.warningMessage = (newValue || \"\").trim()\n * }\n *\n * // called after attributeChangedCallback calls warningMessageChangedCallback\n * update() {\n * this.querySelectorAll(\"h1\").forEach( (e) => e.textContent = this.warningMessage )\n * }\n * }\n *\n *\n * @see {@link https://developer.mozilla.org/en-US/docs/Web/API/Web_components/Using_custom_elements}\n *\n */\n attributeChangedCallback(name,oldValue,newValue) {\n const callbackName = `${new RichString(name).camelize()}ChangedCallback`\n if (this[callbackName]) {\n const newValueAsBoolean = newValue !== null\n this[callbackName]({oldValue,newValue,newValueAsBoolean})\n }\n else if (this.constructor.observedAttributes.indexOf(name) != -1) {\n console.warn(\"Observing %s but no method named %s was found to handle it\",name,callbackName)\n }\n this.__update()\n }\n \n /** Overrides the custom element callback to set internal flags allowing you to know if your\n * element has been disconnected. When an element is disconnected, `update` is not called.\n *\n * If you want to add your own logic during disconnection, override {@link onDisconnected}.\n *\n * @see {@link https://developer.mozilla.org/en-US/docs/Web/API/Web_components/Using_custom_elements}\n */\n disconnectedCallback() {\n this.#_disconnectedCallbackCalled = true\n this.onDisconnected()\n }\n\n /** Override this to add logic when `disconnectedCallback` is called by the browser. This will\n * not be called if you overrode `disconnectedCallback`.\n */\n onDisconnected() {}\n\n /** Overrides the custom element callback to set internal flags allowing you to know if your\n * element has been connected. `update` is still called for elements that have not yet connected, however\n * in practice your element will be connected before any codepath that calls `update` is called.\n *\n * To add logic when your element is connected, override {@link onConnected}\n *\n * @see {@link https://developer.mozilla.org/en-US/docs/Web/API/Web_components/Using_custom_elements}\n * @see BaseCustomElement#connectedCallbackCalled\n */\n connectedCallback() {\n this.#_connectedCallbackCalled = true\n this.onConnected()\n this.__update()\n }\n\n /** Override this to add logic when `connectedCallback` is called by the browser. This will\n * not be called if you overrode `connectedCallback`\n */\n onConnected() {}\n\n /** Returns true if this element is connected and the connected callback has been called.\n * This is different from `Node#isConnected`, which can return true before `connectedCallback` is called.\n */\n get connectedCallbackCalled() { return !!this.#_connectedCallbackCalled }\n\n /** Override this to perform whatever logic your element must perform.\n * Because changes to your element's attributes can happen at any time and in any order,\n * you will want to consolidate all logic into one method\u2014this one. You will also\n * want to make sure that this method is idempotent and fault-tolerant. It will be called multiple times.\n *\n * It is called by {@link BaseCustomElement#attributeChangedCallback|attributeChangedCallback} and {@link BaseCustomElement#connectedCallback|connectedCallback}, however\n * it will *not* be called after the elment has been disconnected.\n *\n * That means that any event listeners, rendering, content manipulation, or other behavior should happen hear\n * and it *must* be idempotent. In particular, any event listeners you attach must be done with care. Using\n * anonymous functions could result in duplicate listeners.\n */\n update() {}\n\n __update() {\n if (this.#_disconnectedCallbackCalled) {\n return\n }\n this.update()\n }\n}\nexport default BaseCustomElement\n", "import BaseCustomElement from \"./BaseCustomElement\"\n\n/** Manages a translation based on a key and value, with the value potentially having interpolation.\n *\n * This is intended to be server-rendered with the subset of keys the server manages that are relevant\n * to the front-end. Any other code on the page can then locate an element with the desired key and\n * call `translation` to get the human-readable key. It is assumed that the server would render\n * in the language required by the visitor, so there is no need to first select by locale.\n *\n * @property {string} key - an i18n key, presumably dot-delimited, however it can be any valid attribute value.\n * @property {string} value - the value of the key, in the browser's locale. It may contain placeholders for interpolation using `%{\u00ABplaceholder\u00BB}` syntax.\n *\n * @example\n * <brut-i18n-translation key=\"greeting\" value=\"Hello %{username}\"></brut-i18n-translation>\n *\n * @customElement brut-i18n-translation\n */\nclass I18nTranslation extends BaseCustomElement {\n static tagName = \"brut-i18n-translation\"\n\n static observedAttributes = [\n \"show-warnings\",\n \"key\",\n \"value\",\n ]\n\n #key = null\n #value = \"\"\n\n keyChangedCallback({newValue}) {\n this.#key = newValue\n }\n\n valueChangedCallback({newValue}) {\n this.#value = newValue ? String(newValue) : \"\"\n }\n\n /**\n * Called by other JavaScript to get the translated string.\n * @param {Object} interpolatedValues - Object where the keys are placeholders in the string for interpolation and the values are\n * the values to replace. Placeholders not in the translated value are ignored. Missing placeholders won't cause an error, but the\n * placeholder will be present verbatim in the translated string.\n *\n * @example\n * const element = document.querySeletor(\"brut-i18n-translation[key='greeting']\")\n * if (element) {\n * const translation = element.translation({ username: \"Pat\" })\n * alert(translation) // Shows 'Hello Pat'\n * }\n */\n translation(interpolatedValues) {\n if (!this.#value) {\n this.logger.warn(\"No value attribute for key '%s', so translation will be blank\", this.#key)\n }\n return this.#value.replaceAll(/%\\{([^}%]+)\\}/g, (match,key) => {\n if (interpolatedValues[key]) {\n return interpolatedValues[key]\n }\n return match\n })\n }\n\n}\nexport default I18nTranslation\n", "import BaseCustomElement from \"./BaseCustomElement\"\nimport RichString from \"./RichString\"\nimport I18nTranslation from \"./I18nTranslation\"\n\n/** Like {@link Message} but specific to constraint violations of input fields. This accepts the name\n * of an input field via `input-name`, which can be used to locate the field's localized name.\n *\n * Here is how the field's name is determined:\n *\n * 1. It will look for a `<brut-i18n-translation>` element with the `key` `cv.cs.fieldNames.\u00ABinput-name\u00BB`.\n * 2. If that's not found, it will attempt to use \"this field\" by locating a `<brut-i18n-translation>` element with the `key`\n * `cv.this_field` (the underscore being what is used on Brut's server side).\n * 3. If that is not found, it will use the literaly string \"this field\" and emit a console warning.\n *\n * @property {string} key - the i18n translation key to use. It must map to the `key` of a `<brut-i18n-translation>` on the page or\n * the element will not render any text.\n * @property {string} input-name - the name of the input, used to insert into the message, e.g. \"Title is required\".\n * @property {boolean} server-generated if true, this indicates the element's HTML was generated on the server.\n * This means that your CSS can target it for display in all cases. If this is not present,\n * you may want to avoid showing this element if the form has not been submitted yet.\n * Does not affect behavior.\n * @property {boolean} server-side if true, this indicates the element contains constraint violation messages\n * from the server. Does not affect behavior.\n * @property {boolean} client-side if true, this indicates the element contains constraint violation messages\n * from the client, however they may have been generated from the server, since the server may\n * re-evaluate the client-side constraints. Does not affect behavior of this tag.\n *\n * @see I18nTranslation\n * @see ConstraintViolationMessages\n * @see Message\n *\n * @customElement brut-cv\n */\nclass ConstraintViolationMessage extends BaseCustomElement {\n static tagName = \"brut-cv\"\n\n static observedAttributes = [\n \"show-warnings\",\n \"key\",\n \"input-name\",\n \"server-side\",\n \"client-side\",\n \"server-generated\",\n ]\n\n static createElement(document,attributes) {\n const element = document.createElement(ConstraintViolationMessage.tagName)\n element.setAttribute(\"key\",this.i18nKey(\"cs\", attributes.key))\n element.setAttribute(\"input-name\",attributes[\"input-name\"])\n element.setAttribute(\"client-side\",\"\")\n if (Object.hasOwn(attributes,\"show-warnings\")) {\n element.setAttribute(\"show-warnings\",attributes[\"show-warnings\"])\n }\n return element\n }\n\n static markServerSide(element) {\n if (element.tagName.toLowerCase() == this.tagName) {\n element.setAttribute(\"server-side\", true)\n }\n }\n\n static clientSideSelector() {\n return `${this.tagName}:not([server-side])`\n }\n\n static serverSideSelector() {\n return `${this.tagName}[server-side]`\n }\n\n /** Returns the I18N key used for front-end constraint violations. This is useful\n * if you need to construct a key and want to follow Brut's conventions on how they\n * are managed.\n *\n * @param {...String} keyPath - parts of the path of the key after the namespace that Brut manages.\n */\n static i18nKey(...keyPath) {\n const path = [ \"cv\" ]\n return path.concat(keyPath).join(\".\")\n }\n\n #key = null\n #inputNameKey = null\n #thisFieldKey = this.#i18nKey(\"this_field\")\n\n keyChangedCallback({newValue}) {\n this.#key = newValue\n }\n\n inputNameChangedCallback({newValue}) {\n this.#inputNameKey = this.#i18nKey(\"cs\", \"fieldNames\", newValue)\n }\n\n serverSideChangedCallback({newValueAsBoolean}) {\n // attribute listed for documentation purposes only\n }\n clientSideChangedCallback({newValueAsBoolean}) {\n // attribute listed for documentation purposes only\n }\n serverGeneratedChangedCallback({newValueAsBoolean}) {\n // attribute listed for documentation purposes only\n }\n\n update() {\n if (!this.#key) {\n this.logger.info(\"No key attribute, so can't do anything\")\n return\n }\n\n const selector = `${I18nTranslation.tagName}[key='${this.#key}']`\n const translation = document.querySelector(selector)\n if (!translation) {\n this.logger.info(\"Could not find translation based on selector '%s'\",selector)\n return\n }\n\n const fieldNameSelector = `${I18nTranslation.tagName}[key='${this.#inputNameKey}']`\n const thisFieldSelector = `${I18nTranslation.tagName}[key='${this.#thisFieldKey}']`\n\n let fieldNameTranslation = document.querySelector(fieldNameSelector)\n if (!fieldNameTranslation) {\n this.logger.info(\"Could not find translation for input/field name based on selector '%s'. Will try 'this field' fallback\",fieldNameSelector)\n fieldNameTranslation = document.querySelector(thisFieldSelector)\n if (!fieldNameTranslation) {\n this.logger.info(\"Could not find translation for 'this field' fallback key, based on selector '%s'\",thisFieldSelector)\n }\n }\n\n const fieldName = fieldNameTranslation ? fieldNameTranslation.translation() : \"this field\"\n this.textContent = RichString.fromString(translation.translation({ field: fieldName })).capitalize().toString()\n }\n\n /** Helper that calls the static version */\n #i18nKey(...keyPath) {\n return this.constructor.i18nKey(...keyPath)\n }\n\n\n}\nexport default ConstraintViolationMessage\n", "import BaseCustomElement from \"./BaseCustomElement\"\nimport RichString from \"./RichString\"\nimport ConstraintViolationMessage from \"./ConstraintViolationMessage\"\n\n/**\n * Custom element to translate keys from {@link external:ValidityState} into \n * actual messges for a human. This works by inserting `<brut-cv>` elements\n * as children, where the key represents the particular errors present in the `ValidityState` passed\n * to `createMessages`. Note that this will not insert an element for `customError`, since there can't be\n * any sort of general error message to correspond to that.\n *\n * @property {string} input-name if set, this indicates this element contains constraint violation messages\n * for the input with this name inside the form this element is in. Currently doesn't affect\n * this element's behavior, however AjaxSubmit will use it to locate where it \n * should insert server-side errors.\n *\n * @see Form\n * @see ConstraintViolationMessage\n * @see AjaxSubmit\n *\n * @customElement brut-cv-messages\n */\nclass ConstraintViolationMessages extends BaseCustomElement {\n static tagName = \"brut-cv-messages\"\n\n static observedAttributes = [\n \"show-warnings\",\n \"input-name\",\n ]\n\n inputNameChangedCallback({newValue}) {\n // attribute listed for documentation purposes only\n }\n\n /** \n * Creates error messages based on the passed `ValidityState` and input name.\n *\n * This should be called as part of a Form validation event to provide a customized UX for\n * the error messages, beyond what the browser would do by default. The keys used are the same\n * as the attributes of a `ValidityState`, so for example, a range underflow would mean that `validity.rangeUnderflow` would return\n * true. Thus, a `<brut-cv>` would be created with `key=\"cv.cs.rangeUnderflow\"`.\n *\n * The `cv.cs` is hard-coded to be consistent with Brut's server-side translation management.\n *\n * @param {ValidityState} validityState - the return from an element's `validity` when it's found to have constraint violations.\n * @param {String} inputName - the element's `name`.\n */\n createMessages({validityState,inputName}) {\n const errors = this.#VALIDITY_STATE_ATTRIBUTES.filter( (attribute) => validityState[attribute] )\n this.clearClientSideMessages()\n errors.forEach( (key) => {\n const options = {\n key: key,\n \"input-name\": inputName,\n }\n const showWarnings = this.getAttribute(\"show-warnings\")\n if (showWarnings != null) {\n options[\"show-warnings\"] = showWarnings\n }\n const element = ConstraintViolationMessage.createElement(document,options)\n this.appendChild(element)\n })\n }\n\n /**\n * Clear any client-side messages previously inserted by another element.\n * This is useful to remove potentially out-of-date messages to replace with up-to-date ones.\n */\n clearClientSideMessages() {\n this.querySelectorAll(ConstraintViolationMessage.clientSideSelector()).forEach( (element) => {\n this.removeChild(element)\n })\n }\n\n /**\n * Clear any server-side messages previously inserted by another element or rendered from the server.\n * This is useful to remove potentially out-of-date messages to replace with up-to-date ones.\n */\n clearServerSideMessages() {\n this.querySelectorAll(ConstraintViolationMessage.serverSideSelector()).forEach( (element) => {\n this.removeChild(element)\n })\n }\n\n #VALIDITY_STATE_ATTRIBUTES = [\n \"badInput\",\n \"patternMismatch\",\n \"rangeOverflow\",\n \"rangeUnderflow\",\n \"stepMismatch\",\n \"tooLong\",\n \"tooShort\",\n \"typeMismatch\",\n \"valueMissing\",\n // customError omitted, since it makes no sense as a general error key to look up\n ]\n}\nexport default ConstraintViolationMessages\n", "import BaseCustomElement from \"./BaseCustomElement\"\nimport ConstraintViolationMessages from \"./ConstraintViolationMessages\"\nimport ConstraintViolationMessage from \"./ConstraintViolationMessage\"\n\n/** Wraps a `<BUTTON>` assumed to be inside a form to indicate that, when clicked, it should submit\n * the form it's a part of via AJAX. It accounts for network failures and timeouts.\n *\n * The general flow is as follows:\n *\n * 1. When the button is clicked, the form's validity is checked. If it's not valid, nothing happens.\n * 2. If the form is valid, this element will be given the `requesting` attribute.\n * 3. The request will be initiated, set to abort after `request-timeout` ms as well\n * as to abort on an externally-provided AbortSignal (see below).\n * The data submitted will be the contents of `new FormData(form)`, along with the \n * name/value of the button that was clicked, if it has a name and value.\n * 4. If the request returns OK:\n * - `requesting` will be removed and `submitted` will be added.\n * - `submitted` will be removed after `submitted-lifetime` ms.\n * - the `brut:submitok` event will be fired with the response text, **parsed as HTML**, as `event.detail`.\n * 5. If the request returned a 422:\n * - If you have set `no-server-side-error-parsing`, the results will be included in the \n * detail field of the `brut:submitinvalid` event.\n * - If you have NOT set `no-server-side-error-parsing`, the response is parsed as\n * errors to be inserted into the DOM. See below for how that works. In this case,\n * `brut:submitinvalid`'s detail bill be null.\n * 6. If the request returns not OK and not 422:\n * - if it has been `request-timeout` ms or more since the button was first clicked, the operation is aborted (see below).\n * - if it has been less than `request-timeout` ms and the HTTP status code was 5xx, the operation is retried.\n * - otherwise, the operation is aborted.\n * 7. If fetch throws an error, the operation is aborted.\n *\n * ## 422 Responses\n *\n * For a 422 response (where `no-server-side-error-parsing` is *not* set),\n * this element assumes the response is `text/html` and contains one or more `<brut-cv>`\n * elements. These elements will be inserted into the proper `<brut-cv-messages>` element, as follows:\n *\n * 1. The `input-name` is examined.\n * 2. A `<brut-cv-messages input-name=\"\u00ABinput-name\u00BB\">` is located\n * 3. The containing form is located\n * 4. The input element(s) are located inside that form, based on `input-name`.\n * 5. The `<brut-cv-messages>` are cleared of any element with attribute `server-side`\n * 6. The messages from the server are inserted, with the attribute `server-side` added if it's not there.\n * 7. The input is set as having a custom validity\n * 8. validity is reported\n * 9. The first input located is scrolled into view\n * 10. If the input is modified after this all happens, custom validity is cleared\n *\n * For the server you are contacting, this element has a few requirements:\n *\n * - If everything is OK/the operation did what it was intended to do:\n * - the server will respond with a 2xx\n * - the response body, if it contains anything, be `text/html` (this is provided in the event detail)\n * - If there are server-side constraint violations.\n * - the server will return 422\n * - the response body will be `text/html`\n * - the response body will contain one or more `<brut-cv>` elements\n *\n * ## Aborting the `fetch` Request\n *\n * By default, the call to `fetch` will be aborted after the value of `request-timeout` ms (default is 5,000).\n * When this happens, the **form is submitted through the browser** without Ajax. This is currently\n * not configurable.\n *\n * You *can* set an additional `AbortSignal` by setting the `abortSignal` property. When this is done, then\n * either a timeout or your custom abort will abort the request and **submit the form through the browser**.\n *\n * For your custom abort signal, you can prevent browser submission by providing a reason\n * with the value `AjaxSubmit.doNotSubmitThroughBrowser`. This can be useful when you are debouncing\n * requests. See the examples. You will also find it useful to set `log-request-errors` on the element\n * so you can see what is happening.\n *\n * @property {boolean} no-server-side-error-parsing - if set, the response body for a 422 will not be parsed and inserted into the DOM. Instead, the body will be part of the detail of the `brut:submitinvalid` event.\n * @property {number} request-timeout - number of ms that the entire operation is expected to complete within. Default is 5000\n * @property {number} submitted-lifetime - number of ms that \"submitted\" should remain on the element after the form has completed. Default is 2000\n * @property {boolean} requesting - boolean attribute that indicates the request has been made, but not yet returned. Don't set this yourself outside of development. It will be set and removed by this element.\n * @property {boolean} submitted - boolean attribute that indicates the form has been successfully submitted. Don't set this yourselr outside of develoment. It will be set and removed by this element.\n * @property {boolean} log-request-errors - if set, logging related to request error handling will appear in the console. It will also\n * cause any form submission to be delayed by 2s to allow you to read the console.\n *\n * @fires brut:submitok Fired when the AJAX request initated by this returns OK and all processing has completed. The detail will include the *parsed document* of the HTML returned in the response.\n * @fires brut:submitinvalid Fired when the AJAX request initated by this returns a 422 and all logic around managing the reponse has completed. The detail will be null unless `no-server-side-error-parsing` is set, in which case it will be the parsed document of the HTML returned in the response.\n *\n * @example <caption>Typical use</caption>\n * <form action=\"/widgets\" method=\"post\">\n * <input type=\"text\" name=\"name\">\n *\n * <brut-ajax-submit>\n * <button name=\"button\" value=\"save\">Save</button>\n * </brut-ajax-submit>\n * <brut-ajax-submit>\n * <button name=\"button\" value=\"analyze\">Analyze</button>\n * </brut-ajax-submit>\n * </form>\n * <!-- When \"Save\" is clicked, \"name\" will have the value from the text field,\n * and \"button\" will have the value \"save\".\n * When \"Analyze\" is clicked, \"name\" will have the value from the text\n * field, and \"button\" will have the value \"analyze\". -->\n *\n * @example <caption>Using a custom abort signal</caption>\n * const ajaxSubmit = document.querySelector(\"brut-ajax-submit\")\n * const controller = new AbortController()\n * ajaxSubmit.abortSignal = controller.signal\n * // later, when you want to abort the request\n * controller.abort(AjaxSubmit.doNotSubmitThroughBrowser)\n *\n * @customelement brut-ajax-submit\n */\nclass AjaxSubmit extends BaseCustomElement {\n static tagName = \"brut-ajax-submit\"\n static observedAttributes = [\n \"show-warnings\",\n \"requesting\",\n \"submitted\",\n \"submitted-lifetime\",\n \"request-timeout\",\n \"max-retry-attempts\",\n \"log-request-errors\",\n \"no-server-side-error-parsing\",\n ]\n\n /* Use this when calling abort() to indicate that the form \n * should be sub submitted through the browser.\n */\n static doNotSubmitThroughBrowser = \"doNotSubmitThroughBrowser\"\n\n #requestErrorLogger = () => {}\n #formSubmitDelay = 0\n #submittedLifetime = 2000\n #requestTimeout = 5000\n #maxRetryAttempts = 25\n #serverSideErrorParsing = true\n #abortSignal = null\n\n /* Set an additional abort signal to be used when\n * the form is submitted via Ajax. This allows you to \n * control the fetch request beyond the built-in timeout.\n *\n * @param {AbortSignal} value the AbortSignal to add to the fetch request.\n */\n set abortSignal(value) {\n this.#abortSignal = value\n }\n\n constructor() {\n super()\n this.domParser = new DOMParser()\n }\n\n submittedLifetimeChangedCallback({newValue}) {\n const newValueAsInt = parseInt(newValue)\n if (isNaN(newValueAsInt)) {\n throw `submitted-lifetime must be a number, not '${newValue}'`\n }\n this.#submittedLifetime = newValueAsInt\n }\n\n noServerSideErrorParsingChangedCallback({newValueAsBoolean}) {\n this.#serverSideErrorParsing = !newValueAsBoolean\n }\n\n maxRetryAttemptsChangedCallback({newValue}) {\n const num = parseInt(newValue)\n if (isNaN(num)) {\n this.logger.warn(`max-retry-attempts '${newValue}' is not a number. Using 1 as a fallback`)\n this.#maxRetryAttempts = 1\n }\n else {\n this.#maxRetryAttempts = num\n }\n }\n\n requestTimeoutChangedCallback({newValue}) {\n const newValueAsInt = parseInt(newValue)\n if (isNaN(newValueAsInt)) {\n throw `request-timeout must be a number, not '${newValue}'`\n }\n this.#requestTimeout = newValueAsInt\n }\n\n submittedChangedCallback({newValueAsBoolean}) {\n // no op\n }\n\n requestingChangedCallback({newValueAsBoolean}) {\n if (this.#button()) {\n if (newValueAsBoolean) {\n this.#button().setAttribute(\"disabled\",true)\n }\n else {\n this.#button().removeAttribute(\"disabled\",true)\n }\n }\n }\n\n logRequestErrorsChangedCallback({newValueAsBoolean}) {\n if (newValueAsBoolean) {\n this.#requestErrorLogger = console.warn\n this.#formSubmitDelay = 2000\n }\n else {\n this.#requestErrorLogger = () => {}\n this.#formSubmitDelay = 0\n }\n }\n\n update() {\n const button = this.#button()\n if (!button)\n {\n this.logger.info(\"Could not find a <button> to attach behavior to\")\n return\n }\n const form = button.form\n if (!form) {\n this.logger.info(\"%o did not have a form associated with it - cannot attach behavior\",button)\n return\n }\n button.form.addEventListener(\"submit\",this.#formSubmitted)\n }\n\n\n #formSubmitted = (event) => {\n const submitter = event.submitter\n if (submitter == this.#button()) {\n event.preventDefault()\n const now = Date.now()\n this.#submitForm(event.target, event.submitter, now, 0)\n }\n }\n\n #submitForm(form, submitter, firstSubmittedAt, numAttempts) {\n\n const headers = new Headers()\n headers.append(\"X-Requested-With\",\"XMLHttpRequest\")\n headers.append(\"Content-Type\",\"application/x-www-form-urlencoded\")\n\n const formData = new FormData(form)\n if (submitter && submitter.name) {\n formData.append(submitter.name, submitter.value)\n }\n const urlSearchParams = new URLSearchParams(formData)\n\n const signals = [\n AbortSignal.timeout(this.#requestTimeout),\n ]\n if (this.#abortSignal) {\n signals.push(this.#abortSignal)\n }\n const abortSignals = AbortSignal.any(signals)\n\n let url = form.action\n let body = null\n\n if (form.method.toLowerCase() == \"get\") {\n const sep = url.includes(\"?\") ? \"&\" : \"?\"\n url += sep + urlSearchParams.toString()\n }\n else {\n body = urlSearchParams\n }\n\n const request = new Request(\n url,\n {\n headers: headers,\n method: form.method,\n body: body,\n signal: abortSignals,\n }\n )\n\n\n if (numAttempts > this.#maxRetryAttempts) {\n this.#requestErrorLogger(\"%d attempts. Giving up\",numAttempts)\n this.#submitFormThroughBrowser(form)\n return\n }\n this.setAttribute(\"requesting\", true)\n fetch(request).then( (response) => {\n if (response.ok) {\n this.removeAttribute(\"requesting\")\n this.setAttribute(\"submitted\",true)\n\n setTimeout( () => this.removeAttribute(\"submitted\"), this.#submittedLifetime )\n response.text().then( (text) => {\n const parsedDocument = this.domParser.parseFromString(text,\"text/html\")\n this.dispatchEvent(new CustomEvent(\"brut:submitok\", { detail: parsedDocument }))\n })\n }\n else {\n\n let retry = false // if true, we retry the request via ajax\n let resubmit = false // if true, and we aren't retrying, we submit the\n // form the old fashioned way\n\n if ( (Date.now() - firstSubmittedAt) > this.#requestTimeout) {\n this.#requestErrorLogger(\"Since initial button press %d, it's taken more than %d ms to get a response.\",firstSubmittedAt,this.#requestTimeout)\n retry = false\n resubmit = true\n }\n else {\n const status = parseInt(response.status)\n if (isNaN(status)) {\n this.#requestErrorLogger(\"Got unparseable status: %d\",response.status)\n retry = false\n }\n else if (status >= 500) {\n this.#requestErrorLogger(\"Got a %d, maybe retry will fix\", status)\n retry = true\n }\n else {\n retry = false\n if (status == 422) {\n this.#handleConstraintViolations(response)\n }\n }\n }\n if (retry) {\n this.#requestErrorLogger(\"Trying again (attempt %d)\",numAttempts +1)\n setTimeout( () => this.#submitForm(form, submitter, firstSubmittedAt, numAttempts + 1), numAttempts * 10)\n }\n else if (resubmit) {\n this.#requestErrorLogger(\"'retry' was marked false, but resubmit is 'true', so submitting through browser\")\n this.#submitFormThroughBrowser(form)\n this.removeAttribute(\"requesting\")\n }\n }\n }).catch( (error) => {\n if (error == AjaxSubmit.doNotSubmitThroughBrowser) {\n this.#requestErrorLogger(\"Error indicates we should not submit through browser: %o\",error)\n }\n else {\n this.#requestErrorLogger(\"Got %o, which cannot be retried\",error)\n this.#submitFormThroughBrowser(form)\n }\n })\n }\n\n #button = () => {\n let button = this.querySelector(\"button\")\n if (!button) {\n button = this.querySelector(\"input[type='submit']\")\n }\n return button\n }\n\n\n #submitFormThroughBrowser(form) {\n form.removeEventListener(\"submit\",this.#formSubmitted)\n if (this.#formSubmitDelay > 0) {\n console.log(\"Form submission has been delayed by %d ms in order to allow examining the log\",this.#formSubmitDelay)\n setTimeout( () => form.requestSubmit(this.#button()), this.#formSubmitDelay)\n }\n else {\n form.requestSubmit(this.#button())\n }\n\n }\n\n #handleConstraintViolations(response) {\n let resubmit = false\n response.text().then( (text) => {\n const parsedDocument = this.domParser.parseFromString(text,\"text/html\")\n let event\n if (this.#serverSideErrorParsing) {\n event = new CustomEvent(\"brut:submitinvalid\")\n }\n else {\n event = new CustomEvent(\"brut:submitinvalid\", { detail: parsedDocument })\n }\n this.dispatchEvent(event)\n if (this.#serverSideErrorParsing) {\n const constraintViolationNodes = parsedDocument.querySelectorAll(ConstraintViolationMessage.tagName)\n try {\n const inputsToMessages = ErrorMessagesForInput.mapInputsToErrorMessages(\n constraintViolationNodes,\n this.#requestErrorLogger\n )\n\n let inputToScrollToAfterReportingValidity\n for (const [inputName, {input, messagesElement, errorMessages}] of Object.entries(inputsToMessages)) {\n if (!inputToScrollToAfterReportingValidity) {\n inputToScrollToAfterReportingValidity = input\n }\n messagesElement.clearServerSideMessages()\n errorMessages.forEach( (element) => {\n ConstraintViolationMessage.markServerSide(element)\n messagesElement.appendChild(element) \n })\n this.#setCustomValidityThatClearsOnChange(input,errorMessages)\n }\n\n if (inputToScrollToAfterReportingValidity) {\n inputToScrollToAfterReportingValidity.scrollIntoView()\n }\n resubmit = false\n this.removeAttribute(\"requesting\")\n }\n catch (e) {\n this.#requestErrorLogger(\"While parsing %s, got %s\", text, e)\n resubmit = true\n }\n if (resubmit) {\n this.#submitFormThroughBrowser(form)\n }\n }\n })\n }\n\n #setCustomValidityThatClearsOnChange(input,errorMessages) {\n input.setCustomValidity(errorMessages[0].textContent)\n input.reportValidity()\n input.addEventListener(\"change\", () => input.setCustomValidity(\"\") )\n }\n}\n\nclass ErrorMessagesForInput {\n static mapInputsToErrorMessages(errorMessages,requestErrorLogger) {\n const errorMessagesForInputs = Array.from(errorMessages).map( (element) => {\n return new ErrorMessagesForInput({\n element: element,\n inputName: element.getAttribute(\"input-name\"),\n document: document,\n })\n })\n\n const inputsToMessages = {}\n\n errorMessagesForInputs.forEach( (errorMessagesForInput) => {\n if (errorMessagesForInput.allElementsFound()) {\n\n if (!inputsToMessages[errorMessagesForInput.inputName]) {\n inputsToMessages[errorMessagesForInput.inputName] = {\n input: errorMessagesForInput.input,\n messagesElement: errorMessagesForInput.messagesElement,\n errorMessages: []\n }\n }\n\n inputsToMessages[errorMessagesForInput.inputName].errorMessages.push(\n errorMessagesForInput.element\n )\n }\n else {\n requestErrorLogger(\n \"Server message %o could not be shown to the user: %s\",\n errorMessagesForInput.element,\n errorMessagesForInput.reasonNotAllElementsFound()\n )\n }\n })\n\n return inputsToMessages\n }\n\n constructor({element,inputName,document}) {\n this.element = element\n this.inputName = inputName\n\n if (this.inputName) {\n const selector = `${ConstraintViolationMessages.tagName}[input-name='${this.inputName}']`\n this.messagesElement = document.querySelector(selector)\n if (this.messagesElement) {\n this.closestForm = this.messagesElement.closest(\"form\")\n }\n if (this.inputName && this.closestForm) {\n this.input = this.closestForm.elements.namedItem(this.inputName)\n }\n }\n }\n\n allElementsFound() {\n return !!this.input\n }\n\n reasonNotAllElementsFound() {\n let reason\n if (this.inputName) {\n if (this.messagesElement) {\n if (this.closestForm) {\n reason = `Form did not contain an input named ${this.inputName}`\n }\n else {\n reason = `Could not find a form that contained the ${ConstraintViolationMessages.tagName} element` \n }\n }\n else {\n reason = `Could not find a ${ConstraintViolationMessages.tagName} element for ${this.inputName}`\n }\n }\n else {\n reason = \"server message was missing an input-name\"\n }\n return reason\n }\n}\n\nexport default AjaxSubmit\n", "import BaseCustomElement from \"./BaseCustomElement\"\n\n/** When the value of the wrapped form element(s) fire(s) a change event, the form is submitted.\n * This will only work if it is inside a form *and* the form elements it contains are part of the form.\n * That means if your input/textarea/select uses the `form` attribute, this custom element has no effect.\n *\n * @example\n *\n * <form>\n * <brut-autosubmit>\n * <!-- when a selection is made, the form is submitted -->\n * <select name=\"status\">\n * <option value=\"draft\">Draft</option>\n * <option value=\"ready\">Ready</option>\n * <option value=\"published\">Published</option>\n * </select>\n * </brut-autosubmit>\n * <!-- when the value is changed, form is NOT submitted -->\n * <input type=\"text\" value=\"notes\">\n * <button>Save</button>\n * </form>\n *\n * @customElement brut-autosubmit\n */\nclass Autosubmit extends BaseCustomElement {\n static tagName = \"brut-autosubmit\"\n\n static observedAttributes = [\n \"show-warnings\",\n ]\n\n #submitForm = (event) => {\n const form = this.closest(\"form\")\n if (!form) {\n this.logger.info(\"No longer a form containing this element\")\n return\n }\n if (event.target.form != form) {\n this.logger.info(\"Event target %o's form is not the form that contains this element\",event.target)\n return\n }\n form.requestSubmit()\n }\n\n update() {\n const form = this.closest(\"form\")\n if (!form) {\n this.logger.info(\"No form containing this element - nothing to autosubmit\")\n return\n }\n const inputs = Array.from(this.querySelectorAll(\"input, textarea, select\")).filter( (element) => {\n return element.form == form\n })\n if (inputs.length == 0) {\n this.logger.info(\"No input, textarea, or select inside this element belongs to the form containing this element\")\n return\n }\n inputs.forEach( (input) => {\n input.addEventListener(\"change\", this.#submitForm)\n })\n }\n}\nexport default Autosubmit\n", "import BaseCustomElement from \"./BaseCustomElement\"\nimport RichString from \"./RichString\"\n\n/**\n * Enhances a `DIALOG` element to allow it to be used for confirmation. Mostly useful\n * with {@link Confirm}.\n *\n * This element must:\n *\n * * Wrap a `<DIALOG>` element\n * * Contain an `<H1>` where the message will go\n * * Have a `<BUTTON>` with value `ok` that will be considered the confirmation button.\n * * Have a `<BUTTON>` with value `cancel` that will be considered the denial button.\n *\n * Omitting these will cause this element to not work properly. Set `show-warnings` to see\n * warnings on this.\n *\n * @property {string} message - the message to use to ask for confirmation\n * @property {string} confirm-label - the label to use for the \"OK\" or \"Confirm\" button\n *\n * @example <caption>Minimal Example</caption>\n * <brut-confirmation-dialog message=\"This cannot be undone\" confirm-label=\"DOIT\">\n * <dialog>\n * <h1></h1>\n * <button value=\"ok\"></button>\n * <button value=\"cancel\">Nevermind</button>\n * </dialog>\n * </brut-confirmation-dialog>\n *\n * @customElement brut-confirmation-dialog\n */\nclass ConfirmationDialog extends BaseCustomElement {\n static tagName = \"brut-confirmation-dialog\"\n static observedAttributes = [\n \"message\",\n \"confirm-label\",\n \"show-warnings\"\n ]\n\n #onClose = () => {}\n #message = new RichString(\"\")\n #confirmLabel = new RichString(\"OK\")\n\n constructor() {\n super()\n this.okListener = (event) => {\n this.#closeDialog()\n this.#onClose(true)\n }\n this.cancelListener = (event) => {\n this.#closeDialog()\n this.#onClose(false)\n }\n }\n\n messageChangedCallback({newValue}) {\n this.#message = RichString.fromString(newValue)\n }\n\n confirmLabelChangedCallback({newValue}) {\n this.#confirmLabel = RichString.fromString(newValue)\n }\n\n /**\n * Call this to show the dialog. When the dialog is closed, `onClose` is called with the result.\n *\n * @param {function} onClose - a function called with either `true` or `false`, if the dialog was confirmed or \n * denied, respectively.\n *\n * @example\n * dialog.showModal( (confirmed) => {\n * if (confirmed) {\n * form.submit()\n * }\n * else {\n * // do nothing\n * }\n * })\n */\n showModal(onClose) {\n const dialog = this.#dialog\n if (dialog) {\n this.#onClose = onClose || (() => {})\n dialog.showModal()\n }\n else {\n this.logger.warn(\"No <dialog> found to show\")\n }\n }\n\n get #dialog() {\n return this.querySelector(\"dialog\")\n }\n\n #closeDialog() {\n const dialog = this.#dialog\n if (dialog) {\n dialog.close()\n }\n }\n\n update() {\n const dialog = this.#dialog\n if (!dialog) {\n this.logger.warn(\"Could not find a <dialog> - this custom element won't do anything\")\n return\n }\n this.#setMessage(dialog)\n this.#setupButtons()\n }\n\n #setMessage(dialog) {\n const h1 = dialog.querySelector(\"h1\")\n if (h1) {\n if (this.#message.isBlank()) {\n h1.textContent = null\n }\n else {\n h1.textContent = this.#message.toString()\n }\n }\n else {\n this.logger.warn(\"Dialog had no <h1>, so nowhere to put the message\")\n }\n }\n\n #setupButtons() {\n const okButton = this.querySelector(\"button[value='ok']\")\n const cancelButton = this.querySelector(\"button[value='cancel']\")\n\n if (!okButton || !cancelButton) {\n if (!okButton) { this.logger.warn(\"no <button value='ok'> which is required for this dialog to work\") }\n if (!cancelButton) { this.logger.warn(\"no <button value='cancel'> which is required for this dialog to work\") }\n return\n }\n\n okButton.textContent = this.#confirmLabel\n\n okButton.addEventListener(\"click\" , this.okListener)\n cancelButton.addEventListener(\"click\" , this.cancelListener)\n }\n}\nexport default ConfirmationDialog\n", "import BaseCustomElement from \"./BaseCustomElement\"\nimport RichString from \"./RichString\"\nimport ConfirmationDialog from \"./ConfirmationDialog\"\n\n/** Confirms form submissions with the user before allowing the form to be submitted. This is applied\n * to buttons, not forms, to allow for finer control over the behavior.\n *\n * * This only works for `button` or `input type=submit`\n * * The elements must have a form, either by being inside a form or having\n * a `form` attribute.\n * * If the form is not valid, it will not show the confirmation dialog.\n *\n *\n * By default, this will use {@link external:Window#confirm}. if \"OK\" is pressed,\n * the button click goes through and the form would be submitted. If \"Cancel\" is pressed,\n * the event is prevented.\n *\n * If there is a `brut-confirmation-dialog` on the page, this component can use that, possibly\n * with help from the `dialog` attribute as followed:\n *\n * * If `dialog` is set:\n * - If that id is on a `<brut-confirmation-dialog>` that is used.\n * - If not, `window.confirm` is used.\n * * If `dialog` is not set:\n * - If there is exactly one `<brut-confirmation-dialog>` on the page, this is used.\n * - If there is more than one, or no `<brut-confirmation-dialog>`s, `window.confirm` is used.\n *\n * If the wrong dialog or notification method is happening, set `show-warnings` on the element, and it will\n * print out why it's doing what it's doing.\n *\n * @see ConfirmationDialog\n *\n * @property {string} message - the message to show that asks for confirmation. It should be written such that\n * \"OK\" is grammatically correct for confirmation and \"Cancel\" is for aborting.\n * @property {string} dialog - optional ID of the `brut-confirmation-dialog` to use instead of `window.confirm`.\n * If there is no such dialog or the id references the wrong element type,\n * `window.confirm` will be used. Setting `show-warnings` will generate a warning for this.\n *\n * @customElement brut-confirm-submit\n */\nclass ConfirmSubmit extends BaseCustomElement {\n static tagName = \"brut-confirm-submit\"\n\n static observedAttributes = [\n \"message\",\n \"dialog\",\n \"show-warnings\",\n ]\n\n #message = new RichString(\"\")\n #confirming = false\n #dialogId = null\n\n messageChangedCallback({newValue}) {\n this.#message = new RichString(newValue || \"\")\n }\n\n dialogChangedCallback({newValue}) {\n this.#dialogId = RichString.fromString(newValue)\n }\n\n constructor() {\n super()\n this.onClick = (event) => {\n if (this.#confirming) {\n this.#confirming = false\n return\n }\n if (this.#message.isBlank()) {\n this.logger.warn(\"No message provided, so cannot confirm\")\n return\n }\n const form = event.currentTarget.form\n\n if (!form) {\n this.logger.warn(\"Element was not part of a form, so cannot confirm submission\")\n return\n }\n\n if (!form.checkValidity()) {\n return\n }\n\n const dialog = this.#findDialog()\n if (dialog) {\n event.preventDefault()\n dialog.setAttribute(\"message\",this.#message.toString())\n const buttonLabel = event.target.getAttribute(\"aria-label\") || event.target.textContent\n dialog.setAttribute(\"confirm-label\",buttonLabel)\n this.#confirming = true\n dialog.showModal((confirm) => {\n if (confirm) {\n event.target.click()\n }\n else {\n this.#confirming = false\n }\n })\n }\n else {\n const result = window.confirm(this.#message)\n if (!result) {\n event.preventDefault()\n }\n }\n }\n }\n\n #findDialog() {\n if (this.#dialogId) {\n const dialog = document.getElementById(this.#dialogId)\n if (dialog) {\n if (dialog.tagName.toLowerCase() != ConfirmationDialog.tagName) {\n throw `${this.#dialogId} is the id of a '${dialog.tagName}', not '${ConfirmationDialog.tagName}'`\n }\n return dialog\n }\n this.logger.warn(`No dialog with id ${this.#dialogId} - using window.confirm as a fallback`)\n return null\n }\n const dialogs = document.querySelectorAll(ConfirmationDialog.tagName)\n if (dialogs.length == 1) {\n return dialogs[0]\n }\n if (dialogs.length == 0) {\n this.logger.warn(`No '${ConfirmationDialog.tagName}' found in document - using window.confirm as a fallback`)\n return null\n }\n throw `Found ${dialogs.length} '${ConfirmationDialog.tagName}' elements. Not sure which to use. Remove all but one or specify the 'dialog' attribute on this element to specify which one to use`\n }\n\n update() {\n this.querySelectorAll(\"button\").forEach( (button) => button.addEventListener(\"click\", this.onClick) )\n this.querySelectorAll(\"input[type=submit]\").forEach( (button) => button.addEventListener(\"click\", this.onClick) )\n }\n}\nexport default ConfirmSubmit\n", "import BaseCustomElement from \"./BaseCustomElement\"\n\n/** Wraps a `<BUTTON>` that will copy text from another element into the system clipboard. It will set various attributes on itself\n * to allow styling the states of the process.\n *\n * Overall, the flow is as follows:\n *\n * 1. When the button is clicked, its default is prevented, and the element with the id of the `element` attribute is located.\n * 2. If found, this element gets the `copying` attribute set.\n * 3. If the copy completes successfully:\n * a. `copying` is removed\n * b. `copied` is set\n * c. `copied` is scheduled for removal in 2000ms or the number of milliseconds in the `copied-lifetime` attribute.\n * 4. If the copy failed:\n * a. `copying` is removed\n * b. `errored` is set\n * c. The `brut:copyfailed` event is fired. It's detail contains a `text:` value with the text that was attempted to be copied.\n *\n * The intention is to use these attributes to style whatever UX you want.\n *\n * @property {string} element - ID of the element whose `textContent` is what will be copied to the clipboard.\n * @property {number} copied-lifetime - number of milliseconds to wait before clearing the `copied` attribute after a successful copy.\n * @property {boolean} copying - Set after a copy is initiated, but before it completes\n * @property {boolean} copied - Set after a copy is completed successfully\n * @property {boolean} errored - Set after a copy is fails\n *\n * @fires brut:copyfailed Fired when the copy fails to complete\n *\n * @example\n * <pre><code id=\"code\">dx/exec bin/setup</code></pre>\n * <brut-copy-to-clipboard element=\"code\">\n * <button>Copy</button>\n * </brut-copy-to-clipboard>\n *\n * @customElement brut-copy-to-clipboard\n */\nclass CopyToClipboard extends BaseCustomElement {\n static tagName = \"brut-copy-to-clipboard\"\n\n static observedAttributes = [\n \"element\",\n \"copied-lifetime\",\n ]\n\n #elementId = null\n #copiedLifetime = 2000\n\n #copyCode = (event) => {\n event.preventDefault()\n\n const element = document.getElementById(this.#elementId)\n\n if (!element) {\n this.logger.info(\"No element with id %s, so nothing to copy\",this.#elementId)\n return\n }\n this.setAttribute(\"copying\",true)\n\n const text = element.textContent\n\n navigator.clipboard.writeText(text).then( () => {\n this.setAttribute(\"copied\", true)\n }).catch( (e) => {\n this.setAttribute(\"errored\", true)\n this.dispatchEvent(new CustomEvent(\"brut:copyfailed\", { detail: { text: text }}))\n }).finally( () => {\n this.removeAttribute(\"copying\")\n setTimeout( () => this.removeAttribute(\"copied\"), 2000)\n })\n }\n\n update() {\n const button = this.querySelector(\"button\")\n if (button) {\n button.addEventListener(\"click\", this.#copyCode)\n }\n else {\n this.logger.info(\"There is no button, so no way to initiate a copy\")\n }\n }\n\n elementChangedCallback({newValue}) {\n this.#elementId = newValue\n }\n\n copiedLifetimeChangedCallback({newValue}) {\n const newValueAsInt = parseInt(newValue)\n if (!isNaN(newValueAsInt)) {\n this.#copiedLifetime = newValueAsInt\n }\n else {\n this.logger.info(\"Value '%s' for copied-lifetime is not a number. Ignoring it\",newValue)\n }\n }\n}\nexport default CopyToClipboard\n", "import BaseCustomElement from \"./BaseCustomElement\"\nimport RichString from \"./RichString\"\nimport AjaxSubmit from \"./AjaxSubmit\"\nimport ConstraintViolationMessages from \"./ConstraintViolationMessages\"\n\n/** A web component that enhances a form it contains to make constraint validations\n * easier to manage and control.\n *\n * This provides two main features:\n *\n * * While the `:user-invalid` selector allows you to target inputs that have been interacted\n * with (thus avoiding issues when using `:invalid`), this still creates the experience of a\n * user tabbing off of a control and getting an error message. If, instead, you only\n * want to show these errors when a submit has been attempted, this element will\n * set `submitted-invalid` on itself when that happens, thus allowing you to target invalid\n * fields only after a submission attempt.\n * * You may wish to control the messaging of client-side constraint violations\n * beyond what the browser gives you. Assuming you have generated a `<brut-cv-messages input-name=\"\u00ABinput name\u00BB\"></brut-cv-messasges>`, it will be populated with `<brut-cv>` elements for each client-side constraint violation, based on the {@link external:ValidityState} of the control.\n *\n * @fires brut:invalid Fired when any element is found to be invalid\n * @fires brut:valid Fired when no element is found to be invalid. This should be reliable to know\n * when constraint violations have cleared.\n *\n * @example <caption>Basic Structure Required</caption>\n * <brut-form>\n * <form ...>\n * <label>\n * <input type=\"text\" required name=\"username\">\n * <brut-cv-messages input-name=\"username\">\n * </brut-cv-messages>\n * </label>\n * <div> <!-- container need not be a label -->\n * <input type=\"text\" required minlength=\"4\" name=\"alias\">\n * <brut-cv-messages input-name=\"alias\">\n * </brut-cv-messages>\n * </div>\n * <button>Submit</button>\n * </form>\n * </brut-form>\n * <!-- after a submit of this form, the HTML will effectively be as follows -->\n * <brut-form submitted-invalid>\n * <form ...>\n * <label>\n * <input type=\"text\" required name=\"username\">\n * <brut-cv-messages input-name=\"username\">\n * <brut-cv>This field is required</brut-cv>\n * </brut-cv-messages>\n * </label>\n * <div> <!-- container need not be a label -->\n * <input type=\"text\" required minlength=\"4\" name=\"alias\">\n * <brut-cv-messages input-name=\"alias\">\n * <brut-cv>This field is required</brut-cv>\n * </brut-cv-messages>\n * </div>\n * <button>Submit</button>\n * </form>\n * </brut-form>\n *\n * @property {boolean} submitted-invalid - set by this element when the form is submitted. Does not trigger any behavior and can be used in CSS.\n * @see ConstraintViolationMessages\n *\n * @customElement brut-form\n */\nclass Form extends BaseCustomElement {\n static tagName = \"brut-form\"\n static observedAttributes = [\n \"submitted-invalid\",\n \"show-warnings\",\n ]\n\n #markFormSubmittedInvalid = (event) => {\n this.setAttribute(\"submitted-invalid\",\"\")\n }\n #updateValidity = (event) => {\n this.#updateErrorMessages(event)\n }\n #sendValid = () => {\n this.dispatchEvent(new CustomEvent(\"brut:valid\"))\n }\n #sendInvalid = () => {\n this.dispatchEvent(new CustomEvent(\"brut:invalid\"))\n }\n\n submittedInvalidChangedCallback() {}\n\n update() {\n const forms = this.querySelectorAll(\"form\")\n if (forms.length == 0) {\n this.logger.warn(\"Didn't find any forms. Ignoring\")\n return\n }\n forms.forEach( (form) => {\n Array.from(form.elements).forEach( (formElement) => {\n formElement.addEventListener(\"invalid\", this.#updateValidity)\n formElement.addEventListener(\"invalid\", this.#markFormSubmittedInvalid)\n formElement.addEventListener(\"input\", this.#updateValidity)\n })\n form.querySelectorAll(AjaxSubmit.tagName).forEach( (ajaxSubmits) => {\n ajaxSubmits.addEventListener(\"brut:submitok\", this.#sendValid)\n ajaxSubmits.addEventListener(\"brut:submitinvalid\", this.#sendInvalid)\n })\n })\n }\n\n #updateErrorMessages(event) {\n const element = event.target\n let constraintViolationMessages = []\n if (element.name && element.form) {\n const selector = `${ConstraintViolationMessages.tagName}[input-name='${element.name}']`\n constraintViolationMessages = element.form.querySelectorAll(selector)\n if (constraintViolationMessages.length == 0) {\n this.logger.warn(`Did not find any elements matching ${selector}, so no error messages will be shown`)\n }\n }\n else {\n if (element.name) {\n this.logger.warn(\"Element has a name (%s), but is not associated with any form.\", element.name)\n }\n else {\n this.logger.warn(\"Element has a form, but has no name, which means we cannot locate %s by input-name\", ConstraintViolationMessages.tagName)\n }\n }\n if (constraintViolationMessages.length == 0) {\n return\n }\n let anyErrors = false\n constraintViolationMessages.forEach( (errorLabel) => {\n if (element.validity.valid) {\n errorLabel.clearClientSideMessages()\n }\n else {\n anyErrors = true\n errorLabel.createMessages({\n validityState: element.validity,\n inputName: element.name\n })\n }\n })\n if (anyErrors) {\n this.#sendInvalid()\n }\n else {\n this.#sendValid()\n }\n }\n}\nexport default Form\n", "import BaseCustomElement from \"./BaseCustomElement\"\n\n/**\n * Send the locale and timezone from the browser to a configured endpoint on the server. This allows\n * the server to have access to a reasonable guess as to the website visitor's locale/timezone.\n *\n * Note that this will not contact the server if both `locale-from-server` and `timezone-from-server` are\n * set. Further note that this will only contact the server once per page load, unless `url` is changed.\n *\n * @property {String} locale-from-server - omit this if the server doesn't know the visitor's locale. If both this and `timezone-from-server` are set, the server will not be contacted.\n * @property {String} timezone-from-server - omit this if the server doesn't know the visitor's timezone. If both this and `locale-from-server` are set, the server will not be contacted.\n * @property {URL} url - the url to send information to on the server.\n * @property {number} timeout-before-ping-ms - MS to wait until this element contacts the server. A value of 0 will contact the server immediately. The default is 1,000, meaning this element will wait 1 second before contacting the server.\n *\n * @example <caption>When no information about the visitor is known</caption>\n * <brut-locale-detection url=\"__brut/locale-detection\"></brut-locale-detection>\n *\n * @example <caption>When all information about the visitor is known</caption>\n * <brut-locale-detection\n * url=\"__brut/locale-detection\"\n * locale-from-server=\"en-US\"\n * timezone-from-server=\"America/New_York\">\n * </brut-locale-detection>\n *\n * @customElement brut-locale-detection\n */\nclass LocaleDetection extends BaseCustomElement {\n static tagName = \"brut-locale-detection\"\n\n static observedAttributes = [\n \"locale-from-server\",\n \"timezone-from-server\",\n \"url\",\n \"timeout-before-ping-ms\",\n \"show-warnings\",\n ]\n\n #localeFromServer = null\n #timezoneFromServer = null\n #reportingURL = null\n #timeoutBeforePing = 1000\n #serverContacted = false\n\n localeFromServerChangedCallback({newValue}) {\n this.#localeFromServer = newValue\n }\n\n timezoneFromServerChangedCallback({newValue}) {\n this.#timezoneFromServer = newValue\n }\n\n urlChangedCallback({newValue}) {\n if (this.#serverContacted) {\n this.#serverContacted = false\n }\n this.#reportingURL = newValue\n }\n\n timeoutBeforePingMsChangedCallback({newValue}) {\n this.#timeoutBeforePing = newValue\n }\n\n update() {\n if (this.#timeoutBeforePing == 0) {\n this.#pingServerWithLocaleInfo()\n }\n else {\n setTimeout(this.#pingServerWithLocaleInfo.bind(this), this.#timeoutBeforePing)\n }\n }\n\n #pingServerWithLocaleInfo() {\n if (!this.#reportingURL) {\n this.logger.info(\"no url= set, so nowhere to report to\")\n return\n }\n if (this.#localeFromServer && this.#timezoneFromServer) {\n this.logger.info(\"locale and timezone both set, not contacting server\")\n return\n }\n\n if (this.#serverContacted) {\n this.logger.info(\"server has already been contacted at the given url, not doing it again\")\n return\n }\n this.#serverContacted = true\n\n const formatOptions = Intl.DateTimeFormat().resolvedOptions()\n const request = new Request(\n this.#reportingURL,\n {\n headers: {\n \"Content-Type\": \"application/json\",\n },\n method: \"POST\",\n body: JSON.stringify({\n locale: formatOptions.locale,\n timeZone: formatOptions.timeZone,\n }),\n }\n )\n\n window.fetch(request).then( (response) => {\n if (response.ok) {\n this.logger.info(\"Server gave us the OK\") \n }\n else {\n console.warn(response)\n }\n }).catch( (e) => {\n console.warn(e)\n })\n }\n\n\n}\nexport default LocaleDetection\n", "import BaseCustomElement from \"./BaseCustomElement\"\nimport RichString from \"./RichString\"\nimport I18nTranslation from \"./I18nTranslation\"\n\n/** Renders a translated message for a given key, handling all the needed interpolation based\n * on the existence of `<brut-i18n-translation>` elements on the page.\n *\n * When the `key` attribute has a value, this element will locate the `<brut-i18-translation>` element and call `translate`. Note that\n * interpolation is not supported.\n *\n * @property {string} key - the i18n translation key to use. It must map to the `key` of a `<brut-i18n-translation>` on the page or\n * the element will not render any text.\n *\n * @see I18nTranslation\n * @see ConstraintViolationMessage\n *\n * @customElement brut-message\n */\nclass Message extends BaseCustomElement {\n static tagName = \"brut-message\"\n static observedAttributes = [\n \"show-warnings\",\n \"key\",\n ]\n\n /*\n * Creates a new `<brut-message>` element with the given attributes.\n */\n static createElement(document,attributes) {\n const element = document.createElement(Message.tagName)\n Object.entries(attributes).forEach(([name,value]) => {\n if (value !== null && value !== undefined) {\n element.setAttribute(name,value)\n }\n })\n return element\n }\n\n #key = null\n\n keyChangedCallback({newValue}) {\n this.#key = newValue\n }\n\n update() {\n if (!this.#key) {\n this.logger.info(\"No key attribute, so can't do anything\")\n return\n }\n\n const selector = `${I18nTranslation.tagName}[key='${this.#key}']`\n const translation = document.querySelector(selector)\n if (!translation) {\n this.logger.info(\"Could not find translation based on selector '%s'\",selector)\n return\n }\n\n this.textContent = RichString.fromString(translation.translation(), {allowBlank: true }).capitalize().toString()\n }\n}\n\nexport default Message\n", "import BaseCustomElement from \"./BaseCustomElement\"\n\n/** Implements an in-page tab selector. It's intended to wrap a set of `<a>` or `<button>` elements\n * that represent the tabs of a tabbed UI, as defined by ARIA roles. \n *\n * Each direct child must be an `<a>` or a `<button>`, though `<a>` is recommended.\n * Any other elements are ignored. Each `<a>` or `<button>`\n * (herafter referred to as \"tab\") must have the correct ARIA attributes:\n *\n * * `role=\"tab\"`\n * * `aria-selected` as true or false, depending on what tab is selected when the page is first rendered. This\n * custom element will ensure this value is updated as different tabs are selected.\n * * `tabindex` should be 0 if selected, -1 otherwise. This custom element will ensure this value is updated as\n * different tabs are selected.\n * * `aria-controls` to the ID or list of IDs of panels that should be shown when this tab is selected.\n * * `id` to allow the `tab-panel` to refer back to this tab.\n *\n * This custom element will set click listeners on all tabs and, when clicked, hide all panels referred to by\n * every tab (by setting the `hidden` attribute), then show only those panels referred to by the clicked\n * tab. You can use CSS to style everything the way you like it.\n *\n * @property {boolean} tab-selection-pushes-and-restores-state if set, this custom element will use the \n * history API to manage state. When a tab\n * implemented by an `<a>` with an `href` is\n * clicked, that `href` will be pushed into \n * the state. When the back button is hit,\n * this will select the previous tab as selected.\n * Note that this will conflict with anything else\n * on the page that manipulates state, so only\n * set this if your UI is a \"full page tab\"\n * style UI.\n *\n * @fires Tabs#brut:tabselected whenever the tab selection has changed\n * @example\n * <brut-tabs>\n * <a role=\"tab\" aria-selected=\"true\" tabindex=\"0\" aria-controls=\"inbox-panel\" id=\"inbox-tab\"\n * href=\"?tab=inbox\">Inbox</a>\n * <a role=\"tab\" aria-selected=\"false\" tabindex=\"-1\" aria-controls=\"drafts-panel\" id=\"drafts-tab\"\n * href=\"?tab=drafts\">Drafts</a>\n * <a role=\"tab\" aria-selected=\"false\" tabindex=\"-1\" aria-controls=\"spam-panel\" id=\"spam-tab\"\n * href=\"?tab=spam\">Spam</a>\n * </brut-tabs>\n * <section role=\"tabpanel\" tabindex=\"0\" id=\"inbox-panel\">\n * <h3>Inbox</h3>\n * </section>\n * <section role=\"tabpanel\" tabindex=\"0\" id=\"drafts-panel\" hidden>\n * <h3>Drafts</h3>\n * </section>\n * <section role=\"tabpanel\" tabindex=\"0\" id=\"spam-panel\" hidden>\n * <h3>Spam</h3>\n * </section>\n * <!-- if a user clicks on 'Drafts', the DOM will be updated to look\n * effectively like so: -->\n * <brut-tabs>\n * <a role=\"tab\" aria-selected=\"false\" tabindex=\"-1\" aria-controls=\"inbox-panel\" id=\"inbox-tab\"\n * href=\"?tab=inbox\">Inbox</a>\n * <a role=\"tab\" aria-selected=\"true\" tabindex=\"0\" aria-controls=\"drafts-panel\" id=\"drafts-tab\"\n * href=\"?tab=drafts\">Drafts</a>\n * <a role=\"tab\" aria-selected=\"false\" tabindex=\"-1\" aria-controls=\"spam-panel\" id=\"spam-tab\"\n * href=\"?tab=spam\">Spam</a>\n * </brut-tabs>\n * <section role=\"tabpanel\" tabindex=\"0\" id=\"inbox-panel\" hidden>\n * <h3>Inbox</h3>\n * </section>\n * <section role=\"tabpanel\" tabindex=\"-1\" id=\"drafts-panel\">\n * <h3>Drafts</h3>\n * </section>\n * <section role=\"tabpanel\" tabindex=\"-1\" id=\"spam-panel\" hidden>\n * <h3>Spam</h3>\n * </section>\n *\n * @customElement brut-tabs\n */\nclass Tabs extends BaseCustomElement {\n static tagName = \"brut-tabs\"\n static observedAttributes = [\n \"tab-selection-pushes-and-restores-state\",\n \"show-warnings\",\n ]\n\n tabSelectionPushesAndRestoresStateChangedCallback({newValue,oldValue}) {\n this.#pushAndRestoreTabState = newValue != null\n }\n\n update() {\n this.#tabs().forEach( (tab) => {\n tab.addEventListener(\"click\", this.#tabClicked)\n })\n }\n\n #pushAndRestoreTabState = false\n\n #tabClicked = (event) => {\n event.preventDefault()\n this.#setTabAsSelected(event.target)\n event.preventDefault()\n }\n\n #reloadTab = (event) => {\n const tab = document.getElementById(event.state.tabId)\n if (tab) {\n this.#setTabAsSelected(tab, { skipPushState: true })\n }\n }\n\n #setTabAsSelected(selectedTab, { skipPushState = false } = {}) {\n this.#tabs().forEach( (tab) => {\n const tabPanels = []\n const ariaControls = tab.getAttribute(\"aria-controls\")\n if (ariaControls) {\n ariaControls.split(/\\s+/).forEach( (id) => {\n const panel = document.getElementById(id)\n if (panel) {\n tabPanels.push(panel)\n }\n else {\n this.logger.warn(\"Tab %o references panel with id %s, but no such element exists with that id\",tab,id)\n }\n })\n }\n if (tab == selectedTab) {\n tab.setAttribute(\"aria-selected\",true)\n tab.setAttribute(\"tabindex\",\"0\")\n tabPanels.forEach( (panel) => panel.removeAttribute(\"hidden\") )\n if (this.#pushAndRestoreTabState && !skipPushState) {\n let href = tab.getAttribute(\"href\") || \"\"\n if (href.startsWith(\"?\")) {\n let hrefQueryString = href.slice(1)\n const anchorIndex = hrefQueryString.indexOf(\"#\")\n if (anchorIndex != -1) {\n hrefQueryString = hrefQueryString.slice(-1 * (hrefQueryString.length - anchorIndex - 1))\n }\n const currentQuery = new URLSearchParams(window.location.search)\n const hrefQuery = new URLSearchParams(hrefQueryString)\n hrefQuery.forEach( (value,key) => {\n currentQuery.set(key,value)\n })\n href = \"?\" + currentQuery.toString() + (anchorIndex == -1 ? \"\" : hrefQueryString.slice(anchorIndex))\n }\n window.history.pushState({ tabId: tab.id },\"\",href)\n window.addEventListener(\"popstate\", this.#reloadTab)\n }\n this.dispatchEvent(new CustomEvent(\"brut:tabselected\", { tabId: tab.id }))\n }\n else {\n tab.setAttribute(\"aria-selected\",false)\n tab.setAttribute(\"tabindex\",\"-1\")\n tabPanels.forEach( (panel) => panel.setAttribute(\"hidden\", true) )\n }\n })\n }\n\n\n #tabs() {\n const tabs = []\n this.querySelectorAll(\"[role=tab]\").forEach( (tab) => {\n if ( (tab.tagName.toLowerCase() == \"a\") || (tab.tagName.toLowerCase() == \"button\") ) {\n tabs.push(tab)\n }\n else {\n this.logger.warn(\"An element with tag %s was assigned role=tab, and %s doesn't work that way. Use an <a> or a <button>\",tab.tagName,this.constructor.name)\n }\n })\n return tabs\n }\n\n}\nexport default Tabs\n", "import { BaseCustomElement, RichString, Message } from \"brut-js\"\n\n/**\n * Support for a toast component, which is a momentary message shown to the user, for example\n * if an aynchronous update has occured.\n *\n * To use this element, you should set up your CSS so that the element is hidden if there is no `key`\n * attribute set. When the `key` attribute *is* set, the element should be shown. You can use CSS animations\n * for this as needed, but the main thing to remember is that, without a `key` attribute, this element\n * should not be visible.\n *\n * The `key` attribute is expected to be an i18n key that references a `<brut-i18n-message>` on\n * the page, which contains the actual message to show the visitor. When the `key` attribute is\n * set, this component will find an `<output>` inside itself, and replace the entire contents\n * with a `<brut-message>` component, using the same `key`. This will cause the `<brut-message>`\n * to look up the key and put that text into the element.\n *\n * In addition to this lookup, this element will set appropriate ARIA attributes on the\n * created `<brut-message>` element.\n *\n * Further, if there is a `<button>` inside this element, it will be used to close the toast by removing the\n * `key` attribute (which, assuming your CSS is correct, will hide the element).\n *\n * @property {string} key - an I18n key of the message to show in the toast. When you generate\n * the toast's HTML on the server, do not set key. Then, when you need\n * to display the toast, use JavaScript to set the key. This will\n * trigger its behavior as described above.\n *\n * @example\n * <style>\n * brut-toast {\n * display: none;\n * }\n * brut-toast[key] {\n * display: block;\n * }\n * </style>\n * <brut-i18n-translation key=\"toast.saved\">Save successful</brut-i18n-translation>\n * <brut-toast>\n * <div>\n * <output></output>\n * <button>Close</button>\n * </div>\n * </brut-toast>\n * <!-- now, if you set the key to \"toast.saved\", the HTML will be changed as follows: -->\n * <brut-toast key=\"toast.saved\">\n * <div>\n * <output>\n * <brut-message key=\"toast.saved\" role=\"status\" aria-live=\"polite\" aria-atomic=\"true\">\n * Save successful\n * </brut-message>\n * </output>\n * <button>Close</button>\n * </div>\n * </brut-toast>\n */\nclass Toast extends BaseCustomElement {\n static tagName = \"brut-toast\"\n\n static observedAttributes = [\n \"show-warnings\",\n \"key\",\n ]\n\n #key = null\n #closeListener = (event) => {\n event.preventDefault()\n this.removeAttribute(\"key\")\n }\n\n keyChangedCallback({newValue}) {\n this.#key = RichString.fromString(newValue)\n }\n\n update() {\n const closeButton = this.querySelector(\"button\")\n\n if (closeButton) {\n closeButton.addEventListener(\"click\", this.#closeListener)\n }\n\n if (!this.#key) {\n return\n }\n const output = this.querySelector(\"output\")\n if (!output) {\n this.logger.warn(\"No <output> element found, so toast will not be displayed\")\n return\n }\n const messageNode = Message.createElement(document,{\n \"key\": this.#key,\n \"role\": \"status\",\n \"aria-live\": \"polite\",\n \"aria-atomic\": \"true\"\n })\n output.replaceChildren(messageNode)\n this.style.animation = \"none\"\n this.offsetWidth // Trigger reflow to restart the animation\n this.style.animation = \"\"\n }\n}\nexport default Toast\n", "import { BaseCustomElement } from \"brut-js\"\n\n/** Sends performance data to an endpoint in a Brut-powered app that is expected to save it as an Open Telemetry span.\n * Uses the W3C-recommended headers \"traceparent\" and \"tracestate\" to do this.\n *\n * ### Supported Metrics\n *\n * Currently, this will attempt to send \"navigation\", \"largest-contentful-paint\", and \"first-contentful-paint\" back to the server.\n * Not all browsers support these, so this element will send back as many as it can. It will wait for all supported metrics to be\n * received before contacting the server. It will attempt to do this exactly once.\n *\n * ### Use\n *\n * To use this element, your page must have a `<meta>` element that contains the value for \"traceparent\". It is expected that your\n * server will include this in server-generatd HTML. The Brut's `Brut::FrontEnd::Components::Traceparent` component will handle this\n * for you. The value for \"traceparent\" is key to connecting the browser metrics to the back-end request that generated the page.\n *\n * The element also requires a `url` attribute to know where to send the data. By default, Brut is listening in\n * `/__brut/instrumentation`. See the example.\n *\n * ### Durations vs Timestamps\n *\n * The performance API produces durations since an origin timestamp. Open Telemetry wants timestamps. In theory,\n * `Performance.timeOrigin` is provided by the browser as a reference time when the page started doing anything.\n * In practice, this value is incorrect on Firefox, so the element records a timestamp when it is created.\n *\n * When the data is merged back to the server span, the specific timestamps will not exactly match reality, however the durations will\n * be accurate. Note that even if `Performance.timeOrigin` was correct, clock drift between client and server would make\n * the timestamps inaccurate anyway.\n *\n * ### Encoding\n *\n * The spec for the \"tracestate\" header leaves open how the data is to be encoded. It supports multiple vendors using a key/value\n * pair:\n *\n * tracestate: honeycomb=\u00ABencoded data\u00BB,newrelic=\u00ABencoded data\u00BB\n *\n * This element uses the vendor name \"brut\". The data is a Base64-encoded JSON blob containing the data. \n *\n * tracestate: brut=\u00ABBase64 encoded JSON\u00BB\n *\n * The values captured and format of the JSON map closely to Open Telemetry's browser instrumentation format.\n * Of course, this element is many magnitudes smaller in size than Open Telemetry's, which is why it exists at all\n *\n * @example\n * <!DOCTYPE html>\n * <html>\n * <head>\n * <meta name=\"traceparent\" content=\"293874293749237439843294\">\n * <brut-tracing url=\"/__brut/instrumentation\"></brut-tracing>\n * <!-- ... -->\n * </head>\n * <body>\n * <!-- ... -->\n * </body>\n * </html>\n *\n * @property {string} url - the url where the trace information is to be sent.\n *\n * @see {@link https://www.w3.org/TR/trace-context/}\n * @see external:Performance\n *\n * @customElement brut-tracing\n */\nclass Tracing extends BaseCustomElement {\n static tagName = \"brut-tracing\"\n\n static observedAttributes = [\n \"url\",\n \"show-warnings\",\n ]\n\n\n #url = null\n #sent = {}\n #payload = {}\n #timeOrigin = null\n #supportedTypes = []\n\n #performanceObserver = new PerformanceObserver( (entries) => {\n const navigation = entries.getEntriesByType(\"navigation\")[0]\n if (navigation && navigation.loadEventEnd != 0 && !this.#payload.navigation) {\n this.#payload.navigation = this.#parseNavigation(navigation)\n }\n const largestContentfulPaint = entries.getEntriesByType(\"largest-contentful-paint\")\n if (largestContentfulPaint.length > 0 && !this.#payload[\"largest-contentful-paint\"]) {\n this.#payload[\"largest-contentful-paint\"] = this.#parseLargestContentfulPaint(largestContentfulPaint)\n }\n const paint = entries.getEntriesByName(\"first-contentful-paint\", \"paint\")[0]\n if (paint && !this.#payload.paint) {\n this.#payload.paint = this.#parseFirstContentfulPaint(paint)\n }\n\n if ( this.#supportedTypes.every( (type) => this.#payload[type] ) ) {\n this.#sendSpans()\n this.#payload = {}\n }\n })\n\n constructor() {\n super()\n this.#timeOrigin = Date.now()\n this.#supportedTypes = [\n \"navigation\",\n \"largest-contentful-paint\",\n \"paint\",\n ].filter( (type) => {\n return PerformanceObserver.supportedEntryTypes.includes(type)\n })\n }\n\n urlChangedCallback({newValue}) {\n this.#url = newValue\n }\n\n update() {\n this.#supportedTypes.forEach( (type) => {\n this.#performanceObserver.observe({type: type, buffered: true})\n })\n }\n\n #sendSpans() {\n const headers = this.#initializerHeadersIfCanContinue()\n if (!headers) {\n return\n }\n const span = this.#payload.navigation\n\n if (this.#payload.paint) {\n span.events.push({\n name: this.#payload.paint.name,\n timestamp: this.#timeOrigin + this.#payload.paint.startTime\n })\n }\n if (this.#payload[\"largest-contentful-paint\"]) {\n this.#payload[\"largest-contentful-paint\"].forEach( (event) => {\n span.events.push({\n name: event.name,\n timestamp: this.#timeOrigin + event.startTime,\n attributes: {\n \"element.tag\": event.element?.tagName,\n \"element.class\": event.element?.className,\n }\n })\n })\n }\n\n this.#sent[this.#url] = true\n headers.append(\"tracestate\",`brut=${window.btoa(JSON.stringify(span))}`)\n const request = new Request(\n this.#url,\n {\n headers: headers,\n method: \"GET\",\n }\n )\n fetch(request).then( (response) => {\n if (!response.ok) {\n console.warn(\"Problem sending instrumentation: %s/%s\", response.status,response.statusText)\n }\n }).catch( (error) => {\n console.warn(\"Problem sending instrumentation: %o\", error)\n })\n }\n\n #parseNavigation(navigation) {\n const documentFetch = {\n name: \"browser.documentFetch\",\n start_timestamp: navigation.fetchStart + this.#timeOrigin,\n end_timestamp: navigation.responseEnd + this.#timeOrigin,\n attributes: {\n \"http.url\": navigation.name,\n },\n }\n const events = [\n \"fetchStart\",\n \"unloadEventStart\",\n \"unloadEventEnd\",\n \"domInteractive\",\n \"domInteractive\",\n \"domContentLoadedEventStart\",\n \"domContentLoadedEventEnd\",\n \"domComplete\",\n \"loadEventStart\",\n \"loadEventEnd\",\n ]\n\n return {\n name: \"browser.documentLoad\",\n start_timestamp: navigation.fetchStart + this.#timeOrigin,\n end_timestamp: navigation.loadEventEnd + this.#timeOrigin,\n attributes: {\n \"http.url\": navigation.name,\n \"http.user_agent\": window.navigator.userAgent,\n },\n events: events.map( (eventName) => {\n return {\n name: eventName,\n timestamp: this.#timeOrigin + navigation[eventName],\n }\n }),\n spans: [\n documentFetch\n ]\n }\n }\n\n #parseFirstContentfulPaint(paint) {\n return {\n name: \"browser.first-contentful-paint\",\n startTime: paint.startTime,\n }\n }\n\n #parseLargestContentfulPaint(largestContentfulPaint) {\n return largestContentfulPaint.map( (entry) => {\n return {\n name: \"browser.largest-contentful-paint\",\n startTime: entry.startTime,\n element: entry.element,\n }\n })\n }\n\n #initializerHeadersIfCanContinue() {\n if (!this.#url) {\n this.logger.info(\"No url set, no traces will be reported\")\n return\n }\n const $traceparent = document.querySelector(\"meta[name='traceparent']\")\n if (!$traceparent) {\n this.logger.info(\"No <meta name='traceparent' ...> in the document, no traces can be reported\")\n return\n }\n if (this.#sent[this.#url]) {\n this.logger.info(\"Already sent to %s\", this.#url)\n return\n }\n const traceparent = $traceparent.getAttribute(\"content\")\n if (!traceparent) {\n this.logger.info(\"%o had no value for the content attribute, no traces can be reported\",$traceparent)\n return\n }\n return new Headers({ traceparent })\n }\n}\nexport default Tracing\n", "import BaseCustomElement from \"./BaseCustomElement\"\nimport AjaxSubmit from \"./AjaxSubmit\"\nimport Autosubmit from \"./Autosubmit\"\nimport ConfirmSubmit from \"./ConfirmSubmit\"\nimport ConfirmationDialog from \"./ConfirmationDialog\"\nimport ConstraintViolationMessage from \"./ConstraintViolationMessage\"\nimport ConstraintViolationMessages from \"./ConstraintViolationMessages\"\nimport CopyToClipboard from \"./CopyToClipboard\"\nimport Form from \"./Form\"\nimport I18nTranslation from \"./I18nTranslation\"\nimport LocaleDetection from \"./LocaleDetection\"\nimport Message from \"./Message\"\nimport RichString from \"./RichString\"\nimport Tabs from \"./Tabs\"\nimport Toast from \"./Toast\"\nimport Tracing from \"./Tracing\"\n\n/**\n * This is the code for a test case. It may return a {@link external:Promise} if there is async behavior that must\n * be waited-on to properly assert behavior.\n *\n * @callback testCodeCallback\n *\n * @param {Object} objects - objects passed into your test that you may need.\n * @param {Window} objects.window - Access to the top-level window object. Note that this provided by JSDOM and is not exactly like the `Window` you'd get in your browser.\n * @param {Document} objects.document - Access to the top-level document object. Note that this provided by JSDOM and is not exactly like the `Document` you'd get in your browser.\n * @param {Object} objects.assert - The NodeJS assert object that you should use to assert behavior.\n * @param {Object} objects.fetchRequests - An array of `Request` instances given to `fetch`. This will be updated as `fetch` is\n * called and can be useful to assert the contents of what was requested via `fetch`.\n *\n * @example\n * test(\"some test\", ({document,assert}) => {\n * const element = document.querySelector(\"div\")\n * assert(div.getAttribute(\"data-foo\") != null)\n * })\n *\n * @example\n * test(\"some other test\", ({document,window,assert}) => {\n * const element = document.querySelector(\"div\")\n * assert.equal(window.history.state[\"foo\"], \"bar\")\n * })\n */\n\n/**\n * @external Performance\n * @see {@link https://developer.mozilla.org/en-US/docs/Web/API/Performance|Performance API}\n */\n\n/**\n * @external Promise\n * @see {@link https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise|Promise}\n */\n/**\n * @external fetch\n * @see {@link https://developer.mozilla.org/en-US/docs/Web/API/Window/fetch|fetch}\n */\n\n/**\n * @external ValidityState\n * @see {@link https://developer.mozilla.org/en-US/docs/Web/API/ValidityState|ValidityState}\n */\n\n/**\n * The standard `CustomElementRegistry`\n *\n * @external CustomElementRegistry\n * @see {@link https://developer.mozilla.org/en-US/docs/Web/API/CustomElementRegistry|CustomElementRegistry}\n */\n\n/**\n * @external Window\n * @see {@link https://developer.mozilla.org/en-US/docs/Web/API/Window/|Window}\n */\n\n/** \n * @method confirm\n * @memberof external:Window#\n * @see {@link https://developer.mozilla.org/en-US/docs/Web/API/Window/confirm|confirm}\n */\n\n/**\n * Class that can be used to automatically define all of brut's custom\n * elements.\n */\nclass BrutCustomElements {\n static elementClasses = []\n static define() {\n this.elementClasses.forEach( (e) => {\n e.define() \n })\n }\n static addElementClasses(...classes) {\n this.elementClasses.push(...classes)\n }\n}\n\nBrutCustomElements.addElementClasses(\n // Ordering is important here - TBD how to make sure these are created in order\n I18nTranslation,\n CopyToClipboard,\n Message,\n ConfirmSubmit,\n ConfirmationDialog,\n ConstraintViolationMessages,\n Form,\n AjaxSubmit,\n ConstraintViolationMessage,\n Tabs,\n Toast,\n LocaleDetection,\n Autosubmit,\n Tracing,\n)\n\nexport {\n AjaxSubmit,\n Autosubmit,\n BaseCustomElement,\n BrutCustomElements,\n ConfirmSubmit,\n ConfirmationDialog,\n ConstraintViolationMessage,\n ConstraintViolationMessages,\n Form,\n I18nTranslation,\n LocaleDetection,\n Message,\n RichString,\n Tabs,\n Toast,\n Tracing\n}\n\n", "import * as BrutJS from \"./index.js\"\ndocument.addEventListener(\"DOMContentLoaded\", () => {\n BrutJS.BrutCustomElements.define()\n if (!HTMLDialogElement.prototype.showModal) {\n HTMLDialogElement.prototype.showModal = function() {\n this.open = true\n }\n }\n if (!HTMLDialogElement.prototype.close) {\n HTMLDialogElement.prototype.close = function(returnValue) {\n this.open = false\n this.returnValue = returnValue\n }\n }\n})\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": ["document", "form", "document", "form", "form", "form", "document"]
|
|
7
7
|
}
|
package/src/I18nTranslation.js
CHANGED
|
@@ -49,6 +49,9 @@ class I18nTranslation extends BaseCustomElement {
|
|
|
49
49
|
* }
|
|
50
50
|
*/
|
|
51
51
|
translation(interpolatedValues) {
|
|
52
|
+
if (!this.#value) {
|
|
53
|
+
this.logger.warn("No value attribute for key '%s', so translation will be blank", this.#key)
|
|
54
|
+
}
|
|
52
55
|
return this.#value.replaceAll(/%\{([^}%]+)\}/g, (match,key) => {
|
|
53
56
|
if (interpolatedValues[key]) {
|
|
54
57
|
return interpolatedValues[key]
|
package/src/Message.js
CHANGED
|
@@ -23,10 +23,16 @@ class Message extends BaseCustomElement {
|
|
|
23
23
|
"key",
|
|
24
24
|
]
|
|
25
25
|
|
|
26
|
+
/*
|
|
27
|
+
* Creates a new `<brut-message>` element with the given attributes.
|
|
28
|
+
*/
|
|
26
29
|
static createElement(document,attributes) {
|
|
27
30
|
const element = document.createElement(Message.tagName)
|
|
28
|
-
|
|
29
|
-
|
|
31
|
+
Object.entries(attributes).forEach(([name,value]) => {
|
|
32
|
+
if (value !== null && value !== undefined) {
|
|
33
|
+
element.setAttribute(name,value)
|
|
34
|
+
}
|
|
35
|
+
})
|
|
30
36
|
return element
|
|
31
37
|
}
|
|
32
38
|
|
|
@@ -49,7 +55,7 @@ class Message extends BaseCustomElement {
|
|
|
49
55
|
return
|
|
50
56
|
}
|
|
51
57
|
|
|
52
|
-
this.textContent = RichString.fromString(translation.translation()).capitalize().toString()
|
|
58
|
+
this.textContent = RichString.fromString(translation.translation(), {allowBlank: true }).capitalize().toString()
|
|
53
59
|
}
|
|
54
60
|
}
|
|
55
61
|
|
package/src/RichString.js
CHANGED
|
@@ -13,10 +13,13 @@ class RichString {
|
|
|
13
13
|
*
|
|
14
14
|
* @param {null|undefined|String|RichString} possiblyDefinedStringOrRichString - if `null`, `undefined`, or otherwise falsey, this method returns `null`. If a String, returns a new `RichString` wrapping it. If a `RichString`, returns the `RichString` unchanged.
|
|
15
15
|
*/
|
|
16
|
-
static fromString(possiblyDefinedStringOrRichString) {
|
|
16
|
+
static fromString(possiblyDefinedStringOrRichString, {allowBlank=false} = {}) {
|
|
17
17
|
if (possiblyDefinedStringOrRichString instanceof RichString) {
|
|
18
18
|
return possiblyDefinedStringOrRichString
|
|
19
19
|
}
|
|
20
|
+
if (allowBlank && possiblyDefinedStringOrRichString === "") {
|
|
21
|
+
return new RichString("")
|
|
22
|
+
}
|
|
20
23
|
if (!possiblyDefinedStringOrRichString) {
|
|
21
24
|
return null
|
|
22
25
|
}
|
package/src/Toast.js
ADDED
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
import { BaseCustomElement, RichString, Message } from "brut-js"
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Support for a toast component, which is a momentary message shown to the user, for example
|
|
5
|
+
* if an aynchronous update has occured.
|
|
6
|
+
*
|
|
7
|
+
* To use this element, you should set up your CSS so that the element is hidden if there is no `key`
|
|
8
|
+
* attribute set. When the `key` attribute *is* set, the element should be shown. You can use CSS animations
|
|
9
|
+
* for this as needed, but the main thing to remember is that, without a `key` attribute, this element
|
|
10
|
+
* should not be visible.
|
|
11
|
+
*
|
|
12
|
+
* The `key` attribute is expected to be an i18n key that references a `<brut-i18n-message>` on
|
|
13
|
+
* the page, which contains the actual message to show the visitor. When the `key` attribute is
|
|
14
|
+
* set, this component will find an `<output>` inside itself, and replace the entire contents
|
|
15
|
+
* with a `<brut-message>` component, using the same `key`. This will cause the `<brut-message>`
|
|
16
|
+
* to look up the key and put that text into the element.
|
|
17
|
+
*
|
|
18
|
+
* In addition to this lookup, this element will set appropriate ARIA attributes on the
|
|
19
|
+
* created `<brut-message>` element.
|
|
20
|
+
*
|
|
21
|
+
* Further, if there is a `<button>` inside this element, it will be used to close the toast by removing the
|
|
22
|
+
* `key` attribute (which, assuming your CSS is correct, will hide the element).
|
|
23
|
+
*
|
|
24
|
+
* @property {string} key - an I18n key of the message to show in the toast. When you generate
|
|
25
|
+
* the toast's HTML on the server, do not set key. Then, when you need
|
|
26
|
+
* to display the toast, use JavaScript to set the key. This will
|
|
27
|
+
* trigger its behavior as described above.
|
|
28
|
+
*
|
|
29
|
+
* @example
|
|
30
|
+
* <style>
|
|
31
|
+
* brut-toast {
|
|
32
|
+
* display: none;
|
|
33
|
+
* }
|
|
34
|
+
* brut-toast[key] {
|
|
35
|
+
* display: block;
|
|
36
|
+
* }
|
|
37
|
+
* </style>
|
|
38
|
+
* <brut-i18n-translation key="toast.saved">Save successful</brut-i18n-translation>
|
|
39
|
+
* <brut-toast>
|
|
40
|
+
* <div>
|
|
41
|
+
* <output></output>
|
|
42
|
+
* <button>Close</button>
|
|
43
|
+
* </div>
|
|
44
|
+
* </brut-toast>
|
|
45
|
+
* <!-- now, if you set the key to "toast.saved", the HTML will be changed as follows: -->
|
|
46
|
+
* <brut-toast key="toast.saved">
|
|
47
|
+
* <div>
|
|
48
|
+
* <output>
|
|
49
|
+
* <brut-message key="toast.saved" role="status" aria-live="polite" aria-atomic="true">
|
|
50
|
+
* Save successful
|
|
51
|
+
* </brut-message>
|
|
52
|
+
* </output>
|
|
53
|
+
* <button>Close</button>
|
|
54
|
+
* </div>
|
|
55
|
+
* </brut-toast>
|
|
56
|
+
*/
|
|
57
|
+
class Toast extends BaseCustomElement {
|
|
58
|
+
static tagName = "brut-toast"
|
|
59
|
+
|
|
60
|
+
static observedAttributes = [
|
|
61
|
+
"show-warnings",
|
|
62
|
+
"key",
|
|
63
|
+
]
|
|
64
|
+
|
|
65
|
+
#key = null
|
|
66
|
+
#closeListener = (event) => {
|
|
67
|
+
event.preventDefault()
|
|
68
|
+
this.removeAttribute("key")
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
keyChangedCallback({newValue}) {
|
|
72
|
+
this.#key = RichString.fromString(newValue)
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
update() {
|
|
76
|
+
const closeButton = this.querySelector("button")
|
|
77
|
+
|
|
78
|
+
if (closeButton) {
|
|
79
|
+
closeButton.addEventListener("click", this.#closeListener)
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
if (!this.#key) {
|
|
83
|
+
return
|
|
84
|
+
}
|
|
85
|
+
const output = this.querySelector("output")
|
|
86
|
+
if (!output) {
|
|
87
|
+
this.logger.warn("No <output> element found, so toast will not be displayed")
|
|
88
|
+
return
|
|
89
|
+
}
|
|
90
|
+
const messageNode = Message.createElement(document,{
|
|
91
|
+
"key": this.#key,
|
|
92
|
+
"role": "status",
|
|
93
|
+
"aria-live": "polite",
|
|
94
|
+
"aria-atomic": "true"
|
|
95
|
+
})
|
|
96
|
+
output.replaceChildren(messageNode)
|
|
97
|
+
this.style.animation = "none"
|
|
98
|
+
this.offsetWidth // Trigger reflow to restart the animation
|
|
99
|
+
this.style.animation = ""
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
export default Toast
|
package/src/index.js
CHANGED
|
@@ -12,6 +12,7 @@ import LocaleDetection from "./LocaleDetection"
|
|
|
12
12
|
import Message from "./Message"
|
|
13
13
|
import RichString from "./RichString"
|
|
14
14
|
import Tabs from "./Tabs"
|
|
15
|
+
import Toast from "./Toast"
|
|
15
16
|
import Tracing from "./Tracing"
|
|
16
17
|
|
|
17
18
|
/**
|
|
@@ -105,6 +106,7 @@ BrutCustomElements.addElementClasses(
|
|
|
105
106
|
AjaxSubmit,
|
|
106
107
|
ConstraintViolationMessage,
|
|
107
108
|
Tabs,
|
|
109
|
+
Toast,
|
|
108
110
|
LocaleDetection,
|
|
109
111
|
Autosubmit,
|
|
110
112
|
Tracing,
|
|
@@ -125,6 +127,7 @@ export {
|
|
|
125
127
|
Message,
|
|
126
128
|
RichString,
|
|
127
129
|
Tabs,
|
|
130
|
+
Toast,
|
|
128
131
|
Tracing
|
|
129
132
|
}
|
|
130
133
|
|