@burger-editor/frozen-patty 4.0.0-alpha.2 → 4.0.0-alpha.4
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 +548 -21
- package/dist/field-name-stringifier.d.ts +14 -0
- package/dist/field-name-stringifier.js +26 -0
- package/dist/frozen-patty.d.ts +7 -0
- 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-values.js +47 -29
- package/dist/handle-picture-element.d.ts +6 -0
- package/dist/handle-picture-element.js +31 -0
- 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 +80 -0
- package/dist/set-value.d.ts +4 -2
- package/dist/set-value.js +98 -66
- package/dist/set.d.ts +2 -1
- package/dist/set.js +5 -4
- 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 +78 -1
- package/dist/utils.js +268 -2
- package/package.json +3 -3
package/dist/frozen-patty.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
import
|
|
2
|
-
import
|
|
1
|
+
import { getComponent } from './get-component.js';
|
|
2
|
+
import { setComponent } from './set-component.js';
|
|
3
|
+
import { sanitizeHtml } from './utils.js';
|
|
3
4
|
export default class FrozenPatty {
|
|
4
5
|
#attr = 'field';
|
|
5
6
|
#dom;
|
|
@@ -8,6 +9,10 @@ export default class FrozenPatty {
|
|
|
8
9
|
*/
|
|
9
10
|
#filter;
|
|
10
11
|
#typeConvert = false;
|
|
12
|
+
/**
|
|
13
|
+
* Enable XSS protection
|
|
14
|
+
*/
|
|
15
|
+
#xssSanitize = true;
|
|
11
16
|
/**
|
|
12
17
|
*
|
|
13
18
|
* @param html Original HTML
|
|
@@ -15,7 +20,14 @@ export default class FrozenPatty {
|
|
|
15
20
|
*/
|
|
16
21
|
constructor(html, options) {
|
|
17
22
|
this.#dom = document.createElement('fp-placeholer');
|
|
18
|
-
|
|
23
|
+
// Sanitize initial HTML if XSS protection is enabled
|
|
24
|
+
if (options?.xssSanitize === false) {
|
|
25
|
+
this.#dom.innerHTML = html;
|
|
26
|
+
this.#xssSanitize = false;
|
|
27
|
+
}
|
|
28
|
+
else {
|
|
29
|
+
this.#dom.innerHTML = sanitizeHtml(html);
|
|
30
|
+
}
|
|
19
31
|
if (options) {
|
|
20
32
|
if (options.attr) {
|
|
21
33
|
this.#attr = options.attr;
|
|
@@ -27,7 +39,8 @@ export default class FrozenPatty {
|
|
|
27
39
|
merge(data) {
|
|
28
40
|
const currentData = this.toJSON(false);
|
|
29
41
|
const newData = Object.assign(currentData, data);
|
|
30
|
-
|
|
42
|
+
// Pass XSS protection flag to set (default is true if not set)
|
|
43
|
+
this.#dom = setComponent(this.#dom, newData, this.#attr, this.#filter, this.#xssSanitize);
|
|
31
44
|
return this;
|
|
32
45
|
}
|
|
33
46
|
toDOM() {
|
|
@@ -38,6 +51,6 @@ export default class FrozenPatty {
|
|
|
38
51
|
}
|
|
39
52
|
toJSON(filtering = true) {
|
|
40
53
|
const filter = filtering ? this.#filter : undefined;
|
|
41
|
-
return
|
|
54
|
+
return getComponent(this.#dom, this.#attr, this.#typeConvert, filter);
|
|
42
55
|
}
|
|
43
56
|
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { Filter, FrozenPattyData } from './types.js';
|
|
2
|
+
/**
|
|
3
|
+
*
|
|
4
|
+
* @param el
|
|
5
|
+
* @param attr
|
|
6
|
+
* @param typeConvert
|
|
7
|
+
* @param filter
|
|
8
|
+
*/
|
|
9
|
+
export declare function getComponent(el: Element, attr: string, typeConvert: boolean, filter?: Filter): FrozenPattyData;
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { getValues } from './get-values.js';
|
|
2
|
+
import { arrayToHash } from './utils.js';
|
|
3
|
+
const reverseListElementSelectors = ['picture'];
|
|
4
|
+
/**
|
|
5
|
+
*
|
|
6
|
+
* @param el
|
|
7
|
+
* @param attr
|
|
8
|
+
* @param typeConvert
|
|
9
|
+
* @param filter
|
|
10
|
+
*/
|
|
11
|
+
export function getComponent(el, attr, typeConvert, filter) {
|
|
12
|
+
el = el.cloneNode(true);
|
|
13
|
+
const reverseListElements = el.querySelectorAll(`:is(${reverseListElementSelectors.join(',')})[data-${attr}-list]`);
|
|
14
|
+
for (const reverseListElement of reverseListElements) {
|
|
15
|
+
const children = [];
|
|
16
|
+
while (reverseListElement.lastElementChild) {
|
|
17
|
+
children.push(reverseListElement.lastElementChild);
|
|
18
|
+
reverseListElement.lastElementChild.remove();
|
|
19
|
+
}
|
|
20
|
+
reverseListElement.append(...children);
|
|
21
|
+
}
|
|
22
|
+
const filedElements = el.querySelectorAll(`[data-${attr}]`);
|
|
23
|
+
let values = [];
|
|
24
|
+
for (const _el of filedElements) {
|
|
25
|
+
values = [...values, ...getValues(_el, typeConvert, attr, filter)];
|
|
26
|
+
}
|
|
27
|
+
const result = arrayToHash(values);
|
|
28
|
+
return result;
|
|
29
|
+
}
|
package/dist/get-values.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { parseFields } from './parse-fields.js';
|
|
2
2
|
import { kebabCase } from './utils.js';
|
|
3
3
|
/**
|
|
4
4
|
* Get value from an element
|
|
@@ -9,16 +9,16 @@ import { kebabCase } from './utils.js';
|
|
|
9
9
|
*/
|
|
10
10
|
export function getValues(el, convertType = false, attr = 'field', filter) {
|
|
11
11
|
const result = [];
|
|
12
|
-
const
|
|
13
|
-
|
|
14
|
-
const forceArray = !!listRoot;
|
|
15
|
-
if (rawValue == null) {
|
|
12
|
+
const query = el.getAttribute(`data-${attr}`);
|
|
13
|
+
if (query == null) {
|
|
16
14
|
throw new Error(`data-${attr} attriblute is empty.`);
|
|
17
15
|
}
|
|
18
|
-
const
|
|
19
|
-
|
|
16
|
+
const listRoot = el.closest(`[data-${attr}-list]`);
|
|
17
|
+
const forceArray = !!listRoot;
|
|
18
|
+
const fields = parseFields(query);
|
|
19
|
+
for (const field of fields) {
|
|
20
20
|
let value;
|
|
21
|
-
const { fieldName, propName } =
|
|
21
|
+
const { fieldName, propName } = field;
|
|
22
22
|
if (propName) {
|
|
23
23
|
switch (propName) {
|
|
24
24
|
case 'node': {
|
|
@@ -33,6 +33,12 @@ export function getValues(el, convertType = false, attr = 'field', filter) {
|
|
|
33
33
|
value = el.innerHTML.trim();
|
|
34
34
|
break;
|
|
35
35
|
}
|
|
36
|
+
case 'style': {
|
|
37
|
+
if (el instanceof HTMLElement) {
|
|
38
|
+
value = el.style.cssText;
|
|
39
|
+
}
|
|
40
|
+
break;
|
|
41
|
+
}
|
|
36
42
|
default: {
|
|
37
43
|
if (/^style\([a-z-]+\)$/i.test(propName)) {
|
|
38
44
|
const css = propName.replace(/^style\(([a-z-]+)\)$/i, '$1');
|
|
@@ -49,7 +55,7 @@ export function getValues(el, convertType = false, attr = 'field', filter) {
|
|
|
49
55
|
}
|
|
50
56
|
break;
|
|
51
57
|
}
|
|
52
|
-
value = getAttribute(el,
|
|
58
|
+
value = getAttribute(el, propName, attr, convertType);
|
|
53
59
|
}
|
|
54
60
|
}
|
|
55
61
|
}
|
|
@@ -77,11 +83,11 @@ export function getValues(el, convertType = false, attr = 'field', filter) {
|
|
|
77
83
|
/**
|
|
78
84
|
*
|
|
79
85
|
* @param el
|
|
80
|
-
* @param attr
|
|
81
86
|
* @param keyAttr
|
|
87
|
+
* @param attr
|
|
82
88
|
* @param typeConvert
|
|
83
89
|
*/
|
|
84
|
-
function getAttribute(el,
|
|
90
|
+
function getAttribute(el, keyAttr, attr, typeConvert) {
|
|
85
91
|
switch (keyAttr) {
|
|
86
92
|
case 'contenteditable': {
|
|
87
93
|
if (el instanceof HTMLElement) {
|
|
@@ -103,24 +109,35 @@ function getAttribute(el, attr, keyAttr, typeConvert) {
|
|
|
103
109
|
return el.hasAttribute('download') ? el.getAttribute('download') : null;
|
|
104
110
|
}
|
|
105
111
|
case 'href': {
|
|
106
|
-
//
|
|
107
|
-
|
|
112
|
+
// Example: (el as HTMLAnchorElement).href;
|
|
113
|
+
// Expected: return defined value of plain string
|
|
114
|
+
return el.getAttribute(keyAttr) ?? '';
|
|
108
115
|
}
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
value = el.getAttribute(keyAttr) || '';
|
|
117
|
-
}
|
|
118
|
-
if (typeConvert) {
|
|
119
|
-
value = convert(value);
|
|
120
|
-
}
|
|
121
|
-
return value;
|
|
116
|
+
}
|
|
117
|
+
let value =
|
|
118
|
+
// @ts-ignore
|
|
119
|
+
el[keyAttr];
|
|
120
|
+
if (value !== undefined) {
|
|
121
|
+
if (typeConvert) {
|
|
122
|
+
value = convert(value);
|
|
122
123
|
}
|
|
124
|
+
return value;
|
|
123
125
|
}
|
|
126
|
+
// For shorthand notation, get value from data-field-* attribute
|
|
127
|
+
const dataAttr = ['data', attr, kebabCase(keyAttr)].join('-');
|
|
128
|
+
if (el.hasAttribute(dataAttr)) {
|
|
129
|
+
return el.getAttribute(dataAttr) ?? '';
|
|
130
|
+
}
|
|
131
|
+
value = el.getAttribute(keyAttr) ?? null;
|
|
132
|
+
if ((value === '' || value == null) &&
|
|
133
|
+
typeof keyAttr === 'string' &&
|
|
134
|
+
keyAttr.startsWith('data-')) {
|
|
135
|
+
return '';
|
|
136
|
+
}
|
|
137
|
+
if (typeConvert) {
|
|
138
|
+
value = convert(value);
|
|
139
|
+
}
|
|
140
|
+
return value;
|
|
124
141
|
}
|
|
125
142
|
/**
|
|
126
143
|
*
|
|
@@ -128,8 +145,9 @@ function getAttribute(el, attr, keyAttr, typeConvert) {
|
|
|
128
145
|
*/
|
|
129
146
|
function convert(value) {
|
|
130
147
|
value = parse(value);
|
|
131
|
-
|
|
132
|
-
|
|
148
|
+
const str = `${value}`;
|
|
149
|
+
if (URL.canParse(str)) {
|
|
150
|
+
const url = new URL(str, location.href);
|
|
133
151
|
if (url.origin === location.origin) {
|
|
134
152
|
return url.pathname;
|
|
135
153
|
}
|
|
@@ -142,7 +160,7 @@ function convert(value) {
|
|
|
142
160
|
*/
|
|
143
161
|
function parse(value) {
|
|
144
162
|
try {
|
|
145
|
-
return JSON.parse(value);
|
|
163
|
+
return JSON.parse(`${value}`);
|
|
146
164
|
}
|
|
147
165
|
catch {
|
|
148
166
|
return value;
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { parseFields } from './parse-fields.js';
|
|
2
|
+
import { setValue } from './set-value.js';
|
|
3
|
+
/**
|
|
4
|
+
* picture要素の特別な処理を行います
|
|
5
|
+
* @param pictureElement picture要素
|
|
6
|
+
* @param attr
|
|
7
|
+
*/
|
|
8
|
+
export function handlePictureElement(pictureElement, attr) {
|
|
9
|
+
const children = [...pictureElement.children];
|
|
10
|
+
const fieldAttrName = `data-${attr}`;
|
|
11
|
+
for (const [i, child] of children.entries()) {
|
|
12
|
+
const fields = parseFields(child.getAttribute(fieldAttrName) ?? '');
|
|
13
|
+
const newChild = child.cloneNode(false);
|
|
14
|
+
child.remove();
|
|
15
|
+
if (i > 0) {
|
|
16
|
+
setValue(newChild, 'node', 'source', attr);
|
|
17
|
+
}
|
|
18
|
+
switch (newChild.localName) {
|
|
19
|
+
case 'source': {
|
|
20
|
+
// newChild.removeAttribute('alt');
|
|
21
|
+
// setValue(newChild, name, attr.value);
|
|
22
|
+
break;
|
|
23
|
+
}
|
|
24
|
+
case 'img': {
|
|
25
|
+
//
|
|
26
|
+
break;
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
pictureElement.insertBefore(newChild, pictureElement.firstChild);
|
|
30
|
+
}
|
|
31
|
+
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { camelCase } from './utils.js';
|
|
2
|
+
const cache = new Map();
|
|
3
|
+
/**
|
|
4
|
+
*
|
|
5
|
+
* @param query
|
|
6
|
+
*/
|
|
7
|
+
export function parseFields(query) {
|
|
8
|
+
const cached = cache.get(query);
|
|
9
|
+
if (cached) {
|
|
10
|
+
return cached;
|
|
11
|
+
}
|
|
12
|
+
const fields = query.split(',');
|
|
13
|
+
const result = fields.map(parseField);
|
|
14
|
+
cache.set(query, result);
|
|
15
|
+
return result;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
*
|
|
19
|
+
* @param field
|
|
20
|
+
*/
|
|
21
|
+
function parseField(field) {
|
|
22
|
+
field = field.trim().toLowerCase();
|
|
23
|
+
if (!field) {
|
|
24
|
+
throw new Error('Field name is empty.');
|
|
25
|
+
}
|
|
26
|
+
let [fieldName, propName,
|
|
27
|
+
// eslint-disable-next-line prefer-const
|
|
28
|
+
...rest] = field.split(':');
|
|
29
|
+
if (rest.length > 0) {
|
|
30
|
+
throw new Error('Invalid field format.');
|
|
31
|
+
}
|
|
32
|
+
propName = propName?.trim() || '';
|
|
33
|
+
fieldName = fieldName?.trim() || propName || field;
|
|
34
|
+
return {
|
|
35
|
+
fieldName: camelCase(fieldName),
|
|
36
|
+
propName,
|
|
37
|
+
};
|
|
38
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { Filter, FrozenPattyData } from './types.js';
|
|
2
|
+
/**
|
|
3
|
+
*
|
|
4
|
+
* @param el
|
|
5
|
+
* @param data
|
|
6
|
+
* @param attr
|
|
7
|
+
* @param filter
|
|
8
|
+
* @param xssSanitize Enable XSS protection
|
|
9
|
+
*/
|
|
10
|
+
export declare function setComponent(el: Element, data: FrozenPattyData, attr: string, filter?: Filter, xssSanitize?: boolean): Element;
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import { setValue } from './set-value.js';
|
|
2
|
+
import { stringifyFields } from './stringify-fields.js';
|
|
3
|
+
import { definedFields, flattenData, getFields, hasField, kebabCase, maxLengthOf, removeProp, replaceNode, replaceProp, } from './utils.js';
|
|
4
|
+
/**
|
|
5
|
+
*
|
|
6
|
+
* @param el
|
|
7
|
+
* @param data
|
|
8
|
+
* @param attr
|
|
9
|
+
* @param filter
|
|
10
|
+
* @param xssSanitize Enable XSS protection
|
|
11
|
+
*/
|
|
12
|
+
export function setComponent(el, data, attr, filter, xssSanitize = true) {
|
|
13
|
+
el = el.cloneNode(true);
|
|
14
|
+
for (const dataKeyName in data) {
|
|
15
|
+
const datum = data[dataKeyName];
|
|
16
|
+
if (Array.isArray(datum)) {
|
|
17
|
+
continue;
|
|
18
|
+
}
|
|
19
|
+
const targetList = [
|
|
20
|
+
// 自身を含める
|
|
21
|
+
el,
|
|
22
|
+
// パフォーマンスのために雑なフィルタリングを行う
|
|
23
|
+
...el.querySelectorAll(`[data-${attr}*="${kebabCase(dataKeyName)}"]`),
|
|
24
|
+
]
|
|
25
|
+
// より正確なフィルタリングを行う
|
|
26
|
+
.filter((el) => hasField(el, attr, dataKeyName));
|
|
27
|
+
for (const targetEl of targetList) {
|
|
28
|
+
setValue(targetEl, dataKeyName, datum, attr, filter, xssSanitize);
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
for (const listRoot of el.querySelectorAll(`[data-${attr}-list]`)) {
|
|
32
|
+
const decendants = listRoot.querySelectorAll('*');
|
|
33
|
+
const definedFieldsOfDecendants = new Set([...decendants].flatMap((el) => definedFields(el, attr)));
|
|
34
|
+
const maxLength = maxLengthOf(data, [...definedFieldsOfDecendants]);
|
|
35
|
+
const listItem = listRoot.children[0]?.cloneNode(true);
|
|
36
|
+
if (!listItem) {
|
|
37
|
+
continue;
|
|
38
|
+
}
|
|
39
|
+
while (listRoot.firstChild) {
|
|
40
|
+
listRoot.firstChild.remove();
|
|
41
|
+
}
|
|
42
|
+
for (let i = 0; i < maxLength; i++) {
|
|
43
|
+
let item = listItem.cloneNode(true);
|
|
44
|
+
const itemData = flattenData(data, i);
|
|
45
|
+
let reverse = false;
|
|
46
|
+
switch (listRoot.localName) {
|
|
47
|
+
case 'picture': {
|
|
48
|
+
reverse = true;
|
|
49
|
+
if (i > 0) {
|
|
50
|
+
// 2つ目以降はsource要素に変換
|
|
51
|
+
let fields = getFields(item, attr);
|
|
52
|
+
item = replaceNode(item, 'source', attr, xssSanitize);
|
|
53
|
+
fields = replaceProp(fields, 'src', 'srcset');
|
|
54
|
+
fields = removeProp(fields, 'alt');
|
|
55
|
+
const fieldQuery = stringifyFields(fields);
|
|
56
|
+
item.setAttribute(`data-${attr}`, fieldQuery);
|
|
57
|
+
}
|
|
58
|
+
break;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
const newEl = setComponent(item, itemData, attr, filter, xssSanitize);
|
|
62
|
+
if (reverse) {
|
|
63
|
+
listRoot.prepend(newEl);
|
|
64
|
+
}
|
|
65
|
+
else {
|
|
66
|
+
listRoot.append(newEl);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
return el;
|
|
71
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { Filter, FrozenPattyData } from './types.js';
|
|
2
|
+
/**
|
|
3
|
+
*
|
|
4
|
+
* @param el
|
|
5
|
+
* @param data
|
|
6
|
+
* @param attr
|
|
7
|
+
* @param filter
|
|
8
|
+
* @param xssSanitize Enable XSS protection
|
|
9
|
+
*/
|
|
10
|
+
export declare function setComponent(el: Element, data: FrozenPattyData, attr: string, filter?: Filter, xssSanitize?: boolean): Element;
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import { setValue } from './set-value.js';
|
|
2
|
+
import { stringifyFields } from './stringify-fields.js';
|
|
3
|
+
import { definedFields, flattenData, getFields, hasField, kebabCase, maxLengthOf, removeProp, replaceNode, replaceProp, } from './utils.js';
|
|
4
|
+
/**
|
|
5
|
+
*
|
|
6
|
+
* @param el
|
|
7
|
+
* @param data
|
|
8
|
+
* @param attr
|
|
9
|
+
* @param filter
|
|
10
|
+
* @param xssSanitize Enable XSS protection
|
|
11
|
+
*/
|
|
12
|
+
export function setComponent(el, data, attr, filter, xssSanitize = true) {
|
|
13
|
+
el = el.cloneNode(true);
|
|
14
|
+
for (const dataKeyName in data) {
|
|
15
|
+
const datum = data[dataKeyName];
|
|
16
|
+
if (Array.isArray(datum)) {
|
|
17
|
+
continue;
|
|
18
|
+
}
|
|
19
|
+
const targetList = [
|
|
20
|
+
// Include self
|
|
21
|
+
el,
|
|
22
|
+
// Perform rough filtering for performance
|
|
23
|
+
...el.querySelectorAll(`[data-${attr}*="${kebabCase(dataKeyName)}"]`),
|
|
24
|
+
]
|
|
25
|
+
// Perform more accurate filtering
|
|
26
|
+
.filter((el) => hasField(el, attr, dataKeyName));
|
|
27
|
+
for (const targetEl of targetList) {
|
|
28
|
+
setValue(targetEl, dataKeyName, datum, attr, filter, xssSanitize);
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
for (const listRoot of el.querySelectorAll(`[data-${attr}-list]`)) {
|
|
32
|
+
const decendants = listRoot.querySelectorAll('*');
|
|
33
|
+
const definedFieldsOfDecendants = new Set([...decendants].flatMap((el) => definedFields(el, attr)));
|
|
34
|
+
const maxLength = maxLengthOf(data, [...definedFieldsOfDecendants]);
|
|
35
|
+
const listItem = listRoot.children[0]?.cloneNode(true);
|
|
36
|
+
if (!listItem) {
|
|
37
|
+
continue;
|
|
38
|
+
}
|
|
39
|
+
while (listRoot.firstChild) {
|
|
40
|
+
listRoot.firstChild.remove();
|
|
41
|
+
}
|
|
42
|
+
for (let i = 0; i < maxLength; i++) {
|
|
43
|
+
let item = listItem.cloneNode(true);
|
|
44
|
+
const itemData = flattenData(data, i);
|
|
45
|
+
let reverse = false;
|
|
46
|
+
switch (listRoot.localName) {
|
|
47
|
+
case 'picture': {
|
|
48
|
+
reverse = true;
|
|
49
|
+
let fields = getFields(item, attr);
|
|
50
|
+
if (i === 0) {
|
|
51
|
+
// Convert first item to img element
|
|
52
|
+
item = replaceNode(item, 'img', attr, xssSanitize);
|
|
53
|
+
fields = replaceProp(fields, 'srcset', 'src');
|
|
54
|
+
fields = removeProp(fields, 'sizes');
|
|
55
|
+
const fieldQuery = stringifyFields(fields);
|
|
56
|
+
item.setAttribute(`data-${attr}`, fieldQuery);
|
|
57
|
+
}
|
|
58
|
+
else {
|
|
59
|
+
// Convert subsequent items to source elements
|
|
60
|
+
item = replaceNode(item, 'source', attr, xssSanitize);
|
|
61
|
+
fields = replaceProp(fields, 'src', 'srcset');
|
|
62
|
+
fields = removeProp(fields, 'alt');
|
|
63
|
+
fields = removeProp(fields, 'loading');
|
|
64
|
+
const fieldQuery = stringifyFields(fields);
|
|
65
|
+
item.setAttribute(`data-${attr}`, fieldQuery);
|
|
66
|
+
}
|
|
67
|
+
break;
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
const newEl = setComponent(item, itemData, attr, filter, xssSanitize);
|
|
71
|
+
if (reverse) {
|
|
72
|
+
listRoot.prepend(newEl);
|
|
73
|
+
}
|
|
74
|
+
else {
|
|
75
|
+
listRoot.append(newEl);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
return el;
|
|
80
|
+
}
|
package/dist/set-value.d.ts
CHANGED
|
@@ -10,12 +10,14 @@ import type { Filter, PrimitiveDatum } from './types.js';
|
|
|
10
10
|
* @param datum A datum of value
|
|
11
11
|
* @param attr Data attribute name for specifying the node that FrozenPatty treats as a field
|
|
12
12
|
* @param filter
|
|
13
|
+
* @param xssSanitize Enable XSS protection
|
|
13
14
|
*/
|
|
14
|
-
export declare function setValue(el: Element, name: string, datum: PrimitiveDatum, attr?: string, filter?: Filter): void;
|
|
15
|
+
export declare function setValue(el: Element, name: string, datum: PrimitiveDatum, attr?: string, filter?: Filter, xssSanitize?: boolean): void;
|
|
15
16
|
/**
|
|
16
17
|
*
|
|
17
18
|
* @param el
|
|
18
19
|
* @param datum
|
|
19
20
|
* @param asHtml
|
|
21
|
+
* @param xssSanitize
|
|
20
22
|
*/
|
|
21
|
-
export declare function setContent(el: Element, datum: PrimitiveDatum, asHtml?: boolean): void;
|
|
23
|
+
export declare function setContent(el: Element, datum: PrimitiveDatum, asHtml?: boolean, xssSanitize?: boolean): void;
|