semantic-forms 5.3.12 → 5.3.14

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.
@@ -51,9 +51,52 @@ const travel=input.clientWidth/2||1;const wasChecked=input.checked;let dragging=
51
51
  dragging=true;moveEvent.preventDefault();// don't select or scroll while dragging
52
52
  input.checked=(wasChecked?1:0)+distance/travel>.5};const drop=upEvent=>{input.releasePointerCapture(upEvent.pointerId);input.removeEventListener("pointermove",drag);input.removeEventListener("pointerup",drop);input.removeEventListener("pointercancel",drop);if(!dragging)return;
53
53
  // the click that follows the drag would otherwise put the switch back where it started
54
- input.addEventListener("click",clickEvent=>clickEvent.preventDefault(),{once:true,capture:true});if(input.checked!==wasChecked){input.dispatchEvent(new Event("input",{bubbles:true}));input.dispatchEvent(new Event("change",{bubbles:true}))}};input.addEventListener("pointermove",drag);input.addEventListener("pointerup",drop);input.addEventListener("pointercancel",drop)})};const enhanceInput=(input,form)=>{
54
+ input.addEventListener("click",clickEvent=>clickEvent.preventDefault(),{once:true,capture:true});if(input.checked!==wasChecked){input.dispatchEvent(new Event("input",{bubbles:true}));input.dispatchEvent(new Event("change",{bubbles:true}))}};input.addEventListener("pointermove",drag);input.addEventListener("pointerup",drop);input.addEventListener("pointercancel",drop)})};
55
+ // turns a text input with the data-auto-grow attribute into a textarea, and makes enter submit the form from it
56
+
57
+ // returns the field, which is the new textarea when one was made
58
+ const enhanceAutoGrow=(input,form)=>{if(input.getAttribute("data-auto-grow")===null)return input;
59
+ // progressively enhance inputs into textareas
60
+ if(input.nodeName==="INPUT"&&input.type==="text"){const newInput=document.createElement("textarea");newInput.id=input.id;newInput.class=input.class;newInput.innerText=input.value;newInput.setAttribute("data-auto-grow","");input.replaceWith(newInput);input=newInput}if(input.nodeName==="TEXTAREA")
61
+ // when pressing enter while this input is focused, we want to submit
62
+ input.addEventListener("keypress",e=>{if(e.key!=="Enter"||e.key==="Enter"&&e.shiftKey)return;e.preventDefault();form.requestSubmit()});return input};
63
+ // the behavior of the control itself, as opposed to the labels and buttons placed around it: switches and textareas. this is all a field in a customLayout element gets, and every other field gets it too
64
+ const enhanceControl=(input,type,form)=>{
65
+ // a checkbox with the switch attribute is a switch; the stylesheet draws one in every browser, and where the attribute is not implemented natively the role and dragging are supplied here
66
+ if(type==="checkbox"&&input.hasAttribute("switch")&&!nativeSwitchSupport){input.setAttribute("role","switch");enableSwitchDragging(input)}
67
+ // add listener to shift clear button when scrollbar present
68
+ if(input.nodeName==="TEXTAREA"){if(input.getAttribute("data-disable-autosize")===null){
69
+ // add auto-sizing
70
+ input.style.setProperty("resize","none");input.style.setProperty("min-height","0");input.style.setProperty("max-height","none");input.style.setProperty("height","auto");const handleInput=()=>{
71
+ // reset rows attribute to get accurate scrollHeight
72
+ let maxRows=input.getAttribute("data-max-rows");if(maxRows)if(isNaN(maxRows)||Number(maxRows)<=0){console.warn(`An invalid value was passed to the "data-max-rows" attribute. This value will be ignored.\n\nProvided value: ${input.getAttribute("data-max-rows")}`);maxRows=null}const minRows=input.getAttribute("data-max-rows")&&Number(input.getAttribute("data-max-rows"))<5?input.getAttribute("data-max-rows"):"5";input.setAttribute("rows",minRows);
73
+ // get the computed values object reference
74
+ const style=window.getComputedStyle(input);
75
+ // force content-box for size accurate line-height calculation, remove scrollbars, lock width (subtract inline padding and inline border widths), and remove inline padding and borders to keep width consistent (for text wrapping accuracy)
76
+ const inlinePadding=parseFloat(style["padding-left"])+parseFloat(style["padding-right"]);const inlineBorderWidth=parseFloat(style["border-left-width"])+parseFloat(style["border-right-width"]);input.style.setProperty("overflow","hidden","important");input.style.setProperty("width",parseFloat(style.width)-inlinePadding-inlineBorderWidth+"px");input.style.setProperty("box-sizing","content-box");input.style.setProperty("padding-inline","0");input.style.setProperty("border-width","0");
77
+ // get the base line height, and top / bottom padding
78
+ const blockPadding=parseFloat(style["padding-top"])+parseFloat(style["padding-bottom"]);const lineHeight=style["line-height"]==="normal"?parseFloat(style.height):parseFloat(style["line-height"]);// otherwise (line-height is explicitly set), use the computed line-height value
79
+ // get the scroll height (rounding to be safe to ensure cross browser consistency)
80
+ const scrollHeight=Math.round(input.scrollHeight);
81
+ // undo overflow, width, border-width, box-sizing & inline padding overrides
82
+ input.style.removeProperty("width");input.style.removeProperty("box-sizing");input.style.removeProperty("padding-inline");input.style.removeProperty("border-width");input.style.removeProperty("overflow");
83
+ // subtract blockPadding from scrollHeight and divide that by our lineHeight to get the row count, round to nearest integer as it will always be within ~.1 of the correct whole number
84
+ const rows=Math.round((scrollHeight-blockPadding)/lineHeight);
85
+ // set the calculated rows attribute (limited by rowLimit)
86
+ if(maxRows)input.setAttribute("rows",""+Math.min(rows,Number(maxRows)));else input.setAttribute("rows",""+rows)};input.addEventListener("input",handleInput);
87
+ // trigger the event to set the initial rows value
88
+ input.dispatchEvent(new Event("input",{bubbles:true}))}
89
+ // progressively enhance textarea for Firefox and Safari
90
+ // this may be removed once fully supported in Firefox and Safari: https://caniuse.com/wf-field-sizing
91
+ if(input.getAttribute("data-auto-grow")!==null)if(!("fieldSizing"in document.createElement("input").style)){const adjustHeight=()=>{if(input.value.length){const borderWidth=parseInt(window.getComputedStyle(input).getPropertyValue("border-width"));input.style.height=input.scrollHeight+borderWidth*2+"px"}else input.style.height=window.getComputedStyle(form).getPropertyValue("--semanticFormsInputHeight")};
92
+ // set initial height to semantic-forms CSS variable
93
+ input.style.height=window.getComputedStyle(form).getPropertyValue("--semanticFormsInputHeight");input.addEventListener("input",adjustHeight)}
94
+ // shifts the clear button to the right if a scrollbar is present
95
+ const shiftClearBtn=()=>{const clearBtn=input.parentElement?.querySelector("button.clear");if(clearBtn)clearBtn.style.marginRight=input.clientHeight<input.scrollHeight?"15px":""};input.addEventListener("input",shiftClearBtn);input.addEventListener("mouseup",shiftClearBtn);shiftClearBtn()}};const enhanceInput=(input,form)=>{
55
96
  // ignore input if it has previously been formatted
56
97
  if(input.classList.contains("semanticform")||!input.id)return;const type=input.getAttribute("type");if(!lookups.nodeName.includes(input.nodeName)&&!lookups.inputType.includes(type))return;
98
+ // inside a customLayout element the app lays out the <dl> itself, so nothing is added, moved or rewritten around the field: no float label, no clear or show password button, no wrapping <div>, no required asterisk or help icon, and no value appended to the label of a range. the control itself is still enhanced. it is marked like any other enhanced field so that reinitializing does not enhance it twice
99
+ if(input.closest(".customLayout")){input.classList.add("semanticform");enhanceControl(enhanceAutoGrow(input,form),type,form);return}
57
100
  // recursively find <dl> element
58
101
  let dl=input.parentNode;while(dl&&dl.nodeName!=="DL")dl=dl.parentNode;if(!dl)return;if(!dl.classList.contains("floatLabelForm"))dl.classList.add("floatLabelForm");
59
102
  // the label is looked up in the input's own tree rather than the document, so that a form inside a shadow root finds its labels instead of coming up empty
@@ -70,12 +113,7 @@ dt.before(div);div.append(dt,dd);
70
113
  if(dt.style.display==="none"&&dd.style.display==="none")div.style.display="none"}}else{newLabel.setAttribute("for",input.id);newLabel.innerHTML=label.innerHTML;if(input.hasAttribute("title")&&label.getAttribute("data-show-help-icon")!==null&&!label.querySelector("span.help")){const text=input.getAttribute("title");newLabel.innerHTML+=` <span title="${text}" class="help">${icons.helpIcon}</span>`}if(input.hasAttribute("required")&&label.getAttribute("data-no-asterisk")===null&&!label.querySelector("span.required")){const text=label.getAttribute("data-asterisk-text")||"This field is required.";newLabel.innerHTML+=` <span title="${text}" class="required">*</span>`}label.setAttribute("hidden","hidden");insertAfter(newLabel,input)}
71
114
  // #endregion
72
115
  // #region standard inputs
73
- // check for auto-grow attribute on textareas
74
- if(input.getAttribute("data-auto-grow")!==null){
75
- // progressively enhance inputs into textareas
76
- if(input.nodeName==="INPUT"&&input.type==="text"){const newInput=document.createElement("textarea");newInput.id=input.id;newInput.class=input.class;newInput.innerText=input.value;newInput.setAttribute("data-auto-grow","");input.replaceWith(newInput);input=newInput}if(input.nodeName==="TEXTAREA")
77
- // when pressing enter while this input is focused, we want to submit
78
- input.addEventListener("keypress",e=>{if(e.key!=="Enter"||e.key==="Enter"&&e.shiftKey)return;e.preventDefault();form.requestSubmit()})}if(type!=="checkbox"&&type!=="radio"){if(!input.getAttribute("placeholder"))input.setAttribute("placeholder"," ");const div=isWrapped?input.closest("dd").parentElement:document.createElement("div");const dt=label.closest("dt");const dd=input.closest("dd");if(!dt||!dd){console.error(`semantic-forms: Found an input (${input.id||input.getAttribute("name")}) that does not have a corresponding ${!dt?"<dt>":"<dd>"} element.`);return}
116
+ input=enhanceAutoGrow(input,form);if(type!=="checkbox"&&type!=="radio"){if(!input.getAttribute("placeholder"))input.setAttribute("placeholder"," ");const div=isWrapped?input.closest("dd").parentElement:document.createElement("div");const dt=label.closest("dt");const dd=input.closest("dd");if(!dt||!dd){console.error(`semantic-forms: Found an input (${input.id||input.getAttribute("name")}) that does not have a corresponding ${!dt?"<dt>":"<dd>"} element.`);return}
79
117
  // input clear button. a field the user cannot edit does not get one, since the button would empty it anyway. a field that only becomes uneditable later is handled in css
80
118
  if(input.nodeName!=="SELECT"&&type!=="range"&&!input.readOnly&&!input.disabled){const clearBtn=document.createElement("button");clearBtn.type="button";clearBtn.title=input.getAttribute("data-clear-field-text")||"Clear field";clearBtn.ariaLabel=input.getAttribute("data-clear-field-text")||"Clear field";clearBtn.tabIndex=-1;clearBtn.innerHTML='<svg viewBox="0 0 16 16" width="18" height="18"><path d="M 1 1 L 15 15 M 1 15 L 15 1" fill="none" stroke-width="2" stroke="currentColor" />';clearBtn.classList.add("clear");clearBtn.id=`semanticFormsClearButton_${input.id}`;clearBtn.addEventListener("click",()=>{input.previousValue=input.value;input.value="";input.focus();lastClearFieldPressed=input.id;
81
119
  // used for any other updates required by various inputs
@@ -97,38 +135,7 @@ if(type==="file"){const clearBtn=input.parentElement.querySelector(".clear");inp
97
135
  // handle range inputs set to display the value. every other per-field flag in the library is a data attribute, so that is the documented way to ask for this, but the original `displayValue` class is still honored
98
136
  if(type==="range"&&(input.getAttribute("data-display-value")!==null||input.classList.contains("displayValue"))){const label=input.parentNode.parentNode.querySelector("dd label");label.innerHTML+=`<span class="seperator">: </span><output>${input.value}</output>`;input.addEventListener("input",event=>{const output=event.target.parentNode.parentNode.querySelector("output");output.innerHTML=event.target.value})}
99
137
  // show password button
100
- if(type==="password"&&input.getAttribute("data-no-reveal")===null){const showBtn=document.createElement("button");showBtn.type="button";showBtn.title=input.getAttribute("data-show-password-text")||"Show password";showBtn.ariaLabel=input.getAttribute("data-show-password-text")||"Show password";showBtn.tabIndex=-1;showBtn.innerHTML=icons.passwordShow;showBtn.classList.add("show");showBtn.id=`semanticFormsShowButton_${input.id}`;const dd=input.closest("dd");if(!dd){console.error(`semantic-forms: Found an input (${input.id||input.getAttribute("name")}) that is not inside a <dd> element.`);return}showBtn.addEventListener("click",()=>{if(input.type==="password"){showBtn.innerHTML=icons.passwordHide;showBtn.title=input.getAttribute("data-hide-password-text")||"Hide password";showBtn.ariaLabel=input.getAttribute("data-hide-password-text")||"Hide password";input.type="text"}else{showBtn.innerHTML=icons.passwordShow;showBtn.title=input.getAttribute("data-show-password-text")||"Show password";showBtn.ariaLabel=input.getAttribute("data-show-password-text")||"Show password";input.type="password"}input.focus()});insertAfter(showBtn,dd.querySelector("label"))}
101
- // a checkbox with the switch attribute is a switch; the stylesheet draws one in every browser, and where the attribute is not implemented natively the role and dragging are supplied here
102
- if(type==="checkbox"&&input.hasAttribute("switch")&&!nativeSwitchSupport){input.setAttribute("role","switch");enableSwitchDragging(input)}
103
- // add listener to shift clear button when scrollbar present
104
- if(input.nodeName==="TEXTAREA"){if(input.getAttribute("data-disable-autosize")===null){
105
- // add auto-sizing
106
- input.style.setProperty("resize","none");input.style.setProperty("min-height","0");input.style.setProperty("max-height","none");input.style.setProperty("height","auto");const handleInput=()=>{
107
- // reset rows attribute to get accurate scrollHeight
108
- let maxRows=input.getAttribute("data-max-rows");if(maxRows)if(isNaN(maxRows)||Number(maxRows)<=0){console.warn(`An invalid value was passed to the "data-max-rows" attribute. This value will be ignored.\n\nProvided value: ${input.getAttribute("data-max-rows")}`);maxRows=null}const minRows=input.getAttribute("data-max-rows")&&Number(input.getAttribute("data-max-rows"))<5?input.getAttribute("data-max-rows"):"5";input.setAttribute("rows",minRows);
109
- // get the computed values object reference
110
- const style=window.getComputedStyle(input);
111
- // force content-box for size accurate line-height calculation, remove scrollbars, lock width (subtract inline padding and inline border widths), and remove inline padding and borders to keep width consistent (for text wrapping accuracy)
112
- const inlinePadding=parseFloat(style["padding-left"])+parseFloat(style["padding-right"]);const inlineBorderWidth=parseFloat(style["border-left-width"])+parseFloat(style["border-right-width"]);input.style.setProperty("overflow","hidden","important");input.style.setProperty("width",parseFloat(style.width)-inlinePadding-inlineBorderWidth+"px");input.style.setProperty("box-sizing","content-box");input.style.setProperty("padding-inline","0");input.style.setProperty("border-width","0");
113
- // get the base line height, and top / bottom padding
114
- const blockPadding=parseFloat(style["padding-top"])+parseFloat(style["padding-bottom"]);const lineHeight=style["line-height"]==="normal"?parseFloat(style.height):parseFloat(style["line-height"]);// otherwise (line-height is explicitly set), use the computed line-height value
115
- // get the scroll height (rounding to be safe to ensure cross browser consistency)
116
- const scrollHeight=Math.round(input.scrollHeight);
117
- // undo overflow, width, border-width, box-sizing & inline padding overrides
118
- input.style.removeProperty("width");input.style.removeProperty("box-sizing");input.style.removeProperty("padding-inline");input.style.removeProperty("border-width");input.style.removeProperty("overflow");
119
- // subtract blockPadding from scrollHeight and divide that by our lineHeight to get the row count, round to nearest integer as it will always be within ~.1 of the correct whole number
120
- const rows=Math.round((scrollHeight-blockPadding)/lineHeight);
121
- // set the calculated rows attribute (limited by rowLimit)
122
- if(maxRows)input.setAttribute("rows",""+Math.min(rows,Number(maxRows)));else input.setAttribute("rows",""+rows)};input.addEventListener("input",handleInput);
123
- // trigger the event to set the initial rows value
124
- input.dispatchEvent(new Event("input",{bubbles:true}))}
125
- // progressively enhance textarea for Firefox and Safari
126
- // this may be removed once fully supported in Firefox and Safari: https://caniuse.com/wf-field-sizing
127
- if(input.getAttribute("data-auto-grow")!==null)if(!("fieldSizing"in document.createElement("input").style)){const adjustHeight=()=>{if(input.value.length){const borderWidth=parseInt(window.getComputedStyle(input).getPropertyValue("border-width"));input.style.height=input.scrollHeight+borderWidth*2+"px"}else input.style.height=window.getComputedStyle(form).getPropertyValue("--semanticFormsInputHeight")};
128
- // set initial height to semantic-forms CSS variable
129
- input.style.height=window.getComputedStyle(form).getPropertyValue("--semanticFormsInputHeight");input.addEventListener("input",adjustHeight)}
130
- // shifts the clear button to the right if a scrollbar is present
131
- const shiftClearBtn=()=>{const clearBtn=input.parentElement?.querySelector("button.clear");if(clearBtn)clearBtn.style.marginRight=input.clientHeight<input.scrollHeight?"15px":""};input.addEventListener("input",shiftClearBtn);input.addEventListener("mouseup",shiftClearBtn);shiftClearBtn()}};const handleUndoRedo=event=>{
138
+ if(type==="password"&&input.getAttribute("data-no-reveal")===null){const showBtn=document.createElement("button");showBtn.type="button";showBtn.title=input.getAttribute("data-show-password-text")||"Show password";showBtn.ariaLabel=input.getAttribute("data-show-password-text")||"Show password";showBtn.tabIndex=-1;showBtn.innerHTML=icons.passwordShow;showBtn.classList.add("show");showBtn.id=`semanticFormsShowButton_${input.id}`;const dd=input.closest("dd");if(!dd){console.error(`semantic-forms: Found an input (${input.id||input.getAttribute("name")}) that is not inside a <dd> element.`);return}showBtn.addEventListener("click",()=>{if(input.type==="password"){showBtn.innerHTML=icons.passwordHide;showBtn.title=input.getAttribute("data-hide-password-text")||"Hide password";showBtn.ariaLabel=input.getAttribute("data-hide-password-text")||"Hide password";input.type="text"}else{showBtn.innerHTML=icons.passwordShow;showBtn.title=input.getAttribute("data-show-password-text")||"Show password";showBtn.ariaLabel=input.getAttribute("data-show-password-text")||"Show password";input.type="password"}input.focus()});insertAfter(showBtn,dd.querySelector("label"))}enhanceControl(input,type,form)};const handleUndoRedo=event=>{
132
139
  // handle undo/redo
133
140
  if((event.ctrlKey||event.metaKey)&&event.key==="z"&&!event.shiftKey){
134
141
  // undo clearing a field
@@ -164,7 +171,9 @@ if(["alt","opt"].includes(modifierKey))modifierSymbol=os==="mac"?"⌥":"⎇";els
164
171
  // add the shortcut to the cached array, if not a duplicate
165
172
  if(shortcuts.some(shortcut=>shortcut.key===focusKey&&shortcut.modifier===modifierKey))console.error(`Duplicate keyboard shortcut "${modifierKey} + ${focusKey}" detected. Only the first input will be focusable using this keyboard shortcut.`,input);
166
173
  // set the shortcut indicator/title
167
- if(input.nodeName==="TEXTAREA"||input.type==="text"||input.type==="number"){
174
+
175
+ // the indicator is drawn over the field beside its float label, so a field in a customLayout element, which has no float label and is laid out by the app, gets the title instead, as the other kinds of field do
176
+ if((input.nodeName==="TEXTAREA"||input.type==="text"||input.type==="number")&&!input.closest(".customLayout")){
168
177
  // create focus indicator for valid inputs
169
178
  const indicator=document.createElement("span");indicator.classList.add("focus-key");indicator.innerHTML=`<kbd>${modifierSymbol} ${focusKey.toUpperCase()}</kbd>`;const label=input.nextSibling;insertAfter(indicator,label)}else
170
179
  // update the input title
@@ -1 +1 @@
1
- {"version":3,"file":"semantic-forms.mjs","mappings":"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","sources":["webpack://semantic-forms/./lib/helpers.js","webpack://semantic-forms/./lib/inputEnhancements.js","webpack://semantic-forms/./lib/keyboardShortcuts.js","webpack://semantic-forms/./lib/tabs.js","webpack://semantic-forms/./semanticForms.js","webpack://semantic-forms/webpack/bootstrap","webpack://semantic-forms/webpack/startup"],"sourcesContent":["// uses the navigator to best determine the client's operating system\n//\n// returns one of `mac`, `windows`, or `linux`\nconst getOS = () => {\n const userAgent = window.navigator.userAgent\n const platform = window.navigator.platform\n let os = null\n\n if (platform.includes('Win')) {\n os = 'windows'\n } else if (platform.includes('Mac') || /iPhone|iPad|iPod/.test(userAgent)) {\n os = 'mac'\n } else if (platform.includes('Linux') || /Android/.test(userAgent)) {\n os = 'linux'\n }\n return os\n}\n\n// places an element immediately after another element\n//\n// newNode: the element being placed after the reference node\n// referenceNode: the element the new node is placed after\nconst insertAfter = (newNode, referenceNode) => {\n if (referenceNode.nextSibling) referenceNode.parentNode.insertBefore(newNode, referenceNode.nextSibling)\n else referenceNode.parentNode.appendChild(newNode)\n}\n\n// works out how far a font's visible text sits from the middle of its own line box\n//\n// browsers place a line of text by the font's ascent and descent, which are rarely symmetrical, so the letters themselves end up slightly above or below the middle of the box holding them. that is invisible at the half pixel it usually amounts to, until a display without a high pixel density rounds it up to a whole one. the shift is the same for every element using the font, so it is measured once per font and reused\n//\n// returns the number of pixels the text sits below the middle of its box, negative when it sits above\nconst inkOffsets = new Map()\nconst getInkOffset = (fontStyle, fontWeight, fontSize, fontFamily) => {\n const key = `${fontStyle} ${fontWeight} ${fontSize} ${fontFamily}`\n if (inkOffsets.has(key)) return inkOffsets.get(key)\n\n // the ascent is where the baseline falls in a line box, which an inline-block aligned to the baseline reports directly\n const probe = document.createElement('div')\n probe.style.cssText = `position:absolute;left:-9999px;top:0;white-space:nowrap;visibility:hidden;line-height:normal;padding:0;border:0;margin:0;font-style:${fontStyle};font-weight:${fontWeight};font-size:${fontSize};font-family:${fontFamily}`\n const marker = document.createElement('span')\n marker.style.cssText = 'display:inline-block;width:0;height:0;vertical-align:baseline'\n probe.append('Hxn', marker)\n document.body.append(probe)\n const lineBox = probe.getBoundingClientRect()\n const ascent = marker.getBoundingClientRect().top - lineBox.top\n const descent = lineBox.height - ascent\n probe.remove()\n\n // the cap height is where the letters actually start, which only the drawn pixels can say. drawing it large keeps the answer accurate to a fraction of a pixel\n const scale = 8\n const size = parseFloat(fontSize)\n const canvas = document.createElement('canvas')\n canvas.width = Math.ceil(size * scale * 4)\n canvas.height = Math.ceil(size * scale * 3)\n const context = canvas.getContext('2d', { willReadFrequently: true })\n context.fillStyle = '#fff'\n context.fillRect(0, 0, canvas.width, canvas.height)\n context.fillStyle = '#000'\n context.font = `${fontStyle} ${fontWeight} ${size * scale}px ${fontFamily}`\n context.textBaseline = 'alphabetic'\n const baseline = Math.round(size * scale * 2)\n context.fillText('Hxn', 10, baseline)\n const pixels = context.getImageData(0, 0, canvas.width, canvas.height).data\n let inkTop = null\n for (let y = 0; y < canvas.height && inkTop === null; y++) {\n for (let x = 0; x < canvas.width; x++) {\n if (pixels[(y * canvas.width + x) * 4] < 128) {\n inkTop = y\n break\n }\n }\n }\n if (inkTop === null) {\n const none = { offset: 0, fieldOffset: 0 }\n inkOffsets.set(key, none)\n return none\n }\n const capHeight = (baseline - inkTop) / scale\n\n // rounded to whole device pixels, because browsers put a line of text on the pixel grid rather than at the fraction of one asked for. a correction finer than that either does nothing or moves the letters a whole pixel, depending on which side of the halfway mark the sum lands\n //\n // two roundings come out of this, because a measurement landing exactly halfway can go either way and the browser does not send every kind of text the same way. our own labels follow the ordinary rounding; the text a field draws inside itself, and the text on a button, follow the one that rounds a half away from zero. they differ only for a measurement of exactly minus a half, and agree everywhere else\n const ratio = window.devicePixelRatio || 1\n const exact = ((ascent - descent) / 2 - capHeight / 2) * ratio\n const offset = Math.round(exact) / ratio\n const fieldOffset = Math.sign(exact) * Math.round(Math.abs(exact)) / ratio\n const pair = { offset, fieldOffset }\n inkOffsets.set(key, pair)\n return pair\n}\n\n// reads the offset for a form's font, which needs its computed style, and puts it on the form as custom properties, for the stylesheet to subtract when it centers the float label on its field\n//\n// these are separate so that a batch of forms can have every one read before any is written to. reading a style after writing one makes the browser lay the page out again to answer, so measuring and writing one form at a time laid out the whole page once per form\nconst readInkOffset = label => {\n if (!label.isConnected) return null\n const style = window.getComputedStyle(label)\n return getInkOffset(style.fontStyle, style.fontWeight, style.fontSize, style.fontFamily)\n}\n\nconst writeInkOffset = (form, pair) => {\n form.style.setProperty('--semanticFormsTextInkOffset', `${pair.offset}px`)\n form.style.setProperty('--semanticFormsFieldTextInkOffset', `${pair.fieldOffset}px`)\n}\n\n// measures a batch of forms: every read first, then every write\nconst measureAll = forms => {\n const read = []\n for (const [form, label] of forms) {\n const pair = readInkOffset(label)\n if (pair) read.push([form, pair])\n }\n for (const [form, pair] of read) writeInkOffset(form, pair)\n}\n\n// a web font that is still downloading when a form is measured is measured in whichever font the page falls back to, and the letters move once the real one arrives. the forms measured so far are remembered so they can be measured again at that point, which the browser reports once for the page rather than once per font\nconst measured = new Map()\nlet awaitingFonts = false\nconst measure = forms => {\n measureAll(forms)\n if (!document.fonts || (document.fonts.status === 'loaded' && awaitingFonts)) return // measured in the fonts the page ended up with, so there is nothing to measure again for\n for (const [form, label] of forms) measured.set(form, label)\n\n if (awaitingFonts) return\n awaitingFonts = true\n document.fonts.ready.then(() => {\n // the cache is keyed by what the font is called, which does not change when the file behind it finally loads\n inkOffsets.clear()\n measureAll(measured)\n measured.clear()\n })\n}\n\n// measures each form once the browser has laid the page out, and a form off screen once it comes near the screen, rather than as it is enhanced\n//\n// reading a form's style while the page is still loading makes the browser lay the whole page out on the spot to answer, before it would have done so anyway. on a page with many forms, that cost several times what laying the page out once does. it also defeated content-visibility: auto, which only skips what is off screen once the browser has worked out what is, so reading anything in there beforehand rendered all of it. an intersection observer reports only after the browser has laid the page out, so reading a form's style is cheap by then, and reports a form off screen only as it comes near, so what content-visibility skips stays skipped\n//\n// the margin is wider than the one the browser renders skipped content within, so a form is measured just before it is drawn rather than once it already has been. a form on screen from the start is measured just after the page is first drawn, which can move its labels by the pixel the measurement corrects for, the same as when a web font arrives\nconst waiting = new Map()\nlet observer\nconst setInkOffset = (form, label) => {\n if (!window.IntersectionObserver) return measure([[form, label]])\n observer = observer || new window.IntersectionObserver(entries => {\n const seen = []\n for (const entry of entries) {\n if (!entry.isIntersecting || !waiting.has(entry.target)) continue\n observer.unobserve(entry.target)\n seen.push([entry.target, waiting.get(entry.target)])\n waiting.delete(entry.target)\n }\n if (seen.length) measure(seen) // every form reported at once is read before any is written to\n }, { rootMargin: '100% 0px' })\n waiting.set(form, label)\n observer.observe(form)\n}\n\nmodule.exports = { getOS, insertAfter, setInkOffset }\n","const { insertAfter } = require('./helpers.js')\n\nconst icons = {\n passwordShow: '<svg fill=\"none\" height=\"256\" viewBox=\"0 0 24 24\" width=\"256\" xmlns=\"http://www.w3.org/2000/svg\"><g stroke=\"currentColor\" stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\"><path d=\"m1 12s4-8 11-8 11 8 11 8\"/><path d=\"m1 12s4 8 11 8 11-8 11-8\"/><circle cx=\"12\" cy=\"12\" r=\"3\"/></g></svg>',\n passwordHide: '<svg fill=\"none\" height=\"256\" viewBox=\"0 0 24 24\" width=\"256\" xmlns=\"http://www.w3.org/2000/svg\"><g stroke=\"currentColor\" stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\"><path d=\"m2 2 20 20\"/><path d=\"m6.71277 6.7226c-3.04798 2.07267-4.71277 5.2774-4.71277 5.2774s3.63636 7 10 7c2.0503 0 3.8174-.7266 5.2711-1.7116m-6.2711-12.23018c.3254-.03809.6588-.05822 1-.05822 6.3636 0 10 7 10 7s-.6918 1.3317-2 2.8335\"/><path d=\"m14 14.2362c-.5308.475-1.2316.7639-2 .7639-1.6569 0-3-1.3431-3-3 0-.8237.33193-1.5698.86932-2.11192\"/></g></svg>',\n helpIcon: '<svg xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\" width=\"16\" height=\"16\" viewBox=\"0 0 16 16\" version=\"1.1\" style=\"overflow:visible;\"><rect width=\"16\" height=\"16\" fill=\"none\"/><path fill=\"currentColor\" d=\"M8,10.5c-0.552,0-1,0.448-1,1c0,0.552,0.448,1,1,1c0.552,0,1-0.448,1-1C9,10.948,8.552,10.5,8,10.5z M8,0 C3.582,0,0,3.582,0,8c0,4.418,3.582,8,8,8s8-3.582,8-8C16,3.582,12.418,0,8,0z M12.243,12.243C11.109,13.376,9.603,14,8,14 s-3.109-0.624-4.243-1.757C2.624,11.109,2,9.603,2,8s0.624-3.109,1.757-4.243C4.891,2.624,6.397,2,8,2s3.109,0.624,4.243,1.757 C13.376,4.891,14,6.397,14,8C14,9.603,13.376,11.109,12.243,12.243z M7.927,3.755c-2.248,0-2.76,1.695-2.802,2.906h1.571 c0.028-0.256,0.101-1.418,1.221-1.418c0.672,0,1.206,0.406,1.206,1.175c0,0.695-0.724,1.155-1.303,1.711 C7.241,8.685,7.168,9.325,7.168,9.824V10c0.146,0,1.615,0,1.615,0V9.824c0-1.256,1.967-1.594,1.967-3.541 C10.75,5.059,9.874,3.755,7.927,3.755z\"/></svg>'\n}\n\nconst lookups = {\n nodeName: ['TEXTAREA', 'SELECT'],\n inputType: ['checkbox', 'color', 'date', 'datetime-local', 'email', 'file', 'image', 'month', 'number', 'password', 'radio', 'range', 'search', 'tel', 'text', 'time', 'url', 'week']\n}\n\nlet lastFocusedInput\nlet lastClearFieldPressed\n\n// browsers that implement the switch attribute expose it on the input prototype, render the control as a switch and report the switch role themselves, so nothing below is needed there\nconst nativeSwitchSupport = 'switch' in window.HTMLInputElement.prototype\n\n// lets a polyfilled switch be toggled by dragging its thumb, which is what the native control does. a pointer that never travels far enough is left alone so it still lands as a click.\nconst enableSwitchDragging = (input) => {\n input.addEventListener('pointerdown', event => {\n if (input.disabled) return\n\n const startX = event.clientX\n const rightToLeft = window.getComputedStyle(input).direction === 'rtl'\n // half the track is the distance the thumb travels between the two positions\n const travel = input.clientWidth / 2 || 1\n const wasChecked = input.checked\n let dragging = false\n\n input.setPointerCapture(event.pointerId)\n\n const drag = moveEvent => {\n const distance = rightToLeft ? startX - moveEvent.clientX : moveEvent.clientX - startX\n if (!dragging && Math.abs(distance) <= 5) return // still within a click\n dragging = true\n moveEvent.preventDefault() // don't select or scroll while dragging\n input.checked = (wasChecked ? 1 : 0) + (distance / travel) > 0.5\n }\n\n const drop = upEvent => {\n input.releasePointerCapture(upEvent.pointerId)\n input.removeEventListener('pointermove', drag)\n input.removeEventListener('pointerup', drop)\n input.removeEventListener('pointercancel', drop)\n if (!dragging) return\n\n // the click that follows the drag would otherwise put the switch back where it started\n input.addEventListener('click', clickEvent => clickEvent.preventDefault(), { once: true, capture: true })\n\n if (input.checked !== wasChecked) {\n input.dispatchEvent(new Event('input', { bubbles: true }))\n input.dispatchEvent(new Event('change', { bubbles: true }))\n }\n }\n\n input.addEventListener('pointermove', drag)\n input.addEventListener('pointerup', drop)\n input.addEventListener('pointercancel', drop)\n })\n}\n\nconst enhanceInput = (input, form) => {\n // ignore input if it has previously been formatted\n if (input.classList.contains('semanticform') || !input.id) return\n\n const type = input.getAttribute('type')\n if (!lookups.nodeName.includes(input.nodeName) && !lookups.inputType.includes(type)) return\n\n // recursively find <dl> element\n let dl = input.parentNode\n while (dl && dl.nodeName !== 'DL') dl = dl.parentNode\n if (!dl) return\n if (!dl.classList.contains('floatLabelForm')) dl.classList.add('floatLabelForm')\n\n // the label is looked up in the input's own tree rather than the document, so that a form inside a shadow root finds its labels instead of coming up empty\n const tree = input.getRootNode()\n const label = input.parentNode.parentNode.id && (type === 'checkbox' || type === 'radio')\n ? tree.querySelector('label[data-for=' + input.parentNode.parentNode.id.replace(/\\./g, '\\\\.') + ']')\n : tree.querySelector('label[for=' + input.id.replace(/\\./g, '\\\\.') + ']')\n\n if (!label) {\n console.error(`semantic-forms: Found an input (${input.id || input.getAttribute('name')}) without a properly associated label. Make sure there is a label for it and the label has a matching \"for\" attribute.`)\n return\n }\n\n input.classList.add('semanticform')\n\n // an input outside a <dd> is reported by the checks further down, so tolerate it here\n const isWrapped = input.closest('dd')?.parentElement.nodeName === 'DIV'\n\n // #region create labels\n\n const newLabel = document.createElement('label')\n newLabel.className = 'floatLabelFormAnimatedLabel'\n\n if (type === 'checkbox' || type === 'radio') {\n const div = isWrapped ? input.closest('dd').parentElement : document.createElement('div')\n const dt = label.closest('dt')\n const dd = input.closest('dd')\n\n if (!dt || !dd) {\n console.error(`semantic-forms: Found an input (${input.id || input.getAttribute('name')}) that is not inside a <dd> element.`)\n return\n }\n\n if (dd.firstChild.nodeName !== 'LABEL') {\n if (type === 'checkbox' && input.parentNode.nodeName === 'DD') {\n newLabel.setAttribute('for', input.id)\n input.parentNode.classList.add('singleCheckbox')\n newLabel.className = ''\n label.setAttribute('hidden', 'hidden')\n insertAfter(newLabel, input)\n }\n\n if (type === 'radio' && input.parentNode.nodeName === 'DD') {\n newLabel.setAttribute('for', input.id)\n input.parentNode.classList.add('singleRadio')\n newLabel.className = ''\n label.setAttribute('hidden', 'hidden')\n insertAfter(newLabel, input)\n }\n\n newLabel.innerHTML = label.innerHTML\n\n if (label.hasAttribute('title') && label.getAttribute('data-show-help-icon') !== null && !label.querySelector('span.help')) {\n const text = label.getAttribute('title')\n label.innerHTML += ` <span title=\"${text}\" class=\"help\">${icons.helpIcon}</span>`\n newLabel.innerHTML += ` <span title=\"${text}\" class=\"help\">${icons.helpIcon}</span>`\n }\n\n if (dd.querySelector(':required') && label.getAttribute('data-no-asterisk') === null && !label.querySelector('span.required')) {\n const text = label.getAttribute('data-asterisk-text') || 'This field is required.'\n label.innerHTML += ` <span title=\"${text}\" class=\"required\">*</span>`\n newLabel.innerHTML += ` <span title=\"${text}\" class=\"required\">*</span>`\n }\n\n if (!dd.querySelector('label')) dd.append(newLabel)\n }\n\n // if the user did not wrap the input in a div, do it for them\n if (!isWrapped) {\n // the wrapper goes where the <dt> was, so the field keeps its place in the markup. a div the author wrote is already in the right place and is deliberately left where it is: moving it would push the field past anything the enhancer does not touch, such as a <dd> holding only a button\n dt.before(div)\n div.append(dt, dd)\n\n // determine visibility of newly created <div>\n if (dt.style.display === 'none' && dd.style.display === 'none') div.style.display = 'none'\n }\n } else {\n newLabel.setAttribute('for', input.id)\n newLabel.innerHTML = label.innerHTML\n\n if (input.hasAttribute('title') && label.getAttribute('data-show-help-icon') !== null && !label.querySelector('span.help')) {\n const text = input.getAttribute('title')\n newLabel.innerHTML += ` <span title=\"${text}\" class=\"help\">${icons.helpIcon}</span>`\n }\n\n if (input.hasAttribute('required') && label.getAttribute('data-no-asterisk') === null && !label.querySelector('span.required')) {\n const text = label.getAttribute('data-asterisk-text') || 'This field is required.'\n newLabel.innerHTML += ` <span title=\"${text}\" class=\"required\">*</span>`\n }\n\n label.setAttribute('hidden', 'hidden')\n\n insertAfter(newLabel, input)\n }\n // #endregion\n\n // #region standard inputs\n\n // check for auto-grow attribute on textareas\n if (input.getAttribute('data-auto-grow') !== null) {\n // progressively enhance inputs into textareas\n if (input.nodeName === 'INPUT' && input.type === 'text') {\n const newInput = document.createElement('textarea')\n newInput.id = input.id\n newInput.class = input.class\n newInput.innerText = input.value\n newInput.setAttribute('data-auto-grow', '')\n input.replaceWith(newInput)\n input = newInput\n }\n\n if (input.nodeName === 'TEXTAREA') {\n // when pressing enter while this input is focused, we want to submit\n input.addEventListener('keypress', (e) => {\n if (e.key !== 'Enter' || (e.key === 'Enter' && e.shiftKey)) return\n e.preventDefault()\n form.requestSubmit()\n })\n }\n }\n\n if (type !== 'checkbox' && type !== 'radio') {\n if (!input.getAttribute('placeholder')) input.setAttribute('placeholder', ' ')\n\n const div = isWrapped ? input.closest('dd').parentElement : document.createElement('div')\n const dt = label.closest('dt')\n const dd = input.closest('dd')\n\n if (!dt || !dd) {\n console.error(`semantic-forms: Found an input (${input.id || input.getAttribute('name')}) that does not have a corresponding ${!dt ? '<dt>' : '<dd>'} element.`)\n return\n }\n\n // input clear button. a field the user cannot edit does not get one, since the button would empty it anyway. a field that only becomes uneditable later is handled in css\n if (input.nodeName !== 'SELECT' && type !== 'range' && !input.readOnly && !input.disabled) {\n const clearBtn = document.createElement('button')\n clearBtn.type = 'button'\n clearBtn.title = input.getAttribute('data-clear-field-text') || 'Clear field'\n clearBtn.ariaLabel = input.getAttribute('data-clear-field-text') || 'Clear field'\n clearBtn.tabIndex = -1\n clearBtn.innerHTML = '<svg viewBox=\"0 0 16 16\" width=\"18\" height=\"18\"><path d=\"M 1 1 L 15 15 M 1 15 L 15 1\" fill=\"none\" stroke-width=\"2\" stroke=\"currentColor\" />'\n clearBtn.classList.add('clear')\n clearBtn.id = `semanticFormsClearButton_${input.id}`\n clearBtn.addEventListener('click', () => {\n input.previousValue = input.value\n input.value = ''\n input.focus()\n lastClearFieldPressed = input.id\n\n // used for any other updates required by various inputs\n input.dispatchEvent(new Event('input', { bubbles: true }))\n })\n insertAfter(clearBtn, dd.querySelector('label'))\n }\n\n input.addEventListener('focus', event => {\n if (event.target.nodeName === 'INPUT') lastFocusedInput = event.target\n })\n\n // the colspan- and col- utility classes are documented as going on the dd, but they size and place the grid item, which is the div wrapping it, so move any of them across\n for (const utility of dd.className.match(/colspan-(?:[0-9]|full)|col-[0-9]/g) || []) {\n dd.classList.remove(utility)\n div.classList.add(utility)\n }\n\n // check for max-content attribute\n // this may be removed once fully supported in Firefox and Safari: https://caniuse.com/wf-field-sizing\n if (input.getAttribute('data-max-content') !== null) {\n if (!('fieldSizing' in document.createElement('input').style)) {\n const adjustWidth = () => {\n const value = input.value !== '' ? input.value : input.placeholder\n const width = value.length * 8 + 40\n input.style.width = width + 'px'\n input.style.maxWidth = '100%'\n }\n adjustWidth()\n input.addEventListener('input', adjustWidth)\n }\n }\n\n // if the user did not wrap the input in a div, do it for them\n if (!isWrapped) {\n // as above, the wrapper takes the <dt>'s position rather than being appended to the end\n dt.before(div)\n div.append(dt, dd)\n\n // determine visibility of newly created <div>\n if (dt.style.display === 'none' && dd.style.display === 'none') div.style.display = 'none'\n }\n }\n // #endregion\n\n // handle file input clear btn, cannot be handled with CSS\n if (type === 'file') {\n const clearBtn = input.parentElement.querySelector('.clear')\n input.addEventListener('input', event => {\n clearBtn.style.display = event.target.files.length ? 'flex' : 'none'\n })\n clearBtn.addEventListener('click', () => {\n clearBtn.style.display = 'none'\n })\n }\n\n // handle range inputs set to display the value. every other per-field flag in the library is a data attribute, so that is the documented way to ask for this, but the original `displayValue` class is still honored\n if (type === 'range' && (input.getAttribute('data-display-value') !== null || input.classList.contains('displayValue'))) {\n const label = input.parentNode.parentNode.querySelector('dd label')\n label.innerHTML += `<span class=\"seperator\">: </span><output>${input.value}</output>`\n input.addEventListener('input', event => {\n const output = event.target.parentNode.parentNode.querySelector('output')\n output.innerHTML = event.target.value\n })\n }\n\n // show password button\n if (type === 'password' && input.getAttribute('data-no-reveal') === null) {\n const showBtn = document.createElement('button')\n showBtn.type = 'button'\n showBtn.title = input.getAttribute('data-show-password-text') || 'Show password'\n showBtn.ariaLabel = input.getAttribute('data-show-password-text') || 'Show password'\n showBtn.tabIndex = -1\n showBtn.innerHTML = icons.passwordShow\n showBtn.classList.add('show')\n showBtn.id = `semanticFormsShowButton_${input.id}`\n const dd = input.closest('dd')\n if (!dd) {\n console.error(`semantic-forms: Found an input (${input.id || input.getAttribute('name')}) that is not inside a <dd> element.`)\n return\n }\n showBtn.addEventListener('click', () => {\n if (input.type === 'password') {\n showBtn.innerHTML = icons.passwordHide\n showBtn.title = input.getAttribute('data-hide-password-text') || 'Hide password'\n showBtn.ariaLabel = input.getAttribute('data-hide-password-text') || 'Hide password'\n input.type = 'text'\n } else {\n showBtn.innerHTML = icons.passwordShow\n showBtn.title = input.getAttribute('data-show-password-text') || 'Show password'\n showBtn.ariaLabel = input.getAttribute('data-show-password-text') || 'Show password'\n input.type = 'password'\n }\n input.focus()\n })\n insertAfter(showBtn, dd.querySelector('label'))\n }\n\n // a checkbox with the switch attribute is a switch; the stylesheet draws one in every browser, and where the attribute is not implemented natively the role and dragging are supplied here\n if (type === 'checkbox' && input.hasAttribute('switch') && !nativeSwitchSupport) {\n input.setAttribute('role', 'switch')\n enableSwitchDragging(input)\n }\n\n // add listener to shift clear button when scrollbar present\n if (input.nodeName === 'TEXTAREA') {\n if (input.getAttribute('data-disable-autosize') === null) {\n // add auto-sizing\n input.style.setProperty('resize', 'none')\n input.style.setProperty('min-height', '0')\n input.style.setProperty('max-height', 'none')\n input.style.setProperty('height', 'auto')\n\n const handleInput = () => {\n // reset rows attribute to get accurate scrollHeight\n let maxRows = input.getAttribute('data-max-rows')\n\n if (maxRows) {\n if (isNaN(maxRows) || Number(maxRows) <= 0) {\n console.warn(`An invalid value was passed to the \"data-max-rows\" attribute. This value will be ignored.\\n\\nProvided value: ${input.getAttribute('data-max-rows')}`)\n maxRows = null\n }\n }\n\n const minRows = input.getAttribute('data-max-rows') && Number(input.getAttribute('data-max-rows')) < 5 ? input.getAttribute('data-max-rows') : '5'\n input.setAttribute('rows', minRows)\n\n // get the computed values object reference\n const style = window.getComputedStyle(input)\n\n // force content-box for size accurate line-height calculation, remove scrollbars, lock width (subtract inline padding and inline border widths), and remove inline padding and borders to keep width consistent (for text wrapping accuracy)\n const inlinePadding = parseFloat(style['padding-left']) + parseFloat(style['padding-right'])\n const inlineBorderWidth = parseFloat(style['border-left-width']) + parseFloat(style['border-right-width'])\n input.style.setProperty('overflow', 'hidden', 'important')\n input.style.setProperty('width', (parseFloat(style.width) - inlinePadding - inlineBorderWidth) + 'px')\n input.style.setProperty('box-sizing', 'content-box')\n input.style.setProperty('padding-inline', '0')\n input.style.setProperty('border-width', '0')\n\n // get the base line height, and top / bottom padding\n const blockPadding = parseFloat(style['padding-top']) + parseFloat(style['padding-bottom'])\n const lineHeight = style['line-height'] === 'normal'\n ? parseFloat(style.height) // if line-height is not explicitly set, use the computed height value (ignore padding due to content-box)\n : parseFloat(style['line-height']) // otherwise (line-height is explicitly set), use the computed line-height value\n\n // get the scroll height (rounding to be safe to ensure cross browser consistency)\n const scrollHeight = Math.round(input.scrollHeight)\n\n // undo overflow, width, border-width, box-sizing & inline padding overrides\n input.style.removeProperty('width')\n input.style.removeProperty('box-sizing')\n input.style.removeProperty('padding-inline')\n input.style.removeProperty('border-width')\n input.style.removeProperty('overflow')\n\n // subtract blockPadding from scrollHeight and divide that by our lineHeight to get the row count, round to nearest integer as it will always be within ~.1 of the correct whole number\n const rows = Math.round((scrollHeight - blockPadding) / lineHeight)\n\n // set the calculated rows attribute (limited by rowLimit)\n if (maxRows) {\n input.setAttribute('rows', '' + Math.min(rows, Number(maxRows)))\n } else {\n input.setAttribute('rows', '' + rows)\n }\n }\n\n input.addEventListener('input', handleInput)\n\n // trigger the event to set the initial rows value\n input.dispatchEvent(new Event('input', { bubbles: true }))\n }\n\n // progressively enhance textarea for Firefox and Safari\n // this may be removed once fully supported in Firefox and Safari: https://caniuse.com/wf-field-sizing\n if (input.getAttribute('data-auto-grow') !== null) {\n if (!('fieldSizing' in document.createElement('input').style)) {\n const adjustHeight = () => {\n if (input.value.length) {\n const borderWidth = parseInt(window.getComputedStyle(input).getPropertyValue('border-width'))\n input.style.height = (input.scrollHeight + (borderWidth * 2)) + 'px'\n } else {\n input.style.height = window.getComputedStyle(form).getPropertyValue('--semanticFormsInputHeight')\n }\n }\n\n // set initial height to semantic-forms CSS variable\n input.style.height = window.getComputedStyle(form).getPropertyValue('--semanticFormsInputHeight')\n input.addEventListener('input', adjustHeight)\n }\n }\n\n // shifts the clear button to the right if a scrollbar is present\n const shiftClearBtn = () => {\n const clearBtn = input.parentElement?.querySelector('button.clear')\n if (clearBtn) clearBtn.style.marginRight = input.clientHeight < input.scrollHeight ? '15px' : ''\n }\n input.addEventListener('input', shiftClearBtn)\n input.addEventListener('mouseup', shiftClearBtn)\n shiftClearBtn()\n }\n}\n\nconst handleUndoRedo = (event) => {\n // handle undo/redo\n if ((event.ctrlKey || event.metaKey) && event.key === 'z' && !event.shiftKey) {\n // undo clearing a field\n if (lastFocusedInput) {\n if (lastFocusedInput?.parentNode?.querySelector('button.clear')?.id === `semanticFormsClearButton_${lastClearFieldPressed}` || lastFocusedInput?.parentNode?.querySelector('button.clear')?.name === `semanticFormsClearButton_${lastClearFieldPressed}`) {\n if (lastFocusedInput.previousValue) {\n // the browser undoes the field's own edit history on this key too, which would apply on top of the value restored here, so the native behavior is suppressed\n event.preventDefault()\n lastFocusedInput.redoValue = lastFocusedInput.value\n lastFocusedInput.value = lastFocusedInput.previousValue\n }\n }\n }\n } else {\n if ((event.ctrlKey && event.key === 'y') || (event.metaKey && event.shiftKey && event.key === 'z')) {\n // redo clearing a field\n if (lastFocusedInput) {\n if (lastFocusedInput?.parentNode?.querySelector('button.clear')?.id === `semanticFormsClearButton_${lastClearFieldPressed}` || lastFocusedInput?.parentNode?.querySelector('button.clear')?.name === `semanticFormsClearButton_${lastClearFieldPressed}`) {\n if (lastFocusedInput.redoValue) {\n // as above, the browser's own redo would otherwise apply on top of this\n event.preventDefault()\n lastFocusedInput.previousValue = lastFocusedInput.value\n lastFocusedInput.value = lastFocusedInput.redoValue\n }\n }\n }\n }\n }\n}\n\nmodule.exports = { enhanceInput, handleUndoRedo }\n","const { getOS, insertAfter } = require('./helpers.js')\n\nconst specialCharMap = {\n Minus: '-',\n Equal: '=',\n BracketLeft: '[',\n BracketRight: ']',\n Backslash: '\\\\',\n Semicolon: ';',\n Quote: '\\'',\n Comma: ',',\n Period: '.',\n Slash: '/',\n Backquote: '`'\n}\nconst shiftSpecialCharMap = {\n 1: '!',\n 2: '@',\n 3: '#',\n 4: '$',\n 5: '%',\n 6: '^',\n 7: '&',\n 8: '*',\n 9: '(',\n 0: ')',\n Minus: '_',\n Equal: '+',\n BracketLeft: '{',\n BracketRight: '}',\n Backslash: '|',\n Semicolon: ':',\n Quote: '\"',\n Comma: '<',\n Period: '>',\n Slash: '?',\n Backquote: '~'\n}\n\n// handles keyboard shortcut events\nconst shortcutListener = (e, shortcuts) => {\n // search for matching shortcut from cached shortcut configs\n const shortcut = shortcuts.find(shortcut => {\n let matchesKey = false\n if (e.altKey && !e.shiftKey) {\n // mac adjusts the key value if altKey is pressed\n matchesKey = 'Key' + shortcut.key.toUpperCase() === e.code ||\n 'Digit' + shortcut.key.toUpperCase() === e.code ||\n shortcut.key === e.key ||\n specialCharMap[e.code] === shortcut.key\n } else if (e.shiftKey) {\n // check shift special character map\n const code = e.code.replace(/Key|Digit/, '')\n matchesKey = (shiftSpecialCharMap[code] || shiftSpecialCharMap[e.key]) &&\n (shiftSpecialCharMap[code] === shortcut.key || shiftSpecialCharMap[e.key] === shortcut.key)\n } else {\n matchesKey = shortcut.key.toUpperCase() === e.key.toUpperCase()\n }\n if (!matchesKey) return false\n\n let matchesModifier\n if (shortcut.modifier) {\n if (shortcut.modifier === shortcut.defaultModifier) matchesModifier = shortcut.os === 'windows' || shortcut.os === 'linux' ? e.ctrlKey : e.metaKey\n if (shortcut.modifier === 'meta') matchesModifier = e.metaKey\n if (shortcut.modifier === 'alt') matchesModifier = e.altKey\n if (shortcut.modifier === 'ctrl') matchesModifier = e.ctrlKey\n }\n return matchesModifier\n })\n\n if (shortcut) {\n e.preventDefault()\n shortcut.input.focus()\n }\n}\n\nconst createKeyboardShortcut = (input, shortcuts) => {\n const os = getOS()\n // this is the custom keyword for meta on linux/mac, ctrl on windows\n const defaultModifier = 'metactrl'\n\n // get focus key value\n let focusKey = input.getAttribute('data-focus-key')\n if (focusKey.length > 1) {\n console.error(`Provided focus key \"${focusKey}\" is more than one character. Using first character only.`)\n focusKey = focusKey.toString()[0]\n }\n\n // get focus modifier value\n let modifierSymbol\n let modifierKey = defaultModifier\n const modifierAttr = {\n default: input.getAttribute('data-focus-modifier') || defaultModifier,\n linux: input.getAttribute('data-focus-modifier-linux'),\n mac: input.getAttribute('data-focus-modifier-mac'),\n windows: input.getAttribute('data-focus-modifier-win')\n }\n if (os && modifierAttr[os]) {\n // a specific modifier key has been set by the user\n modifierKey = modifierAttr[os]\n } else {\n modifierKey = modifierAttr.default\n }\n\n // validate passed in modifier\n const recognizedModifiers = ['ctrl', 'alt', 'opt', 'meta', 'cmd', defaultModifier]\n if (!recognizedModifiers.includes(modifierKey)) {\n console.error(`Received an unrecognized modifier, \"${modifierKey},\" defaulting to \"${defaultModifier}.\"`, input)\n modifierKey = defaultModifier\n }\n\n // retrieve modifier symbol\n if (['alt', 'opt'].includes(modifierKey)) {\n modifierSymbol = os === 'mac' ? '⌥' : '⎇'\n } else if (['meta', 'win', 'cmd'].includes(modifierKey) || (modifierKey === defaultModifier && os === 'mac')) {\n if (os === 'mac') {\n modifierSymbol = '⌘'\n } else if (os === 'linux') {\n modifierSymbol = '◆'\n } else {\n modifierSymbol = '⊞'\n }\n } else if (modifierKey === 'ctrl' || (modifierKey === defaultModifier && (os === 'windows' || os === 'linux'))) {\n if (os === 'mac') {\n modifierSymbol = '⌃'\n } else {\n modifierSymbol = 'Ctrl'\n }\n }\n\n // add the shortcut to the cached array, if not a duplicate\n if (shortcuts.some(shortcut => shortcut.key === focusKey && shortcut.modifier === modifierKey)) {\n console.error(`Duplicate keyboard shortcut \"${modifierKey} + ${focusKey}\" detected. Only the first input will be focusable using this keyboard shortcut.`, input)\n }\n\n // set the shortcut indicator/title\n if (input.nodeName === 'TEXTAREA' || input.type === 'text' || input.type === 'number') {\n // create focus indicator for valid inputs\n const indicator = document.createElement('span')\n indicator.classList.add('focus-key')\n indicator.innerHTML = `<kbd>${modifierSymbol} ${focusKey.toUpperCase()}</kbd>`\n\n const label = input.nextSibling\n insertAfter(indicator, label)\n } else {\n // update the input title\n if (input.getAttribute('title')) {\n input.setAttribute('title', input.getAttribute('title') + ` (${modifierSymbol} + ${focusKey})`)\n } else {\n input.setAttribute('title', `Focus with ${modifierSymbol} + ${focusKey}`)\n }\n }\n\n return {\n key: focusKey,\n modifier: modifierKey,\n input,\n os,\n defaultModifier\n }\n}\n\nmodule.exports = {\n shortcutListener,\n createKeyboardShortcut\n}\n","let tabGroupCount = 0\n\n// turns a group of fieldsets into a set of tabs\n//\n// the markup it enhances is an element with the `tabs` class holding one `<fieldset>` per tab, each titled by its `<legend>`. left alone that is a stack of labelled field groups, which is a perfectly usable form, so a browser without JavaScript still gets everything\n//\n// the tab that opens first is the one whose fieldset carries `data-selected`, or the first fieldset when none do\n//\n// form: form element to search for tab groups\nconst enhanceTabs = (form) => {\n for (const group of form.querySelectorAll('.tabs:not(.semanticFormsTabsActive)')) {\n const panels = Array.from(group.children).filter(child => child.nodeName === 'FIELDSET')\n\n // one fieldset is not a set of tabs, and no fieldsets means the class is on the wrong element\n if (panels.length < 2) {\n if (panels.length === 0) console.error('semantic-forms: Found a .tabs element with no <fieldset> children. Each tab needs to be a <fieldset> with a <legend> naming it.', group)\n continue\n }\n\n group.classList.add('semanticFormsTabsActive')\n const groupId = ++tabGroupCount\n const tabList = document.createElement('div')\n const tabs = []\n\n tabList.classList.add('tabList')\n tabList.setAttribute('role', 'tablist')\n\n // shows one panel and marks its tab as the selected one\n const selectTab = (index, moveFocus) => {\n for (const [i, panel] of panels.entries()) panel.hidden = i !== index\n for (const [i, tab] of tabs.entries()) {\n const selected = i === index\n tab.setAttribute('aria-selected', selected ? 'true' : 'false')\n\n // only the selected tab is in the tab order; the arrow keys reach the others\n tab.tabIndex = selected ? 0 : -1\n }\n if (moveFocus) tabs[index].focus()\n }\n\n for (const [index, panel] of panels.entries()) {\n const legend = panel.querySelector('legend')\n const tab = document.createElement('button')\n\n tab.type = 'button'\n tab.setAttribute('role', 'tab')\n tab.id = `semanticFormsTab_${groupId}_${index}`\n tab.textContent = legend ? legend.textContent : `Tab ${index + 1}`\n\n if (!panel.id) panel.id = `semanticFormsTabPanel_${groupId}_${index}`\n panel.setAttribute('role', 'tabpanel')\n panel.setAttribute('aria-labelledby', tab.id)\n tab.setAttribute('aria-controls', panel.id)\n\n // the tab now names the panel, so the legend would be read out twice\n if (legend) legend.hidden = true\n\n tab.addEventListener('click', () => selectTab(index))\n tabs.push(tab)\n tabList.append(tab)\n }\n\n // the arrow keys move between tabs, which is what a tablist is expected to do\n tabList.addEventListener('keydown', event => {\n const current = tabs.indexOf(document.activeElement)\n if (current === -1) return\n\n const keys = {\n ArrowRight: (current + 1) % tabs.length,\n ArrowLeft: (current - 1 + tabs.length) % tabs.length,\n Home: 0,\n End: tabs.length - 1\n }\n if (!(event.key in keys)) return\n\n event.preventDefault()\n selectTab(keys[event.key], true)\n })\n\n // the panel carrying [data-selected] is the one that opens, defaulting to the first. without javascript every panel is on the page at once, so the attribute only means anything once the tabs have been built\n const marked = panels.filter(panel => panel.hasAttribute('data-selected'))\n if (marked.length > 1) console.error('semantic-forms: Found more than one <fieldset> with a \"data-selected\" attribute in a tab group. Only the first one will be selected.', group)\n\n group.prepend(tabList)\n selectTab(marked.length ? panels.indexOf(marked[0]) : 0)\n }\n}\n\nmodule.exports = { enhanceTabs }\n","const { createKeyboardShortcut, shortcutListener } = require('./lib/keyboardShortcuts.js')\nconst { enhanceInput, handleUndoRedo } = require('./lib/inputEnhancements.js')\nconst { enhanceTabs } = require('./lib/tabs.js')\nconst { setInkOffset } = require('./lib/helpers.js')\n\n// one observer per tree that has been handed to semanticForms, so that a shadow root gets its own without replacing the document's\nconst observers = new WeakMap()\n\n// progressively enhances every semanticForms form in a tree\n//\n// root: document or shadow root to search; defaults to the main document\nconst semanticForms = (root = document) => {\n // do some feature detection so none of the JS executes if the browser is too old\n if (typeof document.getElementsByClassName !== 'function' || typeof document.querySelector !== 'function' || !document.body.classList || !window.MutationObserver) {\n console.warn('semantic-forms was loaded into an unsupported browser and will not execute.')\n return\n }\n\n // custom keyboard shortcut listener\n const keyboardShortcuts = []\n document.addEventListener('keydown', (e) => shortcutListener(e, keyboardShortcuts))\n\n // progressively enhance form elements that have the semanticForms class\n const forms = root.querySelectorAll('form.semanticForms:not(.semanticFormsActive), table.semanticForms:not(.semanticFormsActive)')\n\n for (const form of forms) {\n form.classList.add('semanticFormsActive')\n if (form.classList.contains('lowFlow')) continue\n\n // how far this font's letters sit from the middle of their own line box, which the stylesheet subtracts wherever it centers text. it is measured per form rather than per field, so that a form holding nothing but buttons is corrected too\n setInkOffset(form, form)\n\n // update each input in the semantic form\n const inputs = Array.from(form.querySelectorAll('input, textarea, select'))\n for (const input of inputs) {\n enhanceInput(input, form)\n\n // handle keyboard shortcuts\n if (input.getAttribute('data-focus-key') !== null) {\n const shortcut = createKeyboardShortcut(input, keyboardShortcuts)\n keyboardShortcuts.push(shortcut)\n }\n }\n\n // done after the inputs so that anything sizing itself measures while it is still on screen\n enhanceTabs(form)\n }\n\n // prevents multiple listeners\n document.removeEventListener('keydown', handleUndoRedo)\n document.addEventListener('keydown', handleUndoRedo)\n\n // monitor changes to this tree and enhance new semanticForms forms that get added to it\n if (!observers.has(root)) {\n const observer = new window.MutationObserver(mutations => {\n let stop = false\n for (const mutation of mutations) {\n for (const node of mutation.addedNodes) {\n if (node.nodeName === 'FORM' || node?.querySelector?.('form')) {\n semanticForms(root)\n stop = true\n }\n }\n if (stop) break\n }\n })\n\n // a shadow root is observed directly; the document is observed through its body\n observer.observe(root === document ? document.body : root, { attributes: false, childList: true, characterData: false, subtree: true })\n observers.set(root, observer)\n\n // long standing name for the document's observer, kept for anything already using it\n if (root === document) window.semanticFormsObserver = observer\n }\n\n semanticForms.reinitialize = form => {\n // with no form given this just rescans for forms that are not active yet\n if (!form) return semanticForms(root)\n\n form.classList.remove('semanticFormsActive')\n\n // the form knows which tree it belongs to, so a form in a shadow root reinitializes there\n semanticForms(form.getRootNode())\n }\n}\n\nmodule.exports = semanticForms\n","// The module cache\nconst __webpack_module_cache__ = {};\n\n// The require function\nfunction __webpack_require__(moduleId) {\n\t// Check if module is in cache\n\tconst cachedModule = __webpack_module_cache__[moduleId];\n\tif (cachedModule !== undefined) {\n\t\treturn cachedModule.exports;\n\t}\n\t// Create a new module (and put it into the cache)\n\tconst module = __webpack_module_cache__[moduleId] = {\n\t\t// no module.id needed\n\t\t// no module.loaded needed\n\t\texports: {}\n\t};\n\n\t// Execute the module function\n\tif (!(moduleId in __webpack_modules__)) {\n\t\tdelete __webpack_module_cache__[moduleId];\n\t\tconst e = new Error(\"Cannot find module '\" + moduleId + \"'\");\n\t\te.code = 'MODULE_NOT_FOUND';\n\t\tthrow e;\n\t}\n\t__webpack_modules__[moduleId](module, module.exports, __webpack_require__);\n\n\t// Return the exports of the module\n\treturn module.exports;\n}\n\n","// startup\n// Load entry module and return exports\n// This entry module is referenced by other modules so it can't be inlined\nlet __webpack_exports__ = __webpack_require__(\"./semanticForms.js\");\n"],"names":["getOS","userAgent","window","navigator","platform","os","includes","test","insertAfter","newNode","referenceNode","nextSibling","parentNode","insertBefore","appendChild","inkOffsets","Map","getInkOffset","fontStyle","fontWeight","fontSize","fontFamily","key","has","get","probe","document","createElement","style","cssText","marker","append","body","lineBox","getBoundingClientRect","ascent","top","descent","height","remove","scale","size","parseFloat","canvas","width","Math","ceil","context","getContext","willReadFrequently","fillStyle","fillRect","font","textBaseline","baseline","round","fillText","pixels","getImageData","data","inkTop","y","x","none","offset","fieldOffset","set","capHeight","ratio","devicePixelRatio","exact","sign","abs","pair","readInkOffset","label","isConnected","getComputedStyle","writeInkOffset","form","setProperty","measureAll","forms","read","push","measured","awaitingFonts","measure","fonts","status","ready","then","clear","waiting","observer","setInkOffset","IntersectionObserver","entries","seen","entry","isIntersecting","target","unobserve","delete","length","rootMargin","observe","module","exports","__webpack_require__","icons","passwordShow","passwordHide","helpIcon","lookups","nodeName","inputType","lastFocusedInput","lastClearFieldPressed","nativeSwitchSupport","HTMLInputElement","prototype","enableSwitchDragging","input","addEventListener","event","disabled","startX","clientX","rightToLeft","direction","travel","clientWidth","wasChecked","checked","dragging","setPointerCapture","pointerId","drag","moveEvent","distance","preventDefault","drop","upEvent","releasePointerCapture","removeEventListener","clickEvent","once","capture","dispatchEvent","Event","bubbles","enhanceInput","classList","contains","id","type","getAttribute","dl","add","tree","getRootNode","querySelector","replace","console","error","isWrapped","closest","parentElement","newLabel","className","div","dt","dd","firstChild","setAttribute","innerHTML","hasAttribute","text","before","display","newInput","class","innerText","value","replaceWith","e","shiftKey","requestSubmit","readOnly","clearBtn","title","ariaLabel","tabIndex","previousValue","focus","utility","match","adjustWidth","placeholder","maxWidth","files","output","showBtn","handleInput","maxRows","isNaN","Number","warn","minRows","inlinePadding","inlineBorderWidth","blockPadding","lineHeight","scrollHeight","removeProperty","rows","min","adjustHeight","borderWidth","parseInt","getPropertyValue","shiftClearBtn","marginRight","clientHeight","handleUndoRedo","ctrlKey","metaKey","name","redoValue","specialCharMap","Minus","Equal","BracketLeft","BracketRight","Backslash","Semicolon","Quote","Comma","Period","Slash","Backquote","shiftSpecialCharMap","shortcutListener","shortcuts","shortcut","find","matchesKey","altKey","toUpperCase","code","matchesModifier","modifier","defaultModifier","createKeyboardShortcut","focusKey","toString","modifierSymbol","modifierKey","modifierAttr","default","linux","mac","windows","recognizedModifiers","some","indicator","tabGroupCount","enhanceTabs","group","querySelectorAll","panels","Array","from","children","filter","child","groupId","tabList","tabs","selectTab","index","moveFocus","i","panel","hidden","tab","selected","legend","textContent","current","indexOf","activeElement","keys","ArrowRight","ArrowLeft","Home","End","marked","prepend","observers","WeakMap","semanticForms","root","getElementsByClassName","MutationObserver","keyboardShortcuts","inputs","mutations","stop","mutation","node","addedNodes","attributes","childList","characterData","subtree","semanticFormsObserver","reinitialize","__webpack_module_cache__","moduleId","cachedModule","undefined","__webpack_modules__","Error","__webpack_exports__"],"sourceRoot":""}
1
+ {"version":3,"file":"semantic-forms.mjs","mappings":"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","sources":["webpack://semantic-forms/./lib/helpers.js","webpack://semantic-forms/./lib/inputEnhancements.js","webpack://semantic-forms/./lib/keyboardShortcuts.js","webpack://semantic-forms/./lib/tabs.js","webpack://semantic-forms/./semanticForms.js","webpack://semantic-forms/webpack/bootstrap","webpack://semantic-forms/webpack/startup"],"sourcesContent":["// uses the navigator to best determine the client's operating system\n//\n// returns one of `mac`, `windows`, or `linux`\nconst getOS = () => {\n const userAgent = window.navigator.userAgent\n const platform = window.navigator.platform\n let os = null\n\n if (platform.includes('Win')) {\n os = 'windows'\n } else if (platform.includes('Mac') || /iPhone|iPad|iPod/.test(userAgent)) {\n os = 'mac'\n } else if (platform.includes('Linux') || /Android/.test(userAgent)) {\n os = 'linux'\n }\n return os\n}\n\n// places an element immediately after another element\n//\n// newNode: the element being placed after the reference node\n// referenceNode: the element the new node is placed after\nconst insertAfter = (newNode, referenceNode) => {\n if (referenceNode.nextSibling) referenceNode.parentNode.insertBefore(newNode, referenceNode.nextSibling)\n else referenceNode.parentNode.appendChild(newNode)\n}\n\n// works out how far a font's visible text sits from the middle of its own line box\n//\n// browsers place a line of text by the font's ascent and descent, which are rarely symmetrical, so the letters themselves end up slightly above or below the middle of the box holding them. that is invisible at the half pixel it usually amounts to, until a display without a high pixel density rounds it up to a whole one. the shift is the same for every element using the font, so it is measured once per font and reused\n//\n// returns the number of pixels the text sits below the middle of its box, negative when it sits above\nconst inkOffsets = new Map()\nconst getInkOffset = (fontStyle, fontWeight, fontSize, fontFamily) => {\n const key = `${fontStyle} ${fontWeight} ${fontSize} ${fontFamily}`\n if (inkOffsets.has(key)) return inkOffsets.get(key)\n\n // the ascent is where the baseline falls in a line box, which an inline-block aligned to the baseline reports directly\n const probe = document.createElement('div')\n probe.style.cssText = `position:absolute;left:-9999px;top:0;white-space:nowrap;visibility:hidden;line-height:normal;padding:0;border:0;margin:0;font-style:${fontStyle};font-weight:${fontWeight};font-size:${fontSize};font-family:${fontFamily}`\n const marker = document.createElement('span')\n marker.style.cssText = 'display:inline-block;width:0;height:0;vertical-align:baseline'\n probe.append('Hxn', marker)\n document.body.append(probe)\n const lineBox = probe.getBoundingClientRect()\n const ascent = marker.getBoundingClientRect().top - lineBox.top\n const descent = lineBox.height - ascent\n probe.remove()\n\n // the cap height is where the letters actually start, which only the drawn pixels can say. drawing it large keeps the answer accurate to a fraction of a pixel\n const scale = 8\n const size = parseFloat(fontSize)\n const canvas = document.createElement('canvas')\n canvas.width = Math.ceil(size * scale * 4)\n canvas.height = Math.ceil(size * scale * 3)\n const context = canvas.getContext('2d', { willReadFrequently: true })\n context.fillStyle = '#fff'\n context.fillRect(0, 0, canvas.width, canvas.height)\n context.fillStyle = '#000'\n context.font = `${fontStyle} ${fontWeight} ${size * scale}px ${fontFamily}`\n context.textBaseline = 'alphabetic'\n const baseline = Math.round(size * scale * 2)\n context.fillText('Hxn', 10, baseline)\n const pixels = context.getImageData(0, 0, canvas.width, canvas.height).data\n let inkTop = null\n for (let y = 0; y < canvas.height && inkTop === null; y++) {\n for (let x = 0; x < canvas.width; x++) {\n if (pixels[(y * canvas.width + x) * 4] < 128) {\n inkTop = y\n break\n }\n }\n }\n if (inkTop === null) {\n const none = { offset: 0, fieldOffset: 0 }\n inkOffsets.set(key, none)\n return none\n }\n const capHeight = (baseline - inkTop) / scale\n\n // rounded to whole device pixels, because browsers put a line of text on the pixel grid rather than at the fraction of one asked for. a correction finer than that either does nothing or moves the letters a whole pixel, depending on which side of the halfway mark the sum lands\n //\n // two roundings come out of this, because a measurement landing exactly halfway can go either way and the browser does not send every kind of text the same way. our own labels follow the ordinary rounding; the text a field draws inside itself, and the text on a button, follow the one that rounds a half away from zero. they differ only for a measurement of exactly minus a half, and agree everywhere else\n const ratio = window.devicePixelRatio || 1\n const exact = ((ascent - descent) / 2 - capHeight / 2) * ratio\n const offset = Math.round(exact) / ratio\n const fieldOffset = Math.sign(exact) * Math.round(Math.abs(exact)) / ratio\n const pair = { offset, fieldOffset }\n inkOffsets.set(key, pair)\n return pair\n}\n\n// reads the offset for a form's font, which needs its computed style, and puts it on the form as custom properties, for the stylesheet to subtract when it centers the float label on its field\n//\n// these are separate so that a batch of forms can have every one read before any is written to. reading a style after writing one makes the browser lay the page out again to answer, so measuring and writing one form at a time laid out the whole page once per form\nconst readInkOffset = label => {\n if (!label.isConnected) return null\n const style = window.getComputedStyle(label)\n return getInkOffset(style.fontStyle, style.fontWeight, style.fontSize, style.fontFamily)\n}\n\nconst writeInkOffset = (form, pair) => {\n form.style.setProperty('--semanticFormsTextInkOffset', `${pair.offset}px`)\n form.style.setProperty('--semanticFormsFieldTextInkOffset', `${pair.fieldOffset}px`)\n}\n\n// measures a batch of forms: every read first, then every write\nconst measureAll = forms => {\n const read = []\n for (const [form, label] of forms) {\n const pair = readInkOffset(label)\n if (pair) read.push([form, pair])\n }\n for (const [form, pair] of read) writeInkOffset(form, pair)\n}\n\n// a web font that is still downloading when a form is measured is measured in whichever font the page falls back to, and the letters move once the real one arrives. the forms measured so far are remembered so they can be measured again at that point, which the browser reports once for the page rather than once per font\nconst measured = new Map()\nlet awaitingFonts = false\nconst measure = forms => {\n measureAll(forms)\n if (!document.fonts || (document.fonts.status === 'loaded' && awaitingFonts)) return // measured in the fonts the page ended up with, so there is nothing to measure again for\n for (const [form, label] of forms) measured.set(form, label)\n\n if (awaitingFonts) return\n awaitingFonts = true\n document.fonts.ready.then(() => {\n // the cache is keyed by what the font is called, which does not change when the file behind it finally loads\n inkOffsets.clear()\n measureAll(measured)\n measured.clear()\n })\n}\n\n// measures each form once the browser has laid the page out, and a form off screen once it comes near the screen, rather than as it is enhanced\n//\n// reading a form's style while the page is still loading makes the browser lay the whole page out on the spot to answer, before it would have done so anyway. on a page with many forms, that cost several times what laying the page out once does. it also defeated content-visibility: auto, which only skips what is off screen once the browser has worked out what is, so reading anything in there beforehand rendered all of it. an intersection observer reports only after the browser has laid the page out, so reading a form's style is cheap by then, and reports a form off screen only as it comes near, so what content-visibility skips stays skipped\n//\n// the margin is wider than the one the browser renders skipped content within, so a form is measured just before it is drawn rather than once it already has been. a form on screen from the start is measured just after the page is first drawn, which can move its labels by the pixel the measurement corrects for, the same as when a web font arrives\nconst waiting = new Map()\nlet observer\nconst setInkOffset = (form, label) => {\n if (!window.IntersectionObserver) return measure([[form, label]])\n observer = observer || new window.IntersectionObserver(entries => {\n const seen = []\n for (const entry of entries) {\n if (!entry.isIntersecting || !waiting.has(entry.target)) continue\n observer.unobserve(entry.target)\n seen.push([entry.target, waiting.get(entry.target)])\n waiting.delete(entry.target)\n }\n if (seen.length) measure(seen) // every form reported at once is read before any is written to\n }, { rootMargin: '100% 0px' })\n waiting.set(form, label)\n observer.observe(form)\n}\n\nmodule.exports = { getOS, insertAfter, setInkOffset }\n","const { insertAfter } = require('./helpers.js')\n\nconst icons = {\n passwordShow: '<svg fill=\"none\" height=\"256\" viewBox=\"0 0 24 24\" width=\"256\" xmlns=\"http://www.w3.org/2000/svg\"><g stroke=\"currentColor\" stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\"><path d=\"m1 12s4-8 11-8 11 8 11 8\"/><path d=\"m1 12s4 8 11 8 11-8 11-8\"/><circle cx=\"12\" cy=\"12\" r=\"3\"/></g></svg>',\n passwordHide: '<svg fill=\"none\" height=\"256\" viewBox=\"0 0 24 24\" width=\"256\" xmlns=\"http://www.w3.org/2000/svg\"><g stroke=\"currentColor\" stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\"><path d=\"m2 2 20 20\"/><path d=\"m6.71277 6.7226c-3.04798 2.07267-4.71277 5.2774-4.71277 5.2774s3.63636 7 10 7c2.0503 0 3.8174-.7266 5.2711-1.7116m-6.2711-12.23018c.3254-.03809.6588-.05822 1-.05822 6.3636 0 10 7 10 7s-.6918 1.3317-2 2.8335\"/><path d=\"m14 14.2362c-.5308.475-1.2316.7639-2 .7639-1.6569 0-3-1.3431-3-3 0-.8237.33193-1.5698.86932-2.11192\"/></g></svg>',\n helpIcon: '<svg xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\" width=\"16\" height=\"16\" viewBox=\"0 0 16 16\" version=\"1.1\" style=\"overflow:visible;\"><rect width=\"16\" height=\"16\" fill=\"none\"/><path fill=\"currentColor\" d=\"M8,10.5c-0.552,0-1,0.448-1,1c0,0.552,0.448,1,1,1c0.552,0,1-0.448,1-1C9,10.948,8.552,10.5,8,10.5z M8,0 C3.582,0,0,3.582,0,8c0,4.418,3.582,8,8,8s8-3.582,8-8C16,3.582,12.418,0,8,0z M12.243,12.243C11.109,13.376,9.603,14,8,14 s-3.109-0.624-4.243-1.757C2.624,11.109,2,9.603,2,8s0.624-3.109,1.757-4.243C4.891,2.624,6.397,2,8,2s3.109,0.624,4.243,1.757 C13.376,4.891,14,6.397,14,8C14,9.603,13.376,11.109,12.243,12.243z M7.927,3.755c-2.248,0-2.76,1.695-2.802,2.906h1.571 c0.028-0.256,0.101-1.418,1.221-1.418c0.672,0,1.206,0.406,1.206,1.175c0,0.695-0.724,1.155-1.303,1.711 C7.241,8.685,7.168,9.325,7.168,9.824V10c0.146,0,1.615,0,1.615,0V9.824c0-1.256,1.967-1.594,1.967-3.541 C10.75,5.059,9.874,3.755,7.927,3.755z\"/></svg>'\n}\n\nconst lookups = {\n nodeName: ['TEXTAREA', 'SELECT'],\n inputType: ['checkbox', 'color', 'date', 'datetime-local', 'email', 'file', 'image', 'month', 'number', 'password', 'radio', 'range', 'search', 'tel', 'text', 'time', 'url', 'week']\n}\n\nlet lastFocusedInput\nlet lastClearFieldPressed\n\n// browsers that implement the switch attribute expose it on the input prototype, render the control as a switch and report the switch role themselves, so nothing below is needed there\nconst nativeSwitchSupport = 'switch' in window.HTMLInputElement.prototype\n\n// lets a polyfilled switch be toggled by dragging its thumb, which is what the native control does. a pointer that never travels far enough is left alone so it still lands as a click.\nconst enableSwitchDragging = (input) => {\n input.addEventListener('pointerdown', event => {\n if (input.disabled) return\n\n const startX = event.clientX\n const rightToLeft = window.getComputedStyle(input).direction === 'rtl'\n // half the track is the distance the thumb travels between the two positions\n const travel = input.clientWidth / 2 || 1\n const wasChecked = input.checked\n let dragging = false\n\n input.setPointerCapture(event.pointerId)\n\n const drag = moveEvent => {\n const distance = rightToLeft ? startX - moveEvent.clientX : moveEvent.clientX - startX\n if (!dragging && Math.abs(distance) <= 5) return // still within a click\n dragging = true\n moveEvent.preventDefault() // don't select or scroll while dragging\n input.checked = (wasChecked ? 1 : 0) + (distance / travel) > 0.5\n }\n\n const drop = upEvent => {\n input.releasePointerCapture(upEvent.pointerId)\n input.removeEventListener('pointermove', drag)\n input.removeEventListener('pointerup', drop)\n input.removeEventListener('pointercancel', drop)\n if (!dragging) return\n\n // the click that follows the drag would otherwise put the switch back where it started\n input.addEventListener('click', clickEvent => clickEvent.preventDefault(), { once: true, capture: true })\n\n if (input.checked !== wasChecked) {\n input.dispatchEvent(new Event('input', { bubbles: true }))\n input.dispatchEvent(new Event('change', { bubbles: true }))\n }\n }\n\n input.addEventListener('pointermove', drag)\n input.addEventListener('pointerup', drop)\n input.addEventListener('pointercancel', drop)\n })\n}\n\n// turns a text input with the data-auto-grow attribute into a textarea, and makes enter submit the form from it\n//\n// returns the field, which is the new textarea when one was made\nconst enhanceAutoGrow = (input, form) => {\n if (input.getAttribute('data-auto-grow') === null) return input\n\n // progressively enhance inputs into textareas\n if (input.nodeName === 'INPUT' && input.type === 'text') {\n const newInput = document.createElement('textarea')\n newInput.id = input.id\n newInput.class = input.class\n newInput.innerText = input.value\n newInput.setAttribute('data-auto-grow', '')\n input.replaceWith(newInput)\n input = newInput\n }\n\n if (input.nodeName === 'TEXTAREA') {\n // when pressing enter while this input is focused, we want to submit\n input.addEventListener('keypress', (e) => {\n if (e.key !== 'Enter' || (e.key === 'Enter' && e.shiftKey)) return\n e.preventDefault()\n form.requestSubmit()\n })\n }\n\n return input\n}\n\n// the behavior of the control itself, as opposed to the labels and buttons placed around it: switches and textareas. this is all a field in a customLayout element gets, and every other field gets it too\nconst enhanceControl = (input, type, form) => {\n // a checkbox with the switch attribute is a switch; the stylesheet draws one in every browser, and where the attribute is not implemented natively the role and dragging are supplied here\n if (type === 'checkbox' && input.hasAttribute('switch') && !nativeSwitchSupport) {\n input.setAttribute('role', 'switch')\n enableSwitchDragging(input)\n }\n\n // add listener to shift clear button when scrollbar present\n if (input.nodeName === 'TEXTAREA') {\n if (input.getAttribute('data-disable-autosize') === null) {\n // add auto-sizing\n input.style.setProperty('resize', 'none')\n input.style.setProperty('min-height', '0')\n input.style.setProperty('max-height', 'none')\n input.style.setProperty('height', 'auto')\n\n const handleInput = () => {\n // reset rows attribute to get accurate scrollHeight\n let maxRows = input.getAttribute('data-max-rows')\n\n if (maxRows) {\n if (isNaN(maxRows) || Number(maxRows) <= 0) {\n console.warn(`An invalid value was passed to the \"data-max-rows\" attribute. This value will be ignored.\\n\\nProvided value: ${input.getAttribute('data-max-rows')}`)\n maxRows = null\n }\n }\n\n const minRows = input.getAttribute('data-max-rows') && Number(input.getAttribute('data-max-rows')) < 5 ? input.getAttribute('data-max-rows') : '5'\n input.setAttribute('rows', minRows)\n\n // get the computed values object reference\n const style = window.getComputedStyle(input)\n\n // force content-box for size accurate line-height calculation, remove scrollbars, lock width (subtract inline padding and inline border widths), and remove inline padding and borders to keep width consistent (for text wrapping accuracy)\n const inlinePadding = parseFloat(style['padding-left']) + parseFloat(style['padding-right'])\n const inlineBorderWidth = parseFloat(style['border-left-width']) + parseFloat(style['border-right-width'])\n input.style.setProperty('overflow', 'hidden', 'important')\n input.style.setProperty('width', (parseFloat(style.width) - inlinePadding - inlineBorderWidth) + 'px')\n input.style.setProperty('box-sizing', 'content-box')\n input.style.setProperty('padding-inline', '0')\n input.style.setProperty('border-width', '0')\n\n // get the base line height, and top / bottom padding\n const blockPadding = parseFloat(style['padding-top']) + parseFloat(style['padding-bottom'])\n const lineHeight = style['line-height'] === 'normal'\n ? parseFloat(style.height) // if line-height is not explicitly set, use the computed height value (ignore padding due to content-box)\n : parseFloat(style['line-height']) // otherwise (line-height is explicitly set), use the computed line-height value\n\n // get the scroll height (rounding to be safe to ensure cross browser consistency)\n const scrollHeight = Math.round(input.scrollHeight)\n\n // undo overflow, width, border-width, box-sizing & inline padding overrides\n input.style.removeProperty('width')\n input.style.removeProperty('box-sizing')\n input.style.removeProperty('padding-inline')\n input.style.removeProperty('border-width')\n input.style.removeProperty('overflow')\n\n // subtract blockPadding from scrollHeight and divide that by our lineHeight to get the row count, round to nearest integer as it will always be within ~.1 of the correct whole number\n const rows = Math.round((scrollHeight - blockPadding) / lineHeight)\n\n // set the calculated rows attribute (limited by rowLimit)\n if (maxRows) {\n input.setAttribute('rows', '' + Math.min(rows, Number(maxRows)))\n } else {\n input.setAttribute('rows', '' + rows)\n }\n }\n\n input.addEventListener('input', handleInput)\n\n // trigger the event to set the initial rows value\n input.dispatchEvent(new Event('input', { bubbles: true }))\n }\n\n // progressively enhance textarea for Firefox and Safari\n // this may be removed once fully supported in Firefox and Safari: https://caniuse.com/wf-field-sizing\n if (input.getAttribute('data-auto-grow') !== null) {\n if (!('fieldSizing' in document.createElement('input').style)) {\n const adjustHeight = () => {\n if (input.value.length) {\n const borderWidth = parseInt(window.getComputedStyle(input).getPropertyValue('border-width'))\n input.style.height = (input.scrollHeight + (borderWidth * 2)) + 'px'\n } else {\n input.style.height = window.getComputedStyle(form).getPropertyValue('--semanticFormsInputHeight')\n }\n }\n\n // set initial height to semantic-forms CSS variable\n input.style.height = window.getComputedStyle(form).getPropertyValue('--semanticFormsInputHeight')\n input.addEventListener('input', adjustHeight)\n }\n }\n\n // shifts the clear button to the right if a scrollbar is present\n const shiftClearBtn = () => {\n const clearBtn = input.parentElement?.querySelector('button.clear')\n if (clearBtn) clearBtn.style.marginRight = input.clientHeight < input.scrollHeight ? '15px' : ''\n }\n input.addEventListener('input', shiftClearBtn)\n input.addEventListener('mouseup', shiftClearBtn)\n shiftClearBtn()\n }\n}\n\nconst enhanceInput = (input, form) => {\n // ignore input if it has previously been formatted\n if (input.classList.contains('semanticform') || !input.id) return\n\n const type = input.getAttribute('type')\n if (!lookups.nodeName.includes(input.nodeName) && !lookups.inputType.includes(type)) return\n\n // inside a customLayout element the app lays out the <dl> itself, so nothing is added, moved or rewritten around the field: no float label, no clear or show password button, no wrapping <div>, no required asterisk or help icon, and no value appended to the label of a range. the control itself is still enhanced. it is marked like any other enhanced field so that reinitializing does not enhance it twice\n if (input.closest('.customLayout')) {\n input.classList.add('semanticform')\n enhanceControl(enhanceAutoGrow(input, form), type, form)\n return\n }\n\n // recursively find <dl> element\n let dl = input.parentNode\n while (dl && dl.nodeName !== 'DL') dl = dl.parentNode\n if (!dl) return\n if (!dl.classList.contains('floatLabelForm')) dl.classList.add('floatLabelForm')\n\n // the label is looked up in the input's own tree rather than the document, so that a form inside a shadow root finds its labels instead of coming up empty\n const tree = input.getRootNode()\n const label = input.parentNode.parentNode.id && (type === 'checkbox' || type === 'radio')\n ? tree.querySelector('label[data-for=' + input.parentNode.parentNode.id.replace(/\\./g, '\\\\.') + ']')\n : tree.querySelector('label[for=' + input.id.replace(/\\./g, '\\\\.') + ']')\n\n if (!label) {\n console.error(`semantic-forms: Found an input (${input.id || input.getAttribute('name')}) without a properly associated label. Make sure there is a label for it and the label has a matching \"for\" attribute.`)\n return\n }\n\n input.classList.add('semanticform')\n\n // an input outside a <dd> is reported by the checks further down, so tolerate it here\n const isWrapped = input.closest('dd')?.parentElement.nodeName === 'DIV'\n\n // #region create labels\n\n const newLabel = document.createElement('label')\n newLabel.className = 'floatLabelFormAnimatedLabel'\n\n if (type === 'checkbox' || type === 'radio') {\n const div = isWrapped ? input.closest('dd').parentElement : document.createElement('div')\n const dt = label.closest('dt')\n const dd = input.closest('dd')\n\n if (!dt || !dd) {\n console.error(`semantic-forms: Found an input (${input.id || input.getAttribute('name')}) that is not inside a <dd> element.`)\n return\n }\n\n if (dd.firstChild.nodeName !== 'LABEL') {\n if (type === 'checkbox' && input.parentNode.nodeName === 'DD') {\n newLabel.setAttribute('for', input.id)\n input.parentNode.classList.add('singleCheckbox')\n newLabel.className = ''\n label.setAttribute('hidden', 'hidden')\n insertAfter(newLabel, input)\n }\n\n if (type === 'radio' && input.parentNode.nodeName === 'DD') {\n newLabel.setAttribute('for', input.id)\n input.parentNode.classList.add('singleRadio')\n newLabel.className = ''\n label.setAttribute('hidden', 'hidden')\n insertAfter(newLabel, input)\n }\n\n newLabel.innerHTML = label.innerHTML\n\n if (label.hasAttribute('title') && label.getAttribute('data-show-help-icon') !== null && !label.querySelector('span.help')) {\n const text = label.getAttribute('title')\n label.innerHTML += ` <span title=\"${text}\" class=\"help\">${icons.helpIcon}</span>`\n newLabel.innerHTML += ` <span title=\"${text}\" class=\"help\">${icons.helpIcon}</span>`\n }\n\n if (dd.querySelector(':required') && label.getAttribute('data-no-asterisk') === null && !label.querySelector('span.required')) {\n const text = label.getAttribute('data-asterisk-text') || 'This field is required.'\n label.innerHTML += ` <span title=\"${text}\" class=\"required\">*</span>`\n newLabel.innerHTML += ` <span title=\"${text}\" class=\"required\">*</span>`\n }\n\n if (!dd.querySelector('label')) dd.append(newLabel)\n }\n\n // if the user did not wrap the input in a div, do it for them\n if (!isWrapped) {\n // the wrapper goes where the <dt> was, so the field keeps its place in the markup. a div the author wrote is already in the right place and is deliberately left where it is: moving it would push the field past anything the enhancer does not touch, such as a <dd> holding only a button\n dt.before(div)\n div.append(dt, dd)\n\n // determine visibility of newly created <div>\n if (dt.style.display === 'none' && dd.style.display === 'none') div.style.display = 'none'\n }\n } else {\n newLabel.setAttribute('for', input.id)\n newLabel.innerHTML = label.innerHTML\n\n if (input.hasAttribute('title') && label.getAttribute('data-show-help-icon') !== null && !label.querySelector('span.help')) {\n const text = input.getAttribute('title')\n newLabel.innerHTML += ` <span title=\"${text}\" class=\"help\">${icons.helpIcon}</span>`\n }\n\n if (input.hasAttribute('required') && label.getAttribute('data-no-asterisk') === null && !label.querySelector('span.required')) {\n const text = label.getAttribute('data-asterisk-text') || 'This field is required.'\n newLabel.innerHTML += ` <span title=\"${text}\" class=\"required\">*</span>`\n }\n\n label.setAttribute('hidden', 'hidden')\n\n insertAfter(newLabel, input)\n }\n // #endregion\n\n // #region standard inputs\n\n input = enhanceAutoGrow(input, form)\n\n if (type !== 'checkbox' && type !== 'radio') {\n if (!input.getAttribute('placeholder')) input.setAttribute('placeholder', ' ')\n\n const div = isWrapped ? input.closest('dd').parentElement : document.createElement('div')\n const dt = label.closest('dt')\n const dd = input.closest('dd')\n\n if (!dt || !dd) {\n console.error(`semantic-forms: Found an input (${input.id || input.getAttribute('name')}) that does not have a corresponding ${!dt ? '<dt>' : '<dd>'} element.`)\n return\n }\n\n // input clear button. a field the user cannot edit does not get one, since the button would empty it anyway. a field that only becomes uneditable later is handled in css\n if (input.nodeName !== 'SELECT' && type !== 'range' && !input.readOnly && !input.disabled) {\n const clearBtn = document.createElement('button')\n clearBtn.type = 'button'\n clearBtn.title = input.getAttribute('data-clear-field-text') || 'Clear field'\n clearBtn.ariaLabel = input.getAttribute('data-clear-field-text') || 'Clear field'\n clearBtn.tabIndex = -1\n clearBtn.innerHTML = '<svg viewBox=\"0 0 16 16\" width=\"18\" height=\"18\"><path d=\"M 1 1 L 15 15 M 1 15 L 15 1\" fill=\"none\" stroke-width=\"2\" stroke=\"currentColor\" />'\n clearBtn.classList.add('clear')\n clearBtn.id = `semanticFormsClearButton_${input.id}`\n clearBtn.addEventListener('click', () => {\n input.previousValue = input.value\n input.value = ''\n input.focus()\n lastClearFieldPressed = input.id\n\n // used for any other updates required by various inputs\n input.dispatchEvent(new Event('input', { bubbles: true }))\n })\n insertAfter(clearBtn, dd.querySelector('label'))\n }\n\n input.addEventListener('focus', event => {\n if (event.target.nodeName === 'INPUT') lastFocusedInput = event.target\n })\n\n // the colspan- and col- utility classes are documented as going on the dd, but they size and place the grid item, which is the div wrapping it, so move any of them across\n for (const utility of dd.className.match(/colspan-(?:[0-9]|full)|col-[0-9]/g) || []) {\n dd.classList.remove(utility)\n div.classList.add(utility)\n }\n\n // check for max-content attribute\n // this may be removed once fully supported in Firefox and Safari: https://caniuse.com/wf-field-sizing\n if (input.getAttribute('data-max-content') !== null) {\n if (!('fieldSizing' in document.createElement('input').style)) {\n const adjustWidth = () => {\n const value = input.value !== '' ? input.value : input.placeholder\n const width = value.length * 8 + 40\n input.style.width = width + 'px'\n input.style.maxWidth = '100%'\n }\n adjustWidth()\n input.addEventListener('input', adjustWidth)\n }\n }\n\n // if the user did not wrap the input in a div, do it for them\n if (!isWrapped) {\n // as above, the wrapper takes the <dt>'s position rather than being appended to the end\n dt.before(div)\n div.append(dt, dd)\n\n // determine visibility of newly created <div>\n if (dt.style.display === 'none' && dd.style.display === 'none') div.style.display = 'none'\n }\n }\n // #endregion\n\n // handle file input clear btn, cannot be handled with CSS\n if (type === 'file') {\n const clearBtn = input.parentElement.querySelector('.clear')\n input.addEventListener('input', event => {\n clearBtn.style.display = event.target.files.length ? 'flex' : 'none'\n })\n clearBtn.addEventListener('click', () => {\n clearBtn.style.display = 'none'\n })\n }\n\n // handle range inputs set to display the value. every other per-field flag in the library is a data attribute, so that is the documented way to ask for this, but the original `displayValue` class is still honored\n if (type === 'range' && (input.getAttribute('data-display-value') !== null || input.classList.contains('displayValue'))) {\n const label = input.parentNode.parentNode.querySelector('dd label')\n label.innerHTML += `<span class=\"seperator\">: </span><output>${input.value}</output>`\n input.addEventListener('input', event => {\n const output = event.target.parentNode.parentNode.querySelector('output')\n output.innerHTML = event.target.value\n })\n }\n\n // show password button\n if (type === 'password' && input.getAttribute('data-no-reveal') === null) {\n const showBtn = document.createElement('button')\n showBtn.type = 'button'\n showBtn.title = input.getAttribute('data-show-password-text') || 'Show password'\n showBtn.ariaLabel = input.getAttribute('data-show-password-text') || 'Show password'\n showBtn.tabIndex = -1\n showBtn.innerHTML = icons.passwordShow\n showBtn.classList.add('show')\n showBtn.id = `semanticFormsShowButton_${input.id}`\n const dd = input.closest('dd')\n if (!dd) {\n console.error(`semantic-forms: Found an input (${input.id || input.getAttribute('name')}) that is not inside a <dd> element.`)\n return\n }\n showBtn.addEventListener('click', () => {\n if (input.type === 'password') {\n showBtn.innerHTML = icons.passwordHide\n showBtn.title = input.getAttribute('data-hide-password-text') || 'Hide password'\n showBtn.ariaLabel = input.getAttribute('data-hide-password-text') || 'Hide password'\n input.type = 'text'\n } else {\n showBtn.innerHTML = icons.passwordShow\n showBtn.title = input.getAttribute('data-show-password-text') || 'Show password'\n showBtn.ariaLabel = input.getAttribute('data-show-password-text') || 'Show password'\n input.type = 'password'\n }\n input.focus()\n })\n insertAfter(showBtn, dd.querySelector('label'))\n }\n\n enhanceControl(input, type, form)\n}\n\nconst handleUndoRedo = (event) => {\n // handle undo/redo\n if ((event.ctrlKey || event.metaKey) && event.key === 'z' && !event.shiftKey) {\n // undo clearing a field\n if (lastFocusedInput) {\n if (lastFocusedInput?.parentNode?.querySelector('button.clear')?.id === `semanticFormsClearButton_${lastClearFieldPressed}` || lastFocusedInput?.parentNode?.querySelector('button.clear')?.name === `semanticFormsClearButton_${lastClearFieldPressed}`) {\n if (lastFocusedInput.previousValue) {\n // the browser undoes the field's own edit history on this key too, which would apply on top of the value restored here, so the native behavior is suppressed\n event.preventDefault()\n lastFocusedInput.redoValue = lastFocusedInput.value\n lastFocusedInput.value = lastFocusedInput.previousValue\n }\n }\n }\n } else {\n if ((event.ctrlKey && event.key === 'y') || (event.metaKey && event.shiftKey && event.key === 'z')) {\n // redo clearing a field\n if (lastFocusedInput) {\n if (lastFocusedInput?.parentNode?.querySelector('button.clear')?.id === `semanticFormsClearButton_${lastClearFieldPressed}` || lastFocusedInput?.parentNode?.querySelector('button.clear')?.name === `semanticFormsClearButton_${lastClearFieldPressed}`) {\n if (lastFocusedInput.redoValue) {\n // as above, the browser's own redo would otherwise apply on top of this\n event.preventDefault()\n lastFocusedInput.previousValue = lastFocusedInput.value\n lastFocusedInput.value = lastFocusedInput.redoValue\n }\n }\n }\n }\n }\n}\n\nmodule.exports = { enhanceInput, handleUndoRedo }\n","const { getOS, insertAfter } = require('./helpers.js')\n\nconst specialCharMap = {\n Minus: '-',\n Equal: '=',\n BracketLeft: '[',\n BracketRight: ']',\n Backslash: '\\\\',\n Semicolon: ';',\n Quote: '\\'',\n Comma: ',',\n Period: '.',\n Slash: '/',\n Backquote: '`'\n}\nconst shiftSpecialCharMap = {\n 1: '!',\n 2: '@',\n 3: '#',\n 4: '$',\n 5: '%',\n 6: '^',\n 7: '&',\n 8: '*',\n 9: '(',\n 0: ')',\n Minus: '_',\n Equal: '+',\n BracketLeft: '{',\n BracketRight: '}',\n Backslash: '|',\n Semicolon: ':',\n Quote: '\"',\n Comma: '<',\n Period: '>',\n Slash: '?',\n Backquote: '~'\n}\n\n// handles keyboard shortcut events\nconst shortcutListener = (e, shortcuts) => {\n // search for matching shortcut from cached shortcut configs\n const shortcut = shortcuts.find(shortcut => {\n let matchesKey = false\n if (e.altKey && !e.shiftKey) {\n // mac adjusts the key value if altKey is pressed\n matchesKey = 'Key' + shortcut.key.toUpperCase() === e.code ||\n 'Digit' + shortcut.key.toUpperCase() === e.code ||\n shortcut.key === e.key ||\n specialCharMap[e.code] === shortcut.key\n } else if (e.shiftKey) {\n // check shift special character map\n const code = e.code.replace(/Key|Digit/, '')\n matchesKey = (shiftSpecialCharMap[code] || shiftSpecialCharMap[e.key]) &&\n (shiftSpecialCharMap[code] === shortcut.key || shiftSpecialCharMap[e.key] === shortcut.key)\n } else {\n matchesKey = shortcut.key.toUpperCase() === e.key.toUpperCase()\n }\n if (!matchesKey) return false\n\n let matchesModifier\n if (shortcut.modifier) {\n if (shortcut.modifier === shortcut.defaultModifier) matchesModifier = shortcut.os === 'windows' || shortcut.os === 'linux' ? e.ctrlKey : e.metaKey\n if (shortcut.modifier === 'meta') matchesModifier = e.metaKey\n if (shortcut.modifier === 'alt') matchesModifier = e.altKey\n if (shortcut.modifier === 'ctrl') matchesModifier = e.ctrlKey\n }\n return matchesModifier\n })\n\n if (shortcut) {\n e.preventDefault()\n shortcut.input.focus()\n }\n}\n\nconst createKeyboardShortcut = (input, shortcuts) => {\n const os = getOS()\n // this is the custom keyword for meta on linux/mac, ctrl on windows\n const defaultModifier = 'metactrl'\n\n // get focus key value\n let focusKey = input.getAttribute('data-focus-key')\n if (focusKey.length > 1) {\n console.error(`Provided focus key \"${focusKey}\" is more than one character. Using first character only.`)\n focusKey = focusKey.toString()[0]\n }\n\n // get focus modifier value\n let modifierSymbol\n let modifierKey = defaultModifier\n const modifierAttr = {\n default: input.getAttribute('data-focus-modifier') || defaultModifier,\n linux: input.getAttribute('data-focus-modifier-linux'),\n mac: input.getAttribute('data-focus-modifier-mac'),\n windows: input.getAttribute('data-focus-modifier-win')\n }\n if (os && modifierAttr[os]) {\n // a specific modifier key has been set by the user\n modifierKey = modifierAttr[os]\n } else {\n modifierKey = modifierAttr.default\n }\n\n // validate passed in modifier\n const recognizedModifiers = ['ctrl', 'alt', 'opt', 'meta', 'cmd', defaultModifier]\n if (!recognizedModifiers.includes(modifierKey)) {\n console.error(`Received an unrecognized modifier, \"${modifierKey},\" defaulting to \"${defaultModifier}.\"`, input)\n modifierKey = defaultModifier\n }\n\n // retrieve modifier symbol\n if (['alt', 'opt'].includes(modifierKey)) {\n modifierSymbol = os === 'mac' ? '⌥' : '⎇'\n } else if (['meta', 'win', 'cmd'].includes(modifierKey) || (modifierKey === defaultModifier && os === 'mac')) {\n if (os === 'mac') {\n modifierSymbol = '⌘'\n } else if (os === 'linux') {\n modifierSymbol = '◆'\n } else {\n modifierSymbol = '⊞'\n }\n } else if (modifierKey === 'ctrl' || (modifierKey === defaultModifier && (os === 'windows' || os === 'linux'))) {\n if (os === 'mac') {\n modifierSymbol = '⌃'\n } else {\n modifierSymbol = 'Ctrl'\n }\n }\n\n // add the shortcut to the cached array, if not a duplicate\n if (shortcuts.some(shortcut => shortcut.key === focusKey && shortcut.modifier === modifierKey)) {\n console.error(`Duplicate keyboard shortcut \"${modifierKey} + ${focusKey}\" detected. Only the first input will be focusable using this keyboard shortcut.`, input)\n }\n\n // set the shortcut indicator/title\n //\n // the indicator is drawn over the field beside its float label, so a field in a customLayout element, which has no float label and is laid out by the app, gets the title instead, as the other kinds of field do\n if ((input.nodeName === 'TEXTAREA' || input.type === 'text' || input.type === 'number') && !input.closest('.customLayout')) {\n // create focus indicator for valid inputs\n const indicator = document.createElement('span')\n indicator.classList.add('focus-key')\n indicator.innerHTML = `<kbd>${modifierSymbol} ${focusKey.toUpperCase()}</kbd>`\n\n const label = input.nextSibling\n insertAfter(indicator, label)\n } else {\n // update the input title\n if (input.getAttribute('title')) {\n input.setAttribute('title', input.getAttribute('title') + ` (${modifierSymbol} + ${focusKey})`)\n } else {\n input.setAttribute('title', `Focus with ${modifierSymbol} + ${focusKey}`)\n }\n }\n\n return {\n key: focusKey,\n modifier: modifierKey,\n input,\n os,\n defaultModifier\n }\n}\n\nmodule.exports = {\n shortcutListener,\n createKeyboardShortcut\n}\n","let tabGroupCount = 0\n\n// turns a group of fieldsets into a set of tabs\n//\n// the markup it enhances is an element with the `tabs` class holding one `<fieldset>` per tab, each titled by its `<legend>`. left alone that is a stack of labelled field groups, which is a perfectly usable form, so a browser without JavaScript still gets everything\n//\n// the tab that opens first is the one whose fieldset carries `data-selected`, or the first fieldset when none do\n//\n// form: form element to search for tab groups\nconst enhanceTabs = (form) => {\n for (const group of form.querySelectorAll('.tabs:not(.semanticFormsTabsActive)')) {\n const panels = Array.from(group.children).filter(child => child.nodeName === 'FIELDSET')\n\n // one fieldset is not a set of tabs, and no fieldsets means the class is on the wrong element\n if (panels.length < 2) {\n if (panels.length === 0) console.error('semantic-forms: Found a .tabs element with no <fieldset> children. Each tab needs to be a <fieldset> with a <legend> naming it.', group)\n continue\n }\n\n group.classList.add('semanticFormsTabsActive')\n const groupId = ++tabGroupCount\n const tabList = document.createElement('div')\n const tabs = []\n\n tabList.classList.add('tabList')\n tabList.setAttribute('role', 'tablist')\n\n // shows one panel and marks its tab as the selected one\n const selectTab = (index, moveFocus) => {\n for (const [i, panel] of panels.entries()) panel.hidden = i !== index\n for (const [i, tab] of tabs.entries()) {\n const selected = i === index\n tab.setAttribute('aria-selected', selected ? 'true' : 'false')\n\n // only the selected tab is in the tab order; the arrow keys reach the others\n tab.tabIndex = selected ? 0 : -1\n }\n if (moveFocus) tabs[index].focus()\n }\n\n for (const [index, panel] of panels.entries()) {\n const legend = panel.querySelector('legend')\n const tab = document.createElement('button')\n\n tab.type = 'button'\n tab.setAttribute('role', 'tab')\n tab.id = `semanticFormsTab_${groupId}_${index}`\n tab.textContent = legend ? legend.textContent : `Tab ${index + 1}`\n\n if (!panel.id) panel.id = `semanticFormsTabPanel_${groupId}_${index}`\n panel.setAttribute('role', 'tabpanel')\n panel.setAttribute('aria-labelledby', tab.id)\n tab.setAttribute('aria-controls', panel.id)\n\n // the tab now names the panel, so the legend would be read out twice\n if (legend) legend.hidden = true\n\n tab.addEventListener('click', () => selectTab(index))\n tabs.push(tab)\n tabList.append(tab)\n }\n\n // the arrow keys move between tabs, which is what a tablist is expected to do\n tabList.addEventListener('keydown', event => {\n const current = tabs.indexOf(document.activeElement)\n if (current === -1) return\n\n const keys = {\n ArrowRight: (current + 1) % tabs.length,\n ArrowLeft: (current - 1 + tabs.length) % tabs.length,\n Home: 0,\n End: tabs.length - 1\n }\n if (!(event.key in keys)) return\n\n event.preventDefault()\n selectTab(keys[event.key], true)\n })\n\n // the panel carrying [data-selected] is the one that opens, defaulting to the first. without javascript every panel is on the page at once, so the attribute only means anything once the tabs have been built\n const marked = panels.filter(panel => panel.hasAttribute('data-selected'))\n if (marked.length > 1) console.error('semantic-forms: Found more than one <fieldset> with a \"data-selected\" attribute in a tab group. Only the first one will be selected.', group)\n\n group.prepend(tabList)\n selectTab(marked.length ? panels.indexOf(marked[0]) : 0)\n }\n}\n\nmodule.exports = { enhanceTabs }\n","const { createKeyboardShortcut, shortcutListener } = require('./lib/keyboardShortcuts.js')\nconst { enhanceInput, handleUndoRedo } = require('./lib/inputEnhancements.js')\nconst { enhanceTabs } = require('./lib/tabs.js')\nconst { setInkOffset } = require('./lib/helpers.js')\n\n// one observer per tree that has been handed to semanticForms, so that a shadow root gets its own without replacing the document's\nconst observers = new WeakMap()\n\n// progressively enhances every semanticForms form in a tree\n//\n// root: document or shadow root to search; defaults to the main document\nconst semanticForms = (root = document) => {\n // do some feature detection so none of the JS executes if the browser is too old\n if (typeof document.getElementsByClassName !== 'function' || typeof document.querySelector !== 'function' || !document.body.classList || !window.MutationObserver) {\n console.warn('semantic-forms was loaded into an unsupported browser and will not execute.')\n return\n }\n\n // custom keyboard shortcut listener\n const keyboardShortcuts = []\n document.addEventListener('keydown', (e) => shortcutListener(e, keyboardShortcuts))\n\n // progressively enhance form elements that have the semanticForms class\n const forms = root.querySelectorAll('form.semanticForms:not(.semanticFormsActive), table.semanticForms:not(.semanticFormsActive)')\n\n for (const form of forms) {\n form.classList.add('semanticFormsActive')\n if (form.classList.contains('lowFlow')) continue\n\n // how far this font's letters sit from the middle of their own line box, which the stylesheet subtracts wherever it centers text. it is measured per form rather than per field, so that a form holding nothing but buttons is corrected too\n setInkOffset(form, form)\n\n // update each input in the semantic form\n const inputs = Array.from(form.querySelectorAll('input, textarea, select'))\n for (const input of inputs) {\n enhanceInput(input, form)\n\n // handle keyboard shortcuts\n if (input.getAttribute('data-focus-key') !== null) {\n const shortcut = createKeyboardShortcut(input, keyboardShortcuts)\n keyboardShortcuts.push(shortcut)\n }\n }\n\n // done after the inputs so that anything sizing itself measures while it is still on screen\n enhanceTabs(form)\n }\n\n // prevents multiple listeners\n document.removeEventListener('keydown', handleUndoRedo)\n document.addEventListener('keydown', handleUndoRedo)\n\n // monitor changes to this tree and enhance new semanticForms forms that get added to it\n if (!observers.has(root)) {\n const observer = new window.MutationObserver(mutations => {\n let stop = false\n for (const mutation of mutations) {\n for (const node of mutation.addedNodes) {\n if (node.nodeName === 'FORM' || node?.querySelector?.('form')) {\n semanticForms(root)\n stop = true\n }\n }\n if (stop) break\n }\n })\n\n // a shadow root is observed directly; the document is observed through its body\n observer.observe(root === document ? document.body : root, { attributes: false, childList: true, characterData: false, subtree: true })\n observers.set(root, observer)\n\n // long standing name for the document's observer, kept for anything already using it\n if (root === document) window.semanticFormsObserver = observer\n }\n\n semanticForms.reinitialize = form => {\n // with no form given this just rescans for forms that are not active yet\n if (!form) return semanticForms(root)\n\n form.classList.remove('semanticFormsActive')\n\n // the form knows which tree it belongs to, so a form in a shadow root reinitializes there\n semanticForms(form.getRootNode())\n }\n}\n\nmodule.exports = semanticForms\n","// The module cache\nconst __webpack_module_cache__ = {};\n\n// The require function\nfunction __webpack_require__(moduleId) {\n\t// Check if module is in cache\n\tconst cachedModule = __webpack_module_cache__[moduleId];\n\tif (cachedModule !== undefined) {\n\t\treturn cachedModule.exports;\n\t}\n\t// Create a new module (and put it into the cache)\n\tconst module = __webpack_module_cache__[moduleId] = {\n\t\t// no module.id needed\n\t\t// no module.loaded needed\n\t\texports: {}\n\t};\n\n\t// Execute the module function\n\tif (!(moduleId in __webpack_modules__)) {\n\t\tdelete __webpack_module_cache__[moduleId];\n\t\tconst e = new Error(\"Cannot find module '\" + moduleId + \"'\");\n\t\te.code = 'MODULE_NOT_FOUND';\n\t\tthrow e;\n\t}\n\t__webpack_modules__[moduleId](module, module.exports, __webpack_require__);\n\n\t// Return the exports of the module\n\treturn module.exports;\n}\n\n","// startup\n// Load entry module and return exports\n// This entry module is referenced by other modules so it can't be inlined\nlet __webpack_exports__ = __webpack_require__(\"./semanticForms.js\");\n"],"names":["getOS","userAgent","window","navigator","platform","os","includes","test","insertAfter","newNode","referenceNode","nextSibling","parentNode","insertBefore","appendChild","inkOffsets","Map","getInkOffset","fontStyle","fontWeight","fontSize","fontFamily","key","has","get","probe","document","createElement","style","cssText","marker","append","body","lineBox","getBoundingClientRect","ascent","top","descent","height","remove","scale","size","parseFloat","canvas","width","Math","ceil","context","getContext","willReadFrequently","fillStyle","fillRect","font","textBaseline","baseline","round","fillText","pixels","getImageData","data","inkTop","y","x","none","offset","fieldOffset","set","capHeight","ratio","devicePixelRatio","exact","sign","abs","pair","readInkOffset","label","isConnected","getComputedStyle","writeInkOffset","form","setProperty","measureAll","forms","read","push","measured","awaitingFonts","measure","fonts","status","ready","then","clear","waiting","observer","setInkOffset","IntersectionObserver","entries","seen","entry","isIntersecting","target","unobserve","delete","length","rootMargin","observe","module","exports","__webpack_require__","icons","passwordShow","passwordHide","helpIcon","lookups","nodeName","inputType","lastFocusedInput","lastClearFieldPressed","nativeSwitchSupport","HTMLInputElement","prototype","enableSwitchDragging","input","addEventListener","event","disabled","startX","clientX","rightToLeft","direction","travel","clientWidth","wasChecked","checked","dragging","setPointerCapture","pointerId","drag","moveEvent","distance","preventDefault","drop","upEvent","releasePointerCapture","removeEventListener","clickEvent","once","capture","dispatchEvent","Event","bubbles","enhanceAutoGrow","getAttribute","type","newInput","id","class","innerText","value","setAttribute","replaceWith","e","shiftKey","requestSubmit","enhanceControl","hasAttribute","handleInput","maxRows","isNaN","Number","console","warn","minRows","inlinePadding","inlineBorderWidth","blockPadding","lineHeight","scrollHeight","removeProperty","rows","min","adjustHeight","borderWidth","parseInt","getPropertyValue","shiftClearBtn","clearBtn","parentElement","querySelector","marginRight","clientHeight","enhanceInput","classList","contains","closest","add","dl","tree","getRootNode","replace","error","isWrapped","newLabel","className","div","dt","dd","firstChild","innerHTML","text","before","display","readOnly","title","ariaLabel","tabIndex","previousValue","focus","utility","match","adjustWidth","placeholder","maxWidth","files","output","showBtn","handleUndoRedo","ctrlKey","metaKey","name","redoValue","specialCharMap","Minus","Equal","BracketLeft","BracketRight","Backslash","Semicolon","Quote","Comma","Period","Slash","Backquote","shiftSpecialCharMap","shortcutListener","shortcuts","shortcut","find","matchesKey","altKey","toUpperCase","code","matchesModifier","modifier","defaultModifier","createKeyboardShortcut","focusKey","toString","modifierSymbol","modifierKey","modifierAttr","default","linux","mac","windows","recognizedModifiers","some","indicator","tabGroupCount","enhanceTabs","group","querySelectorAll","panels","Array","from","children","filter","child","groupId","tabList","tabs","selectTab","index","moveFocus","i","panel","hidden","tab","selected","legend","textContent","current","indexOf","activeElement","keys","ArrowRight","ArrowLeft","Home","End","marked","prepend","observers","WeakMap","semanticForms","root","getElementsByClassName","MutationObserver","keyboardShortcuts","inputs","mutations","stop","mutation","node","addedNodes","attributes","childList","characterData","subtree","semanticFormsObserver","reinitialize","__webpack_module_cache__","moduleId","cachedModule","undefined","__webpack_modules__","Error","__webpack_exports__"],"sourceRoot":""}
@@ -68,3 +68,64 @@ body.no-js #flow_toggle,
68
68
  body.no-js #high_flow {
69
69
  display: none;
70
70
  }
71
+
72
+ // the custom layout example on the layout page, which lays its <dl> out as rows itself. the same rules are shown on that page
73
+ form.settingsExample {
74
+ dl {
75
+ display: flex;
76
+ flex-direction: column;
77
+ gap: 15px;
78
+ }
79
+
80
+ dl > div {
81
+ display: flex;
82
+ flex-wrap: wrap;
83
+ align-items: center;
84
+ justify-content: space-between;
85
+ gap: 10px 20px;
86
+ }
87
+
88
+ dt {
89
+ flex: 1 1 250px;
90
+ }
91
+
92
+ dt p {
93
+ margin: 2px 0 0;
94
+ font-size: small;
95
+ color: var(--semanticFormsPlaceholderColor);
96
+ }
97
+
98
+ dd {
99
+ flex: 0 0 200px;
100
+ margin-left: auto;
101
+ display: flex;
102
+ flex-direction: column;
103
+ align-items: flex-end;
104
+ gap: 5px;
105
+ }
106
+
107
+ dd:has(output) {
108
+ flex-direction: row;
109
+ align-items: center;
110
+ }
111
+
112
+ dd ul {
113
+ margin: 0;
114
+ padding: 0;
115
+ flex-direction: column;
116
+ align-items: flex-start;
117
+ gap: 5px;
118
+ }
119
+
120
+ dd li {
121
+ gap: 5px;
122
+ }
123
+
124
+ input[type=number] {
125
+ width: 110px;
126
+ }
127
+
128
+ input[type=color] {
129
+ width: 80px;
130
+ }
131
+ }
@@ -26,3 +26,7 @@ if (flowToggle) {
26
26
  flowToggle.addEventListener('change', showLowFlow)
27
27
  showLowFlow()
28
28
  }
29
+
30
+ // the custom layout example on the layout page shows a range's value in an <output> beside it, which the page's own code keeps up to date, as shown there
31
+ const customLayoutVolume = document.getElementById('custom-ex-volume')
32
+ if (customLayoutVolume) customLayoutVolume.addEventListener('input', () => { customLayoutVolume.nextElementSibling.value = customLayoutVolume.value })