@oscarpalmer/toretto 0.29.0 → 0.30.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.
@@ -1,4 +1,16 @@
1
1
  import { getAttribute, getAttributes } from "./get.js";
2
- import { booleanAttributes, isBadAttribute, isBooleanAttribute, isEmptyNonBooleanAttribute, isInvalidBooleanAttribute } from "../internal/attribute.js";
2
+ import { booleanAttributes, isBadAttribute as isBadAttribute$1, isBooleanAttribute as isBooleanAttribute$1, isEmptyNonBooleanAttribute as isEmptyNonBooleanAttribute$1, isInvalidBooleanAttribute as isInvalidBooleanAttribute$1 } from "../internal/attribute.js";
3
3
  import { setAttribute, setAttributes, setProperties, setProperty } from "./set.js";
4
+ function isBadAttribute(first, second) {
5
+ return isBadAttribute$1(first, second, true);
6
+ }
7
+ function isBooleanAttribute(first) {
8
+ return isBooleanAttribute$1(first, true);
9
+ }
10
+ function isEmptyNonBooleanAttribute(first, second) {
11
+ return isEmptyNonBooleanAttribute$1(first, second, true);
12
+ }
13
+ function isInvalidBooleanAttribute(first, second) {
14
+ return isInvalidBooleanAttribute$1(first, second, true);
15
+ }
4
16
  export { booleanAttributes, getAttribute, getAttributes, isBadAttribute, isBooleanAttribute, isEmptyNonBooleanAttribute, isInvalidBooleanAttribute, setAttribute, setAttributes, setProperties, setProperty };
@@ -1,15 +1,15 @@
1
- import { sanitizeNodes } from "./internal/sanitize.js";
1
+ import { sanitizeNodes } from "./sanitize.js";
2
2
  import { isPlainObject } from "@oscarpalmer/atoms/is";
3
3
  function createHtml(value) {
4
4
  const html$1 = getParser().parseFromString(typeof value === "string" ? value : value.innerHTML, HTML_PARSE_TYPE);
5
5
  html$1.body.normalize();
6
- sanitizeNodes([html$1.body]);
6
+ sanitizeNodes([html$1.body], 0);
7
7
  return html$1.body.innerHTML;
8
8
  }
9
9
  function createTemplate(value, options) {
10
10
  const template = document.createElement(TEMPLATE_TAG);
11
11
  template.innerHTML = createHtml(value);
12
- if (typeof value === "string" && !options.ignoreCache) templates[value] = template;
12
+ if (typeof value === "string" && options.cache) templates[value] = template;
13
13
  return template;
14
14
  }
15
15
  function getNodes(value, options) {
@@ -19,7 +19,7 @@ function getNodes(value, options) {
19
19
  }
20
20
  function getOptions(input) {
21
21
  const options = isPlainObject(input) ? input : {};
22
- options.ignoreCache = typeof options.ignoreCache === "boolean" ? options.ignoreCache : false;
22
+ options.cache = typeof options.cache === "boolean" ? options.cache : true;
23
23
  return options;
24
24
  }
25
25
  function getParser() {
@@ -53,7 +53,7 @@ html.remove = (template) => {
53
53
  templates = updated;
54
54
  };
55
55
  function sanitize(value) {
56
- return sanitizeNodes(Array.isArray(value) ? value : [value]);
56
+ return sanitizeNodes(Array.isArray(value) ? value : [value], 0);
57
57
  }
58
58
  var EXPRESSION_ID = /^[a-z][\w-]*$/i;
59
59
  var HTML_PARSE_TYPE = "text/html";
@@ -0,0 +1,40 @@
1
+ import { isBadAttribute, isEmptyNonBooleanAttribute, isInvalidBooleanAttribute } from "../internal/attribute.js";
2
+ function handleElement(element, depth) {
3
+ if (isClobbered(element)) {
4
+ element.remove();
5
+ return true;
6
+ }
7
+ if (depth === 0) {
8
+ const scripts = element.querySelectorAll("script");
9
+ for (const script of scripts) script.remove();
10
+ }
11
+ sanitizeAttributes(element, [...element.attributes]);
12
+ return false;
13
+ }
14
+ function isClobbered(element) {
15
+ return element instanceof HTMLFormElement && (typeof element.nodeName !== "string" || typeof element.textContent !== "string" || typeof element.removeChild !== "function" || !(element.attributes instanceof NamedNodeMap) || typeof element.removeAttribute !== "function" || typeof element.setAttribute !== "function" || typeof element.namespaceURI !== "string" || typeof element.insertBefore !== "function" || typeof element.hasChildNodes !== "function");
16
+ }
17
+ function sanitizeAttributes(element, attributes) {
18
+ const { length } = attributes;
19
+ for (let index = 0; index < length; index += 1) {
20
+ const { name, value } = attributes[index];
21
+ if (isBadAttribute(name, value, false) || isEmptyNonBooleanAttribute(name, value, false)) element.removeAttribute(name);
22
+ else if (isInvalidBooleanAttribute(name, value, false)) element.setAttribute(name, "");
23
+ }
24
+ }
25
+ function sanitizeNodes(nodes, depth) {
26
+ const actual = nodes.filter((node) => node instanceof Node);
27
+ let { length } = nodes;
28
+ for (let index = 0; index < length; index += 1) {
29
+ const node = actual[index];
30
+ if (node instanceof Element && handleElement(node, depth)) {
31
+ actual.splice(index, 1);
32
+ length -= 1;
33
+ index -= 1;
34
+ continue;
35
+ }
36
+ if (node.hasChildNodes()) sanitizeNodes([...node.childNodes], depth + 1);
37
+ }
38
+ return nodes;
39
+ }
40
+ export { sanitizeAttributes, sanitizeNodes };
package/dist/index.js CHANGED
@@ -1,15 +1,12 @@
1
1
  import { isEventTarget, isHTMLOrSVGElement } from "./internal/is.js";
2
- import { getAttribute, getAttributes } from "./attribute/get.js";
3
- import { booleanAttributes, isBadAttribute, isBooleanAttribute, isEmptyNonBooleanAttribute, isInvalidBooleanAttribute } from "./internal/attribute.js";
4
- import { setAttribute, setAttributes, setProperties, setProperty } from "./attribute/set.js";
5
- import "./attribute/index.js";
2
+ import { isBadAttribute, isBooleanAttribute, isEmptyNonBooleanAttribute, isInvalidBooleanAttribute } from "./attribute/index.js";
6
3
  import { isChildNode, isInDocument } from "./is.js";
7
4
  import { getData, setData } from "./data.js";
8
5
  import { dispatch, getPosition, off, on } from "./event/index.js";
9
6
  import { findAncestor, findRelatives } from "./find/relative.js";
10
7
  import { $ as findElement, $$ as findElements, getElementUnderPointer } from "./find/index.js";
11
8
  import { getFocusable, getTabbable, isFocusable, isTabbable } from "./focusable.js";
12
- import { html, sanitize } from "./html.js";
9
+ import { html, sanitize } from "./html/index.js";
13
10
  import touch_default from "./touch.js";
14
11
  import { getStyle, getStyles, getTextDirection, setStyle, setStyles, toggleStyles } from "./style.js";
15
- export { findElement as $, findElement, findElements as $$, findElements, booleanAttributes, dispatch, findAncestor, findRelatives, getAttribute, getAttributes, getData, getElementUnderPointer, getFocusable, getPosition, getStyle, getStyles, getTabbable, getTextDirection, html, isBadAttribute, isBooleanAttribute, isChildNode, isEmptyNonBooleanAttribute, isEventTarget, isFocusable, isHTMLOrSVGElement, isInDocument, isInvalidBooleanAttribute, isTabbable, off, on, sanitize, setAttribute, setAttributes, setData, setProperties, setProperty, setStyle, setStyles, touch_default as supportsTouch, toggleStyles };
12
+ export { findElement as $, findElement, findElements as $$, findElements, dispatch, findAncestor, findRelatives, getData, getElementUnderPointer, getFocusable, getPosition, getStyle, getStyles, getTabbable, getTextDirection, html, isBadAttribute, isBooleanAttribute, isChildNode, isEmptyNonBooleanAttribute, isEventTarget, isFocusable, isHTMLOrSVGElement, isInDocument, isInvalidBooleanAttribute, isTabbable, off, on, sanitize, setData, setStyle, setStyles, touch_default as supportsTouch, toggleStyles };
@@ -1,40 +1,59 @@
1
1
  import { isHTMLOrSVGElement } from "./is.js";
2
2
  import { getString } from "@oscarpalmer/atoms/string";
3
3
  import { isPlainObject } from "@oscarpalmer/atoms/is";
4
+ function badAttributeHandler(name, value) {
5
+ if (name == null || value == null) return true;
6
+ if (EXPRESSION_CLOBBERED_NAME.test(name) && (value in document || value in formElement) || EXPRESSION_EVENT_NAME.test(name)) return true;
7
+ if (EXPRESSION_SKIP_NAME.test(name) || EXPRESSION_URI_VALUE.test(value) || isValidSourceAttribute(name, value)) return false;
8
+ return EXPRESSION_DATA_OR_SCRIPT.test(value);
9
+ }
10
+ function booleanAttributeHandler(name, value) {
11
+ if (name == null || value == null) return true;
12
+ if (!booleanAttributesSet.has(name)) return false;
13
+ const normalized = value.toLowerCase().trim();
14
+ return !(normalized.length === 0 || normalized === name);
15
+ }
16
+ function decodeAttribute(value) {
17
+ textArea ??= document.createElement("textarea");
18
+ textArea.innerHTML = value;
19
+ return decodeURIComponent(textArea.value);
20
+ }
21
+ function handleAttribute(callback, decode, first, second) {
22
+ let name;
23
+ let value;
24
+ if (isAttribute(first)) {
25
+ name = first.name;
26
+ value = String(first.value);
27
+ } else if (typeof first === "string" && typeof second === "string") {
28
+ name = first;
29
+ value = second;
30
+ }
31
+ if (decode && value != null) value = decodeAttribute(value);
32
+ return callback(name, value?.replace(EXPRESSION_WHITESPACE, ""));
33
+ }
4
34
  function isAttribute(value) {
5
35
  return value instanceof Attr || isPlainObject(value) && typeof value.name === "string" && typeof value.value === "string";
6
36
  }
7
- function isBadAttribute(first, second) {
8
- return isValidAttribute((attribute) => attribute == null || EXPRESSION_ON_PREFIX.test(attribute.name) || EXPRESSION_SOURCE_PREFIX.test(attribute.name) && EXPRESSION_VALUE_PREFIX.test(String(attribute.value)), first, second);
37
+ function isBadAttribute(first, second, decode) {
38
+ return handleAttribute(badAttributeHandler, decode, first, second);
9
39
  }
10
- function isBooleanAttribute(value) {
11
- return isValidAttribute((attribute) => attribute != null && booleanAttributes.includes(attribute.name.toLowerCase()), value, "");
40
+ function isBooleanAttribute(first, decode) {
41
+ return handleAttribute((name) => booleanAttributesSet.has(name?.toLowerCase()), decode, first, "");
12
42
  }
13
- function isEmptyNonBooleanAttribute(first, second) {
14
- return isValidAttribute((attribute) => attribute != null && !booleanAttributes.includes(attribute.name) && String(attribute.value).trim().length === 0, first, second);
43
+ function isEmptyNonBooleanAttribute(first, second, decode) {
44
+ return handleAttribute((name, value) => name != null && value != null && !booleanAttributesSet.has(name) && value.trim().length === 0, decode, first, second);
15
45
  }
16
- function isInvalidBooleanAttribute(first, second) {
17
- return isValidAttribute((attribute) => {
18
- if (attribute == null) return true;
19
- if (!booleanAttributes.includes(attribute.name)) return false;
20
- const normalized = String(attribute.value).toLowerCase().trim();
21
- return !(normalized.length === 0 || normalized === attribute.name);
22
- }, first, second);
46
+ function isInvalidBooleanAttribute(first, second, decode) {
47
+ return handleAttribute(booleanAttributeHandler, decode, first, second);
23
48
  }
24
49
  function isProperty(value) {
25
50
  return isPlainObject(value) && typeof value.name === "string";
26
51
  }
27
- function isValidAttribute(callback, first, second) {
28
- let attribute;
29
- if (isAttribute(first)) attribute = first;
30
- else if (typeof first === "string" && typeof second === "string") attribute = {
31
- name: first,
32
- value: second
33
- };
34
- return callback(attribute);
52
+ function isValidSourceAttribute(name, value) {
53
+ return EXPRESSION_SOURCE_NAME.test(name) && EXPRESSION_SOURCE_VALUE.test(value);
35
54
  }
36
55
  function updateAttribute(element, name, value) {
37
- const isBoolean = booleanAttributes.includes(name.toLowerCase());
56
+ const isBoolean = booleanAttributesSet.has(name.toLowerCase());
38
57
  if (isBoolean) updateProperty(element, name, value);
39
58
  if (isBoolean ? value !== true : value == null) element.removeAttribute(name);
40
59
  else element.setAttribute(name, isBoolean ? "" : getString(value));
@@ -59,9 +78,14 @@ function updateValues(element, values) {
59
78
  else updateAttribute(element, entry[0], entry[1]);
60
79
  }
61
80
  }
62
- var EXPRESSION_ON_PREFIX = /^on/i;
63
- var EXPRESSION_SOURCE_PREFIX = /^(href|src|xlink:href)$/i;
64
- var EXPRESSION_VALUE_PREFIX = /(data:text\/html|javascript:)/i;
81
+ var EXPRESSION_CLOBBERED_NAME = /^(id|name)$/i;
82
+ var EXPRESSION_DATA_OR_SCRIPT = /^(?:data|\w+script):/i;
83
+ var EXPRESSION_EVENT_NAME = /^on/i;
84
+ var EXPRESSION_SKIP_NAME = /^(aria-[-\w]+|data-[-\w.\u00B7-\uFFFF]+)$/i;
85
+ var EXPRESSION_SOURCE_NAME = /^src$/i;
86
+ var EXPRESSION_SOURCE_VALUE = /^data:/i;
87
+ var EXPRESSION_URI_VALUE = /^(?:(?:(?:f|ht)tps?|mailto|tel|callto|sms|cid|xmpp|matrix):|[^a-z]|[a-z+.-]+(?:[^a-z+.\-:]|$))/i;
88
+ var EXPRESSION_WHITESPACE = /[\u0000-\u0020\u00A0\u1680\u180E\u2000-\u2029\u205F\u3000]/g;
65
89
  const booleanAttributes = Object.freeze([
66
90
  "async",
67
91
  "autofocus",
@@ -88,4 +112,7 @@ const booleanAttributes = Object.freeze([
88
112
  "reversed",
89
113
  "selected"
90
114
  ]);
115
+ var booleanAttributesSet = new Set(booleanAttributes);
116
+ var formElement = document.createElement("form");
117
+ var textArea;
91
118
  export { booleanAttributes, isBadAttribute, isBooleanAttribute, isEmptyNonBooleanAttribute, isInvalidBooleanAttribute, isProperty, updateValue, updateValues };