@oscarpalmer/toretto 0.40.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.
Files changed (56) hide show
  1. package/dist/attribute/{get.d.mts → get.attribute.d.mts} +3 -2
  2. package/dist/attribute/{get.mjs → get.attribute.mjs} +4 -3
  3. package/dist/attribute/index.d.mts +3 -16
  4. package/dist/attribute/index.mjs +4 -7
  5. package/dist/attribute/{set.d.mts → set.attribute.d.mts} +4 -4
  6. package/dist/attribute/{set.mjs → set.attribute.mjs} +2 -2
  7. package/dist/create.d.mts +25 -0
  8. package/dist/create.mjs +17 -0
  9. package/dist/data.d.mts +1 -1
  10. package/dist/data.mjs +7 -7
  11. package/dist/event/delegation.mjs +8 -1
  12. package/dist/html/index.d.mts +23 -26
  13. package/dist/html/index.mjs +85 -18
  14. package/dist/html/sanitize.mjs +6 -5
  15. package/dist/index.d.mts +114 -53
  16. package/dist/index.mjs +553 -561
  17. package/dist/internal/attribute.d.mts +4 -3
  18. package/dist/internal/attribute.mjs +13 -23
  19. package/dist/internal/element-value.d.mts +2 -2
  20. package/dist/internal/element-value.mjs +4 -2
  21. package/dist/internal/get-value.mjs +1 -1
  22. package/dist/internal/property.d.mts +4 -0
  23. package/dist/internal/property.mjs +21 -0
  24. package/dist/property/get.property.d.mts +20 -0
  25. package/dist/property/get.property.mjs +35 -0
  26. package/dist/property/index.d.mts +3 -0
  27. package/dist/property/index.mjs +3 -0
  28. package/dist/property/set.property.d.mts +32 -0
  29. package/dist/property/set.property.mjs +34 -0
  30. package/dist/style.d.mts +12 -7
  31. package/dist/style.mjs +14 -18
  32. package/package.json +12 -5
  33. package/src/attribute/{get.ts → get.attribute.ts} +14 -3
  34. package/src/attribute/index.ts +10 -22
  35. package/src/attribute/{set.ts → set.attribute.ts} +9 -5
  36. package/src/create.ts +81 -0
  37. package/src/data.ts +17 -9
  38. package/src/event/delegation.ts +24 -3
  39. package/src/event/index.ts +9 -3
  40. package/src/find/index.ts +12 -4
  41. package/src/find/relative.ts +4 -0
  42. package/src/focusable.ts +10 -2
  43. package/src/html/index.ts +166 -58
  44. package/src/html/sanitize.ts +14 -11
  45. package/src/index.ts +2 -1
  46. package/src/internal/attribute.ts +23 -42
  47. package/src/internal/element-value.ts +11 -4
  48. package/src/internal/get-value.ts +8 -0
  49. package/src/internal/is.ts +4 -0
  50. package/src/internal/property.ts +42 -0
  51. package/src/is.ts +10 -2
  52. package/src/property/get.property.ts +73 -0
  53. package/src/property/index.ts +2 -0
  54. package/src/property/set.property.ts +102 -0
  55. package/src/style.ts +52 -27
  56. 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: `data-${string}`, parse?: boolean): unknown;
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
- //#region src/attribute/get.ts
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, isEmptyNonBooleanAttribute, isInvalidBooleanAttribute, setAttribute, setAttributes };
50
+ export { booleanAttributes, getAttribute, getAttributes, isBadAttribute, isBooleanAttribute, isInvalidBooleanAttribute, setAttribute, setAttributes };
@@ -1,6 +1,6 @@
1
- import { _isBadAttribute, _isBooleanAttribute, _isEmptyNonBooleanAttribute, _isInvalidBooleanAttribute, booleanAttributes } from "../internal/attribute.mjs";
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, isEmptyNonBooleanAttribute, isInvalidBooleanAttribute, setAttribute, setAttributes };
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 DispatchedAttribute = 'checked' | 'open' | 'value';
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<Name extends DispatchedAttribute>(element: Element, name: Name, value?: unknown, dispatch?: boolean): void;
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
- //#region src/attribute/set.ts
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 };
@@ -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.d.mts CHANGED
@@ -1,4 +1,4 @@
1
- import { PlainObject } from "@oscarpalmer/atoms";
1
+ import { PlainObject } from "@oscarpalmer/atoms/models";
2
2
 
3
3
  //#region src/data.d.ts
4
4
  /**
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 shouldParse = parseValues !== false;
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 shouldParse ? parse(value) : value;
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] = shouldParse ? parse(value) : value;
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).replace(EXPRESSION_DATA_PREFIX, "")}`;
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 (event.cancelBubble) return;
47
+ if (cancelled) return;
41
48
  }
42
49
  }
43
50
  }
@@ -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
- declare const html: Html;
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
@@ -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[value] = template;
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
- return template == null ? [] : [...template.content.cloneNode(true).childNodes];
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[value];
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
- const html = ((value, options) => {
42
- return getNodes(value, getOptions(options));
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
- if (typeof template !== "string" || templates[template] == null) return;
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
- let templates = {};
139
+ window.templates = templates;
73
140
  //#endregion
74
141
  export { html, sanitize };
@@ -1,10 +1,11 @@
1
- import { _isBadAttribute, _isEmptyNonBooleanAttribute, _isInvalidBooleanAttribute } from "../internal/attribute.mjs";
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
- for (const item of removable) item.remove();
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) || _isEmptyNonBooleanAttribute(name, value, false)) element.removeAttribute(name);
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
  }