@burger-editor/frozen-patty 4.0.0-alpha.7 → 4.0.0-alpha.70
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +18 -0
- package/dist/get-values.js +5 -3
- package/dist/normalize-indent.d.ts +19 -0
- package/dist/normalize-indent.js +29 -0
- package/dist/set-component.js +10 -0
- package/dist/set-value.d.ts +6 -5
- package/dist/set-value.js +250 -441
- package/dist/utils.js +18 -10
- package/package.json +6 -4
- package/dist/field-name-parser.d.ts +0 -8
- package/dist/field-name-parser.js +0 -18
- package/dist/field-name-stringifier.d.ts +0 -14
- package/dist/field-name-stringifier.js +0 -26
- package/dist/get.d.ts +0 -9
- package/dist/get.js +0 -19
- package/dist/handle-picture-element.d.ts +0 -6
- package/dist/handle-picture-element.js +0 -31
- package/dist/set-comonent.d.ts +0 -10
- package/dist/set-comonent.js +0 -71
- package/dist/set.d.ts +0 -9
- package/dist/set.js +0 -60
package/dist/set-value.js
CHANGED
|
@@ -25,7 +25,6 @@ export function setValue(el, name, datum, attr = 'field', filter, xssSanitize =
|
|
|
25
25
|
if (filter) {
|
|
26
26
|
datum = filter(datum);
|
|
27
27
|
}
|
|
28
|
-
// console.log({ name, field, fieldName, propName, datum, el: el.innerHTML });
|
|
29
28
|
if (!propName) {
|
|
30
29
|
setContent(el, datum, undefined, xssSanitize);
|
|
31
30
|
return;
|
|
@@ -74,6 +73,53 @@ export function setValue(el, name, datum, attr = 'field', filter, xssSanitize =
|
|
|
74
73
|
}
|
|
75
74
|
el.setAttribute(propName, `${datum}`);
|
|
76
75
|
}
|
|
76
|
+
/**
|
|
77
|
+
* Set a boolean attribute on an element via its DOM property.
|
|
78
|
+
* Empty string is treated as true (HTML boolean attribute convention).
|
|
79
|
+
* Falls back to setAttribute if the element doesn't match any of the expected types.
|
|
80
|
+
* @param el
|
|
81
|
+
* @param datum
|
|
82
|
+
* @param types
|
|
83
|
+
* @param prop
|
|
84
|
+
* @param emptyIsTrue
|
|
85
|
+
* @returns true if handled
|
|
86
|
+
*/
|
|
87
|
+
function setBooleanAttr(el, datum, types, prop, emptyIsTrue = true) {
|
|
88
|
+
if (types.some((T) => el instanceof T)) {
|
|
89
|
+
// SAFETY: prop is always a hardcoded string literal from setBooleanAttrByName
|
|
90
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
91
|
+
el[prop] = emptyIsTrue ? (datum === '' ? true : !!datum) : !!datum;
|
|
92
|
+
return true;
|
|
93
|
+
}
|
|
94
|
+
return false;
|
|
95
|
+
}
|
|
96
|
+
/**
|
|
97
|
+
* Set an integer attribute on an element via its DOM property.
|
|
98
|
+
* Removes the attribute if the parsed value is NaN.
|
|
99
|
+
* Falls back to setAttribute if the element doesn't match any of the expected types.
|
|
100
|
+
* @param el
|
|
101
|
+
* @param name
|
|
102
|
+
* @param datum
|
|
103
|
+
* @param types
|
|
104
|
+
* @param prop
|
|
105
|
+
* @param floor
|
|
106
|
+
* @returns true if handled
|
|
107
|
+
*/
|
|
108
|
+
function setIntegerAttr(el, name, datum, types, prop, floor = false) {
|
|
109
|
+
if (types.some((T) => el instanceof T)) {
|
|
110
|
+
const val = toInt(datum);
|
|
111
|
+
if (Number.isNaN(val)) {
|
|
112
|
+
el.removeAttribute(name);
|
|
113
|
+
}
|
|
114
|
+
else {
|
|
115
|
+
// SAFETY: prop is always a hardcoded string literal from setIntegerAttrByName
|
|
116
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
117
|
+
el[prop] = floor ? Math.floor(val) : val;
|
|
118
|
+
}
|
|
119
|
+
return true;
|
|
120
|
+
}
|
|
121
|
+
return false;
|
|
122
|
+
}
|
|
77
123
|
/**
|
|
78
124
|
*
|
|
79
125
|
* @param el
|
|
@@ -121,6 +167,11 @@ function set(el, prefix, name, datum, xssSanitize = true) {
|
|
|
121
167
|
el.removeAttribute(name);
|
|
122
168
|
return;
|
|
123
169
|
}
|
|
170
|
+
// Custom elements
|
|
171
|
+
if (el.localName.includes('-')) {
|
|
172
|
+
el.setAttribute(name, `${datum}`);
|
|
173
|
+
return;
|
|
174
|
+
}
|
|
124
175
|
if (!name.startsWith('data-') && !propInElement(el, name)) {
|
|
125
176
|
const dataAttr = `data-${prefix}-${kebabCase(name)}`;
|
|
126
177
|
if (el.hasAttribute(dataAttr)) {
|
|
@@ -128,6 +179,25 @@ function set(el, prefix, name, datum, xssSanitize = true) {
|
|
|
128
179
|
}
|
|
129
180
|
return;
|
|
130
181
|
}
|
|
182
|
+
if (setSpecialAttr(el, name, datum)) {
|
|
183
|
+
return;
|
|
184
|
+
}
|
|
185
|
+
if (setBooleanAttrByName(el, name, datum)) {
|
|
186
|
+
return;
|
|
187
|
+
}
|
|
188
|
+
if (setIntegerAttrByName(el, name, datum)) {
|
|
189
|
+
return;
|
|
190
|
+
}
|
|
191
|
+
el.setAttribute(name, `${datum}`);
|
|
192
|
+
}
|
|
193
|
+
/**
|
|
194
|
+
* Handle attributes with unique logic that doesn't fit boolean/integer patterns.
|
|
195
|
+
* @param el
|
|
196
|
+
* @param name
|
|
197
|
+
* @param datum
|
|
198
|
+
* @returns true if handled
|
|
199
|
+
*/
|
|
200
|
+
function setSpecialAttr(el, name, datum) {
|
|
131
201
|
switch (name) {
|
|
132
202
|
case 'contenteditable': {
|
|
133
203
|
switch (datum) {
|
|
@@ -141,7 +211,7 @@ function set(el, prefix, name, datum, xssSanitize = true) {
|
|
|
141
211
|
el.removeAttribute(name);
|
|
142
212
|
}
|
|
143
213
|
}
|
|
144
|
-
return;
|
|
214
|
+
return true;
|
|
145
215
|
}
|
|
146
216
|
case 'dir': {
|
|
147
217
|
switch (datum) {
|
|
@@ -150,45 +220,45 @@ function set(el, prefix, name, datum, xssSanitize = true) {
|
|
|
150
220
|
el.dir = datum;
|
|
151
221
|
break;
|
|
152
222
|
}
|
|
153
|
-
// case 'auto':
|
|
154
223
|
default: {
|
|
155
224
|
el.removeAttribute(name);
|
|
156
225
|
}
|
|
157
226
|
}
|
|
158
|
-
return;
|
|
227
|
+
return true;
|
|
159
228
|
}
|
|
160
229
|
case 'draggable': {
|
|
161
230
|
if (typeof datum === 'boolean') {
|
|
162
231
|
el.draggable = datum;
|
|
163
|
-
break;
|
|
164
232
|
}
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
233
|
+
else {
|
|
234
|
+
switch (datum) {
|
|
235
|
+
case 'true': {
|
|
236
|
+
el.draggable = true;
|
|
237
|
+
break;
|
|
238
|
+
}
|
|
239
|
+
case 'false': {
|
|
240
|
+
el.draggable = false;
|
|
241
|
+
break;
|
|
242
|
+
}
|
|
243
|
+
default: {
|
|
244
|
+
el.removeAttribute(name);
|
|
245
|
+
}
|
|
177
246
|
}
|
|
178
247
|
}
|
|
179
|
-
return;
|
|
248
|
+
return true;
|
|
180
249
|
}
|
|
181
250
|
case 'hidden': {
|
|
182
251
|
if (datum === 'until-found') {
|
|
183
252
|
el.setAttribute(name, datum);
|
|
184
|
-
return;
|
|
185
253
|
}
|
|
186
|
-
|
|
187
|
-
|
|
254
|
+
else {
|
|
255
|
+
el.hidden = !!datum;
|
|
256
|
+
}
|
|
257
|
+
return true;
|
|
188
258
|
}
|
|
189
259
|
case 'spellcheck': {
|
|
190
260
|
el.spellcheck = !!datum;
|
|
191
|
-
return;
|
|
261
|
+
return true;
|
|
192
262
|
}
|
|
193
263
|
case 'tabindex': {
|
|
194
264
|
let i;
|
|
@@ -199,501 +269,224 @@ function set(el, prefix, name, datum, xssSanitize = true) {
|
|
|
199
269
|
i = Number.parseInt(datum, 10);
|
|
200
270
|
}
|
|
201
271
|
else {
|
|
202
|
-
i = datum;
|
|
272
|
+
i = datum ?? -1;
|
|
203
273
|
}
|
|
204
274
|
el.tabIndex = Number.isNaN(i) ? -1 : Math.floor(i);
|
|
205
|
-
return;
|
|
206
|
-
}
|
|
207
|
-
case 'async': {
|
|
208
|
-
if (el instanceof HTMLScriptElement) {
|
|
209
|
-
el.async = !!datum;
|
|
210
|
-
}
|
|
211
|
-
else {
|
|
212
|
-
el.setAttribute(name, `${datum}`);
|
|
213
|
-
}
|
|
214
|
-
return;
|
|
275
|
+
return true;
|
|
215
276
|
}
|
|
216
277
|
case 'autocomplete': {
|
|
217
278
|
if (el instanceof HTMLInputElement || el instanceof HTMLFormElement) {
|
|
218
|
-
|
|
219
|
-
el.autocomplete = datum ? `${datum}` : 'off';
|
|
279
|
+
el.setAttribute(name, datum ? `${datum}` : 'off');
|
|
220
280
|
}
|
|
221
281
|
else {
|
|
222
282
|
el.setAttribute(name, `${datum}`);
|
|
223
283
|
}
|
|
224
|
-
return;
|
|
284
|
+
return true;
|
|
225
285
|
}
|
|
226
|
-
case '
|
|
227
|
-
if (el instanceof
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
286
|
+
case 'download': {
|
|
287
|
+
if (el instanceof HTMLAnchorElement) {
|
|
288
|
+
if (datum || datum === '') {
|
|
289
|
+
el.download = `${datum}`;
|
|
290
|
+
}
|
|
291
|
+
else {
|
|
292
|
+
el.removeAttribute(name);
|
|
293
|
+
}
|
|
232
294
|
}
|
|
233
295
|
else {
|
|
234
296
|
el.setAttribute(name, `${datum}`);
|
|
235
297
|
}
|
|
236
|
-
return;
|
|
298
|
+
return true;
|
|
237
299
|
}
|
|
238
|
-
case '
|
|
300
|
+
case 'preload': {
|
|
239
301
|
if (el instanceof HTMLAudioElement || el instanceof HTMLVideoElement) {
|
|
240
|
-
|
|
302
|
+
switch (datum) {
|
|
303
|
+
case '':
|
|
304
|
+
case 'auto':
|
|
305
|
+
case 'metadata': {
|
|
306
|
+
el.preload = datum;
|
|
307
|
+
break;
|
|
308
|
+
}
|
|
309
|
+
default: {
|
|
310
|
+
el.preload = 'none';
|
|
311
|
+
}
|
|
312
|
+
}
|
|
241
313
|
}
|
|
242
314
|
else {
|
|
243
315
|
el.setAttribute(name, `${datum}`);
|
|
244
316
|
}
|
|
245
|
-
return;
|
|
317
|
+
return true;
|
|
246
318
|
}
|
|
247
|
-
case '
|
|
319
|
+
case 'step': {
|
|
248
320
|
if (el instanceof HTMLInputElement) {
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
else {
|
|
252
|
-
el.setAttribute(name, `${datum}`);
|
|
253
|
-
}
|
|
254
|
-
return;
|
|
255
|
-
}
|
|
256
|
-
case 'cols': {
|
|
257
|
-
if (el instanceof HTMLTextAreaElement) {
|
|
258
|
-
const cols = toInt(datum);
|
|
259
|
-
if (Number.isNaN(cols)) {
|
|
260
|
-
el.removeAttribute(name);
|
|
321
|
+
if (datum === 'any') {
|
|
322
|
+
el.step = datum;
|
|
261
323
|
}
|
|
262
324
|
else {
|
|
263
|
-
|
|
325
|
+
const step = toNum(datum);
|
|
326
|
+
if (Number.isNaN(step)) {
|
|
327
|
+
el.removeAttribute(name);
|
|
328
|
+
}
|
|
329
|
+
else {
|
|
330
|
+
el.step = step.toString(10);
|
|
331
|
+
}
|
|
264
332
|
}
|
|
265
333
|
}
|
|
266
334
|
else {
|
|
267
335
|
el.setAttribute(name, `${datum}`);
|
|
268
336
|
}
|
|
269
|
-
return;
|
|
337
|
+
return true;
|
|
270
338
|
}
|
|
271
|
-
case '
|
|
272
|
-
if (el instanceof
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
339
|
+
case 'target': {
|
|
340
|
+
if (el instanceof HTMLAnchorElement ||
|
|
341
|
+
el instanceof HTMLAreaElement ||
|
|
342
|
+
el instanceof HTMLFormElement) {
|
|
343
|
+
if (datum) {
|
|
344
|
+
el.target = `${datum}`;
|
|
276
345
|
}
|
|
277
346
|
else {
|
|
278
|
-
el.
|
|
347
|
+
el.removeAttribute(name);
|
|
279
348
|
}
|
|
280
349
|
}
|
|
281
350
|
else {
|
|
282
351
|
el.setAttribute(name, `${datum}`);
|
|
283
352
|
}
|
|
284
|
-
return;
|
|
353
|
+
return true;
|
|
354
|
+
}
|
|
355
|
+
default: {
|
|
356
|
+
return false;
|
|
357
|
+
}
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
/**
|
|
361
|
+
* Handle boolean HTML attributes via their DOM properties.
|
|
362
|
+
* @param el
|
|
363
|
+
* @param name
|
|
364
|
+
* @param datum
|
|
365
|
+
* @returns true if handled
|
|
366
|
+
*/
|
|
367
|
+
function setBooleanAttrByName(el, name, datum) {
|
|
368
|
+
switch (name) {
|
|
369
|
+
case 'async': {
|
|
370
|
+
return setBooleanAttr(el, datum, [HTMLScriptElement], 'async', false);
|
|
371
|
+
}
|
|
372
|
+
case 'autofocus': {
|
|
373
|
+
return setBooleanAttr(el, datum, [HTMLButtonElement, HTMLInputElement, HTMLSelectElement, HTMLTextAreaElement], 'autofocus');
|
|
374
|
+
}
|
|
375
|
+
case 'autoplay': {
|
|
376
|
+
return setBooleanAttr(el, datum, [HTMLAudioElement, HTMLVideoElement], 'autoplay');
|
|
377
|
+
}
|
|
378
|
+
case 'checked': {
|
|
379
|
+
return setBooleanAttr(el, datum, [HTMLInputElement], 'checked');
|
|
285
380
|
}
|
|
286
381
|
case 'controls': {
|
|
287
|
-
|
|
288
|
-
el.controls = datum === '' ? true : !!datum;
|
|
289
|
-
}
|
|
290
|
-
else {
|
|
291
|
-
el.setAttribute(name, `${datum}`);
|
|
292
|
-
}
|
|
293
|
-
return;
|
|
382
|
+
return setBooleanAttr(el, datum, [HTMLAudioElement, HTMLVideoElement], 'controls');
|
|
294
383
|
}
|
|
295
384
|
case 'default': {
|
|
296
|
-
|
|
297
|
-
el.default = datum === '' ? true : !!datum;
|
|
298
|
-
}
|
|
299
|
-
else {
|
|
300
|
-
el.setAttribute(name, `${datum}`);
|
|
301
|
-
}
|
|
302
|
-
return;
|
|
385
|
+
return setBooleanAttr(el, datum, [HTMLTrackElement], 'default');
|
|
303
386
|
}
|
|
304
387
|
case 'defer': {
|
|
305
|
-
|
|
306
|
-
el.defer = !!datum;
|
|
307
|
-
}
|
|
308
|
-
else {
|
|
309
|
-
el.setAttribute(name, `${datum}`);
|
|
310
|
-
}
|
|
311
|
-
return;
|
|
388
|
+
return setBooleanAttr(el, datum, [HTMLScriptElement], 'defer', false);
|
|
312
389
|
}
|
|
313
390
|
case 'disabled': {
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
else {
|
|
324
|
-
el.setAttribute(name, `${datum}`);
|
|
325
|
-
}
|
|
326
|
-
return;
|
|
327
|
-
}
|
|
328
|
-
case 'download': {
|
|
329
|
-
if (el instanceof HTMLAnchorElement) {
|
|
330
|
-
if (datum || datum === '') {
|
|
331
|
-
el.download = `${datum}`;
|
|
332
|
-
}
|
|
333
|
-
else {
|
|
334
|
-
el.removeAttribute(name);
|
|
335
|
-
}
|
|
336
|
-
}
|
|
337
|
-
else {
|
|
338
|
-
el.setAttribute(name, `${datum}`);
|
|
339
|
-
}
|
|
340
|
-
return;
|
|
341
|
-
}
|
|
342
|
-
case 'high': {
|
|
343
|
-
if (el instanceof HTMLMeterElement) {
|
|
344
|
-
const high = toInt(datum);
|
|
345
|
-
if (Number.isNaN(high)) {
|
|
346
|
-
el.removeAttribute(name);
|
|
347
|
-
}
|
|
348
|
-
else {
|
|
349
|
-
el.high = high;
|
|
350
|
-
}
|
|
351
|
-
}
|
|
352
|
-
else {
|
|
353
|
-
el.setAttribute(name, `${datum}`);
|
|
354
|
-
}
|
|
355
|
-
return;
|
|
391
|
+
return setBooleanAttr(el, datum, [
|
|
392
|
+
HTMLButtonElement,
|
|
393
|
+
HTMLFieldSetElement,
|
|
394
|
+
HTMLInputElement,
|
|
395
|
+
HTMLOptGroupElement,
|
|
396
|
+
HTMLOptionElement,
|
|
397
|
+
HTMLSelectElement,
|
|
398
|
+
HTMLTextAreaElement,
|
|
399
|
+
], 'disabled');
|
|
356
400
|
}
|
|
357
401
|
case 'ismap': {
|
|
358
|
-
|
|
359
|
-
el.isMap = datum === '' ? true : !!datum;
|
|
360
|
-
}
|
|
361
|
-
else {
|
|
362
|
-
el.setAttribute(name, `${datum}`);
|
|
363
|
-
}
|
|
364
|
-
return;
|
|
402
|
+
return setBooleanAttr(el, datum, [HTMLImageElement], 'isMap');
|
|
365
403
|
}
|
|
366
404
|
case 'loop': {
|
|
367
|
-
|
|
368
|
-
el.loop = datum === '' ? true : !!datum;
|
|
369
|
-
}
|
|
370
|
-
else {
|
|
371
|
-
el.setAttribute(name, `${datum}`);
|
|
372
|
-
}
|
|
373
|
-
return;
|
|
374
|
-
}
|
|
375
|
-
case 'low': {
|
|
376
|
-
if (el instanceof HTMLMeterElement) {
|
|
377
|
-
const low = toInt(datum);
|
|
378
|
-
if (Number.isNaN(low)) {
|
|
379
|
-
el.removeAttribute(name);
|
|
380
|
-
}
|
|
381
|
-
else {
|
|
382
|
-
el.low = low;
|
|
383
|
-
}
|
|
384
|
-
}
|
|
385
|
-
else {
|
|
386
|
-
el.setAttribute(name, `${datum}`);
|
|
387
|
-
}
|
|
388
|
-
return;
|
|
389
|
-
}
|
|
390
|
-
case 'max': {
|
|
391
|
-
if (el instanceof HTMLInputElement ||
|
|
392
|
-
el instanceof HTMLMeterElement ||
|
|
393
|
-
el instanceof HTMLProgressElement) {
|
|
394
|
-
const max = toInt(datum);
|
|
395
|
-
if (Number.isNaN(max)) {
|
|
396
|
-
el.removeAttribute(name);
|
|
397
|
-
}
|
|
398
|
-
else {
|
|
399
|
-
el.max = max;
|
|
400
|
-
}
|
|
401
|
-
}
|
|
402
|
-
else {
|
|
403
|
-
el.setAttribute(name, `${datum}`);
|
|
404
|
-
}
|
|
405
|
-
return;
|
|
406
|
-
}
|
|
407
|
-
case 'maxlength': {
|
|
408
|
-
if (el instanceof HTMLInputElement || el instanceof HTMLTextAreaElement) {
|
|
409
|
-
const maxLength = toInt(datum);
|
|
410
|
-
if (Number.isNaN(maxLength)) {
|
|
411
|
-
el.removeAttribute(name);
|
|
412
|
-
}
|
|
413
|
-
else {
|
|
414
|
-
el.maxLength = Math.floor(maxLength);
|
|
415
|
-
}
|
|
416
|
-
}
|
|
417
|
-
else {
|
|
418
|
-
el.setAttribute(name, `${datum}`);
|
|
419
|
-
}
|
|
420
|
-
return;
|
|
421
|
-
}
|
|
422
|
-
case 'min': {
|
|
423
|
-
if (el instanceof HTMLInputElement || el instanceof HTMLMeterElement) {
|
|
424
|
-
const min = toInt(datum);
|
|
425
|
-
if (Number.isNaN(min)) {
|
|
426
|
-
el.removeAttribute(name);
|
|
427
|
-
}
|
|
428
|
-
else {
|
|
429
|
-
el.min = min;
|
|
430
|
-
}
|
|
431
|
-
}
|
|
432
|
-
else {
|
|
433
|
-
el.setAttribute(name, `${datum}`);
|
|
434
|
-
}
|
|
435
|
-
return;
|
|
405
|
+
return setBooleanAttr(el, datum, [HTMLAudioElement, HTMLVideoElement], 'loop');
|
|
436
406
|
}
|
|
437
407
|
case 'multiple': {
|
|
438
|
-
|
|
439
|
-
el.multiple = datum === '' ? true : !!datum;
|
|
440
|
-
}
|
|
441
|
-
else {
|
|
442
|
-
el.setAttribute(name, `${datum}`);
|
|
443
|
-
}
|
|
444
|
-
return;
|
|
408
|
+
return setBooleanAttr(el, datum, [HTMLInputElement, HTMLSelectElement], 'multiple');
|
|
445
409
|
}
|
|
446
410
|
case 'novalidate': {
|
|
447
|
-
|
|
448
|
-
el.noValidate = datum === '' ? true : !!datum;
|
|
449
|
-
}
|
|
450
|
-
else {
|
|
451
|
-
el.setAttribute(name, `${datum}`);
|
|
452
|
-
}
|
|
453
|
-
return;
|
|
411
|
+
return setBooleanAttr(el, datum, [HTMLFormElement], 'noValidate');
|
|
454
412
|
}
|
|
455
413
|
case 'open': {
|
|
456
|
-
|
|
457
|
-
el.open = datum === '' ? true : !!datum;
|
|
458
|
-
}
|
|
459
|
-
else {
|
|
460
|
-
el.setAttribute(name, `${datum}`);
|
|
461
|
-
}
|
|
462
|
-
break;
|
|
463
|
-
}
|
|
464
|
-
case 'optimum': {
|
|
465
|
-
if (el instanceof HTMLMeterElement) {
|
|
466
|
-
const optimum = toInt(datum);
|
|
467
|
-
if (Number.isNaN(optimum)) {
|
|
468
|
-
el.removeAttribute(name);
|
|
469
|
-
}
|
|
470
|
-
else {
|
|
471
|
-
el.optimum = Math.floor(optimum);
|
|
472
|
-
}
|
|
473
|
-
}
|
|
474
|
-
else {
|
|
475
|
-
el.setAttribute(name, `${datum}`);
|
|
476
|
-
}
|
|
477
|
-
return;
|
|
478
|
-
}
|
|
479
|
-
case 'preload': {
|
|
480
|
-
if (el instanceof HTMLAudioElement || el instanceof HTMLVideoElement) {
|
|
481
|
-
switch (datum) {
|
|
482
|
-
case '':
|
|
483
|
-
case 'auto':
|
|
484
|
-
case 'metadata': {
|
|
485
|
-
el.preload = datum;
|
|
486
|
-
break;
|
|
487
|
-
}
|
|
488
|
-
default: {
|
|
489
|
-
el.preload = 'none';
|
|
490
|
-
}
|
|
491
|
-
}
|
|
492
|
-
}
|
|
493
|
-
else {
|
|
494
|
-
el.setAttribute(name, `${datum}`);
|
|
495
|
-
}
|
|
496
|
-
return;
|
|
414
|
+
return setBooleanAttr(el, datum, [HTMLDetailsElement], 'open');
|
|
497
415
|
}
|
|
498
416
|
case 'readonly': {
|
|
499
|
-
|
|
500
|
-
el.readOnly = datum === '' ? true : !!datum;
|
|
501
|
-
}
|
|
502
|
-
else {
|
|
503
|
-
el.setAttribute(name, `${datum}`);
|
|
504
|
-
}
|
|
505
|
-
return;
|
|
417
|
+
return setBooleanAttr(el, datum, [HTMLInputElement, HTMLTextAreaElement], 'readOnly');
|
|
506
418
|
}
|
|
507
419
|
case 'required': {
|
|
508
|
-
|
|
509
|
-
el instanceof HTMLSelectElement ||
|
|
510
|
-
el instanceof HTMLTextAreaElement) {
|
|
511
|
-
el.required = datum === '' ? true : !!datum;
|
|
512
|
-
}
|
|
513
|
-
else {
|
|
514
|
-
el.setAttribute(name, `${datum}`);
|
|
515
|
-
}
|
|
516
|
-
return;
|
|
420
|
+
return setBooleanAttr(el, datum, [HTMLInputElement, HTMLSelectElement, HTMLTextAreaElement], 'required');
|
|
517
421
|
}
|
|
518
422
|
case 'reversed': {
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
423
|
+
return setBooleanAttr(el, datum, [HTMLOListElement], 'reversed');
|
|
424
|
+
}
|
|
425
|
+
default: {
|
|
426
|
+
return false;
|
|
427
|
+
}
|
|
428
|
+
}
|
|
429
|
+
}
|
|
430
|
+
/**
|
|
431
|
+
* Handle integer HTML attributes via their DOM properties.
|
|
432
|
+
* @param el
|
|
433
|
+
* @param name
|
|
434
|
+
* @param datum
|
|
435
|
+
* @returns true if handled
|
|
436
|
+
*/
|
|
437
|
+
function setIntegerAttrByName(el, name, datum) {
|
|
438
|
+
switch (name) {
|
|
439
|
+
case 'cols': {
|
|
440
|
+
return setIntegerAttr(el, name, datum, [HTMLTextAreaElement], 'cols');
|
|
441
|
+
}
|
|
442
|
+
case 'colspan': {
|
|
443
|
+
return setIntegerAttr(el, name, datum, [HTMLTableCellElement], 'colSpan');
|
|
444
|
+
}
|
|
445
|
+
case 'high': {
|
|
446
|
+
return setIntegerAttr(el, name, datum, [HTMLMeterElement], 'high');
|
|
447
|
+
}
|
|
448
|
+
case 'low': {
|
|
449
|
+
return setIntegerAttr(el, name, datum, [HTMLMeterElement], 'low');
|
|
450
|
+
}
|
|
451
|
+
case 'max': {
|
|
452
|
+
return setIntegerAttr(el, name, datum, [HTMLInputElement, HTMLMeterElement, HTMLProgressElement], 'max');
|
|
453
|
+
}
|
|
454
|
+
case 'maxlength': {
|
|
455
|
+
return setIntegerAttr(el, name, datum, [HTMLInputElement, HTMLTextAreaElement], 'maxLength', true);
|
|
456
|
+
}
|
|
457
|
+
case 'min': {
|
|
458
|
+
return setIntegerAttr(el, name, datum, [HTMLInputElement, HTMLMeterElement], 'min');
|
|
459
|
+
}
|
|
460
|
+
case 'optimum': {
|
|
461
|
+
return setIntegerAttr(el, name, datum, [HTMLMeterElement], 'optimum', true);
|
|
526
462
|
}
|
|
527
463
|
case 'rows': {
|
|
528
|
-
|
|
529
|
-
const rows = toInt(datum);
|
|
530
|
-
if (Number.isNaN(rows)) {
|
|
531
|
-
el.removeAttribute(name);
|
|
532
|
-
}
|
|
533
|
-
else {
|
|
534
|
-
el.rows = Math.floor(rows);
|
|
535
|
-
}
|
|
536
|
-
}
|
|
537
|
-
else {
|
|
538
|
-
el.setAttribute(name, `${datum}`);
|
|
539
|
-
}
|
|
540
|
-
return;
|
|
464
|
+
return setIntegerAttr(el, name, datum, [HTMLTextAreaElement], 'rows', true);
|
|
541
465
|
}
|
|
542
466
|
case 'rowspan': {
|
|
543
|
-
|
|
544
|
-
const rowSpan = toInt(datum);
|
|
545
|
-
if (Number.isNaN(rowSpan)) {
|
|
546
|
-
el.removeAttribute(name);
|
|
547
|
-
}
|
|
548
|
-
else {
|
|
549
|
-
el.rowSpan = Math.floor(rowSpan);
|
|
550
|
-
}
|
|
551
|
-
}
|
|
552
|
-
else {
|
|
553
|
-
el.setAttribute(name, `${datum}`);
|
|
554
|
-
}
|
|
555
|
-
return;
|
|
467
|
+
return setIntegerAttr(el, name, datum, [HTMLTableCellElement], 'rowSpan', true);
|
|
556
468
|
}
|
|
557
469
|
case 'size': {
|
|
558
|
-
|
|
559
|
-
const size = toInt(datum);
|
|
560
|
-
if (Number.isNaN(size)) {
|
|
561
|
-
el.removeAttribute(name);
|
|
562
|
-
}
|
|
563
|
-
else {
|
|
564
|
-
el.size = size;
|
|
565
|
-
}
|
|
566
|
-
}
|
|
567
|
-
else {
|
|
568
|
-
el.setAttribute(name, `${datum}`);
|
|
569
|
-
}
|
|
570
|
-
return;
|
|
470
|
+
return setIntegerAttr(el, name, datum, [HTMLInputElement, HTMLSelectElement], 'size');
|
|
571
471
|
}
|
|
572
472
|
case 'span': {
|
|
573
|
-
|
|
574
|
-
const span = toInt(datum);
|
|
575
|
-
if (Number.isNaN(span)) {
|
|
576
|
-
el.removeAttribute(name);
|
|
577
|
-
}
|
|
578
|
-
else {
|
|
579
|
-
el.span = span;
|
|
580
|
-
}
|
|
581
|
-
}
|
|
582
|
-
else {
|
|
583
|
-
el.setAttribute(name, `${datum}`);
|
|
584
|
-
}
|
|
585
|
-
return;
|
|
473
|
+
return setIntegerAttr(el, name, datum, [HTMLTableColElement], 'span');
|
|
586
474
|
}
|
|
587
475
|
case 'start': {
|
|
588
|
-
|
|
589
|
-
const start = toInt(datum);
|
|
590
|
-
if (Number.isNaN(start)) {
|
|
591
|
-
el.removeAttribute(name);
|
|
592
|
-
}
|
|
593
|
-
else {
|
|
594
|
-
el.start = start;
|
|
595
|
-
}
|
|
596
|
-
}
|
|
597
|
-
else {
|
|
598
|
-
el.setAttribute(name, `${datum}`);
|
|
599
|
-
}
|
|
600
|
-
return;
|
|
601
|
-
}
|
|
602
|
-
case 'step': {
|
|
603
|
-
if (el instanceof HTMLInputElement) {
|
|
604
|
-
if (datum === 'any') {
|
|
605
|
-
el.step = datum;
|
|
606
|
-
break;
|
|
607
|
-
}
|
|
608
|
-
const step = toNum(datum);
|
|
609
|
-
if (Number.isNaN(step)) {
|
|
610
|
-
el.removeAttribute(name);
|
|
611
|
-
}
|
|
612
|
-
else {
|
|
613
|
-
el.step = step.toString(10);
|
|
614
|
-
}
|
|
615
|
-
}
|
|
616
|
-
else {
|
|
617
|
-
el.setAttribute(name, `${datum}`);
|
|
618
|
-
}
|
|
619
|
-
return;
|
|
620
|
-
}
|
|
621
|
-
case 'target': {
|
|
622
|
-
if (el instanceof HTMLAnchorElement ||
|
|
623
|
-
el instanceof HTMLAreaElement ||
|
|
624
|
-
el instanceof HTMLFormElement) {
|
|
625
|
-
if (datum) {
|
|
626
|
-
el.target = `${datum}`;
|
|
627
|
-
}
|
|
628
|
-
else {
|
|
629
|
-
el.removeAttribute(name);
|
|
630
|
-
}
|
|
631
|
-
}
|
|
632
|
-
else {
|
|
633
|
-
el.setAttribute(name, `${datum}`);
|
|
634
|
-
}
|
|
635
|
-
return;
|
|
476
|
+
return setIntegerAttr(el, name, datum, [HTMLOListElement], 'start');
|
|
636
477
|
}
|
|
637
|
-
// case 'accesskey':
|
|
638
|
-
// case 'class':
|
|
639
|
-
// case 'contextmenu':
|
|
640
|
-
// case 'dropzone': // breakable support
|
|
641
|
-
// case 'id':
|
|
642
|
-
// case 'itemid':
|
|
643
|
-
// case 'itemprop':
|
|
644
|
-
// case 'itemref':
|
|
645
|
-
// case 'itemscope':
|
|
646
|
-
// case 'itemtype':
|
|
647
|
-
// case 'lang':
|
|
648
|
-
// case 'slot': // breakable support
|
|
649
|
-
// case 'style':
|
|
650
|
-
// case 'title':
|
|
651
|
-
// case 'translate': // breakable support
|
|
652
|
-
// case 'accept':
|
|
653
|
-
// case 'accept-charset':
|
|
654
|
-
// case 'action':
|
|
655
|
-
// case 'align':
|
|
656
|
-
// case 'alt':
|
|
657
|
-
// case 'cite':
|
|
658
|
-
// case 'coords':
|
|
659
|
-
// case 'datetime':
|
|
660
|
-
// case 'enctype':
|
|
661
|
-
// case 'for':
|
|
662
|
-
// case 'headers':
|
|
663
|
-
// case 'href':
|
|
664
|
-
// case 'hreflang':
|
|
665
|
-
// case 'kind':
|
|
666
|
-
// case 'label':
|
|
667
|
-
// case 'list':
|
|
668
|
-
// case 'media':
|
|
669
|
-
// case 'method':
|
|
670
|
-
// case 'name':
|
|
671
|
-
// case 'pattern':
|
|
672
|
-
// case 'placeholder':
|
|
673
|
-
// case 'poster':
|
|
674
|
-
// case 'pubdate':
|
|
675
|
-
// case 'rel':
|
|
676
|
-
// case 'sandbox':
|
|
677
|
-
// case 'src':
|
|
678
|
-
// case 'srcdoc':
|
|
679
|
-
// case 'srclang':
|
|
680
|
-
// case 'summary':
|
|
681
|
-
// case 'type':
|
|
682
|
-
// case 'usemap':
|
|
683
|
-
// case 'value':
|
|
684
|
-
// case 'wrap':
|
|
685
|
-
// and Data-* attributes
|
|
686
478
|
default: {
|
|
687
|
-
|
|
479
|
+
return false;
|
|
688
480
|
}
|
|
689
481
|
}
|
|
690
482
|
}
|
|
691
483
|
/**
|
|
692
|
-
*
|
|
693
|
-
*
|
|
694
|
-
* @param
|
|
695
|
-
* @param
|
|
696
|
-
* @param
|
|
484
|
+
* Set content value on a form element or general element.
|
|
485
|
+
* Handles checkbox/radio checked state, input values, and innerHTML/textContent.
|
|
486
|
+
* @param el - The target element
|
|
487
|
+
* @param datum - The value to set
|
|
488
|
+
* @param asHtml - Whether to set as innerHTML (true) or textContent (false)
|
|
489
|
+
* @param xssSanitize - Whether to sanitize HTML content for XSS protection
|
|
697
490
|
*/
|
|
698
491
|
export function setContent(el, datum, asHtml = true, xssSanitize = true) {
|
|
699
492
|
if (el instanceof HTMLInputElement ||
|
|
@@ -702,11 +495,27 @@ export function setContent(el, datum, asHtml = true, xssSanitize = true) {
|
|
|
702
495
|
el instanceof HTMLOutputElement) {
|
|
703
496
|
if (el instanceof HTMLInputElement &&
|
|
704
497
|
(el.type === 'radio' || el.type === 'checkbox')) {
|
|
498
|
+
// Boolean values are always set directly regardless of value attribute
|
|
705
499
|
if (typeof datum === 'boolean') {
|
|
706
500
|
el.checked = datum;
|
|
707
501
|
return;
|
|
708
502
|
}
|
|
709
|
-
|
|
503
|
+
// Checkbox/radio with value attribute: traditional value comparison
|
|
504
|
+
if (el.hasAttribute('value')) {
|
|
505
|
+
el.checked = el.value === `${datum}`;
|
|
506
|
+
return;
|
|
507
|
+
}
|
|
508
|
+
// Checkbox without value attribute: switch-like behavior (string boolean processing)
|
|
509
|
+
if (datum === 'true') {
|
|
510
|
+
el.checked = true;
|
|
511
|
+
return;
|
|
512
|
+
}
|
|
513
|
+
if (datum === 'false') {
|
|
514
|
+
el.checked = false;
|
|
515
|
+
return;
|
|
516
|
+
}
|
|
517
|
+
// Other values default to false
|
|
518
|
+
el.checked = false;
|
|
710
519
|
return;
|
|
711
520
|
}
|
|
712
521
|
el.value = `${datum}`;
|