@oscarpalmer/toretto 0.41.0 → 0.42.0
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/dist/attribute/{get.d.mts → get.attribute.d.mts} +3 -2
- package/dist/attribute/{get.mjs → get.attribute.mjs} +4 -3
- package/dist/attribute/index.d.mts +3 -16
- package/dist/attribute/index.mjs +4 -7
- package/dist/attribute/{set.d.mts → set.attribute.d.mts} +4 -4
- package/dist/attribute/{set.mjs → set.attribute.mjs} +2 -2
- package/dist/create.d.mts +25 -0
- package/dist/create.mjs +17 -0
- package/dist/data.mjs +7 -7
- package/dist/event/delegation.mjs +8 -1
- package/dist/html/index.d.mts +23 -26
- package/dist/html/index.mjs +85 -18
- package/dist/html/sanitize.mjs +6 -5
- package/dist/index.d.mts +113 -52
- package/dist/index.mjs +510 -361
- package/dist/internal/attribute.d.mts +4 -3
- package/dist/internal/attribute.mjs +13 -23
- package/dist/internal/element-value.d.mts +2 -2
- package/dist/internal/element-value.mjs +4 -2
- package/dist/internal/get-value.mjs +1 -1
- package/dist/internal/property.d.mts +4 -0
- package/dist/internal/property.mjs +21 -0
- package/dist/property/get.property.d.mts +20 -0
- package/dist/property/get.property.mjs +35 -0
- package/dist/property/index.d.mts +3 -0
- package/dist/property/index.mjs +3 -0
- package/dist/property/set.property.d.mts +32 -0
- package/dist/property/set.property.mjs +34 -0
- package/dist/style.d.mts +12 -7
- package/dist/style.mjs +14 -18
- package/package.json +12 -5
- package/src/attribute/{get.ts → get.attribute.ts} +14 -3
- package/src/attribute/index.ts +10 -22
- package/src/attribute/{set.ts → set.attribute.ts} +9 -5
- package/src/create.ts +81 -0
- package/src/data.ts +16 -8
- package/src/event/delegation.ts +24 -3
- package/src/event/index.ts +9 -3
- package/src/find/index.ts +11 -3
- package/src/find/relative.ts +4 -0
- package/src/focusable.ts +10 -2
- package/src/html/index.ts +166 -58
- package/src/html/sanitize.ts +14 -11
- package/src/index.ts +2 -1
- package/src/internal/attribute.ts +23 -42
- package/src/internal/element-value.ts +11 -4
- package/src/internal/get-value.ts +8 -0
- package/src/internal/is.ts +4 -0
- package/src/internal/property.ts +42 -0
- package/src/is.ts +10 -2
- package/src/property/get.property.ts +73 -0
- package/src/property/index.ts +2 -0
- package/src/property/set.property.ts +102 -0
- package/src/style.ts +52 -27
- package/src/touch.ts +14 -2
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
//#region src/attribute/get.d.ts
|
|
1
|
+
//#region src/attribute/get.attribute.d.ts
|
|
2
|
+
type DataPrefixedName = `data-${string}`;
|
|
2
3
|
/**
|
|
3
4
|
* Get the value of a specific attribute from an element
|
|
4
5
|
* @param element Element to get attribute from
|
|
@@ -6,7 +7,7 @@
|
|
|
6
7
|
* @param parse Parse value? _(defaults to `true`)_
|
|
7
8
|
* @returns Attribute value _(or `undefined`)_
|
|
8
9
|
*/
|
|
9
|
-
declare function getAttribute(element: Element, name:
|
|
10
|
+
declare function getAttribute(element: Element, name: DataPrefixedName, parse?: boolean): unknown;
|
|
10
11
|
/**
|
|
11
12
|
* Get the value of a specific attribute from an element
|
|
12
13
|
* @param element Element to get attribute from
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import { isHTMLOrSVGElement } from "../internal/is.mjs";
|
|
2
2
|
import { getAttributeValue } from "../internal/get-value.mjs";
|
|
3
|
-
|
|
3
|
+
import { kebabCase } from "@oscarpalmer/atoms/string/case";
|
|
4
|
+
//#region src/attribute/get.attribute.ts
|
|
4
5
|
function getAttribute(element, name, parseValues) {
|
|
5
|
-
if (isHTMLOrSVGElement(element) && typeof name === "string") return getAttributeValue(element, name, parseValues !== false);
|
|
6
|
+
if (isHTMLOrSVGElement(element) && typeof name === "string") return getAttributeValue(element, kebabCase(name), parseValues !== false);
|
|
6
7
|
}
|
|
7
8
|
/**
|
|
8
9
|
* Get specific attributes from an element
|
|
@@ -18,7 +19,7 @@ function getAttributes(element, names, parseData) {
|
|
|
18
19
|
const { length } = names;
|
|
19
20
|
for (let index = 0; index < length; index += 1) {
|
|
20
21
|
const name = names[index];
|
|
21
|
-
if (typeof name === "string") attributes[name] = getAttributeValue(element, name, shouldParse);
|
|
22
|
+
if (typeof name === "string") attributes[name] = getAttributeValue(element, kebabCase(name), shouldParse);
|
|
22
23
|
}
|
|
23
24
|
return attributes;
|
|
24
25
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { getAttribute, getAttributes } from "./get.mjs";
|
|
1
|
+
import { getAttribute, getAttributes } from "./get.attribute.mjs";
|
|
2
2
|
import { Attribute } from "../models.mjs";
|
|
3
3
|
import { booleanAttributes } from "../internal/attribute.mjs";
|
|
4
|
-
import { setAttribute, setAttributes } from "./set.mjs";
|
|
4
|
+
import { setAttribute, setAttributes } from "./set.attribute.mjs";
|
|
5
5
|
|
|
6
6
|
//#region src/attribute/index.d.ts
|
|
7
7
|
/**
|
|
@@ -29,19 +29,6 @@ declare function isBooleanAttribute(attribute: Attr | Attribute): boolean;
|
|
|
29
29
|
* @returns `true` if attribute is a boolean attribute
|
|
30
30
|
*/
|
|
31
31
|
declare function isBooleanAttribute(name: string): boolean;
|
|
32
|
-
/**
|
|
33
|
-
* Is the attribute empty and not a boolean attribute?
|
|
34
|
-
* @param attribute Attribute to check
|
|
35
|
-
* @returns `true` if attribute is empty and not a boolean attribute
|
|
36
|
-
*/
|
|
37
|
-
declare function isEmptyNonBooleanAttribute(attribute: Attr | Attribute): boolean;
|
|
38
|
-
/**
|
|
39
|
-
* Is the attribute empty and not a boolean attribute?
|
|
40
|
-
* @param name Attribute name
|
|
41
|
-
* @param value Attribute value
|
|
42
|
-
* @returns `true` if attribute is empty and not a boolean attribute
|
|
43
|
-
*/
|
|
44
|
-
declare function isEmptyNonBooleanAttribute(name: string, value: string): boolean;
|
|
45
32
|
/**
|
|
46
33
|
* Is the attribute an invalid boolean attribute?
|
|
47
34
|
*
|
|
@@ -60,4 +47,4 @@ declare function isInvalidBooleanAttribute(attribute: Attr | Attribute): boolean
|
|
|
60
47
|
*/
|
|
61
48
|
declare function isInvalidBooleanAttribute(name: string, value: string): boolean;
|
|
62
49
|
//#endregion
|
|
63
|
-
export { booleanAttributes, getAttribute, getAttributes, isBadAttribute, isBooleanAttribute,
|
|
50
|
+
export { booleanAttributes, getAttribute, getAttributes, isBadAttribute, isBooleanAttribute, isInvalidBooleanAttribute, setAttribute, setAttributes };
|
package/dist/attribute/index.mjs
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { _isBadAttribute, _isBooleanAttribute,
|
|
2
|
-
import { getAttribute, getAttributes } from "./get.mjs";
|
|
3
|
-
import { setAttribute, setAttributes } from "./set.mjs";
|
|
1
|
+
import { _isBadAttribute, _isBooleanAttribute, _isInvalidBooleanAttribute, booleanAttributes } from "../internal/attribute.mjs";
|
|
2
|
+
import { getAttribute, getAttributes } from "./get.attribute.mjs";
|
|
3
|
+
import { setAttribute, setAttributes } from "./set.attribute.mjs";
|
|
4
4
|
//#region src/attribute/index.ts
|
|
5
5
|
function isBadAttribute(first, second) {
|
|
6
6
|
return _isBadAttribute(first, second, true);
|
|
@@ -8,11 +8,8 @@ function isBadAttribute(first, second) {
|
|
|
8
8
|
function isBooleanAttribute(first) {
|
|
9
9
|
return _isBooleanAttribute(first, true);
|
|
10
10
|
}
|
|
11
|
-
function isEmptyNonBooleanAttribute(first, second) {
|
|
12
|
-
return _isEmptyNonBooleanAttribute(first, second, true);
|
|
13
|
-
}
|
|
14
11
|
function isInvalidBooleanAttribute(first, second) {
|
|
15
12
|
return _isInvalidBooleanAttribute(first, second, true);
|
|
16
13
|
}
|
|
17
14
|
//#endregion
|
|
18
|
-
export { booleanAttributes, getAttribute, getAttributes, isBadAttribute, isBooleanAttribute,
|
|
15
|
+
export { booleanAttributes, getAttribute, getAttributes, isBadAttribute, isBooleanAttribute, isInvalidBooleanAttribute, setAttribute, setAttributes };
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { Attribute } from "../models.mjs";
|
|
2
2
|
|
|
3
|
-
//#region src/attribute/set.d.ts
|
|
4
|
-
type
|
|
3
|
+
//#region src/attribute/set.attribute.d.ts
|
|
4
|
+
type DispatchedAttributeName = 'checked' | 'open' | 'value';
|
|
5
5
|
/**
|
|
6
6
|
* Set an attribute on an element
|
|
7
7
|
*
|
|
@@ -11,7 +11,7 @@ type DispatchedAttribute = 'checked' | 'open' | 'value';
|
|
|
11
11
|
* @param value Attribute value
|
|
12
12
|
* @param dispatch Dispatch event for attribute? _(defaults to `true`)_
|
|
13
13
|
*/
|
|
14
|
-
declare function setAttribute
|
|
14
|
+
declare function setAttribute(element: Element, name: DispatchedAttributeName, value?: unknown, dispatch?: boolean): void;
|
|
15
15
|
/**
|
|
16
16
|
* Set an attribute on an element
|
|
17
17
|
*
|
|
@@ -49,4 +49,4 @@ declare function setAttributes(element: Element, attributes: Array<Attr | Attrib
|
|
|
49
49
|
*/
|
|
50
50
|
declare function setAttributes(element: Element, attributes: Record<string, unknown>, dispatch?: boolean): void;
|
|
51
51
|
//#endregion
|
|
52
|
-
export { setAttribute, setAttributes };
|
|
52
|
+
export { DispatchedAttributeName, setAttribute, setAttributes };
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { updateAttribute } from "../internal/attribute.mjs";
|
|
2
1
|
import { setElementValue, setElementValues } from "../internal/element-value.mjs";
|
|
3
|
-
|
|
2
|
+
import { updateAttribute } from "../internal/attribute.mjs";
|
|
3
|
+
//#region src/attribute/set.attribute.ts
|
|
4
4
|
function setAttribute(element, first, second, third) {
|
|
5
5
|
setElementValue(element, first, second, third, updateAttribute);
|
|
6
6
|
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { Primitive } from "@oscarpalmer/atoms/models";
|
|
2
|
+
|
|
3
|
+
//#region src/create.d.ts
|
|
4
|
+
type Properties<Target extends Element> = { [Property in keyof Target]?: Target[Property] extends Primitive ? Target[Property] : never };
|
|
5
|
+
type Styles = Partial<Record<keyof CSSStyleDeclaration, unknown>>;
|
|
6
|
+
/**
|
|
7
|
+
* Creates an HTML element with the specified tag name together with optional properties, attributes, and styles
|
|
8
|
+
* @param tag Tag name
|
|
9
|
+
* @param properties Element properties
|
|
10
|
+
* @param attributes Element attributes
|
|
11
|
+
* @param styles Element styles
|
|
12
|
+
* @returns Created element
|
|
13
|
+
*/
|
|
14
|
+
declare function createElement<TagName extends keyof HTMLElementTagNameMap>(tag: TagName, properties?: Properties<HTMLElementTagNameMap[TagName]>, attributes?: Record<string, unknown>, styles?: Styles): HTMLElementTagNameMap[TagName];
|
|
15
|
+
/**
|
|
16
|
+
* Creates an HTML element with the specified tag name together with optional properties, attributes, and styles
|
|
17
|
+
* @param tag Tag name
|
|
18
|
+
* @param properties Element properties
|
|
19
|
+
* @param attributes Element attributes
|
|
20
|
+
* @param styles Element styles
|
|
21
|
+
* @returns Created element
|
|
22
|
+
*/
|
|
23
|
+
declare function createElement(tag: string, properties?: Properties<HTMLElement>, attributes?: Record<string, unknown>, styles?: Styles): HTMLUnknownElement;
|
|
24
|
+
//#endregion
|
|
25
|
+
export { createElement };
|
package/dist/create.mjs
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { setAttributes } from "./attribute/set.attribute.mjs";
|
|
2
|
+
import "./attribute/index.mjs";
|
|
3
|
+
import { setStyles } from "./style.mjs";
|
|
4
|
+
import { setProperties } from "./property/set.property.mjs";
|
|
5
|
+
import "./property/index.mjs";
|
|
6
|
+
//#region src/create.ts
|
|
7
|
+
function createElement(tag, properties, attributes, styles) {
|
|
8
|
+
if (typeof tag !== "string") throw new TypeError(MESSAGE);
|
|
9
|
+
const element = document.createElement(tag);
|
|
10
|
+
if (properties != null) setProperties(element, properties);
|
|
11
|
+
if (attributes != null) setAttributes(element, attributes);
|
|
12
|
+
if (styles != null) setStyles(element, styles);
|
|
13
|
+
return element;
|
|
14
|
+
}
|
|
15
|
+
const MESSAGE = "Tag name must be a string";
|
|
16
|
+
//#endregion
|
|
17
|
+
export { createElement };
|
package/dist/data.mjs
CHANGED
|
@@ -1,29 +1,29 @@
|
|
|
1
1
|
import { isHTMLOrSVGElement } from "./internal/is.mjs";
|
|
2
2
|
import { setElementValues, updateElementValue } from "./internal/element-value.mjs";
|
|
3
3
|
import { EXPRESSION_DATA_PREFIX } from "./internal/get-value.mjs";
|
|
4
|
+
import { camelCase, kebabCase } from "@oscarpalmer/atoms/string/case";
|
|
4
5
|
import { parse } from "@oscarpalmer/atoms/string";
|
|
5
|
-
import { kebabCase } from "@oscarpalmer/atoms/string/case";
|
|
6
6
|
//#region src/data.ts
|
|
7
7
|
function getData(element, keys, parseValues) {
|
|
8
8
|
if (!isHTMLOrSVGElement(element)) return;
|
|
9
|
-
const
|
|
9
|
+
const noParse = parseValues === false;
|
|
10
10
|
if (typeof keys === "string") {
|
|
11
|
-
const value = element.dataset[keys];
|
|
11
|
+
const value = element.dataset[camelCase(keys)];
|
|
12
12
|
if (value === void 0) return;
|
|
13
|
-
return
|
|
13
|
+
return noParse ? value : parse(value);
|
|
14
14
|
}
|
|
15
15
|
const { length } = keys;
|
|
16
16
|
const data = {};
|
|
17
17
|
for (let index = 0; index < length; index += 1) {
|
|
18
18
|
const key = keys[index];
|
|
19
|
-
const value = element.dataset[key];
|
|
19
|
+
const value = element.dataset[camelCase(key)];
|
|
20
20
|
if (value == null) data[key] = void 0;
|
|
21
|
-
else data[key] =
|
|
21
|
+
else data[key] = noParse ? value : parse(value);
|
|
22
22
|
}
|
|
23
23
|
return data;
|
|
24
24
|
}
|
|
25
25
|
function getName(original) {
|
|
26
|
-
return `${ATTRIBUTE_DATA_PREFIX}${kebabCase(original
|
|
26
|
+
return `${ATTRIBUTE_DATA_PREFIX}${kebabCase(original.replace(EXPRESSION_DATA_PREFIX, ""))}`;
|
|
27
27
|
}
|
|
28
28
|
function setData(element, first, second) {
|
|
29
29
|
setElementValues(element, first, second, null, updateDataAttribute);
|
|
@@ -17,7 +17,14 @@ function delegatedEventHandler(event) {
|
|
|
17
17
|
const key = `${EVENT_PREFIX}${event.type}${this ? EVENT_SUFFIX_PASSIVE : EVENT_SUFFIX_ACTIVE}`;
|
|
18
18
|
const items = event.composedPath();
|
|
19
19
|
const { length } = items;
|
|
20
|
+
let cancelled = false;
|
|
20
21
|
let target = items[0];
|
|
22
|
+
const originalStopPropagation = event.stopPropagation;
|
|
23
|
+
event.stopPropagation = function() {
|
|
24
|
+
cancelled = true;
|
|
25
|
+
originalStopPropagation.call(event);
|
|
26
|
+
};
|
|
27
|
+
event.stopImmediatePropagation = event.stopPropagation.bind(event);
|
|
21
28
|
Object.defineProperties(event, {
|
|
22
29
|
currentTarget: {
|
|
23
30
|
configurable: true,
|
|
@@ -37,7 +44,7 @@ function delegatedEventHandler(event) {
|
|
|
37
44
|
target = item;
|
|
38
45
|
for (const listener of listeners) {
|
|
39
46
|
listener.call(item, event);
|
|
40
|
-
if (
|
|
47
|
+
if (cancelled) return;
|
|
41
48
|
}
|
|
42
49
|
}
|
|
43
50
|
}
|
package/dist/html/index.d.mts
CHANGED
|
@@ -1,36 +1,33 @@
|
|
|
1
1
|
//#region src/html/index.d.ts
|
|
2
|
-
type Html = {
|
|
3
|
-
/**
|
|
4
|
-
* Create nodes from an HTML string or a template element
|
|
5
|
-
* @param value HTML string or id for a template element
|
|
6
|
-
* @param options Options for creating nodes
|
|
7
|
-
* @returns Created nodes
|
|
8
|
-
*/
|
|
9
|
-
(value: string, options?: HtmlOptions): Node[];
|
|
10
|
-
/**
|
|
11
|
-
* Create nodes from a template element
|
|
12
|
-
* @param template Template element
|
|
13
|
-
* @param options Options for creating nodes
|
|
14
|
-
* @returns Created nodes
|
|
15
|
-
*/
|
|
16
|
-
(template: HTMLTemplateElement, options?: HtmlOptions): Node[];
|
|
17
|
-
/**
|
|
18
|
-
* Clear cache of template elements
|
|
19
|
-
*/
|
|
20
|
-
clear(): void;
|
|
21
|
-
/**
|
|
22
|
-
* Remove cached template element for an HTML string or id
|
|
23
|
-
* @param template HTML string or id for a template element
|
|
24
|
-
*/
|
|
25
|
-
remove(template: string): void;
|
|
26
|
-
};
|
|
27
2
|
type HtmlOptions = {
|
|
28
3
|
/**
|
|
29
4
|
* Cache template element for the HTML string? _(defaults to `true`)_
|
|
30
5
|
*/
|
|
31
6
|
cache?: boolean;
|
|
32
7
|
};
|
|
33
|
-
|
|
8
|
+
/**
|
|
9
|
+
* Create nodes from a template string
|
|
10
|
+
* @returns Created nodes
|
|
11
|
+
*/
|
|
12
|
+
declare function html(strings: TemplateStringsArray, ...values: unknown[]): Node[];
|
|
13
|
+
/**
|
|
14
|
+
* Create nodes from an HTML string or a template element
|
|
15
|
+
* @param value HTML string or id for a template element
|
|
16
|
+
* @param options Options for creating nodes
|
|
17
|
+
* @returns Created nodes
|
|
18
|
+
*/
|
|
19
|
+
declare function html(value: string, options?: HtmlOptions): Node[];
|
|
20
|
+
/**
|
|
21
|
+
* Create nodes from a template element
|
|
22
|
+
* @param template Template element
|
|
23
|
+
* @param options Options for creating nodes
|
|
24
|
+
* @returns Created nodes
|
|
25
|
+
*/
|
|
26
|
+
declare function html(template: HTMLTemplateElement, options?: HtmlOptions): Node[];
|
|
27
|
+
declare namespace html {
|
|
28
|
+
var clear: () => void;
|
|
29
|
+
var remove: (template: string) => void;
|
|
30
|
+
}
|
|
34
31
|
/**
|
|
35
32
|
* Sanitize one or more nodes, recursively
|
|
36
33
|
* @param value Node or nodes to sanitize
|
package/dist/html/index.mjs
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import { sanitizeNodes } from "./sanitize.mjs";
|
|
2
2
|
import { isPlainObject } from "@oscarpalmer/atoms/is";
|
|
3
|
+
import { getString } from "@oscarpalmer/atoms/string";
|
|
4
|
+
import { SizedMap } from "@oscarpalmer/atoms/sized/map";
|
|
3
5
|
//#region src/html/index.ts
|
|
4
6
|
function createHtml(value) {
|
|
5
7
|
const parsed = getParser().parseFromString(getHtml(value), PARSE_TYPE_HTML);
|
|
@@ -10,16 +12,22 @@ function createHtml(value) {
|
|
|
10
12
|
function createTemplate(value, options) {
|
|
11
13
|
const template = document.createElement(TEMPLATE_TAG);
|
|
12
14
|
template.innerHTML = createHtml(value);
|
|
13
|
-
if (typeof value === "string" && options.cache) templates
|
|
15
|
+
if (typeof value === "string" && options.cache) templates.set(value, template);
|
|
14
16
|
return template;
|
|
15
17
|
}
|
|
18
|
+
function getComment(index) {
|
|
19
|
+
return COMMENT_TEMPLATE.replace(COMMENT_INDEX, String(index));
|
|
20
|
+
}
|
|
16
21
|
function getHtml(value) {
|
|
17
22
|
return `${TEMPORARY_ELEMENT}${typeof value === "string" ? value : value.innerHTML}${TEMPORARY_ELEMENT}`;
|
|
18
23
|
}
|
|
19
|
-
function getNodes(value, options) {
|
|
24
|
+
function getNodes(value, options, nodes) {
|
|
20
25
|
if (typeof value !== "string" && !(value instanceof HTMLTemplateElement)) return [];
|
|
21
26
|
const template = getTemplate(value, options);
|
|
22
|
-
|
|
27
|
+
if (template == null) return [];
|
|
28
|
+
const cloned = [...template.content.cloneNode(true).childNodes];
|
|
29
|
+
if (nodes != null) replaceComments(cloned, nodes);
|
|
30
|
+
return cloned;
|
|
23
31
|
}
|
|
24
32
|
function getOptions(input) {
|
|
25
33
|
const options = isPlainObject(input) ? input : {};
|
|
@@ -30,31 +38,86 @@ function getParser() {
|
|
|
30
38
|
parser ??= new DOMParser();
|
|
31
39
|
return parser;
|
|
32
40
|
}
|
|
41
|
+
function getTagged(strings, values) {
|
|
42
|
+
const tagged = {
|
|
43
|
+
nodes: [],
|
|
44
|
+
template: ""
|
|
45
|
+
};
|
|
46
|
+
const stringsLength = strings.length;
|
|
47
|
+
let nodeIndex = 0;
|
|
48
|
+
for (let stringIndex = 0; stringIndex < stringsLength; stringIndex += 1) {
|
|
49
|
+
const value = values[stringIndex];
|
|
50
|
+
tagged.template += strings[stringIndex];
|
|
51
|
+
if (value instanceof Node) {
|
|
52
|
+
tagged.nodes.push(value);
|
|
53
|
+
tagged.template += getComment(nodeIndex);
|
|
54
|
+
nodeIndex += 1;
|
|
55
|
+
} else if (hasNodes(value)) {
|
|
56
|
+
const items = [...value];
|
|
57
|
+
const itemsLength = items.length;
|
|
58
|
+
for (let itemIndex = 0; itemIndex < itemsLength; itemIndex += 1) {
|
|
59
|
+
const item = items[itemIndex];
|
|
60
|
+
if (item instanceof Node) {
|
|
61
|
+
tagged.nodes.push(item);
|
|
62
|
+
tagged.template += getComment(nodeIndex);
|
|
63
|
+
nodeIndex += 1;
|
|
64
|
+
} else tagged.template += getString(item);
|
|
65
|
+
}
|
|
66
|
+
} else if (Array.isArray(value)) {
|
|
67
|
+
const valueLength = value.length;
|
|
68
|
+
for (let valueIndex = 0; valueIndex < valueLength; valueIndex += 1) tagged.template += getString(value[valueIndex]);
|
|
69
|
+
} else tagged.template += getString(value);
|
|
70
|
+
}
|
|
71
|
+
return tagged;
|
|
72
|
+
}
|
|
33
73
|
function getTemplate(value, options) {
|
|
34
74
|
if (value instanceof HTMLTemplateElement) return createTemplate(value, options);
|
|
35
75
|
if (value.trim().length === 0) return;
|
|
36
|
-
let template = templates
|
|
76
|
+
let template = templates.get(value);
|
|
37
77
|
if (template != null) return template;
|
|
38
78
|
const element = EXPRESSION_ID.test(value) ? document.querySelector(`#${value}`) : null;
|
|
39
79
|
return createTemplate(element instanceof HTMLTemplateElement ? element : value, options);
|
|
40
80
|
}
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
81
|
+
function hasNodes(value) {
|
|
82
|
+
if (value instanceof HTMLCollection || value instanceof NodeList) return true;
|
|
83
|
+
return Array.isArray(value) && value.some((item) => item instanceof Node);
|
|
84
|
+
}
|
|
85
|
+
function html(first, ...second) {
|
|
86
|
+
if (isTagged(first)) {
|
|
87
|
+
const tagged = getTagged(first, second);
|
|
88
|
+
return getNodes(tagged.template, getOptions(), tagged.nodes);
|
|
89
|
+
}
|
|
90
|
+
return getNodes(first, getOptions(second[0]));
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* Clear cache of template elements
|
|
94
|
+
*/
|
|
44
95
|
html.clear = () => {
|
|
45
|
-
templates
|
|
96
|
+
templates.clear();
|
|
46
97
|
};
|
|
98
|
+
/**
|
|
99
|
+
* Remove cached template element for an HTML string or id
|
|
100
|
+
* @param template HTML string or id for a template element
|
|
101
|
+
*/
|
|
47
102
|
html.remove = (template) => {
|
|
48
|
-
|
|
49
|
-
const keys = Object.keys(templates);
|
|
50
|
-
const { length } = keys;
|
|
51
|
-
const updated = {};
|
|
52
|
-
for (let index = 0; index < length; index += 1) {
|
|
53
|
-
const key = keys[index];
|
|
54
|
-
if (key !== template) updated[key] = templates[key];
|
|
55
|
-
}
|
|
56
|
-
templates = updated;
|
|
103
|
+
templates.delete(template);
|
|
57
104
|
};
|
|
105
|
+
function isTagged(value) {
|
|
106
|
+
return Array.isArray(value) && Array.isArray(value.raw);
|
|
107
|
+
}
|
|
108
|
+
function replaceComments(origin, replacements) {
|
|
109
|
+
const nodes = [...origin];
|
|
110
|
+
const { length } = nodes;
|
|
111
|
+
for (let nodeIndex = 0; nodeIndex < length; nodeIndex += 1) {
|
|
112
|
+
const node = nodes[nodeIndex];
|
|
113
|
+
if (node instanceof Comment) {
|
|
114
|
+
const [, index] = EXPRESSION_COMMENT.exec(node.textContent) ?? [];
|
|
115
|
+
if (index != null) node.replaceWith(replacements[Number(index)]);
|
|
116
|
+
continue;
|
|
117
|
+
}
|
|
118
|
+
if (node.hasChildNodes()) replaceComments(node.childNodes, replacements);
|
|
119
|
+
}
|
|
120
|
+
}
|
|
58
121
|
/**
|
|
59
122
|
* Sanitize one or more nodes, recursively
|
|
60
123
|
* @param value Node or nodes to sanitize
|
|
@@ -64,11 +127,15 @@ html.remove = (template) => {
|
|
|
64
127
|
function sanitize(value) {
|
|
65
128
|
return sanitizeNodes(Array.isArray(value) ? value : [value], 0);
|
|
66
129
|
}
|
|
130
|
+
const COMMENT_INDEX = "<index>";
|
|
131
|
+
const COMMENT_TEMPLATE = `<!--toretto.node:${COMMENT_INDEX}-->`;
|
|
132
|
+
const EXPRESSION_COMMENT = /^toretto\.node:(\d+)$/;
|
|
67
133
|
const EXPRESSION_ID = /^[a-z][\w-]*$/i;
|
|
68
134
|
const PARSE_TYPE_HTML = "text/html";
|
|
69
135
|
const TEMPLATE_TAG = "template";
|
|
70
136
|
const TEMPORARY_ELEMENT = "<toretto-temporary></toretto-temporary>";
|
|
137
|
+
const templates = new SizedMap(128);
|
|
71
138
|
let parser;
|
|
72
|
-
|
|
139
|
+
window.templates = templates;
|
|
73
140
|
//#endregion
|
|
74
141
|
export { html, sanitize };
|
package/dist/html/sanitize.mjs
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
|
-
import { _isBadAttribute,
|
|
2
|
-
import { setAttribute } from "../attribute/set.mjs";
|
|
1
|
+
import { _isBadAttribute, _isInvalidBooleanAttribute } from "../internal/attribute.mjs";
|
|
2
|
+
import { setAttribute } from "../attribute/set.attribute.mjs";
|
|
3
3
|
//#region src/html/sanitize.ts
|
|
4
4
|
function handleElement(element, depth) {
|
|
5
5
|
if (depth === 0) {
|
|
6
|
-
const removable = element.querySelectorAll(REMOVE_SELECTOR);
|
|
7
|
-
|
|
6
|
+
const removable = [...element.querySelectorAll(REMOVE_SELECTOR)];
|
|
7
|
+
const { length } = removable;
|
|
8
|
+
for (let index = 0; index < length; index += 1) removable[index].remove();
|
|
8
9
|
}
|
|
9
10
|
sanitizeAttributes(element, [...element.attributes]);
|
|
10
11
|
}
|
|
@@ -23,7 +24,7 @@ function sanitizeAttributes(element, attributes) {
|
|
|
23
24
|
const { length } = attributes;
|
|
24
25
|
for (let index = 0; index < length; index += 1) {
|
|
25
26
|
const { name, value } = attributes[index];
|
|
26
|
-
if (_isBadAttribute(name, value, false)
|
|
27
|
+
if (_isBadAttribute(name, value, false)) element.removeAttribute(name);
|
|
27
28
|
else if (_isInvalidBooleanAttribute(name, value, false)) setAttribute(element, name, true);
|
|
28
29
|
}
|
|
29
30
|
}
|