Spaces:
Runtime error
Runtime error
/** @returns {void} */ | |
function noop() {} | |
const identity = (x) => x; | |
/** | |
* @template T | |
* @template S | |
* @param {T} tar | |
* @param {S} src | |
* @returns {T & S} | |
*/ | |
function assign(tar, src) { | |
// @ts-ignore | |
for (const k in src) tar[k] = src[k]; | |
return /** @type {T & S} */ (tar); | |
} | |
// Adapted from https://github.com/then/is-promise/blob/master/index.js | |
// Distributed under MIT License https://github.com/then/is-promise/blob/master/LICENSE | |
/** | |
* @param {any} value | |
* @returns {value is PromiseLike<any>} | |
*/ | |
function is_promise(value) { | |
return ( | |
!!value && | |
(typeof value === 'object' || typeof value === 'function') && | |
typeof (/** @type {any} */ (value).then) === 'function' | |
); | |
} | |
/** @returns {void} */ | |
function add_location(element, file, line, column, char) { | |
element.__svelte_meta = { | |
loc: { file, line, column, char } | |
}; | |
} | |
function run(fn) { | |
return fn(); | |
} | |
function blank_object() { | |
return Object.create(null); | |
} | |
/** | |
* @param {Function[]} fns | |
* @returns {void} | |
*/ | |
function run_all(fns) { | |
fns.forEach(run); | |
} | |
/** | |
* @param {any} thing | |
* @returns {thing is Function} | |
*/ | |
function is_function(thing) { | |
return typeof thing === 'function'; | |
} | |
/** @returns {boolean} */ | |
function safe_not_equal(a, b) { | |
return a != a ? b == b : a !== b || (a && typeof a === 'object') || typeof a === 'function'; | |
} | |
let src_url_equal_anchor; | |
/** | |
* @param {string} element_src | |
* @param {string} url | |
* @returns {boolean} | |
*/ | |
function src_url_equal(element_src, url) { | |
if (element_src === url) return true; | |
if (!src_url_equal_anchor) { | |
src_url_equal_anchor = document.createElement('a'); | |
} | |
// This is actually faster than doing URL(..).href | |
src_url_equal_anchor.href = url; | |
return element_src === src_url_equal_anchor.href; | |
} | |
/** @param {string} srcset */ | |
function split_srcset(srcset) { | |
return srcset.split(',').map((src) => src.trim().split(' ').filter(Boolean)); | |
} | |
/** | |
* @param {HTMLSourceElement | HTMLImageElement} element_srcset | |
* @param {string | undefined | null} srcset | |
* @returns {boolean} | |
*/ | |
function srcset_url_equal(element_srcset, srcset) { | |
const element_urls = split_srcset(element_srcset.srcset); | |
const urls = split_srcset(srcset || ''); | |
return ( | |
urls.length === element_urls.length && | |
urls.every( | |
([url, width], i) => | |
width === element_urls[i][1] && | |
// We need to test both ways because Vite will create an a full URL with | |
// `new URL(asset, import.meta.url).href` for the client when `base: './'`, and the | |
// relative URLs inside srcset are not automatically resolved to absolute URLs by | |
// browsers (in contrast to img.src). This means both SSR and DOM code could | |
// contain relative or absolute URLs. | |
(src_url_equal(element_urls[i][0], url) || src_url_equal(url, element_urls[i][0])) | |
) | |
); | |
} | |
/** @returns {boolean} */ | |
function not_equal(a, b) { | |
return a != a ? b == b : a !== b; | |
} | |
/** @returns {boolean} */ | |
function is_empty(obj) { | |
return Object.keys(obj).length === 0; | |
} | |
/** @returns {void} */ | |
function validate_store(store, name) { | |
if (store != null && typeof store.subscribe !== 'function') { | |
throw new Error(`'${name}' is not a store with a 'subscribe' method`); | |
} | |
} | |
function subscribe(store, ...callbacks) { | |
if (store == null) { | |
for (const callback of callbacks) { | |
callback(undefined); | |
} | |
return noop; | |
} | |
const unsub = store.subscribe(...callbacks); | |
return unsub.unsubscribe ? () => unsub.unsubscribe() : unsub; | |
} | |
/** | |
* Get the current value from a store by subscribing and immediately unsubscribing. | |
* | |
* https://svelte.dev/docs/svelte-store#get | |
* @template T | |
* @param {import('../store/public.js').Readable<T>} store | |
* @returns {T} | |
*/ | |
function get_store_value(store) { | |
let value; | |
subscribe(store, (_) => (value = _))(); | |
return value; | |
} | |
/** @returns {void} */ | |
function component_subscribe(component, store, callback) { | |
component.$$.on_destroy.push(subscribe(store, callback)); | |
} | |
function create_slot(definition, ctx, $$scope, fn) { | |
if (definition) { | |
const slot_ctx = get_slot_context(definition, ctx, $$scope, fn); | |
return definition[0](slot_ctx); | |
} | |
} | |
function get_slot_context(definition, ctx, $$scope, fn) { | |
return definition[1] && fn ? assign($$scope.ctx.slice(), definition[1](fn(ctx))) : $$scope.ctx; | |
} | |
function get_slot_changes(definition, $$scope, dirty, fn) { | |
if (definition[2] && fn) { | |
const lets = definition[2](fn(dirty)); | |
if ($$scope.dirty === undefined) { | |
return lets; | |
} | |
if (typeof lets === 'object') { | |
const merged = []; | |
const len = Math.max($$scope.dirty.length, lets.length); | |
for (let i = 0; i < len; i += 1) { | |
merged[i] = $$scope.dirty[i] | lets[i]; | |
} | |
return merged; | |
} | |
return $$scope.dirty | lets; | |
} | |
return $$scope.dirty; | |
} | |
/** @returns {void} */ | |
function update_slot_base( | |
slot, | |
slot_definition, | |
ctx, | |
$$scope, | |
slot_changes, | |
get_slot_context_fn | |
) { | |
if (slot_changes) { | |
const slot_context = get_slot_context(slot_definition, ctx, $$scope, get_slot_context_fn); | |
slot.p(slot_context, slot_changes); | |
} | |
} | |
/** @returns {void} */ | |
function update_slot( | |
slot, | |
slot_definition, | |
ctx, | |
$$scope, | |
dirty, | |
get_slot_changes_fn, | |
get_slot_context_fn | |
) { | |
const slot_changes = get_slot_changes(slot_definition, $$scope, dirty, get_slot_changes_fn); | |
update_slot_base(slot, slot_definition, ctx, $$scope, slot_changes, get_slot_context_fn); | |
} | |
/** @returns {any[] | -1} */ | |
function get_all_dirty_from_scope($$scope) { | |
if ($$scope.ctx.length > 32) { | |
const dirty = []; | |
const length = $$scope.ctx.length / 32; | |
for (let i = 0; i < length; i++) { | |
dirty[i] = -1; | |
} | |
return dirty; | |
} | |
return -1; | |
} | |
/** @returns {{}} */ | |
function exclude_internal_props(props) { | |
const result = {}; | |
for (const k in props) if (k[0] !== '$') result[k] = props[k]; | |
return result; | |
} | |
/** @returns {{}} */ | |
function compute_rest_props(props, keys) { | |
const rest = {}; | |
keys = new Set(keys); | |
for (const k in props) if (!keys.has(k) && k[0] !== '$') rest[k] = props[k]; | |
return rest; | |
} | |
/** @returns {{}} */ | |
function compute_slots(slots) { | |
const result = {}; | |
for (const key in slots) { | |
result[key] = true; | |
} | |
return result; | |
} | |
/** @returns {(this: any, ...args: any[]) => void} */ | |
function once(fn) { | |
let ran = false; | |
return function (...args) { | |
if (ran) return; | |
ran = true; | |
fn.call(this, ...args); | |
}; | |
} | |
function null_to_empty(value) { | |
return value == null ? '' : value; | |
} | |
function set_store_value(store, ret, value) { | |
store.set(value); | |
return ret; | |
} | |
const has_prop = (obj, prop) => Object.prototype.hasOwnProperty.call(obj, prop); | |
function action_destroyer(action_result) { | |
return action_result && is_function(action_result.destroy) ? action_result.destroy : noop; | |
} | |
/** @param {number | string} value | |
* @returns {[number, string]} | |
*/ | |
function split_css_unit(value) { | |
const split = typeof value === 'string' && value.match(/^\s*(-?[\d.]+)([^\s]*)\s*$/); | |
return split ? [parseFloat(split[1]), split[2] || 'px'] : [/** @type {number} */ (value), 'px']; | |
} | |
const contenteditable_truthy_values = ['', true, 1, 'true', 'contenteditable']; | |
const is_client = typeof window !== 'undefined'; | |
/** @type {() => number} */ | |
let now = is_client ? () => window.performance.now() : () => Date.now(); | |
let raf = is_client ? (cb) => requestAnimationFrame(cb) : noop; | |
// used internally for testing | |
/** @returns {void} */ | |
function set_now(fn) { | |
now = fn; | |
} | |
/** @returns {void} */ | |
function set_raf(fn) { | |
raf = fn; | |
} | |
const tasks = new Set(); | |
/** | |
* @param {number} now | |
* @returns {void} | |
*/ | |
function run_tasks(now) { | |
tasks.forEach((task) => { | |
if (!task.c(now)) { | |
tasks.delete(task); | |
task.f(); | |
} | |
}); | |
if (tasks.size !== 0) raf(run_tasks); | |
} | |
/** | |
* For testing purposes only! | |
* @returns {void} | |
*/ | |
function clear_loops() { | |
tasks.clear(); | |
} | |
/** | |
* Creates a new task that runs on each raf frame | |
* until it returns a falsy value or is aborted | |
* @param {import('./private.js').TaskCallback} callback | |
* @returns {import('./private.js').Task} | |
*/ | |
function loop(callback) { | |
/** @type {import('./private.js').TaskEntry} */ | |
let task; | |
if (tasks.size === 0) raf(run_tasks); | |
return { | |
promise: new Promise((fulfill) => { | |
tasks.add((task = { c: callback, f: fulfill })); | |
}), | |
abort() { | |
tasks.delete(task); | |
} | |
}; | |
} | |
/** @type {typeof globalThis} */ | |
const globals = | |
typeof window !== 'undefined' | |
? window | |
: typeof globalThis !== 'undefined' | |
? globalThis | |
: // @ts-ignore Node typings have this | |
global; | |
/** | |
* Resize observer singleton. | |
* One listener per element only! | |
* https://groups.google.com/a/chromium.org/g/blink-dev/c/z6ienONUb5A/m/F5-VcUZtBAAJ | |
*/ | |
class ResizeObserverSingleton { | |
/** | |
* @private | |
* @readonly | |
* @type {WeakMap<Element, import('./private.js').Listener>} | |
*/ | |
_listeners = 'WeakMap' in globals ? new WeakMap() : undefined; | |
/** | |
* @private | |
* @type {ResizeObserver} | |
*/ | |
_observer = undefined; | |
/** @type {ResizeObserverOptions} */ | |
options; | |
/** @param {ResizeObserverOptions} options */ | |
constructor(options) { | |
this.options = options; | |
} | |
/** | |
* @param {Element} element | |
* @param {import('./private.js').Listener} listener | |
* @returns {() => void} | |
*/ | |
observe(element, listener) { | |
this._listeners.set(element, listener); | |
this._getObserver().observe(element, this.options); | |
return () => { | |
this._listeners.delete(element); | |
this._observer.unobserve(element); // this line can probably be removed | |
}; | |
} | |
/** | |
* @private | |
*/ | |
_getObserver() { | |
return ( | |
this._observer ?? | |
(this._observer = new ResizeObserver((entries) => { | |
for (const entry of entries) { | |
ResizeObserverSingleton.entries.set(entry.target, entry); | |
this._listeners.get(entry.target)?.(entry); | |
} | |
})) | |
); | |
} | |
} | |
// Needs to be written like this to pass the tree-shake-test | |
ResizeObserverSingleton.entries = 'WeakMap' in globals ? new WeakMap() : undefined; | |
// Track which nodes are claimed during hydration. Unclaimed nodes can then be removed from the DOM | |
// at the end of hydration without touching the remaining nodes. | |
let is_hydrating = false; | |
/** | |
* @returns {void} | |
*/ | |
function start_hydrating() { | |
is_hydrating = true; | |
} | |
/** | |
* @returns {void} | |
*/ | |
function end_hydrating() { | |
is_hydrating = false; | |
} | |
/** | |
* @param {number} low | |
* @param {number} high | |
* @param {(index: number) => number} key | |
* @param {number} value | |
* @returns {number} | |
*/ | |
function upper_bound(low, high, key, value) { | |
// Return first index of value larger than input value in the range [low, high) | |
while (low < high) { | |
const mid = low + ((high - low) >> 1); | |
if (key(mid) <= value) { | |
low = mid + 1; | |
} else { | |
high = mid; | |
} | |
} | |
return low; | |
} | |
/** | |
* @param {NodeEx} target | |
* @returns {void} | |
*/ | |
function init_hydrate(target) { | |
if (target.hydrate_init) return; | |
target.hydrate_init = true; | |
// We know that all children have claim_order values since the unclaimed have been detached if target is not <head> | |
let children = /** @type {ArrayLike<NodeEx2>} */ (target.childNodes); | |
// If target is <head>, there may be children without claim_order | |
if (target.nodeName === 'HEAD') { | |
const my_children = []; | |
for (let i = 0; i < children.length; i++) { | |
const node = children[i]; | |
if (node.claim_order !== undefined) { | |
my_children.push(node); | |
} | |
} | |
children = my_children; | |
} | |
/* | |
* Reorder claimed children optimally. | |
* We can reorder claimed children optimally by finding the longest subsequence of | |
* nodes that are already claimed in order and only moving the rest. The longest | |
* subsequence of nodes that are claimed in order can be found by | |
* computing the longest increasing subsequence of .claim_order values. | |
* | |
* This algorithm is optimal in generating the least amount of reorder operations | |
* possible. | |
* | |
* Proof: | |
* We know that, given a set of reordering operations, the nodes that do not move | |
* always form an increasing subsequence, since they do not move among each other | |
* meaning that they must be already ordered among each other. Thus, the maximal | |
* set of nodes that do not move form a longest increasing subsequence. | |
*/ | |
// Compute longest increasing subsequence | |
// m: subsequence length j => index k of smallest value that ends an increasing subsequence of length j | |
const m = new Int32Array(children.length + 1); | |
// Predecessor indices + 1 | |
const p = new Int32Array(children.length); | |
m[0] = -1; | |
let longest = 0; | |
for (let i = 0; i < children.length; i++) { | |
const current = children[i].claim_order; | |
// Find the largest subsequence length such that it ends in a value less than our current value | |
// upper_bound returns first greater value, so we subtract one | |
// with fast path for when we are on the current longest subsequence | |
const seq_len = | |
(longest > 0 && children[m[longest]].claim_order <= current | |
? longest + 1 | |
: upper_bound(1, longest, (idx) => children[m[idx]].claim_order, current)) - 1; | |
p[i] = m[seq_len] + 1; | |
const new_len = seq_len + 1; | |
// We can guarantee that current is the smallest value. Otherwise, we would have generated a longer sequence. | |
m[new_len] = i; | |
longest = Math.max(new_len, longest); | |
} | |
// The longest increasing subsequence of nodes (initially reversed) | |
/** | |
* @type {NodeEx2[]} | |
*/ | |
const lis = []; | |
// The rest of the nodes, nodes that will be moved | |
/** | |
* @type {NodeEx2[]} | |
*/ | |
const to_move = []; | |
let last = children.length - 1; | |
for (let cur = m[longest] + 1; cur != 0; cur = p[cur - 1]) { | |
lis.push(children[cur - 1]); | |
for (; last >= cur; last--) { | |
to_move.push(children[last]); | |
} | |
last--; | |
} | |
for (; last >= 0; last--) { | |
to_move.push(children[last]); | |
} | |
lis.reverse(); | |
// We sort the nodes being moved to guarantee that their insertion order matches the claim order | |
to_move.sort((a, b) => a.claim_order - b.claim_order); | |
// Finally, we move the nodes | |
for (let i = 0, j = 0; i < to_move.length; i++) { | |
while (j < lis.length && to_move[i].claim_order >= lis[j].claim_order) { | |
j++; | |
} | |
const anchor = j < lis.length ? lis[j] : null; | |
target.insertBefore(to_move[i], anchor); | |
} | |
} | |
/** | |
* @param {Node} target | |
* @param {Node} node | |
* @returns {void} | |
*/ | |
function append(target, node) { | |
target.appendChild(node); | |
} | |
/** | |
* @param {Node} target | |
* @param {string} style_sheet_id | |
* @param {string} styles | |
* @returns {void} | |
*/ | |
function append_styles(target, style_sheet_id, styles) { | |
const append_styles_to = get_root_for_style(target); | |
if (!append_styles_to.getElementById(style_sheet_id)) { | |
const style = element('style'); | |
style.id = style_sheet_id; | |
style.textContent = styles; | |
append_stylesheet(append_styles_to, style); | |
} | |
} | |
/** | |
* @param {Node} node | |
* @returns {ShadowRoot | Document} | |
*/ | |
function get_root_for_style(node) { | |
if (!node) return document; | |
const root = node.getRootNode ? node.getRootNode() : node.ownerDocument; | |
if (root && /** @type {ShadowRoot} */ (root).host) { | |
return /** @type {ShadowRoot} */ (root); | |
} | |
return node.ownerDocument; | |
} | |
/** | |
* @param {Node} node | |
* @returns {CSSStyleSheet} | |
*/ | |
function append_empty_stylesheet(node) { | |
const style_element = element('style'); | |
// For transitions to work without 'style-src: unsafe-inline' Content Security Policy, | |
// these empty tags need to be allowed with a hash as a workaround until we move to the Web Animations API. | |
// Using the hash for the empty string (for an empty tag) works in all browsers except Safari. | |
// So as a workaround for the workaround, when we append empty style tags we set their content to /* empty */. | |
// The hash 'sha256-9OlNO0DNEeaVzHL4RZwCLsBHA8WBQ8toBp/4F5XV2nc=' will then work even in Safari. | |
style_element.textContent = '/* empty */'; | |
append_stylesheet(get_root_for_style(node), style_element); | |
return style_element.sheet; | |
} | |
/** | |
* @param {ShadowRoot | Document} node | |
* @param {HTMLStyleElement} style | |
* @returns {CSSStyleSheet} | |
*/ | |
function append_stylesheet(node, style) { | |
append(/** @type {Document} */ (node).head || node, style); | |
return style.sheet; | |
} | |
/** | |
* @param {NodeEx} target | |
* @param {NodeEx} node | |
* @returns {void} | |
*/ | |
function append_hydration(target, node) { | |
if (is_hydrating) { | |
init_hydrate(target); | |
if ( | |
target.actual_end_child === undefined || | |
(target.actual_end_child !== null && target.actual_end_child.parentNode !== target) | |
) { | |
target.actual_end_child = target.firstChild; | |
} | |
// Skip nodes of undefined ordering | |
while (target.actual_end_child !== null && target.actual_end_child.claim_order === undefined) { | |
target.actual_end_child = target.actual_end_child.nextSibling; | |
} | |
if (node !== target.actual_end_child) { | |
// We only insert if the ordering of this node should be modified or the parent node is not target | |
if (node.claim_order !== undefined || node.parentNode !== target) { | |
target.insertBefore(node, target.actual_end_child); | |
} | |
} else { | |
target.actual_end_child = node.nextSibling; | |
} | |
} else if (node.parentNode !== target || node.nextSibling !== null) { | |
target.appendChild(node); | |
} | |
} | |
/** | |
* @param {Node} target | |
* @param {Node} node | |
* @param {Node} [anchor] | |
* @returns {void} | |
*/ | |
function insert(target, node, anchor) { | |
target.insertBefore(node, anchor || null); | |
} | |
/** | |
* @param {NodeEx} target | |
* @param {NodeEx} node | |
* @param {NodeEx} [anchor] | |
* @returns {void} | |
*/ | |
function insert_hydration(target, node, anchor) { | |
if (is_hydrating && !anchor) { | |
append_hydration(target, node); | |
} else if (node.parentNode !== target || node.nextSibling != anchor) { | |
target.insertBefore(node, anchor || null); | |
} | |
} | |
/** | |
* @param {Node} node | |
* @returns {void} | |
*/ | |
function detach(node) { | |
if (node.parentNode) { | |
node.parentNode.removeChild(node); | |
} | |
} | |
/** | |
* @returns {void} */ | |
function destroy_each(iterations, detaching) { | |
for (let i = 0; i < iterations.length; i += 1) { | |
if (iterations[i]) iterations[i].d(detaching); | |
} | |
} | |
/** | |
* @template {keyof HTMLElementTagNameMap} K | |
* @param {K} name | |
* @returns {HTMLElementTagNameMap[K]} | |
*/ | |
function element(name) { | |
return document.createElement(name); | |
} | |
/** | |
* @template {keyof HTMLElementTagNameMap} K | |
* @param {K} name | |
* @param {string} is | |
* @returns {HTMLElementTagNameMap[K]} | |
*/ | |
function element_is(name, is) { | |
return document.createElement(name, { is }); | |
} | |
/** | |
* @template T | |
* @template {keyof T} K | |
* @param {T} obj | |
* @param {K[]} exclude | |
* @returns {Pick<T, Exclude<keyof T, K>>} | |
*/ | |
function object_without_properties(obj, exclude) { | |
const target = /** @type {Pick<T, Exclude<keyof T, K>>} */ ({}); | |
for (const k in obj) { | |
if ( | |
has_prop(obj, k) && | |
// @ts-ignore | |
exclude.indexOf(k) === -1 | |
) { | |
// @ts-ignore | |
target[k] = obj[k]; | |
} | |
} | |
return target; | |
} | |
/** | |
* @template {keyof SVGElementTagNameMap} K | |
* @param {K} name | |
* @returns {SVGElement} | |
*/ | |
function svg_element(name) { | |
return document.createElementNS('http://www.w3.org/2000/svg', name); | |
} | |
/** | |
* @param {string} data | |
* @returns {Text} | |
*/ | |
function text(data) { | |
return document.createTextNode(data); | |
} | |
/** | |
* @returns {Text} */ | |
function space() { | |
return text(' '); | |
} | |
/** | |
* @returns {Text} */ | |
function empty() { | |
return text(''); | |
} | |
/** | |
* @param {string} content | |
* @returns {Comment} | |
*/ | |
function comment(content) { | |
return document.createComment(content); | |
} | |
/** | |
* @param {EventTarget} node | |
* @param {string} event | |
* @param {EventListenerOrEventListenerObject} handler | |
* @param {boolean | AddEventListenerOptions | EventListenerOptions} [options] | |
* @returns {() => void} | |
*/ | |
function listen(node, event, handler, options) { | |
node.addEventListener(event, handler, options); | |
return () => node.removeEventListener(event, handler, options); | |
} | |
/** | |
* @returns {(event: any) => any} */ | |
function prevent_default(fn) { | |
return function (event) { | |
event.preventDefault(); | |
// @ts-ignore | |
return fn.call(this, event); | |
}; | |
} | |
/** | |
* @returns {(event: any) => any} */ | |
function stop_propagation(fn) { | |
return function (event) { | |
event.stopPropagation(); | |
// @ts-ignore | |
return fn.call(this, event); | |
}; | |
} | |
/** | |
* @returns {(event: any) => any} */ | |
function stop_immediate_propagation(fn) { | |
return function (event) { | |
event.stopImmediatePropagation(); | |
// @ts-ignore | |
return fn.call(this, event); | |
}; | |
} | |
/** | |
* @returns {(event: any) => void} */ | |
function self(fn) { | |
return function (event) { | |
// @ts-ignore | |
if (event.target === this) fn.call(this, event); | |
}; | |
} | |
/** | |
* @returns {(event: any) => void} */ | |
function trusted(fn) { | |
return function (event) { | |
// @ts-ignore | |
if (event.isTrusted) fn.call(this, event); | |
}; | |
} | |
/** | |
* @param {Element} node | |
* @param {string} attribute | |
* @param {string} [value] | |
* @returns {void} | |
*/ | |
function attr(node, attribute, value) { | |
if (value == null) node.removeAttribute(attribute); | |
else if (node.getAttribute(attribute) !== value) node.setAttribute(attribute, value); | |
} | |
/** | |
* List of attributes that should always be set through the attr method, | |
* because updating them through the property setter doesn't work reliably. | |
* In the example of `width`/`height`, the problem is that the setter only | |
* accepts numeric values, but the attribute can also be set to a string like `50%`. | |
* If this list becomes too big, rethink this approach. | |
*/ | |
const always_set_through_set_attribute = ['width', 'height']; | |
/** | |
* @param {Element & ElementCSSInlineStyle} node | |
* @param {{ [x: string]: string }} attributes | |
* @returns {void} | |
*/ | |
function set_attributes(node, attributes) { | |
// @ts-ignore | |
const descriptors = Object.getOwnPropertyDescriptors(node.__proto__); | |
for (const key in attributes) { | |
if (attributes[key] == null) { | |
node.removeAttribute(key); | |
} else if (key === 'style') { | |
node.style.cssText = attributes[key]; | |
} else if (key === '__value') { | |
/** @type {any} */ (node).value = node[key] = attributes[key]; | |
} else if ( | |
descriptors[key] && | |
descriptors[key].set && | |
always_set_through_set_attribute.indexOf(key) === -1 | |
) { | |
node[key] = attributes[key]; | |
} else { | |
attr(node, key, attributes[key]); | |
} | |
} | |
} | |
/** | |
* @param {Element & ElementCSSInlineStyle} node | |
* @param {{ [x: string]: string }} attributes | |
* @returns {void} | |
*/ | |
function set_svg_attributes(node, attributes) { | |
for (const key in attributes) { | |
attr(node, key, attributes[key]); | |
} | |
} | |
/** | |
* @param {Record<string, unknown>} data_map | |
* @returns {void} | |
*/ | |
function set_custom_element_data_map(node, data_map) { | |
Object.keys(data_map).forEach((key) => { | |
set_custom_element_data(node, key, data_map[key]); | |
}); | |
} | |
/** | |
* @returns {void} */ | |
function set_custom_element_data(node, prop, value) { | |
const lower = prop.toLowerCase(); // for backwards compatibility with existing behavior we do lowercase first | |
if (lower in node) { | |
node[lower] = typeof node[lower] === 'boolean' && value === '' ? true : value; | |
} else if (prop in node) { | |
node[prop] = typeof node[prop] === 'boolean' && value === '' ? true : value; | |
} else { | |
attr(node, prop, value); | |
} | |
} | |
/** | |
* @param {string} tag | |
*/ | |
function set_dynamic_element_data(tag) { | |
return /-/.test(tag) ? set_custom_element_data_map : set_attributes; | |
} | |
/** | |
* @returns {void} | |
*/ | |
function xlink_attr(node, attribute, value) { | |
node.setAttributeNS('http://www.w3.org/1999/xlink', attribute, value); | |
} | |
/** | |
* @param {HTMLElement} node | |
* @returns {string} | |
*/ | |
function get_svelte_dataset(node) { | |
return node.dataset.svelteH; | |
} | |
/** | |
* @returns {unknown[]} */ | |
function get_binding_group_value(group, __value, checked) { | |
const value = new Set(); | |
for (let i = 0; i < group.length; i += 1) { | |
if (group[i].checked) value.add(group[i].__value); | |
} | |
if (!checked) { | |
value.delete(__value); | |
} | |
return Array.from(value); | |
} | |
/** | |
* @param {HTMLInputElement[]} group | |
* @returns {{ p(...inputs: HTMLInputElement[]): void; r(): void; }} | |
*/ | |
function init_binding_group(group) { | |
/** | |
* @type {HTMLInputElement[]} */ | |
let _inputs; | |
return { | |
/* push */ p(...inputs) { | |
_inputs = inputs; | |
_inputs.forEach((input) => group.push(input)); | |
}, | |
/* remove */ r() { | |
_inputs.forEach((input) => group.splice(group.indexOf(input), 1)); | |
} | |
}; | |
} | |
/** | |
* @param {number[]} indexes | |
* @returns {{ u(new_indexes: number[]): void; p(...inputs: HTMLInputElement[]): void; r: () => void; }} | |
*/ | |
function init_binding_group_dynamic(group, indexes) { | |
/** | |
* @type {HTMLInputElement[]} */ | |
let _group = get_binding_group(group); | |
/** | |
* @type {HTMLInputElement[]} */ | |
let _inputs; | |
function get_binding_group(group) { | |
for (let i = 0; i < indexes.length; i++) { | |
group = group[indexes[i]] = group[indexes[i]] || []; | |
} | |
return group; | |
} | |
/** | |
* @returns {void} */ | |
function push() { | |
_inputs.forEach((input) => _group.push(input)); | |
} | |
/** | |
* @returns {void} */ | |
function remove() { | |
_inputs.forEach((input) => _group.splice(_group.indexOf(input), 1)); | |
} | |
return { | |
/* update */ u(new_indexes) { | |
indexes = new_indexes; | |
const new_group = get_binding_group(group); | |
if (new_group !== _group) { | |
remove(); | |
_group = new_group; | |
push(); | |
} | |
}, | |
/* push */ p(...inputs) { | |
_inputs = inputs; | |
push(); | |
}, | |
/* remove */ r: remove | |
}; | |
} | |
/** @returns {number} */ | |
function to_number(value) { | |
return value === '' ? null : +value; | |
} | |
/** @returns {any[]} */ | |
function time_ranges_to_array(ranges) { | |
const array = []; | |
for (let i = 0; i < ranges.length; i += 1) { | |
array.push({ start: ranges.start(i), end: ranges.end(i) }); | |
} | |
return array; | |
} | |
/** | |
* @param {Element} element | |
* @returns {ChildNode[]} | |
*/ | |
function children(element) { | |
return Array.from(element.childNodes); | |
} | |
/** | |
* @param {ChildNodeArray} nodes | |
* @returns {void} | |
*/ | |
function init_claim_info(nodes) { | |
if (nodes.claim_info === undefined) { | |
nodes.claim_info = { last_index: 0, total_claimed: 0 }; | |
} | |
} | |
/** | |
* @template {ChildNodeEx} R | |
* @param {ChildNodeArray} nodes | |
* @param {(node: ChildNodeEx) => node is R} predicate | |
* @param {(node: ChildNodeEx) => ChildNodeEx | undefined} process_node | |
* @param {() => R} create_node | |
* @param {boolean} dont_update_last_index | |
* @returns {R} | |
*/ | |
function claim_node(nodes, predicate, process_node, create_node, dont_update_last_index = false) { | |
// Try to find nodes in an order such that we lengthen the longest increasing subsequence | |
init_claim_info(nodes); | |
const result_node = (() => { | |
// We first try to find an element after the previous one | |
for (let i = nodes.claim_info.last_index; i < nodes.length; i++) { | |
const node = nodes[i]; | |
if (predicate(node)) { | |
const replacement = process_node(node); | |
if (replacement === undefined) { | |
nodes.splice(i, 1); | |
} else { | |
nodes[i] = replacement; | |
} | |
if (!dont_update_last_index) { | |
nodes.claim_info.last_index = i; | |
} | |
return node; | |
} | |
} | |
// Otherwise, we try to find one before | |
// We iterate in reverse so that we don't go too far back | |
for (let i = nodes.claim_info.last_index - 1; i >= 0; i--) { | |
const node = nodes[i]; | |
if (predicate(node)) { | |
const replacement = process_node(node); | |
if (replacement === undefined) { | |
nodes.splice(i, 1); | |
} else { | |
nodes[i] = replacement; | |
} | |
if (!dont_update_last_index) { | |
nodes.claim_info.last_index = i; | |
} else if (replacement === undefined) { | |
// Since we spliced before the last_index, we decrease it | |
nodes.claim_info.last_index--; | |
} | |
return node; | |
} | |
} | |
// If we can't find any matching node, we create a new one | |
return create_node(); | |
})(); | |
result_node.claim_order = nodes.claim_info.total_claimed; | |
nodes.claim_info.total_claimed += 1; | |
return result_node; | |
} | |
/** | |
* @param {ChildNodeArray} nodes | |
* @param {string} name | |
* @param {{ [key: string]: boolean }} attributes | |
* @param {(name: string) => Element | SVGElement} create_element | |
* @returns {Element | SVGElement} | |
*/ | |
function claim_element_base(nodes, name, attributes, create_element) { | |
return claim_node( | |
nodes, | |
/** @returns {node is Element | SVGElement} */ | |
(node) => node.nodeName === name, | |
/** @param {Element} node */ | |
(node) => { | |
const remove = []; | |
for (let j = 0; j < node.attributes.length; j++) { | |
const attribute = node.attributes[j]; | |
if (!attributes[attribute.name]) { | |
remove.push(attribute.name); | |
} | |
} | |
remove.forEach((v) => node.removeAttribute(v)); | |
return undefined; | |
}, | |
() => create_element(name) | |
); | |
} | |
/** | |
* @param {ChildNodeArray} nodes | |
* @param {string} name | |
* @param {{ [key: string]: boolean }} attributes | |
* @returns {Element | SVGElement} | |
*/ | |
function claim_element(nodes, name, attributes) { | |
return claim_element_base(nodes, name, attributes, element); | |
} | |
/** | |
* @param {ChildNodeArray} nodes | |
* @param {string} name | |
* @param {{ [key: string]: boolean }} attributes | |
* @returns {Element | SVGElement} | |
*/ | |
function claim_svg_element(nodes, name, attributes) { | |
return claim_element_base(nodes, name, attributes, svg_element); | |
} | |
/** | |
* @param {ChildNodeArray} nodes | |
* @returns {Text} | |
*/ | |
function claim_text(nodes, data) { | |
return claim_node( | |
nodes, | |
/** @returns {node is Text} */ | |
(node) => node.nodeType === 3, | |
/** @param {Text} node */ | |
(node) => { | |
const data_str = '' + data; | |
if (node.data.startsWith(data_str)) { | |
if (node.data.length !== data_str.length) { | |
return node.splitText(data_str.length); | |
} | |
} else { | |
node.data = data_str; | |
} | |
}, | |
() => text(data), | |
true // Text nodes should not update last index since it is likely not worth it to eliminate an increasing subsequence of actual elements | |
); | |
} | |
/** | |
* @returns {Text} */ | |
function claim_space(nodes) { | |
return claim_text(nodes, ' '); | |
} | |
/** | |
* @param {ChildNodeArray} nodes | |
* @returns {Comment} | |
*/ | |
function claim_comment(nodes, data) { | |
return claim_node( | |
nodes, | |
/** @returns {node is Comment} */ | |
(node) => node.nodeType === 8, | |
/** @param {Comment} node */ | |
(node) => { | |
node.data = '' + data; | |
return undefined; | |
}, | |
() => comment(data), | |
true | |
); | |
} | |
function get_comment_idx(nodes, text, start) { | |
for (let i = start; i < nodes.length; i += 1) { | |
const node = nodes[i]; | |
if (node.nodeType === 8 /* comment node */ && node.textContent.trim() === text) { | |
return i; | |
} | |
} | |
return -1; | |
} | |
/** | |
* @param {boolean} is_svg | |
* @returns {HtmlTagHydration} | |
*/ | |
function claim_html_tag(nodes, is_svg) { | |
// find html opening tag | |
const start_index = get_comment_idx(nodes, 'HTML_TAG_START', 0); | |
const end_index = get_comment_idx(nodes, 'HTML_TAG_END', start_index + 1); | |
if (start_index === -1 || end_index === -1) { | |
return new HtmlTagHydration(is_svg); | |
} | |
init_claim_info(nodes); | |
const html_tag_nodes = nodes.splice(start_index, end_index - start_index + 1); | |
detach(html_tag_nodes[0]); | |
detach(html_tag_nodes[html_tag_nodes.length - 1]); | |
const claimed_nodes = html_tag_nodes.slice(1, html_tag_nodes.length - 1); | |
for (const n of claimed_nodes) { | |
n.claim_order = nodes.claim_info.total_claimed; | |
nodes.claim_info.total_claimed += 1; | |
} | |
return new HtmlTagHydration(is_svg, claimed_nodes); | |
} | |
/** | |
* @param {Text} text | |
* @param {unknown} data | |
* @returns {void} | |
*/ | |
function set_data(text, data) { | |
data = '' + data; | |
if (text.data === data) return; | |
text.data = /** @type {string} */ (data); | |
} | |
/** | |
* @param {Text} text | |
* @param {unknown} data | |
* @returns {void} | |
*/ | |
function set_data_contenteditable(text, data) { | |
data = '' + data; | |
if (text.wholeText === data) return; | |
text.data = /** @type {string} */ (data); | |
} | |
/** | |
* @param {Text} text | |
* @param {unknown} data | |
* @param {string} attr_value | |
* @returns {void} | |
*/ | |
function set_data_maybe_contenteditable(text, data, attr_value) { | |
if (~contenteditable_truthy_values.indexOf(attr_value)) { | |
set_data_contenteditable(text, data); | |
} else { | |
set_data(text, data); | |
} | |
} | |
/** | |
* @returns {void} */ | |
function set_input_value(input, value) { | |
input.value = value == null ? '' : value; | |
} | |
/** | |
* @returns {void} */ | |
function set_input_type(input, type) { | |
try { | |
input.type = type; | |
} catch (e) { | |
// do nothing | |
} | |
} | |
/** | |
* @returns {void} */ | |
function set_style(node, key, value, important) { | |
if (value == null) { | |
node.style.removeProperty(key); | |
} else { | |
node.style.setProperty(key, value, important ? 'important' : ''); | |
} | |
} | |
/** | |
* @returns {void} */ | |
function select_option(select, value, mounting) { | |
for (let i = 0; i < select.options.length; i += 1) { | |
const option = select.options[i]; | |
if (option.__value === value) { | |
option.selected = true; | |
return; | |
} | |
} | |
if (!mounting || value !== undefined) { | |
select.selectedIndex = -1; // no option should be selected | |
} | |
} | |
/** | |
* @returns {void} */ | |
function select_options(select, value) { | |
for (let i = 0; i < select.options.length; i += 1) { | |
const option = select.options[i]; | |
option.selected = ~value.indexOf(option.__value); | |
} | |
} | |
function select_value(select) { | |
const selected_option = select.querySelector(':checked'); | |
return selected_option && selected_option.__value; | |
} | |
function select_multiple_value(select) { | |
return [].map.call(select.querySelectorAll(':checked'), (option) => option.__value); | |
} | |
// unfortunately this can't be a constant as that wouldn't be tree-shakeable | |
// so we cache the result instead | |
/** | |
* @type {boolean} */ | |
let crossorigin; | |
/** | |
* @returns {boolean} */ | |
function is_crossorigin() { | |
if (crossorigin === undefined) { | |
crossorigin = false; | |
try { | |
if (typeof window !== 'undefined' && window.parent) { | |
void window.parent.document; | |
} | |
} catch (error) { | |
crossorigin = true; | |
} | |
} | |
return crossorigin; | |
} | |
/** | |
* @param {HTMLElement} node | |
* @param {() => void} fn | |
* @returns {() => void} | |
*/ | |
function add_iframe_resize_listener(node, fn) { | |
const computed_style = getComputedStyle(node); | |
if (computed_style.position === 'static') { | |
node.style.position = 'relative'; | |
} | |
const iframe = element('iframe'); | |
iframe.setAttribute( | |
'style', | |
'display: block; position: absolute; top: 0; left: 0; width: 100%; height: 100%; ' + | |
'overflow: hidden; border: 0; opacity: 0; pointer-events: none; z-index: -1;' | |
); | |
iframe.setAttribute('aria-hidden', 'true'); | |
iframe.tabIndex = -1; | |
const crossorigin = is_crossorigin(); | |
/** | |
* @type {() => void} | |
*/ | |
let unsubscribe; | |
if (crossorigin) { | |
iframe.src = "data:text/html,<script>onresize=function(){parent.postMessage(0,'*')}</script>"; | |
unsubscribe = listen( | |
window, | |
'message', | |
/** @param {MessageEvent} event */ (event) => { | |
if (event.source === iframe.contentWindow) fn(); | |
} | |
); | |
} else { | |
iframe.src = 'about:blank'; | |
iframe.onload = () => { | |
unsubscribe = listen(iframe.contentWindow, 'resize', fn); | |
// make sure an initial resize event is fired _after_ the iframe is loaded (which is asynchronous) | |
// see https://github.com/sveltejs/svelte/issues/4233 | |
fn(); | |
}; | |
} | |
append(node, iframe); | |
return () => { | |
if (crossorigin) { | |
unsubscribe(); | |
} else if (unsubscribe && iframe.contentWindow) { | |
unsubscribe(); | |
} | |
detach(iframe); | |
}; | |
} | |
const resize_observer_content_box = /* @__PURE__ */ new ResizeObserverSingleton({ | |
box: 'content-box' | |
}); | |
const resize_observer_border_box = /* @__PURE__ */ new ResizeObserverSingleton({ | |
box: 'border-box' | |
}); | |
const resize_observer_device_pixel_content_box = /* @__PURE__ */ new ResizeObserverSingleton( | |
{ box: 'device-pixel-content-box' } | |
); | |
/** | |
* @returns {void} */ | |
function toggle_class(element, name, toggle) { | |
// The `!!` is required because an `undefined` flag means flipping the current state. | |
element.classList.toggle(name, !!toggle); | |
} | |
/** | |
* @template T | |
* @param {string} type | |
* @param {T} [detail] | |
* @param {{ bubbles?: boolean, cancelable?: boolean }} [options] | |
* @returns {CustomEvent<T>} | |
*/ | |
function custom_event(type, detail, { bubbles = false, cancelable = false } = {}) { | |
return new CustomEvent(type, { detail, bubbles, cancelable }); | |
} | |
/** | |
* @param {string} selector | |
* @param {HTMLElement} parent | |
* @returns {ChildNodeArray} | |
*/ | |
function query_selector_all(selector, parent = document.body) { | |
return Array.from(parent.querySelectorAll(selector)); | |
} | |
/** | |
* @param {string} nodeId | |
* @param {HTMLElement} head | |
* @returns {any[]} | |
*/ | |
function head_selector(nodeId, head) { | |
const result = []; | |
let started = 0; | |
for (const node of head.childNodes) { | |
if (node.nodeType === 8 /* comment node */) { | |
const comment = node.textContent.trim(); | |
if (comment === `HEAD_${nodeId}_END`) { | |
started -= 1; | |
result.push(node); | |
} else if (comment === `HEAD_${nodeId}_START`) { | |
started += 1; | |
result.push(node); | |
} | |
} else if (started > 0) { | |
result.push(node); | |
} | |
} | |
return result; | |
} | |
/** */ | |
class HtmlTag { | |
/** | |
* @private | |
* @default false | |
*/ | |
is_svg = false; | |
/** parent for creating node */ | |
e = undefined; | |
/** html tag nodes */ | |
n = undefined; | |
/** target */ | |
t = undefined; | |
/** anchor */ | |
a = undefined; | |
constructor(is_svg = false) { | |
this.is_svg = is_svg; | |
this.e = this.n = null; | |
} | |
/** | |
* @param {string} html | |
* @returns {void} | |
*/ | |
c(html) { | |
this.h(html); | |
} | |
/** | |
* @param {string} html | |
* @param {HTMLElement | SVGElement} target | |
* @param {HTMLElement | SVGElement} anchor | |
* @returns {void} | |
*/ | |
m(html, target, anchor = null) { | |
if (!this.e) { | |
if (this.is_svg) | |
this.e = svg_element(/** @type {keyof SVGElementTagNameMap} */ (target.nodeName)); | |
/** #7364 target for <template> may be provided as #document-fragment(11) */ else | |
this.e = element( | |
/** @type {keyof HTMLElementTagNameMap} */ ( | |
target.nodeType === 11 ? 'TEMPLATE' : target.nodeName | |
) | |
); | |
this.t = | |
target.tagName !== 'TEMPLATE' | |
? target | |
: /** @type {HTMLTemplateElement} */ (target).content; | |
this.c(html); | |
} | |
this.i(anchor); | |
} | |
/** | |
* @param {string} html | |
* @returns {void} | |
*/ | |
h(html) { | |
this.e.innerHTML = html; | |
this.n = Array.from( | |
this.e.nodeName === 'TEMPLATE' ? this.e.content.childNodes : this.e.childNodes | |
); | |
} | |
/** | |
* @returns {void} */ | |
i(anchor) { | |
for (let i = 0; i < this.n.length; i += 1) { | |
insert(this.t, this.n[i], anchor); | |
} | |
} | |
/** | |
* @param {string} html | |
* @returns {void} | |
*/ | |
p(html) { | |
this.d(); | |
this.h(html); | |
this.i(this.a); | |
} | |
/** | |
* @returns {void} */ | |
d() { | |
this.n.forEach(detach); | |
} | |
} | |
class HtmlTagHydration extends HtmlTag { | |
/** @type {Element[]} hydration claimed nodes */ | |
l = undefined; | |
constructor(is_svg = false, claimed_nodes) { | |
super(is_svg); | |
this.e = this.n = null; | |
this.l = claimed_nodes; | |
} | |
/** | |
* @param {string} html | |
* @returns {void} | |
*/ | |
c(html) { | |
if (this.l) { | |
this.n = this.l; | |
} else { | |
super.c(html); | |
} | |
} | |
/** | |
* @returns {void} */ | |
i(anchor) { | |
for (let i = 0; i < this.n.length; i += 1) { | |
insert_hydration(this.t, this.n[i], anchor); | |
} | |
} | |
} | |
/** | |
* @param {NamedNodeMap} attributes | |
* @returns {{}} | |
*/ | |
function attribute_to_object(attributes) { | |
const result = {}; | |
for (const attribute of attributes) { | |
result[attribute.name] = attribute.value; | |
} | |
return result; | |
} | |
/** | |
* @param {HTMLElement} element | |
* @returns {{}} | |
*/ | |
function get_custom_elements_slots(element) { | |
const result = {}; | |
element.childNodes.forEach( | |
/** @param {Element} node */ (node) => { | |
result[node.slot || 'default'] = true; | |
} | |
); | |
return result; | |
} | |
function construct_svelte_component(component, props) { | |
return new component(props); | |
} | |
/** | |
* @typedef {Node & { | |
* claim_order?: number; | |
* hydrate_init?: true; | |
* actual_end_child?: NodeEx; | |
* childNodes: NodeListOf<NodeEx>; | |
* }} NodeEx | |
*/ | |
/** @typedef {ChildNode & NodeEx} ChildNodeEx */ | |
/** @typedef {NodeEx & { claim_order: number }} NodeEx2 */ | |
/** | |
* @typedef {ChildNodeEx[] & { | |
* claim_info?: { | |
* last_index: number; | |
* total_claimed: number; | |
* }; | |
* }} ChildNodeArray | |
*/ | |
// we need to store the information for multiple documents because a Svelte application could also contain iframes | |
// https://github.com/sveltejs/svelte/issues/3624 | |
/** @type {Map<Document | ShadowRoot, import('./private.d.ts').StyleInformation>} */ | |
const managed_styles = new Map(); | |
let active = 0; | |
// https://github.com/darkskyapp/string-hash/blob/master/index.js | |
/** | |
* @param {string} str | |
* @returns {number} | |
*/ | |
function hash(str) { | |
let hash = 5381; | |
let i = str.length; | |
while (i--) hash = ((hash << 5) - hash) ^ str.charCodeAt(i); | |
return hash >>> 0; | |
} | |
/** | |
* @param {Document | ShadowRoot} doc | |
* @param {Element & ElementCSSInlineStyle} node | |
* @returns {{ stylesheet: any; rules: {}; }} | |
*/ | |
function create_style_information(doc, node) { | |
const info = { stylesheet: append_empty_stylesheet(node), rules: {} }; | |
managed_styles.set(doc, info); | |
return info; | |
} | |
/** | |
* @param {Element & ElementCSSInlineStyle} node | |
* @param {number} a | |
* @param {number} b | |
* @param {number} duration | |
* @param {number} delay | |
* @param {(t: number) => number} ease | |
* @param {(t: number, u: number) => string} fn | |
* @param {number} uid | |
* @returns {string} | |
*/ | |
function create_rule(node, a, b, duration, delay, ease, fn, uid = 0) { | |
const step = 16.666 / duration; | |
let keyframes = '{\n'; | |
for (let p = 0; p <= 1; p += step) { | |
const t = a + (b - a) * ease(p); | |
keyframes += p * 100 + `%{${fn(t, 1 - t)}}\n`; | |
} | |
const rule = keyframes + `100% {${fn(b, 1 - b)}}\n}`; | |
const name = `__svelte_${hash(rule)}_${uid}`; | |
const doc = get_root_for_style(node); | |
const { stylesheet, rules } = managed_styles.get(doc) || create_style_information(doc, node); | |
if (!rules[name]) { | |
rules[name] = true; | |
stylesheet.insertRule(`@keyframes ${name} ${rule}`, stylesheet.cssRules.length); | |
} | |
const animation = node.style.animation || ''; | |
node.style.animation = `${ | |
animation ? `${animation}, ` : '' | |
}${name} ${duration}ms linear ${delay}ms 1 both`; | |
active += 1; | |
return name; | |
} | |
/** | |
* @param {Element & ElementCSSInlineStyle} node | |
* @param {string} [name] | |
* @returns {void} | |
*/ | |
function delete_rule(node, name) { | |
const previous = (node.style.animation || '').split(', '); | |
const next = previous.filter( | |
name | |
? (anim) => anim.indexOf(name) < 0 // remove specific animation | |
: (anim) => anim.indexOf('__svelte') === -1 // remove all Svelte animations | |
); | |
const deleted = previous.length - next.length; | |
if (deleted) { | |
node.style.animation = next.join(', '); | |
active -= deleted; | |
if (!active) clear_rules(); | |
} | |
} | |
/** @returns {void} */ | |
function clear_rules() { | |
raf(() => { | |
if (active) return; | |
managed_styles.forEach((info) => { | |
const { ownerNode } = info.stylesheet; | |
// there is no ownerNode if it runs on jsdom. | |
if (ownerNode) detach(ownerNode); | |
}); | |
managed_styles.clear(); | |
}); | |
} | |
/** | |
* @param {Element & ElementCSSInlineStyle} node | |
* @param {import('./private.js').PositionRect} from | |
* @param {import('./private.js').AnimationFn} fn | |
*/ | |
function create_animation(node, from, fn, params) { | |
if (!from) return noop; | |
const to = node.getBoundingClientRect(); | |
if ( | |
from.left === to.left && | |
from.right === to.right && | |
from.top === to.top && | |
from.bottom === to.bottom | |
) | |
return noop; | |
const { | |
delay = 0, | |
duration = 300, | |
easing = identity, | |
// @ts-ignore todo: should this be separated from destructuring? Or start/end added to public api and documentation? | |
start: start_time = now() + delay, | |
// @ts-ignore todo: | |
end = start_time + duration, | |
tick = noop, | |
css | |
} = fn(node, { from, to }, params); | |
let running = true; | |
let started = false; | |
let name; | |
/** @returns {void} */ | |
function start() { | |
if (css) { | |
name = create_rule(node, 0, 1, duration, delay, easing, css); | |
} | |
if (!delay) { | |
started = true; | |
} | |
} | |
/** @returns {void} */ | |
function stop() { | |
if (css) delete_rule(node, name); | |
running = false; | |
} | |
loop((now) => { | |
if (!started && now >= start_time) { | |
started = true; | |
} | |
if (started && now >= end) { | |
tick(1, 0); | |
stop(); | |
} | |
if (!running) { | |
return false; | |
} | |
if (started) { | |
const p = now - start_time; | |
const t = 0 + 1 * easing(p / duration); | |
tick(t, 1 - t); | |
} | |
return true; | |
}); | |
start(); | |
tick(0, 1); | |
return stop; | |
} | |
/** | |
* @param {Element & ElementCSSInlineStyle} node | |
* @returns {void} | |
*/ | |
function fix_position(node) { | |
const style = getComputedStyle(node); | |
if (style.position !== 'absolute' && style.position !== 'fixed') { | |
const { width, height } = style; | |
const a = node.getBoundingClientRect(); | |
node.style.position = 'absolute'; | |
node.style.width = width; | |
node.style.height = height; | |
add_transform(node, a); | |
} | |
} | |
/** | |
* @param {Element & ElementCSSInlineStyle} node | |
* @param {import('./private.js').PositionRect} a | |
* @returns {void} | |
*/ | |
function add_transform(node, a) { | |
const b = node.getBoundingClientRect(); | |
if (a.left !== b.left || a.top !== b.top) { | |
const style = getComputedStyle(node); | |
const transform = style.transform === 'none' ? '' : style.transform; | |
node.style.transform = `${transform} translate(${a.left - b.left}px, ${a.top - b.top}px)`; | |
} | |
} | |
let current_component; | |
/** @returns {void} */ | |
function set_current_component(component) { | |
current_component = component; | |
} | |
function get_current_component() { | |
if (!current_component) throw new Error('Function called outside component initialization'); | |
return current_component; | |
} | |
/** | |
* Schedules a callback to run immediately before the component is updated after any state change. | |
* | |
* The first time the callback runs will be before the initial `onMount` | |
* | |
* https://svelte.dev/docs/svelte#beforeupdate | |
* @param {() => any} fn | |
* @returns {void} | |
*/ | |
function beforeUpdate(fn) { | |
get_current_component().$$.before_update.push(fn); | |
} | |
/** | |
* The `onMount` function schedules a callback to run as soon as the component has been mounted to the DOM. | |
* It must be called during the component's initialisation (but doesn't need to live *inside* the component; | |
* it can be called from an external module). | |
* | |
* If a function is returned _synchronously_ from `onMount`, it will be called when the component is unmounted. | |
* | |
* `onMount` does not run inside a [server-side component](/docs#run-time-server-side-component-api). | |
* | |
* https://svelte.dev/docs/svelte#onmount | |
* @template T | |
* @param {() => import('./private.js').NotFunction<T> | Promise<import('./private.js').NotFunction<T>> | (() => any)} fn | |
* @returns {void} | |
*/ | |
function onMount(fn) { | |
get_current_component().$$.on_mount.push(fn); | |
} | |
/** | |
* Schedules a callback to run immediately after the component has been updated. | |
* | |
* The first time the callback runs will be after the initial `onMount` | |
* | |
* https://svelte.dev/docs/svelte#afterupdate | |
* @param {() => any} fn | |
* @returns {void} | |
*/ | |
function afterUpdate(fn) { | |
get_current_component().$$.after_update.push(fn); | |
} | |
/** | |
* Schedules a callback to run immediately before the component is unmounted. | |
* | |
* Out of `onMount`, `beforeUpdate`, `afterUpdate` and `onDestroy`, this is the | |
* only one that runs inside a server-side component. | |
* | |
* https://svelte.dev/docs/svelte#ondestroy | |
* @param {() => any} fn | |
* @returns {void} | |
*/ | |
function onDestroy(fn) { | |
get_current_component().$$.on_destroy.push(fn); | |
} | |
/** | |
* Creates an event dispatcher that can be used to dispatch [component events](/docs#template-syntax-component-directives-on-eventname). | |
* Event dispatchers are functions that can take two arguments: `name` and `detail`. | |
* | |
* Component events created with `createEventDispatcher` create a | |
* [CustomEvent](https://developer.mozilla.org/en-US/docs/Web/API/CustomEvent). | |
* These events do not [bubble](https://developer.mozilla.org/en-US/docs/Learn/JavaScript/Building_blocks/Events#Event_bubbling_and_capture). | |
* The `detail` argument corresponds to the [CustomEvent.detail](https://developer.mozilla.org/en-US/docs/Web/API/CustomEvent/detail) | |
* property and can contain any type of data. | |
* | |
* The event dispatcher can be typed to narrow the allowed event names and the type of the `detail` argument: | |
* ```ts | |
* const dispatch = createEventDispatcher<{ | |
* loaded: never; // does not take a detail argument | |
* change: string; // takes a detail argument of type string, which is required | |
* optional: number | null; // takes an optional detail argument of type number | |
* }>(); | |
* ``` | |
* | |
* https://svelte.dev/docs/svelte#createeventdispatcher | |
* @template {Record<string, any>} [EventMap=any] | |
* @returns {import('./public.js').EventDispatcher<EventMap>} | |
*/ | |
function createEventDispatcher() { | |
const component = get_current_component(); | |
return (type, detail, { cancelable = false } = {}) => { | |
const callbacks = component.$$.callbacks[type]; | |
if (callbacks) { | |
// TODO are there situations where events could be dispatched | |
// in a server (non-DOM) environment? | |
const event = custom_event(/** @type {string} */ (type), detail, { cancelable }); | |
callbacks.slice().forEach((fn) => { | |
fn.call(component, event); | |
}); | |
return !event.defaultPrevented; | |
} | |
return true; | |
}; | |
} | |
/** | |
* Associates an arbitrary `context` object with the current component and the specified `key` | |
* and returns that object. The context is then available to children of the component | |
* (including slotted content) with `getContext`. | |
* | |
* Like lifecycle functions, this must be called during component initialisation. | |
* | |
* https://svelte.dev/docs/svelte#setcontext | |
* @template T | |
* @param {any} key | |
* @param {T} context | |
* @returns {T} | |
*/ | |
function setContext(key, context) { | |
get_current_component().$$.context.set(key, context); | |
return context; | |
} | |
/** | |
* Retrieves the context that belongs to the closest parent component with the specified `key`. | |
* Must be called during component initialisation. | |
* | |
* https://svelte.dev/docs/svelte#getcontext | |
* @template T | |
* @param {any} key | |
* @returns {T} | |
*/ | |
function getContext(key) { | |
return get_current_component().$$.context.get(key); | |
} | |
/** | |
* Retrieves the whole context map that belongs to the closest parent component. | |
* Must be called during component initialisation. Useful, for example, if you | |
* programmatically create a component and want to pass the existing context to it. | |
* | |
* https://svelte.dev/docs/svelte#getallcontexts | |
* @template {Map<any, any>} [T=Map<any, any>] | |
* @returns {T} | |
*/ | |
function getAllContexts() { | |
return get_current_component().$$.context; | |
} | |
/** | |
* Checks whether a given `key` has been set in the context of a parent component. | |
* Must be called during component initialisation. | |
* | |
* https://svelte.dev/docs/svelte#hascontext | |
* @param {any} key | |
* @returns {boolean} | |
*/ | |
function hasContext(key) { | |
return get_current_component().$$.context.has(key); | |
} | |
// TODO figure out if we still want to support | |
// shorthand events, or if we want to implement | |
// a real bubbling mechanism | |
/** | |
* @param component | |
* @param event | |
* @returns {void} | |
*/ | |
function bubble(component, event) { | |
const callbacks = component.$$.callbacks[event.type]; | |
if (callbacks) { | |
// @ts-ignore | |
callbacks.slice().forEach((fn) => fn.call(this, event)); | |
} | |
} | |
const dirty_components = []; | |
const intros = { enabled: false }; | |
const binding_callbacks = []; | |
let render_callbacks = []; | |
const flush_callbacks = []; | |
const resolved_promise = /* @__PURE__ */ Promise.resolve(); | |
let update_scheduled = false; | |
/** @returns {void} */ | |
function schedule_update() { | |
if (!update_scheduled) { | |
update_scheduled = true; | |
resolved_promise.then(flush); | |
} | |
} | |
/** @returns {Promise<void>} */ | |
function tick() { | |
schedule_update(); | |
return resolved_promise; | |
} | |
/** @returns {void} */ | |
function add_render_callback(fn) { | |
render_callbacks.push(fn); | |
} | |
/** @returns {void} */ | |
function add_flush_callback(fn) { | |
flush_callbacks.push(fn); | |
} | |
// flush() calls callbacks in this order: | |
// 1. All beforeUpdate callbacks, in order: parents before children | |
// 2. All bind:this callbacks, in reverse order: children before parents. | |
// 3. All afterUpdate callbacks, in order: parents before children. EXCEPT | |
// for afterUpdates called during the initial onMount, which are called in | |
// reverse order: children before parents. | |
// Since callbacks might update component values, which could trigger another | |
// call to flush(), the following steps guard against this: | |
// 1. During beforeUpdate, any updated components will be added to the | |
// dirty_components array and will cause a reentrant call to flush(). Because | |
// the flush index is kept outside the function, the reentrant call will pick | |
// up where the earlier call left off and go through all dirty components. The | |
// current_component value is saved and restored so that the reentrant call will | |
// not interfere with the "parent" flush() call. | |
// 2. bind:this callbacks cannot trigger new flush() calls. | |
// 3. During afterUpdate, any updated components will NOT have their afterUpdate | |
// callback called a second time; the seen_callbacks set, outside the flush() | |
// function, guarantees this behavior. | |
const seen_callbacks = new Set(); | |
let flushidx = 0; // Do *not* move this inside the flush() function | |
/** @returns {void} */ | |
function flush() { | |
// Do not reenter flush while dirty components are updated, as this can | |
// result in an infinite loop. Instead, let the inner flush handle it. | |
// Reentrancy is ok afterwards for bindings etc. | |
if (flushidx !== 0) { | |
return; | |
} | |
const saved_component = current_component; | |
do { | |
// first, call beforeUpdate functions | |
// and update components | |
try { | |
while (flushidx < dirty_components.length) { | |
const component = dirty_components[flushidx]; | |
flushidx++; | |
set_current_component(component); | |
update(component.$$); | |
} | |
} catch (e) { | |
// reset dirty state to not end up in a deadlocked state and then rethrow | |
dirty_components.length = 0; | |
flushidx = 0; | |
throw e; | |
} | |
set_current_component(null); | |
dirty_components.length = 0; | |
flushidx = 0; | |
while (binding_callbacks.length) binding_callbacks.pop()(); | |
// then, once components are updated, call | |
// afterUpdate functions. This may cause | |
// subsequent updates... | |
for (let i = 0; i < render_callbacks.length; i += 1) { | |
const callback = render_callbacks[i]; | |
if (!seen_callbacks.has(callback)) { | |
// ...so guard against infinite loops | |
seen_callbacks.add(callback); | |
callback(); | |
} | |
} | |
render_callbacks.length = 0; | |
} while (dirty_components.length); | |
while (flush_callbacks.length) { | |
flush_callbacks.pop()(); | |
} | |
update_scheduled = false; | |
seen_callbacks.clear(); | |
set_current_component(saved_component); | |
} | |
/** @returns {void} */ | |
function update($$) { | |
if ($$.fragment !== null) { | |
$$.update(); | |
run_all($$.before_update); | |
const dirty = $$.dirty; | |
$$.dirty = [-1]; | |
$$.fragment && $$.fragment.p($$.ctx, dirty); | |
$$.after_update.forEach(add_render_callback); | |
} | |
} | |
/** | |
* Useful for example to execute remaining `afterUpdate` callbacks before executing `destroy`. | |
* @param {Function[]} fns | |
* @returns {void} | |
*/ | |
function flush_render_callbacks(fns) { | |
const filtered = []; | |
const targets = []; | |
render_callbacks.forEach((c) => (fns.indexOf(c) === -1 ? filtered.push(c) : targets.push(c))); | |
targets.forEach((c) => c()); | |
render_callbacks = filtered; | |
} | |
/** | |
* @type {Promise<void> | null} | |
*/ | |
let promise; | |
/** | |
* @returns {Promise<void>} | |
*/ | |
function wait() { | |
if (!promise) { | |
promise = Promise.resolve(); | |
promise.then(() => { | |
promise = null; | |
}); | |
} | |
return promise; | |
} | |
/** | |
* @param {Element} node | |
* @param {INTRO | OUTRO | boolean} direction | |
* @param {'start' | 'end'} kind | |
* @returns {void} | |
*/ | |
function dispatch(node, direction, kind) { | |
node.dispatchEvent(custom_event(`${direction ? 'intro' : 'outro'}${kind}`)); | |
} | |
const outroing = new Set(); | |
/** | |
* @type {Outro} | |
*/ | |
let outros; | |
/** | |
* @returns {void} */ | |
function group_outros() { | |
outros = { | |
r: 0, | |
c: [], | |
p: outros // parent group | |
}; | |
} | |
/** | |
* @returns {void} */ | |
function check_outros() { | |
if (!outros.r) { | |
run_all(outros.c); | |
} | |
outros = outros.p; | |
} | |
/** | |
* @param {import('./private.js').Fragment} block | |
* @param {0 | 1} [local] | |
* @returns {void} | |
*/ | |
function transition_in(block, local) { | |
if (block && block.i) { | |
outroing.delete(block); | |
block.i(local); | |
} | |
} | |
/** | |
* @param {import('./private.js').Fragment} block | |
* @param {0 | 1} local | |
* @param {0 | 1} [detach] | |
* @param {() => void} [callback] | |
* @returns {void} | |
*/ | |
function transition_out(block, local, detach, callback) { | |
if (block && block.o) { | |
if (outroing.has(block)) return; | |
outroing.add(block); | |
outros.c.push(() => { | |
outroing.delete(block); | |
if (callback) { | |
if (detach) block.d(1); | |
callback(); | |
} | |
}); | |
block.o(local); | |
} else if (callback) { | |
callback(); | |
} | |
} | |
/** | |
* @type {import('../transition/public.js').TransitionConfig} | |
*/ | |
const null_transition = { duration: 0 }; | |
/** | |
* @param {Element & ElementCSSInlineStyle} node | |
* @param {TransitionFn} fn | |
* @param {any} params | |
* @returns {{ start(): void; invalidate(): void; end(): void; }} | |
*/ | |
function create_in_transition(node, fn, params) { | |
/** | |
* @type {TransitionOptions} */ | |
const options = { direction: 'in' }; | |
let config = fn(node, params, options); | |
let running = false; | |
let animation_name; | |
let task; | |
let uid = 0; | |
/** | |
* @returns {void} */ | |
function cleanup() { | |
if (animation_name) delete_rule(node, animation_name); | |
} | |
/** | |
* @returns {void} */ | |
function go() { | |
const { | |
delay = 0, | |
duration = 300, | |
easing = identity, | |
tick = noop, | |
css | |
} = config || null_transition; | |
if (css) animation_name = create_rule(node, 0, 1, duration, delay, easing, css, uid++); | |
tick(0, 1); | |
const start_time = now() + delay; | |
const end_time = start_time + duration; | |
if (task) task.abort(); | |
running = true; | |
add_render_callback(() => dispatch(node, true, 'start')); | |
task = loop((now) => { | |
if (running) { | |
if (now >= end_time) { | |
tick(1, 0); | |
dispatch(node, true, 'end'); | |
cleanup(); | |
return (running = false); | |
} | |
if (now >= start_time) { | |
const t = easing((now - start_time) / duration); | |
tick(t, 1 - t); | |
} | |
} | |
return running; | |
}); | |
} | |
let started = false; | |
return { | |
start() { | |
if (started) return; | |
started = true; | |
delete_rule(node); | |
if (is_function(config)) { | |
config = config(options); | |
wait().then(go); | |
} else { | |
go(); | |
} | |
}, | |
invalidate() { | |
started = false; | |
}, | |
end() { | |
if (running) { | |
cleanup(); | |
running = false; | |
} | |
} | |
}; | |
} | |
/** | |
* @param {Element & ElementCSSInlineStyle} node | |
* @param {TransitionFn} fn | |
* @param {any} params | |
* @returns {{ end(reset: any): void; }} | |
*/ | |
function create_out_transition(node, fn, params) { | |
/** @type {TransitionOptions} */ | |
const options = { direction: 'out' }; | |
let config = fn(node, params, options); | |
let running = true; | |
let animation_name; | |
const group = outros; | |
group.r += 1; | |
/** @type {boolean} */ | |
let original_inert_value; | |
/** | |
* @returns {void} */ | |
function go() { | |
const { | |
delay = 0, | |
duration = 300, | |
easing = identity, | |
tick = noop, | |
css | |
} = config || null_transition; | |
if (css) animation_name = create_rule(node, 1, 0, duration, delay, easing, css); | |
const start_time = now() + delay; | |
const end_time = start_time + duration; | |
add_render_callback(() => dispatch(node, false, 'start')); | |
if ('inert' in node) { | |
original_inert_value = /** @type {HTMLElement} */ (node).inert; | |
node.inert = true; | |
} | |
loop((now) => { | |
if (running) { | |
if (now >= end_time) { | |
tick(0, 1); | |
dispatch(node, false, 'end'); | |
if (!--group.r) { | |
// this will result in `end()` being called, | |
// so we don't need to clean up here | |
run_all(group.c); | |
} | |
return false; | |
} | |
if (now >= start_time) { | |
const t = easing((now - start_time) / duration); | |
tick(1 - t, t); | |
} | |
} | |
return running; | |
}); | |
} | |
if (is_function(config)) { | |
wait().then(() => { | |
// @ts-ignore | |
config = config(options); | |
go(); | |
}); | |
} else { | |
go(); | |
} | |
return { | |
end(reset) { | |
if (reset && 'inert' in node) { | |
node.inert = original_inert_value; | |
} | |
if (reset && config.tick) { | |
config.tick(1, 0); | |
} | |
if (running) { | |
if (animation_name) delete_rule(node, animation_name); | |
running = false; | |
} | |
} | |
}; | |
} | |
/** | |
* @param {Element & ElementCSSInlineStyle} node | |
* @param {TransitionFn} fn | |
* @param {any} params | |
* @param {boolean} intro | |
* @returns {{ run(b: 0 | 1): void; end(): void; }} | |
*/ | |
function create_bidirectional_transition(node, fn, params, intro) { | |
/** | |
* @type {TransitionOptions} */ | |
const options = { direction: 'both' }; | |
let config = fn(node, params, options); | |
let t = intro ? 0 : 1; | |
/** | |
* @type {Program | null} */ | |
let running_program = null; | |
/** | |
* @type {PendingProgram | null} */ | |
let pending_program = null; | |
let animation_name = null; | |
/** @type {boolean} */ | |
let original_inert_value; | |
/** | |
* @returns {void} */ | |
function clear_animation() { | |
if (animation_name) delete_rule(node, animation_name); | |
} | |
/** | |
* @param {PendingProgram} program | |
* @param {number} duration | |
* @returns {Program} | |
*/ | |
function init(program, duration) { | |
const d = /** @type {Program['d']} */ (program.b - t); | |
duration *= Math.abs(d); | |
return { | |
a: t, | |
b: program.b, | |
d, | |
duration, | |
start: program.start, | |
end: program.start + duration, | |
group: program.group | |
}; | |
} | |
/** | |
* @param {INTRO | OUTRO} b | |
* @returns {void} | |
*/ | |
function go(b) { | |
const { | |
delay = 0, | |
duration = 300, | |
easing = identity, | |
tick = noop, | |
css | |
} = config || null_transition; | |
/** | |
* @type {PendingProgram} */ | |
const program = { | |
start: now() + delay, | |
b | |
}; | |
if (!b) { | |
// @ts-ignore todo: improve typings | |
program.group = outros; | |
outros.r += 1; | |
} | |
if ('inert' in node) { | |
if (b) { | |
if (original_inert_value !== undefined) { | |
// aborted/reversed outro — restore previous inert value | |
node.inert = original_inert_value; | |
} | |
} else { | |
original_inert_value = /** @type {HTMLElement} */ (node).inert; | |
node.inert = true; | |
} | |
} | |
if (running_program || pending_program) { | |
pending_program = program; | |
} else { | |
// if this is an intro, and there's a delay, we need to do | |
// an initial tick and/or apply CSS animation immediately | |
if (css) { | |
clear_animation(); | |
animation_name = create_rule(node, t, b, duration, delay, easing, css); | |
} | |
if (b) tick(0, 1); | |
running_program = init(program, duration); | |
add_render_callback(() => dispatch(node, b, 'start')); | |
loop((now) => { | |
if (pending_program && now > pending_program.start) { | |
running_program = init(pending_program, duration); | |
pending_program = null; | |
dispatch(node, running_program.b, 'start'); | |
if (css) { | |
clear_animation(); | |
animation_name = create_rule( | |
node, | |
t, | |
running_program.b, | |
running_program.duration, | |
0, | |
easing, | |
config.css | |
); | |
} | |
} | |
if (running_program) { | |
if (now >= running_program.end) { | |
tick((t = running_program.b), 1 - t); | |
dispatch(node, running_program.b, 'end'); | |
if (!pending_program) { | |
// we're done | |
if (running_program.b) { | |
// intro — we can tidy up immediately | |
clear_animation(); | |
} else { | |
// outro — needs to be coordinated | |
if (!--running_program.group.r) run_all(running_program.group.c); | |
} | |
} | |
running_program = null; | |
} else if (now >= running_program.start) { | |
const p = now - running_program.start; | |
t = running_program.a + running_program.d * easing(p / running_program.duration); | |
tick(t, 1 - t); | |
} | |
} | |
return !!(running_program || pending_program); | |
}); | |
} | |
} | |
return { | |
run(b) { | |
if (is_function(config)) { | |
wait().then(() => { | |
const opts = { direction: b ? 'in' : 'out' }; | |
// @ts-ignore | |
config = config(opts); | |
go(b); | |
}); | |
} else { | |
go(b); | |
} | |
}, | |
end() { | |
clear_animation(); | |
running_program = pending_program = null; | |
} | |
}; | |
} | |
/** @typedef {1} INTRO */ | |
/** @typedef {0} OUTRO */ | |
/** @typedef {{ direction: 'in' | 'out' | 'both' }} TransitionOptions */ | |
/** @typedef {(node: Element, params: any, options: TransitionOptions) => import('../transition/public.js').TransitionConfig} TransitionFn */ | |
/** | |
* @typedef {Object} Outro | |
* @property {number} r | |
* @property {Function[]} c | |
* @property {Object} p | |
*/ | |
/** | |
* @typedef {Object} PendingProgram | |
* @property {number} start | |
* @property {INTRO|OUTRO} b | |
* @property {Outro} [group] | |
*/ | |
/** | |
* @typedef {Object} Program | |
* @property {number} a | |
* @property {INTRO|OUTRO} b | |
* @property {1|-1} d | |
* @property {number} duration | |
* @property {number} start | |
* @property {number} end | |
* @property {Outro} [group] | |
*/ | |
/** | |
* @template T | |
* @param {Promise<T>} promise | |
* @param {import('./private.js').PromiseInfo<T>} info | |
* @returns {boolean} | |
*/ | |
function handle_promise(promise, info) { | |
const token = (info.token = {}); | |
/** | |
* @param {import('./private.js').FragmentFactory} type | |
* @param {0 | 1 | 2} index | |
* @param {number} [key] | |
* @param {any} [value] | |
* @returns {void} | |
*/ | |
function update(type, index, key, value) { | |
if (info.token !== token) return; | |
info.resolved = value; | |
let child_ctx = info.ctx; | |
if (key !== undefined) { | |
child_ctx = child_ctx.slice(); | |
child_ctx[key] = value; | |
} | |
const block = type && (info.current = type)(child_ctx); | |
let needs_flush = false; | |
if (info.block) { | |
if (info.blocks) { | |
info.blocks.forEach((block, i) => { | |
if (i !== index && block) { | |
group_outros(); | |
transition_out(block, 1, 1, () => { | |
if (info.blocks[i] === block) { | |
info.blocks[i] = null; | |
} | |
}); | |
check_outros(); | |
} | |
}); | |
} else { | |
info.block.d(1); | |
} | |
block.c(); | |
transition_in(block, 1); | |
block.m(info.mount(), info.anchor); | |
needs_flush = true; | |
} | |
info.block = block; | |
if (info.blocks) info.blocks[index] = block; | |
if (needs_flush) { | |
flush(); | |
} | |
} | |
if (is_promise(promise)) { | |
const current_component = get_current_component(); | |
promise.then( | |
(value) => { | |
set_current_component(current_component); | |
update(info.then, 1, info.value, value); | |
set_current_component(null); | |
}, | |
(error) => { | |
set_current_component(current_component); | |
update(info.catch, 2, info.error, error); | |
set_current_component(null); | |
if (!info.hasCatch) { | |
throw error; | |
} | |
} | |
); | |
// if we previously had a then/catch block, destroy it | |
if (info.current !== info.pending) { | |
update(info.pending, 0); | |
return true; | |
} | |
} else { | |
if (info.current !== info.then) { | |
update(info.then, 1, info.value, promise); | |
return true; | |
} | |
info.resolved = /** @type {T} */ (promise); | |
} | |
} | |
/** @returns {void} */ | |
function update_await_block_branch(info, ctx, dirty) { | |
const child_ctx = ctx.slice(); | |
const { resolved } = info; | |
if (info.current === info.then) { | |
child_ctx[info.value] = resolved; | |
} | |
if (info.current === info.catch) { | |
child_ctx[info.error] = resolved; | |
} | |
info.block.p(child_ctx, dirty); | |
} | |
// general each functions: | |
function ensure_array_like(array_like_or_iterator) { | |
return array_like_or_iterator?.length !== undefined | |
? array_like_or_iterator | |
: Array.from(array_like_or_iterator); | |
} | |
// keyed each functions: | |
/** @returns {void} */ | |
function destroy_block(block, lookup) { | |
block.d(1); | |
lookup.delete(block.key); | |
} | |
/** @returns {void} */ | |
function outro_and_destroy_block(block, lookup) { | |
transition_out(block, 1, 1, () => { | |
lookup.delete(block.key); | |
}); | |
} | |
/** @returns {void} */ | |
function fix_and_destroy_block(block, lookup) { | |
block.f(); | |
destroy_block(block, lookup); | |
} | |
/** @returns {void} */ | |
function fix_and_outro_and_destroy_block(block, lookup) { | |
block.f(); | |
outro_and_destroy_block(block, lookup); | |
} | |
/** @returns {any[]} */ | |
function update_keyed_each( | |
old_blocks, | |
dirty, | |
get_key, | |
dynamic, | |
ctx, | |
list, | |
lookup, | |
node, | |
destroy, | |
create_each_block, | |
next, | |
get_context | |
) { | |
let o = old_blocks.length; | |
let n = list.length; | |
let i = o; | |
const old_indexes = {}; | |
while (i--) old_indexes[old_blocks[i].key] = i; | |
const new_blocks = []; | |
const new_lookup = new Map(); | |
const deltas = new Map(); | |
const updates = []; | |
i = n; | |
while (i--) { | |
const child_ctx = get_context(ctx, list, i); | |
const key = get_key(child_ctx); | |
let block = lookup.get(key); | |
if (!block) { | |
block = create_each_block(key, child_ctx); | |
block.c(); | |
} else if (dynamic) { | |
// defer updates until all the DOM shuffling is done | |
updates.push(() => block.p(child_ctx, dirty)); | |
} | |
new_lookup.set(key, (new_blocks[i] = block)); | |
if (key in old_indexes) deltas.set(key, Math.abs(i - old_indexes[key])); | |
} | |
const will_move = new Set(); | |
const did_move = new Set(); | |
/** @returns {void} */ | |
function insert(block) { | |
transition_in(block, 1); | |
block.m(node, next); | |
lookup.set(block.key, block); | |
next = block.first; | |
n--; | |
} | |
while (o && n) { | |
const new_block = new_blocks[n - 1]; | |
const old_block = old_blocks[o - 1]; | |
const new_key = new_block.key; | |
const old_key = old_block.key; | |
if (new_block === old_block) { | |
// do nothing | |
next = new_block.first; | |
o--; | |
n--; | |
} else if (!new_lookup.has(old_key)) { | |
// remove old block | |
destroy(old_block, lookup); | |
o--; | |
} else if (!lookup.has(new_key) || will_move.has(new_key)) { | |
insert(new_block); | |
} else if (did_move.has(old_key)) { | |
o--; | |
} else if (deltas.get(new_key) > deltas.get(old_key)) { | |
did_move.add(new_key); | |
insert(new_block); | |
} else { | |
will_move.add(old_key); | |
o--; | |
} | |
} | |
while (o--) { | |
const old_block = old_blocks[o]; | |
if (!new_lookup.has(old_block.key)) destroy(old_block, lookup); | |
} | |
while (n) insert(new_blocks[n - 1]); | |
run_all(updates); | |
return new_blocks; | |
} | |
/** @returns {void} */ | |
function validate_each_keys(ctx, list, get_context, get_key) { | |
const keys = new Map(); | |
for (let i = 0; i < list.length; i++) { | |
const key = get_key(get_context(ctx, list, i)); | |
if (keys.has(key)) { | |
let value = ''; | |
try { | |
value = `with value '${String(key)}' `; | |
} catch (e) { | |
// can't stringify | |
} | |
throw new Error( | |
`Cannot have duplicate keys in a keyed each: Keys at index ${keys.get( | |
key | |
)} and ${i} ${value}are duplicates` | |
); | |
} | |
keys.set(key, i); | |
} | |
} | |
/** @returns {{}} */ | |
function get_spread_update(levels, updates) { | |
const update = {}; | |
const to_null_out = {}; | |
const accounted_for = { $$scope: 1 }; | |
let i = levels.length; | |
while (i--) { | |
const o = levels[i]; | |
const n = updates[i]; | |
if (n) { | |
for (const key in o) { | |
if (!(key in n)) to_null_out[key] = 1; | |
} | |
for (const key in n) { | |
if (!accounted_for[key]) { | |
update[key] = n[key]; | |
accounted_for[key] = 1; | |
} | |
} | |
levels[i] = n; | |
} else { | |
for (const key in o) { | |
accounted_for[key] = 1; | |
} | |
} | |
} | |
for (const key in to_null_out) { | |
if (!(key in update)) update[key] = undefined; | |
} | |
return update; | |
} | |
function get_spread_object(spread_props) { | |
return typeof spread_props === 'object' && spread_props !== null ? spread_props : {}; | |
} | |
const _boolean_attributes = /** @type {const} */ ([ | |
'allowfullscreen', | |
'allowpaymentrequest', | |
'async', | |
'autofocus', | |
'autoplay', | |
'checked', | |
'controls', | |
'default', | |
'defer', | |
'disabled', | |
'formnovalidate', | |
'hidden', | |
'inert', | |
'ismap', | |
'loop', | |
'multiple', | |
'muted', | |
'nomodule', | |
'novalidate', | |
'open', | |
'playsinline', | |
'readonly', | |
'required', | |
'reversed', | |
'selected' | |
]); | |
/** | |
* List of HTML boolean attributes (e.g. `<input disabled>`). | |
* Source: https://html.spec.whatwg.org/multipage/indices.html | |
* | |
* @type {Set<string>} | |
*/ | |
const boolean_attributes = new Set([..._boolean_attributes]); | |
/** @typedef {typeof _boolean_attributes[number]} BooleanAttributes */ | |
/** regex of all html void element names */ | |
const void_element_names = | |
/^(?:area|base|br|col|command|embed|hr|img|input|keygen|link|meta|param|source|track|wbr)$/; | |
/** | |
* @param {string} name | |
* @returns {boolean} | |
*/ | |
function is_void(name) { | |
return void_element_names.test(name) || name.toLowerCase() === '!doctype'; | |
} | |
const invalid_attribute_name_character = | |
/[\s'">/=\u{FDD0}-\u{FDEF}\u{FFFE}\u{FFFF}\u{1FFFE}\u{1FFFF}\u{2FFFE}\u{2FFFF}\u{3FFFE}\u{3FFFF}\u{4FFFE}\u{4FFFF}\u{5FFFE}\u{5FFFF}\u{6FFFE}\u{6FFFF}\u{7FFFE}\u{7FFFF}\u{8FFFE}\u{8FFFF}\u{9FFFE}\u{9FFFF}\u{AFFFE}\u{AFFFF}\u{BFFFE}\u{BFFFF}\u{CFFFE}\u{CFFFF}\u{DFFFE}\u{DFFFF}\u{EFFFE}\u{EFFFF}\u{FFFFE}\u{FFFFF}\u{10FFFE}\u{10FFFF}]/u; | |
// https://html.spec.whatwg.org/multipage/syntax.html#attributes-2 | |
// https://infra.spec.whatwg.org/#noncharacter | |
/** @returns {string} */ | |
function spread(args, attrs_to_add) { | |
const attributes = Object.assign({}, ...args); | |
if (attrs_to_add) { | |
const classes_to_add = attrs_to_add.classes; | |
const styles_to_add = attrs_to_add.styles; | |
if (classes_to_add) { | |
if (attributes.class == null) { | |
attributes.class = classes_to_add; | |
} else { | |
attributes.class += ' ' + classes_to_add; | |
} | |
} | |
if (styles_to_add) { | |
if (attributes.style == null) { | |
attributes.style = style_object_to_string(styles_to_add); | |
} else { | |
attributes.style = style_object_to_string( | |
merge_ssr_styles(attributes.style, styles_to_add) | |
); | |
} | |
} | |
} | |
let str = ''; | |
Object.keys(attributes).forEach((name) => { | |
if (invalid_attribute_name_character.test(name)) return; | |
const value = attributes[name]; | |
if (value === true) str += ' ' + name; | |
else if (boolean_attributes.has(name.toLowerCase())) { | |
if (value) str += ' ' + name; | |
} else if (value != null) { | |
str += ` ${name}="${value}"`; | |
} | |
}); | |
return str; | |
} | |
/** @returns {{}} */ | |
function merge_ssr_styles(style_attribute, style_directive) { | |
const style_object = {}; | |
for (const individual_style of style_attribute.split(';')) { | |
const colon_index = individual_style.indexOf(':'); | |
const name = individual_style.slice(0, colon_index).trim(); | |
const value = individual_style.slice(colon_index + 1).trim(); | |
if (!name) continue; | |
style_object[name] = value; | |
} | |
for (const name in style_directive) { | |
const value = style_directive[name]; | |
if (value) { | |
style_object[name] = value; | |
} else { | |
delete style_object[name]; | |
} | |
} | |
return style_object; | |
} | |
const ATTR_REGEX = /[&"]/g; | |
const CONTENT_REGEX = /[&<]/g; | |
/** | |
* Note: this method is performance sensitive and has been optimized | |
* https://github.com/sveltejs/svelte/pull/5701 | |
* @param {unknown} value | |
* @returns {string} | |
*/ | |
function escape(value, is_attr = false) { | |
const str = String(value); | |
const pattern = is_attr ? ATTR_REGEX : CONTENT_REGEX; | |
pattern.lastIndex = 0; | |
let escaped = ''; | |
let last = 0; | |
while (pattern.test(str)) { | |
const i = pattern.lastIndex - 1; | |
const ch = str[i]; | |
escaped += str.substring(last, i) + (ch === '&' ? '&' : ch === '"' ? '"' : '<'); | |
last = i + 1; | |
} | |
return escaped + str.substring(last); | |
} | |
function escape_attribute_value(value) { | |
// keep booleans, null, and undefined for the sake of `spread` | |
const should_escape = typeof value === 'string' || (value && typeof value === 'object'); | |
return should_escape ? escape(value, true) : value; | |
} | |
/** @returns {{}} */ | |
function escape_object(obj) { | |
const result = {}; | |
for (const key in obj) { | |
result[key] = escape_attribute_value(obj[key]); | |
} | |
return result; | |
} | |
/** @returns {string} */ | |
function each(items, fn) { | |
items = ensure_array_like(items); | |
let str = ''; | |
for (let i = 0; i < items.length; i += 1) { | |
str += fn(items[i], i); | |
} | |
return str; | |
} | |
const missing_component = { | |
$$render: () => '' | |
}; | |
function validate_component(component, name) { | |
if (!component || !component.$$render) { | |
if (name === 'svelte:component') name += ' this={...}'; | |
throw new Error( | |
`<${name}> is not a valid SSR component. You may need to review your build config to ensure that dependencies are compiled, rather than imported as pre-compiled modules. Otherwise you may need to fix a <${name}>.` | |
); | |
} | |
return component; | |
} | |
/** @returns {string} */ | |
function debug(file, line, column, values) { | |
console.log(`{@debug} ${file ? file + ' ' : ''}(${line}:${column})`); // eslint-disable-line no-console | |
console.log(values); // eslint-disable-line no-console | |
return ''; | |
} | |
let on_destroy; | |
/** @returns {{ render: (props?: {}, { $$slots, context }?: { $$slots?: {}; context?: Map<any, any>; }) => { html: any; css: { code: string; map: any; }; head: string; }; $$render: (result: any, props: any, bindings: any, slots: any, context: any) => any; }} */ | |
function create_ssr_component(fn) { | |
function $$render(result, props, bindings, slots, context) { | |
const parent_component = current_component; | |
const $$ = { | |
on_destroy, | |
context: new Map(context || (parent_component ? parent_component.$$.context : [])), | |
// these will be immediately discarded | |
on_mount: [], | |
before_update: [], | |
after_update: [], | |
callbacks: blank_object() | |
}; | |
set_current_component({ $$ }); | |
const html = fn(result, props, bindings, slots); | |
set_current_component(parent_component); | |
return html; | |
} | |
return { | |
render: (props = {}, { $$slots = {}, context = new Map() } = {}) => { | |
on_destroy = []; | |
const result = { title: '', head: '', css: new Set() }; | |
const html = $$render(result, props, {}, $$slots, context); | |
run_all(on_destroy); | |
return { | |
html, | |
css: { | |
code: Array.from(result.css) | |
.map((css) => css.code) | |
.join('\n'), | |
map: null // TODO | |
}, | |
head: result.title + result.head | |
}; | |
}, | |
$$render | |
}; | |
} | |
/** @returns {string} */ | |
function add_attribute(name, value, boolean) { | |
if (value == null || (boolean && !value)) return ''; | |
const assignment = boolean && value === true ? '' : `="${escape(value, true)}"`; | |
return ` ${name}${assignment}`; | |
} | |
/** @returns {string} */ | |
function add_classes(classes) { | |
return classes ? ` class="${classes}"` : ''; | |
} | |
/** @returns {string} */ | |
function style_object_to_string(style_object) { | |
return Object.keys(style_object) | |
.filter((key) => style_object[key]) | |
.map((key) => `${key}: ${escape_attribute_value(style_object[key])};`) | |
.join(' '); | |
} | |
/** @returns {string} */ | |
function add_styles(style_object) { | |
const styles = style_object_to_string(style_object); | |
return styles ? ` style="${styles}"` : ''; | |
} | |
/** @returns {void} */ | |
function bind(component, name, callback) { | |
const index = component.$$.props[name]; | |
if (index !== undefined) { | |
component.$$.bound[index] = callback; | |
callback(component.$$.ctx[index]); | |
} | |
} | |
/** @returns {void} */ | |
function create_component(block) { | |
block && block.c(); | |
} | |
/** @returns {void} */ | |
function claim_component(block, parent_nodes) { | |
block && block.l(parent_nodes); | |
} | |
/** @returns {void} */ | |
function mount_component(component, target, anchor) { | |
const { fragment, after_update } = component.$$; | |
fragment && fragment.m(target, anchor); | |
// onMount happens before the initial afterUpdate | |
add_render_callback(() => { | |
const new_on_destroy = component.$$.on_mount.map(run).filter(is_function); | |
// if the component was destroyed immediately | |
// it will update the `$$.on_destroy` reference to `null`. | |
// the destructured on_destroy may still reference to the old array | |
if (component.$$.on_destroy) { | |
component.$$.on_destroy.push(...new_on_destroy); | |
} else { | |
// Edge case - component was destroyed immediately, | |
// most likely as a result of a binding initialising | |
run_all(new_on_destroy); | |
} | |
component.$$.on_mount = []; | |
}); | |
after_update.forEach(add_render_callback); | |
} | |
/** @returns {void} */ | |
function destroy_component(component, detaching) { | |
const $$ = component.$$; | |
if ($$.fragment !== null) { | |
flush_render_callbacks($$.after_update); | |
run_all($$.on_destroy); | |
$$.fragment && $$.fragment.d(detaching); | |
// TODO null out other refs, including component.$$ (but need to | |
// preserve final state?) | |
$$.on_destroy = $$.fragment = null; | |
$$.ctx = []; | |
} | |
} | |
/** @returns {void} */ | |
function make_dirty(component, i) { | |
if (component.$$.dirty[0] === -1) { | |
dirty_components.push(component); | |
schedule_update(); | |
component.$$.dirty.fill(0); | |
} | |
component.$$.dirty[(i / 31) | 0] |= 1 << i % 31; | |
} | |
// TODO: Document the other params | |
/** | |
* @param {SvelteComponent} component | |
* @param {import('./public.js').ComponentConstructorOptions} options | |
* | |
* @param {import('./utils.js')['not_equal']} not_equal Used to compare props and state values. | |
* @param {(target: Element | ShadowRoot) => void} [append_styles] Function that appends styles to the DOM when the component is first initialised. | |
* This will be the `add_css` function from the compiled component. | |
* | |
* @returns {void} | |
*/ | |
function init( | |
component, | |
options, | |
instance, | |
create_fragment, | |
not_equal, | |
props, | |
append_styles = null, | |
dirty = [-1] | |
) { | |
const parent_component = current_component; | |
set_current_component(component); | |
/** @type {import('./private.js').T$$} */ | |
const $$ = (component.$$ = { | |
fragment: null, | |
ctx: [], | |
// state | |
props, | |
update: noop, | |
not_equal, | |
bound: blank_object(), | |
// lifecycle | |
on_mount: [], | |
on_destroy: [], | |
on_disconnect: [], | |
before_update: [], | |
after_update: [], | |
context: new Map(options.context || (parent_component ? parent_component.$$.context : [])), | |
// everything else | |
callbacks: blank_object(), | |
dirty, | |
skip_bound: false, | |
root: options.target || parent_component.$$.root | |
}); | |
append_styles && append_styles($$.root); | |
let ready = false; | |
$$.ctx = instance | |
? instance(component, options.props || {}, (i, ret, ...rest) => { | |
const value = rest.length ? rest[0] : ret; | |
if ($$.ctx && not_equal($$.ctx[i], ($$.ctx[i] = value))) { | |
if (!$$.skip_bound && $$.bound[i]) $$.bound[i](value); | |
if (ready) make_dirty(component, i); | |
} | |
return ret; | |
}) | |
: []; | |
$$.update(); | |
ready = true; | |
run_all($$.before_update); | |
// `false` as a special case of no DOM component | |
$$.fragment = create_fragment ? create_fragment($$.ctx) : false; | |
if (options.target) { | |
if (options.hydrate) { | |
start_hydrating(); | |
// TODO: what is the correct type here? | |
// @ts-expect-error | |
const nodes = children(options.target); | |
$$.fragment && $$.fragment.l(nodes); | |
nodes.forEach(detach); | |
} else { | |
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion | |
$$.fragment && $$.fragment.c(); | |
} | |
if (options.intro) transition_in(component.$$.fragment); | |
mount_component(component, options.target, options.anchor); | |
end_hydrating(); | |
flush(); | |
} | |
set_current_component(parent_component); | |
} | |
let SvelteElement; | |
if (typeof HTMLElement === 'function') { | |
SvelteElement = class extends HTMLElement { | |
/** The Svelte component constructor */ | |
$$ctor; | |
/** Slots */ | |
$$s; | |
/** The Svelte component instance */ | |
$$c; | |
/** Whether or not the custom element is connected */ | |
$$cn = false; | |
/** Component props data */ | |
$$d = {}; | |
/** `true` if currently in the process of reflecting component props back to attributes */ | |
$$r = false; | |
/** @type {Record<string, CustomElementPropDefinition>} Props definition (name, reflected, type etc) */ | |
$$p_d = {}; | |
/** @type {Record<string, Function[]>} Event listeners */ | |
$$l = {}; | |
/** @type {Map<Function, Function>} Event listener unsubscribe functions */ | |
$$l_u = new Map(); | |
constructor($$componentCtor, $$slots, use_shadow_dom) { | |
super(); | |
this.$$ctor = $$componentCtor; | |
this.$$s = $$slots; | |
if (use_shadow_dom) { | |
this.attachShadow({ mode: 'open' }); | |
} | |
} | |
addEventListener(type, listener, options) { | |
// We can't determine upfront if the event is a custom event or not, so we have to | |
// listen to both. If someone uses a custom event with the same name as a regular | |
// browser event, this fires twice - we can't avoid that. | |
this.$$l[type] = this.$$l[type] || []; | |
this.$$l[type].push(listener); | |
if (this.$$c) { | |
const unsub = this.$$c.$on(type, listener); | |
this.$$l_u.set(listener, unsub); | |
} | |
super.addEventListener(type, listener, options); | |
} | |
removeEventListener(type, listener, options) { | |
super.removeEventListener(type, listener, options); | |
if (this.$$c) { | |
const unsub = this.$$l_u.get(listener); | |
if (unsub) { | |
unsub(); | |
this.$$l_u.delete(listener); | |
} | |
} | |
} | |
async connectedCallback() { | |
this.$$cn = true; | |
if (!this.$$c) { | |
// We wait one tick to let possible child slot elements be created/mounted | |
await Promise.resolve(); | |
if (!this.$$cn) { | |
return; | |
} | |
function create_slot(name) { | |
return () => { | |
let node; | |
const obj = { | |
c: function create() { | |
node = element('slot'); | |
if (name !== 'default') { | |
attr(node, 'name', name); | |
} | |
}, | |
/** | |
* @param {HTMLElement} target | |
* @param {HTMLElement} [anchor] | |
*/ | |
m: function mount(target, anchor) { | |
insert(target, node, anchor); | |
}, | |
d: function destroy(detaching) { | |
if (detaching) { | |
detach(node); | |
} | |
} | |
}; | |
return obj; | |
}; | |
} | |
const $$slots = {}; | |
const existing_slots = get_custom_elements_slots(this); | |
for (const name of this.$$s) { | |
if (name in existing_slots) { | |
$$slots[name] = [create_slot(name)]; | |
} | |
} | |
for (const attribute of this.attributes) { | |
// this.$$data takes precedence over this.attributes | |
const name = this.$$g_p(attribute.name); | |
if (!(name in this.$$d)) { | |
this.$$d[name] = get_custom_element_value(name, attribute.value, this.$$p_d, 'toProp'); | |
} | |
} | |
this.$$c = new this.$$ctor({ | |
target: this.shadowRoot || this, | |
props: { | |
...this.$$d, | |
$$slots, | |
$$scope: { | |
ctx: [] | |
} | |
} | |
}); | |
// Reflect component props as attributes | |
const reflect_attributes = () => { | |
this.$$r = true; | |
for (const key in this.$$p_d) { | |
this.$$d[key] = this.$$c.$$.ctx[this.$$c.$$.props[key]]; | |
if (this.$$p_d[key].reflect) { | |
const attribute_value = get_custom_element_value( | |
key, | |
this.$$d[key], | |
this.$$p_d, | |
'toAttribute' | |
); | |
if (attribute_value == null) { | |
this.removeAttribute(this.$$p_d[key].attribute || key); | |
} else { | |
this.setAttribute(this.$$p_d[key].attribute || key, attribute_value); | |
} | |
} | |
} | |
this.$$r = false; | |
}; | |
this.$$c.$$.after_update.push(reflect_attributes); | |
reflect_attributes(); // once initially because after_update is added too late for first render | |
for (const type in this.$$l) { | |
for (const listener of this.$$l[type]) { | |
const unsub = this.$$c.$on(type, listener); | |
this.$$l_u.set(listener, unsub); | |
} | |
} | |
this.$$l = {}; | |
} | |
} | |
// We don't need this when working within Svelte code, but for compatibility of people using this outside of Svelte | |
// and setting attributes through setAttribute etc, this is helpful | |
attributeChangedCallback(attr, _oldValue, newValue) { | |
if (this.$$r) return; | |
attr = this.$$g_p(attr); | |
this.$$d[attr] = get_custom_element_value(attr, newValue, this.$$p_d, 'toProp'); | |
this.$$c?.$set({ [attr]: this.$$d[attr] }); | |
} | |
disconnectedCallback() { | |
this.$$cn = false; | |
// In a microtask, because this could be a move within the DOM | |
Promise.resolve().then(() => { | |
if (!this.$$cn) { | |
this.$$c.$destroy(); | |
this.$$c = undefined; | |
} | |
}); | |
} | |
$$g_p(attribute_name) { | |
return ( | |
Object.keys(this.$$p_d).find( | |
(key) => | |
this.$$p_d[key].attribute === attribute_name || | |
(!this.$$p_d[key].attribute && key.toLowerCase() === attribute_name) | |
) || attribute_name | |
); | |
} | |
}; | |
} | |
/** | |
* @param {string} prop | |
* @param {any} value | |
* @param {Record<string, CustomElementPropDefinition>} props_definition | |
* @param {'toAttribute' | 'toProp'} [transform] | |
*/ | |
function get_custom_element_value(prop, value, props_definition, transform) { | |
const type = props_definition[prop]?.type; | |
value = type === 'Boolean' && typeof value !== 'boolean' ? value != null : value; | |
if (!transform || !props_definition[prop]) { | |
return value; | |
} else if (transform === 'toAttribute') { | |
switch (type) { | |
case 'Object': | |
case 'Array': | |
return value == null ? null : JSON.stringify(value); | |
case 'Boolean': | |
return value ? '' : null; | |
case 'Number': | |
return value == null ? null : value; | |
default: | |
return value; | |
} | |
} else { | |
switch (type) { | |
case 'Object': | |
case 'Array': | |
return value && JSON.parse(value); | |
case 'Boolean': | |
return value; // conversion already handled above | |
case 'Number': | |
return value != null ? +value : value; | |
default: | |
return value; | |
} | |
} | |
} | |
/** | |
* @internal | |
* | |
* Turn a Svelte component into a custom element. | |
* @param {import('./public.js').ComponentType} Component A Svelte component constructor | |
* @param {Record<string, CustomElementPropDefinition>} props_definition The props to observe | |
* @param {string[]} slots The slots to create | |
* @param {string[]} accessors Other accessors besides the ones for props the component has | |
* @param {boolean} use_shadow_dom Whether to use shadow DOM | |
* @param {(ce: new () => HTMLElement) => new () => HTMLElement} [extend] | |
*/ | |
function create_custom_element( | |
Component, | |
props_definition, | |
slots, | |
accessors, | |
use_shadow_dom, | |
extend | |
) { | |
let Class = class extends SvelteElement { | |
constructor() { | |
super(Component, slots, use_shadow_dom); | |
this.$$p_d = props_definition; | |
} | |
static get observedAttributes() { | |
return Object.keys(props_definition).map((key) => | |
(props_definition[key].attribute || key).toLowerCase() | |
); | |
} | |
}; | |
Object.keys(props_definition).forEach((prop) => { | |
Object.defineProperty(Class.prototype, prop, { | |
get() { | |
return this.$$c && prop in this.$$c ? this.$$c[prop] : this.$$d[prop]; | |
}, | |
set(value) { | |
value = get_custom_element_value(prop, value, props_definition); | |
this.$$d[prop] = value; | |
this.$$c?.$set({ [prop]: value }); | |
} | |
}); | |
}); | |
accessors.forEach((accessor) => { | |
Object.defineProperty(Class.prototype, accessor, { | |
get() { | |
return this.$$c?.[accessor]; | |
} | |
}); | |
}); | |
if (extend) { | |
// @ts-expect-error - assigning here is fine | |
Class = extend(Class); | |
} | |
Component.element = /** @type {any} */ (Class); | |
return Class; | |
} | |
/** | |
* Base class for Svelte components. Used when dev=false. | |
* | |
* @template {Record<string, any>} [Props=any] | |
* @template {Record<string, any>} [Events=any] | |
*/ | |
class SvelteComponent { | |
/** | |
* ### PRIVATE API | |
* | |
* Do not use, may change at any time | |
* | |
* @type {any} | |
*/ | |
$$ = undefined; | |
/** | |
* ### PRIVATE API | |
* | |
* Do not use, may change at any time | |
* | |
* @type {any} | |
*/ | |
$$set = undefined; | |
/** @returns {void} */ | |
$destroy() { | |
destroy_component(this, 1); | |
this.$destroy = noop; | |
} | |
/** | |
* @template {Extract<keyof Events, string>} K | |
* @param {K} type | |
* @param {((e: Events[K]) => void) | null | undefined} callback | |
* @returns {() => void} | |
*/ | |
$on(type, callback) { | |
if (!is_function(callback)) { | |
return noop; | |
} | |
const callbacks = this.$$.callbacks[type] || (this.$$.callbacks[type] = []); | |
callbacks.push(callback); | |
return () => { | |
const index = callbacks.indexOf(callback); | |
if (index !== -1) callbacks.splice(index, 1); | |
}; | |
} | |
/** | |
* @param {Partial<Props>} props | |
* @returns {void} | |
*/ | |
$set(props) { | |
if (this.$$set && !is_empty(props)) { | |
this.$$.skip_bound = true; | |
this.$$set(props); | |
this.$$.skip_bound = false; | |
} | |
} | |
} | |
/** | |
* @typedef {Object} CustomElementPropDefinition | |
* @property {string} [attribute] | |
* @property {boolean} [reflect] | |
* @property {'String'|'Boolean'|'Number'|'Array'|'Object'} [type] | |
*/ | |
// generated during release, do not modify | |
/** | |
* The current version, as set in package.json. | |
* | |
* https://svelte.dev/docs/svelte-compiler#svelte-version | |
* @type {string} | |
*/ | |
const VERSION = '4.2.2'; | |
/** | |
* @template T | |
* @param {string} type | |
* @param {T} [detail] | |
* @returns {void} | |
*/ | |
function dispatch_dev(type, detail) { | |
document.dispatchEvent(custom_event(type, { version: VERSION, ...detail }, { bubbles: true })); | |
} | |
/** | |
* @param {Node} target | |
* @param {Node} node | |
* @returns {void} | |
*/ | |
function append_dev(target, node) { | |
dispatch_dev('SvelteDOMInsert', { target, node }); | |
append(target, node); | |
} | |
/** | |
* @param {Node} target | |
* @param {Node} node | |
* @returns {void} | |
*/ | |
function append_hydration_dev(target, node) { | |
dispatch_dev('SvelteDOMInsert', { target, node }); | |
append_hydration(target, node); | |
} | |
/** | |
* @param {Node} target | |
* @param {Node} node | |
* @param {Node} [anchor] | |
* @returns {void} | |
*/ | |
function insert_dev(target, node, anchor) { | |
dispatch_dev('SvelteDOMInsert', { target, node, anchor }); | |
insert(target, node, anchor); | |
} | |
/** @param {Node} target | |
* @param {Node} node | |
* @param {Node} [anchor] | |
* @returns {void} | |
*/ | |
function insert_hydration_dev(target, node, anchor) { | |
dispatch_dev('SvelteDOMInsert', { target, node, anchor }); | |
insert_hydration(target, node, anchor); | |
} | |
/** | |
* @param {Node} node | |
* @returns {void} | |
*/ | |
function detach_dev(node) { | |
dispatch_dev('SvelteDOMRemove', { node }); | |
detach(node); | |
} | |
/** | |
* @param {Node} before | |
* @param {Node} after | |
* @returns {void} | |
*/ | |
function detach_between_dev(before, after) { | |
while (before.nextSibling && before.nextSibling !== after) { | |
detach_dev(before.nextSibling); | |
} | |
} | |
/** | |
* @param {Node} after | |
* @returns {void} | |
*/ | |
function detach_before_dev(after) { | |
while (after.previousSibling) { | |
detach_dev(after.previousSibling); | |
} | |
} | |
/** | |
* @param {Node} before | |
* @returns {void} | |
*/ | |
function detach_after_dev(before) { | |
while (before.nextSibling) { | |
detach_dev(before.nextSibling); | |
} | |
} | |
/** | |
* @param {Node} node | |
* @param {string} event | |
* @param {EventListenerOrEventListenerObject} handler | |
* @param {boolean | AddEventListenerOptions | EventListenerOptions} [options] | |
* @param {boolean} [has_prevent_default] | |
* @param {boolean} [has_stop_propagation] | |
* @param {boolean} [has_stop_immediate_propagation] | |
* @returns {() => void} | |
*/ | |
function listen_dev( | |
node, | |
event, | |
handler, | |
options, | |
has_prevent_default, | |
has_stop_propagation, | |
has_stop_immediate_propagation | |
) { | |
const modifiers = | |
options === true ? ['capture'] : options ? Array.from(Object.keys(options)) : []; | |
if (has_prevent_default) modifiers.push('preventDefault'); | |
if (has_stop_propagation) modifiers.push('stopPropagation'); | |
if (has_stop_immediate_propagation) modifiers.push('stopImmediatePropagation'); | |
dispatch_dev('SvelteDOMAddEventListener', { node, event, handler, modifiers }); | |
const dispose = listen(node, event, handler, options); | |
return () => { | |
dispatch_dev('SvelteDOMRemoveEventListener', { node, event, handler, modifiers }); | |
dispose(); | |
}; | |
} | |
/** | |
* @param {Element} node | |
* @param {string} attribute | |
* @param {string} [value] | |
* @returns {void} | |
*/ | |
function attr_dev(node, attribute, value) { | |
attr(node, attribute, value); | |
if (value == null) dispatch_dev('SvelteDOMRemoveAttribute', { node, attribute }); | |
else dispatch_dev('SvelteDOMSetAttribute', { node, attribute, value }); | |
} | |
/** | |
* @param {Element} node | |
* @param {string} property | |
* @param {any} [value] | |
* @returns {void} | |
*/ | |
function prop_dev(node, property, value) { | |
node[property] = value; | |
dispatch_dev('SvelteDOMSetProperty', { node, property, value }); | |
} | |
/** | |
* @param {HTMLElement} node | |
* @param {string} property | |
* @param {any} [value] | |
* @returns {void} | |
*/ | |
function dataset_dev(node, property, value) { | |
node.dataset[property] = value; | |
dispatch_dev('SvelteDOMSetDataset', { node, property, value }); | |
} | |
/** | |
* @param {Text} text | |
* @param {unknown} data | |
* @returns {void} | |
*/ | |
function set_data_dev(text, data) { | |
data = '' + data; | |
if (text.data === data) return; | |
dispatch_dev('SvelteDOMSetData', { node: text, data }); | |
text.data = /** @type {string} */ (data); | |
} | |
/** | |
* @param {Text} text | |
* @param {unknown} data | |
* @returns {void} | |
*/ | |
function set_data_contenteditable_dev(text, data) { | |
data = '' + data; | |
if (text.wholeText === data) return; | |
dispatch_dev('SvelteDOMSetData', { node: text, data }); | |
text.data = /** @type {string} */ (data); | |
} | |
/** | |
* @param {Text} text | |
* @param {unknown} data | |
* @param {string} attr_value | |
* @returns {void} | |
*/ | |
function set_data_maybe_contenteditable_dev(text, data, attr_value) { | |
if (~contenteditable_truthy_values.indexOf(attr_value)) { | |
set_data_contenteditable_dev(text, data); | |
} else { | |
set_data_dev(text, data); | |
} | |
} | |
function ensure_array_like_dev(arg) { | |
if ( | |
typeof arg !== 'string' && | |
!(arg && typeof arg === 'object' && 'length' in arg) && | |
!(typeof Symbol === 'function' && arg && Symbol.iterator in arg) | |
) { | |
throw new Error('{#each} only works with iterable values.'); | |
} | |
return ensure_array_like(arg); | |
} | |
/** | |
* @returns {void} */ | |
function validate_slots(name, slot, keys) { | |
for (const slot_key of Object.keys(slot)) { | |
if (!~keys.indexOf(slot_key)) { | |
console.warn(`<${name}> received an unexpected slot "${slot_key}".`); | |
} | |
} | |
} | |
/** | |
* @param {unknown} tag | |
* @returns {void} | |
*/ | |
function validate_dynamic_element(tag) { | |
const is_string = typeof tag === 'string'; | |
if (tag && !is_string) { | |
throw new Error('<svelte:element> expects "this" attribute to be a string.'); | |
} | |
} | |
/** | |
* @param {undefined | string} tag | |
* @returns {void} | |
*/ | |
function validate_void_dynamic_element(tag) { | |
if (tag && is_void(tag)) { | |
console.warn(`<svelte:element this="${tag}"> is self-closing and cannot have content.`); | |
} | |
} | |
function construct_svelte_component_dev(component, props) { | |
const error_message = 'this={...} of <svelte:component> should specify a Svelte component.'; | |
try { | |
const instance = new component(props); | |
if (!instance.$$ || !instance.$set || !instance.$on || !instance.$destroy) { | |
throw new Error(error_message); | |
} | |
return instance; | |
} catch (err) { | |
const { message } = err; | |
if (typeof message === 'string' && message.indexOf('is not a constructor') !== -1) { | |
throw new Error(error_message); | |
} else { | |
throw err; | |
} | |
} | |
} | |
/** | |
* Base class for Svelte components with some minor dev-enhancements. Used when dev=true. | |
* | |
* Can be used to create strongly typed Svelte components. | |
* | |
* #### Example: | |
* | |
* You have component library on npm called `component-library`, from which | |
* you export a component called `MyComponent`. For Svelte+TypeScript users, | |
* you want to provide typings. Therefore you create a `index.d.ts`: | |
* ```ts | |
* import { SvelteComponent } from "svelte"; | |
* export class MyComponent extends SvelteComponent<{foo: string}> {} | |
* ``` | |
* Typing this makes it possible for IDEs like VS Code with the Svelte extension | |
* to provide intellisense and to use the component like this in a Svelte file | |
* with TypeScript: | |
* ```svelte | |
* <script lang="ts"> | |
* import { MyComponent } from "component-library"; | |
* </script> | |
* <MyComponent foo={'bar'} /> | |
* ``` | |
* @template {Record<string, any>} [Props=any] | |
* @template {Record<string, any>} [Events=any] | |
* @template {Record<string, any>} [Slots=any] | |
* @extends {SvelteComponent<Props, Events>} | |
*/ | |
class SvelteComponentDev extends SvelteComponent { | |
/** | |
* For type checking capabilities only. | |
* Does not exist at runtime. | |
* ### DO NOT USE! | |
* | |
* @type {Props} | |
*/ | |
$$prop_def; | |
/** | |
* For type checking capabilities only. | |
* Does not exist at runtime. | |
* ### DO NOT USE! | |
* | |
* @type {Events} | |
*/ | |
$$events_def; | |
/** | |
* For type checking capabilities only. | |
* Does not exist at runtime. | |
* ### DO NOT USE! | |
* | |
* @type {Slots} | |
*/ | |
$$slot_def; | |
/** @param {import('./public.js').ComponentConstructorOptions<Props>} options */ | |
constructor(options) { | |
if (!options || (!options.target && !options.$$inline)) { | |
throw new Error("'target' is a required option"); | |
} | |
super(); | |
} | |
/** @returns {void} */ | |
$destroy() { | |
super.$destroy(); | |
this.$destroy = () => { | |
console.warn('Component was already destroyed'); // eslint-disable-line no-console | |
}; | |
} | |
/** @returns {void} */ | |
$capture_state() {} | |
/** @returns {void} */ | |
$inject_state() {} | |
} | |
/** | |
* @template {Record<string, any>} [Props=any] | |
* @template {Record<string, any>} [Events=any] | |
* @template {Record<string, any>} [Slots=any] | |
* @deprecated Use `SvelteComponent` instead. See PR for more information: https://github.com/sveltejs/svelte/pull/8512 | |
* @extends {SvelteComponentDev<Props, Events, Slots>} | |
*/ | |
class SvelteComponentTyped extends SvelteComponentDev {} | |
/** @returns {() => void} */ | |
function loop_guard(timeout) { | |
const start = Date.now(); | |
return () => { | |
if (Date.now() - start > timeout) { | |
throw new Error('Infinite loop detected'); | |
} | |
}; | |
} | |
export { HtmlTag, HtmlTagHydration, ResizeObserverSingleton, SvelteComponent, SvelteComponentDev, SvelteComponentTyped, SvelteElement, action_destroyer, add_attribute, add_classes, add_flush_callback, add_iframe_resize_listener, add_location, add_render_callback, add_styles, add_transform, afterUpdate, append, append_dev, append_empty_stylesheet, append_hydration, append_hydration_dev, append_styles, assign, attr, attr_dev, attribute_to_object, beforeUpdate, bind, binding_callbacks, blank_object, bubble, check_outros, children, claim_comment, claim_component, claim_element, claim_html_tag, claim_space, claim_svg_element, claim_text, clear_loops, comment, component_subscribe, compute_rest_props, compute_slots, construct_svelte_component, construct_svelte_component_dev, contenteditable_truthy_values, createEventDispatcher, create_animation, create_bidirectional_transition, create_component, create_custom_element, create_in_transition, create_out_transition, create_slot, create_ssr_component, current_component, custom_event, dataset_dev, debug, destroy_block, destroy_component, destroy_each, detach, detach_after_dev, detach_before_dev, detach_between_dev, detach_dev, dirty_components, dispatch_dev, each, element, element_is, empty, end_hydrating, ensure_array_like, ensure_array_like_dev, escape, escape_attribute_value, escape_object, exclude_internal_props, fix_and_destroy_block, fix_and_outro_and_destroy_block, fix_position, flush, flush_render_callbacks, getAllContexts, getContext, get_all_dirty_from_scope, get_binding_group_value, get_current_component, get_custom_elements_slots, get_root_for_style, get_slot_changes, get_spread_object, get_spread_update, get_store_value, get_svelte_dataset, globals, group_outros, handle_promise, hasContext, has_prop, head_selector, identity, init, init_binding_group, init_binding_group_dynamic, insert, insert_dev, insert_hydration, insert_hydration_dev, intros, invalid_attribute_name_character, is_client, is_crossorigin, is_empty, is_function, is_promise, is_void, listen, listen_dev, loop, loop_guard, merge_ssr_styles, missing_component, mount_component, noop, not_equal, now, null_to_empty, object_without_properties, onDestroy, onMount, once, outro_and_destroy_block, prevent_default, prop_dev, query_selector_all, raf, resize_observer_border_box, resize_observer_content_box, resize_observer_device_pixel_content_box, run, run_all, safe_not_equal, schedule_update, select_multiple_value, select_option, select_options, select_value, self, setContext, set_attributes, set_current_component, set_custom_element_data, set_custom_element_data_map, set_data, set_data_contenteditable, set_data_contenteditable_dev, set_data_dev, set_data_maybe_contenteditable, set_data_maybe_contenteditable_dev, set_dynamic_element_data, set_input_type, set_input_value, set_now, set_raf, set_store_value, set_style, set_svg_attributes, space, split_css_unit, spread, src_url_equal, srcset_url_equal, start_hydrating, stop_immediate_propagation, stop_propagation, subscribe, svg_element, text, tick, time_ranges_to_array, to_number, toggle_class, transition_in, transition_out, trusted, update_await_block_branch, update_keyed_each, update_slot, update_slot_base, validate_component, validate_dynamic_element, validate_each_keys, validate_slots, validate_store, validate_void_dynamic_element, xlink_attr }; | |