@massimo-cassandro/minimo 1.10.1 → 1.11.1
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/package.json +3 -1
- package/src/components/form-reset-submit.js +1 -1
- package/src/utilities/better-text.js +39 -30
- package/src/utilities/cookies.js +8 -0
- package/src/utilities/dom-builder/dom-builder.js +68 -30
- package/src/utilities/dom-builder/domBuilderBasicSetup.js +34 -35
- package/src/utilities/dom-builder/parseDomString.js +9 -9
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@massimo-cassandro/minimo",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.11.1",
|
|
4
4
|
"description": "Light css framework & utilities",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./index.js",
|
|
@@ -36,6 +36,7 @@
|
|
|
36
36
|
"upd@m": "npx upd@m",
|
|
37
37
|
"build-tokens": "node ./design-tokens/utilities/build-tokens.mjs --config ./design-tokens/tokens-config.mjs",
|
|
38
38
|
"check unresolved props": "node ./design-tokens/utilities/check-unresolved-custom-props.mjs --config ./design-tokens/tokens-config.mjs",
|
|
39
|
+
"test": "node --test \"test/**/*.test.mjs\"",
|
|
39
40
|
"checkJS": "npx tsc --noEmit -p jsconfig.json",
|
|
40
41
|
"checkJS ./src": "npx tsc --noEmit --allowJs --checkJs --target ES2023 --module ESNext --allowSyntheticDefaultImports --moduleResolution bundler declarations.d.ts src/**/*.js"
|
|
41
42
|
},
|
|
@@ -68,6 +69,7 @@
|
|
|
68
69
|
"devDependencies": {
|
|
69
70
|
"@massimo-cassandro/eslint-config": "^3.0.11",
|
|
70
71
|
"@massimo-cassandro/stylelint-config": "^2.3.11",
|
|
72
|
+
"jsdom": "^29.1.1",
|
|
71
73
|
"style-dictionary": "^5.5.0"
|
|
72
74
|
},
|
|
73
75
|
"dependencies": {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// import './form.css'; // included in the main CSS entry point
|
|
2
|
-
import { enableSubmitBtns } from '
|
|
2
|
+
import { enableSubmitBtns } from '../utilities/enable-submit-btns.js';
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
5
|
* Wires up submit validation and button-disabling behaviour on all forms.
|
|
@@ -1,5 +1,41 @@
|
|
|
1
1
|
// @ts-check
|
|
2
2
|
|
|
3
|
+
// Italian particles/articles (plus a few English and topographic terms) whose casing is
|
|
4
|
+
// enforced and after which spaces are turned into non-breaking spaces. Hoisted to module
|
|
5
|
+
// scope with the regexes/lookup built from it below, since none of this depends on the
|
|
6
|
+
// function's arguments and was previously rebuilt on every single betterText() call.
|
|
7
|
+
const PARTICELLE = [
|
|
8
|
+
'e', 'ed',
|
|
9
|
+
'di', 'a', 'da', 'in', 'per', 'con', 'su', 'per', 'tra', 'fra',
|
|
10
|
+
'il', 'lo', 'la', 'i', 'gli', 'le',
|
|
11
|
+
'un', 'uno', 'una',
|
|
12
|
+
'del', 'dello', 'della', 'dei', 'degli', 'delle',
|
|
13
|
+
'al', 'allo', 'alla', 'ai', 'agli', 'alle',
|
|
14
|
+
'dal', 'dallo', 'dalla', 'dai', 'dagli', 'dalle',
|
|
15
|
+
'nel', 'nello', 'nella', 'nei', 'negli', 'nelle',
|
|
16
|
+
'col', 'collo', 'colla', 'con', 'coi', 'cogli', 'colle',
|
|
17
|
+
'sul', 'sullo', 'sulla', 'sui', 'sugli', 'sulle',
|
|
18
|
+
|
|
19
|
+
// topographic terms
|
|
20
|
+
'C.so', 'Corso', 'Via', 'P.za', 'Piazza', 'L.go', 'Largo', 'V.le', 'Viale',
|
|
21
|
+
'De', 'San', 'c/o', 'Loc.',
|
|
22
|
+
|
|
23
|
+
// English
|
|
24
|
+
'the', 'a', 'an', 'at', 'out', 'of',
|
|
25
|
+
|
|
26
|
+
// words used in geonames
|
|
27
|
+
'isola', 'isole'
|
|
28
|
+
];
|
|
29
|
+
|
|
30
|
+
// matches a particle followed by one-or-more spaces, to be turned into non-breaking spaces
|
|
31
|
+
const NBSP_AFTER_PARTICLE_REGEX = new RegExp('\\b(' + PARTICELLE.join('|') + ')\\b +', 'gmi');
|
|
32
|
+
|
|
33
|
+
// matches any particle regardless of case, to enforce its exact casing in a single pass
|
|
34
|
+
const PARTICLE_CASING_REGEX = new RegExp('\\b(' + PARTICELLE.join('|') + ')\\b', 'gmi');
|
|
35
|
+
|
|
36
|
+
// lowercased particle → its exact casing, for the replacer used with PARTICLE_CASING_REGEX
|
|
37
|
+
const PARTICLE_CASING_MAP = new Map(PARTICELLE.map(term => [term.toLowerCase(), term]));
|
|
38
|
+
|
|
3
39
|
/**
|
|
4
40
|
* Improves a text string by fixing punctuation spacing, multiple spaces,
|
|
5
41
|
* typographic quotes, non-breaking spaces after Italian particles/articles,
|
|
@@ -11,29 +47,6 @@
|
|
|
11
47
|
*/
|
|
12
48
|
export function betterText(str, custom_words = []) {
|
|
13
49
|
|
|
14
|
-
const particelle = [
|
|
15
|
-
'e', 'ed',
|
|
16
|
-
'di', 'a', 'da', 'in', 'per', 'con', 'su', 'per', 'tra', 'fra',
|
|
17
|
-
'il', 'lo', 'la', 'i', 'gli', 'le',
|
|
18
|
-
'un', 'uno', 'una',
|
|
19
|
-
'del', 'dello', 'della', 'dei', 'degli', 'delle',
|
|
20
|
-
'al', 'allo', 'alla', 'ai', 'agli', 'alle',
|
|
21
|
-
'dal', 'dallo', 'dalla', 'dai', 'dagli', 'dalle',
|
|
22
|
-
'nel', 'nello', 'nella', 'nei', 'negli', 'nelle',
|
|
23
|
-
'col', 'collo', 'colla', 'con', 'coi', 'cogli', 'colle',
|
|
24
|
-
'sul', 'sullo', 'sulla', 'sui', 'sugli', 'sulle',
|
|
25
|
-
|
|
26
|
-
// topographic terms
|
|
27
|
-
'C.so', 'Corso', 'Via', 'P.za', 'Piazza', 'L.go', 'Largo', 'V.le', 'Viale',
|
|
28
|
-
'De', 'San', 'c/o', 'Loc.',
|
|
29
|
-
|
|
30
|
-
// English
|
|
31
|
-
'the', 'a', 'an', 'at', 'out', 'of',
|
|
32
|
-
|
|
33
|
-
// words used in geonames
|
|
34
|
-
'isola', 'isole'
|
|
35
|
-
];
|
|
36
|
-
|
|
37
50
|
if(str) {
|
|
38
51
|
str = str.trim();
|
|
39
52
|
|
|
@@ -57,16 +70,12 @@ export function betterText(str, custom_words = []) {
|
|
|
57
70
|
str = str.replace(/((^| )(l|un|d|all|dell|nell|sull)('|')) /gi, '$1' );
|
|
58
71
|
|
|
59
72
|
// replace regular spaces with non-breaking spaces after Italian particles/articles
|
|
60
|
-
|
|
61
|
-
str = str.replace(re, function (match) {
|
|
73
|
+
str = str.replace(NBSP_AFTER_PARTICLE_REGEX, function (match) {
|
|
62
74
|
return match.replace(/ +/g, '\u00A0');
|
|
63
75
|
});
|
|
64
76
|
|
|
65
|
-
// enforce exact casing of
|
|
66
|
-
|
|
67
|
-
const re = new RegExp('\\b(' + term + ')\\b', 'gmi');
|
|
68
|
-
str = str.replace(re, term);
|
|
69
|
-
});
|
|
77
|
+
// enforce exact casing of every particle in a single pass
|
|
78
|
+
str = str.replace(PARTICLE_CASING_REGEX, match => PARTICLE_CASING_MAP.get(match.toLowerCase()) ?? match);
|
|
70
79
|
|
|
71
80
|
if(custom_words.length) {
|
|
72
81
|
custom_words.forEach(item => {
|
package/src/utilities/cookies.js
CHANGED
|
@@ -19,6 +19,14 @@ export function getCookie(name) {
|
|
|
19
19
|
* @param {string} params.value
|
|
20
20
|
* @param {string|null} [params.path=null]
|
|
21
21
|
* @param {number|null} [params.expire=null] - Max-Age in seconds
|
|
22
|
+
* @example
|
|
23
|
+
* // Cookie scoped to the current page only, lasting 15 days
|
|
24
|
+
* setCookie({
|
|
25
|
+
* name: 'cookieName',
|
|
26
|
+
* value: 'cookieValue',
|
|
27
|
+
* path: window.location.pathname,
|
|
28
|
+
* expire: 15 * 24 * 60 * 60 // 15 days
|
|
29
|
+
* });
|
|
22
30
|
*/
|
|
23
31
|
export function setCookie({name, value, path = null, expire = null}) {
|
|
24
32
|
document.cookie = `${name}=${value}` +
|
|
@@ -13,6 +13,9 @@ import { parseDomString } from './parseDomString.js';
|
|
|
13
13
|
* @property {string | null} [id] - Unique element ID.
|
|
14
14
|
* @property {[string, *] | [string, *][] | Object<string, *>} [attrs] - Attributes: a `[name, value]` pair, array of pairs, or `{name: value}` object.
|
|
15
15
|
* @property {string | number | Function | Element | DomBuilderItem[] | null} [content] - Element content.
|
|
16
|
+
* A string or number is set as plain text (`textContent`) unless it contains `<`, in which case it is
|
|
17
|
+
* treated as markup: sanitized and inserted via the native Sanitizer API (`Element.setHTML`) where
|
|
18
|
+
* supported, falling back to raw `innerHTML` on browsers without it.
|
|
16
19
|
* @property {boolean} [condition=true] - When false, the element is skipped.
|
|
17
20
|
* @property {function(HTMLElement): void} [callback] - Callback invoked after the element is created.
|
|
18
21
|
* @property {DomBuilderItem[]} [children] - Configuration array for child elements.
|
|
@@ -35,7 +38,7 @@ import { parseDomString } from './parseDomString.js';
|
|
|
35
38
|
* className: 'xxx' | ['class1', 'class2'], // also `class`
|
|
36
39
|
* id: 'element-id',
|
|
37
40
|
* attrs: [attr_name, attr_value] | [[...], [...]] | {name: value},
|
|
38
|
-
* content: 'xxx' | 123 | domBuilder Array | function | Element,
|
|
41
|
+
* content: 'xxx' | 'xxx <strong>yyy</strong>' | 123 | domBuilder Array | function | Element, // see DomBuilderItem.content above
|
|
39
42
|
* condition: true | false,
|
|
40
43
|
* callback: el => ...,
|
|
41
44
|
* children: [...]
|
|
@@ -78,20 +81,30 @@ export function domBuilder(structureArray = [], parent, options = {}) {
|
|
|
78
81
|
...options
|
|
79
82
|
};
|
|
80
83
|
|
|
81
|
-
|
|
84
|
+
if(parent && options.emptyParent) {
|
|
85
|
+
parent.innerHTML = '';
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
// For the default 'append' insert mode, root-level siblings are accumulated into a
|
|
89
|
+
// DocumentFragment and attached to `parent` with a single appendChild at the end,
|
|
90
|
+
// instead of one appendChild per sibling. 'before'/'after' modes need `target` to stay
|
|
91
|
+
// the real, live parent node throughout the loop for anchor resolution (see afterAnchors
|
|
92
|
+
// below), so batching is skipped for those.
|
|
93
|
+
const useFragment = !!parent && options.insertMode === 'append';
|
|
94
|
+
|
|
95
|
+
/** @type {HTMLElement | DocumentFragment | null} */
|
|
96
|
+
let target = useFragment ? document.createDocumentFragment() : (parent ?? null);
|
|
97
|
+
|
|
98
|
+
/** @type {Map<HTMLElement | DocumentFragment, HTMLElement>} tracks, for `insertMode: 'after'`, the last inserted sibling per parent node */
|
|
82
99
|
const afterAnchors = new Map();
|
|
83
100
|
|
|
84
101
|
/** @type {HTMLElement | null} */
|
|
85
102
|
let mainElement = null;
|
|
86
103
|
/** @type {HTMLElement} */
|
|
87
104
|
let el;
|
|
88
|
-
/** @type {HTMLElement | null} */
|
|
105
|
+
/** @type {HTMLElement | DocumentFragment | null} */
|
|
89
106
|
let grand_parent = null;
|
|
90
107
|
|
|
91
|
-
if(parent && options.emptyParent) {
|
|
92
|
-
parent.innerHTML = '';
|
|
93
|
-
}
|
|
94
|
-
|
|
95
108
|
structureArray.forEach(inputItem => {
|
|
96
109
|
|
|
97
110
|
/** @type {DomBuilderItem | null} */
|
|
@@ -112,7 +125,7 @@ export function domBuilder(structureArray = [], parent, options = {}) {
|
|
|
112
125
|
// the last one receives the remaining object properties
|
|
113
126
|
if (Array.isArray(safeItem.tag)) {
|
|
114
127
|
|
|
115
|
-
grand_parent =
|
|
128
|
+
grand_parent = target;
|
|
116
129
|
const tags = /** @type {string[]} */ (safeItem.tag);
|
|
117
130
|
|
|
118
131
|
tags.forEach((tagItem, idx) => {
|
|
@@ -130,10 +143,10 @@ export function domBuilder(structureArray = [], parent, options = {}) {
|
|
|
130
143
|
);
|
|
131
144
|
|
|
132
145
|
if (!isLast) { // the last element is handled by the standard path below
|
|
133
|
-
if (
|
|
134
|
-
|
|
146
|
+
if (target) {
|
|
147
|
+
target.appendChild(el);
|
|
135
148
|
}
|
|
136
|
-
|
|
149
|
+
target = el;
|
|
137
150
|
}
|
|
138
151
|
|
|
139
152
|
});
|
|
@@ -148,23 +161,43 @@ export function domBuilder(structureArray = [], parent, options = {}) {
|
|
|
148
161
|
|
|
149
162
|
if (safeItem.content != null) {
|
|
150
163
|
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
164
|
+
if (Array.isArray(safeItem.content)) {
|
|
165
|
+
// build directly into `el` (instead of passing no parent) so that every item of
|
|
166
|
+
// the array is actually inserted, not just the first one returned as `mainElement`
|
|
167
|
+
domBuilder(safeItem.content, el);
|
|
155
168
|
|
|
156
|
-
} else
|
|
157
|
-
content = domBuilder(safeItem.content);
|
|
169
|
+
} else {
|
|
158
170
|
|
|
159
|
-
|
|
160
|
-
content =
|
|
161
|
-
|
|
171
|
+
/** @type {string | Element | null} */
|
|
172
|
+
let content = null;
|
|
173
|
+
if (typeof safeItem.content === 'function') {
|
|
174
|
+
content = safeItem.content();
|
|
175
|
+
|
|
176
|
+
} else if (safeItem.content != null) {
|
|
177
|
+
content = String(safeItem.content);
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
if (content instanceof Element) {
|
|
181
|
+
el.appendChild(content);
|
|
182
|
+
|
|
183
|
+
} else if (content != null) {
|
|
162
184
|
|
|
163
|
-
|
|
164
|
-
|
|
185
|
+
if (!content.includes('<')) {
|
|
186
|
+
// plain text: no HTML parsing needed, textContent is faster and safe by construction
|
|
187
|
+
el.textContent = content;
|
|
165
188
|
|
|
166
|
-
|
|
167
|
-
|
|
189
|
+
} else if (typeof el.setHTML === 'function') {
|
|
190
|
+
// markup: sanitize via the native Sanitizer API, stripping <script>, event handler
|
|
191
|
+
// attributes, javascript: URLs, etc. while still allowing harmless formatting tags
|
|
192
|
+
// (<strong>, <em>, <a>, ...)
|
|
193
|
+
el.setHTML(content);
|
|
194
|
+
|
|
195
|
+
} else {
|
|
196
|
+
// Sanitizer API not supported by this browser: fall back to the historical,
|
|
197
|
+
// unsanitized behavior
|
|
198
|
+
el.innerHTML = content;
|
|
199
|
+
}
|
|
200
|
+
}
|
|
168
201
|
}
|
|
169
202
|
}
|
|
170
203
|
|
|
@@ -181,17 +214,18 @@ export function domBuilder(structureArray = [], parent, options = {}) {
|
|
|
181
214
|
mainElement = el;
|
|
182
215
|
}
|
|
183
216
|
|
|
184
|
-
if (
|
|
217
|
+
if (target) {
|
|
185
218
|
if (options.insertMode === 'before') {
|
|
186
|
-
|
|
219
|
+
const anchor = /** @type {HTMLElement} */ (target);
|
|
220
|
+
anchor.parentNode?.insertBefore(el, anchor);
|
|
187
221
|
|
|
188
222
|
} else if (options.insertMode === 'after') {
|
|
189
|
-
const anchor = afterAnchors.get(
|
|
223
|
+
const anchor = afterAnchors.get(/** @type {HTMLElement} */ (target)) ?? /** @type {HTMLElement} */ (target);
|
|
190
224
|
anchor.parentNode?.insertBefore(el, anchor.nextSibling);
|
|
191
|
-
afterAnchors.set(
|
|
225
|
+
afterAnchors.set(/** @type {HTMLElement} */ (target), el);
|
|
192
226
|
|
|
193
227
|
} else {
|
|
194
|
-
|
|
228
|
+
target.appendChild(el);
|
|
195
229
|
}
|
|
196
230
|
}
|
|
197
231
|
|
|
@@ -201,11 +235,15 @@ export function domBuilder(structureArray = [], parent, options = {}) {
|
|
|
201
235
|
}
|
|
202
236
|
|
|
203
237
|
if (grand_parent) {
|
|
204
|
-
|
|
238
|
+
target = grand_parent;
|
|
205
239
|
}
|
|
206
240
|
}
|
|
207
241
|
|
|
208
242
|
});
|
|
209
243
|
|
|
244
|
+
if (useFragment && parent && target) {
|
|
245
|
+
parent.appendChild(target);
|
|
246
|
+
}
|
|
247
|
+
|
|
210
248
|
return mainElement;
|
|
211
249
|
}
|
|
@@ -1,6 +1,39 @@
|
|
|
1
1
|
|
|
2
2
|
/** @typedef {import('./dom-builder.js').DomBuilderItem} DomBuilderItem */
|
|
3
3
|
|
|
4
|
+
// boolean attributes are omitted when their value is explicitly false;
|
|
5
|
+
// declared once at module level (Set for O(1) lookup) since the list never changes between calls
|
|
6
|
+
const boolAttrs = new Set([
|
|
7
|
+
'allowfullscreen',
|
|
8
|
+
'async',
|
|
9
|
+
'autocomplete',
|
|
10
|
+
'autofocus',
|
|
11
|
+
'autoplay',
|
|
12
|
+
'border',
|
|
13
|
+
'checked',
|
|
14
|
+
'compact',
|
|
15
|
+
'contenteditable',
|
|
16
|
+
'controls',
|
|
17
|
+
'default',
|
|
18
|
+
'loop',
|
|
19
|
+
'defer',
|
|
20
|
+
'disabled',
|
|
21
|
+
'formnovalidate',
|
|
22
|
+
'hidden',
|
|
23
|
+
'inert',
|
|
24
|
+
'ismap',
|
|
25
|
+
'multiple',
|
|
26
|
+
'muted',
|
|
27
|
+
'novalidate',
|
|
28
|
+
'open',
|
|
29
|
+
'readonly',
|
|
30
|
+
'required',
|
|
31
|
+
'reversed',
|
|
32
|
+
'selected',
|
|
33
|
+
'spellcheck',
|
|
34
|
+
'translate'
|
|
35
|
+
]);
|
|
36
|
+
|
|
4
37
|
/**
|
|
5
38
|
* Applies basic domBuilder configuration to an element: classes, id, and attributes.
|
|
6
39
|
* @param {HTMLElement} element - The element to configure.
|
|
@@ -23,42 +56,8 @@ export function domBuilderBasicSetup (element, domBuilderItem) {
|
|
|
23
56
|
domBuilderItem.attrs = Object.entries(domBuilderItem.attrs);
|
|
24
57
|
}
|
|
25
58
|
|
|
26
|
-
// boolean attributes are omitted when their value is explicitly false
|
|
27
|
-
const boolAttrs = [
|
|
28
|
-
'allowfullscreen',
|
|
29
|
-
'async',
|
|
30
|
-
'autocomplete',
|
|
31
|
-
'autofocus',
|
|
32
|
-
'autoplay',
|
|
33
|
-
'border',
|
|
34
|
-
'checked',
|
|
35
|
-
'compact',
|
|
36
|
-
'contenteditable',
|
|
37
|
-
'controls',
|
|
38
|
-
'default',
|
|
39
|
-
'loop',
|
|
40
|
-
'defer',
|
|
41
|
-
'disabled',
|
|
42
|
-
'formnovalidate',
|
|
43
|
-
'hidden',
|
|
44
|
-
'inert',
|
|
45
|
-
'ismap',
|
|
46
|
-
'multiple',
|
|
47
|
-
'muted',
|
|
48
|
-
'novalidate',
|
|
49
|
-
'open',
|
|
50
|
-
'readonly',
|
|
51
|
-
'required',
|
|
52
|
-
'reversed',
|
|
53
|
-
'selected',
|
|
54
|
-
'spellcheck',
|
|
55
|
-
'translate'
|
|
56
|
-
];
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
59
|
(/** @type {[string, unknown][]} */ (domBuilderItem.attrs ?? [])).forEach(attr => {
|
|
61
|
-
if (attr[1] != null && !(boolAttrs.
|
|
60
|
+
if (attr[1] != null && !(boolAttrs.has(attr[0]) && attr[1] === false)) {
|
|
62
61
|
element.setAttribute(attr[0], String(attr[1]));
|
|
63
62
|
}
|
|
64
63
|
});
|
|
@@ -1,3 +1,11 @@
|
|
|
1
|
+
// compiled once at module load, since the pattern never changes between calls
|
|
2
|
+
const DOM_STRING_REGEX = new RegExp(
|
|
3
|
+
/^([a-zA-Z][a-zA-Z0-9-]*)?/.source + // tag (also accepts web component names)
|
|
4
|
+
/((?:[#.][a-zA-Z0-9_-]+)*)?/.source + // id and classes, in any order
|
|
5
|
+
/([([{].*?[)\]}])?/.source + // attrs
|
|
6
|
+
/(?: +(.*))?$/.source // content
|
|
7
|
+
);
|
|
8
|
+
|
|
1
9
|
/**
|
|
2
10
|
* Parses a string to extract tag name, ID, classes, and attributes.
|
|
3
11
|
*
|
|
@@ -24,17 +32,9 @@
|
|
|
24
32
|
* @param {string} domString - The domBuilder string to parse.
|
|
25
33
|
* @returns {{tag: string, id: string|null, className: string, attrs: Object<string, string|true>, content: string|null}|null}
|
|
26
34
|
*/
|
|
27
|
-
|
|
28
35
|
export function parseDomString(domString) {
|
|
29
36
|
|
|
30
|
-
const
|
|
31
|
-
/^([a-zA-Z][a-zA-Z0-9-]*)?/.source + // tag (also accepts web component names)
|
|
32
|
-
/((?:[#.][a-zA-Z0-9_-]+)*)?/.source + // id and classes, in any order
|
|
33
|
-
/([([{].*?[)\]}])?/.source + // attrs
|
|
34
|
-
/(?: +(.*))?$/.source // content
|
|
35
|
-
);
|
|
36
|
-
|
|
37
|
-
const matches = domString.match(regex);
|
|
37
|
+
const matches = domString.match(DOM_STRING_REGEX);
|
|
38
38
|
|
|
39
39
|
|
|
40
40
|
if (!matches) {
|