|
|
|
|
@ -5,9 +5,15 @@ import { init_array_prototype_warnings } from '../dev/equality.js';
|
|
|
|
|
import { get_descriptor, is_extensible } from '../../shared/utils.js';
|
|
|
|
|
import { active_effect } from '../runtime.js';
|
|
|
|
|
import { async_mode_flag } from '../../flags/index.js';
|
|
|
|
|
import { TEXT_NODE, REACTION_RAN } from '#client/constants';
|
|
|
|
|
import {
|
|
|
|
|
TEXT_NODE,
|
|
|
|
|
REACTION_RAN,
|
|
|
|
|
CUSTOM_RENDERER_NODE_TYPE_MAP,
|
|
|
|
|
COMMENT_NODE
|
|
|
|
|
} from '#client/constants';
|
|
|
|
|
import { eager_block_effects } from '../reactivity/batch.js';
|
|
|
|
|
import { NAMESPACE_HTML } from '../../../constants.js';
|
|
|
|
|
import { renderer } from '../custom-renderer/state.js';
|
|
|
|
|
|
|
|
|
|
// export these for reference in the compiled code, making global name deduplication unnecessary
|
|
|
|
|
/** @type {Window} */
|
|
|
|
|
@ -78,6 +84,7 @@ export function init_operations() {
|
|
|
|
|
* @returns {Text}
|
|
|
|
|
*/
|
|
|
|
|
export function create_text(value = '') {
|
|
|
|
|
if (renderer) return /** @type {Text} */ (renderer.createTextNode(value));
|
|
|
|
|
return document.createTextNode(value);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@ -87,6 +94,7 @@ export function create_text(value = '') {
|
|
|
|
|
*/
|
|
|
|
|
/*@__NO_SIDE_EFFECTS__*/
|
|
|
|
|
export function get_first_child(node) {
|
|
|
|
|
if (renderer) return /** @type {TemplateNode | null} */ (renderer.getFirstChild(node));
|
|
|
|
|
return /** @type {TemplateNode | null} */ (first_child_getter.call(node));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@ -96,6 +104,7 @@ export function get_first_child(node) {
|
|
|
|
|
*/
|
|
|
|
|
/*@__NO_SIDE_EFFECTS__*/
|
|
|
|
|
export function get_next_sibling(node) {
|
|
|
|
|
if (renderer) return /** @type {TemplateNode | null} */ (renderer.getNextSibling(node));
|
|
|
|
|
return /** @type {TemplateNode | null} */ (next_sibling_getter.call(node));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@ -115,10 +124,10 @@ export function child(node, is_text) {
|
|
|
|
|
|
|
|
|
|
// Child can be null if we have an element with a single child, like `<p>{text}</p>`, where `text` is empty
|
|
|
|
|
if (child === null) {
|
|
|
|
|
child = hydrate_node.appendChild(create_text());
|
|
|
|
|
} else if (is_text && child.nodeType !== TEXT_NODE) {
|
|
|
|
|
child = /** @type {TemplateNode} */ (append_child(hydrate_node, create_text()));
|
|
|
|
|
} else if (is_text && node_type(child) !== TEXT_NODE) {
|
|
|
|
|
var text = create_text();
|
|
|
|
|
child?.before(text);
|
|
|
|
|
insert_before(child, text);
|
|
|
|
|
set_hydrate_node(text);
|
|
|
|
|
return text;
|
|
|
|
|
}
|
|
|
|
|
@ -142,7 +151,8 @@ export function first_child(node, is_text = false) {
|
|
|
|
|
var first = get_first_child(node);
|
|
|
|
|
|
|
|
|
|
// TODO prevent user comments with the empty string when preserveComments is true
|
|
|
|
|
if (first instanceof Comment && first.data === '') return get_next_sibling(first);
|
|
|
|
|
// TODO RENDERER: can we find another way to not have an `isComment` on the renderer?
|
|
|
|
|
if (is_comment(first) && get_node_value(first) === '') return get_next_sibling(first);
|
|
|
|
|
|
|
|
|
|
return first;
|
|
|
|
|
}
|
|
|
|
|
@ -150,10 +160,10 @@ export function first_child(node, is_text = false) {
|
|
|
|
|
if (is_text) {
|
|
|
|
|
// if an {expression} is empty during SSR, there might be no
|
|
|
|
|
// text node to hydrate — we must therefore create one
|
|
|
|
|
if (hydrate_node?.nodeType !== TEXT_NODE) {
|
|
|
|
|
if (node_type(hydrate_node) !== TEXT_NODE) {
|
|
|
|
|
var text = create_text();
|
|
|
|
|
|
|
|
|
|
hydrate_node?.before(text);
|
|
|
|
|
if (hydrate_node) insert_before(hydrate_node, text);
|
|
|
|
|
set_hydrate_node(text);
|
|
|
|
|
return text;
|
|
|
|
|
}
|
|
|
|
|
@ -187,15 +197,15 @@ export function sibling(node, count = 1, is_text = false) {
|
|
|
|
|
if (is_text) {
|
|
|
|
|
// if a sibling {expression} is empty during SSR, there might be no
|
|
|
|
|
// text node to hydrate — we must therefore create one
|
|
|
|
|
if (next_sibling?.nodeType !== TEXT_NODE) {
|
|
|
|
|
if (node_type(next_sibling) !== TEXT_NODE) {
|
|
|
|
|
var text = create_text();
|
|
|
|
|
// If the next sibling is `null` and we're handling text then it's because
|
|
|
|
|
// the SSR content was empty for the text, so we need to generate a new text
|
|
|
|
|
// node and insert it after the last sibling
|
|
|
|
|
if (next_sibling === null) {
|
|
|
|
|
last_sibling?.after(text);
|
|
|
|
|
if (last_sibling) insert_after(last_sibling, text);
|
|
|
|
|
} else {
|
|
|
|
|
next_sibling.before(text);
|
|
|
|
|
insert_before(next_sibling, text);
|
|
|
|
|
}
|
|
|
|
|
set_hydrate_node(text);
|
|
|
|
|
return text;
|
|
|
|
|
@ -214,6 +224,15 @@ export function sibling(node, count = 1, is_text = false) {
|
|
|
|
|
* @returns {void}
|
|
|
|
|
*/
|
|
|
|
|
export function clear_text_content(node) {
|
|
|
|
|
if (renderer) {
|
|
|
|
|
var child = renderer.getFirstChild(node);
|
|
|
|
|
while (child !== null) {
|
|
|
|
|
var next = renderer.getNextSibling(child);
|
|
|
|
|
renderer.remove(child);
|
|
|
|
|
child = next;
|
|
|
|
|
}
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
node.textContent = '';
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@ -239,6 +258,10 @@ export function should_defer_append() {
|
|
|
|
|
* @returns {T extends keyof HTMLElementTagNameMap ? HTMLElementTagNameMap[T] : Element}
|
|
|
|
|
*/
|
|
|
|
|
export function create_element(tag, namespace, is) {
|
|
|
|
|
if (renderer)
|
|
|
|
|
return /** @type {T extends keyof HTMLElementTagNameMap ? HTMLElementTagNameMap[T] : Element} */ (
|
|
|
|
|
renderer.createElement(tag)
|
|
|
|
|
);
|
|
|
|
|
let options = is ? { is } : undefined;
|
|
|
|
|
return /** @type {T extends keyof HTMLElementTagNameMap ? HTMLElementTagNameMap[T] : Element} */ (
|
|
|
|
|
document.createElementNS(namespace ?? NAMESPACE_HTML, tag, options)
|
|
|
|
|
@ -246,6 +269,7 @@ export function create_element(tag, namespace, is) {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export function create_fragment() {
|
|
|
|
|
if (renderer) return /** @type {DocumentFragment} */ (renderer.createFragment());
|
|
|
|
|
return document.createDocumentFragment();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@ -254,6 +278,7 @@ export function create_fragment() {
|
|
|
|
|
* @returns
|
|
|
|
|
*/
|
|
|
|
|
export function create_comment(data = '') {
|
|
|
|
|
if (renderer) return /** @type {Comment} */ (renderer.createComment(data));
|
|
|
|
|
return document.createComment(data);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@ -264,6 +289,10 @@ export function create_comment(data = '') {
|
|
|
|
|
* @returns
|
|
|
|
|
*/
|
|
|
|
|
export function set_attribute(element, key, value = '') {
|
|
|
|
|
if (renderer) {
|
|
|
|
|
renderer.setAttribute(element, key, value);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
if (key.startsWith('xlink:')) {
|
|
|
|
|
element.setAttributeNS('http://www.w3.org/1999/xlink', key, value);
|
|
|
|
|
return;
|
|
|
|
|
@ -277,13 +306,16 @@ export function set_attribute(element, key, value = '') {
|
|
|
|
|
* @param {Text} text
|
|
|
|
|
*/
|
|
|
|
|
export function merge_text_nodes(text) {
|
|
|
|
|
// if we have a renderer we will not hydrate so we can skip this
|
|
|
|
|
if (renderer) return;
|
|
|
|
|
|
|
|
|
|
if (/** @type {string} */ (text.nodeValue).length < 65536) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
let next = text.nextSibling;
|
|
|
|
|
|
|
|
|
|
while (next !== null && next.nodeType === TEXT_NODE) {
|
|
|
|
|
while (next !== null && node_type(next) === TEXT_NODE) {
|
|
|
|
|
next.remove();
|
|
|
|
|
|
|
|
|
|
/** @type {string} */ (text.nodeValue) += /** @type {string} */ (next.nodeValue);
|
|
|
|
|
@ -291,3 +323,438 @@ export function merge_text_nodes(text) {
|
|
|
|
|
next = text.nextSibling;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @param {TemplateNode | null} node
|
|
|
|
|
* @returns {node is Comment}
|
|
|
|
|
*/
|
|
|
|
|
function is_comment(node) {
|
|
|
|
|
if (renderer) return !!node && node_type(node) === COMMENT_NODE;
|
|
|
|
|
return node instanceof Comment;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @param {Node | null | undefined} node
|
|
|
|
|
*/
|
|
|
|
|
export function node_type(node) {
|
|
|
|
|
if (node == null) return undefined;
|
|
|
|
|
if (renderer) {
|
|
|
|
|
const type = renderer.nodeType(node);
|
|
|
|
|
return CUSTOM_RENDERER_NODE_TYPE_MAP[type];
|
|
|
|
|
}
|
|
|
|
|
return node?.nodeType;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @param {Node | null | undefined} node
|
|
|
|
|
*/
|
|
|
|
|
export function node_name(node) {
|
|
|
|
|
if (node == null) return undefined;
|
|
|
|
|
if (renderer) {
|
|
|
|
|
return '';
|
|
|
|
|
}
|
|
|
|
|
return node?.nodeName;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @param {Node} node
|
|
|
|
|
* @returns {TemplateNode | null}
|
|
|
|
|
*/
|
|
|
|
|
export function get_last_child(node) {
|
|
|
|
|
if (renderer) return /** @type {TemplateNode | null} */ (renderer.getLastChild(node));
|
|
|
|
|
return /** @type {TemplateNode | null} */ (node.lastChild);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @param {Node} node
|
|
|
|
|
* @returns {TemplateNode | null}
|
|
|
|
|
*/
|
|
|
|
|
export function get_parent_node(node) {
|
|
|
|
|
if (renderer) return /** @type {TemplateNode | null} */ (renderer.getParent(node));
|
|
|
|
|
return /** @type {TemplateNode | null} */ (node.parentNode);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @param {Node} parent
|
|
|
|
|
* @param {Node} child
|
|
|
|
|
* @returns {Node}
|
|
|
|
|
*/
|
|
|
|
|
export function append_child(parent, child) {
|
|
|
|
|
if (renderer) {
|
|
|
|
|
renderer.insert(parent, child, null);
|
|
|
|
|
return child;
|
|
|
|
|
}
|
|
|
|
|
return parent.appendChild(child);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Insert `new_node` before `ref_node` (equivalent to `ref_node.before(new_node)`)
|
|
|
|
|
* @param {ChildNode} ref_node
|
|
|
|
|
* @param {Node} new_node
|
|
|
|
|
*/
|
|
|
|
|
export function insert_before(ref_node, new_node) {
|
|
|
|
|
if (renderer) {
|
|
|
|
|
var parent = renderer.getParent(ref_node);
|
|
|
|
|
renderer.insert(parent, new_node, ref_node);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
ref_node.before(new_node);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Insert `new_node` after `ref_node` (equivalent to `ref_node.after(new_node)`)
|
|
|
|
|
* @param {ChildNode} ref_node
|
|
|
|
|
* @param {Node} new_node
|
|
|
|
|
*/
|
|
|
|
|
export function insert_after(ref_node, new_node) {
|
|
|
|
|
if (renderer) {
|
|
|
|
|
var parent = renderer.getParent(ref_node);
|
|
|
|
|
var next = renderer.getNextSibling(ref_node);
|
|
|
|
|
renderer.insert(parent, new_node, next);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
ref_node.after(new_node);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @param {ChildNode} node
|
|
|
|
|
*/
|
|
|
|
|
export function remove_node(node) {
|
|
|
|
|
if (renderer) {
|
|
|
|
|
renderer.remove(node);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
node.remove();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @param {Node} parent
|
|
|
|
|
* @param {ChildNode} child
|
|
|
|
|
* @returns {ChildNode}
|
|
|
|
|
*/
|
|
|
|
|
export function remove_child(parent, child) {
|
|
|
|
|
if (renderer) {
|
|
|
|
|
renderer.remove(child);
|
|
|
|
|
return child;
|
|
|
|
|
}
|
|
|
|
|
return /** @type {ChildNode} */ (parent.removeChild(child));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @param {ChildNode} old_node
|
|
|
|
|
* @param {Node} new_node
|
|
|
|
|
*/
|
|
|
|
|
export function replace_with(old_node, new_node) {
|
|
|
|
|
if (renderer) {
|
|
|
|
|
var parent = renderer.getParent(old_node);
|
|
|
|
|
renderer.insert(parent, new_node, old_node);
|
|
|
|
|
renderer.remove(old_node);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
old_node.replaceWith(new_node);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @param {Node} node
|
|
|
|
|
* @param {string} value
|
|
|
|
|
*/
|
|
|
|
|
export function set_text_content(node, value) {
|
|
|
|
|
if (renderer) {
|
|
|
|
|
renderer.setText(node, value);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
node.textContent = value;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @param {Node} node
|
|
|
|
|
* @param {string} value
|
|
|
|
|
*/
|
|
|
|
|
export function set_node_value(node, value) {
|
|
|
|
|
if (renderer) {
|
|
|
|
|
renderer.setText(node, value);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
node.nodeValue = value;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// --- Helpers for style attribute string manipulation (custom renderer) ---
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @param {string} style_string
|
|
|
|
|
* @param {string} property
|
|
|
|
|
* @param {string} value
|
|
|
|
|
* @param {string} [priority]
|
|
|
|
|
* @returns {string}
|
|
|
|
|
*/
|
|
|
|
|
function set_style_property_in_string(style_string, property, value, priority) {
|
|
|
|
|
var declaration = property + ': ' + value + (priority ? ' !' + priority : '');
|
|
|
|
|
var parts = style_string.split(';');
|
|
|
|
|
var found = false;
|
|
|
|
|
|
|
|
|
|
for (var i = 0; i < parts.length; i++) {
|
|
|
|
|
var colon_index = parts[i].indexOf(':');
|
|
|
|
|
if (colon_index !== -1 && parts[i].substring(0, colon_index).trim() === property) {
|
|
|
|
|
parts[i] = ' ' + declaration;
|
|
|
|
|
found = true;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!found) {
|
|
|
|
|
parts.push(' ' + declaration);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return parts
|
|
|
|
|
.map((p) => p.trim())
|
|
|
|
|
.filter(Boolean)
|
|
|
|
|
.join('; ');
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @param {string} style_string
|
|
|
|
|
* @param {string} property
|
|
|
|
|
* @returns {string}
|
|
|
|
|
*/
|
|
|
|
|
function remove_style_property_in_string(style_string, property) {
|
|
|
|
|
return style_string
|
|
|
|
|
.split(';')
|
|
|
|
|
.filter((part) => {
|
|
|
|
|
var colon_index = part.indexOf(':');
|
|
|
|
|
if (colon_index === -1) return false;
|
|
|
|
|
return part.substring(0, colon_index).trim() !== property;
|
|
|
|
|
})
|
|
|
|
|
.map((p) => p.trim())
|
|
|
|
|
.filter(Boolean)
|
|
|
|
|
.join('; ');
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @param {Node} node
|
|
|
|
|
* @returns {string | null}
|
|
|
|
|
*/
|
|
|
|
|
export function get_node_value(node) {
|
|
|
|
|
if (renderer) return renderer.getNodeValue(node);
|
|
|
|
|
return node.nodeValue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @param {Element} element
|
|
|
|
|
* @param {string} name
|
|
|
|
|
* @returns {string | null}
|
|
|
|
|
*/
|
|
|
|
|
export function get_attribute(element, name) {
|
|
|
|
|
if (renderer) return renderer.getAttribute(element, name);
|
|
|
|
|
return element.getAttribute(name);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @param {Element} element
|
|
|
|
|
* @param {string} name
|
|
|
|
|
*/
|
|
|
|
|
export function remove_attribute(element, name) {
|
|
|
|
|
if (renderer) {
|
|
|
|
|
renderer.removeAttribute(element, name);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
element.removeAttribute(name);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @param {Element} element
|
|
|
|
|
* @param {string} name
|
|
|
|
|
* @returns {boolean}
|
|
|
|
|
*/
|
|
|
|
|
export function has_attribute(element, name) {
|
|
|
|
|
// TODO RENDERER: could be worth removing and just using get?
|
|
|
|
|
|
|
|
|
|
if (renderer) return renderer.hasAttribute(element, name);
|
|
|
|
|
return element.hasAttribute(name);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @param {Element} element
|
|
|
|
|
* @param {string} value
|
|
|
|
|
*/
|
|
|
|
|
export function set_inner_html(element, value) {
|
|
|
|
|
if (renderer) {
|
|
|
|
|
throw new Error('setInnerHTML is not supported with custom renderers');
|
|
|
|
|
}
|
|
|
|
|
element.innerHTML = value;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @param {Node} node
|
|
|
|
|
* @param {boolean} deep
|
|
|
|
|
* @returns {Node}
|
|
|
|
|
*/
|
|
|
|
|
export function clone_node(node, deep) {
|
|
|
|
|
if (renderer) return renderer.cloneNode(node, deep);
|
|
|
|
|
return node.cloneNode(deep);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @param {Node} node
|
|
|
|
|
* @param {boolean} deep
|
|
|
|
|
* @returns {Node}
|
|
|
|
|
*/
|
|
|
|
|
export function import_node(node, deep) {
|
|
|
|
|
if (renderer) return renderer.cloneNode(node, deep);
|
|
|
|
|
return document.importNode(node, deep);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @param {EventTarget} target
|
|
|
|
|
* @param {string} type
|
|
|
|
|
* @param {EventListenerOrEventListenerObject} handler
|
|
|
|
|
* @param {boolean | AddEventListenerOptions} [options]
|
|
|
|
|
*/
|
|
|
|
|
export function add_event_listener(target, type, handler, options) {
|
|
|
|
|
if (renderer) {
|
|
|
|
|
renderer.addEventListener(target, type, handler, options);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
target.addEventListener(type, handler, options);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @param {EventTarget} target
|
|
|
|
|
* @param {string} type
|
|
|
|
|
* @param {EventListenerOrEventListenerObject} handler
|
|
|
|
|
* @param {boolean | EventListenerOptions} [options]
|
|
|
|
|
*/
|
|
|
|
|
export function remove_event_listener(target, type, handler, options) {
|
|
|
|
|
if (renderer) {
|
|
|
|
|
renderer.removeEventListener(target, type, handler, options);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
target.removeEventListener(type, handler, options);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @param {EventTarget} target
|
|
|
|
|
* @param {Event} event
|
|
|
|
|
* @returns {boolean}
|
|
|
|
|
*/
|
|
|
|
|
export function dispatch_event(target, event) {
|
|
|
|
|
if (renderer) {
|
|
|
|
|
// is only used in SSR which is not a thing for custom renderers
|
|
|
|
|
throw new Error('dispatchEvent is not supported with custom renderers');
|
|
|
|
|
}
|
|
|
|
|
return target.dispatchEvent(event);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @param {HTMLElement} element
|
|
|
|
|
* @param {string} property
|
|
|
|
|
* @param {string} value
|
|
|
|
|
* @param {string} [priority]
|
|
|
|
|
*/
|
|
|
|
|
export function style_set_property(element, property, value, priority) {
|
|
|
|
|
if (renderer) {
|
|
|
|
|
var style = renderer.getAttribute(element, 'style') || '';
|
|
|
|
|
var updated = set_style_property_in_string(style, property, value, priority);
|
|
|
|
|
renderer.setAttribute(element, 'style', updated);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
element.style.setProperty(property, value, priority);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @param {HTMLElement} element
|
|
|
|
|
* @param {string} property
|
|
|
|
|
*/
|
|
|
|
|
export function style_remove_property(element, property) {
|
|
|
|
|
if (renderer) {
|
|
|
|
|
var style = renderer.getAttribute(element, 'style') || '';
|
|
|
|
|
var updated = remove_style_property_in_string(style, property);
|
|
|
|
|
renderer.setAttribute(element, 'style', updated);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
element.style.removeProperty(property);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @param {HTMLElement} element
|
|
|
|
|
* @param {string} value
|
|
|
|
|
*/
|
|
|
|
|
export function set_css_text(element, value) {
|
|
|
|
|
if (renderer) {
|
|
|
|
|
renderer.setAttribute(element, 'style', value);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
element.style.cssText = value;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @param {Element} element
|
|
|
|
|
* @param {string} name
|
|
|
|
|
* @param {boolean} force
|
|
|
|
|
*/
|
|
|
|
|
export function class_list_toggle(element, name, force) {
|
|
|
|
|
if (renderer) {
|
|
|
|
|
const classes = element.getAttribute('class')?.split(/\s+/) ?? [];
|
|
|
|
|
const has_class = classes.includes(name);
|
|
|
|
|
if (force === has_class) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
if (force) {
|
|
|
|
|
classes.push(name);
|
|
|
|
|
} else {
|
|
|
|
|
const index = classes.indexOf(name);
|
|
|
|
|
if (index !== -1) {
|
|
|
|
|
classes.splice(index, 1);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
element.setAttribute('class', classes.join(' '));
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
element.classList.toggle(name, force);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @param {Element} element
|
|
|
|
|
* @param {string} selector
|
|
|
|
|
*/
|
|
|
|
|
export function query_selector(element, selector) {
|
|
|
|
|
if (renderer) {
|
|
|
|
|
// TODO RENDERER: just used with css inject which doesn't make sense with custom renderers
|
|
|
|
|
throw new Error('querySelector is not supported with custom renderers');
|
|
|
|
|
}
|
|
|
|
|
return element.querySelector(selector);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @param {Element | DocumentFragment} element
|
|
|
|
|
* @param {string} selector
|
|
|
|
|
*/
|
|
|
|
|
export function query_selector_all(element, selector) {
|
|
|
|
|
if (renderer) {
|
|
|
|
|
// TODO RENDERER: just used with css inject which doesn't make sense with custom renderers
|
|
|
|
|
throw new Error('querySelectorAll is not supported with custom renderers');
|
|
|
|
|
}
|
|
|
|
|
return element.querySelectorAll(selector);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @param {Node} node
|
|
|
|
|
* @returns {Node}
|
|
|
|
|
*/
|
|
|
|
|
export function get_root_node(node) {
|
|
|
|
|
if (renderer) {
|
|
|
|
|
// TODO RENDERER: just used with css inject which doesn't make sense with custom renderers
|
|
|
|
|
throw new Error('getRootNode is not supported with custom renderers');
|
|
|
|
|
}
|
|
|
|
|
return node.getRootNode();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @param {HTMLElement} element
|
|
|
|
|
*/
|
|
|
|
|
export function focus(element) {
|
|
|
|
|
if (renderer) {
|
|
|
|
|
// doesn't make sense with custom renderers
|
|
|
|
|
throw new Error('focus is not supported with custom renderers');
|
|
|
|
|
}
|
|
|
|
|
element.focus();
|
|
|
|
|
}
|
|
|
|
|
|