@burger-editor/frozen-patty 4.0.0-alpha.6 → 4.0.0-alpha.61
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 +0 -4
- 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.js +22 -1
- package/dist/utils.js +18 -10
- package/package.json +3 -3
- 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/README.md
CHANGED
|
@@ -218,7 +218,6 @@ data-field="フィールド名1:属性名1, フィールド名2:属性名2"
|
|
|
218
218
|
このHTMLから抽出する際に、以下の特別な考慮がされます:
|
|
219
219
|
|
|
220
220
|
1. 配列データの順序:
|
|
221
|
-
|
|
222
221
|
- HTML内の`picture`要素では一般的に`source`要素が先に配置され、`img`要素が最後に配置されます
|
|
223
222
|
- 抽出されるデータの配列では逆順になり、img要素のデータが先、source要素のデータが後
|
|
224
223
|
|
|
@@ -311,12 +310,10 @@ const data = {
|
|
|
311
310
|
適用時には以下の特別処理が行われます:
|
|
312
311
|
|
|
313
312
|
1. 要素の配置と変換:
|
|
314
|
-
|
|
315
313
|
- 配列の最初の要素(インデックス0)は`img`要素に変換され、DOMでは最後に配置されます
|
|
316
314
|
- 配列の2番目以降の要素は`source`要素に変換され、配列の逆順にDOMに挿入されます(配列の末尾から先頭方向に)
|
|
317
315
|
|
|
318
316
|
2. 属性の自動変換:
|
|
319
|
-
|
|
320
317
|
- `img`要素:`srcset`属性は`src`属性に変換され、`sizes`属性は削除されます
|
|
321
318
|
- `source`要素:`src`属性は`srcset`属性に変換され、`alt`属性と`loading`属性は削除されます
|
|
322
319
|
- `data-field`属性のシンタックスは、それぞれの要素に対応する属性名に再設定されます
|
|
@@ -480,7 +477,6 @@ fpUnsafe.merge(data).toHTML();
|
|
|
480
477
|
以下のセキュリティ対策が実装されています:
|
|
481
478
|
|
|
482
479
|
1. 危険な要素の削除:
|
|
483
|
-
|
|
484
480
|
- `script`, `style`, `template`, `object`, `embed`, `iframe`, `frame`, `frameset`, `applet`
|
|
485
481
|
|
|
486
482
|
2. 危険な属性の削除:
|
package/dist/get-values.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { kebabCase } from '@burger-editor/utils';
|
|
2
|
+
import { normalizeIndent } from './normalize-indent.js';
|
|
2
3
|
import { parseFields } from './parse-fields.js';
|
|
3
4
|
/**
|
|
4
5
|
* Get value from an element
|
|
@@ -30,7 +31,7 @@ export function getValues(el, convertType = false, attr = 'field', filter) {
|
|
|
30
31
|
break;
|
|
31
32
|
}
|
|
32
33
|
case 'html': {
|
|
33
|
-
value = el.innerHTML
|
|
34
|
+
value = normalizeIndent(el.innerHTML);
|
|
34
35
|
break;
|
|
35
36
|
}
|
|
36
37
|
case 'style': {
|
|
@@ -67,7 +68,7 @@ export function getValues(el, convertType = false, attr = 'field', filter) {
|
|
|
67
68
|
value = convertType ? convert(val) : val;
|
|
68
69
|
}
|
|
69
70
|
else {
|
|
70
|
-
value = el.innerHTML
|
|
71
|
+
value = normalizeIndent(el.innerHTML);
|
|
71
72
|
}
|
|
72
73
|
}
|
|
73
74
|
if (filter) {
|
|
@@ -127,7 +128,8 @@ function getAttribute(el, keyAttr, attr, typeConvert) {
|
|
|
127
128
|
// For shorthand notation, get value from data-field-* attribute
|
|
128
129
|
const dataAttr = ['data', attr, kebabCase(keyAttr)].join('-');
|
|
129
130
|
if (el.hasAttribute(dataAttr)) {
|
|
130
|
-
|
|
131
|
+
const value = el.getAttribute(dataAttr);
|
|
132
|
+
return typeConvert ? convert(value) : value;
|
|
131
133
|
}
|
|
132
134
|
value = el.getAttribute(keyAttr) ?? null;
|
|
133
135
|
if ((value === '' || value == null) &&
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Normalizes the indentation of a multi-line HTML string by detecting the indentation
|
|
3
|
+
* of the first line and removing the same amount of leading whitespace (spaces or tabs)
|
|
4
|
+
* from all lines.
|
|
5
|
+
*
|
|
6
|
+
* - The function first removes any leading empty lines (lines containing only line breaks).
|
|
7
|
+
* - It then detects the leading whitespace (indent) from the first line.
|
|
8
|
+
* - For each line, if that indent is present at the start of the line, it is removed.
|
|
9
|
+
* - After normalization, any trailing empty lines (containing only whitespace or line breaks)
|
|
10
|
+
* are also removed.
|
|
11
|
+
* - Supports both LF (\n) and CRLF (\r\n) line endings.
|
|
12
|
+
*
|
|
13
|
+
* This is useful when copying and pasting or serializing innerHTML that includes
|
|
14
|
+
* indentation due to template formatting, so the returned HTML has normalized,
|
|
15
|
+
* consistent indentation and no unwanted leading/trailing blank lines.
|
|
16
|
+
* @param html - The multi-line HTML string to normalize.
|
|
17
|
+
* @returns The HTML string with normalized indentation and no leading/trailing empty lines.
|
|
18
|
+
*/
|
|
19
|
+
export declare function normalizeIndent(html: string): string;
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Normalizes the indentation of a multi-line HTML string by detecting the indentation
|
|
3
|
+
* of the first line and removing the same amount of leading whitespace (spaces or tabs)
|
|
4
|
+
* from all lines.
|
|
5
|
+
*
|
|
6
|
+
* - The function first removes any leading empty lines (lines containing only line breaks).
|
|
7
|
+
* - It then detects the leading whitespace (indent) from the first line.
|
|
8
|
+
* - For each line, if that indent is present at the start of the line, it is removed.
|
|
9
|
+
* - After normalization, any trailing empty lines (containing only whitespace or line breaks)
|
|
10
|
+
* are also removed.
|
|
11
|
+
* - Supports both LF (\n) and CRLF (\r\n) line endings.
|
|
12
|
+
*
|
|
13
|
+
* This is useful when copying and pasting or serializing innerHTML that includes
|
|
14
|
+
* indentation due to template formatting, so the returned HTML has normalized,
|
|
15
|
+
* consistent indentation and no unwanted leading/trailing blank lines.
|
|
16
|
+
* @param html - The multi-line HTML string to normalize.
|
|
17
|
+
* @returns The HTML string with normalized indentation and no leading/trailing empty lines.
|
|
18
|
+
*/
|
|
19
|
+
export function normalizeIndent(html) {
|
|
20
|
+
// Remove leading line breaks
|
|
21
|
+
const trimmedHtml = html.replace(/^(?:\r?\n)+/, '');
|
|
22
|
+
const lines = trimmedHtml.split(/(?<=\r\n|\n|$)/);
|
|
23
|
+
const firstLineIndent = lines[0]?.match(/^\s+/)?.[0] ?? '';
|
|
24
|
+
if (firstLineIndent.length === 0) {
|
|
25
|
+
return trimmedHtml.trimEnd();
|
|
26
|
+
}
|
|
27
|
+
const normalizedLines = lines.map((line) => line.replace(firstLineIndent, ''));
|
|
28
|
+
return normalizedLines.join('').trimEnd();
|
|
29
|
+
}
|
package/dist/set-component.js
CHANGED
|
@@ -40,6 +40,8 @@ export function setComponent(el, data, attr, filter, xssSanitize = true) {
|
|
|
40
40
|
while (listRoot.firstChild) {
|
|
41
41
|
listRoot.firstChild.remove();
|
|
42
42
|
}
|
|
43
|
+
// Track used paths for picture elements to avoid duplicates
|
|
44
|
+
const usedPaths = new Set();
|
|
43
45
|
for (let i = 0; i < maxLength; i++) {
|
|
44
46
|
let item = listItem.cloneNode(true);
|
|
45
47
|
const itemData = flattenData(data, i);
|
|
@@ -47,6 +49,14 @@ export function setComponent(el, data, attr, filter, xssSanitize = true) {
|
|
|
47
49
|
switch (listRoot.localName) {
|
|
48
50
|
case 'picture': {
|
|
49
51
|
reverse = true;
|
|
52
|
+
// Check for duplicate paths and skip if found
|
|
53
|
+
const pathValue = itemData.path;
|
|
54
|
+
if (pathValue != null && usedPaths.has(String(pathValue))) {
|
|
55
|
+
continue;
|
|
56
|
+
}
|
|
57
|
+
if (pathValue != null) {
|
|
58
|
+
usedPaths.add(String(pathValue));
|
|
59
|
+
}
|
|
50
60
|
let fields = getFields(item, attr);
|
|
51
61
|
if (i === 0) {
|
|
52
62
|
// Convert first item to img element
|
package/dist/set-value.js
CHANGED
|
@@ -121,6 +121,11 @@ function set(el, prefix, name, datum, xssSanitize = true) {
|
|
|
121
121
|
el.removeAttribute(name);
|
|
122
122
|
return;
|
|
123
123
|
}
|
|
124
|
+
// Custom elements
|
|
125
|
+
if (el.localName.includes('-')) {
|
|
126
|
+
el.setAttribute(name, `${datum}`);
|
|
127
|
+
return;
|
|
128
|
+
}
|
|
124
129
|
if (!name.startsWith('data-') && !propInElement(el, name)) {
|
|
125
130
|
const dataAttr = `data-${prefix}-${kebabCase(name)}`;
|
|
126
131
|
if (el.hasAttribute(dataAttr)) {
|
|
@@ -702,11 +707,27 @@ export function setContent(el, datum, asHtml = true, xssSanitize = true) {
|
|
|
702
707
|
el instanceof HTMLOutputElement) {
|
|
703
708
|
if (el instanceof HTMLInputElement &&
|
|
704
709
|
(el.type === 'radio' || el.type === 'checkbox')) {
|
|
710
|
+
// Boolean values are always set directly regardless of value attribute
|
|
705
711
|
if (typeof datum === 'boolean') {
|
|
706
712
|
el.checked = datum;
|
|
707
713
|
return;
|
|
708
714
|
}
|
|
709
|
-
|
|
715
|
+
// Checkbox/radio with value attribute: traditional value comparison
|
|
716
|
+
if (el.hasAttribute('value')) {
|
|
717
|
+
el.checked = el.value === `${datum}`;
|
|
718
|
+
return;
|
|
719
|
+
}
|
|
720
|
+
// Checkbox without value attribute: switch-like behavior (string boolean processing)
|
|
721
|
+
if (datum === 'true') {
|
|
722
|
+
el.checked = true;
|
|
723
|
+
return;
|
|
724
|
+
}
|
|
725
|
+
if (datum === 'false') {
|
|
726
|
+
el.checked = false;
|
|
727
|
+
return;
|
|
728
|
+
}
|
|
729
|
+
// Other values default to false
|
|
730
|
+
el.checked = false;
|
|
710
731
|
return;
|
|
711
732
|
}
|
|
712
733
|
el.value = `${datum}`;
|
package/dist/utils.js
CHANGED
|
@@ -96,22 +96,30 @@ export function sanitizeHtml(html) {
|
|
|
96
96
|
sanitized = sanitized.replace(regex, '$1');
|
|
97
97
|
}
|
|
98
98
|
// Parse HTML safely using DOM parser for remaining sanitization
|
|
99
|
-
const
|
|
100
|
-
const body = doc.body;
|
|
99
|
+
const fragment = new Range().createContextualFragment(sanitized);
|
|
101
100
|
// Remove event handler attributes from all elements
|
|
102
|
-
sanitizeElementAndChildren(
|
|
103
|
-
|
|
101
|
+
sanitizeElementAndChildren(fragment);
|
|
102
|
+
const newHtml = [...fragment.childNodes]
|
|
103
|
+
.map((node) => {
|
|
104
|
+
if (node instanceof Element) {
|
|
105
|
+
return node.outerHTML;
|
|
106
|
+
}
|
|
107
|
+
return node.textContent ?? node.nodeValue ?? '';
|
|
108
|
+
})
|
|
109
|
+
.join('');
|
|
110
|
+
return newHtml;
|
|
104
111
|
}
|
|
105
112
|
/**
|
|
106
113
|
* Sanitizes an element and all its child elements
|
|
107
|
-
* @param
|
|
114
|
+
* @param node Target element
|
|
108
115
|
*/
|
|
109
|
-
function sanitizeElementAndChildren(
|
|
110
|
-
|
|
111
|
-
|
|
116
|
+
function sanitizeElementAndChildren(node) {
|
|
117
|
+
if (node instanceof Element) {
|
|
118
|
+
// Remove dangerous attributes from current element
|
|
119
|
+
sanitizeAttributes(node);
|
|
120
|
+
}
|
|
112
121
|
// Process child elements recursively
|
|
113
|
-
const
|
|
114
|
-
for (const child of children) {
|
|
122
|
+
for (const child of node.childNodes) {
|
|
115
123
|
sanitizeElementAndChildren(child);
|
|
116
124
|
}
|
|
117
125
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@burger-editor/frozen-patty",
|
|
3
|
-
"version": "4.0.0-alpha.
|
|
3
|
+
"version": "4.0.0-alpha.61",
|
|
4
4
|
"description": "Pure HTML to JSON converter that does not use a template engine.",
|
|
5
5
|
"author": "D-ZERO",
|
|
6
6
|
"license": "(MIT OR Apache-2.0)",
|
|
@@ -41,7 +41,7 @@
|
|
|
41
41
|
"clean": "tsc --build --clean"
|
|
42
42
|
},
|
|
43
43
|
"dependencies": {
|
|
44
|
-
"@burger-editor/utils": "4.0.0-alpha.
|
|
44
|
+
"@burger-editor/utils": "4.0.0-alpha.61"
|
|
45
45
|
},
|
|
46
|
-
"gitHead": "
|
|
46
|
+
"gitHead": "7058a1016e98b8d33b992fb70d0e8cb946e4450e"
|
|
47
47
|
}
|
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
import { camelCase } from './utils.js';
|
|
2
|
-
/**
|
|
3
|
-
*
|
|
4
|
-
* @param field
|
|
5
|
-
*/
|
|
6
|
-
export function fieldNameParser(field) {
|
|
7
|
-
field = field.trim().toLowerCase();
|
|
8
|
-
if (!field) {
|
|
9
|
-
throw new Error('Field name is empty.');
|
|
10
|
-
}
|
|
11
|
-
let [fieldName, propName] = field.split(':');
|
|
12
|
-
propName = propName?.trim() || '';
|
|
13
|
-
fieldName = fieldName?.trim() || propName || field;
|
|
14
|
-
return {
|
|
15
|
-
fieldName: camelCase(fieldName),
|
|
16
|
-
propName,
|
|
17
|
-
};
|
|
18
|
-
}
|
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
import type { FieldDefinition } from './types.js';
|
|
2
|
-
/**
|
|
3
|
-
* フィールド名と属性名からフィールド定義文字列を生成します
|
|
4
|
-
* @param fieldName フィールド名
|
|
5
|
-
* @param propName 属性名(オプション)
|
|
6
|
-
* @returns フィールド定義文字列
|
|
7
|
-
*/
|
|
8
|
-
export declare function fieldNameStringify(fieldName: string, propName?: string): string;
|
|
9
|
-
/**
|
|
10
|
-
* 複数のフィールド定義をカンマ区切りで結合します
|
|
11
|
-
* @param fields フィールド定義の配列
|
|
12
|
-
* @returns カンマ区切りのフィールド定義文字列
|
|
13
|
-
*/
|
|
14
|
-
export declare function fieldsStringify(fields: readonly FieldDefinition[]): string;
|
|
@@ -1,26 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* フィールド名と属性名からフィールド定義文字列を生成します
|
|
3
|
-
* @param fieldName フィールド名
|
|
4
|
-
* @param propName 属性名(オプション)
|
|
5
|
-
* @returns フィールド定義文字列
|
|
6
|
-
*/
|
|
7
|
-
export function fieldNameStringify(fieldName, propName) {
|
|
8
|
-
if (!propName) {
|
|
9
|
-
return fieldName;
|
|
10
|
-
}
|
|
11
|
-
// fieldNameとpropNameが同じ場合は省略記法を使用
|
|
12
|
-
if (fieldName === propName) {
|
|
13
|
-
return `:${propName}`;
|
|
14
|
-
}
|
|
15
|
-
return `${fieldName}:${propName}`;
|
|
16
|
-
}
|
|
17
|
-
/**
|
|
18
|
-
* 複数のフィールド定義をカンマ区切りで結合します
|
|
19
|
-
* @param fields フィールド定義の配列
|
|
20
|
-
* @returns カンマ区切りのフィールド定義文字列
|
|
21
|
-
*/
|
|
22
|
-
export function fieldsStringify(fields) {
|
|
23
|
-
return fields
|
|
24
|
-
.map((field) => fieldNameStringify(field.fieldName, field.propName))
|
|
25
|
-
.join(', ');
|
|
26
|
-
}
|
package/dist/get.d.ts
DELETED
package/dist/get.js
DELETED
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
import { getValues } from './get-values.js';
|
|
2
|
-
import { arrayToHash } from './utils.js';
|
|
3
|
-
/**
|
|
4
|
-
*
|
|
5
|
-
* @param el
|
|
6
|
-
* @param attr
|
|
7
|
-
* @param typeConvert
|
|
8
|
-
* @param filter
|
|
9
|
-
*/
|
|
10
|
-
export default function (el, attr, typeConvert, filter) {
|
|
11
|
-
// eslint-disable-next-line unicorn/prefer-spread
|
|
12
|
-
const filedElements = Array.from(el.querySelectorAll(`[data-${attr}]`));
|
|
13
|
-
let values = [];
|
|
14
|
-
for (const _el of filedElements) {
|
|
15
|
-
values = [...values, ...getValues(_el, typeConvert, attr, filter)];
|
|
16
|
-
}
|
|
17
|
-
const result = arrayToHash(values);
|
|
18
|
-
return result;
|
|
19
|
-
}
|
|
@@ -1,31 +0,0 @@
|
|
|
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
|
-
}
|
package/dist/set-comonent.d.ts
DELETED
|
@@ -1,10 +0,0 @@
|
|
|
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;
|
package/dist/set-comonent.js
DELETED
|
@@ -1,71 +0,0 @@
|
|
|
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
|
-
}
|
package/dist/set.d.ts
DELETED
package/dist/set.js
DELETED
|
@@ -1,60 +0,0 @@
|
|
|
1
|
-
import { setValue } from './set-value.js';
|
|
2
|
-
import { kebabCase } from './utils.js';
|
|
3
|
-
/**
|
|
4
|
-
*
|
|
5
|
-
* @param el
|
|
6
|
-
* @param data
|
|
7
|
-
* @param attr
|
|
8
|
-
* @param filter
|
|
9
|
-
*/
|
|
10
|
-
export default function (el, data, attr, filter) {
|
|
11
|
-
el = el.cloneNode(true);
|
|
12
|
-
for (const dataKeyName in data) {
|
|
13
|
-
if (dataKeyName in data) {
|
|
14
|
-
const datum = data[dataKeyName];
|
|
15
|
-
const selector = `[data-${attr}*="${kebabCase(dataKeyName)}"]`;
|
|
16
|
-
// eslint-disable-next-line unicorn/prefer-spread
|
|
17
|
-
const targetList = Array.from(el.querySelectorAll(selector));
|
|
18
|
-
if (Array.isArray(datum)) {
|
|
19
|
-
const targetEl = targetList[0];
|
|
20
|
-
if (!targetEl) {
|
|
21
|
-
continue;
|
|
22
|
-
}
|
|
23
|
-
const listRoot = targetEl.closest(`[data-${attr}-list]`);
|
|
24
|
-
if (!listRoot || (listRoot && listRoot.children.length === 0)) {
|
|
25
|
-
continue;
|
|
26
|
-
}
|
|
27
|
-
const listItem = listRoot.children[0]?.cloneNode(true);
|
|
28
|
-
while (listItem && datum.length > listRoot.children.length) {
|
|
29
|
-
listRoot.append(listItem.cloneNode(true));
|
|
30
|
-
}
|
|
31
|
-
// eslint-disable-next-line unicorn/prefer-spread
|
|
32
|
-
const newChildren = Array.from(listRoot.querySelectorAll(selector));
|
|
33
|
-
// eslint-disable-next-line unicorn/prefer-spread
|
|
34
|
-
const oldChildList = Array.from(listRoot.children);
|
|
35
|
-
let deleteNodeList = [];
|
|
36
|
-
for (const [i, child] of [...newChildren].entries()) {
|
|
37
|
-
if (datum[i] == null) {
|
|
38
|
-
setValue(child, dataKeyName, '', attr, filter);
|
|
39
|
-
const oldChild = oldChildList[i];
|
|
40
|
-
if (oldChild) {
|
|
41
|
-
deleteNodeList.push(oldChild);
|
|
42
|
-
}
|
|
43
|
-
}
|
|
44
|
-
else {
|
|
45
|
-
setValue(child, dataKeyName, datum[i], attr, filter);
|
|
46
|
-
deleteNodeList = [];
|
|
47
|
-
}
|
|
48
|
-
}
|
|
49
|
-
for (const node of deleteNodeList)
|
|
50
|
-
node.remove();
|
|
51
|
-
}
|
|
52
|
-
else {
|
|
53
|
-
for (const [, targetEl] of [...targetList].entries()) {
|
|
54
|
-
setValue(targetEl, dataKeyName, datum, attr, filter);
|
|
55
|
-
}
|
|
56
|
-
}
|
|
57
|
-
}
|
|
58
|
-
}
|
|
59
|
-
return el;
|
|
60
|
-
}
|