@oscarpalmer/toretto 0.30.0 → 0.31.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/package.json CHANGED
@@ -11,8 +11,8 @@
11
11
  "@types/node": "^25",
12
12
  "@vitest/coverage-istanbul": "^4",
13
13
  "jsdom": "^27.3",
14
- "oxfmt": "^0.18",
15
- "oxlint": "^1.33",
14
+ "oxfmt": "^0.19",
15
+ "oxlint": "^1.34",
16
16
  "rolldown": "1.0.0-beta.55",
17
17
  "tslib": "^2.8",
18
18
  "typescript": "^5.9",
@@ -46,8 +46,8 @@
46
46
  "default": "./dist/focusable.js"
47
47
  },
48
48
  "./html": {
49
- "types": "./types/html.d.ts",
50
- "default": "./dist/html.js"
49
+ "types": "./types/html/index.d.ts",
50
+ "default": "./dist/html/index.js"
51
51
  },
52
52
  "./is": {
53
53
  "types": "./types/is.d.ts",
@@ -87,11 +87,11 @@
87
87
  "build": "npm run clean && npx vite build && npm run rolldown:build && npx tsc",
88
88
  "clean": "rm -rf ./dist && rm -rf ./types && rm -f ./tsconfig.tsbuildinfo",
89
89
  "rolldown:build": "npx rolldown -c",
90
- "rolldown:watch": "npx rolldown -c --watch",
90
+ "rolldown:watch": "npx rolldown -c ./rolldown.config.js --watch",
91
91
  "test": "npx vitest --coverage",
92
92
  "watch": "npx vite build --watch"
93
93
  },
94
94
  "type": "module",
95
95
  "types": "types/index.d.ts",
96
- "version": "0.30.0"
96
+ "version": "0.31.0"
97
97
  }
@@ -8,11 +8,7 @@ import {isHTMLOrSVGElement} from '../internal/is';
8
8
  * @param parse Parse value? _(defaults to `true`)_
9
9
  * @returns Attribute value _(or `undefined`)_
10
10
  */
11
- export function getAttribute(
12
- element: HTMLOrSVGElement,
13
- name: `data-${string}`,
14
- parse?: boolean,
15
- ): unknown;
11
+ export function getAttribute(element: Element, name: `data-${string}`, parse?: boolean): unknown;
16
12
 
17
13
  /**
18
14
  * Get the value of a specific attribute from an element
@@ -20,13 +16,9 @@ export function getAttribute(
20
16
  * @param name Attribute name
21
17
  * @returns Attribute value _(or `undefined`)_
22
18
  */
23
- export function getAttribute(element: HTMLOrSVGElement, name: string): unknown;
19
+ export function getAttribute(element: Element, name: string): unknown;
24
20
 
25
- export function getAttribute(
26
- element: HTMLOrSVGElement,
27
- name: string,
28
- parseValues?: boolean,
29
- ): unknown {
21
+ export function getAttribute(element: Element, name: string, parseValues?: boolean): unknown {
30
22
  if (isHTMLOrSVGElement(element) && typeof name === 'string') {
31
23
  return getAttributeValue(element, name, parseValues !== false);
32
24
  }
@@ -40,7 +32,7 @@ export function getAttribute(
40
32
  * @returns Object of named attributes
41
33
  */
42
34
  export function getAttributes<Key extends string>(
43
- element: HTMLOrSVGElement,
35
+ element: Element,
44
36
  names: Key[],
45
37
  parseData?: boolean,
46
38
  ): Record<Key, unknown> {
@@ -1,8 +1,8 @@
1
1
  import {
2
- isBadAttribute as internalIsBadAttribute,
3
- isBooleanAttribute as _isBooleanAttribute,
4
- isEmptyNonBooleanAttribute as _isEmptyNonBooleanAttribute,
5
- isInvalidBooleanAttribute as _isInvalidBooleanAttribute,
2
+ _isBadAttribute,
3
+ _isBooleanAttribute,
4
+ _isEmptyNonBooleanAttribute,
5
+ _isInvalidBooleanAttribute,
6
6
  } from '../internal/attribute';
7
7
  import type {Attribute} from '../models';
8
8
 
@@ -22,7 +22,7 @@ export function isBadAttribute(attribute: Attr | Attribute): boolean;
22
22
  export function isBadAttribute(name: string, value: string): boolean;
23
23
 
24
24
  export function isBadAttribute(first: unknown, second?: unknown): boolean {
25
- return internalIsBadAttribute(first, second, true);
25
+ return _isBadAttribute(first, second, true);
26
26
  }
27
27
 
28
28
  /**
@@ -1,5 +1,5 @@
1
1
  import {updateValue, updateValues} from '../internal/attribute';
2
- import type {Attribute, HTMLOrSVGElement, Property} from '../models';
2
+ import type {Attribute, Property} from '../models';
3
3
 
4
4
  /**
5
5
  * Set an attribute on an element
@@ -9,7 +9,7 @@ import type {Attribute, HTMLOrSVGElement, Property} from '../models';
9
9
  * @param name Attribute name
10
10
  * @param value Attribute value
11
11
  */
12
- export function setAttribute(element: HTMLOrSVGElement, name: string, value?: unknown): void;
12
+ export function setAttribute(element: Element, name: string, value?: unknown): void;
13
13
 
14
14
  /**
15
15
  * Set an attribute on an element
@@ -18,9 +18,9 @@ export function setAttribute(element: HTMLOrSVGElement, name: string, value?: un
18
18
  * @param element Element for attribute
19
19
  * @param attribute Attribute to set
20
20
  */
21
- export function setAttribute(element: HTMLOrSVGElement, attribute: Attr | Attribute): void;
21
+ export function setAttribute(element: Element, attribute: Attr | Attribute): void;
22
22
 
23
- export function setAttribute(element: HTMLOrSVGElement, first: unknown, second?: unknown): void {
23
+ export function setAttribute(element: Element, first: unknown, second?: unknown): void {
24
24
  updateValue(element, first, second);
25
25
  }
26
26
 
@@ -31,7 +31,7 @@ export function setAttribute(element: HTMLOrSVGElement, first: unknown, second?:
31
31
  * @param element Element for attributes
32
32
  * @param attributes Attributes to set
33
33
  */
34
- export function setAttributes(element: HTMLOrSVGElement, attributes: Array<Attr | Attribute>): void;
34
+ export function setAttributes(element: Element, attributes: Array<Attr | Attribute>): void;
35
35
 
36
36
  /**
37
37
  * Set one or more attributes on an element
@@ -40,10 +40,10 @@ export function setAttributes(element: HTMLOrSVGElement, attributes: Array<Attr
40
40
  * @param element Element for attributes
41
41
  * @param attributes Attributes to set
42
42
  */
43
- export function setAttributes(element: HTMLOrSVGElement, attributes: Record<string, unknown>): void;
43
+ export function setAttributes(element: Element, attributes: Record<string, unknown>): void;
44
44
 
45
45
  export function setAttributes(
46
- element: HTMLOrSVGElement,
46
+ element: Element,
47
47
  attributes: Attribute[] | Record<string, unknown>,
48
48
  ): void {
49
49
  updateValues(element, attributes);
@@ -57,7 +57,7 @@ export function setAttributes(
57
57
  * @param name Property name
58
58
  * @param value Property value
59
59
  */
60
- export function setProperty(element: HTMLOrSVGElement, name: string, value: boolean | string): void;
60
+ export function setProperty(element: Element, name: string, value: boolean | string): void;
61
61
 
62
62
  /**
63
63
  * Set a property on an element
@@ -66,9 +66,9 @@ export function setProperty(element: HTMLOrSVGElement, name: string, value: bool
66
66
  * @param element Element for property
67
67
  * @param property Property to set
68
68
  */
69
- export function setProperty(element: HTMLOrSVGElement, property: Property): void;
69
+ export function setProperty(element: Element, property: Property): void;
70
70
 
71
- export function setProperty(element: HTMLOrSVGElement, first: unknown, second?: unknown): void {
71
+ export function setProperty(element: Element, first: unknown, second?: unknown): void {
72
72
  updateValue(element, first, second);
73
73
  }
74
74
 
@@ -79,7 +79,7 @@ export function setProperty(element: HTMLOrSVGElement, first: unknown, second?:
79
79
  * @param element Element for properties
80
80
  * @param properties Properties to set
81
81
  */
82
- export function setProperties(element: HTMLOrSVGElement, properties: Property[]): void;
82
+ export function setProperties(element: Element, properties: Property[]): void;
83
83
 
84
84
  /**
85
85
  * Set one or more properties on an element
@@ -88,10 +88,10 @@ export function setProperties(element: HTMLOrSVGElement, properties: Property[])
88
88
  * @param element Element for properties
89
89
  * @param properties Properties to set
90
90
  */
91
- export function setProperties(element: HTMLOrSVGElement, properties: Record<string, unknown>): void;
91
+ export function setProperties(element: Element, properties: Record<string, unknown>): void;
92
92
 
93
93
  export function setProperties(
94
- element: HTMLOrSVGElement,
94
+ element: Element,
95
95
  properties: Property[] | Record<string, unknown>,
96
96
  ): void {
97
97
  updateValues(element, properties);
package/src/data.ts CHANGED
@@ -3,7 +3,6 @@ import {kebabCase, parse} from '@oscarpalmer/atoms/string';
3
3
  import {setElementValues, updateElementValue} from './internal/element-value';
4
4
  import {EXPRESSION_DATA_PREFIX} from './internal/get-value';
5
5
  import {isHTMLOrSVGElement} from './internal/is';
6
- import type {HTMLOrSVGElement} from './models';
7
6
 
8
7
  /**
9
8
  * Get a keyed data value from an element
@@ -12,7 +11,7 @@ import type {HTMLOrSVGElement} from './models';
12
11
  * @param parse Parse values? _(defaults to `true`)_
13
12
  * @returns Data value
14
13
  */
15
- export function getData(element: HTMLOrSVGElement, key: string, parse?: boolean): unknown;
14
+ export function getData(element: Element, key: string, parse?: boolean): unknown;
16
15
 
17
16
  /**
18
17
  * Get keyed data values from an element
@@ -22,16 +21,12 @@ export function getData(element: HTMLOrSVGElement, key: string, parse?: boolean)
22
21
  * @returns Keyed data values
23
22
  */
24
23
  export function getData<Key extends string>(
25
- element: HTMLOrSVGElement,
24
+ element: Element,
26
25
  keys: Key[],
27
26
  parse?: boolean,
28
27
  ): Record<Key, unknown>;
29
28
 
30
- export function getData(
31
- element: HTMLOrSVGElement,
32
- keys: string | string[],
33
- parseValues?: boolean,
34
- ): unknown {
29
+ export function getData(element: Element, keys: string | string[], parseValues?: boolean): unknown {
35
30
  if (!isHTMLOrSVGElement(element)) {
36
31
  return;
37
32
  }
@@ -75,7 +70,7 @@ function getName(original: string): string {
75
70
  * @param element Element to set data on
76
71
  * @param data Data to set
77
72
  */
78
- export function setData(element: HTMLOrSVGElement, data: PlainObject): void;
73
+ export function setData(element: Element, data: PlainObject): void;
79
74
 
80
75
  /**
81
76
  * Set a data value on an element
@@ -83,17 +78,13 @@ export function setData(element: HTMLOrSVGElement, data: PlainObject): void;
83
78
  * @param key Data key
84
79
  * @param value Data value
85
80
  */
86
- export function setData(element: HTMLOrSVGElement, key: string, value: unknown): void;
81
+ export function setData(element: Element, key: string, value: unknown): void;
87
82
 
88
- export function setData(
89
- element: HTMLOrSVGElement,
90
- first: PlainObject | string,
91
- second?: unknown,
92
- ): void {
83
+ export function setData(element: Element, first: PlainObject | string, second?: unknown): void {
93
84
  setElementValues(element, first, second, updateDataAttribute);
94
85
  }
95
86
 
96
- function updateDataAttribute(element: HTMLOrSVGElement, key: string, value: unknown): void {
87
+ function updateDataAttribute(element: Element, key: string, value: unknown): void {
97
88
  updateElementValue(
98
89
  element,
99
90
  getName(key),
@@ -14,22 +14,22 @@ export type EventTargetWithListeners = EventTarget &
14
14
  }>;
15
15
 
16
16
  function addDelegatedHandler(
17
- document: DocumentWithListenerCounts,
17
+ doc: DocumentWithListenerCounts,
18
18
  type: string,
19
19
  name: string,
20
20
  passive: boolean,
21
21
  ): void {
22
22
  const count = `${name}${COUNT_SUFFIX}`;
23
23
 
24
- if (document[count] != null) {
25
- (document[count] as number) += 1;
24
+ if (doc[count] != null) {
25
+ (doc[count] as number) += 1;
26
26
 
27
27
  return;
28
28
  }
29
29
 
30
- document[count] = 1;
30
+ doc[count] = 1;
31
31
 
32
- document.addEventListener(type, passive ? HANDLER_PASSIVE : HANDLER_ACTIVE, {
32
+ doc.addEventListener(type, passive ? HANDLER_PASSIVE : HANDLER_ACTIVE, {
33
33
  passive,
34
34
  });
35
35
  }
@@ -103,19 +103,19 @@ export function getDelegatedName(
103
103
  }
104
104
 
105
105
  function removeDelegatedHandler(
106
- document: DocumentWithListenerCounts,
106
+ doc: DocumentWithListenerCounts,
107
107
  type: string,
108
108
  name: string,
109
109
  passive: boolean,
110
110
  ): void {
111
111
  const count = `${name}${COUNT_SUFFIX}`;
112
112
 
113
- (document[count] as number) -= 1;
113
+ (doc[count] as number) -= 1;
114
114
 
115
- if ((document[count] as number) < 1) {
116
- document[count] = undefined;
115
+ if ((doc[count] as number) < 1) {
116
+ doc[count] = undefined;
117
117
 
118
- document.removeEventListener(type, passive ? HANDLER_PASSIVE : HANDLER_ACTIVE);
118
+ doc.removeEventListener(type, passive ? HANDLER_PASSIVE : HANDLER_ACTIVE);
119
119
  }
120
120
  }
121
121
 
package/src/find/index.ts CHANGED
@@ -93,7 +93,7 @@ function findElementOrElementsFromNodes(
93
93
  element != null &&
94
94
  (context == null ||
95
95
  contexts.length === 0 ||
96
- contexts.some(context => context === element || context.contains(element))) &&
96
+ contexts.some(ctx => ctx === element || ctx.contains(element))) &&
97
97
  !result.includes(element)
98
98
  ) {
99
99
  result.push(element);
@@ -3,20 +3,24 @@
3
3
  * - If the distance cannot be calculated, `-1` is returned
4
4
  */
5
5
  function getDistanceBetweenElements(origin: Element, target: Element): number | undefined {
6
- if (origin === target || origin.parentElement === target) {
6
+ if (origin === target) {
7
7
  return 0;
8
8
  }
9
9
 
10
- const comparison = origin.compareDocumentPosition(target);
10
+ if (origin.parentElement === target) {
11
+ return 1;
12
+ }
13
+
11
14
  const children = [...(origin.parentElement?.children ?? [])];
12
15
 
13
16
  if (children.includes(target)) {
14
17
  return Math.abs(children.indexOf(origin) - children.indexOf(target));
15
18
  }
16
19
 
17
- const beforeOrInside = !!(comparison & 2 || comparison & 8);
20
+ const comparison = origin.compareDocumentPosition(target);
21
+ const beforeOrInside = Boolean(comparison & 2 || comparison & 8);
18
22
 
19
- if (beforeOrInside || !!(comparison & 4 || comparison & 16)) {
23
+ if (beforeOrInside || Boolean(comparison & 4 || comparison & 16)) {
20
24
  return traverse(beforeOrInside ? origin : target, beforeOrInside ? target : origin) ?? -1;
21
25
  }
22
26
  }
@@ -86,10 +90,6 @@ export function findRelatives(
86
90
  return [];
87
91
  }
88
92
 
89
- if (origin.matches(selector)) {
90
- return [origin];
91
- }
92
-
93
93
  const elements = [
94
94
  ...(context instanceof Document || context instanceof Element
95
95
  ? context
@@ -99,12 +99,8 @@ export function findRelatives(
99
99
 
100
100
  const {length} = elements;
101
101
 
102
- if (length === 0) {
103
- return [];
104
- }
105
-
106
- if (length === 1) {
107
- return [elements[0]];
102
+ if (length < 2) {
103
+ return elements.filter(element => element !== origin);
108
104
  }
109
105
 
110
106
  const distances = [];
@@ -113,9 +109,9 @@ export function findRelatives(
113
109
 
114
110
  for (let index = 0; index < length; index += 1) {
115
111
  const element = elements[index];
116
- const distance = getDistanceBetweenElements(origin, element) ?? -1;
112
+ const distance = getDistanceBetweenElements(origin, element);
117
113
 
118
- if (distance > -1) {
114
+ if (distance != null && distance > 0) {
119
115
  if (minimum == null || distance < minimum) {
120
116
  minimum = distance;
121
117
  }
@@ -127,16 +123,16 @@ export function findRelatives(
127
123
  }
128
124
  }
129
125
 
130
- return minimum == null
131
- ? []
132
- : distances.filter(found => found.distance === minimum).map(found => found.element);
126
+ return distances
127
+ .filter(found => found.distance === minimum && found.element !== origin)
128
+ .map(found => found.element);
133
129
  }
134
130
 
135
131
  function traverse(from: Element, to: Element): number | undefined {
136
132
  let index = [...to.children].indexOf(from);
137
133
 
138
134
  if (index > -1) {
139
- return index + 1;
135
+ return 1;
140
136
  }
141
137
 
142
138
  let current = from;
@@ -148,7 +144,7 @@ function traverse(from: Element, to: Element): number | undefined {
148
144
  return distance + 1;
149
145
  }
150
146
 
151
- const children = [...(parent.children ?? [])];
147
+ const children = [...parent.children];
152
148
 
153
149
  if (children.includes(to)) {
154
150
  return distance + Math.abs(children.indexOf(current) - children.indexOf(to));
@@ -157,9 +153,9 @@ function traverse(from: Element, to: Element): number | undefined {
157
153
  index = children.findIndex(child => child.contains(to));
158
154
 
159
155
  if (index > -1) {
160
- const traversed = traverse(current, children[index]) ?? -1;
156
+ const traversed = traverse(current, children[index]);
161
157
 
162
- return traversed === -1
158
+ return traversed == null || traversed === -1
163
159
  ? -1
164
160
  : distance + Math.abs(index - children.indexOf(current)) + traversed;
165
161
  }
package/src/html/index.ts CHANGED
@@ -44,16 +44,13 @@ type Options = Required<HtmlOptions>;
44
44
  //
45
45
 
46
46
  function createHtml(value: string | HTMLTemplateElement): string {
47
- const html = getParser().parseFromString(
48
- typeof value === 'string' ? value : value.innerHTML,
49
- HTML_PARSE_TYPE,
50
- );
47
+ const parsed = getParser().parseFromString(getHtml(value), PARSE_TYPE_HTML);
51
48
 
52
- html.body.normalize();
49
+ parsed.body.normalize();
53
50
 
54
- sanitizeNodes([html.body], 0);
51
+ sanitizeNodes([parsed.body], 0);
55
52
 
56
- return html.body.innerHTML;
53
+ return parsed.body.innerHTML;
57
54
  }
58
55
 
59
56
  function createTemplate(
@@ -71,6 +68,10 @@ function createTemplate(
71
68
  return template;
72
69
  }
73
70
 
71
+ function getHtml(value: string | HTMLTemplateElement): string {
72
+ return `${TEMPORARY_ELEMENT}${typeof value === 'string' ? value : value.innerHTML}${TEMPORARY_ELEMENT}`;
73
+ }
74
+
74
75
  function getNodes(value: string | HTMLTemplateElement, options: Options): Node[] {
75
76
  if (typeof value !== 'string' && !(value instanceof HTMLTemplateElement)) {
76
77
  return [];
@@ -103,7 +104,7 @@ function getTemplate(
103
104
  return createTemplate(value, options);
104
105
  }
105
106
 
106
- if (typeof value !== 'string' || value.trim().length === 0) {
107
+ if (value.trim().length === 0) {
107
108
  return;
108
109
  }
109
110
 
@@ -115,9 +116,7 @@ function getTemplate(
115
116
 
116
117
  const element = EXPRESSION_ID.test(value) ? document.querySelector(`#${value}`) : null;
117
118
 
118
- template = element instanceof HTMLTemplateElement ? element : createTemplate(value, options);
119
-
120
- return template;
119
+ return createTemplate(element instanceof HTMLTemplateElement ? element : value, options);
121
120
  }
122
121
 
123
122
  const html = ((value: string | HTMLTemplateElement, options?: Options): Node[] => {
@@ -163,10 +162,12 @@ export function sanitize(value: Node | Node[]): Node[] {
163
162
 
164
163
  const EXPRESSION_ID = /^[a-z][\w-]*$/i;
165
164
 
166
- const HTML_PARSE_TYPE = 'text/html';
165
+ const PARSE_TYPE_HTML = 'text/html';
167
166
 
168
167
  const TEMPLATE_TAG = 'template';
169
168
 
169
+ const TEMPORARY_ELEMENT = '<toretto-temporary></toretto-temporary>';
170
+
170
171
  let parser: DOMParser;
171
172
 
172
173
  let templates: Record<string, HTMLTemplateElement> = {};
@@ -1,27 +1,20 @@
1
+ import {setAttribute} from '../attribute/set';
1
2
  import {
2
- isBadAttribute,
3
- isEmptyNonBooleanAttribute,
4
- isInvalidBooleanAttribute,
3
+ _isBadAttribute,
4
+ _isEmptyNonBooleanAttribute,
5
+ _isInvalidBooleanAttribute,
5
6
  } from '../internal/attribute';
6
7
 
7
- function handleElement(element: Element, depth: number): boolean {
8
- if (isClobbered(element)) {
9
- element.remove();
10
-
11
- return true;
12
- }
13
-
8
+ function handleElement(element: Element, depth: number): void {
14
9
  if (depth === 0) {
15
- const scripts = element.querySelectorAll('script');
10
+ const removable = element.querySelectorAll(REMOVE_SELECTOR);
16
11
 
17
- for (const script of scripts) {
18
- script.remove();
12
+ for (const item of removable) {
13
+ item.remove();
19
14
  }
20
15
  }
21
16
 
22
17
  sanitizeAttributes(element, [...element.attributes]);
23
-
24
- return false;
25
18
  }
26
19
 
27
20
  /**
@@ -29,47 +22,75 @@ function handleElement(element: Element, depth: number): boolean {
29
22
  *
30
23
  * Thanks, DOMPurify _(https://github.com/cure53/DOMPurify)_
31
24
  */
32
- function isClobbered(element: Element): boolean {
25
+ function isClobbered(value: unknown): boolean {
33
26
  return (
34
- element instanceof HTMLFormElement &&
35
- (typeof element.nodeName !== 'string' ||
36
- typeof element.textContent !== 'string' ||
37
- typeof element.removeChild !== 'function' ||
38
- !(element.attributes instanceof NamedNodeMap) ||
39
- typeof element.removeAttribute !== 'function' ||
40
- typeof element.setAttribute !== 'function' ||
41
- typeof element.namespaceURI !== 'string' ||
42
- typeof element.insertBefore !== 'function' ||
43
- typeof element.hasChildNodes !== 'function')
27
+ value instanceof HTMLFormElement &&
28
+ (typeof value.nodeName !== 'string' ||
29
+ typeof value.textContent !== 'string' ||
30
+ typeof value.removeChild !== 'function' ||
31
+ !(value.attributes instanceof NamedNodeMap) ||
32
+ typeof value.removeAttribute !== 'function' ||
33
+ typeof value.setAttribute !== 'function' ||
34
+ typeof value.namespaceURI !== 'string' ||
35
+ typeof value.insertBefore !== 'function' ||
36
+ typeof value.hasChildNodes !== 'function')
44
37
  );
45
38
  }
46
39
 
40
+ function removeNode(node: Node): void {
41
+ if (typeof (node as ChildNode).remove === 'function') {
42
+ (node as ChildNode).remove();
43
+ }
44
+ }
45
+
47
46
  export function sanitizeAttributes(element: Element, attributes: Attr[]): void {
48
47
  const {length} = attributes;
49
48
 
50
49
  for (let index = 0; index < length; index += 1) {
51
50
  const {name, value} = attributes[index];
52
51
 
53
- if (isBadAttribute(name, value, false) || isEmptyNonBooleanAttribute(name, value, false)) {
52
+ if (_isBadAttribute(name, value, false) || _isEmptyNonBooleanAttribute(name, value, false)) {
54
53
  element.removeAttribute(name);
55
- } else if (isInvalidBooleanAttribute(name, value, false)) {
56
- element.setAttribute(name, '');
54
+ } else if (_isInvalidBooleanAttribute(name, value, false)) {
55
+ setAttribute(element, name, true);
57
56
  }
58
57
  }
59
58
  }
60
59
 
61
60
  export function sanitizeNodes(nodes: Node[], depth: number): Node[] {
62
61
  const actual = nodes.filter(node => node instanceof Node);
62
+
63
63
  let {length} = nodes;
64
64
 
65
65
  for (let index = 0; index < length; index += 1) {
66
66
  const node = actual[index];
67
67
 
68
- if (node instanceof Element && handleElement(node, depth)) {
68
+ let remove = isClobbered(node);
69
+
70
+ if (!remove) {
71
+ switch (node.nodeType) {
72
+ case Node.ELEMENT_NODE:
73
+ handleElement(node as Element, depth);
74
+ break;
75
+
76
+ case Node.COMMENT_NODE:
77
+ remove = COMMENT_HARMFUL.test((node as Comment).data);
78
+ break;
79
+
80
+ case Node.DOCUMENT_TYPE_NODE:
81
+ case Node.PROCESSING_INSTRUCTION_NODE:
82
+ remove = true;
83
+ break;
84
+ }
85
+ }
86
+
87
+ if (remove) {
88
+ removeNode(node);
89
+
69
90
  actual.splice(index, 1);
70
91
 
71
- length -= 1;
72
92
  index -= 1;
93
+ length -= 1;
73
94
 
74
95
  continue;
75
96
  }
@@ -81,3 +102,9 @@ export function sanitizeNodes(nodes: Node[], depth: number): Node[] {
81
102
 
82
103
  return nodes;
83
104
  }
105
+
106
+ //
107
+
108
+ const COMMENT_HARMFUL = /<[/\w]/g;
109
+
110
+ const REMOVE_SELECTOR = 'script, toretto-temporary';