@burger-editor/frozen-patty 0.11.0 → 4.0.0-alpha.10
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 +553 -30
- package/dist/field-name-parser.d.ts +8 -0
- package/dist/field-name-parser.js +18 -0
- package/dist/field-name-stringifier.d.ts +14 -0
- package/dist/field-name-stringifier.js +26 -0
- package/dist/frozen-patty.d.ts +7 -2
- package/dist/frozen-patty.js +18 -5
- package/dist/get-component.d.ts +9 -0
- package/dist/get-component.js +29 -0
- package/dist/get-value.d.ts +1 -1
- package/dist/get-value.js +1 -1
- package/dist/get-values.d.ts +3 -4
- package/dist/get-values.js +114 -77
- package/dist/get.d.ts +7 -0
- package/dist/get.js +7 -0
- package/dist/handle-picture-element.d.ts +6 -0
- package/dist/handle-picture-element.js +31 -0
- package/dist/index.d.ts +5 -1
- package/dist/index.js +5 -1
- package/dist/parse-fields.d.ts +6 -0
- package/dist/parse-fields.js +38 -0
- package/dist/set-comonent.d.ts +10 -0
- package/dist/set-comonent.js +71 -0
- package/dist/set-component.d.ts +10 -0
- package/dist/set-component.js +91 -0
- package/dist/set-value.d.ts +11 -2
- package/dist/set-value.js +259 -197
- package/dist/set.d.ts +7 -0
- package/dist/set.js +9 -1
- package/dist/stringify-fields.d.ts +14 -0
- package/dist/stringify-fields.js +26 -0
- package/dist/types.d.ts +7 -0
- package/dist/utils.d.ts +82 -1
- package/dist/utils.js +265 -9
- package/package.json +10 -7
- package/LICENSE +0 -21
package/dist/set-value.js
CHANGED
|
@@ -1,136 +1,147 @@
|
|
|
1
|
+
import { kebabCase } from '@burger-editor/utils';
|
|
2
|
+
import { parseFields } from './parse-fields.js';
|
|
3
|
+
import { propInElement, replaceNode, sanitizeAttributeValue, sanitizeHtml, } from './utils.js';
|
|
1
4
|
/**
|
|
2
5
|
* Set value to an element
|
|
3
6
|
*
|
|
4
7
|
* ```html
|
|
5
8
|
* <div [target-attribute] data-[attr]="[name]:[target-attribute]"></div>
|
|
6
9
|
* ```
|
|
7
|
-
*
|
|
8
10
|
* @param el A target element
|
|
9
11
|
* @param name A label name
|
|
10
12
|
* @param datum A datum of value
|
|
11
13
|
* @param attr Data attribute name for specifying the node that FrozenPatty treats as a field
|
|
14
|
+
* @param filter
|
|
15
|
+
* @param xssSanitize Enable XSS protection
|
|
12
16
|
*/
|
|
13
|
-
export function setValue(el, name, datum, attr = 'field', filter) {
|
|
14
|
-
const
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
}
|
|
46
|
-
if (filter) {
|
|
47
|
-
datum = filter(datum);
|
|
48
|
-
}
|
|
49
|
-
//
|
|
50
|
-
// 属性による対応
|
|
51
|
-
//
|
|
52
|
-
if (targetAttr) {
|
|
53
|
-
//
|
|
54
|
-
// style属性
|
|
55
|
-
//
|
|
56
|
-
if (/^style\([a-z-]+\)$/i.test(targetAttr)) {
|
|
57
|
-
const cssPropertyName = targetAttr.replace(/^style\(([a-z-]+)\)$/i, '$1');
|
|
58
|
-
let cssValue;
|
|
59
|
-
switch (cssPropertyName) {
|
|
60
|
-
case 'background-image': {
|
|
61
|
-
//
|
|
62
|
-
// NGパターン
|
|
63
|
-
// $changeDom.css(cssPropertyName, 'url("' + value + '")');
|
|
64
|
-
//
|
|
65
|
-
// cssメソッドを経由すると styleAPIを使用するので URLがホストを含めた絶対パスになる
|
|
66
|
-
// デモサーバーから本番サーバーへの移行ができなくなってしまうので避ける
|
|
67
|
-
// 単純な文字列を流し込む(setAttributeを利用)
|
|
68
|
-
// urlはマルチバイト文字や空白記号を含まないはずであるがエスケープする
|
|
69
|
-
const url = encodeURI(`${datum}`);
|
|
70
|
-
cssValue = `url(${url})`;
|
|
71
|
-
break;
|
|
72
|
-
}
|
|
73
|
-
//
|
|
74
|
-
// TODO: 他にもvalueに単位が必要なケースなどに対応したい
|
|
75
|
-
//
|
|
76
|
-
default: {
|
|
77
|
-
cssValue = `${datum}`;
|
|
78
|
-
}
|
|
79
|
-
}
|
|
80
|
-
el.setAttribute('style', `${cssPropertyName}: ${cssValue}`);
|
|
81
|
-
//
|
|
82
|
-
// 属性
|
|
83
|
-
//
|
|
84
|
-
}
|
|
85
|
-
else if (el instanceof HTMLElement) {
|
|
86
|
-
setAttribute(el, targetAttr, datum);
|
|
87
|
-
}
|
|
88
|
-
else {
|
|
89
|
-
// SVGElement or more
|
|
90
|
-
el.setAttribute(targetAttr, `${datum}`);
|
|
17
|
+
export function setValue(el, name, datum, attr = 'field', filter, xssSanitize = true) {
|
|
18
|
+
const rawValue = el.getAttribute(`data-${attr}`);
|
|
19
|
+
const fields = parseFields(rawValue ?? '');
|
|
20
|
+
const field = fields.find((field) => field.fieldName === name);
|
|
21
|
+
if (!field) {
|
|
22
|
+
return;
|
|
23
|
+
}
|
|
24
|
+
const { propName } = field;
|
|
25
|
+
if (filter) {
|
|
26
|
+
datum = filter(datum);
|
|
27
|
+
}
|
|
28
|
+
// console.log({ name, field, fieldName, propName, datum, el: el.innerHTML });
|
|
29
|
+
if (!propName) {
|
|
30
|
+
setContent(el, datum, undefined, xssSanitize);
|
|
31
|
+
return;
|
|
32
|
+
}
|
|
33
|
+
if (propName === 'style' && el instanceof HTMLElement) {
|
|
34
|
+
el.style.cssText = `${datum}`;
|
|
35
|
+
return;
|
|
36
|
+
}
|
|
37
|
+
if (/^style\([a-z-]+\)$/i.test(propName)) {
|
|
38
|
+
const cssPropertyName = propName.replace(/^style\(([a-z-]+)\)$/i, '$1');
|
|
39
|
+
let cssValue;
|
|
40
|
+
switch (cssPropertyName) {
|
|
41
|
+
case 'background-image': {
|
|
42
|
+
// Using CSS method would create absolute URLs with host
|
|
43
|
+
// This would prevent migration from demo to production server
|
|
44
|
+
// Using simple string insertion (setAttribute) instead
|
|
45
|
+
// URL may not contain multibyte or whitespace characters, but escape anyway
|
|
46
|
+
const url = encodeURI(`${datum}`);
|
|
47
|
+
cssValue = `url(${url})`;
|
|
48
|
+
break;
|
|
91
49
|
}
|
|
92
50
|
//
|
|
93
|
-
//
|
|
51
|
+
// TODO: Handle other cases where values need units
|
|
94
52
|
//
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
if (el instanceof HTMLInputElement ||
|
|
98
|
-
el instanceof HTMLSelectElement ||
|
|
99
|
-
el instanceof HTMLTextAreaElement) {
|
|
100
|
-
el.value = `${datum}`;
|
|
101
|
-
}
|
|
102
|
-
else {
|
|
103
|
-
el.innerHTML = `${datum}`;
|
|
53
|
+
default: {
|
|
54
|
+
cssValue = `${datum}`;
|
|
104
55
|
}
|
|
105
56
|
}
|
|
57
|
+
el.setAttribute('style', `${cssPropertyName}: ${cssValue}`);
|
|
58
|
+
return;
|
|
59
|
+
}
|
|
60
|
+
if (el instanceof HTMLElement) {
|
|
61
|
+
// HTMLElement
|
|
62
|
+
set(el, attr, propName, datum, xssSanitize);
|
|
63
|
+
return;
|
|
106
64
|
}
|
|
65
|
+
// SVGElement or more
|
|
66
|
+
// ↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓
|
|
67
|
+
// Check attribute value if XSS protection is enabled
|
|
68
|
+
if (xssSanitize) {
|
|
69
|
+
const safeValue = sanitizeAttributeValue(propName, `${datum}`);
|
|
70
|
+
if (safeValue !== null) {
|
|
71
|
+
el.setAttribute(propName, safeValue);
|
|
72
|
+
}
|
|
73
|
+
return;
|
|
74
|
+
}
|
|
75
|
+
el.setAttribute(propName, `${datum}`);
|
|
107
76
|
}
|
|
108
77
|
/**
|
|
109
|
-
* Set attribute
|
|
110
|
-
*
|
|
111
|
-
* @param el Target HTML element
|
|
112
|
-
* @param attr Attribute name
|
|
113
|
-
* @param datum Datum
|
|
114
78
|
*
|
|
79
|
+
* @param el
|
|
80
|
+
* @param prefix
|
|
81
|
+
* @param name
|
|
82
|
+
* @param datum
|
|
83
|
+
* @param xssSanitize
|
|
115
84
|
*/
|
|
116
|
-
function
|
|
85
|
+
function set(el, prefix, name, datum, xssSanitize = true) {
|
|
86
|
+
if (datum == null) {
|
|
87
|
+
el.removeAttribute(name);
|
|
88
|
+
return;
|
|
89
|
+
}
|
|
90
|
+
switch (name) {
|
|
91
|
+
case 'text': {
|
|
92
|
+
setContent(el, datum, false, xssSanitize);
|
|
93
|
+
return;
|
|
94
|
+
}
|
|
95
|
+
case 'html': {
|
|
96
|
+
setContent(el, datum, true, xssSanitize);
|
|
97
|
+
return;
|
|
98
|
+
}
|
|
99
|
+
case 'node': {
|
|
100
|
+
if (typeof datum !== 'string') {
|
|
101
|
+
return;
|
|
102
|
+
}
|
|
103
|
+
replaceNode(el, datum, prefix, xssSanitize);
|
|
104
|
+
return;
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
// If XSS protection is enabled
|
|
108
|
+
if (xssSanitize) {
|
|
109
|
+
// Don't set event handler attributes (starting with 'on')
|
|
110
|
+
if (name.toLowerCase().startsWith('on')) {
|
|
111
|
+
return;
|
|
112
|
+
}
|
|
113
|
+
const beforeSanitize = datum;
|
|
114
|
+
datum = sanitizeAttributeValue(name, datum);
|
|
115
|
+
// If sanitization results in null value (except when original value was null), don't change the value
|
|
116
|
+
if (datum === null && beforeSanitize !== null) {
|
|
117
|
+
return;
|
|
118
|
+
}
|
|
119
|
+
}
|
|
117
120
|
if (datum == null) {
|
|
118
|
-
el.removeAttribute(
|
|
121
|
+
el.removeAttribute(name);
|
|
119
122
|
return;
|
|
120
123
|
}
|
|
121
|
-
|
|
124
|
+
if (!name.startsWith('data-') && !propInElement(el, name)) {
|
|
125
|
+
const dataAttr = `data-${prefix}-${kebabCase(name)}`;
|
|
126
|
+
if (el.hasAttribute(dataAttr)) {
|
|
127
|
+
el.setAttribute(dataAttr, `${datum}`);
|
|
128
|
+
}
|
|
129
|
+
return;
|
|
130
|
+
}
|
|
131
|
+
switch (name) {
|
|
122
132
|
case 'contenteditable': {
|
|
123
133
|
switch (datum) {
|
|
134
|
+
case true:
|
|
124
135
|
case 'true':
|
|
125
136
|
case '': {
|
|
126
137
|
el.contentEditable = 'true';
|
|
127
138
|
break;
|
|
128
139
|
}
|
|
129
140
|
default: {
|
|
130
|
-
el.removeAttribute(
|
|
141
|
+
el.removeAttribute(name);
|
|
131
142
|
}
|
|
132
143
|
}
|
|
133
|
-
|
|
144
|
+
return;
|
|
134
145
|
}
|
|
135
146
|
case 'dir': {
|
|
136
147
|
switch (datum) {
|
|
@@ -141,10 +152,10 @@ function setAttribute(el, attr, datum) {
|
|
|
141
152
|
}
|
|
142
153
|
// case 'auto':
|
|
143
154
|
default: {
|
|
144
|
-
el.removeAttribute(
|
|
155
|
+
el.removeAttribute(name);
|
|
145
156
|
}
|
|
146
157
|
}
|
|
147
|
-
|
|
158
|
+
return;
|
|
148
159
|
}
|
|
149
160
|
case 'draggable': {
|
|
150
161
|
if (typeof datum === 'boolean') {
|
|
@@ -162,18 +173,22 @@ function setAttribute(el, attr, datum) {
|
|
|
162
173
|
}
|
|
163
174
|
// case 'auto':
|
|
164
175
|
default: {
|
|
165
|
-
el.removeAttribute(
|
|
176
|
+
el.removeAttribute(name);
|
|
166
177
|
}
|
|
167
178
|
}
|
|
168
|
-
|
|
179
|
+
return;
|
|
169
180
|
}
|
|
170
181
|
case 'hidden': {
|
|
182
|
+
if (datum === 'until-found') {
|
|
183
|
+
el.setAttribute(name, datum);
|
|
184
|
+
return;
|
|
185
|
+
}
|
|
171
186
|
el.hidden = !!datum;
|
|
172
|
-
|
|
187
|
+
return;
|
|
173
188
|
}
|
|
174
189
|
case 'spellcheck': {
|
|
175
190
|
el.spellcheck = !!datum;
|
|
176
|
-
|
|
191
|
+
return;
|
|
177
192
|
}
|
|
178
193
|
case 'tabindex': {
|
|
179
194
|
let i;
|
|
@@ -187,16 +202,16 @@ function setAttribute(el, attr, datum) {
|
|
|
187
202
|
i = datum;
|
|
188
203
|
}
|
|
189
204
|
el.tabIndex = Number.isNaN(i) ? -1 : Math.floor(i);
|
|
190
|
-
|
|
205
|
+
return;
|
|
191
206
|
}
|
|
192
207
|
case 'async': {
|
|
193
208
|
if (el instanceof HTMLScriptElement) {
|
|
194
209
|
el.async = !!datum;
|
|
195
210
|
}
|
|
196
211
|
else {
|
|
197
|
-
el.setAttribute(
|
|
212
|
+
el.setAttribute(name, `${datum}`);
|
|
198
213
|
}
|
|
199
|
-
|
|
214
|
+
return;
|
|
200
215
|
}
|
|
201
216
|
case 'autocomplete': {
|
|
202
217
|
if (el instanceof HTMLInputElement || el instanceof HTMLFormElement) {
|
|
@@ -204,9 +219,9 @@ function setAttribute(el, attr, datum) {
|
|
|
204
219
|
el.autocomplete = datum ? `${datum}` : 'off';
|
|
205
220
|
}
|
|
206
221
|
else {
|
|
207
|
-
el.setAttribute(
|
|
222
|
+
el.setAttribute(name, `${datum}`);
|
|
208
223
|
}
|
|
209
|
-
|
|
224
|
+
return;
|
|
210
225
|
}
|
|
211
226
|
case 'autofocus': {
|
|
212
227
|
if (el instanceof HTMLButtonElement ||
|
|
@@ -216,84 +231,84 @@ function setAttribute(el, attr, datum) {
|
|
|
216
231
|
el.autofocus = datum === '' ? true : !!datum;
|
|
217
232
|
}
|
|
218
233
|
else {
|
|
219
|
-
el.setAttribute(
|
|
234
|
+
el.setAttribute(name, `${datum}`);
|
|
220
235
|
}
|
|
221
|
-
|
|
236
|
+
return;
|
|
222
237
|
}
|
|
223
238
|
case 'autoplay': {
|
|
224
239
|
if (el instanceof HTMLAudioElement || el instanceof HTMLVideoElement) {
|
|
225
240
|
el.autoplay = datum === '' ? true : !!datum;
|
|
226
241
|
}
|
|
227
242
|
else {
|
|
228
|
-
el.setAttribute(
|
|
243
|
+
el.setAttribute(name, `${datum}`);
|
|
229
244
|
}
|
|
230
|
-
|
|
245
|
+
return;
|
|
231
246
|
}
|
|
232
247
|
case 'checked': {
|
|
233
248
|
if (el instanceof HTMLInputElement) {
|
|
234
249
|
el.checked = datum === '' ? true : !!datum;
|
|
235
250
|
}
|
|
236
251
|
else {
|
|
237
|
-
el.setAttribute(
|
|
252
|
+
el.setAttribute(name, `${datum}`);
|
|
238
253
|
}
|
|
239
|
-
|
|
254
|
+
return;
|
|
240
255
|
}
|
|
241
256
|
case 'cols': {
|
|
242
257
|
if (el instanceof HTMLTextAreaElement) {
|
|
243
258
|
const cols = toInt(datum);
|
|
244
259
|
if (Number.isNaN(cols)) {
|
|
245
|
-
el.removeAttribute(
|
|
260
|
+
el.removeAttribute(name);
|
|
246
261
|
}
|
|
247
262
|
else {
|
|
248
263
|
el.cols = cols;
|
|
249
264
|
}
|
|
250
265
|
}
|
|
251
266
|
else {
|
|
252
|
-
el.setAttribute(
|
|
267
|
+
el.setAttribute(name, `${datum}`);
|
|
253
268
|
}
|
|
254
|
-
|
|
269
|
+
return;
|
|
255
270
|
}
|
|
256
271
|
case 'colspan': {
|
|
257
272
|
if (el instanceof HTMLTableCellElement) {
|
|
258
273
|
const colSpan = toInt(datum);
|
|
259
274
|
if (Number.isNaN(colSpan)) {
|
|
260
|
-
el.removeAttribute(
|
|
275
|
+
el.removeAttribute(name);
|
|
261
276
|
}
|
|
262
277
|
else {
|
|
263
278
|
el.colSpan = colSpan;
|
|
264
279
|
}
|
|
265
280
|
}
|
|
266
281
|
else {
|
|
267
|
-
el.setAttribute(
|
|
282
|
+
el.setAttribute(name, `${datum}`);
|
|
268
283
|
}
|
|
269
|
-
|
|
284
|
+
return;
|
|
270
285
|
}
|
|
271
286
|
case 'controls': {
|
|
272
287
|
if (el instanceof HTMLAudioElement || el instanceof HTMLVideoElement) {
|
|
273
288
|
el.controls = datum === '' ? true : !!datum;
|
|
274
289
|
}
|
|
275
290
|
else {
|
|
276
|
-
el.setAttribute(
|
|
291
|
+
el.setAttribute(name, `${datum}`);
|
|
277
292
|
}
|
|
278
|
-
|
|
293
|
+
return;
|
|
279
294
|
}
|
|
280
295
|
case 'default': {
|
|
281
296
|
if (el instanceof HTMLTrackElement) {
|
|
282
297
|
el.default = datum === '' ? true : !!datum;
|
|
283
298
|
}
|
|
284
299
|
else {
|
|
285
|
-
el.setAttribute(
|
|
300
|
+
el.setAttribute(name, `${datum}`);
|
|
286
301
|
}
|
|
287
|
-
|
|
302
|
+
return;
|
|
288
303
|
}
|
|
289
304
|
case 'defer': {
|
|
290
305
|
if (el instanceof HTMLScriptElement) {
|
|
291
306
|
el.defer = !!datum;
|
|
292
307
|
}
|
|
293
308
|
else {
|
|
294
|
-
el.setAttribute(
|
|
309
|
+
el.setAttribute(name, `${datum}`);
|
|
295
310
|
}
|
|
296
|
-
|
|
311
|
+
return;
|
|
297
312
|
}
|
|
298
313
|
case 'disabled': {
|
|
299
314
|
if (el instanceof HTMLButtonElement ||
|
|
@@ -306,9 +321,9 @@ function setAttribute(el, attr, datum) {
|
|
|
306
321
|
el.disabled = datum === '' ? true : !!datum;
|
|
307
322
|
}
|
|
308
323
|
else {
|
|
309
|
-
el.setAttribute(
|
|
324
|
+
el.setAttribute(name, `${datum}`);
|
|
310
325
|
}
|
|
311
|
-
|
|
326
|
+
return;
|
|
312
327
|
}
|
|
313
328
|
case 'download': {
|
|
314
329
|
if (el instanceof HTMLAnchorElement) {
|
|
@@ -316,61 +331,61 @@ function setAttribute(el, attr, datum) {
|
|
|
316
331
|
el.download = `${datum}`;
|
|
317
332
|
}
|
|
318
333
|
else {
|
|
319
|
-
el.removeAttribute(
|
|
334
|
+
el.removeAttribute(name);
|
|
320
335
|
}
|
|
321
336
|
}
|
|
322
337
|
else {
|
|
323
|
-
el.setAttribute(
|
|
338
|
+
el.setAttribute(name, `${datum}`);
|
|
324
339
|
}
|
|
325
|
-
|
|
340
|
+
return;
|
|
326
341
|
}
|
|
327
342
|
case 'high': {
|
|
328
343
|
if (el instanceof HTMLMeterElement) {
|
|
329
344
|
const high = toInt(datum);
|
|
330
345
|
if (Number.isNaN(high)) {
|
|
331
|
-
el.removeAttribute(
|
|
346
|
+
el.removeAttribute(name);
|
|
332
347
|
}
|
|
333
348
|
else {
|
|
334
349
|
el.high = high;
|
|
335
350
|
}
|
|
336
351
|
}
|
|
337
352
|
else {
|
|
338
|
-
el.setAttribute(
|
|
353
|
+
el.setAttribute(name, `${datum}`);
|
|
339
354
|
}
|
|
340
|
-
|
|
355
|
+
return;
|
|
341
356
|
}
|
|
342
357
|
case 'ismap': {
|
|
343
358
|
if (el instanceof HTMLImageElement) {
|
|
344
359
|
el.isMap = datum === '' ? true : !!datum;
|
|
345
360
|
}
|
|
346
361
|
else {
|
|
347
|
-
el.setAttribute(
|
|
362
|
+
el.setAttribute(name, `${datum}`);
|
|
348
363
|
}
|
|
349
|
-
|
|
364
|
+
return;
|
|
350
365
|
}
|
|
351
366
|
case 'loop': {
|
|
352
367
|
if (el instanceof HTMLAudioElement || el instanceof HTMLVideoElement) {
|
|
353
368
|
el.loop = datum === '' ? true : !!datum;
|
|
354
369
|
}
|
|
355
370
|
else {
|
|
356
|
-
el.setAttribute(
|
|
371
|
+
el.setAttribute(name, `${datum}`);
|
|
357
372
|
}
|
|
358
|
-
|
|
373
|
+
return;
|
|
359
374
|
}
|
|
360
375
|
case 'low': {
|
|
361
376
|
if (el instanceof HTMLMeterElement) {
|
|
362
377
|
const low = toInt(datum);
|
|
363
378
|
if (Number.isNaN(low)) {
|
|
364
|
-
el.removeAttribute(
|
|
379
|
+
el.removeAttribute(name);
|
|
365
380
|
}
|
|
366
381
|
else {
|
|
367
382
|
el.low = low;
|
|
368
383
|
}
|
|
369
384
|
}
|
|
370
385
|
else {
|
|
371
|
-
el.setAttribute(
|
|
386
|
+
el.setAttribute(name, `${datum}`);
|
|
372
387
|
}
|
|
373
|
-
|
|
388
|
+
return;
|
|
374
389
|
}
|
|
375
390
|
case 'max': {
|
|
376
391
|
if (el instanceof HTMLInputElement ||
|
|
@@ -378,88 +393,88 @@ function setAttribute(el, attr, datum) {
|
|
|
378
393
|
el instanceof HTMLProgressElement) {
|
|
379
394
|
const max = toInt(datum);
|
|
380
395
|
if (Number.isNaN(max)) {
|
|
381
|
-
el.removeAttribute(
|
|
396
|
+
el.removeAttribute(name);
|
|
382
397
|
}
|
|
383
398
|
else {
|
|
384
399
|
el.max = max;
|
|
385
400
|
}
|
|
386
401
|
}
|
|
387
402
|
else {
|
|
388
|
-
el.setAttribute(
|
|
403
|
+
el.setAttribute(name, `${datum}`);
|
|
389
404
|
}
|
|
390
|
-
|
|
405
|
+
return;
|
|
391
406
|
}
|
|
392
407
|
case 'maxlength': {
|
|
393
408
|
if (el instanceof HTMLInputElement || el instanceof HTMLTextAreaElement) {
|
|
394
409
|
const maxLength = toInt(datum);
|
|
395
410
|
if (Number.isNaN(maxLength)) {
|
|
396
|
-
el.removeAttribute(
|
|
411
|
+
el.removeAttribute(name);
|
|
397
412
|
}
|
|
398
413
|
else {
|
|
399
414
|
el.maxLength = Math.floor(maxLength);
|
|
400
415
|
}
|
|
401
416
|
}
|
|
402
417
|
else {
|
|
403
|
-
el.setAttribute(
|
|
418
|
+
el.setAttribute(name, `${datum}`);
|
|
404
419
|
}
|
|
405
|
-
|
|
420
|
+
return;
|
|
406
421
|
}
|
|
407
422
|
case 'min': {
|
|
408
423
|
if (el instanceof HTMLInputElement || el instanceof HTMLMeterElement) {
|
|
409
424
|
const min = toInt(datum);
|
|
410
425
|
if (Number.isNaN(min)) {
|
|
411
|
-
el.removeAttribute(
|
|
426
|
+
el.removeAttribute(name);
|
|
412
427
|
}
|
|
413
428
|
else {
|
|
414
429
|
el.min = min;
|
|
415
430
|
}
|
|
416
431
|
}
|
|
417
432
|
else {
|
|
418
|
-
el.setAttribute(
|
|
433
|
+
el.setAttribute(name, `${datum}`);
|
|
419
434
|
}
|
|
420
|
-
|
|
435
|
+
return;
|
|
421
436
|
}
|
|
422
437
|
case 'multiple': {
|
|
423
438
|
if (el instanceof HTMLInputElement || el instanceof HTMLSelectElement) {
|
|
424
439
|
el.multiple = datum === '' ? true : !!datum;
|
|
425
440
|
}
|
|
426
441
|
else {
|
|
427
|
-
el.setAttribute(
|
|
442
|
+
el.setAttribute(name, `${datum}`);
|
|
428
443
|
}
|
|
429
|
-
|
|
444
|
+
return;
|
|
430
445
|
}
|
|
431
446
|
case 'novalidate': {
|
|
432
447
|
if (el instanceof HTMLFormElement) {
|
|
433
448
|
el.noValidate = datum === '' ? true : !!datum;
|
|
434
449
|
}
|
|
435
450
|
else {
|
|
436
|
-
el.setAttribute(
|
|
451
|
+
el.setAttribute(name, `${datum}`);
|
|
452
|
+
}
|
|
453
|
+
return;
|
|
454
|
+
}
|
|
455
|
+
case 'open': {
|
|
456
|
+
if (el instanceof HTMLDetailsElement) {
|
|
457
|
+
el.open = datum === '' ? true : !!datum;
|
|
458
|
+
}
|
|
459
|
+
else {
|
|
460
|
+
el.setAttribute(name, `${datum}`);
|
|
437
461
|
}
|
|
438
462
|
break;
|
|
439
463
|
}
|
|
440
|
-
// ⚰️ Unsupported HTMLDetailsElement yet...
|
|
441
|
-
// case 'open': {
|
|
442
|
-
// if (el instanceof HTMLDetailsElement) {
|
|
443
|
-
// el.open = datum === '' ? true : !!datum;
|
|
444
|
-
// } else {
|
|
445
|
-
// el.setAttribute(attr, `${datum}`);
|
|
446
|
-
// }
|
|
447
|
-
// break;
|
|
448
|
-
// }
|
|
449
464
|
case 'optimum': {
|
|
450
465
|
if (el instanceof HTMLMeterElement) {
|
|
451
466
|
const optimum = toInt(datum);
|
|
452
467
|
if (Number.isNaN(optimum)) {
|
|
453
|
-
el.removeAttribute(
|
|
468
|
+
el.removeAttribute(name);
|
|
454
469
|
}
|
|
455
470
|
else {
|
|
456
471
|
el.optimum = Math.floor(optimum);
|
|
457
472
|
}
|
|
458
473
|
}
|
|
459
474
|
else {
|
|
460
|
-
el.setAttribute(
|
|
475
|
+
el.setAttribute(name, `${datum}`);
|
|
461
476
|
}
|
|
462
|
-
|
|
477
|
+
return;
|
|
463
478
|
}
|
|
464
479
|
case 'preload': {
|
|
465
480
|
if (el instanceof HTMLAudioElement || el instanceof HTMLVideoElement) {
|
|
@@ -476,18 +491,18 @@ function setAttribute(el, attr, datum) {
|
|
|
476
491
|
}
|
|
477
492
|
}
|
|
478
493
|
else {
|
|
479
|
-
el.setAttribute(
|
|
494
|
+
el.setAttribute(name, `${datum}`);
|
|
480
495
|
}
|
|
481
|
-
|
|
496
|
+
return;
|
|
482
497
|
}
|
|
483
498
|
case 'readonly': {
|
|
484
499
|
if (el instanceof HTMLInputElement || el instanceof HTMLTextAreaElement) {
|
|
485
500
|
el.readOnly = datum === '' ? true : !!datum;
|
|
486
501
|
}
|
|
487
502
|
else {
|
|
488
|
-
el.setAttribute(
|
|
503
|
+
el.setAttribute(name, `${datum}`);
|
|
489
504
|
}
|
|
490
|
-
|
|
505
|
+
return;
|
|
491
506
|
}
|
|
492
507
|
case 'required': {
|
|
493
508
|
if (el instanceof HTMLInputElement ||
|
|
@@ -496,93 +511,93 @@ function setAttribute(el, attr, datum) {
|
|
|
496
511
|
el.required = datum === '' ? true : !!datum;
|
|
497
512
|
}
|
|
498
513
|
else {
|
|
499
|
-
el.setAttribute(
|
|
514
|
+
el.setAttribute(name, `${datum}`);
|
|
500
515
|
}
|
|
501
|
-
|
|
516
|
+
return;
|
|
502
517
|
}
|
|
503
518
|
case 'reversed': {
|
|
504
519
|
if (el instanceof HTMLOListElement) {
|
|
505
520
|
el.reversed = datum === '' ? true : !!datum;
|
|
506
521
|
}
|
|
507
522
|
else {
|
|
508
|
-
el.setAttribute(
|
|
523
|
+
el.setAttribute(name, `${datum}`);
|
|
509
524
|
}
|
|
510
|
-
|
|
525
|
+
return;
|
|
511
526
|
}
|
|
512
527
|
case 'rows': {
|
|
513
528
|
if (el instanceof HTMLTextAreaElement) {
|
|
514
529
|
const rows = toInt(datum);
|
|
515
530
|
if (Number.isNaN(rows)) {
|
|
516
|
-
el.removeAttribute(
|
|
531
|
+
el.removeAttribute(name);
|
|
517
532
|
}
|
|
518
533
|
else {
|
|
519
534
|
el.rows = Math.floor(rows);
|
|
520
535
|
}
|
|
521
536
|
}
|
|
522
537
|
else {
|
|
523
|
-
el.setAttribute(
|
|
538
|
+
el.setAttribute(name, `${datum}`);
|
|
524
539
|
}
|
|
525
|
-
|
|
540
|
+
return;
|
|
526
541
|
}
|
|
527
542
|
case 'rowspan': {
|
|
528
543
|
if (el instanceof HTMLTableCellElement) {
|
|
529
544
|
const rowSpan = toInt(datum);
|
|
530
545
|
if (Number.isNaN(rowSpan)) {
|
|
531
|
-
el.removeAttribute(
|
|
546
|
+
el.removeAttribute(name);
|
|
532
547
|
}
|
|
533
548
|
else {
|
|
534
549
|
el.rowSpan = Math.floor(rowSpan);
|
|
535
550
|
}
|
|
536
551
|
}
|
|
537
552
|
else {
|
|
538
|
-
el.setAttribute(
|
|
553
|
+
el.setAttribute(name, `${datum}`);
|
|
539
554
|
}
|
|
540
|
-
|
|
555
|
+
return;
|
|
541
556
|
}
|
|
542
557
|
case 'size': {
|
|
543
558
|
if (el instanceof HTMLInputElement || el instanceof HTMLSelectElement) {
|
|
544
559
|
const size = toInt(datum);
|
|
545
560
|
if (Number.isNaN(size)) {
|
|
546
|
-
el.removeAttribute(
|
|
561
|
+
el.removeAttribute(name);
|
|
547
562
|
}
|
|
548
563
|
else {
|
|
549
564
|
el.size = size;
|
|
550
565
|
}
|
|
551
566
|
}
|
|
552
567
|
else {
|
|
553
|
-
el.setAttribute(
|
|
568
|
+
el.setAttribute(name, `${datum}`);
|
|
554
569
|
}
|
|
555
|
-
|
|
570
|
+
return;
|
|
556
571
|
}
|
|
557
572
|
case 'span': {
|
|
558
573
|
if (el instanceof HTMLTableColElement) {
|
|
559
574
|
const span = toInt(datum);
|
|
560
575
|
if (Number.isNaN(span)) {
|
|
561
|
-
el.removeAttribute(
|
|
576
|
+
el.removeAttribute(name);
|
|
562
577
|
}
|
|
563
578
|
else {
|
|
564
579
|
el.span = span;
|
|
565
580
|
}
|
|
566
581
|
}
|
|
567
582
|
else {
|
|
568
|
-
el.setAttribute(
|
|
583
|
+
el.setAttribute(name, `${datum}`);
|
|
569
584
|
}
|
|
570
|
-
|
|
585
|
+
return;
|
|
571
586
|
}
|
|
572
587
|
case 'start': {
|
|
573
588
|
if (el instanceof HTMLOListElement) {
|
|
574
589
|
const start = toInt(datum);
|
|
575
590
|
if (Number.isNaN(start)) {
|
|
576
|
-
el.removeAttribute(
|
|
591
|
+
el.removeAttribute(name);
|
|
577
592
|
}
|
|
578
593
|
else {
|
|
579
594
|
el.start = start;
|
|
580
595
|
}
|
|
581
596
|
}
|
|
582
597
|
else {
|
|
583
|
-
el.setAttribute(
|
|
598
|
+
el.setAttribute(name, `${datum}`);
|
|
584
599
|
}
|
|
585
|
-
|
|
600
|
+
return;
|
|
586
601
|
}
|
|
587
602
|
case 'step': {
|
|
588
603
|
if (el instanceof HTMLInputElement) {
|
|
@@ -592,16 +607,16 @@ function setAttribute(el, attr, datum) {
|
|
|
592
607
|
}
|
|
593
608
|
const step = toNum(datum);
|
|
594
609
|
if (Number.isNaN(step)) {
|
|
595
|
-
el.removeAttribute(
|
|
610
|
+
el.removeAttribute(name);
|
|
596
611
|
}
|
|
597
612
|
else {
|
|
598
613
|
el.step = step.toString(10);
|
|
599
614
|
}
|
|
600
615
|
}
|
|
601
616
|
else {
|
|
602
|
-
el.setAttribute(
|
|
617
|
+
el.setAttribute(name, `${datum}`);
|
|
603
618
|
}
|
|
604
|
-
|
|
619
|
+
return;
|
|
605
620
|
}
|
|
606
621
|
case 'target': {
|
|
607
622
|
if (el instanceof HTMLAnchorElement ||
|
|
@@ -611,13 +626,13 @@ function setAttribute(el, attr, datum) {
|
|
|
611
626
|
el.target = `${datum}`;
|
|
612
627
|
}
|
|
613
628
|
else {
|
|
614
|
-
el.removeAttribute(
|
|
629
|
+
el.removeAttribute(name);
|
|
615
630
|
}
|
|
616
631
|
}
|
|
617
632
|
else {
|
|
618
|
-
el.setAttribute(
|
|
633
|
+
el.setAttribute(name, `${datum}`);
|
|
619
634
|
}
|
|
620
|
-
|
|
635
|
+
return;
|
|
621
636
|
}
|
|
622
637
|
// case 'accesskey':
|
|
623
638
|
// case 'class':
|
|
@@ -669,10 +684,53 @@ function setAttribute(el, attr, datum) {
|
|
|
669
684
|
// case 'wrap':
|
|
670
685
|
// and Data-* attributes
|
|
671
686
|
default: {
|
|
672
|
-
el.setAttribute(
|
|
687
|
+
el.setAttribute(name, `${datum}`);
|
|
673
688
|
}
|
|
674
689
|
}
|
|
675
690
|
}
|
|
691
|
+
/**
|
|
692
|
+
*
|
|
693
|
+
* @param el
|
|
694
|
+
* @param datum
|
|
695
|
+
* @param asHtml
|
|
696
|
+
* @param xssSanitize
|
|
697
|
+
*/
|
|
698
|
+
export function setContent(el, datum, asHtml = true, xssSanitize = true) {
|
|
699
|
+
if (el instanceof HTMLInputElement ||
|
|
700
|
+
el instanceof HTMLSelectElement ||
|
|
701
|
+
el instanceof HTMLTextAreaElement ||
|
|
702
|
+
el instanceof HTMLOutputElement) {
|
|
703
|
+
if (el instanceof HTMLInputElement &&
|
|
704
|
+
(el.type === 'radio' || el.type === 'checkbox')) {
|
|
705
|
+
if (typeof datum === 'boolean') {
|
|
706
|
+
el.checked = datum;
|
|
707
|
+
return;
|
|
708
|
+
}
|
|
709
|
+
el.checked = el.value === `${datum}`;
|
|
710
|
+
return;
|
|
711
|
+
}
|
|
712
|
+
el.value = `${datum}`;
|
|
713
|
+
return;
|
|
714
|
+
}
|
|
715
|
+
if (asHtml) {
|
|
716
|
+
// Sanitize HTML if XSS protection is enabled
|
|
717
|
+
const htmlStr = datum == null ? '' : `${datum}`;
|
|
718
|
+
if (xssSanitize && typeof htmlStr === 'string') {
|
|
719
|
+
// sanitizeHtml will now replace dangerous elements instead of removing them
|
|
720
|
+
el.innerHTML = sanitizeHtml(htmlStr);
|
|
721
|
+
}
|
|
722
|
+
else {
|
|
723
|
+
el.innerHTML = htmlStr;
|
|
724
|
+
}
|
|
725
|
+
return;
|
|
726
|
+
}
|
|
727
|
+
// No need to sanitize textContent (automatically escaped)
|
|
728
|
+
el.textContent = datum == null ? '' : `${datum}`;
|
|
729
|
+
}
|
|
730
|
+
/**
|
|
731
|
+
*
|
|
732
|
+
* @param datum
|
|
733
|
+
*/
|
|
676
734
|
function toNum(datum) {
|
|
677
735
|
let i;
|
|
678
736
|
if (typeof datum === 'boolean') {
|
|
@@ -686,6 +744,10 @@ function toNum(datum) {
|
|
|
686
744
|
}
|
|
687
745
|
return i;
|
|
688
746
|
}
|
|
747
|
+
/**
|
|
748
|
+
*
|
|
749
|
+
* @param datum
|
|
750
|
+
*/
|
|
689
751
|
function toInt(datum) {
|
|
690
752
|
return Math.floor(toNum(datum));
|
|
691
753
|
}
|