Created
April 12, 2024 10:19
-
-
Save hexagon6/c80a850741ef1f7ecdb9e36c5bb5839f to your computer and use it in GitHub Desktop.
visualize tables web component build test
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// generated during release, do not modify | |
const PUBLIC_VERSION = '5' | |
if (typeof window !== 'undefined') | |
// @ts-ignore | |
(window.__svelte ||= { v: new Set() }).v.add(PUBLIC_VERSION) | |
const DERIVED = 1 << 1 | |
const EFFECT = 1 << 2 | |
const RENDER_EFFECT = 1 << 3 | |
const BLOCK_EFFECT = 1 << 4 | |
const BRANCH_EFFECT = 1 << 5 | |
const ROOT_EFFECT = 1 << 6 | |
const UNOWNED = 1 << 7 | |
const CLEAN = 1 << 8 | |
const DIRTY = 1 << 9 | |
const MAYBE_DIRTY = 1 << 10 | |
const INERT = 1 << 11 | |
const DESTROYED = 1 << 12 | |
const IS_ELSEIF = 1 << 13 | |
const EFFECT_RAN = 1 << 14 | |
const STATE_SYMBOL = Symbol('$state') | |
// Store the references to globals in case someone tries to monkey patch these, causing the below | |
// to de-opt (this occurs often when using popular extensions). | |
var is_array = Array.isArray | |
var array_from = Array.from | |
var object_keys = Object.keys | |
var is_frozen = Object.isFrozen | |
var define_property = Object.defineProperty | |
var get_descriptor = Object.getOwnPropertyDescriptor | |
var get_descriptors = Object.getOwnPropertyDescriptors | |
var object_prototype = Object.prototype | |
var array_prototype = Array.prototype | |
var get_prototype_of = Object.getPrototypeOf | |
/** @type {import('#client').Equals} */ | |
function equals$1(value) { | |
return value === this.v | |
} | |
/** | |
* @param {unknown} a | |
* @param {unknown} b | |
* @returns {boolean} | |
*/ | |
function safe_not_equal(a, b) { | |
// eslint-disable-next-line eqeqeq | |
return a != a | |
? // eslint-disable-next-line eqeqeq | |
b == b | |
: a !== b || | |
(a !== null && typeof a === 'object') || | |
typeof a === 'function' | |
} | |
/** @type {import('#client').Equals} */ | |
function safe_equals(value) { | |
return !safe_not_equal(value, this.v) | |
} | |
const EACH_ITEM_REACTIVE = 1 | |
const EACH_INDEX_REACTIVE = 1 << 1 | |
const EACH_KEYED = 1 << 2 | |
/** See EachBlock interface metadata.is_controlled for an explanation what this is */ | |
const EACH_IS_CONTROLLED = 1 << 3 | |
const EACH_IS_STRICT_EQUALS = 1 << 6 | |
const PROPS_IS_IMMUTABLE = 1 | |
const PROPS_IS_RUNES = 1 << 1 | |
const PROPS_IS_UPDATED = 1 << 2 | |
const PROPS_IS_LAZY_INITIAL = 1 << 3 | |
const TEMPLATE_FRAGMENT = 1 | |
const TEMPLATE_USE_IMPORT_NODE = 1 << 1 | |
const HYDRATION_START = '[' | |
const HYDRATION_END = ']' | |
const HYDRATION_END_ELSE = `${HYDRATION_END}!` // used to indicate that an `{:else}...` block was rendered | |
const UNINITIALIZED = Symbol() | |
/** List of Element events that will be delegated and are passive */ | |
const PassiveDelegatedEvents = ['touchstart', 'touchmove', 'touchend'] | |
/** | |
* @template V | |
* @param {V} value | |
* @returns {import('#client').Source<V>} | |
*/ | |
/*#__NO_SIDE_EFFECTS__*/ | |
function source(value) { | |
/** @type {import('#client').Source<V>} */ | |
const source = { | |
f: 0, // TODO ideally we could skip this altogether, but it causes type errors | |
reactions: null, | |
equals: equals$1, | |
v: value, | |
version: 0, | |
} | |
return source | |
} | |
/** | |
* @template V | |
* @param {V} initial_value | |
* @returns {import('#client').Source<V>} | |
*/ | |
/*#__NO_SIDE_EFFECTS__*/ | |
function mutable_source(initial_value) { | |
const s = source(initial_value) | |
s.equals = safe_equals | |
// bind the signal to the component context, in case we need to | |
// track updates to trigger beforeUpdate/afterUpdate callbacks | |
if (current_component_context) { | |
;(current_component_context.d ??= []).push(s) | |
} | |
return s | |
} | |
/** | |
* @template V | |
* @param {import('#client').Source<V>} signal | |
* @param {V} value | |
* @returns {V} | |
*/ | |
function set$2(signal, value) { | |
var initialized = signal.v !== UNINITIALIZED | |
if ( | |
!current_untracking && | |
initialized && | |
current_reaction !== null && | |
is_runes() && | |
(current_reaction.f & DERIVED) !== 0 | |
) { | |
throw new Error('ERR_SVELTE_UNSAFE_MUTATION' + '') | |
} | |
if (!signal.equals(value)) { | |
signal.v = value | |
// Increment write version so that unowned signals can properly track dirtiness | |
signal.version++ | |
// If the current signal is running for the first time, it won't have any | |
// reactions as we only allocate and assign the reactions after the signal | |
// has fully executed. So in the case of ensuring it registers the reaction | |
// properly for itself, we need to ensure the current effect actually gets | |
// scheduled. i.e: | |
// | |
// $effect(() => x++) | |
// | |
// We additionally want to skip this logic when initialising store sources | |
if ( | |
is_runes() && | |
initialized && | |
current_effect !== null && | |
(current_effect.f & CLEAN) !== 0 && | |
(current_effect.f & BRANCH_EFFECT) === 0 | |
) { | |
if ( | |
current_dependencies !== null && | |
current_dependencies.includes(signal) | |
) { | |
set_signal_status(current_effect, DIRTY) | |
schedule_effect(current_effect) | |
} else { | |
if (current_untracked_writes === null) { | |
set_current_untracked_writes([signal]) | |
} else { | |
current_untracked_writes.push(signal) | |
} | |
} | |
} | |
mark_reactions(signal, DIRTY, true) | |
} | |
return value | |
} | |
/** @param {string} html */ | |
function create_fragment_from_html(html) { | |
var elem = document.createElement('template') | |
elem.innerHTML = html | |
return elem.content | |
} | |
/** | |
* @param {import('#client').Dom} current | |
*/ | |
function remove$1(current) { | |
if (is_array(current)) { | |
for (var i = 0; i < current.length; i++) { | |
var node = current[i] | |
if (node.isConnected) { | |
node.remove() | |
} | |
} | |
} else if (current.isConnected) { | |
current.remove() | |
} | |
} | |
/** | |
* @param {import("#client").Effect} effect | |
* @param {import("#client").Reaction} parent_effect | |
*/ | |
function push_effect(effect, parent_effect) { | |
var parent_last = parent_effect.last | |
if (parent_last === null) { | |
parent_effect.last = parent_effect.first = effect | |
} else { | |
parent_last.next = effect | |
effect.prev = parent_last | |
parent_effect.last = effect | |
} | |
} | |
/** | |
* @param {number} type | |
* @param {(() => void | (() => void))} fn | |
* @param {boolean} sync | |
* @returns {import('#client').Effect} | |
*/ | |
function create_effect(type, fn, sync) { | |
var is_root = (type & ROOT_EFFECT) !== 0 | |
/** @type {import('#client').Effect} */ | |
var effect = { | |
ctx: current_component_context, | |
deps: null, | |
dom: null, | |
f: type | DIRTY, | |
first: null, | |
fn, | |
last: null, | |
next: null, | |
parent: is_root ? null : current_effect, | |
prev: null, | |
teardown: null, | |
transitions: null, | |
} | |
if (current_reaction !== null && !is_root) { | |
push_effect(effect, current_reaction) | |
} | |
if (sync) { | |
var previously_flushing_effect = is_flushing_effect | |
try { | |
set_is_flushing_effect(true) | |
execute_effect(effect) | |
effect.f |= EFFECT_RAN | |
} finally { | |
set_is_flushing_effect(previously_flushing_effect) | |
} | |
} else { | |
schedule_effect(effect) | |
} | |
return effect | |
} | |
/** | |
* Internal representation of `$effect.active()` | |
* @returns {boolean} | |
*/ | |
function effect_active() { | |
return current_effect | |
? (current_effect.f & (BRANCH_EFFECT | ROOT_EFFECT)) === 0 | |
: false | |
} | |
/** | |
* Internal representation of `$effect.root(...)` | |
* @param {() => void | (() => void)} fn | |
* @returns {() => void} | |
*/ | |
function effect_root(fn) { | |
const effect = create_effect(ROOT_EFFECT, fn, true) | |
return () => { | |
destroy_effect(effect) | |
} | |
} | |
/** | |
* @param {() => void | (() => void)} fn | |
* @returns {import('#client').Effect} | |
*/ | |
function effect(fn) { | |
return create_effect(EFFECT, fn, false) | |
} | |
/** | |
* @param {() => void | (() => void)} fn | |
* @returns {import('#client').Effect} | |
*/ | |
function render_effect(fn) { | |
return create_effect(RENDER_EFFECT, fn, true) | |
} | |
/** @param {(() => void)} fn */ | |
function block(fn) { | |
return create_effect(RENDER_EFFECT | BLOCK_EFFECT, fn, true) | |
} | |
/** @param {(() => void)} fn */ | |
function branch(fn) { | |
return create_effect(RENDER_EFFECT | BRANCH_EFFECT, fn, true) | |
} | |
/** | |
* @param {import('#client').Effect} effect | |
* @returns {void} | |
*/ | |
function destroy_effect(effect) { | |
var dom = effect.dom | |
if (dom !== null) { | |
remove$1(dom) | |
} | |
destroy_effect_children(effect) | |
remove_reactions(effect, 0) | |
set_signal_status(effect, DESTROYED) | |
if (effect.transitions) { | |
for (const transition of effect.transitions) { | |
transition.stop() | |
} | |
} | |
effect.teardown?.call(null) | |
var parent = effect.parent | |
// If the parent doesn't have any children, then skip this work altogether | |
if ( | |
parent !== null && | |
(effect.f & BRANCH_EFFECT) !== 0 && | |
parent.first !== null | |
) { | |
var previous = effect.prev | |
var next = effect.next | |
if (previous !== null) { | |
if (next !== null) { | |
previous.next = next | |
next.prev = previous | |
} else { | |
previous.next = null | |
parent.last = previous | |
} | |
} else if (next !== null) { | |
next.prev = null | |
parent.first = next | |
} else { | |
parent.first = null | |
parent.last = null | |
} | |
} | |
// `first` and `child` are nulled out in destroy_effect_children | |
effect.next = | |
effect.prev = | |
effect.teardown = | |
effect.ctx = | |
effect.dom = | |
effect.deps = | |
effect.parent = | |
// @ts-expect-error | |
effect.fn = | |
null | |
} | |
/** | |
* When a block effect is removed, we don't immediately destroy it or yank it | |
* out of the DOM, because it might have transitions. Instead, we 'pause' it. | |
* It stays around (in memory, and in the DOM) until outro transitions have | |
* completed, and if the state change is reversed then we _resume_ it. | |
* A paused effect does not update, and the DOM subtree becomes inert. | |
* @param {import('#client').Effect} effect | |
* @param {() => void} [callback] | |
*/ | |
function pause_effect(effect, callback) { | |
/** @type {import('#client').TransitionManager[]} */ | |
var transitions = [] | |
pause_children(effect, transitions, true) | |
run_out_transitions(transitions, () => { | |
destroy_effect(effect) | |
if (callback) callback() | |
}) | |
} | |
/** | |
* @param {import('#client').TransitionManager[]} transitions | |
* @param {() => void} fn | |
*/ | |
function run_out_transitions(transitions, fn) { | |
var remaining = transitions.length | |
if (remaining > 0) { | |
var check = () => --remaining || fn() | |
for (var transition of transitions) { | |
transition.out(check) | |
} | |
} else { | |
fn() | |
} | |
} | |
/** | |
* @param {import('#client').Effect} effect | |
* @param {import('#client').TransitionManager[]} transitions | |
* @param {boolean} local | |
*/ | |
function pause_children(effect, transitions, local) { | |
if ((effect.f & INERT) !== 0) return | |
effect.f ^= INERT | |
if (effect.transitions !== null) { | |
for (const transition of effect.transitions) { | |
if (transition.is_global || local) { | |
transitions.push(transition) | |
} | |
} | |
} | |
var child = effect.first | |
while (child !== null) { | |
var sibling = child.next | |
var transparent = | |
(child.f & IS_ELSEIF) !== 0 || (child.f & BRANCH_EFFECT) !== 0 | |
// TODO we don't need to call pause_children recursively with a linked list in place | |
// it's slightly more involved though as we have to account for `transparent` changing | |
// through the tree. | |
pause_children(child, transitions, transparent ? local : false) | |
child = sibling | |
} | |
} | |
/** | |
* The opposite of `pause_effect`. We call this if (for example) | |
* `x` becomes falsy then truthy: `{#if x}...{/if}` | |
* @param {import('#client').Effect} effect | |
*/ | |
function resume_effect(effect) { | |
resume_children(effect, true) | |
} | |
/** | |
* @param {import('#client').Effect} effect | |
* @param {boolean} local | |
*/ | |
function resume_children(effect, local) { | |
if ((effect.f & INERT) === 0) return | |
effect.f ^= INERT | |
// If a dependency of this effect changed while it was paused, | |
// apply the change now | |
if (check_dirtiness(effect)) { | |
execute_effect(effect) | |
} | |
var child = effect.first | |
while (child !== null) { | |
var sibling = child.next | |
var transparent = | |
(child.f & IS_ELSEIF) !== 0 || (child.f & BRANCH_EFFECT) !== 0 | |
// TODO we don't need to call resume_children recursively with a linked list in place | |
// it's slightly more involved though as we have to account for `transparent` changing | |
// through the tree. | |
resume_children(child, transparent ? local : false) | |
child = sibling | |
} | |
if (effect.transitions !== null) { | |
for (const transition of effect.transitions) { | |
if (transition.is_global || local) { | |
transition.in() | |
} | |
} | |
} | |
} | |
let updating_derived = false | |
/** | |
* @template V | |
* @param {() => V} fn | |
* @returns {import('#client').Derived<V>} | |
*/ | |
/*#__NO_SIDE_EFFECTS__*/ | |
function derived(fn) { | |
let flags = DERIVED | DIRTY | |
if (current_effect === null) flags |= UNOWNED | |
/** @type {import('#client').Derived<V>} */ | |
const signal = { | |
deps: null, | |
deriveds: null, | |
equals: equals$1, | |
f: flags, | |
first: null, | |
fn, | |
last: null, | |
reactions: null, | |
v: /** @type {V} */ (null), | |
version: 0, | |
} | |
if (current_reaction !== null && (current_reaction.f & DERIVED) !== 0) { | |
var current_derived = /** @type {import('#client').Derived<V>} */ ( | |
current_reaction | |
) | |
if (current_derived.deriveds === null) { | |
current_derived.deriveds = [signal] | |
} else { | |
current_derived.deriveds.push(signal) | |
} | |
} | |
return signal | |
} | |
/** | |
* @param {import('./types.js').Derived} signal | |
* @returns {void} | |
*/ | |
function destroy_derived_children(signal) { | |
destroy_effect_children(signal) | |
var deriveds = signal.deriveds | |
if (deriveds !== null) { | |
signal.deriveds = null | |
for (var i = 0; i < deriveds.length; i += 1) { | |
destroy_derived(deriveds[i]) | |
} | |
} | |
} | |
/** | |
* @param {import('#client').Derived} derived | |
* @param {boolean} force_schedule | |
* @returns {void} | |
*/ | |
function update_derived(derived, force_schedule) { | |
var previous_updating_derived = updating_derived | |
updating_derived = true | |
destroy_derived_children(derived) | |
var value = execute_reaction_fn(derived) | |
updating_derived = previous_updating_derived | |
var status = | |
(current_skip_reaction || (derived.f & UNOWNED) !== 0) && | |
derived.deps !== null | |
? MAYBE_DIRTY | |
: CLEAN | |
set_signal_status(derived, status) | |
if (!derived.equals(value)) { | |
derived.v = value | |
mark_reactions(derived, DIRTY, force_schedule) | |
} | |
} | |
/** | |
* @param {import('#client').Derived} signal | |
* @returns {void} | |
*/ | |
function destroy_derived(signal) { | |
destroy_derived_children(signal) | |
remove_reactions(signal, 0) | |
set_signal_status(signal, DESTROYED) | |
// TODO we need to ensure we remove the derived from any parent derives | |
signal.first = | |
signal.last = | |
signal.deps = | |
signal.reactions = | |
// @ts-expect-error `signal.fn` cannot be `null` while the signal is alive | |
signal.fn = | |
null | |
} | |
/** | |
* @template T | |
* @param {T} value | |
* @param {boolean} [immutable] | |
* @param {Set<Function> | null} [owners] | |
* @returns {import('#client').ProxyStateObject<T> | T} | |
*/ | |
function proxy(value, immutable = true, owners) { | |
if (typeof value === 'object' && value != null && !is_frozen(value)) { | |
// If we have an existing proxy, return it... | |
if (STATE_SYMBOL in value) { | |
const metadata = /** @type {import('#client').ProxyMetadata<T>} */ ( | |
value[STATE_SYMBOL] | |
) | |
// ...unless the proxy belonged to a different object, because | |
// someone copied the state symbol using `Reflect.ownKeys(...)` | |
if (metadata.t === value || metadata.p === value) { | |
return metadata.p | |
} | |
} | |
const prototype = get_prototype_of(value) | |
if (prototype === object_prototype || prototype === array_prototype) { | |
const proxy = new Proxy(value, state_proxy_handler) | |
define_property(value, STATE_SYMBOL, { | |
value: /** @type {import('#client').ProxyMetadata} */ ({ | |
s: new Map(), | |
v: source(0), | |
a: is_array(value), | |
i: immutable, | |
p: proxy, | |
t: value, | |
}), | |
writable: true, | |
enumerable: false, | |
}) | |
return proxy | |
} | |
} | |
return value | |
} | |
/** | |
* @param {import('#client').Source<number>} signal | |
* @param {1 | -1} [d] | |
*/ | |
function update_version(signal, d = 1) { | |
set$2(signal, signal.v + d) | |
} | |
/** @type {ProxyHandler<import('#client').ProxyStateObject<any>>} */ | |
const state_proxy_handler = { | |
defineProperty(target, prop, descriptor) { | |
if (descriptor.value) { | |
/** @type {import('#client').ProxyMetadata} */ | |
const metadata = target[STATE_SYMBOL] | |
const s = metadata.s.get(prop) | |
if (s !== undefined) | |
set$2(s, proxy(descriptor.value, metadata.i, metadata.o)) | |
} | |
return Reflect.defineProperty(target, prop, descriptor) | |
}, | |
deleteProperty(target, prop) { | |
/** @type {import('#client').ProxyMetadata} */ | |
const metadata = target[STATE_SYMBOL] | |
const s = metadata.s.get(prop) | |
const is_array = metadata.a | |
const boolean = delete target[prop] | |
// If we have mutated an array directly, and the deletion | |
// was successful we will also need to update the length | |
// before updating the field or the version. This is to | |
// ensure any effects observing length can execute before | |
// effects that listen to the fields – otherwise they will | |
// operate an an index that no longer exists. | |
if (is_array && boolean) { | |
const ls = metadata.s.get('length') | |
const length = target.length - 1 | |
if (ls !== undefined && ls.v !== length) { | |
set$2(ls, length) | |
} | |
} | |
if (s !== undefined) set$2(s, UNINITIALIZED) | |
if (boolean) { | |
update_version(metadata.v) | |
} | |
return boolean | |
}, | |
get(target, prop, receiver) { | |
if (prop === STATE_SYMBOL) { | |
return Reflect.get(target, STATE_SYMBOL) | |
} | |
/** @type {import('#client').ProxyMetadata} */ | |
const metadata = target[STATE_SYMBOL] | |
let s = metadata.s.get(prop) | |
// if we're reading a property in a reactive context, create a source, | |
// but only if it's an own property and not a prototype property | |
if ( | |
s === undefined && | |
(effect_active() || updating_derived) && | |
(!(prop in target) || get_descriptor(target, prop)?.writable) | |
) { | |
s = (metadata.i ? source : mutable_source)( | |
proxy(target[prop], metadata.i, metadata.o), | |
) | |
metadata.s.set(prop, s) | |
} | |
if (s !== undefined) { | |
const value = get$2(s) | |
return value === UNINITIALIZED ? undefined : value | |
} | |
return Reflect.get(target, prop, receiver) | |
}, | |
getOwnPropertyDescriptor(target, prop) { | |
const descriptor = Reflect.getOwnPropertyDescriptor(target, prop) | |
if (descriptor && 'value' in descriptor) { | |
/** @type {import('#client').ProxyMetadata} */ | |
const metadata = target[STATE_SYMBOL] | |
const s = metadata.s.get(prop) | |
if (s) { | |
descriptor.value = get$2(s) | |
} | |
} | |
return descriptor | |
}, | |
has(target, prop) { | |
if (prop === STATE_SYMBOL) { | |
return true | |
} | |
/** @type {import('#client').ProxyMetadata} */ | |
const metadata = target[STATE_SYMBOL] | |
const has = Reflect.has(target, prop) | |
let s = metadata.s.get(prop) | |
if ( | |
s !== undefined || | |
(effect_active() && (!has || get_descriptor(target, prop)?.writable)) | |
) { | |
if (s === undefined) { | |
s = (metadata.i ? source : mutable_source)( | |
has ? proxy(target[prop], metadata.i, metadata.o) : UNINITIALIZED, | |
) | |
metadata.s.set(prop, s) | |
} | |
const value = get$2(s) | |
if (value === UNINITIALIZED) { | |
return false | |
} | |
} | |
return has | |
}, | |
set(target, prop, value, receiver) { | |
/** @type {import('#client').ProxyMetadata} */ | |
const metadata = target[STATE_SYMBOL] | |
let s = metadata.s.get(prop) | |
// If we haven't yet created a source for this property, we need to ensure | |
// we do so otherwise if we read it later, then the write won't be tracked and | |
// the heuristics of effects will be different vs if we had read the proxied | |
// object property before writing to that property. | |
if (s === undefined && effect_active()) { | |
// the read creates a signal | |
untrack(() => receiver[prop]) | |
s = metadata.s.get(prop) | |
} | |
if (s !== undefined) { | |
set$2(s, proxy(value, metadata.i, metadata.o)) | |
} | |
const is_array = metadata.a | |
const not_has = !(prop in target) | |
// variable.length = value -> clear all signals with index >= value | |
if (is_array && prop === 'length') { | |
for (let i = value; i < target.length; i += 1) { | |
const s = metadata.s.get(i + '') | |
if (s !== undefined) set$2(s, UNINITIALIZED) | |
} | |
} | |
// Set the new value before updating any signals so that any listeners get the new value | |
// @ts-ignore | |
target[prop] = value | |
if (not_has) { | |
// If we have mutated an array directly, we might need to | |
// signal that length has also changed. Do it before updating metadata | |
// to ensure that iterating over the array as a result of a metadata update | |
// will not cause the length to be out of sync. | |
if (is_array) { | |
const ls = metadata.s.get('length') | |
const length = target.length | |
if (ls !== undefined && ls.v !== length) { | |
set$2(ls, length) | |
} | |
} | |
update_version(metadata.v) | |
} | |
return true | |
}, | |
ownKeys(target) { | |
/** @type {import('#client').ProxyMetadata} */ | |
const metadata = target[STATE_SYMBOL] | |
get$2(metadata.v) | |
return Reflect.ownKeys(target) | |
}, | |
} | |
/** | |
* Synchronously run any queued tasks. | |
*/ | |
function flush_tasks() {} | |
const FLUSH_MICROTASK = 0 | |
const FLUSH_SYNC = 1 | |
// Used for controlling the flush of effects. | |
let current_scheduler_mode = FLUSH_MICROTASK | |
// Used for handling scheduling | |
let is_micro_task_queued = false | |
let is_flushing_effect = false | |
/** @param {boolean} value */ | |
function set_is_flushing_effect(value) { | |
is_flushing_effect = value | |
} | |
// Handle effect queues | |
/** @type {import('./types.js').Effect[]} */ | |
let current_queued_root_effects = [] | |
let flush_count = 0 | |
// Handle signal reactivity tree dependencies and reactions | |
/** @type {null | import('./types.js').Reaction} */ | |
let current_reaction = null | |
/** @type {null | import('./types.js').Effect} */ | |
let current_effect = null | |
/** @type {null | import('./types.js').Value[]} */ | |
let current_dependencies = null | |
let current_dependencies_index = 0 | |
/** | |
* Tracks writes that the effect it's executed in doesn't listen to yet, | |
* so that the dependency can be added to the effect later on if it then reads it | |
* @type {null | import('./types.js').Source[]} | |
*/ | |
let current_untracked_writes = null | |
/** @param {null | import('./types.js').Source[]} value */ | |
function set_current_untracked_writes(value) { | |
current_untracked_writes = value | |
} | |
/** If `true`, `get`ting the signal should not register it as a dependency */ | |
let current_untracking = false | |
// If we are working with a get() chain that has no active container, | |
// to prevent memory leaks, we skip adding the reaction. | |
let current_skip_reaction = false | |
// Handling runtime component context | |
/** @type {import('./types.js').ComponentContext | null} */ | |
let current_component_context = null | |
/** @returns {boolean} */ | |
function is_runes() { | |
return current_component_context !== null && current_component_context.r | |
} | |
/** | |
* Determines whether a derived or effect is dirty. | |
* If it is MAYBE_DIRTY, will set the status to CLEAN | |
* @param {import('./types.js').Reaction} reaction | |
* @returns {boolean} | |
*/ | |
function check_dirtiness(reaction) { | |
var flags = reaction.f | |
if ((flags & DIRTY) !== 0) { | |
return true | |
} | |
if ((flags & MAYBE_DIRTY) !== 0) { | |
var dependencies = reaction.deps | |
var is_unowned = (flags & UNOWNED) !== 0 | |
if (dependencies !== null) { | |
var length = dependencies.length | |
for (var i = 0; i < length; i++) { | |
var dependency = dependencies[i] | |
if ( | |
check_dirtiness(/** @type {import('#client').Derived} */ (dependency)) | |
) { | |
update_derived( | |
/** @type {import('#client').Derived} **/ (dependency), | |
true, | |
) | |
// `signal` might now be dirty, as a result of calling `update_derived` | |
if ((reaction.f & DIRTY) !== 0) { | |
return true | |
} | |
} | |
// If we're working with an unowned derived signal, then we need to check | |
// if our dependency write version is higher. If it is then we can assume | |
// that state has changed to a newer version and thus this unowned signal | |
// is also dirty. | |
var version = dependency.version | |
if ( | |
is_unowned && | |
version > /** @type {import('#client').Derived} */ (reaction).version | |
) { | |
/** @type {import('#client').Derived} */ ;(reaction).version = version | |
return true | |
} | |
} | |
} | |
// Unowned signals are always maybe dirty, as we instead check their dependency versions. | |
if (!is_unowned) { | |
set_signal_status(reaction, CLEAN) | |
} | |
} | |
return false | |
} | |
/** | |
* @template V | |
* @param {import('./types.js').Reaction} signal | |
* @returns {V} | |
*/ | |
function execute_reaction_fn(signal) { | |
const previous_dependencies = current_dependencies | |
const previous_dependencies_index = current_dependencies_index | |
const previous_untracked_writes = current_untracked_writes | |
const previous_reaction = current_reaction | |
const previous_skip_reaction = current_skip_reaction | |
const previous_untracking = current_untracking | |
current_dependencies = /** @type {null | import('./types.js').Value[]} */ ( | |
null | |
) | |
current_dependencies_index = 0 | |
current_untracked_writes = null | |
current_reaction = signal | |
current_skip_reaction = !is_flushing_effect && (signal.f & UNOWNED) !== 0 | |
current_untracking = false | |
try { | |
let res = signal.fn() | |
let dependencies = /** @type {import('./types.js').Value<unknown>[]} **/ ( | |
signal.deps | |
) | |
if (current_dependencies !== null) { | |
let i | |
if (dependencies !== null) { | |
const deps_length = dependencies.length | |
// Include any dependencies up until the current_dependencies_index. | |
const full_current_dependencies = | |
current_dependencies_index === 0 | |
? current_dependencies | |
: dependencies | |
.slice(0, current_dependencies_index) | |
.concat(current_dependencies) | |
const current_dep_length = full_current_dependencies.length | |
// If we have more than 16 elements in the array then use a Set for faster performance | |
// TODO: evaluate if we should always just use a Set or not here? | |
const full_current_dependencies_set = | |
current_dep_length > 16 && | |
deps_length - current_dependencies_index > 1 | |
? new Set(full_current_dependencies) | |
: null | |
for (i = current_dependencies_index; i < deps_length; i++) { | |
const dependency = dependencies[i] | |
if ( | |
full_current_dependencies_set !== null | |
? !full_current_dependencies_set.has(dependency) | |
: !full_current_dependencies.includes(dependency) | |
) { | |
remove_reaction(signal, dependency) | |
} | |
} | |
} | |
if (dependencies !== null && current_dependencies_index > 0) { | |
dependencies.length = | |
current_dependencies_index + current_dependencies.length | |
for (i = 0; i < current_dependencies.length; i++) { | |
dependencies[current_dependencies_index + i] = current_dependencies[i] | |
} | |
} else { | |
signal.deps = /** @type {import('./types.js').Value<V>[]} **/ ( | |
dependencies = current_dependencies | |
) | |
} | |
if (!current_skip_reaction) { | |
for (i = current_dependencies_index; i < dependencies.length; i++) { | |
const dependency = dependencies[i] | |
const reactions = dependency.reactions | |
if (reactions === null) { | |
dependency.reactions = [signal] | |
} else if (reactions[reactions.length - 1] !== signal) { | |
// TODO: should this be: | |
// | |
// } else if (!reactions.includes(signal)) { | |
// | |
reactions.push(signal) | |
} | |
} | |
} | |
} else if ( | |
dependencies !== null && | |
current_dependencies_index < dependencies.length | |
) { | |
remove_reactions(signal, current_dependencies_index) | |
dependencies.length = current_dependencies_index | |
} | |
return res | |
} finally { | |
current_dependencies = previous_dependencies | |
current_dependencies_index = previous_dependencies_index | |
current_untracked_writes = previous_untracked_writes | |
current_reaction = previous_reaction | |
current_skip_reaction = previous_skip_reaction | |
current_untracking = previous_untracking | |
} | |
} | |
/** | |
* @template V | |
* @param {import('./types.js').Reaction} signal | |
* @param {import('./types.js').Value<V>} dependency | |
* @returns {void} | |
*/ | |
function remove_reaction(signal, dependency) { | |
const reactions = dependency.reactions | |
let reactions_length = 0 | |
if (reactions !== null) { | |
reactions_length = reactions.length - 1 | |
const index = reactions.indexOf(signal) | |
if (index !== -1) { | |
if (reactions_length === 0) { | |
dependency.reactions = null | |
} else { | |
// Swap with last element and then remove. | |
reactions[index] = reactions[reactions_length] | |
reactions.pop() | |
} | |
} | |
} | |
if (reactions_length === 0 && (dependency.f & UNOWNED) !== 0) { | |
// If the signal is unowned then we need to make sure to change it to dirty. | |
set_signal_status(dependency, DIRTY) | |
remove_reactions( | |
/** @type {import('./types.js').Derived} **/ (dependency), | |
0, | |
) | |
} | |
} | |
/** | |
* @param {import('./types.js').Reaction} signal | |
* @param {number} start_index | |
* @returns {void} | |
*/ | |
function remove_reactions(signal, start_index) { | |
const dependencies = signal.deps | |
if (dependencies !== null) { | |
const active_dependencies = | |
start_index === 0 ? null : dependencies.slice(0, start_index) | |
let i | |
for (i = start_index; i < dependencies.length; i++) { | |
const dependency = dependencies[i] | |
// Avoid removing a reaction if we know that it is active (start_index will not be 0) | |
if ( | |
active_dependencies === null || | |
!active_dependencies.includes(dependency) | |
) { | |
remove_reaction(signal, dependency) | |
} | |
} | |
} | |
} | |
/** | |
* @param {import('./types.js').Reaction} signal | |
* @returns {void} | |
*/ | |
function destroy_effect_children(signal) { | |
let effect = signal.first | |
signal.first = null | |
signal.last = null | |
var sibling | |
while (effect !== null) { | |
sibling = effect.next | |
destroy_effect(effect) | |
effect = sibling | |
} | |
} | |
/** | |
* @param {import('./types.js').Effect} effect | |
* @returns {void} | |
*/ | |
function execute_effect(effect) { | |
var flags = effect.f | |
if ((flags & DESTROYED) !== 0) { | |
return | |
} | |
set_signal_status(effect, CLEAN) | |
var component_context = effect.ctx | |
var previous_effect = current_effect | |
var previous_component_context = current_component_context | |
current_effect = effect | |
current_component_context = component_context | |
try { | |
if ((flags & BLOCK_EFFECT) === 0) { | |
destroy_effect_children(effect) | |
} | |
effect.teardown?.call(null) | |
var teardown = execute_reaction_fn(effect) | |
effect.teardown = typeof teardown === 'function' ? teardown : null | |
} finally { | |
current_effect = previous_effect | |
current_component_context = previous_component_context | |
} | |
} | |
function infinite_loop_guard() { | |
if (flush_count > 1000) { | |
flush_count = 0 | |
throw new Error('ERR_SVELTE_TOO_MANY_UPDATES' + '') | |
} | |
flush_count++ | |
} | |
/** | |
* @param {Array<import('./types.js').Effect>} root_effects | |
* @returns {void} | |
*/ | |
function flush_queued_root_effects(root_effects) { | |
for (var i = 0; i < root_effects.length; i++) { | |
var signal = root_effects[i] | |
flush_nested_effects(signal, RENDER_EFFECT | EFFECT) | |
} | |
} | |
/** | |
* @param {Array<import('./types.js').Effect>} effects | |
* @returns {void} | |
*/ | |
function flush_queued_effects(effects) { | |
var length = effects.length | |
if (length === 0) return | |
infinite_loop_guard() | |
for (var i = 0; i < length; i++) { | |
var effect = effects[i] | |
if ((effect.f & (DESTROYED | INERT)) === 0 && check_dirtiness(effect)) { | |
execute_effect(effect) | |
} | |
} | |
} | |
function process_microtask() { | |
is_micro_task_queued = false | |
if (flush_count > 101) { | |
return | |
} | |
const previous_queued_root_effects = current_queued_root_effects | |
current_queued_root_effects = [] | |
flush_queued_root_effects(previous_queued_root_effects) | |
if (!is_micro_task_queued) { | |
flush_count = 0 | |
} | |
} | |
/** | |
* @param {import('./types.js').Effect} signal | |
* @returns {void} | |
*/ | |
function schedule_effect(signal) { | |
if (current_scheduler_mode === FLUSH_MICROTASK) { | |
if (!is_micro_task_queued) { | |
is_micro_task_queued = true | |
queueMicrotask(process_microtask) | |
} | |
} | |
var effect = signal | |
while (effect.parent !== null) { | |
effect = effect.parent | |
var flags = effect.f | |
if ((flags & BRANCH_EFFECT) !== 0) { | |
if ((flags & CLEAN) === 0) return | |
set_signal_status(effect, MAYBE_DIRTY) | |
} | |
} | |
current_queued_root_effects.push(effect) | |
} | |
/** | |
* | |
* This function both runs render effects and collects user effects in topological order | |
* from the starting effect passed in. Effects will be collected when they match the filtered | |
* bitwise flag passed in only. The collected effects array will be populated with all the user | |
* effects to be flushed. | |
* | |
* @param {import('./types.js').Effect} effect | |
* @param {number} filter_flags | |
* @param {boolean} shallow | |
* @param {import('./types.js').Effect[]} collected_effects | |
* @returns {void} | |
*/ | |
function process_effects(effect, filter_flags, shallow, collected_effects) { | |
var current_effect = effect.first | |
var effects = [] | |
main_loop: while (current_effect !== null) { | |
var flags = current_effect.f | |
// TODO: we probably don't need to check for destroyed as it shouldn't be encountered? | |
var is_active = (flags & (DESTROYED | INERT)) === 0 | |
var is_branch = flags & BRANCH_EFFECT | |
var is_clean = (flags & CLEAN) !== 0 | |
var child = current_effect.first | |
// Skip this branch if it's clean | |
if (is_active && (!is_branch || !is_clean)) { | |
if (is_branch) { | |
set_signal_status(current_effect, CLEAN) | |
} | |
if ((flags & RENDER_EFFECT) !== 0) { | |
if (is_branch) { | |
if (!shallow && child !== null) { | |
current_effect = child | |
continue | |
} | |
} else { | |
if (check_dirtiness(current_effect)) { | |
execute_effect(current_effect) | |
// Child might have been mutated since running the effect | |
child = current_effect.first | |
} | |
if (!shallow && child !== null) { | |
current_effect = child | |
continue | |
} | |
} | |
} else if ((flags & EFFECT) !== 0) { | |
if (is_branch || is_clean) { | |
if (!shallow && child !== null) { | |
current_effect = child | |
continue | |
} | |
} else { | |
effects.push(current_effect) | |
} | |
} | |
} | |
var sibling = current_effect.next | |
if (sibling === null) { | |
let parent = current_effect.parent | |
while (parent !== null) { | |
if (effect === parent) { | |
break main_loop | |
} | |
var parent_sibling = parent.next | |
if (parent_sibling !== null) { | |
current_effect = parent_sibling | |
continue main_loop | |
} | |
parent = parent.parent | |
} | |
} | |
current_effect = sibling | |
} | |
if (effects.length > 0) { | |
if ((filter_flags & EFFECT) !== 0) { | |
collected_effects.push(...effects) | |
} | |
if (!shallow) { | |
for (var i = 0; i < effects.length; i++) { | |
process_effects(effects[i], filter_flags, false, collected_effects) | |
} | |
} | |
} | |
} | |
/** | |
* | |
* This function recursively collects effects in topological order from the starting effect passed in. | |
* Effects will be collected when they match the filtered bitwise flag passed in only. The collected | |
* array will be populated with all the effects. | |
* | |
* @param {import('./types.js').Effect} effect | |
* @param {number} filter_flags | |
* @param {boolean} [shallow] | |
* @returns {void} | |
*/ | |
function flush_nested_effects(effect, filter_flags, shallow = false) { | |
/** @type {import('#client').Effect[]} */ | |
var collected_effects = [] | |
var previously_flushing_effect = is_flushing_effect | |
is_flushing_effect = true | |
try { | |
// When working with custom elements, the root effects might not have a root | |
if (effect.first === null && (effect.f & BRANCH_EFFECT) === 0) { | |
flush_queued_effects([effect]) | |
} else { | |
process_effects(effect, filter_flags, shallow, collected_effects) | |
flush_queued_effects(collected_effects) | |
} | |
} finally { | |
is_flushing_effect = previously_flushing_effect | |
} | |
} | |
/** | |
* Internal version of `flushSync` with the option to not flush previous effects. | |
* Returns the result of the passed function, if given. | |
* @param {() => any} [fn] | |
* @param {boolean} [flush_previous] | |
* @returns {any} | |
*/ | |
function flush_sync(fn, flush_previous = true) { | |
var previous_scheduler_mode = current_scheduler_mode | |
var previous_queued_root_effects = current_queued_root_effects | |
try { | |
infinite_loop_guard() | |
/** @type {import('./types.js').Effect[]} */ | |
const root_effects = [] | |
current_scheduler_mode = FLUSH_SYNC | |
current_queued_root_effects = root_effects | |
if (flush_previous) { | |
flush_queued_root_effects(previous_queued_root_effects) | |
} | |
var result = fn?.() | |
if (current_queued_root_effects.length > 0 || root_effects.length > 0) { | |
flush_sync() | |
} | |
flush_tasks() | |
flush_count = 0 | |
return result | |
} finally { | |
current_scheduler_mode = previous_scheduler_mode | |
current_queued_root_effects = previous_queued_root_effects | |
} | |
} | |
/** | |
* @template V | |
* @param {import('./types.js').Value<V>} signal | |
* @returns {V} | |
*/ | |
function get$2(signal) { | |
const flags = signal.f | |
if ((flags & DESTROYED) !== 0) { | |
return signal.v | |
} | |
// Register the dependency on the current reaction signal. | |
if ( | |
current_reaction !== null && | |
(current_reaction.f & (BRANCH_EFFECT | ROOT_EFFECT)) === 0 && | |
!current_untracking | |
) { | |
const unowned = (current_reaction.f & UNOWNED) !== 0 | |
const dependencies = current_reaction.deps | |
if ( | |
current_dependencies === null && | |
dependencies !== null && | |
dependencies[current_dependencies_index] === signal && | |
!(unowned && current_effect !== null) | |
) { | |
current_dependencies_index++ | |
} else if ( | |
dependencies === null || | |
current_dependencies_index === 0 || | |
dependencies[current_dependencies_index - 1] !== signal | |
) { | |
if (current_dependencies === null) { | |
current_dependencies = [signal] | |
} else { | |
current_dependencies.push(signal) | |
} | |
} | |
if ( | |
current_untracked_writes !== null && | |
current_effect !== null && | |
(current_effect.f & CLEAN) !== 0 && | |
(current_effect.f & BRANCH_EFFECT) === 0 && | |
current_untracked_writes.includes(signal) | |
) { | |
set_signal_status(current_effect, DIRTY) | |
schedule_effect(current_effect) | |
} | |
} | |
if ( | |
(flags & DERIVED) !== 0 && | |
check_dirtiness(/** @type {import('#client').Derived} */ (signal)) | |
) { | |
{ | |
update_derived( | |
/** @type {import('./types.js').Derived} **/ (signal), | |
false, | |
) | |
} | |
} | |
return signal.v | |
} | |
/** | |
* @param {import('#client').Value} signal | |
* @param {number} to_status | |
* @param {boolean} force_schedule | |
* @returns {void} | |
*/ | |
function mark_reactions(signal, to_status, force_schedule) { | |
var reactions = signal.reactions | |
if (reactions === null) return | |
var runes = is_runes() | |
var length = reactions.length | |
for (var i = 0; i < length; i++) { | |
var reaction = reactions[i] | |
// We skip any effects that are already dirty (but not unowned). Additionally, we also | |
// skip if the reaction is the same as the current effect (except if we're not in runes or we | |
// are in force schedule mode). | |
if ((!force_schedule || !runes) && reaction === current_effect) { | |
continue | |
} | |
var flags = reaction.f | |
set_signal_status(reaction, to_status) | |
// If the signal is not clean, then skip over it – with the exception of unowned signals that | |
// are already maybe dirty. Unowned signals might be dirty because they are not captured as part of an | |
// effect. | |
var maybe_dirty = (flags & MAYBE_DIRTY) !== 0 | |
var unowned = (flags & UNOWNED) !== 0 | |
if ((flags & CLEAN) !== 0 || (maybe_dirty && unowned)) { | |
if ((reaction.f & DERIVED) !== 0) { | |
mark_reactions( | |
/** @type {import('#client').Derived} */ (reaction), | |
MAYBE_DIRTY, | |
force_schedule, | |
) | |
} else { | |
schedule_effect(/** @type {import('#client').Effect} */ (reaction)) | |
} | |
} | |
} | |
} | |
/** | |
* Use `untrack` to prevent something from being treated as an `$effect`/`$derived` dependency. | |
* | |
* https://svelte-5-preview.vercel.app/docs/functions#untrack | |
* @template T | |
* @param {() => T} fn | |
* @returns {T} | |
*/ | |
function untrack(fn) { | |
const previous_untracking = current_untracking | |
try { | |
current_untracking = true | |
return fn() | |
} finally { | |
current_untracking = previous_untracking | |
} | |
} | |
const STATUS_MASK = ~(DIRTY | MAYBE_DIRTY | CLEAN) | |
/** | |
* @param {import('./types.js').Signal} signal | |
* @param {number} status | |
* @returns {void} | |
*/ | |
function set_signal_status(signal, status) { | |
signal.f = (signal.f & STATUS_MASK) | status | |
} | |
/** | |
* @template V | |
* @param {V | import('./types.js').Value<V>} val | |
* @returns {val is import('./types.js').Value<V>} | |
*/ | |
function is_signal(val) { | |
return ( | |
typeof val === 'object' && | |
val !== null && | |
typeof (/** @type {import('./types.js').Value<V>} */ (val).f) === 'number' | |
) | |
} | |
/** | |
* @param {Record<string, unknown>} props | |
* @param {any} runes | |
* @param {Function} [fn] | |
* @returns {void} | |
*/ | |
function push(props, runes = false, fn) { | |
current_component_context = { | |
// exports (and props, if `accessors: true`) | |
x: null, | |
// context | |
c: null, | |
// effects | |
e: null, | |
// mounted | |
m: false, | |
// parent | |
p: current_component_context, | |
// signals | |
d: null, | |
// props | |
s: props, | |
// runes | |
r: runes, | |
// legacy $: | |
l1: [], | |
l2: source(false), | |
// update_callbacks | |
u: null, | |
} | |
} | |
/** | |
* @template {Record<string, any>} T | |
* @param {T} [component] | |
* @returns {T} | |
*/ | |
function pop(component) { | |
const context_stack_item = current_component_context | |
if (context_stack_item !== null) { | |
if (component !== undefined) { | |
context_stack_item.x = component | |
} | |
const effects = context_stack_item.e | |
if (effects !== null) { | |
context_stack_item.e = null | |
for (let i = 0; i < effects.length; i++) { | |
effect(effects[i]) | |
} | |
} | |
current_component_context = context_stack_item.p | |
context_stack_item.m = true | |
} | |
// Micro-optimization: Don't set .a above to the empty object | |
// so it can be garbage-collected when the return here is unused | |
return component || /** @type {T} */ ({}) | |
} | |
/** | |
* Possibly traverse an object and read all its properties so that they're all reactive in case this is `$state`. | |
* Does only check first level of an object for performance reasons (heuristic should be good for 99% of all cases). | |
* @param {any} value | |
* @returns {void} | |
*/ | |
function deep_read_state(value) { | |
if (typeof value !== 'object' || !value || value instanceof EventTarget) { | |
return | |
} | |
if (STATE_SYMBOL in value) { | |
deep_read(value) | |
} else if (!Array.isArray(value)) { | |
for (let key in value) { | |
const prop = value[key] | |
if (typeof prop === 'object' && prop && STATE_SYMBOL in prop) { | |
deep_read(prop) | |
} | |
} | |
} | |
} | |
/** | |
* Deeply traverse an object and read all its properties | |
* so that they're all reactive in case this is `$state` | |
* @param {any} value | |
* @param {Set<any>} visited | |
* @returns {void} | |
*/ | |
function deep_read(value, visited = new Set()) { | |
if ( | |
typeof value === 'object' && | |
value !== null && | |
// We don't want to traverse DOM elements | |
!(value instanceof EventTarget) && | |
!visited.has(value) | |
) { | |
visited.add(value) | |
for (let key in value) { | |
try { | |
deep_read(value[key], visited) | |
} catch (e) { | |
// continue | |
} | |
} | |
const proto = Object.getPrototypeOf(value) | |
if ( | |
proto !== Object.prototype && | |
proto !== Array.prototype && | |
proto !== Map.prototype && | |
proto !== Set.prototype && | |
proto !== Date.prototype | |
) { | |
const descriptors = get_descriptors(proto) | |
for (let key in descriptors) { | |
const get = descriptors[key].get | |
if (get) { | |
try { | |
get.call(value) | |
} catch (e) { | |
// continue | |
} | |
} | |
} | |
} | |
} | |
} | |
/** | |
* @template V | |
* @param {V | import('#client').Value<V>} value | |
* @returns {V} | |
*/ | |
function unwrap(value) { | |
if (is_signal(value)) { | |
// @ts-ignore | |
return get$2(value) | |
} | |
// @ts-ignore | |
return value | |
} | |
/** | |
* Use this variable to guard everything related to hydration code so it can be treeshaken out | |
* if the user doesn't use the `hydrate` method and these code paths are therefore not needed. | |
*/ | |
let hydrating = false | |
/** @param {boolean} value */ | |
function set_hydrating(value) { | |
hydrating = value | |
} | |
/** | |
* Array of nodes to traverse for hydration. This will be null if we're not hydrating, but for | |
* the sake of simplicity we're not going to use `null` checks everywhere and instead rely on | |
* the `hydrating` flag to tell whether or not we're in hydration mode at which point this is set. | |
* @type {import('#client').TemplateNode[]} | |
*/ | |
let hydrate_nodes = /** @type {any} */ (null) | |
/** @param {import('#client').TemplateNode[]} nodes */ | |
function set_hydrate_nodes(nodes) { | |
hydrate_nodes = nodes | |
} | |
/** | |
* This function is only called when `hydrating` is true. If passed a `<!--[-->` opening | |
* hydration marker, it finds the corresponding closing marker and sets `hydrate_nodes` | |
* to everything between the markers, before returning the closing marker. | |
* @param {Node} node | |
* @returns {Node} | |
*/ | |
function hydrate_anchor(node) { | |
if (node.nodeType !== 8) { | |
return node | |
} | |
var current = /** @type {Node | null} */ (node) | |
// TODO this could have false positives, if a user comment consisted of `[`. need to tighten that up | |
if (/** @type {Comment} */ (current)?.data !== HYDRATION_START) { | |
return node | |
} | |
/** @type {Node[]} */ | |
var nodes = [] | |
var depth = 0 | |
while ((current = /** @type {Node} */ (current).nextSibling) !== null) { | |
if (current.nodeType === 8) { | |
var data = /** @type {Comment} */ (current).data | |
if (data === HYDRATION_START) { | |
depth += 1 | |
} else if (data[0] === HYDRATION_END) { | |
if (depth === 0) { | |
hydrate_nodes = /** @type {import('#client').TemplateNode[]} */ ( | |
nodes | |
) | |
return current | |
} | |
depth -= 1 | |
} | |
} | |
nodes.push(current) | |
} | |
throw new Error('Expected a closing hydration marker') | |
} | |
/** | |
* @param {Comment} anchor | |
* @param {() => boolean} get_condition | |
* @param {(anchor: Node) => import('#client').Dom} consequent_fn | |
* @param {null | ((anchor: Node) => import('#client').Dom)} [alternate_fn] | |
* @param {boolean} [elseif] True if this is an `{:else if ...}` block rather than an `{#if ...}`, as that affects which transitions are considered 'local' | |
* @returns {void} | |
*/ | |
function if_block( | |
anchor, | |
get_condition, | |
consequent_fn, | |
alternate_fn = null, | |
elseif = false, | |
) { | |
/** @type {import('#client').Effect | null} */ | |
let consequent_effect = null | |
/** @type {import('#client').Effect | null} */ | |
let alternate_effect = null | |
/** @type {boolean | null} */ | |
let condition = null | |
const effect = block(() => { | |
if (condition === (condition = !!get_condition())) return | |
/** Whether or not there was a hydration mismatch. Needs to be a `let` or else it isn't treeshaken out */ | |
let mismatch = false | |
if (hydrating) { | |
const is_else = anchor.data === HYDRATION_END_ELSE | |
if (condition === is_else) { | |
// Hydration mismatch: remove everything inside the anchor and start fresh. | |
// This could happen with `{#if browser}...{/if}`, for example | |
remove$1(hydrate_nodes) | |
set_hydrating(false) | |
mismatch = true | |
} | |
} | |
if (condition) { | |
if (consequent_effect) { | |
resume_effect(consequent_effect) | |
} else { | |
consequent_effect = branch(() => consequent_fn(anchor)) | |
} | |
if (alternate_effect) { | |
pause_effect(alternate_effect, () => { | |
alternate_effect = null | |
}) | |
} | |
} else { | |
if (alternate_effect) { | |
resume_effect(alternate_effect) | |
} else if (alternate_fn) { | |
alternate_effect = branch(() => alternate_fn(anchor)) | |
} | |
if (consequent_effect) { | |
pause_effect(consequent_effect, () => { | |
consequent_effect = null | |
}) | |
} | |
} | |
if (mismatch) { | |
// continue in hydration mode | |
set_hydrating(true) | |
} | |
}) | |
if (elseif) { | |
effect.f |= IS_ELSEIF | |
} | |
} | |
// We cache the Node and Element prototype methods, so that we can avoid doing | |
// expensive prototype chain lookups. | |
/** @type {Node} */ | |
var node_prototype | |
/** @type {Element} */ | |
var element_prototype | |
/** @type {Text} */ | |
var text_prototype | |
/** @type {typeof Node.prototype.cloneNode} */ | |
var clone_node_method | |
/** @type {(this: Node) => ChildNode | null} */ | |
var first_child_get | |
/** @type {(this: Node) => ChildNode | null} */ | |
var next_sibling_get | |
/** @type {(this: Node, text: string ) => void} */ | |
var text_content_set | |
/** | |
* Initialize these lazily to avoid issues when using the runtime in a server context | |
* where these globals are not available while avoiding a separate server entry point | |
*/ | |
function init_operations() { | |
if (node_prototype !== undefined) { | |
return | |
} | |
node_prototype = Node.prototype | |
element_prototype = Element.prototype | |
text_prototype = Text.prototype | |
node_prototype.appendChild | |
clone_node_method = node_prototype.cloneNode | |
// the following assignments improve perf of lookups on DOM nodes | |
// @ts-expect-error | |
element_prototype.__click = undefined | |
// @ts-expect-error | |
text_prototype.__nodeValue = ' ' | |
// @ts-expect-error | |
element_prototype.__className = '' | |
// @ts-expect-error | |
element_prototype.__attributes = null | |
first_child_get = /** @type {(this: Node) => ChildNode | null} */ ( | |
// @ts-ignore | |
get_descriptor(node_prototype, 'firstChild').get | |
) | |
next_sibling_get = /** @type {(this: Node) => ChildNode | null} */ ( | |
// @ts-ignore | |
get_descriptor(node_prototype, 'nextSibling').get | |
) | |
text_content_set = /** @type {(this: Node, text: string ) => void} */ ( | |
// @ts-ignore | |
get_descriptor(node_prototype, 'textContent').set | |
) | |
/** @type {(this: Element, class_name: string) => void} */ ;( | |
// @ts-ignore | |
get_descriptor(element_prototype, 'className').set | |
) | |
} | |
/** | |
* @template {Node} N | |
* @param {N} node | |
* @param {boolean} deep | |
* @returns {N} | |
*/ | |
/*#__NO_SIDE_EFFECTS__*/ | |
function clone_node(node, deep) { | |
return /** @type {N} */ (clone_node_method.call(node, deep)) | |
} | |
/** @returns {Text} */ | |
function empty$1() { | |
return document.createTextNode('') | |
} | |
/** | |
* @template {Node} N | |
* @param {N} node | |
* @returns {Node | null} | |
*/ | |
/*#__NO_SIDE_EFFECTS__*/ | |
function child(node) { | |
const child = first_child_get.call(node) | |
if (!hydrating) return child | |
// Child can be null if we have an element with a single child, like `<p>{text}</p>`, where `text` is empty | |
if (child === null) { | |
return node.appendChild(empty$1()) | |
} | |
return hydrate_anchor(child) | |
} | |
/** | |
* @param {DocumentFragment | import('#client').TemplateNode[]} fragment | |
* @param {boolean} is_text | |
* @returns {Node | null} | |
*/ | |
/*#__NO_SIDE_EFFECTS__*/ | |
function first_child(fragment, is_text) { | |
if (!hydrating) { | |
// when not hydrating, `fragment` is a `DocumentFragment` (the result of calling `open_frag`) | |
return first_child_get.call(/** @type {DocumentFragment} */ (fragment)) | |
} | |
// when we _are_ hydrating, `fragment` is an array of nodes | |
const first_node = /** @type {import('#client').TemplateNode[]} */ ( | |
fragment | |
)[0] | |
// if an {expression} is empty during SSR, there might be no | |
// text node to hydrate — we must therefore create one | |
if (is_text && first_node?.nodeType !== 3) { | |
const text = empty$1() | |
hydrate_nodes.unshift(text) | |
first_node?.before(text) | |
return text | |
} | |
return hydrate_anchor(first_node) | |
} | |
/** | |
* @template {Node} N | |
* @param {N} node | |
* @param {boolean} is_text | |
* @returns {Node | null} | |
*/ | |
/*#__NO_SIDE_EFFECTS__*/ | |
function sibling(node, is_text = false) { | |
const next_sibling = next_sibling_get.call(node) | |
if (!hydrating) { | |
return next_sibling | |
} | |
// if a sibling {expression} is empty during SSR, there might be no | |
// text node to hydrate — we must therefore create one | |
if (is_text && next_sibling?.nodeType !== 3) { | |
const text = empty$1() | |
if (next_sibling) { | |
const index = hydrate_nodes.indexOf( | |
/** @type {Text | Comment | Element} */ (next_sibling), | |
) | |
hydrate_nodes.splice(index, 0, text) | |
next_sibling.before(text) | |
} else { | |
hydrate_nodes.push(text) | |
} | |
return text | |
} | |
return hydrate_anchor(/** @type {Node} */ (next_sibling)) | |
} | |
/** | |
* @template {Node} N | |
* @param {N} node | |
* @returns {void} | |
*/ | |
function clear_text_content(node) { | |
text_content_set.call(node, '') | |
} | |
/** | |
* The row of a keyed each block that is currently updating. We track this | |
* so that `animate:` directives have something to attach themselves to | |
* @type {import('#client').EachItem | null} | |
*/ | |
let current_each_item = null | |
/** | |
* Pause multiple effects simultaneously, and coordinate their | |
* subsequent destruction. Used in each blocks | |
* @param {import('#client').Effect[]} effects | |
* @param {null | Node} controlled_anchor | |
* @param {() => void} [callback] | |
*/ | |
function pause_effects(effects, controlled_anchor, callback) { | |
/** @type {import('#client').TransitionManager[]} */ | |
var transitions = [] | |
var length = effects.length | |
for (var i = 0; i < length; i++) { | |
pause_children(effects[i], transitions, true) | |
} | |
// If we have a controlled anchor, it means that the each block is inside a single | |
// DOM element, so we can apply a fast-path for clearing the contents of the element. | |
if ( | |
effects.length > 0 && | |
transitions.length === 0 && | |
controlled_anchor !== null | |
) { | |
var parent_node = /** @type {Element} */ (controlled_anchor.parentNode) | |
parent_node.textContent = '' | |
parent_node.append(controlled_anchor) | |
} | |
run_out_transitions(transitions, () => { | |
for (var i = 0; i < length; i++) { | |
destroy_effect(effects[i]) | |
} | |
if (callback !== undefined) callback() | |
}) | |
} | |
/** | |
* @template V | |
* @param {Element | Comment} anchor The next sibling node, or the parent node if this is a 'controlled' block | |
* @param {number} flags | |
* @param {() => V[]} get_collection | |
* @param {null | ((item: V) => string)} get_key | |
* @param {(anchor: Node, item: import('#client').MaybeSource<V>, index: import('#client').MaybeSource<number>) => void} render_fn | |
* @param {null | ((anchor: Node) => void)} fallback_fn | |
* @param {typeof reconcile_indexed_array | reconcile_tracked_array} reconcile_fn | |
* @returns {void} | |
*/ | |
function each( | |
anchor, | |
flags, | |
get_collection, | |
get_key, | |
render_fn, | |
fallback_fn, | |
reconcile_fn, | |
) { | |
/** @type {import('#client').EachState} */ | |
var state = { flags, items: [] } | |
var is_controlled = (flags & EACH_IS_CONTROLLED) !== 0 | |
if (is_controlled) { | |
var parent_node = /** @type {Element} */ (anchor) | |
anchor = hydrating | |
? /** @type {Comment | Text} */ ( | |
hydrate_anchor(/** @type {Comment | Text} */ (parent_node.firstChild)) | |
) | |
: parent_node.appendChild(empty$1()) | |
} | |
/** @type {import('#client').Effect | null} */ | |
var fallback = null | |
block(() => { | |
var collection = get_collection() | |
var array = is_array(collection) | |
? collection | |
: collection == null | |
? [] | |
: Array.from(collection) | |
var keys = get_key === null ? array : array.map(get_key) | |
var length = array.length | |
// If we are working with an array that isn't proxied or frozen, then remove strict equality and ensure the items | |
// are treated as reactive, so they get wrapped in a signal. | |
var flags = state.flags | |
if ( | |
(flags & EACH_IS_STRICT_EQUALS) !== 0 && | |
!is_frozen(array) && | |
!(STATE_SYMBOL in array) | |
) { | |
flags ^= EACH_IS_STRICT_EQUALS | |
// Additionally if we're in an keyed each block, we'll need ensure the items are all wrapped in signals. | |
if ((flags & EACH_KEYED) !== 0 && (flags & EACH_ITEM_REACTIVE) === 0) { | |
flags ^= EACH_ITEM_REACTIVE | |
} | |
} | |
/** `true` if there was a hydration mismatch. Needs to be a `let` or else it isn't treeshaken out */ | |
let mismatch = false | |
if (hydrating) { | |
var is_else = /** @type {Comment} */ (anchor).data === HYDRATION_END_ELSE | |
if (is_else !== (length === 0)) { | |
// hydration mismatch — remove the server-rendered DOM and start over | |
remove$1(hydrate_nodes) | |
set_hydrating(false) | |
mismatch = true | |
} | |
} | |
// this is separate to the previous block because `hydrating` might change | |
if (hydrating) { | |
var b_items = [] | |
/** @type {Node} */ | |
var child_anchor = hydrate_nodes[0] | |
for (var i = 0; i < length; i++) { | |
if ( | |
child_anchor.nodeType !== 8 || | |
/** @type {Comment} */ (child_anchor).data !== HYDRATION_START | |
) { | |
// If `nodes` is null, then that means that the server rendered fewer items than what | |
// expected, so break out and continue appending non-hydrated items | |
mismatch = true | |
set_hydrating(false) | |
break | |
} | |
child_anchor = hydrate_anchor(child_anchor) | |
b_items[i] = create_item( | |
child_anchor, | |
array[i], | |
keys?.[i], | |
i, | |
render_fn, | |
flags, | |
) | |
child_anchor = /** @type {Comment} */ (child_anchor.nextSibling) | |
} | |
// remove excess nodes | |
if (length > 0) { | |
while (child_anchor !== anchor) { | |
var next = /** @type {import('#client').TemplateNode} */ ( | |
child_anchor.nextSibling | |
) | |
/** @type {import('#client').TemplateNode} */ ;(child_anchor).remove() | |
child_anchor = next | |
} | |
} | |
state.items = b_items | |
} | |
if (!hydrating) { | |
reconcile_fn(array, state, anchor, render_fn, flags, keys) | |
} | |
if (fallback_fn !== null) { | |
if (length === 0) { | |
if (fallback) { | |
resume_effect(fallback) | |
} else { | |
fallback = branch(() => fallback_fn(anchor)) | |
} | |
} else if (fallback !== null) { | |
pause_effect(fallback, () => { | |
fallback = null | |
}) | |
} | |
} | |
if (mismatch) { | |
// continue in hydration mode | |
set_hydrating(true) | |
} | |
}) | |
} | |
/** | |
* @template V | |
* @param {Element | Comment} anchor | |
* @param {number} flags | |
* @param {() => V[]} get_collection | |
* @param {(anchor: Node, item: import('#client').MaybeSource<V>, index: import('#client').MaybeSource<number>) => void} render_fn | |
* @param {null | ((anchor: Node) => void)} [fallback_fn] | |
* @returns {void} | |
*/ | |
function each_indexed( | |
anchor, | |
flags, | |
get_collection, | |
render_fn, | |
fallback_fn = null, | |
) { | |
each( | |
anchor, | |
flags, | |
get_collection, | |
null, | |
render_fn, | |
fallback_fn, | |
reconcile_indexed_array, | |
) | |
} | |
/** | |
* @template V | |
* @param {Array<V>} array | |
* @param {import('#client').EachState} state | |
* @param {Element | Comment | Text} anchor | |
* @param {(anchor: Node, item: import('#client').MaybeSource<V>, index: number | import('#client').Source<number>) => void} render_fn | |
* @param {number} flags | |
* @returns {void} | |
*/ | |
function reconcile_indexed_array(array, state, anchor, render_fn, flags) { | |
var a_items = state.items | |
var a = a_items.length | |
var b = array.length | |
var min = Math.min(a, b) | |
/** @type {typeof a_items} */ | |
var b_items = Array(b) | |
var item | |
var value | |
// update items | |
for (var i = 0; i < min; i += 1) { | |
value = array[i] | |
item = a_items[i] | |
b_items[i] = item | |
update_item(item, value, i, flags) | |
resume_effect(item.e) | |
} | |
if (b > a) { | |
// add items | |
for (; i < b; i += 1) { | |
value = array[i] | |
item = create_item(anchor, value, null, i, render_fn, flags) | |
b_items[i] = item | |
} | |
state.items = b_items | |
} else if (a > b) { | |
// remove items | |
var effects = [] | |
for (i = b; i < a; i += 1) { | |
effects.push(a_items[i].e) | |
} | |
var controlled_anchor = | |
(flags & EACH_IS_CONTROLLED) !== 0 && b === 0 ? anchor : null | |
pause_effects(effects, controlled_anchor, () => { | |
state.items.length = b | |
}) | |
} | |
} | |
/** | |
* @param {import('#client').EachItem} item | |
* @param {any} value | |
* @param {number} index | |
* @param {number} type | |
* @returns {void} | |
*/ | |
function update_item(item, value, index, type) { | |
if ((type & EACH_ITEM_REACTIVE) !== 0) { | |
set$2(item.v, value) | |
} | |
if ((type & EACH_INDEX_REACTIVE) !== 0) { | |
set$2(/** @type {import('#client').Value<number>} */ (item.i), index) | |
} else { | |
item.i = index | |
} | |
} | |
/** | |
* @template V | |
* @param {Node} anchor | |
* @param {V} value | |
* @param {unknown} key | |
* @param {number} index | |
* @param {(anchor: Node, item: V | import('#client').Source<V>, index: number | import('#client').Value<number>) => void} render_fn | |
* @param {number} flags | |
* @returns {import('#client').EachItem} | |
*/ | |
function create_item(anchor, value, key, index, render_fn, flags) { | |
var previous_each_item = current_each_item | |
try { | |
var reactive = (flags & EACH_ITEM_REACTIVE) !== 0 | |
var mutable = (flags & EACH_IS_STRICT_EQUALS) === 0 | |
var v = reactive ? (mutable ? mutable_source(value) : source(value)) : value | |
var i = (flags & EACH_INDEX_REACTIVE) === 0 ? index : source(index) | |
/** @type {import('#client').EachItem} */ | |
var item = { | |
i, | |
v, | |
k: key, | |
a: null, | |
// @ts-expect-error | |
e: null, | |
} | |
current_each_item = item | |
item.e = branch(() => render_fn(anchor, v, i)) | |
return item | |
} finally { | |
current_each_item = previous_each_item | |
} | |
} | |
/** | |
* @param {Node} handler_element | |
* @param {Event} event | |
* @returns {void} | |
*/ | |
function handle_event_propagation(handler_element, event) { | |
var owner_document = handler_element.ownerDocument | |
var event_name = event.type | |
var path = event.composedPath?.() || [] | |
var current_target = /** @type {null | Element} */ (path[0] || event.target) | |
if (event.target !== current_target) { | |
define_property(event, 'target', { | |
configurable: true, | |
value: current_target, | |
}) | |
} | |
// composedPath contains list of nodes the event has propagated through. | |
// We check __root to skip all nodes below it in case this is a | |
// parent of the __root node, which indicates that there's nested | |
// mounted apps. In this case we don't want to trigger events multiple times. | |
var path_idx = 0 | |
// @ts-expect-error is added below | |
var handled_at = event.__root | |
if (handled_at) { | |
var at_idx = path.indexOf(handled_at) | |
if ( | |
at_idx !== -1 && | |
(handler_element === document || | |
handler_element === /** @type {any} */ (window)) | |
) { | |
// This is the fallback document listener or a window listener, but the event was already handled | |
// -> ignore, but set handle_at to document/window so that we're resetting the event | |
// chain in case someone manually dispatches the same event object again. | |
// @ts-expect-error | |
event.__root = handler_element | |
return | |
} | |
// We're deliberately not skipping if the index is higher, because | |
// someone could create an event programmatically and emit it multiple times, | |
// in which case we want to handle the whole propagation chain properly each time. | |
// (this will only be a false negative if the event is dispatched multiple times and | |
// the fallback document listener isn't reached in between, but that's super rare) | |
var handler_idx = path.indexOf(handler_element) | |
if (handler_idx === -1) { | |
// handle_idx can theoretically be -1 (happened in some JSDOM testing scenarios with an event listener on the window object) | |
// so guard against that, too, and assume that everything was handled at this point. | |
return | |
} | |
if (at_idx <= handler_idx) { | |
// +1 because at_idx is the element which was already handled, and there can only be one delegated event per element. | |
// Avoids on:click and onclick on the same event resulting in onclick being fired twice. | |
path_idx = at_idx + 1 | |
} | |
} | |
current_target = /** @type {Element} */ (path[path_idx] || event.target) | |
// Proxy currentTarget to correct target | |
define_property(event, 'currentTarget', { | |
configurable: true, | |
get() { | |
return current_target || owner_document | |
}, | |
}) | |
while (current_target !== null) { | |
/** @type {null | Element} */ | |
var parent_element = | |
current_target.parentNode || | |
/** @type {any} */ (current_target).host || | |
null | |
var internal_prop_name = '__' + event_name | |
// @ts-ignore | |
var delegated = current_target[internal_prop_name] | |
if ( | |
delegated !== undefined && | |
!(/** @type {any} */ (current_target).disabled) | |
) { | |
if (is_array(delegated)) { | |
var [fn, ...data] = delegated | |
fn.apply(current_target, [event, ...data]) | |
} else { | |
delegated.call(current_target, event) | |
} | |
} | |
if ( | |
event.cancelBubble || | |
parent_element === handler_element || | |
current_target === handler_element | |
) { | |
break | |
} | |
current_target = parent_element | |
} | |
// @ts-expect-error is used above | |
event.__root = handler_element | |
// @ts-expect-error is used above | |
current_target = handler_element | |
} | |
/** @type {Set<string>} */ | |
const all_registered_events = new Set() | |
/** @type {Set<(events: Array<string>) => void>} */ | |
const root_event_handles = new Set() | |
/** | |
* @param {Element} dom | |
* @param {string} value | |
* @returns {void} | |
*/ | |
function set_text(dom, value) { | |
// @ts-expect-error need to add __value to patched prototype | |
const prev_node_value = dom.__nodeValue | |
const next_node_value = stringify(value) | |
if (hydrating && dom.nodeValue === next_node_value) { | |
// In case of hydration don't reset the nodeValue as it's already correct. | |
// @ts-expect-error need to add __nodeValue to patched prototype | |
dom.__nodeValue = next_node_value | |
} else if (prev_node_value !== next_node_value) { | |
dom.nodeValue = next_node_value | |
// @ts-expect-error need to add __className to patched prototype | |
dom.__nodeValue = next_node_value | |
} | |
} | |
/** | |
* @param {unknown} value | |
* @returns {string} | |
*/ | |
function stringify(value) { | |
return typeof value === 'string' ? value : value == null ? '' : value + '' | |
} | |
/** | |
* Mounts a component to the given target and returns the exports and potentially the props (if compiled with `accessors: true`) of the component | |
* | |
* @template {Record<string, any>} Props | |
* @template {Record<string, any>} Exports | |
* @template {Record<string, any>} Events | |
* @param {import('../../index.js').ComponentType<import('../../index.js').SvelteComponent<Props, Events>>} component | |
* @param {{ | |
* target: Document | Element | ShadowRoot; | |
* anchor?: Node; | |
* props?: Props; | |
* events?: { [Property in keyof Events]: (e: Events[Property]) => any }; | |
* context?: Map<any, any>; | |
* intro?: boolean; | |
* }} options | |
* @returns {Exports} | |
*/ | |
function mount(component, options) { | |
const anchor = options.anchor ?? options.target.appendChild(empty$1()) | |
// Don't flush previous effects to ensure order of outer effects stays consistent | |
return flush_sync(() => _mount(component, { ...options, anchor }), false) | |
} | |
/** | |
* Hydrates a component on the given target and returns the exports and potentially the props (if compiled with `accessors: true`) of the component | |
* | |
* @template {Record<string, any>} Props | |
* @template {Record<string, any>} Exports | |
* @template {Record<string, any>} Events | |
* @param {import('../../index.js').ComponentType<import('../../index.js').SvelteComponent<Props, Events>>} component | |
* @param {{ | |
* target: Document | Element | ShadowRoot; | |
* props?: Props; | |
* events?: { [Property in keyof Events]: (e: Events[Property]) => any }; | |
* context?: Map<any, any>; | |
* intro?: boolean; | |
* recover?: false; | |
* }} options | |
* @returns {Exports} | |
*/ | |
function hydrate(component, options) { | |
const target = options.target | |
const previous_hydrate_nodes = hydrate_nodes | |
let hydrated = false | |
try { | |
// Don't flush previous effects to ensure order of outer effects stays consistent | |
return flush_sync(() => { | |
set_hydrating(true) | |
var node = target.firstChild | |
while ( | |
node && | |
(node.nodeType !== 8 || | |
/** @type {Comment} */ (node).data !== HYDRATION_START) | |
) { | |
node = node.nextSibling | |
} | |
if (!node) { | |
throw new Error('Missing hydration marker') | |
} | |
const anchor = hydrate_anchor(node) | |
const instance = _mount(component, { ...options, anchor }) | |
// flush_sync will run this callback and then synchronously run any pending effects, | |
// which don't belong to the hydration phase anymore - therefore reset it here | |
set_hydrating(false) | |
hydrated = true | |
return instance | |
}, false) | |
} catch (error) { | |
if (!hydrated && options.recover !== false) { | |
// eslint-disable-next-line no-console | |
console.error('ERR_SVELTE_HYDRATION_MISMATCH' + '', error) | |
clear_text_content(target) | |
set_hydrating(false) | |
return mount(component, options) | |
} else { | |
throw error | |
} | |
} finally { | |
set_hydrating(!!previous_hydrate_nodes) | |
set_hydrate_nodes(previous_hydrate_nodes) | |
} | |
} | |
/** | |
* @template {Record<string, any>} Props | |
* @template {Record<string, any>} Exports | |
* @template {Record<string, any>} Events | |
* @param {import('../../index.js').ComponentType<import('../../index.js').SvelteComponent<Props, Events>>} Component | |
* @param {{ | |
* target: Document | Element | ShadowRoot; | |
* anchor: Node; | |
* props?: Props; | |
* events?: { [Property in keyof Events]: (e: Events[Property]) => any }; | |
* context?: Map<any, any>; | |
* intro?: boolean; | |
* }} options | |
* @returns {Exports} | |
*/ | |
function _mount( | |
Component, | |
{ | |
target, | |
anchor, | |
props = /** @type {Props} */ ({}), | |
events, | |
context, | |
intro = false, | |
}, | |
) { | |
init_operations() | |
const registered_events = new Set() | |
const bound_event_listener = handle_event_propagation.bind(null, target) | |
const bound_document_event_listener = handle_event_propagation.bind( | |
null, | |
document, | |
) | |
/** @param {Array<string>} events */ | |
const event_handle = (events) => { | |
for (let i = 0; i < events.length; i++) { | |
const event_name = events[i] | |
if (!registered_events.has(event_name)) { | |
registered_events.add(event_name) | |
// Add the event listener to both the container and the document. | |
// The container listener ensures we catch events from within in case | |
// the outer content stops propagation of the event. | |
target.addEventListener( | |
event_name, | |
bound_event_listener, | |
PassiveDelegatedEvents.includes(event_name) | |
? { | |
passive: true, | |
} | |
: undefined, | |
) | |
// The document listener ensures we catch events that originate from elements that were | |
// manually moved outside of the container (e.g. via manual portals). | |
document.addEventListener( | |
event_name, | |
bound_document_event_listener, | |
PassiveDelegatedEvents.includes(event_name) | |
? { | |
passive: true, | |
} | |
: undefined, | |
) | |
} | |
} | |
} | |
event_handle(array_from(all_registered_events)) | |
root_event_handles.add(event_handle) | |
/** @type {Exports} */ | |
// @ts-expect-error will be defined because the render effect runs synchronously | |
let component = undefined | |
const unmount = effect_root(() => { | |
branch(() => { | |
if (context) { | |
push({}) | |
var ctx = /** @type {import('#client').ComponentContext} */ ( | |
current_component_context | |
) | |
ctx.c = context | |
} | |
if (events) { | |
// We can't spread the object or else we'd lose the state proxy stuff, if it is one | |
/** @type {any} */ ;(props).$$events = events | |
} | |
// @ts-expect-error the public typings are not what the actual function looks like | |
component = Component(anchor, props) || {} | |
if (context) { | |
pop() | |
} | |
}) | |
return () => { | |
for (const event_name of registered_events) { | |
target.removeEventListener(event_name, bound_event_listener) | |
} | |
root_event_handles.delete(event_handle) | |
} | |
}) | |
mounted_components.set(component, unmount) | |
return component | |
} | |
/** | |
* References of the components that were mounted or hydrated. | |
* Uses a `WeakMap` to avoid memory leaks. | |
*/ | |
let mounted_components = new WeakMap() | |
/** | |
* Unmounts a component that was previously mounted using `mount` or `hydrate`. | |
* @param {Record<string, any>} component | |
*/ | |
function unmount(component) { | |
const fn = mounted_components.get(component) | |
fn?.() | |
} | |
/** | |
* @template P | |
* @param {Element} dom | |
* @param {(dom: Element, value?: P) => import('#client').ActionPayload<P>} action | |
* @param {() => P} [get_value] | |
* @returns {void} | |
*/ | |
function action(dom, action, get_value) { | |
effect(() => { | |
var payload = untrack(() => action(dom, get_value?.()) || {}) | |
if (get_value && payload?.update) { | |
var inited = false | |
render_effect(() => { | |
var value = get_value() | |
// Action's update method is coarse-grained, i.e. when anything in the passed value changes, update. | |
// This works in legacy mode because of mutable_source being updated as a whole, but when using $state | |
// together with actions and mutation, it wouldn't notice the change without a deep read. | |
deep_read_state(value) | |
if (inited) { | |
/** @type {Function} */ ;(payload.update)(value) | |
} | |
}) | |
inited = true | |
} | |
if (payload?.destroy) { | |
return () => /** @type {Function} */ (payload.destroy)() | |
} | |
}) | |
} | |
/** | |
* @param {Element} element | |
* @param {string} attribute | |
* @param {string | null} value | |
*/ | |
function set_attribute(element, attribute, value) { | |
value = value == null ? null : value + '' | |
// @ts-expect-error | |
var attributes = (element.__attributes ??= {}) | |
if (hydrating) { | |
attributes[attribute] = element.getAttribute(attribute) | |
if (attribute === 'src' || attribute === 'href' || attribute === 'srcset') { | |
// If we reset these attributes, they would result in another network request, which we want to avoid. | |
// We assume they are the same between client and server as checking if they are equal is expensive | |
// (we can't just compare the strings as they can be different between client and server but result in the | |
// same url, so we would need to create hidden anchor elements to compare them) | |
return | |
} | |
} | |
if (attributes[attribute] === (attributes[attribute] = value)) return | |
if (value === null) { | |
element.removeAttribute(attribute) | |
} else { | |
element.setAttribute(attribute, value) | |
} | |
} | |
/** | |
* @param {string} content | |
* @param {number} flags | |
* @returns {() => Node | Node[]} | |
*/ | |
/*#__NO_SIDE_EFFECTS__*/ | |
function template(content, flags) { | |
var is_fragment = (flags & TEMPLATE_FRAGMENT) !== 0 | |
var use_import_node = (flags & TEMPLATE_USE_IMPORT_NODE) !== 0 | |
/** @type {Node} */ | |
var node | |
return () => { | |
if (hydrating) { | |
return is_fragment | |
? hydrate_nodes | |
: /** @type {Node} */ (hydrate_nodes[0]) | |
} | |
if (!node) { | |
node = create_fragment_from_html(content) | |
if (!is_fragment) node = /** @type {Node} */ (node.firstChild) | |
} | |
return use_import_node | |
? document.importNode(node, true) | |
: clone_node(node, true) | |
} | |
} | |
/** | |
* @param {string} content | |
* @param {number} flags | |
* @returns {() => Node | Node[]} | |
*/ | |
/*#__NO_SIDE_EFFECTS__*/ | |
function svg_template(content, flags) { | |
var is_fragment = (flags & TEMPLATE_FRAGMENT) !== 0 | |
var fn = template(`<svg>${content}</svg>`, 0) // we don't need to worry about using importNode for SVGs | |
/** @type {Element | DocumentFragment} */ | |
var node | |
return () => { | |
if (hydrating) { | |
return is_fragment | |
? hydrate_nodes | |
: /** @type {Node} */ (hydrate_nodes[0]) | |
} | |
if (!node) { | |
var svg = /** @type {Element} */ (fn()) | |
if ((flags & TEMPLATE_FRAGMENT) === 0) { | |
node = /** @type {Element} */ (svg.firstChild) | |
} else { | |
node = document.createDocumentFragment() | |
while (svg.firstChild) { | |
node.appendChild(svg.firstChild) | |
} | |
} | |
} | |
return clone_node(node, true) | |
} | |
} | |
const comment = template('<!>', TEMPLATE_FRAGMENT) | |
/** | |
* Assign the created (or in hydration mode, traversed) dom elements to the current block | |
* and insert the elements into the dom (in client mode). | |
* @param {Text | Comment | Element} anchor | |
* @param {import('#client').Dom} dom | |
*/ | |
function append(anchor, dom) { | |
var current = dom | |
if (!hydrating) { | |
var node = /** @type {Node} */ (dom) | |
if (node.nodeType === 11) { | |
// if hydrating, `dom` is already an array of nodes, but if not then | |
// we need to create an array to store it on the current effect | |
current = /** @type {import('#client').Dom} */ ([...node.childNodes]) | |
} | |
anchor.before(node) | |
} | |
/** @type {import('#client').Effect} */ ;(current_effect).dom = current | |
} | |
/** | |
* This function is responsible for synchronizing a possibly bound prop with the inner component state. | |
* It is used whenever the compiler sees that the component writes to the prop, or when it has a default prop_value. | |
* @template V | |
* @param {Record<string, unknown>} props | |
* @param {string} key | |
* @param {number} flags | |
* @param {V | (() => V)} [fallback] | |
* @returns {(() => V | ((arg: V) => V) | ((arg: V, mutation: boolean) => V))} | |
*/ | |
function prop(props, key, flags, fallback) { | |
var immutable = (flags & PROPS_IS_IMMUTABLE) !== 0 | |
var runes = (flags & PROPS_IS_RUNES) !== 0 | |
var lazy = (flags & PROPS_IS_LAZY_INITIAL) !== 0 | |
var prop_value = /** @type {V} */ (props[key]) | |
var setter = get_descriptor(props, key)?.set | |
var fallback_value = /** @type {V} */ (fallback) | |
var fallback_dirty = true | |
var get_fallback = () => { | |
if (lazy && fallback_dirty) { | |
fallback_dirty = false | |
fallback_value = untrack(/** @type {() => V} */ (fallback)) | |
} | |
return fallback_value | |
} | |
if (prop_value === undefined && fallback !== undefined) { | |
if (setter && runes) { | |
// TODO consolidate all these random runtime errors | |
throw new Error('ERR_SVELTE_BINDING_FALLBACK' + '') | |
} | |
prop_value = get_fallback() | |
if (setter) setter(prop_value) | |
} | |
var getter = runes | |
? () => { | |
var value = /** @type {V} */ (props[key]) | |
if (value === undefined) return get_fallback() | |
fallback_dirty = true | |
return value | |
} | |
: () => { | |
var value = /** @type {V} */ (props[key]) | |
if (value !== undefined) fallback_value = /** @type {V} */ (undefined) | |
return value === undefined ? fallback_value : value | |
} | |
// easy mode — prop is never written to | |
if ((flags & PROPS_IS_UPDATED) === 0) { | |
return getter | |
} | |
// intermediate mode — prop is written to, but the parent component had | |
// `bind:foo` which means we can just call `$$props.foo = value` directly | |
if (setter) { | |
return function (/** @type {V} */ value) { | |
if (arguments.length === 1) { | |
/** @type {Function} */ ;(setter)(value) | |
return value | |
} else { | |
return getter() | |
} | |
} | |
} | |
// hard mode. this is where it gets ugly — the value in the child should | |
// synchronize with the parent, but it should also be possible to temporarily | |
// set the value to something else locally. | |
var from_child = false | |
// The derived returns the current value. The underlying mutable | |
// source is written to from various places to persist this value. | |
var inner_current_value = mutable_source(prop_value) | |
var current_value = derived(() => { | |
var parent_value = getter() | |
var child_value = get$2(inner_current_value) | |
if (from_child) { | |
from_child = false | |
return child_value | |
} | |
return (inner_current_value.v = parent_value) | |
}) | |
if (!immutable) current_value.equals = safe_equals | |
return function (/** @type {V} */ value) { | |
var current = get$2(current_value) | |
if (arguments.length > 0) { | |
if (!current_value.equals(value)) { | |
from_child = true | |
set$2(inner_current_value, value) | |
get$2(current_value) // force a synchronisation immediately | |
} | |
return value | |
} | |
return current | |
} | |
} | |
/** | |
* Takes the same options as a Svelte 4 component and the component function and returns a Svelte 4 compatible component. | |
* | |
* @deprecated Use this only as a temporary solution to migrate your imperative component code to Svelte 5. | |
* | |
* @template {Record<string, any>} Props | |
* @template {Record<string, any>} Exports | |
* @template {Record<string, any>} Events | |
* @template {Record<string, any>} Slots | |
* | |
* @param {import('svelte').ComponentConstructorOptions<Props> & { | |
* component: import('svelte').ComponentType<import('svelte').SvelteComponent<Props, Events, Slots>>; | |
* immutable?: boolean; | |
* hydrate?: boolean; | |
* recover?: boolean; | |
* }} options | |
* @returns {import('svelte').SvelteComponent<Props, Events, Slots> & Exports} | |
*/ | |
function createClassComponent(options) { | |
// @ts-expect-error $$prop_def etc are not actually defined | |
return new Svelte4Component(options) | |
} | |
class Svelte4Component { | |
/** @type {any} */ | |
#events = {} | |
/** @type {Record<string, any>} */ | |
#instance | |
/** | |
* @param {import('svelte').ComponentConstructorOptions & { | |
* component: any; | |
* immutable?: boolean; | |
* hydrate?: boolean; | |
* recover?: false; | |
* }} options | |
*/ | |
constructor(options) { | |
// Using proxy state here isn't completely mirroring the Svelte 4 behavior, because mutations to a property | |
// cause fine-grained updates to only the places where that property is used, and not the entire property. | |
// Reactive statements and actions (the things where this matters) are handling this properly regardless, so it should be fine in practise. | |
const props = proxy( | |
{ ...(options.props || {}), $$events: this.#events }, | |
false, | |
) | |
this.#instance = (options.hydrate ? hydrate : mount)(options.component, { | |
target: options.target, | |
props, | |
context: options.context, | |
intro: options.intro, | |
recover: options.recover, | |
}) | |
for (const key of Object.keys(this.#instance)) { | |
if (key === '$set' || key === '$destroy' || key === '$on') continue | |
define_property(this, key, { | |
get() { | |
return this.#instance[key] | |
}, | |
/** @param {any} value */ | |
set(value) { | |
this.#instance[key] = value | |
}, | |
enumerable: true, | |
}) | |
} | |
this.#instance.$set = /** @param {Record<string, any>} next */ (next) => { | |
Object.assign(props, next) | |
} | |
this.#instance.$destroy = () => { | |
unmount(this.#instance) | |
} | |
} | |
/** @param {Record<string, any>} props */ | |
$set(props) { | |
this.#instance.$set(props) | |
} | |
/** | |
* @param {string} event | |
* @param {(...args: any[]) => any} callback | |
* @returns {any} | |
*/ | |
$on(event, callback) { | |
this.#events[event] = this.#events[event] || [] | |
/** @param {any[]} args */ | |
const cb = (...args) => callback.call(this, ...args) | |
this.#events[event].push(cb) | |
return () => { | |
this.#events[event] = this.#events[event].filter( | |
/** @param {any} fn */ (fn) => fn !== cb, | |
) | |
} | |
} | |
$destroy() { | |
this.#instance.$destroy() | |
} | |
} | |
/** | |
* @typedef {Object} CustomElementPropDefinition | |
* @property {string} [attribute] | |
* @property {boolean} [reflect] | |
* @property {'String'|'Boolean'|'Number'|'Array'|'Object'} [type] | |
*/ | |
/** @type {any} */ | |
let SvelteElement | |
if (typeof HTMLElement === 'function') { | |
SvelteElement = class extends HTMLElement { | |
/** The Svelte component constructor */ | |
$$ctor | |
/** Slots */ | |
$$s | |
/** @type {any} The Svelte component instance */ | |
$$c | |
/** Whether or not the custom element is connected */ | |
$$cn = false | |
/** @type {Record<string, any>} 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, EventListenerOrEventListenerObject[]>} Event listeners */ | |
$$l = {} | |
/** @type {Map<EventListenerOrEventListenerObject, Function>} Event listener unsubscribe functions */ | |
$$l_u = new Map() | |
/** @type {any} The managed render effect for reflecting attributes */ | |
$$me | |
/** | |
* @param {*} $$componentCtor | |
* @param {*} $$slots | |
* @param {*} use_shadow_dom | |
*/ | |
constructor($$componentCtor, $$slots, use_shadow_dom) { | |
super() | |
this.$$ctor = $$componentCtor | |
this.$$s = $$slots | |
if (use_shadow_dom) { | |
this.attachShadow({ mode: 'open' }) | |
} | |
} | |
/** | |
* @param {string} type | |
* @param {EventListenerOrEventListenerObject} listener | |
* @param {boolean | AddEventListenerOptions} [options] | |
*/ | |
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) | |
} | |
/** | |
* @param {string} type | |
* @param {EventListenerOrEventListenerObject} listener | |
* @param {boolean | AddEventListenerOptions} [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 || this.$$c) { | |
return | |
} | |
/** @param {string} name */ | |
function create_slot(name) { | |
/** | |
* @param {Element} anchor | |
*/ | |
return (anchor) => { | |
const slot = document.createElement('slot') | |
if (name !== 'default') slot.name = name | |
append(anchor, slot) | |
} | |
} | |
/** @type {Record<string, any>} */ | |
const $$slots = {} | |
const existing_slots = get_custom_elements_slots(this) | |
for (const name of this.$$s) { | |
if (name in existing_slots) { | |
if (name === 'default') { | |
this.$$d.children = create_slot(name) | |
} else { | |
$$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', | |
) | |
} | |
} | |
// Port over props that were set programmatically before ce was initialized | |
for (const key in this.$$p_d) { | |
// @ts-expect-error | |
if (!(key in this.$$d) && this[key] !== undefined) { | |
// @ts-expect-error | |
this.$$d[key] = this[key] // don't transform, these were set through JavaScript | |
// @ts-expect-error | |
delete this[key] // remove the property that shadows the getter/setter | |
} | |
} | |
this.$$c = createClassComponent({ | |
component: this.$$ctor, | |
target: this.shadowRoot || this, | |
props: { | |
...this.$$d, | |
$$slots, | |
}, | |
}) | |
// Reflect component props as attributes | |
this.$$me = render_effect(() => { | |
this.$$r = true | |
for (const key of object_keys(this.$$c)) { | |
if (!this.$$p_d[key]?.reflect) continue | |
this.$$d[key] = this.$$c[key] | |
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 | |
}) | |
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 | |
/** | |
* @param {string} attr | |
* @param {string} _oldValue | |
* @param {string} newValue | |
*/ | |
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() | |
destroy_effect(this.$$me) | |
this.$$c = undefined | |
} | |
}) | |
} | |
/** | |
* @param {string} attribute_name | |
*/ | |
$$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 | |
} | |
} | |
} | |
/** | |
* @param {HTMLElement} element | |
*/ | |
function get_custom_elements_slots(element) { | |
/** @type {Record<string, true>} */ | |
const result = {} | |
element.childNodes.forEach((node) => { | |
result[/** @type {Element} node */ (node).slot || 'default'] = true | |
}) | |
return result | |
} | |
/** | |
* @internal | |
* | |
* Turn a Svelte component into a custom element. | |
* @param {any} Component A Svelte component function | |
* @param {Record<string, CustomElementPropDefinition>} props_definition The props to observe | |
* @param {string[]} slots The slots to create | |
* @param {string[]} exports Explicitly exported values, other than props | |
* @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, | |
exports, | |
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) => { | |
define_property(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 }) | |
}, | |
}) | |
}) | |
exports.forEach((property) => { | |
define_property(Class.prototype, property, { | |
get() { | |
return this.$$c?.[property] | |
}, | |
}) | |
}) | |
if (extend) { | |
// @ts-expect-error - assigning here is fine | |
Class = extend(Class) | |
} | |
Component.element = /** @type {any} */ Class | |
return Class | |
} | |
class InternMap extends Map { | |
constructor(entries, key = keyof) { | |
super() | |
Object.defineProperties(this, { | |
_intern: { value: new Map() }, | |
_key: { value: key }, | |
}) | |
if (entries != null) | |
for (const [key, value] of entries) this.set(key, value) | |
} | |
get(key) { | |
return super.get(intern_get(this, key)) | |
} | |
has(key) { | |
return super.has(intern_get(this, key)) | |
} | |
set(key, value) { | |
return super.set(intern_set(this, key), value) | |
} | |
delete(key) { | |
return super.delete(intern_delete(this, key)) | |
} | |
} | |
function intern_get({ _intern, _key }, value) { | |
const key = _key(value) | |
return _intern.has(key) ? _intern.get(key) : value | |
} | |
function intern_set({ _intern, _key }, value) { | |
const key = _key(value) | |
if (_intern.has(key)) return _intern.get(key) | |
_intern.set(key, value) | |
return value | |
} | |
function intern_delete({ _intern, _key }, value) { | |
const key = _key(value) | |
if (_intern.has(key)) { | |
value = _intern.get(key) | |
_intern.delete(key) | |
} | |
return value | |
} | |
function keyof(value) { | |
return value !== null && typeof value === 'object' ? value.valueOf() : value | |
} | |
var noop = { value: () => {} } | |
function dispatch() { | |
for (var i = 0, n = arguments.length, _ = {}, t; i < n; ++i) { | |
if (!(t = arguments[i] + '') || t in _ || /[\s.]/.test(t)) | |
throw new Error('illegal type: ' + t) | |
_[t] = [] | |
} | |
return new Dispatch(_) | |
} | |
function Dispatch(_) { | |
this._ = _ | |
} | |
function parseTypenames$1(typenames, types) { | |
return typenames | |
.trim() | |
.split(/^|\s+/) | |
.map(function (t) { | |
var name = '', | |
i = t.indexOf('.') | |
if (i >= 0) (name = t.slice(i + 1)), (t = t.slice(0, i)) | |
if (t && !types.hasOwnProperty(t)) throw new Error('unknown type: ' + t) | |
return { type: t, name: name } | |
}) | |
} | |
Dispatch.prototype = dispatch.prototype = { | |
constructor: Dispatch, | |
on: function (typename, callback) { | |
var _ = this._, | |
T = parseTypenames$1(typename + '', _), | |
t, | |
i = -1, | |
n = T.length | |
// If no callback was specified, return the callback of the given type and name. | |
if (arguments.length < 2) { | |
while (++i < n) | |
if ((t = (typename = T[i]).type) && (t = get$1(_[t], typename.name))) | |
return t | |
return | |
} | |
// If a type was specified, set the callback for the given type and name. | |
// Otherwise, if a null callback was specified, remove callbacks of the given name. | |
if (callback != null && typeof callback !== 'function') | |
throw new Error('invalid callback: ' + callback) | |
while (++i < n) { | |
if ((t = (typename = T[i]).type)) | |
_[t] = set$1(_[t], typename.name, callback) | |
else if (callback == null) | |
for (t in _) _[t] = set$1(_[t], typename.name, null) | |
} | |
return this | |
}, | |
copy: function () { | |
var copy = {}, | |
_ = this._ | |
for (var t in _) copy[t] = _[t].slice() | |
return new Dispatch(copy) | |
}, | |
call: function (type, that) { | |
if ((n = arguments.length - 2) > 0) | |
for (var args = new Array(n), i = 0, n, t; i < n; ++i) | |
args[i] = arguments[i + 2] | |
if (!this._.hasOwnProperty(type)) throw new Error('unknown type: ' + type) | |
for (t = this._[type], i = 0, n = t.length; i < n; ++i) | |
t[i].value.apply(that, args) | |
}, | |
apply: function (type, that, args) { | |
if (!this._.hasOwnProperty(type)) throw new Error('unknown type: ' + type) | |
for (var t = this._[type], i = 0, n = t.length; i < n; ++i) | |
t[i].value.apply(that, args) | |
}, | |
} | |
function get$1(type, name) { | |
for (var i = 0, n = type.length, c; i < n; ++i) { | |
if ((c = type[i]).name === name) { | |
return c.value | |
} | |
} | |
} | |
function set$1(type, name, callback) { | |
for (var i = 0, n = type.length; i < n; ++i) { | |
if (type[i].name === name) { | |
;(type[i] = noop), (type = type.slice(0, i).concat(type.slice(i + 1))) | |
break | |
} | |
} | |
if (callback != null) type.push({ name: name, value: callback }) | |
return type | |
} | |
var xhtml = 'http://www.w3.org/1999/xhtml' | |
var namespaces = { | |
svg: 'http://www.w3.org/2000/svg', | |
xhtml: xhtml, | |
xlink: 'http://www.w3.org/1999/xlink', | |
xml: 'http://www.w3.org/XML/1998/namespace', | |
xmlns: 'http://www.w3.org/2000/xmlns/', | |
} | |
function namespace(name) { | |
var prefix = (name += ''), | |
i = prefix.indexOf(':') | |
if (i >= 0 && (prefix = name.slice(0, i)) !== 'xmlns') | |
name = name.slice(i + 1) | |
return namespaces.hasOwnProperty(prefix) | |
? { space: namespaces[prefix], local: name } | |
: name // eslint-disable-line no-prototype-builtins | |
} | |
function creatorInherit(name) { | |
return function () { | |
var document = this.ownerDocument, | |
uri = this.namespaceURI | |
return uri === xhtml && document.documentElement.namespaceURI === xhtml | |
? document.createElement(name) | |
: document.createElementNS(uri, name) | |
} | |
} | |
function creatorFixed(fullname) { | |
return function () { | |
return this.ownerDocument.createElementNS(fullname.space, fullname.local) | |
} | |
} | |
function creator(name) { | |
var fullname = namespace(name) | |
return (fullname.local ? creatorFixed : creatorInherit)(fullname) | |
} | |
function none() {} | |
function selector(selector) { | |
return selector == null | |
? none | |
: function () { | |
return this.querySelector(selector) | |
} | |
} | |
function selection_select(select) { | |
if (typeof select !== 'function') select = selector(select) | |
for ( | |
var groups = this._groups, | |
m = groups.length, | |
subgroups = new Array(m), | |
j = 0; | |
j < m; | |
++j | |
) { | |
for ( | |
var group = groups[j], | |
n = group.length, | |
subgroup = (subgroups[j] = new Array(n)), | |
node, | |
subnode, | |
i = 0; | |
i < n; | |
++i | |
) { | |
if ( | |
(node = group[i]) && | |
(subnode = select.call(node, node.__data__, i, group)) | |
) { | |
if ('__data__' in node) subnode.__data__ = node.__data__ | |
subgroup[i] = subnode | |
} | |
} | |
} | |
return new Selection$1(subgroups, this._parents) | |
} | |
// Given something array like (or null), returns something that is strictly an | |
// array. This is used to ensure that array-like objects passed to d3.selectAll | |
// or selection.selectAll are converted into proper arrays when creating a | |
// selection; we don’t ever want to create a selection backed by a live | |
// HTMLCollection or NodeList. However, note that selection.selectAll will use a | |
// static NodeList as a group, since it safely derived from querySelectorAll. | |
function array(x) { | |
return x == null ? [] : Array.isArray(x) ? x : Array.from(x) | |
} | |
function empty() { | |
return [] | |
} | |
function selectorAll(selector) { | |
return selector == null | |
? empty | |
: function () { | |
return this.querySelectorAll(selector) | |
} | |
} | |
function arrayAll(select) { | |
return function () { | |
return array(select.apply(this, arguments)) | |
} | |
} | |
function selection_selectAll(select) { | |
if (typeof select === 'function') select = arrayAll(select) | |
else select = selectorAll(select) | |
for ( | |
var groups = this._groups, | |
m = groups.length, | |
subgroups = [], | |
parents = [], | |
j = 0; | |
j < m; | |
++j | |
) { | |
for (var group = groups[j], n = group.length, node, i = 0; i < n; ++i) { | |
if ((node = group[i])) { | |
subgroups.push(select.call(node, node.__data__, i, group)) | |
parents.push(node) | |
} | |
} | |
} | |
return new Selection$1(subgroups, parents) | |
} | |
function matcher(selector) { | |
return function () { | |
return this.matches(selector) | |
} | |
} | |
function childMatcher(selector) { | |
return function (node) { | |
return node.matches(selector) | |
} | |
} | |
var find$1 = Array.prototype.find | |
function childFind(match) { | |
return function () { | |
return find$1.call(this.children, match) | |
} | |
} | |
function childFirst() { | |
return this.firstElementChild | |
} | |
function selection_selectChild(match) { | |
return this.select( | |
match == null | |
? childFirst | |
: childFind(typeof match === 'function' ? match : childMatcher(match)), | |
) | |
} | |
var filter$1 = Array.prototype.filter | |
function children() { | |
return Array.from(this.children) | |
} | |
function childrenFilter(match) { | |
return function () { | |
return filter$1.call(this.children, match) | |
} | |
} | |
function selection_selectChildren(match) { | |
return this.selectAll( | |
match == null | |
? children | |
: childrenFilter( | |
typeof match === 'function' ? match : childMatcher(match), | |
), | |
) | |
} | |
function selection_filter(match) { | |
if (typeof match !== 'function') match = matcher(match) | |
for ( | |
var groups = this._groups, | |
m = groups.length, | |
subgroups = new Array(m), | |
j = 0; | |
j < m; | |
++j | |
) { | |
for ( | |
var group = groups[j], | |
n = group.length, | |
subgroup = (subgroups[j] = []), | |
node, | |
i = 0; | |
i < n; | |
++i | |
) { | |
if ((node = group[i]) && match.call(node, node.__data__, i, group)) { | |
subgroup.push(node) | |
} | |
} | |
} | |
return new Selection$1(subgroups, this._parents) | |
} | |
function sparse(update) { | |
return new Array(update.length) | |
} | |
function selection_enter() { | |
return new Selection$1(this._enter || this._groups.map(sparse), this._parents) | |
} | |
function EnterNode(parent, datum) { | |
this.ownerDocument = parent.ownerDocument | |
this.namespaceURI = parent.namespaceURI | |
this._next = null | |
this._parent = parent | |
this.__data__ = datum | |
} | |
EnterNode.prototype = { | |
constructor: EnterNode, | |
appendChild: function (child) { | |
return this._parent.insertBefore(child, this._next) | |
}, | |
insertBefore: function (child, next) { | |
return this._parent.insertBefore(child, next) | |
}, | |
querySelector: function (selector) { | |
return this._parent.querySelector(selector) | |
}, | |
querySelectorAll: function (selector) { | |
return this._parent.querySelectorAll(selector) | |
}, | |
} | |
function constant$3(x) { | |
return function () { | |
return x | |
} | |
} | |
function bindIndex(parent, group, enter, update, exit, data) { | |
var i = 0, | |
node, | |
groupLength = group.length, | |
dataLength = data.length | |
// Put any non-null nodes that fit into update. | |
// Put any null nodes into enter. | |
// Put any remaining data into enter. | |
for (; i < dataLength; ++i) { | |
if ((node = group[i])) { | |
node.__data__ = data[i] | |
update[i] = node | |
} else { | |
enter[i] = new EnterNode(parent, data[i]) | |
} | |
} | |
// Put any non-null nodes that don’t fit into exit. | |
for (; i < groupLength; ++i) { | |
if ((node = group[i])) { | |
exit[i] = node | |
} | |
} | |
} | |
function bindKey(parent, group, enter, update, exit, data, key) { | |
var i, | |
node, | |
nodeByKeyValue = new Map(), | |
groupLength = group.length, | |
dataLength = data.length, | |
keyValues = new Array(groupLength), | |
keyValue | |
// Compute the key for each node. | |
// If multiple nodes have the same key, the duplicates are added to exit. | |
for (i = 0; i < groupLength; ++i) { | |
if ((node = group[i])) { | |
keyValues[i] = keyValue = key.call(node, node.__data__, i, group) + '' | |
if (nodeByKeyValue.has(keyValue)) { | |
exit[i] = node | |
} else { | |
nodeByKeyValue.set(keyValue, node) | |
} | |
} | |
} | |
// Compute the key for each datum. | |
// If there a node associated with this key, join and add it to update. | |
// If there is not (or the key is a duplicate), add it to enter. | |
for (i = 0; i < dataLength; ++i) { | |
keyValue = key.call(parent, data[i], i, data) + '' | |
if ((node = nodeByKeyValue.get(keyValue))) { | |
update[i] = node | |
node.__data__ = data[i] | |
nodeByKeyValue.delete(keyValue) | |
} else { | |
enter[i] = new EnterNode(parent, data[i]) | |
} | |
} | |
// Add any remaining nodes that were not bound to data to exit. | |
for (i = 0; i < groupLength; ++i) { | |
if ((node = group[i]) && nodeByKeyValue.get(keyValues[i]) === node) { | |
exit[i] = node | |
} | |
} | |
} | |
function datum(node) { | |
return node.__data__ | |
} | |
function selection_data(value, key) { | |
if (!arguments.length) return Array.from(this, datum) | |
var bind = key ? bindKey : bindIndex, | |
parents = this._parents, | |
groups = this._groups | |
if (typeof value !== 'function') value = constant$3(value) | |
for ( | |
var m = groups.length, | |
update = new Array(m), | |
enter = new Array(m), | |
exit = new Array(m), | |
j = 0; | |
j < m; | |
++j | |
) { | |
var parent = parents[j], | |
group = groups[j], | |
groupLength = group.length, | |
data = arraylike( | |
value.call(parent, parent && parent.__data__, j, parents), | |
), | |
dataLength = data.length, | |
enterGroup = (enter[j] = new Array(dataLength)), | |
updateGroup = (update[j] = new Array(dataLength)), | |
exitGroup = (exit[j] = new Array(groupLength)) | |
bind(parent, group, enterGroup, updateGroup, exitGroup, data, key) | |
// Now connect the enter nodes to their following update node, such that | |
// appendChild can insert the materialized enter node before this node, | |
// rather than at the end of the parent node. | |
for (var i0 = 0, i1 = 0, previous, next; i0 < dataLength; ++i0) { | |
if ((previous = enterGroup[i0])) { | |
if (i0 >= i1) i1 = i0 + 1 | |
while (!(next = updateGroup[i1]) && ++i1 < dataLength); | |
previous._next = next || null | |
} | |
} | |
} | |
update = new Selection$1(update, parents) | |
update._enter = enter | |
update._exit = exit | |
return update | |
} | |
// Given some data, this returns an array-like view of it: an object that | |
// exposes a length property and allows numeric indexing. Note that unlike | |
// selectAll, this isn’t worried about “live” collections because the resulting | |
// array will only be used briefly while data is being bound. (It is possible to | |
// cause the data to change while iterating by using a key function, but please | |
// don’t; we’d rather avoid a gratuitous copy.) | |
function arraylike(data) { | |
return typeof data === 'object' && 'length' in data | |
? data // Array, TypedArray, NodeList, array-like | |
: Array.from(data) // Map, Set, iterable, string, or anything else | |
} | |
function selection_exit() { | |
return new Selection$1(this._exit || this._groups.map(sparse), this._parents) | |
} | |
function selection_join(onenter, onupdate, onexit) { | |
var enter = this.enter(), | |
update = this, | |
exit = this.exit() | |
if (typeof onenter === 'function') { | |
enter = onenter(enter) | |
if (enter) enter = enter.selection() | |
} else { | |
enter = enter.append(onenter + '') | |
} | |
if (onupdate != null) { | |
update = onupdate(update) | |
if (update) update = update.selection() | |
} | |
if (onexit == null) exit.remove() | |
else onexit(exit) | |
return enter && update ? enter.merge(update).order() : update | |
} | |
function selection_merge(context) { | |
var selection = context.selection ? context.selection() : context | |
for ( | |
var groups0 = this._groups, | |
groups1 = selection._groups, | |
m0 = groups0.length, | |
m1 = groups1.length, | |
m = Math.min(m0, m1), | |
merges = new Array(m0), | |
j = 0; | |
j < m; | |
++j | |
) { | |
for ( | |
var group0 = groups0[j], | |
group1 = groups1[j], | |
n = group0.length, | |
merge = (merges[j] = new Array(n)), | |
node, | |
i = 0; | |
i < n; | |
++i | |
) { | |
if ((node = group0[i] || group1[i])) { | |
merge[i] = node | |
} | |
} | |
} | |
for (; j < m0; ++j) { | |
merges[j] = groups0[j] | |
} | |
return new Selection$1(merges, this._parents) | |
} | |
function selection_order() { | |
for (var groups = this._groups, j = -1, m = groups.length; ++j < m; ) { | |
for ( | |
var group = groups[j], i = group.length - 1, next = group[i], node; | |
--i >= 0; | |
) { | |
if ((node = group[i])) { | |
if (next && node.compareDocumentPosition(next) ^ 4) | |
next.parentNode.insertBefore(node, next) | |
next = node | |
} | |
} | |
} | |
return this | |
} | |
function selection_sort(compare) { | |
if (!compare) compare = ascending | |
function compareNode(a, b) { | |
return a && b ? compare(a.__data__, b.__data__) : !a - !b | |
} | |
for ( | |
var groups = this._groups, | |
m = groups.length, | |
sortgroups = new Array(m), | |
j = 0; | |
j < m; | |
++j | |
) { | |
for ( | |
var group = groups[j], | |
n = group.length, | |
sortgroup = (sortgroups[j] = new Array(n)), | |
node, | |
i = 0; | |
i < n; | |
++i | |
) { | |
if ((node = group[i])) { | |
sortgroup[i] = node | |
} | |
} | |
sortgroup.sort(compareNode) | |
} | |
return new Selection$1(sortgroups, this._parents).order() | |
} | |
function ascending(a, b) { | |
return a < b ? -1 : a > b ? 1 : a >= b ? 0 : NaN | |
} | |
function selection_call() { | |
var callback = arguments[0] | |
arguments[0] = this | |
callback.apply(null, arguments) | |
return this | |
} | |
function selection_nodes() { | |
return Array.from(this) | |
} | |
function selection_node() { | |
for (var groups = this._groups, j = 0, m = groups.length; j < m; ++j) { | |
for (var group = groups[j], i = 0, n = group.length; i < n; ++i) { | |
var node = group[i] | |
if (node) return node | |
} | |
} | |
return null | |
} | |
function selection_size() { | |
let size = 0 | |
for (const node of this) ++size // eslint-disable-line no-unused-vars | |
return size | |
} | |
function selection_empty() { | |
return !this.node() | |
} | |
function selection_each(callback) { | |
for (var groups = this._groups, j = 0, m = groups.length; j < m; ++j) { | |
for (var group = groups[j], i = 0, n = group.length, node; i < n; ++i) { | |
if ((node = group[i])) callback.call(node, node.__data__, i, group) | |
} | |
} | |
return this | |
} | |
function attrRemove$1(name) { | |
return function () { | |
this.removeAttribute(name) | |
} | |
} | |
function attrRemoveNS$1(fullname) { | |
return function () { | |
this.removeAttributeNS(fullname.space, fullname.local) | |
} | |
} | |
function attrConstant$1(name, value) { | |
return function () { | |
this.setAttribute(name, value) | |
} | |
} | |
function attrConstantNS$1(fullname, value) { | |
return function () { | |
this.setAttributeNS(fullname.space, fullname.local, value) | |
} | |
} | |
function attrFunction$1(name, value) { | |
return function () { | |
var v = value.apply(this, arguments) | |
if (v == null) this.removeAttribute(name) | |
else this.setAttribute(name, v) | |
} | |
} | |
function attrFunctionNS$1(fullname, value) { | |
return function () { | |
var v = value.apply(this, arguments) | |
if (v == null) this.removeAttributeNS(fullname.space, fullname.local) | |
else this.setAttributeNS(fullname.space, fullname.local, v) | |
} | |
} | |
function selection_attr(name, value) { | |
var fullname = namespace(name) | |
if (arguments.length < 2) { | |
var node = this.node() | |
return fullname.local | |
? node.getAttributeNS(fullname.space, fullname.local) | |
: node.getAttribute(fullname) | |
} | |
return this.each( | |
(value == null | |
? fullname.local | |
? attrRemoveNS$1 | |
: attrRemove$1 | |
: typeof value === 'function' | |
? fullname.local | |
? attrFunctionNS$1 | |
: attrFunction$1 | |
: fullname.local | |
? attrConstantNS$1 | |
: attrConstant$1)(fullname, value), | |
) | |
} | |
function defaultView(node) { | |
return ( | |
(node.ownerDocument && node.ownerDocument.defaultView) || // node is a Node | |
(node.document && node) || // node is a Window | |
node.defaultView | |
) // node is a Document | |
} | |
function styleRemove$1(name) { | |
return function () { | |
this.style.removeProperty(name) | |
} | |
} | |
function styleConstant$1(name, value, priority) { | |
return function () { | |
this.style.setProperty(name, value, priority) | |
} | |
} | |
function styleFunction$1(name, value, priority) { | |
return function () { | |
var v = value.apply(this, arguments) | |
if (v == null) this.style.removeProperty(name) | |
else this.style.setProperty(name, v, priority) | |
} | |
} | |
function selection_style(name, value, priority) { | |
return arguments.length > 1 | |
? this.each( | |
(value == null | |
? styleRemove$1 | |
: typeof value === 'function' | |
? styleFunction$1 | |
: styleConstant$1)(name, value, priority == null ? '' : priority), | |
) | |
: styleValue(this.node(), name) | |
} | |
function styleValue(node, name) { | |
return ( | |
node.style.getPropertyValue(name) || | |
defaultView(node).getComputedStyle(node, null).getPropertyValue(name) | |
) | |
} | |
function propertyRemove(name) { | |
return function () { | |
delete this[name] | |
} | |
} | |
function propertyConstant(name, value) { | |
return function () { | |
this[name] = value | |
} | |
} | |
function propertyFunction(name, value) { | |
return function () { | |
var v = value.apply(this, arguments) | |
if (v == null) delete this[name] | |
else this[name] = v | |
} | |
} | |
function selection_property(name, value) { | |
return arguments.length > 1 | |
? this.each( | |
(value == null | |
? propertyRemove | |
: typeof value === 'function' | |
? propertyFunction | |
: propertyConstant)(name, value), | |
) | |
: this.node()[name] | |
} | |
function classArray(string) { | |
return string.trim().split(/^|\s+/) | |
} | |
function classList(node) { | |
return node.classList || new ClassList(node) | |
} | |
function ClassList(node) { | |
this._node = node | |
this._names = classArray(node.getAttribute('class') || '') | |
} | |
ClassList.prototype = { | |
add: function (name) { | |
var i = this._names.indexOf(name) | |
if (i < 0) { | |
this._names.push(name) | |
this._node.setAttribute('class', this._names.join(' ')) | |
} | |
}, | |
remove: function (name) { | |
var i = this._names.indexOf(name) | |
if (i >= 0) { | |
this._names.splice(i, 1) | |
this._node.setAttribute('class', this._names.join(' ')) | |
} | |
}, | |
contains: function (name) { | |
return this._names.indexOf(name) >= 0 | |
}, | |
} | |
function classedAdd(node, names) { | |
var list = classList(node), | |
i = -1, | |
n = names.length | |
while (++i < n) list.add(names[i]) | |
} | |
function classedRemove(node, names) { | |
var list = classList(node), | |
i = -1, | |
n = names.length | |
while (++i < n) list.remove(names[i]) | |
} | |
function classedTrue(names) { | |
return function () { | |
classedAdd(this, names) | |
} | |
} | |
function classedFalse(names) { | |
return function () { | |
classedRemove(this, names) | |
} | |
} | |
function classedFunction(names, value) { | |
return function () { | |
;(value.apply(this, arguments) ? classedAdd : classedRemove)(this, names) | |
} | |
} | |
function selection_classed(name, value) { | |
var names = classArray(name + '') | |
if (arguments.length < 2) { | |
var list = classList(this.node()), | |
i = -1, | |
n = names.length | |
while (++i < n) if (!list.contains(names[i])) return false | |
return true | |
} | |
return this.each( | |
(typeof value === 'function' | |
? classedFunction | |
: value | |
? classedTrue | |
: classedFalse)(names, value), | |
) | |
} | |
function textRemove() { | |
this.textContent = '' | |
} | |
function textConstant$1(value) { | |
return function () { | |
this.textContent = value | |
} | |
} | |
function textFunction$1(value) { | |
return function () { | |
var v = value.apply(this, arguments) | |
this.textContent = v == null ? '' : v | |
} | |
} | |
function selection_text(value) { | |
return arguments.length | |
? this.each( | |
value == null | |
? textRemove | |
: (typeof value === 'function' ? textFunction$1 : textConstant$1)( | |
value, | |
), | |
) | |
: this.node().textContent | |
} | |
function htmlRemove() { | |
this.innerHTML = '' | |
} | |
function htmlConstant(value) { | |
return function () { | |
this.innerHTML = value | |
} | |
} | |
function htmlFunction(value) { | |
return function () { | |
var v = value.apply(this, arguments) | |
this.innerHTML = v == null ? '' : v | |
} | |
} | |
function selection_html(value) { | |
return arguments.length | |
? this.each( | |
value == null | |
? htmlRemove | |
: (typeof value === 'function' ? htmlFunction : htmlConstant)(value), | |
) | |
: this.node().innerHTML | |
} | |
function raise() { | |
if (this.nextSibling) this.parentNode.appendChild(this) | |
} | |
function selection_raise() { | |
return this.each(raise) | |
} | |
function lower() { | |
if (this.previousSibling) | |
this.parentNode.insertBefore(this, this.parentNode.firstChild) | |
} | |
function selection_lower() { | |
return this.each(lower) | |
} | |
function selection_append(name) { | |
var create = typeof name === 'function' ? name : creator(name) | |
return this.select(function () { | |
return this.appendChild(create.apply(this, arguments)) | |
}) | |
} | |
function constantNull() { | |
return null | |
} | |
function selection_insert(name, before) { | |
var create = typeof name === 'function' ? name : creator(name), | |
select = | |
before == null | |
? constantNull | |
: typeof before === 'function' | |
? before | |
: selector(before) | |
return this.select(function () { | |
return this.insertBefore( | |
create.apply(this, arguments), | |
select.apply(this, arguments) || null, | |
) | |
}) | |
} | |
function remove() { | |
var parent = this.parentNode | |
if (parent) parent.removeChild(this) | |
} | |
function selection_remove() { | |
return this.each(remove) | |
} | |
function selection_cloneShallow() { | |
var clone = this.cloneNode(false), | |
parent = this.parentNode | |
return parent ? parent.insertBefore(clone, this.nextSibling) : clone | |
} | |
function selection_cloneDeep() { | |
var clone = this.cloneNode(true), | |
parent = this.parentNode | |
return parent ? parent.insertBefore(clone, this.nextSibling) : clone | |
} | |
function selection_clone(deep) { | |
return this.select(deep ? selection_cloneDeep : selection_cloneShallow) | |
} | |
function selection_datum(value) { | |
return arguments.length | |
? this.property('__data__', value) | |
: this.node().__data__ | |
} | |
function contextListener(listener) { | |
return function (event) { | |
listener.call(this, event, this.__data__) | |
} | |
} | |
function parseTypenames(typenames) { | |
return typenames | |
.trim() | |
.split(/^|\s+/) | |
.map(function (t) { | |
var name = '', | |
i = t.indexOf('.') | |
if (i >= 0) (name = t.slice(i + 1)), (t = t.slice(0, i)) | |
return { type: t, name: name } | |
}) | |
} | |
function onRemove(typename) { | |
return function () { | |
var on = this.__on | |
if (!on) return | |
for (var j = 0, i = -1, m = on.length, o; j < m; ++j) { | |
if ( | |
((o = on[j]), | |
(!typename.type || o.type === typename.type) && | |
o.name === typename.name) | |
) { | |
this.removeEventListener(o.type, o.listener, o.options) | |
} else { | |
on[++i] = o | |
} | |
} | |
if (++i) on.length = i | |
else delete this.__on | |
} | |
} | |
function onAdd(typename, value, options) { | |
return function () { | |
var on = this.__on, | |
o, | |
listener = contextListener(value) | |
if (on) | |
for (var j = 0, m = on.length; j < m; ++j) { | |
if ((o = on[j]).type === typename.type && o.name === typename.name) { | |
this.removeEventListener(o.type, o.listener, o.options) | |
this.addEventListener( | |
o.type, | |
(o.listener = listener), | |
(o.options = options), | |
) | |
o.value = value | |
return | |
} | |
} | |
this.addEventListener(typename.type, listener, options) | |
o = { | |
type: typename.type, | |
name: typename.name, | |
value: value, | |
listener: listener, | |
options: options, | |
} | |
if (!on) this.__on = [o] | |
else on.push(o) | |
} | |
} | |
function selection_on(typename, value, options) { | |
var typenames = parseTypenames(typename + ''), | |
i, | |
n = typenames.length, | |
t | |
if (arguments.length < 2) { | |
var on = this.node().__on | |
if (on) | |
for (var j = 0, m = on.length, o; j < m; ++j) { | |
for (i = 0, o = on[j]; i < n; ++i) { | |
if ((t = typenames[i]).type === o.type && t.name === o.name) { | |
return o.value | |
} | |
} | |
} | |
return | |
} | |
on = value ? onAdd : onRemove | |
for (i = 0; i < n; ++i) this.each(on(typenames[i], value, options)) | |
return this | |
} | |
function dispatchEvent(node, type, params) { | |
var window = defaultView(node), | |
event = window.CustomEvent | |
if (typeof event === 'function') { | |
event = new event(type, params) | |
} else { | |
event = window.document.createEvent('Event') | |
if (params) | |
event.initEvent(type, params.bubbles, params.cancelable), | |
(event.detail = params.detail) | |
else event.initEvent(type, false, false) | |
} | |
node.dispatchEvent(event) | |
} | |
function dispatchConstant(type, params) { | |
return function () { | |
return dispatchEvent(this, type, params) | |
} | |
} | |
function dispatchFunction(type, params) { | |
return function () { | |
return dispatchEvent(this, type, params.apply(this, arguments)) | |
} | |
} | |
function selection_dispatch(type, params) { | |
return this.each( | |
(typeof params === 'function' ? dispatchFunction : dispatchConstant)( | |
type, | |
params, | |
), | |
) | |
} | |
function* selection_iterator() { | |
for (var groups = this._groups, j = 0, m = groups.length; j < m; ++j) { | |
for (var group = groups[j], i = 0, n = group.length, node; i < n; ++i) { | |
if ((node = group[i])) yield node | |
} | |
} | |
} | |
var root$2 = [null] | |
function Selection$1(groups, parents) { | |
this._groups = groups | |
this._parents = parents | |
} | |
function selection() { | |
return new Selection$1([[document.documentElement]], root$2) | |
} | |
function selection_selection() { | |
return this | |
} | |
Selection$1.prototype = selection.prototype = { | |
constructor: Selection$1, | |
select: selection_select, | |
selectAll: selection_selectAll, | |
selectChild: selection_selectChild, | |
selectChildren: selection_selectChildren, | |
filter: selection_filter, | |
data: selection_data, | |
enter: selection_enter, | |
exit: selection_exit, | |
join: selection_join, | |
merge: selection_merge, | |
selection: selection_selection, | |
order: selection_order, | |
sort: selection_sort, | |
call: selection_call, | |
nodes: selection_nodes, | |
node: selection_node, | |
size: selection_size, | |
empty: selection_empty, | |
each: selection_each, | |
attr: selection_attr, | |
style: selection_style, | |
property: selection_property, | |
classed: selection_classed, | |
text: selection_text, | |
html: selection_html, | |
raise: selection_raise, | |
lower: selection_lower, | |
append: selection_append, | |
insert: selection_insert, | |
remove: selection_remove, | |
clone: selection_clone, | |
datum: selection_datum, | |
on: selection_on, | |
dispatch: selection_dispatch, | |
[Symbol.iterator]: selection_iterator, | |
} | |
function select(selector) { | |
return typeof selector === 'string' | |
? new Selection$1( | |
[[document.querySelector(selector)]], | |
[document.documentElement], | |
) | |
: new Selection$1([[selector]], root$2) | |
} | |
function create$1(name) { | |
return select(creator(name).call(document.documentElement)) | |
} | |
function sourceEvent(event) { | |
let sourceEvent | |
while ((sourceEvent = event.sourceEvent)) event = sourceEvent | |
return event | |
} | |
function pointer(event, node) { | |
event = sourceEvent(event) | |
if (node === undefined) node = event.currentTarget | |
if (node) { | |
var svg = node.ownerSVGElement || node | |
if (svg.createSVGPoint) { | |
var point = svg.createSVGPoint() | |
;(point.x = event.clientX), (point.y = event.clientY) | |
point = point.matrixTransform(node.getScreenCTM().inverse()) | |
return [point.x, point.y] | |
} | |
if (node.getBoundingClientRect) { | |
var rect = node.getBoundingClientRect() | |
return [ | |
event.clientX - rect.left - node.clientLeft, | |
event.clientY - rect.top - node.clientTop, | |
] | |
} | |
} | |
return [event.pageX, event.pageY] | |
} | |
// These are typically used in conjunction with noevent to ensure that we can | |
// preventDefault on the event. | |
const nonpassive = { passive: false } | |
const nonpassivecapture = { capture: true, passive: false } | |
function nopropagation(event) { | |
event.stopImmediatePropagation() | |
} | |
function noevent(event) { | |
event.preventDefault() | |
event.stopImmediatePropagation() | |
} | |
function dragDisable(view) { | |
var root = view.document.documentElement, | |
selection = select(view).on('dragstart.drag', noevent, nonpassivecapture) | |
if ('onselectstart' in root) { | |
selection.on('selectstart.drag', noevent, nonpassivecapture) | |
} else { | |
root.__noselect = root.style.MozUserSelect | |
root.style.MozUserSelect = 'none' | |
} | |
} | |
function yesdrag(view, noclick) { | |
var root = view.document.documentElement, | |
selection = select(view).on('dragstart.drag', null) | |
if (noclick) { | |
selection.on('click.drag', noevent, nonpassivecapture) | |
setTimeout(function () { | |
selection.on('click.drag', null) | |
}, 0) | |
} | |
if ('onselectstart' in root) { | |
selection.on('selectstart.drag', null) | |
} else { | |
root.style.MozUserSelect = root.__noselect | |
delete root.__noselect | |
} | |
} | |
var constant$2 = (x) => () => x | |
function DragEvent( | |
type, | |
{ sourceEvent, subject, target, identifier, active, x, y, dx, dy, dispatch }, | |
) { | |
Object.defineProperties(this, { | |
type: { value: type, enumerable: true, configurable: true }, | |
sourceEvent: { value: sourceEvent, enumerable: true, configurable: true }, | |
subject: { value: subject, enumerable: true, configurable: true }, | |
target: { value: target, enumerable: true, configurable: true }, | |
identifier: { value: identifier, enumerable: true, configurable: true }, | |
active: { value: active, enumerable: true, configurable: true }, | |
x: { value: x, enumerable: true, configurable: true }, | |
y: { value: y, enumerable: true, configurable: true }, | |
dx: { value: dx, enumerable: true, configurable: true }, | |
dy: { value: dy, enumerable: true, configurable: true }, | |
_: { value: dispatch }, | |
}) | |
} | |
DragEvent.prototype.on = function () { | |
var value = this._.on.apply(this._, arguments) | |
return value === this._ ? this : value | |
} | |
// Ignore right-click, since that should open the context menu. | |
function defaultFilter(event) { | |
return !event.ctrlKey && !event.button | |
} | |
function defaultContainer() { | |
return this.parentNode | |
} | |
function defaultSubject(event, d) { | |
return d == null ? { x: event.x, y: event.y } : d | |
} | |
function defaultTouchable() { | |
return navigator.maxTouchPoints || 'ontouchstart' in this | |
} | |
function drag() { | |
var filter = defaultFilter, | |
container = defaultContainer, | |
subject = defaultSubject, | |
touchable = defaultTouchable, | |
gestures = {}, | |
listeners = dispatch('start', 'drag', 'end'), | |
active = 0, | |
mousedownx, | |
mousedowny, | |
mousemoving, | |
touchending, | |
clickDistance2 = 0 | |
function drag(selection) { | |
selection | |
.on('mousedown.drag', mousedowned) | |
.filter(touchable) | |
.on('touchstart.drag', touchstarted) | |
.on('touchmove.drag', touchmoved, nonpassive) | |
.on('touchend.drag touchcancel.drag', touchended) | |
.style('touch-action', 'none') | |
.style('-webkit-tap-highlight-color', 'rgba(0,0,0,0)') | |
} | |
function mousedowned(event, d) { | |
if (touchending || !filter.call(this, event, d)) return | |
var gesture = beforestart( | |
this, | |
container.call(this, event, d), | |
event, | |
d, | |
'mouse', | |
) | |
if (!gesture) return | |
select(event.view) | |
.on('mousemove.drag', mousemoved, nonpassivecapture) | |
.on('mouseup.drag', mouseupped, nonpassivecapture) | |
dragDisable(event.view) | |
nopropagation(event) | |
mousemoving = false | |
mousedownx = event.clientX | |
mousedowny = event.clientY | |
gesture('start', event) | |
} | |
function mousemoved(event) { | |
noevent(event) | |
if (!mousemoving) { | |
var dx = event.clientX - mousedownx, | |
dy = event.clientY - mousedowny | |
mousemoving = dx * dx + dy * dy > clickDistance2 | |
} | |
gestures.mouse('drag', event) | |
} | |
function mouseupped(event) { | |
select(event.view).on('mousemove.drag mouseup.drag', null) | |
yesdrag(event.view, mousemoving) | |
noevent(event) | |
gestures.mouse('end', event) | |
} | |
function touchstarted(event, d) { | |
if (!filter.call(this, event, d)) return | |
var touches = event.changedTouches, | |
c = container.call(this, event, d), | |
n = touches.length, | |
i, | |
gesture | |
for (i = 0; i < n; ++i) { | |
if ( | |
(gesture = beforestart( | |
this, | |
c, | |
event, | |
d, | |
touches[i].identifier, | |
touches[i], | |
)) | |
) { | |
nopropagation(event) | |
gesture('start', event, touches[i]) | |
} | |
} | |
} | |
function touchmoved(event) { | |
var touches = event.changedTouches, | |
n = touches.length, | |
i, | |
gesture | |
for (i = 0; i < n; ++i) { | |
if ((gesture = gestures[touches[i].identifier])) { | |
noevent(event) | |
gesture('drag', event, touches[i]) | |
} | |
} | |
} | |
function touchended(event) { | |
var touches = event.changedTouches, | |
n = touches.length, | |
i, | |
gesture | |
if (touchending) clearTimeout(touchending) | |
touchending = setTimeout(function () { | |
touchending = null | |
}, 500) // Ghost clicks are delayed! | |
for (i = 0; i < n; ++i) { | |
if ((gesture = gestures[touches[i].identifier])) { | |
nopropagation(event) | |
gesture('end', event, touches[i]) | |
} | |
} | |
} | |
function beforestart(that, container, event, d, identifier, touch) { | |
var dispatch = listeners.copy(), | |
p = pointer(touch || event, container), | |
dx, | |
dy, | |
s | |
if ( | |
(s = subject.call( | |
that, | |
new DragEvent('beforestart', { | |
sourceEvent: event, | |
target: drag, | |
identifier, | |
active, | |
x: p[0], | |
y: p[1], | |
dx: 0, | |
dy: 0, | |
dispatch, | |
}), | |
d, | |
)) == null | |
) | |
return | |
dx = s.x - p[0] || 0 | |
dy = s.y - p[1] || 0 | |
return function gesture(type, event, touch) { | |
var p0 = p, | |
n | |
switch (type) { | |
case 'start': | |
;(gestures[identifier] = gesture), (n = active++) | |
break | |
case 'end': | |
delete gestures[identifier], --active // falls through | |
case 'drag': | |
;(p = pointer(touch || event, container)), (n = active) | |
break | |
} | |
dispatch.call( | |
type, | |
that, | |
new DragEvent(type, { | |
sourceEvent: event, | |
subject: s, | |
target: drag, | |
identifier, | |
active: n, | |
x: p[0] + dx, | |
y: p[1] + dy, | |
dx: p[0] - p0[0], | |
dy: p[1] - p0[1], | |
dispatch, | |
}), | |
d, | |
) | |
} | |
} | |
drag.filter = function (_) { | |
return arguments.length | |
? ((filter = typeof _ === 'function' ? _ : constant$2(!!_)), drag) | |
: filter | |
} | |
drag.container = function (_) { | |
return arguments.length | |
? ((container = typeof _ === 'function' ? _ : constant$2(_)), drag) | |
: container | |
} | |
drag.subject = function (_) { | |
return arguments.length | |
? ((subject = typeof _ === 'function' ? _ : constant$2(_)), drag) | |
: subject | |
} | |
drag.touchable = function (_) { | |
return arguments.length | |
? ((touchable = typeof _ === 'function' ? _ : constant$2(!!_)), drag) | |
: touchable | |
} | |
drag.on = function () { | |
var value = listeners.on.apply(listeners, arguments) | |
return value === listeners ? drag : value | |
} | |
drag.clickDistance = function (_) { | |
return arguments.length | |
? ((clickDistance2 = (_ = +_) * _), drag) | |
: Math.sqrt(clickDistance2) | |
} | |
return drag | |
} | |
function define(constructor, factory, prototype) { | |
constructor.prototype = factory.prototype = prototype | |
prototype.constructor = constructor | |
} | |
function extend(parent, definition) { | |
var prototype = Object.create(parent.prototype) | |
for (var key in definition) prototype[key] = definition[key] | |
return prototype | |
} | |
function Color() {} | |
var darker = 0.7 | |
var brighter = 1 / darker | |
var reI = '\\s*([+-]?\\d+)\\s*', | |
reN = '\\s*([+-]?(?:\\d*\\.)?\\d+(?:[eE][+-]?\\d+)?)\\s*', | |
reP = '\\s*([+-]?(?:\\d*\\.)?\\d+(?:[eE][+-]?\\d+)?)%\\s*', | |
reHex = /^#([0-9a-f]{3,8})$/, | |
reRgbInteger = new RegExp(`^rgb\\(${reI},${reI},${reI}\\)$`), | |
reRgbPercent = new RegExp(`^rgb\\(${reP},${reP},${reP}\\)$`), | |
reRgbaInteger = new RegExp(`^rgba\\(${reI},${reI},${reI},${reN}\\)$`), | |
reRgbaPercent = new RegExp(`^rgba\\(${reP},${reP},${reP},${reN}\\)$`), | |
reHslPercent = new RegExp(`^hsl\\(${reN},${reP},${reP}\\)$`), | |
reHslaPercent = new RegExp(`^hsla\\(${reN},${reP},${reP},${reN}\\)$`) | |
var named = { | |
aliceblue: 0xf0f8ff, | |
antiquewhite: 0xfaebd7, | |
aqua: 0x00ffff, | |
aquamarine: 0x7fffd4, | |
azure: 0xf0ffff, | |
beige: 0xf5f5dc, | |
bisque: 0xffe4c4, | |
black: 0x000000, | |
blanchedalmond: 0xffebcd, | |
blue: 0x0000ff, | |
blueviolet: 0x8a2be2, | |
brown: 0xa52a2a, | |
burlywood: 0xdeb887, | |
cadetblue: 0x5f9ea0, | |
chartreuse: 0x7fff00, | |
chocolate: 0xd2691e, | |
coral: 0xff7f50, | |
cornflowerblue: 0x6495ed, | |
cornsilk: 0xfff8dc, | |
crimson: 0xdc143c, | |
cyan: 0x00ffff, | |
darkblue: 0x00008b, | |
darkcyan: 0x008b8b, | |
darkgoldenrod: 0xb8860b, | |
darkgray: 0xa9a9a9, | |
darkgreen: 0x006400, | |
darkgrey: 0xa9a9a9, | |
darkkhaki: 0xbdb76b, | |
darkmagenta: 0x8b008b, | |
darkolivegreen: 0x556b2f, | |
darkorange: 0xff8c00, | |
darkorchid: 0x9932cc, | |
darkred: 0x8b0000, | |
darksalmon: 0xe9967a, | |
darkseagreen: 0x8fbc8f, | |
darkslateblue: 0x483d8b, | |
darkslategray: 0x2f4f4f, | |
darkslategrey: 0x2f4f4f, | |
darkturquoise: 0x00ced1, | |
darkviolet: 0x9400d3, | |
deeppink: 0xff1493, | |
deepskyblue: 0x00bfff, | |
dimgray: 0x696969, | |
dimgrey: 0x696969, | |
dodgerblue: 0x1e90ff, | |
firebrick: 0xb22222, | |
floralwhite: 0xfffaf0, | |
forestgreen: 0x228b22, | |
fuchsia: 0xff00ff, | |
gainsboro: 0xdcdcdc, | |
ghostwhite: 0xf8f8ff, | |
gold: 0xffd700, | |
goldenrod: 0xdaa520, | |
gray: 0x808080, | |
green: 0x008000, | |
greenyellow: 0xadff2f, | |
grey: 0x808080, | |
honeydew: 0xf0fff0, | |
hotpink: 0xff69b4, | |
indianred: 0xcd5c5c, | |
indigo: 0x4b0082, | |
ivory: 0xfffff0, | |
khaki: 0xf0e68c, | |
lavender: 0xe6e6fa, | |
lavenderblush: 0xfff0f5, | |
lawngreen: 0x7cfc00, | |
lemonchiffon: 0xfffacd, | |
lightblue: 0xadd8e6, | |
lightcoral: 0xf08080, | |
lightcyan: 0xe0ffff, | |
lightgoldenrodyellow: 0xfafad2, | |
lightgray: 0xd3d3d3, | |
lightgreen: 0x90ee90, | |
lightgrey: 0xd3d3d3, | |
lightpink: 0xffb6c1, | |
lightsalmon: 0xffa07a, | |
lightseagreen: 0x20b2aa, | |
lightskyblue: 0x87cefa, | |
lightslategray: 0x778899, | |
lightslategrey: 0x778899, | |
lightsteelblue: 0xb0c4de, | |
lightyellow: 0xffffe0, | |
lime: 0x00ff00, | |
limegreen: 0x32cd32, | |
linen: 0xfaf0e6, | |
magenta: 0xff00ff, | |
maroon: 0x800000, | |
mediumaquamarine: 0x66cdaa, | |
mediumblue: 0x0000cd, | |
mediumorchid: 0xba55d3, | |
mediumpurple: 0x9370db, | |
mediumseagreen: 0x3cb371, | |
mediumslateblue: 0x7b68ee, | |
mediumspringgreen: 0x00fa9a, | |
mediumturquoise: 0x48d1cc, | |
mediumvioletred: 0xc71585, | |
midnightblue: 0x191970, | |
mintcream: 0xf5fffa, | |
mistyrose: 0xffe4e1, | |
moccasin: 0xffe4b5, | |
navajowhite: 0xffdead, | |
navy: 0x000080, | |
oldlace: 0xfdf5e6, | |
olive: 0x808000, | |
olivedrab: 0x6b8e23, | |
orange: 0xffa500, | |
orangered: 0xff4500, | |
orchid: 0xda70d6, | |
palegoldenrod: 0xeee8aa, | |
palegreen: 0x98fb98, | |
paleturquoise: 0xafeeee, | |
palevioletred: 0xdb7093, | |
papayawhip: 0xffefd5, | |
peachpuff: 0xffdab9, | |
peru: 0xcd853f, | |
pink: 0xffc0cb, | |
plum: 0xdda0dd, | |
powderblue: 0xb0e0e6, | |
purple: 0x800080, | |
rebeccapurple: 0x663399, | |
red: 0xff0000, | |
rosybrown: 0xbc8f8f, | |
royalblue: 0x4169e1, | |
saddlebrown: 0x8b4513, | |
salmon: 0xfa8072, | |
sandybrown: 0xf4a460, | |
seagreen: 0x2e8b57, | |
seashell: 0xfff5ee, | |
sienna: 0xa0522d, | |
silver: 0xc0c0c0, | |
skyblue: 0x87ceeb, | |
slateblue: 0x6a5acd, | |
slategray: 0x708090, | |
slategrey: 0x708090, | |
snow: 0xfffafa, | |
springgreen: 0x00ff7f, | |
steelblue: 0x4682b4, | |
tan: 0xd2b48c, | |
teal: 0x008080, | |
thistle: 0xd8bfd8, | |
tomato: 0xff6347, | |
turquoise: 0x40e0d0, | |
violet: 0xee82ee, | |
wheat: 0xf5deb3, | |
white: 0xffffff, | |
whitesmoke: 0xf5f5f5, | |
yellow: 0xffff00, | |
yellowgreen: 0x9acd32, | |
} | |
define(Color, color, { | |
copy(channels) { | |
return Object.assign(new this.constructor(), this, channels) | |
}, | |
displayable() { | |
return this.rgb().displayable() | |
}, | |
hex: color_formatHex, // Deprecated! Use color.formatHex. | |
formatHex: color_formatHex, | |
formatHex8: color_formatHex8, | |
formatHsl: color_formatHsl, | |
formatRgb: color_formatRgb, | |
toString: color_formatRgb, | |
}) | |
function color_formatHex() { | |
return this.rgb().formatHex() | |
} | |
function color_formatHex8() { | |
return this.rgb().formatHex8() | |
} | |
function color_formatHsl() { | |
return hslConvert(this).formatHsl() | |
} | |
function color_formatRgb() { | |
return this.rgb().formatRgb() | |
} | |
function color(format) { | |
var m, l | |
format = (format + '').trim().toLowerCase() | |
return (m = reHex.exec(format)) | |
? ((l = m[1].length), | |
(m = parseInt(m[1], 16)), | |
l === 6 | |
? rgbn(m) // #ff0000 | |
: l === 3 | |
? new Rgb( | |
((m >> 8) & 0xf) | ((m >> 4) & 0xf0), | |
((m >> 4) & 0xf) | (m & 0xf0), | |
((m & 0xf) << 4) | (m & 0xf), | |
1, | |
) // #f00 | |
: l === 8 | |
? rgba( | |
(m >> 24) & 0xff, | |
(m >> 16) & 0xff, | |
(m >> 8) & 0xff, | |
(m & 0xff) / 0xff, | |
) // #ff000000 | |
: l === 4 | |
? rgba( | |
((m >> 12) & 0xf) | ((m >> 8) & 0xf0), | |
((m >> 8) & 0xf) | ((m >> 4) & 0xf0), | |
((m >> 4) & 0xf) | (m & 0xf0), | |
(((m & 0xf) << 4) | (m & 0xf)) / 0xff, | |
) // #f000 | |
: null) // invalid hex | |
: (m = reRgbInteger.exec(format)) | |
? new Rgb(m[1], m[2], m[3], 1) // rgb(255, 0, 0) | |
: (m = reRgbPercent.exec(format)) | |
? new Rgb((m[1] * 255) / 100, (m[2] * 255) / 100, (m[3] * 255) / 100, 1) // rgb(100%, 0%, 0%) | |
: (m = reRgbaInteger.exec(format)) | |
? rgba(m[1], m[2], m[3], m[4]) // rgba(255, 0, 0, 1) | |
: (m = reRgbaPercent.exec(format)) | |
? rgba( | |
(m[1] * 255) / 100, | |
(m[2] * 255) / 100, | |
(m[3] * 255) / 100, | |
m[4], | |
) // rgb(100%, 0%, 0%, 1) | |
: (m = reHslPercent.exec(format)) | |
? hsla(m[1], m[2] / 100, m[3] / 100, 1) // hsl(120, 50%, 50%) | |
: (m = reHslaPercent.exec(format)) | |
? hsla(m[1], m[2] / 100, m[3] / 100, m[4]) // hsla(120, 50%, 50%, 1) | |
: named.hasOwnProperty(format) | |
? rgbn(named[format]) // eslint-disable-line no-prototype-builtins | |
: format === 'transparent' | |
? new Rgb(NaN, NaN, NaN, 0) | |
: null | |
} | |
function rgbn(n) { | |
return new Rgb((n >> 16) & 0xff, (n >> 8) & 0xff, n & 0xff, 1) | |
} | |
function rgba(r, g, b, a) { | |
if (a <= 0) r = g = b = NaN | |
return new Rgb(r, g, b, a) | |
} | |
function rgbConvert(o) { | |
if (!(o instanceof Color)) o = color(o) | |
if (!o) return new Rgb() | |
o = o.rgb() | |
return new Rgb(o.r, o.g, o.b, o.opacity) | |
} | |
function rgb(r, g, b, opacity) { | |
return arguments.length === 1 | |
? rgbConvert(r) | |
: new Rgb(r, g, b, opacity == null ? 1 : opacity) | |
} | |
function Rgb(r, g, b, opacity) { | |
this.r = +r | |
this.g = +g | |
this.b = +b | |
this.opacity = +opacity | |
} | |
define( | |
Rgb, | |
rgb, | |
extend(Color, { | |
brighter(k) { | |
k = k == null ? brighter : Math.pow(brighter, k) | |
return new Rgb(this.r * k, this.g * k, this.b * k, this.opacity) | |
}, | |
darker(k) { | |
k = k == null ? darker : Math.pow(darker, k) | |
return new Rgb(this.r * k, this.g * k, this.b * k, this.opacity) | |
}, | |
rgb() { | |
return this | |
}, | |
clamp() { | |
return new Rgb( | |
clampi(this.r), | |
clampi(this.g), | |
clampi(this.b), | |
clampa(this.opacity), | |
) | |
}, | |
displayable() { | |
return ( | |
-0.5 <= this.r && | |
this.r < 255.5 && | |
-0.5 <= this.g && | |
this.g < 255.5 && | |
-0.5 <= this.b && | |
this.b < 255.5 && | |
0 <= this.opacity && | |
this.opacity <= 1 | |
) | |
}, | |
hex: rgb_formatHex, // Deprecated! Use color.formatHex. | |
formatHex: rgb_formatHex, | |
formatHex8: rgb_formatHex8, | |
formatRgb: rgb_formatRgb, | |
toString: rgb_formatRgb, | |
}), | |
) | |
function rgb_formatHex() { | |
return `#${hex(this.r)}${hex(this.g)}${hex(this.b)}` | |
} | |
function rgb_formatHex8() { | |
return `#${hex(this.r)}${hex(this.g)}${hex(this.b)}${hex((isNaN(this.opacity) ? 1 : this.opacity) * 255)}` | |
} | |
function rgb_formatRgb() { | |
const a = clampa(this.opacity) | |
return `${a === 1 ? 'rgb(' : 'rgba('}${clampi(this.r)}, ${clampi(this.g)}, ${clampi(this.b)}${a === 1 ? ')' : `, ${a})`}` | |
} | |
function clampa(opacity) { | |
return isNaN(opacity) ? 1 : Math.max(0, Math.min(1, opacity)) | |
} | |
function clampi(value) { | |
return Math.max(0, Math.min(255, Math.round(value) || 0)) | |
} | |
function hex(value) { | |
value = clampi(value) | |
return (value < 16 ? '0' : '') + value.toString(16) | |
} | |
function hsla(h, s, l, a) { | |
if (a <= 0) h = s = l = NaN | |
else if (l <= 0 || l >= 1) h = s = NaN | |
else if (s <= 0) h = NaN | |
return new Hsl(h, s, l, a) | |
} | |
function hslConvert(o) { | |
if (o instanceof Hsl) return new Hsl(o.h, o.s, o.l, o.opacity) | |
if (!(o instanceof Color)) o = color(o) | |
if (!o) return new Hsl() | |
if (o instanceof Hsl) return o | |
o = o.rgb() | |
var r = o.r / 255, | |
g = o.g / 255, | |
b = o.b / 255, | |
min = Math.min(r, g, b), | |
max = Math.max(r, g, b), | |
h = NaN, | |
s = max - min, | |
l = (max + min) / 2 | |
if (s) { | |
if (r === max) h = (g - b) / s + (g < b) * 6 | |
else if (g === max) h = (b - r) / s + 2 | |
else h = (r - g) / s + 4 | |
s /= l < 0.5 ? max + min : 2 - max - min | |
h *= 60 | |
} else { | |
s = l > 0 && l < 1 ? 0 : h | |
} | |
return new Hsl(h, s, l, o.opacity) | |
} | |
function hsl(h, s, l, opacity) { | |
return arguments.length === 1 | |
? hslConvert(h) | |
: new Hsl(h, s, l, opacity == null ? 1 : opacity) | |
} | |
function Hsl(h, s, l, opacity) { | |
this.h = +h | |
this.s = +s | |
this.l = +l | |
this.opacity = +opacity | |
} | |
define( | |
Hsl, | |
hsl, | |
extend(Color, { | |
brighter(k) { | |
k = k == null ? brighter : Math.pow(brighter, k) | |
return new Hsl(this.h, this.s, this.l * k, this.opacity) | |
}, | |
darker(k) { | |
k = k == null ? darker : Math.pow(darker, k) | |
return new Hsl(this.h, this.s, this.l * k, this.opacity) | |
}, | |
rgb() { | |
var h = (this.h % 360) + (this.h < 0) * 360, | |
s = isNaN(h) || isNaN(this.s) ? 0 : this.s, | |
l = this.l, | |
m2 = l + (l < 0.5 ? l : 1 - l) * s, | |
m1 = 2 * l - m2 | |
return new Rgb( | |
hsl2rgb(h >= 240 ? h - 240 : h + 120, m1, m2), | |
hsl2rgb(h, m1, m2), | |
hsl2rgb(h < 120 ? h + 240 : h - 120, m1, m2), | |
this.opacity, | |
) | |
}, | |
clamp() { | |
return new Hsl( | |
clamph(this.h), | |
clampt(this.s), | |
clampt(this.l), | |
clampa(this.opacity), | |
) | |
}, | |
displayable() { | |
return ( | |
((0 <= this.s && this.s <= 1) || isNaN(this.s)) && | |
0 <= this.l && | |
this.l <= 1 && | |
0 <= this.opacity && | |
this.opacity <= 1 | |
) | |
}, | |
formatHsl() { | |
const a = clampa(this.opacity) | |
return `${a === 1 ? 'hsl(' : 'hsla('}${clamph(this.h)}, ${clampt(this.s) * 100}%, ${clampt(this.l) * 100}%${a === 1 ? ')' : `, ${a})`}` | |
}, | |
}), | |
) | |
function clamph(value) { | |
value = (value || 0) % 360 | |
return value < 0 ? value + 360 : value | |
} | |
function clampt(value) { | |
return Math.max(0, Math.min(1, value || 0)) | |
} | |
/* From FvD 13.37, CSS Color Module Level 3 */ | |
function hsl2rgb(h, m1, m2) { | |
return ( | |
(h < 60 | |
? m1 + ((m2 - m1) * h) / 60 | |
: h < 180 | |
? m2 | |
: h < 240 | |
? m1 + ((m2 - m1) * (240 - h)) / 60 | |
: m1) * 255 | |
) | |
} | |
var constant$1 = (x) => () => x | |
function linear(a, d) { | |
return function (t) { | |
return a + t * d | |
} | |
} | |
function exponential(a, b, y) { | |
return ( | |
(a = Math.pow(a, y)), | |
(b = Math.pow(b, y) - a), | |
(y = 1 / y), | |
function (t) { | |
return Math.pow(a + t * b, y) | |
} | |
) | |
} | |
function gamma(y) { | |
return (y = +y) === 1 | |
? nogamma | |
: function (a, b) { | |
return b - a ? exponential(a, b, y) : constant$1(isNaN(a) ? b : a) | |
} | |
} | |
function nogamma(a, b) { | |
var d = b - a | |
return d ? linear(a, d) : constant$1(isNaN(a) ? b : a) | |
} | |
var interpolateRgb = (function rgbGamma(y) { | |
var color = gamma(y) | |
function rgb$1(start, end) { | |
var r = color((start = rgb(start)).r, (end = rgb(end)).r), | |
g = color(start.g, end.g), | |
b = color(start.b, end.b), | |
opacity = nogamma(start.opacity, end.opacity) | |
return function (t) { | |
start.r = r(t) | |
start.g = g(t) | |
start.b = b(t) | |
start.opacity = opacity(t) | |
return start + '' | |
} | |
} | |
rgb$1.gamma = rgbGamma | |
return rgb$1 | |
})(1) | |
function interpolateNumber(a, b) { | |
return ( | |
(a = +a), | |
(b = +b), | |
function (t) { | |
return a * (1 - t) + b * t | |
} | |
) | |
} | |
var reA = /[-+]?(?:\d+\.?\d*|\.?\d+)(?:[eE][-+]?\d+)?/g, | |
reB = new RegExp(reA.source, 'g') | |
function zero(b) { | |
return function () { | |
return b | |
} | |
} | |
function one(b) { | |
return function (t) { | |
return b(t) + '' | |
} | |
} | |
function interpolateString(a, b) { | |
var bi = (reA.lastIndex = reB.lastIndex = 0), // scan index for next number in b | |
am, // current match in a | |
bm, // current match in b | |
bs, // string preceding current number in b, if any | |
i = -1, // index in s | |
s = [], // string constants and placeholders | |
q = [] // number interpolators | |
// Coerce inputs to strings. | |
;(a = a + ''), (b = b + '') | |
// Interpolate pairs of numbers in a & b. | |
while ((am = reA.exec(a)) && (bm = reB.exec(b))) { | |
if ((bs = bm.index) > bi) { | |
// a string precedes the next number in b | |
bs = b.slice(bi, bs) | |
if (s[i]) | |
s[i] += bs // coalesce with previous string | |
else s[++i] = bs | |
} | |
if ((am = am[0]) === (bm = bm[0])) { | |
// numbers in a & b match | |
if (s[i]) | |
s[i] += bm // coalesce with previous string | |
else s[++i] = bm | |
} else { | |
// interpolate non-matching numbers | |
s[++i] = null | |
q.push({ i: i, x: interpolateNumber(am, bm) }) | |
} | |
bi = reB.lastIndex | |
} | |
// Add remains of b. | |
if (bi < b.length) { | |
bs = b.slice(bi) | |
if (s[i]) | |
s[i] += bs // coalesce with previous string | |
else s[++i] = bs | |
} | |
// Special optimization for only a single match. | |
// Otherwise, interpolate each of the numbers and rejoin the string. | |
return s.length < 2 | |
? q[0] | |
? one(q[0].x) | |
: zero(b) | |
: ((b = q.length), | |
function (t) { | |
for (var i = 0, o; i < b; ++i) s[(o = q[i]).i] = o.x(t) | |
return s.join('') | |
}) | |
} | |
var degrees = 180 / Math.PI | |
var identity$2 = { | |
translateX: 0, | |
translateY: 0, | |
rotate: 0, | |
skewX: 0, | |
scaleX: 1, | |
scaleY: 1, | |
} | |
function decompose(a, b, c, d, e, f) { | |
var scaleX, scaleY, skewX | |
if ((scaleX = Math.sqrt(a * a + b * b))) (a /= scaleX), (b /= scaleX) | |
if ((skewX = a * c + b * d)) (c -= a * skewX), (d -= b * skewX) | |
if ((scaleY = Math.sqrt(c * c + d * d))) | |
(c /= scaleY), (d /= scaleY), (skewX /= scaleY) | |
if (a * d < b * c) (a = -a), (b = -b), (skewX = -skewX), (scaleX = -scaleX) | |
return { | |
translateX: e, | |
translateY: f, | |
rotate: Math.atan2(b, a) * degrees, | |
skewX: Math.atan(skewX) * degrees, | |
scaleX: scaleX, | |
scaleY: scaleY, | |
} | |
} | |
var svgNode | |
/* eslint-disable no-undef */ | |
function parseCss(value) { | |
const m = new (typeof DOMMatrix === 'function' ? DOMMatrix : WebKitCSSMatrix)( | |
value + '', | |
) | |
return m.isIdentity ? identity$2 : decompose(m.a, m.b, m.c, m.d, m.e, m.f) | |
} | |
function parseSvg(value) { | |
if (value == null) return identity$2 | |
if (!svgNode) | |
svgNode = document.createElementNS('http://www.w3.org/2000/svg', 'g') | |
svgNode.setAttribute('transform', value) | |
if (!(value = svgNode.transform.baseVal.consolidate())) return identity$2 | |
value = value.matrix | |
return decompose(value.a, value.b, value.c, value.d, value.e, value.f) | |
} | |
function interpolateTransform(parse, pxComma, pxParen, degParen) { | |
function pop(s) { | |
return s.length ? s.pop() + ' ' : '' | |
} | |
function translate(xa, ya, xb, yb, s, q) { | |
if (xa !== xb || ya !== yb) { | |
var i = s.push('translate(', null, pxComma, null, pxParen) | |
q.push( | |
{ i: i - 4, x: interpolateNumber(xa, xb) }, | |
{ i: i - 2, x: interpolateNumber(ya, yb) }, | |
) | |
} else if (xb || yb) { | |
s.push('translate(' + xb + pxComma + yb + pxParen) | |
} | |
} | |
function rotate(a, b, s, q) { | |
if (a !== b) { | |
if (a - b > 180) b += 360 | |
else if (b - a > 180) a += 360 // shortest path | |
q.push({ | |
i: s.push(pop(s) + 'rotate(', null, degParen) - 2, | |
x: interpolateNumber(a, b), | |
}) | |
} else if (b) { | |
s.push(pop(s) + 'rotate(' + b + degParen) | |
} | |
} | |
function skewX(a, b, s, q) { | |
if (a !== b) { | |
q.push({ | |
i: s.push(pop(s) + 'skewX(', null, degParen) - 2, | |
x: interpolateNumber(a, b), | |
}) | |
} else if (b) { | |
s.push(pop(s) + 'skewX(' + b + degParen) | |
} | |
} | |
function scale(xa, ya, xb, yb, s, q) { | |
if (xa !== xb || ya !== yb) { | |
var i = s.push(pop(s) + 'scale(', null, ',', null, ')') | |
q.push( | |
{ i: i - 4, x: interpolateNumber(xa, xb) }, | |
{ i: i - 2, x: interpolateNumber(ya, yb) }, | |
) | |
} else if (xb !== 1 || yb !== 1) { | |
s.push(pop(s) + 'scale(' + xb + ',' + yb + ')') | |
} | |
} | |
return function (a, b) { | |
var s = [], // string constants and placeholders | |
q = [] // number interpolators | |
;(a = parse(a)), (b = parse(b)) | |
translate(a.translateX, a.translateY, b.translateX, b.translateY, s, q) | |
rotate(a.rotate, b.rotate, s, q) | |
skewX(a.skewX, b.skewX, s, q) | |
scale(a.scaleX, a.scaleY, b.scaleX, b.scaleY, s, q) | |
a = b = null // gc | |
return function (t) { | |
var i = -1, | |
n = q.length, | |
o | |
while (++i < n) s[(o = q[i]).i] = o.x(t) | |
return s.join('') | |
} | |
} | |
} | |
var interpolateTransformCss = interpolateTransform( | |
parseCss, | |
'px, ', | |
'px)', | |
'deg)', | |
) | |
var interpolateTransformSvg = interpolateTransform(parseSvg, ', ', ')', ')') | |
var frame = 0, // is an animation frame pending? | |
timeout$1 = 0, // is a timeout pending? | |
interval = 0, // are any timers active? | |
pokeDelay = 1000, // how frequently we check for clock skew | |
taskHead, | |
taskTail, | |
clockLast = 0, | |
clockNow = 0, | |
clockSkew = 0, | |
clock = | |
typeof performance === 'object' && performance.now ? performance : Date, | |
setFrame = | |
typeof window === 'object' && window.requestAnimationFrame | |
? window.requestAnimationFrame.bind(window) | |
: function (f) { | |
setTimeout(f, 17) | |
} | |
function now() { | |
return clockNow || (setFrame(clearNow), (clockNow = clock.now() + clockSkew)) | |
} | |
function clearNow() { | |
clockNow = 0 | |
} | |
function Timer() { | |
this._call = this._time = this._next = null | |
} | |
Timer.prototype = timer.prototype = { | |
constructor: Timer, | |
restart: function (callback, delay, time) { | |
if (typeof callback !== 'function') | |
throw new TypeError('callback is not a function') | |
time = (time == null ? now() : +time) + (delay == null ? 0 : +delay) | |
if (!this._next && taskTail !== this) { | |
if (taskTail) taskTail._next = this | |
else taskHead = this | |
taskTail = this | |
} | |
this._call = callback | |
this._time = time | |
sleep() | |
}, | |
stop: function () { | |
if (this._call) { | |
this._call = null | |
this._time = Infinity | |
sleep() | |
} | |
}, | |
} | |
function timer(callback, delay, time) { | |
var t = new Timer() | |
t.restart(callback, delay, time) | |
return t | |
} | |
function timerFlush() { | |
now() // Get the current time, if not already set. | |
++frame // Pretend we’ve set an alarm, if we haven’t already. | |
var t = taskHead, | |
e | |
while (t) { | |
if ((e = clockNow - t._time) >= 0) t._call.call(undefined, e) | |
t = t._next | |
} | |
--frame | |
} | |
function wake() { | |
clockNow = (clockLast = clock.now()) + clockSkew | |
frame = timeout$1 = 0 | |
try { | |
timerFlush() | |
} finally { | |
frame = 0 | |
nap() | |
clockNow = 0 | |
} | |
} | |
function poke() { | |
var now = clock.now(), | |
delay = now - clockLast | |
if (delay > pokeDelay) (clockSkew -= delay), (clockLast = now) | |
} | |
function nap() { | |
var t0, | |
t1 = taskHead, | |
t2, | |
time = Infinity | |
while (t1) { | |
if (t1._call) { | |
if (time > t1._time) time = t1._time | |
;(t0 = t1), (t1 = t1._next) | |
} else { | |
;(t2 = t1._next), (t1._next = null) | |
t1 = t0 ? (t0._next = t2) : (taskHead = t2) | |
} | |
} | |
taskTail = t0 | |
sleep(time) | |
} | |
function sleep(time) { | |
if (frame) return // Soonest alarm already set, or will be. | |
if (timeout$1) timeout$1 = clearTimeout(timeout$1) | |
var delay = time - clockNow // Strictly less than if we recomputed clockNow. | |
if (delay > 24) { | |
if (time < Infinity) | |
timeout$1 = setTimeout(wake, time - clock.now() - clockSkew) | |
if (interval) interval = clearInterval(interval) | |
} else { | |
if (!interval) | |
(clockLast = clock.now()), (interval = setInterval(poke, pokeDelay)) | |
;(frame = 1), setFrame(wake) | |
} | |
} | |
function timeout(callback, delay, time) { | |
var t = new Timer() | |
delay = delay == null ? 0 : +delay | |
t.restart( | |
(elapsed) => { | |
t.stop() | |
callback(elapsed + delay) | |
}, | |
delay, | |
time, | |
) | |
return t | |
} | |
var emptyOn = dispatch('start', 'end', 'cancel', 'interrupt') | |
var emptyTween = [] | |
var CREATED = 0 | |
var SCHEDULED = 1 | |
var STARTING = 2 | |
var STARTED = 3 | |
var RUNNING = 4 | |
var ENDING = 5 | |
var ENDED = 6 | |
function schedule(node, name, id, index, group, timing) { | |
var schedules = node.__transition | |
if (!schedules) node.__transition = {} | |
else if (id in schedules) return | |
create(node, id, { | |
name: name, | |
index: index, // For context during callback. | |
group: group, // For context during callback. | |
on: emptyOn, | |
tween: emptyTween, | |
time: timing.time, | |
delay: timing.delay, | |
duration: timing.duration, | |
ease: timing.ease, | |
timer: null, | |
state: CREATED, | |
}) | |
} | |
function init(node, id) { | |
var schedule = get(node, id) | |
if (schedule.state > CREATED) throw new Error('too late; already scheduled') | |
return schedule | |
} | |
function set(node, id) { | |
var schedule = get(node, id) | |
if (schedule.state > STARTED) throw new Error('too late; already running') | |
return schedule | |
} | |
function get(node, id) { | |
var schedule = node.__transition | |
if (!schedule || !(schedule = schedule[id])) | |
throw new Error('transition not found') | |
return schedule | |
} | |
function create(node, id, self) { | |
var schedules = node.__transition, | |
tween | |
// Initialize the self timer when the transition is created. | |
// Note the actual delay is not known until the first callback! | |
schedules[id] = self | |
self.timer = timer(schedule, 0, self.time) | |
function schedule(elapsed) { | |
self.state = SCHEDULED | |
self.timer.restart(start, self.delay, self.time) | |
// If the elapsed delay is less than our first sleep, start immediately. | |
if (self.delay <= elapsed) start(elapsed - self.delay) | |
} | |
function start(elapsed) { | |
var i, j, n, o | |
// If the state is not SCHEDULED, then we previously errored on start. | |
if (self.state !== SCHEDULED) return stop() | |
for (i in schedules) { | |
o = schedules[i] | |
if (o.name !== self.name) continue | |
// While this element already has a starting transition during this frame, | |
// defer starting an interrupting transition until that transition has a | |
// chance to tick (and possibly end); see d3/d3-transition#54! | |
if (o.state === STARTED) return timeout(start) | |
// Interrupt the active transition, if any. | |
if (o.state === RUNNING) { | |
o.state = ENDED | |
o.timer.stop() | |
o.on.call('interrupt', node, node.__data__, o.index, o.group) | |
delete schedules[i] | |
} | |
// Cancel any pre-empted transitions. | |
else if (+i < id) { | |
o.state = ENDED | |
o.timer.stop() | |
o.on.call('cancel', node, node.__data__, o.index, o.group) | |
delete schedules[i] | |
} | |
} | |
// Defer the first tick to end of the current frame; see d3/d3#1576. | |
// Note the transition may be canceled after start and before the first tick! | |
// Note this must be scheduled before the start event; see d3/d3-transition#16! | |
// Assuming this is successful, subsequent callbacks go straight to tick. | |
timeout(function () { | |
if (self.state === STARTED) { | |
self.state = RUNNING | |
self.timer.restart(tick, self.delay, self.time) | |
tick(elapsed) | |
} | |
}) | |
// Dispatch the start event. | |
// Note this must be done before the tween are initialized. | |
self.state = STARTING | |
self.on.call('start', node, node.__data__, self.index, self.group) | |
if (self.state !== STARTING) return // interrupted | |
self.state = STARTED | |
// Initialize the tween, deleting null tween. | |
tween = new Array((n = self.tween.length)) | |
for (i = 0, j = -1; i < n; ++i) { | |
if ( | |
(o = self.tween[i].value.call( | |
node, | |
node.__data__, | |
self.index, | |
self.group, | |
)) | |
) { | |
tween[++j] = o | |
} | |
} | |
tween.length = j + 1 | |
} | |
function tick(elapsed) { | |
var t = | |
elapsed < self.duration | |
? self.ease.call(null, elapsed / self.duration) | |
: (self.timer.restart(stop), (self.state = ENDING), 1), | |
i = -1, | |
n = tween.length | |
while (++i < n) { | |
tween[i].call(node, t) | |
} | |
// Dispatch the end event. | |
if (self.state === ENDING) { | |
self.on.call('end', node, node.__data__, self.index, self.group) | |
stop() | |
} | |
} | |
function stop() { | |
self.state = ENDED | |
self.timer.stop() | |
delete schedules[id] | |
for (var i in schedules) return // eslint-disable-line no-unused-vars | |
delete node.__transition | |
} | |
} | |
function interrupt(node, name) { | |
var schedules = node.__transition, | |
schedule, | |
active, | |
empty = true, | |
i | |
if (!schedules) return | |
name = name == null ? null : name + '' | |
for (i in schedules) { | |
if ((schedule = schedules[i]).name !== name) { | |
empty = false | |
continue | |
} | |
active = schedule.state > STARTING && schedule.state < ENDING | |
schedule.state = ENDED | |
schedule.timer.stop() | |
schedule.on.call( | |
active ? 'interrupt' : 'cancel', | |
node, | |
node.__data__, | |
schedule.index, | |
schedule.group, | |
) | |
delete schedules[i] | |
} | |
if (empty) delete node.__transition | |
} | |
function selection_interrupt(name) { | |
return this.each(function () { | |
interrupt(this, name) | |
}) | |
} | |
function tweenRemove(id, name) { | |
var tween0, tween1 | |
return function () { | |
var schedule = set(this, id), | |
tween = schedule.tween | |
// If this node shared tween with the previous node, | |
// just assign the updated shared tween and we’re done! | |
// Otherwise, copy-on-write. | |
if (tween !== tween0) { | |
tween1 = tween0 = tween | |
for (var i = 0, n = tween1.length; i < n; ++i) { | |
if (tween1[i].name === name) { | |
tween1 = tween1.slice() | |
tween1.splice(i, 1) | |
break | |
} | |
} | |
} | |
schedule.tween = tween1 | |
} | |
} | |
function tweenFunction(id, name, value) { | |
var tween0, tween1 | |
if (typeof value !== 'function') throw new Error() | |
return function () { | |
var schedule = set(this, id), | |
tween = schedule.tween | |
// If this node shared tween with the previous node, | |
// just assign the updated shared tween and we’re done! | |
// Otherwise, copy-on-write. | |
if (tween !== tween0) { | |
tween1 = (tween0 = tween).slice() | |
for ( | |
var t = { name: name, value: value }, i = 0, n = tween1.length; | |
i < n; | |
++i | |
) { | |
if (tween1[i].name === name) { | |
tween1[i] = t | |
break | |
} | |
} | |
if (i === n) tween1.push(t) | |
} | |
schedule.tween = tween1 | |
} | |
} | |
function transition_tween(name, value) { | |
var id = this._id | |
name += '' | |
if (arguments.length < 2) { | |
var tween = get(this.node(), id).tween | |
for (var i = 0, n = tween.length, t; i < n; ++i) { | |
if ((t = tween[i]).name === name) { | |
return t.value | |
} | |
} | |
return null | |
} | |
return this.each( | |
(value == null ? tweenRemove : tweenFunction)(id, name, value), | |
) | |
} | |
function tweenValue(transition, name, value) { | |
var id = transition._id | |
transition.each(function () { | |
var schedule = set(this, id) | |
;(schedule.value || (schedule.value = {}))[name] = value.apply( | |
this, | |
arguments, | |
) | |
}) | |
return function (node) { | |
return get(node, id).value[name] | |
} | |
} | |
function interpolate(a, b) { | |
var c | |
return ( | |
typeof b === 'number' | |
? interpolateNumber | |
: b instanceof color | |
? interpolateRgb | |
: (c = color(b)) | |
? ((b = c), interpolateRgb) | |
: interpolateString | |
)(a, b) | |
} | |
function attrRemove(name) { | |
return function () { | |
this.removeAttribute(name) | |
} | |
} | |
function attrRemoveNS(fullname) { | |
return function () { | |
this.removeAttributeNS(fullname.space, fullname.local) | |
} | |
} | |
function attrConstant(name, interpolate, value1) { | |
var string00, | |
string1 = value1 + '', | |
interpolate0 | |
return function () { | |
var string0 = this.getAttribute(name) | |
return string0 === string1 | |
? null | |
: string0 === string00 | |
? interpolate0 | |
: (interpolate0 = interpolate((string00 = string0), value1)) | |
} | |
} | |
function attrConstantNS(fullname, interpolate, value1) { | |
var string00, | |
string1 = value1 + '', | |
interpolate0 | |
return function () { | |
var string0 = this.getAttributeNS(fullname.space, fullname.local) | |
return string0 === string1 | |
? null | |
: string0 === string00 | |
? interpolate0 | |
: (interpolate0 = interpolate((string00 = string0), value1)) | |
} | |
} | |
function attrFunction(name, interpolate, value) { | |
var string00, string10, interpolate0 | |
return function () { | |
var string0, | |
value1 = value(this), | |
string1 | |
if (value1 == null) return void this.removeAttribute(name) | |
string0 = this.getAttribute(name) | |
string1 = value1 + '' | |
return string0 === string1 | |
? null | |
: string0 === string00 && string1 === string10 | |
? interpolate0 | |
: ((string10 = string1), | |
(interpolate0 = interpolate((string00 = string0), value1))) | |
} | |
} | |
function attrFunctionNS(fullname, interpolate, value) { | |
var string00, string10, interpolate0 | |
return function () { | |
var string0, | |
value1 = value(this), | |
string1 | |
if (value1 == null) | |
return void this.removeAttributeNS(fullname.space, fullname.local) | |
string0 = this.getAttributeNS(fullname.space, fullname.local) | |
string1 = value1 + '' | |
return string0 === string1 | |
? null | |
: string0 === string00 && string1 === string10 | |
? interpolate0 | |
: ((string10 = string1), | |
(interpolate0 = interpolate((string00 = string0), value1))) | |
} | |
} | |
function transition_attr(name, value) { | |
var fullname = namespace(name), | |
i = fullname === 'transform' ? interpolateTransformSvg : interpolate | |
return this.attrTween( | |
name, | |
typeof value === 'function' | |
? (fullname.local ? attrFunctionNS : attrFunction)( | |
fullname, | |
i, | |
tweenValue(this, 'attr.' + name, value), | |
) | |
: value == null | |
? (fullname.local ? attrRemoveNS : attrRemove)(fullname) | |
: (fullname.local ? attrConstantNS : attrConstant)(fullname, i, value), | |
) | |
} | |
function attrInterpolate(name, i) { | |
return function (t) { | |
this.setAttribute(name, i.call(this, t)) | |
} | |
} | |
function attrInterpolateNS(fullname, i) { | |
return function (t) { | |
this.setAttributeNS(fullname.space, fullname.local, i.call(this, t)) | |
} | |
} | |
function attrTweenNS(fullname, value) { | |
var t0, i0 | |
function tween() { | |
var i = value.apply(this, arguments) | |
if (i !== i0) t0 = (i0 = i) && attrInterpolateNS(fullname, i) | |
return t0 | |
} | |
tween._value = value | |
return tween | |
} | |
function attrTween(name, value) { | |
var t0, i0 | |
function tween() { | |
var i = value.apply(this, arguments) | |
if (i !== i0) t0 = (i0 = i) && attrInterpolate(name, i) | |
return t0 | |
} | |
tween._value = value | |
return tween | |
} | |
function transition_attrTween(name, value) { | |
var key = 'attr.' + name | |
if (arguments.length < 2) return (key = this.tween(key)) && key._value | |
if (value == null) return this.tween(key, null) | |
if (typeof value !== 'function') throw new Error() | |
var fullname = namespace(name) | |
return this.tween( | |
key, | |
(fullname.local ? attrTweenNS : attrTween)(fullname, value), | |
) | |
} | |
function delayFunction(id, value) { | |
return function () { | |
init(this, id).delay = +value.apply(this, arguments) | |
} | |
} | |
function delayConstant(id, value) { | |
return ( | |
(value = +value), | |
function () { | |
init(this, id).delay = value | |
} | |
) | |
} | |
function transition_delay(value) { | |
var id = this._id | |
return arguments.length | |
? this.each( | |
(typeof value === 'function' ? delayFunction : delayConstant)( | |
id, | |
value, | |
), | |
) | |
: get(this.node(), id).delay | |
} | |
function durationFunction(id, value) { | |
return function () { | |
set(this, id).duration = +value.apply(this, arguments) | |
} | |
} | |
function durationConstant(id, value) { | |
return ( | |
(value = +value), | |
function () { | |
set(this, id).duration = value | |
} | |
) | |
} | |
function transition_duration(value) { | |
var id = this._id | |
return arguments.length | |
? this.each( | |
(typeof value === 'function' ? durationFunction : durationConstant)( | |
id, | |
value, | |
), | |
) | |
: get(this.node(), id).duration | |
} | |
function easeConstant(id, value) { | |
if (typeof value !== 'function') throw new Error() | |
return function () { | |
set(this, id).ease = value | |
} | |
} | |
function transition_ease(value) { | |
var id = this._id | |
return arguments.length | |
? this.each(easeConstant(id, value)) | |
: get(this.node(), id).ease | |
} | |
function easeVarying(id, value) { | |
return function () { | |
var v = value.apply(this, arguments) | |
if (typeof v !== 'function') throw new Error() | |
set(this, id).ease = v | |
} | |
} | |
function transition_easeVarying(value) { | |
if (typeof value !== 'function') throw new Error() | |
return this.each(easeVarying(this._id, value)) | |
} | |
function transition_filter(match) { | |
if (typeof match !== 'function') match = matcher(match) | |
for ( | |
var groups = this._groups, | |
m = groups.length, | |
subgroups = new Array(m), | |
j = 0; | |
j < m; | |
++j | |
) { | |
for ( | |
var group = groups[j], | |
n = group.length, | |
subgroup = (subgroups[j] = []), | |
node, | |
i = 0; | |
i < n; | |
++i | |
) { | |
if ((node = group[i]) && match.call(node, node.__data__, i, group)) { | |
subgroup.push(node) | |
} | |
} | |
} | |
return new Transition(subgroups, this._parents, this._name, this._id) | |
} | |
function transition_merge(transition) { | |
if (transition._id !== this._id) throw new Error() | |
for ( | |
var groups0 = this._groups, | |
groups1 = transition._groups, | |
m0 = groups0.length, | |
m1 = groups1.length, | |
m = Math.min(m0, m1), | |
merges = new Array(m0), | |
j = 0; | |
j < m; | |
++j | |
) { | |
for ( | |
var group0 = groups0[j], | |
group1 = groups1[j], | |
n = group0.length, | |
merge = (merges[j] = new Array(n)), | |
node, | |
i = 0; | |
i < n; | |
++i | |
) { | |
if ((node = group0[i] || group1[i])) { | |
merge[i] = node | |
} | |
} | |
} | |
for (; j < m0; ++j) { | |
merges[j] = groups0[j] | |
} | |
return new Transition(merges, this._parents, this._name, this._id) | |
} | |
function start(name) { | |
return (name + '') | |
.trim() | |
.split(/^|\s+/) | |
.every(function (t) { | |
var i = t.indexOf('.') | |
if (i >= 0) t = t.slice(0, i) | |
return !t || t === 'start' | |
}) | |
} | |
function onFunction(id, name, listener) { | |
var on0, | |
on1, | |
sit = start(name) ? init : set | |
return function () { | |
var schedule = sit(this, id), | |
on = schedule.on | |
// If this node shared a dispatch with the previous node, | |
// just assign the updated shared dispatch and we’re done! | |
// Otherwise, copy-on-write. | |
if (on !== on0) (on1 = (on0 = on).copy()).on(name, listener) | |
schedule.on = on1 | |
} | |
} | |
function transition_on(name, listener) { | |
var id = this._id | |
return arguments.length < 2 | |
? get(this.node(), id).on.on(name) | |
: this.each(onFunction(id, name, listener)) | |
} | |
function removeFunction(id) { | |
return function () { | |
var parent = this.parentNode | |
for (var i in this.__transition) if (+i !== id) return | |
if (parent) parent.removeChild(this) | |
} | |
} | |
function transition_remove() { | |
return this.on('end.remove', removeFunction(this._id)) | |
} | |
function transition_select(select) { | |
var name = this._name, | |
id = this._id | |
if (typeof select !== 'function') select = selector(select) | |
for ( | |
var groups = this._groups, | |
m = groups.length, | |
subgroups = new Array(m), | |
j = 0; | |
j < m; | |
++j | |
) { | |
for ( | |
var group = groups[j], | |
n = group.length, | |
subgroup = (subgroups[j] = new Array(n)), | |
node, | |
subnode, | |
i = 0; | |
i < n; | |
++i | |
) { | |
if ( | |
(node = group[i]) && | |
(subnode = select.call(node, node.__data__, i, group)) | |
) { | |
if ('__data__' in node) subnode.__data__ = node.__data__ | |
subgroup[i] = subnode | |
schedule(subgroup[i], name, id, i, subgroup, get(node, id)) | |
} | |
} | |
} | |
return new Transition(subgroups, this._parents, name, id) | |
} | |
function transition_selectAll(select) { | |
var name = this._name, | |
id = this._id | |
if (typeof select !== 'function') select = selectorAll(select) | |
for ( | |
var groups = this._groups, | |
m = groups.length, | |
subgroups = [], | |
parents = [], | |
j = 0; | |
j < m; | |
++j | |
) { | |
for (var group = groups[j], n = group.length, node, i = 0; i < n; ++i) { | |
if ((node = group[i])) { | |
for ( | |
var children = select.call(node, node.__data__, i, group), | |
child, | |
inherit = get(node, id), | |
k = 0, | |
l = children.length; | |
k < l; | |
++k | |
) { | |
if ((child = children[k])) { | |
schedule(child, name, id, k, children, inherit) | |
} | |
} | |
subgroups.push(children) | |
parents.push(node) | |
} | |
} | |
} | |
return new Transition(subgroups, parents, name, id) | |
} | |
var Selection = selection.prototype.constructor | |
function transition_selection() { | |
return new Selection(this._groups, this._parents) | |
} | |
function styleNull(name, interpolate) { | |
var string00, string10, interpolate0 | |
return function () { | |
var string0 = styleValue(this, name), | |
string1 = (this.style.removeProperty(name), styleValue(this, name)) | |
return string0 === string1 | |
? null | |
: string0 === string00 && string1 === string10 | |
? interpolate0 | |
: (interpolate0 = interpolate( | |
(string00 = string0), | |
(string10 = string1), | |
)) | |
} | |
} | |
function styleRemove(name) { | |
return function () { | |
this.style.removeProperty(name) | |
} | |
} | |
function styleConstant(name, interpolate, value1) { | |
var string00, | |
string1 = value1 + '', | |
interpolate0 | |
return function () { | |
var string0 = styleValue(this, name) | |
return string0 === string1 | |
? null | |
: string0 === string00 | |
? interpolate0 | |
: (interpolate0 = interpolate((string00 = string0), value1)) | |
} | |
} | |
function styleFunction(name, interpolate, value) { | |
var string00, string10, interpolate0 | |
return function () { | |
var string0 = styleValue(this, name), | |
value1 = value(this), | |
string1 = value1 + '' | |
if (value1 == null) | |
string1 = value1 = | |
(this.style.removeProperty(name), styleValue(this, name)) | |
return string0 === string1 | |
? null | |
: string0 === string00 && string1 === string10 | |
? interpolate0 | |
: ((string10 = string1), | |
(interpolate0 = interpolate((string00 = string0), value1))) | |
} | |
} | |
function styleMaybeRemove(id, name) { | |
var on0, | |
on1, | |
listener0, | |
key = 'style.' + name, | |
event = 'end.' + key, | |
remove | |
return function () { | |
var schedule = set(this, id), | |
on = schedule.on, | |
listener = | |
schedule.value[key] == null | |
? remove || (remove = styleRemove(name)) | |
: undefined | |
// If this node shared a dispatch with the previous node, | |
// just assign the updated shared dispatch and we’re done! | |
// Otherwise, copy-on-write. | |
if (on !== on0 || listener0 !== listener) | |
(on1 = (on0 = on).copy()).on(event, (listener0 = listener)) | |
schedule.on = on1 | |
} | |
} | |
function transition_style(name, value, priority) { | |
var i = (name += '') === 'transform' ? interpolateTransformCss : interpolate | |
return value == null | |
? this.styleTween(name, styleNull(name, i)).on( | |
'end.style.' + name, | |
styleRemove(name), | |
) | |
: typeof value === 'function' | |
? this.styleTween( | |
name, | |
styleFunction(name, i, tweenValue(this, 'style.' + name, value)), | |
).each(styleMaybeRemove(this._id, name)) | |
: this.styleTween(name, styleConstant(name, i, value), priority).on( | |
'end.style.' + name, | |
null, | |
) | |
} | |
function styleInterpolate(name, i, priority) { | |
return function (t) { | |
this.style.setProperty(name, i.call(this, t), priority) | |
} | |
} | |
function styleTween(name, value, priority) { | |
var t, i0 | |
function tween() { | |
var i = value.apply(this, arguments) | |
if (i !== i0) t = (i0 = i) && styleInterpolate(name, i, priority) | |
return t | |
} | |
tween._value = value | |
return tween | |
} | |
function transition_styleTween(name, value, priority) { | |
var key = 'style.' + (name += '') | |
if (arguments.length < 2) return (key = this.tween(key)) && key._value | |
if (value == null) return this.tween(key, null) | |
if (typeof value !== 'function') throw new Error() | |
return this.tween( | |
key, | |
styleTween(name, value, priority == null ? '' : priority), | |
) | |
} | |
function textConstant(value) { | |
return function () { | |
this.textContent = value | |
} | |
} | |
function textFunction(value) { | |
return function () { | |
var value1 = value(this) | |
this.textContent = value1 == null ? '' : value1 | |
} | |
} | |
function transition_text(value) { | |
return this.tween( | |
'text', | |
typeof value === 'function' | |
? textFunction(tweenValue(this, 'text', value)) | |
: textConstant(value == null ? '' : value + ''), | |
) | |
} | |
function textInterpolate(i) { | |
return function (t) { | |
this.textContent = i.call(this, t) | |
} | |
} | |
function textTween(value) { | |
var t0, i0 | |
function tween() { | |
var i = value.apply(this, arguments) | |
if (i !== i0) t0 = (i0 = i) && textInterpolate(i) | |
return t0 | |
} | |
tween._value = value | |
return tween | |
} | |
function transition_textTween(value) { | |
var key = 'text' | |
if (arguments.length < 1) return (key = this.tween(key)) && key._value | |
if (value == null) return this.tween(key, null) | |
if (typeof value !== 'function') throw new Error() | |
return this.tween(key, textTween(value)) | |
} | |
function transition_transition() { | |
var name = this._name, | |
id0 = this._id, | |
id1 = newId() | |
for (var groups = this._groups, m = groups.length, j = 0; j < m; ++j) { | |
for (var group = groups[j], n = group.length, node, i = 0; i < n; ++i) { | |
if ((node = group[i])) { | |
var inherit = get(node, id0) | |
schedule(node, name, id1, i, group, { | |
time: inherit.time + inherit.delay + inherit.duration, | |
delay: 0, | |
duration: inherit.duration, | |
ease: inherit.ease, | |
}) | |
} | |
} | |
} | |
return new Transition(groups, this._parents, name, id1) | |
} | |
function transition_end() { | |
var on0, | |
on1, | |
that = this, | |
id = that._id, | |
size = that.size() | |
return new Promise(function (resolve, reject) { | |
var cancel = { value: reject }, | |
end = { | |
value: function () { | |
if (--size === 0) resolve() | |
}, | |
} | |
that.each(function () { | |
var schedule = set(this, id), | |
on = schedule.on | |
// If this node shared a dispatch with the previous node, | |
// just assign the updated shared dispatch and we’re done! | |
// Otherwise, copy-on-write. | |
if (on !== on0) { | |
on1 = (on0 = on).copy() | |
on1._.cancel.push(cancel) | |
on1._.interrupt.push(cancel) | |
on1._.end.push(end) | |
} | |
schedule.on = on1 | |
}) | |
// The selection was empty, resolve end immediately | |
if (size === 0) resolve() | |
}) | |
} | |
var id = 0 | |
function Transition(groups, parents, name, id) { | |
this._groups = groups | |
this._parents = parents | |
this._name = name | |
this._id = id | |
} | |
function newId() { | |
return ++id | |
} | |
var selection_prototype = selection.prototype | |
Transition.prototype = { | |
constructor: Transition, | |
select: transition_select, | |
selectAll: transition_selectAll, | |
selectChild: selection_prototype.selectChild, | |
selectChildren: selection_prototype.selectChildren, | |
filter: transition_filter, | |
merge: transition_merge, | |
selection: transition_selection, | |
transition: transition_transition, | |
call: selection_prototype.call, | |
nodes: selection_prototype.nodes, | |
node: selection_prototype.node, | |
size: selection_prototype.size, | |
empty: selection_prototype.empty, | |
each: selection_prototype.each, | |
on: transition_on, | |
attr: transition_attr, | |
attrTween: transition_attrTween, | |
style: transition_style, | |
styleTween: transition_styleTween, | |
text: transition_text, | |
textTween: transition_textTween, | |
remove: transition_remove, | |
tween: transition_tween, | |
delay: transition_delay, | |
duration: transition_duration, | |
ease: transition_ease, | |
easeVarying: transition_easeVarying, | |
end: transition_end, | |
[Symbol.iterator]: selection_prototype[Symbol.iterator], | |
} | |
function cubicInOut(t) { | |
return ((t *= 2) <= 1 ? t * t * t : (t -= 2) * t * t + 2) / 2 | |
} | |
var defaultTiming = { | |
time: null, // Set on use. | |
delay: 0, | |
duration: 250, | |
ease: cubicInOut, | |
} | |
function inherit(node, id) { | |
var timing | |
while (!(timing = node.__transition) || !(timing = timing[id])) { | |
if (!(node = node.parentNode)) { | |
throw new Error(`transition ${id} not found`) | |
} | |
} | |
return timing | |
} | |
function selection_transition(name) { | |
var id, timing | |
if (name instanceof Transition) { | |
;(id = name._id), (name = name._name) | |
} else { | |
;(id = newId()), | |
((timing = defaultTiming).time = now()), | |
(name = name == null ? null : name + '') | |
} | |
for (var groups = this._groups, m = groups.length, j = 0; j < m; ++j) { | |
for (var group = groups[j], n = group.length, node, i = 0; i < n; ++i) { | |
if ((node = group[i])) { | |
schedule(node, name, id, i, group, timing || inherit(node, id)) | |
} | |
} | |
} | |
return new Transition(groups, this._parents, name, id) | |
} | |
selection.prototype.interrupt = selection_interrupt | |
selection.prototype.transition = selection_transition | |
function center(x, y) { | |
var nodes, | |
strength = 1 | |
if (x == null) x = 0 | |
if (y == null) y = 0 | |
function force() { | |
var i, | |
n = nodes.length, | |
node, | |
sx = 0, | |
sy = 0 | |
for (i = 0; i < n; ++i) { | |
;(node = nodes[i]), (sx += node.x), (sy += node.y) | |
} | |
for ( | |
sx = (sx / n - x) * strength, sy = (sy / n - y) * strength, i = 0; | |
i < n; | |
++i | |
) { | |
;(node = nodes[i]), (node.x -= sx), (node.y -= sy) | |
} | |
} | |
force.initialize = function (_) { | |
nodes = _ | |
} | |
force.x = function (_) { | |
return arguments.length ? ((x = +_), force) : x | |
} | |
force.y = function (_) { | |
return arguments.length ? ((y = +_), force) : y | |
} | |
force.strength = function (_) { | |
return arguments.length ? ((strength = +_), force) : strength | |
} | |
return force | |
} | |
function tree_add(d) { | |
const x = +this._x.call(null, d), | |
y = +this._y.call(null, d) | |
return add(this.cover(x, y), x, y, d) | |
} | |
function add(tree, x, y, d) { | |
if (isNaN(x) || isNaN(y)) return tree // ignore invalid points | |
var parent, | |
node = tree._root, | |
leaf = { data: d }, | |
x0 = tree._x0, | |
y0 = tree._y0, | |
x1 = tree._x1, | |
y1 = tree._y1, | |
xm, | |
ym, | |
xp, | |
yp, | |
right, | |
bottom, | |
i, | |
j | |
// If the tree is empty, initialize the root as a leaf. | |
if (!node) return (tree._root = leaf), tree | |
// Find the existing leaf for the new point, or add it. | |
while (node.length) { | |
if ((right = x >= (xm = (x0 + x1) / 2))) x0 = xm | |
else x1 = xm | |
if ((bottom = y >= (ym = (y0 + y1) / 2))) y0 = ym | |
else y1 = ym | |
if (((parent = node), !(node = node[(i = (bottom << 1) | right)]))) | |
return (parent[i] = leaf), tree | |
} | |
// Is the new point is exactly coincident with the existing point? | |
xp = +tree._x.call(null, node.data) | |
yp = +tree._y.call(null, node.data) | |
if (x === xp && y === yp) | |
return ( | |
(leaf.next = node), | |
parent ? (parent[i] = leaf) : (tree._root = leaf), | |
tree | |
) | |
// Otherwise, split the leaf node until the old and new point are separated. | |
do { | |
parent = parent ? (parent[i] = new Array(4)) : (tree._root = new Array(4)) | |
if ((right = x >= (xm = (x0 + x1) / 2))) x0 = xm | |
else x1 = xm | |
if ((bottom = y >= (ym = (y0 + y1) / 2))) y0 = ym | |
else y1 = ym | |
} while ((i = (bottom << 1) | right) === (j = ((yp >= ym) << 1) | (xp >= xm))) | |
return (parent[j] = node), (parent[i] = leaf), tree | |
} | |
function addAll(data) { | |
var d, | |
i, | |
n = data.length, | |
x, | |
y, | |
xz = new Array(n), | |
yz = new Array(n), | |
x0 = Infinity, | |
y0 = Infinity, | |
x1 = -Infinity, | |
y1 = -Infinity | |
// Compute the points and their extent. | |
for (i = 0; i < n; ++i) { | |
if ( | |
isNaN((x = +this._x.call(null, (d = data[i])))) || | |
isNaN((y = +this._y.call(null, d))) | |
) | |
continue | |
xz[i] = x | |
yz[i] = y | |
if (x < x0) x0 = x | |
if (x > x1) x1 = x | |
if (y < y0) y0 = y | |
if (y > y1) y1 = y | |
} | |
// If there were no (valid) points, abort. | |
if (x0 > x1 || y0 > y1) return this | |
// Expand the tree to cover the new points. | |
this.cover(x0, y0).cover(x1, y1) | |
// Add the new points. | |
for (i = 0; i < n; ++i) { | |
add(this, xz[i], yz[i], data[i]) | |
} | |
return this | |
} | |
function tree_cover(x, y) { | |
if (isNaN((x = +x)) || isNaN((y = +y))) return this // ignore invalid points | |
var x0 = this._x0, | |
y0 = this._y0, | |
x1 = this._x1, | |
y1 = this._y1 | |
// If the quadtree has no extent, initialize them. | |
// Integer extent are necessary so that if we later double the extent, | |
// the existing quadrant boundaries don’t change due to floating point error! | |
if (isNaN(x0)) { | |
x1 = (x0 = Math.floor(x)) + 1 | |
y1 = (y0 = Math.floor(y)) + 1 | |
} | |
// Otherwise, double repeatedly to cover. | |
else { | |
var z = x1 - x0 || 1, | |
node = this._root, | |
parent, | |
i | |
while (x0 > x || x >= x1 || y0 > y || y >= y1) { | |
i = ((y < y0) << 1) | (x < x0) | |
;(parent = new Array(4)), (parent[i] = node), (node = parent), (z *= 2) | |
switch (i) { | |
case 0: | |
;(x1 = x0 + z), (y1 = y0 + z) | |
break | |
case 1: | |
;(x0 = x1 - z), (y1 = y0 + z) | |
break | |
case 2: | |
;(x1 = x0 + z), (y0 = y1 - z) | |
break | |
case 3: | |
;(x0 = x1 - z), (y0 = y1 - z) | |
break | |
} | |
} | |
if (this._root && this._root.length) this._root = node | |
} | |
this._x0 = x0 | |
this._y0 = y0 | |
this._x1 = x1 | |
this._y1 = y1 | |
return this | |
} | |
function tree_data() { | |
var data = [] | |
this.visit(function (node) { | |
if (!node.length) | |
do data.push(node.data) | |
while ((node = node.next)) | |
}) | |
return data | |
} | |
function tree_extent(_) { | |
return arguments.length | |
? this.cover(+_[0][0], +_[0][1]).cover(+_[1][0], +_[1][1]) | |
: isNaN(this._x0) | |
? undefined | |
: [ | |
[this._x0, this._y0], | |
[this._x1, this._y1], | |
] | |
} | |
function Quad(node, x0, y0, x1, y1) { | |
this.node = node | |
this.x0 = x0 | |
this.y0 = y0 | |
this.x1 = x1 | |
this.y1 = y1 | |
} | |
function tree_find(x, y, radius) { | |
var data, | |
x0 = this._x0, | |
y0 = this._y0, | |
x1, | |
y1, | |
x2, | |
y2, | |
x3 = this._x1, | |
y3 = this._y1, | |
quads = [], | |
node = this._root, | |
q, | |
i | |
if (node) quads.push(new Quad(node, x0, y0, x3, y3)) | |
if (radius == null) radius = Infinity | |
else { | |
;(x0 = x - radius), (y0 = y - radius) | |
;(x3 = x + radius), (y3 = y + radius) | |
radius *= radius | |
} | |
while ((q = quads.pop())) { | |
// Stop searching if this quadrant can’t contain a closer node. | |
if ( | |
!(node = q.node) || | |
(x1 = q.x0) > x3 || | |
(y1 = q.y0) > y3 || | |
(x2 = q.x1) < x0 || | |
(y2 = q.y1) < y0 | |
) | |
continue | |
// Bisect the current quadrant. | |
if (node.length) { | |
var xm = (x1 + x2) / 2, | |
ym = (y1 + y2) / 2 | |
quads.push( | |
new Quad(node[3], xm, ym, x2, y2), | |
new Quad(node[2], x1, ym, xm, y2), | |
new Quad(node[1], xm, y1, x2, ym), | |
new Quad(node[0], x1, y1, xm, ym), | |
) | |
// Visit the closest quadrant first. | |
if ((i = ((y >= ym) << 1) | (x >= xm))) { | |
q = quads[quads.length - 1] | |
quads[quads.length - 1] = quads[quads.length - 1 - i] | |
quads[quads.length - 1 - i] = q | |
} | |
} | |
// Visit this point. (Visiting coincident points isn’t necessary!) | |
else { | |
var dx = x - +this._x.call(null, node.data), | |
dy = y - +this._y.call(null, node.data), | |
d2 = dx * dx + dy * dy | |
if (d2 < radius) { | |
var d = Math.sqrt((radius = d2)) | |
;(x0 = x - d), (y0 = y - d) | |
;(x3 = x + d), (y3 = y + d) | |
data = node.data | |
} | |
} | |
} | |
return data | |
} | |
function tree_remove(d) { | |
if ( | |
isNaN((x = +this._x.call(null, d))) || | |
isNaN((y = +this._y.call(null, d))) | |
) | |
return this // ignore invalid points | |
var parent, | |
node = this._root, | |
retainer, | |
previous, | |
next, | |
x0 = this._x0, | |
y0 = this._y0, | |
x1 = this._x1, | |
y1 = this._y1, | |
x, | |
y, | |
xm, | |
ym, | |
right, | |
bottom, | |
i, | |
j | |
// If the tree is empty, initialize the root as a leaf. | |
if (!node) return this | |
// Find the leaf node for the point. | |
// While descending, also retain the deepest parent with a non-removed sibling. | |
if (node.length) | |
while (true) { | |
if ((right = x >= (xm = (x0 + x1) / 2))) x0 = xm | |
else x1 = xm | |
if ((bottom = y >= (ym = (y0 + y1) / 2))) y0 = ym | |
else y1 = ym | |
if (!((parent = node), (node = node[(i = (bottom << 1) | right)]))) | |
return this | |
if (!node.length) break | |
if (parent[(i + 1) & 3] || parent[(i + 2) & 3] || parent[(i + 3) & 3]) | |
(retainer = parent), (j = i) | |
} | |
// Find the point to remove. | |
while (node.data !== d) | |
if (!((previous = node), (node = node.next))) return this | |
if ((next = node.next)) delete node.next | |
// If there are multiple coincident points, remove just the point. | |
if (previous) | |
return next ? (previous.next = next) : delete previous.next, this | |
// If this is the root point, remove it. | |
if (!parent) return (this._root = next), this | |
// Remove this leaf. | |
next ? (parent[i] = next) : delete parent[i] | |
// If the parent now contains exactly one leaf, collapse superfluous parents. | |
if ( | |
(node = parent[0] || parent[1] || parent[2] || parent[3]) && | |
node === (parent[3] || parent[2] || parent[1] || parent[0]) && | |
!node.length | |
) { | |
if (retainer) retainer[j] = node | |
else this._root = node | |
} | |
return this | |
} | |
function removeAll(data) { | |
for (var i = 0, n = data.length; i < n; ++i) this.remove(data[i]) | |
return this | |
} | |
function tree_root() { | |
return this._root | |
} | |
function tree_size() { | |
var size = 0 | |
this.visit(function (node) { | |
if (!node.length) | |
do ++size | |
while ((node = node.next)) | |
}) | |
return size | |
} | |
function tree_visit(callback) { | |
var quads = [], | |
q, | |
node = this._root, | |
child, | |
x0, | |
y0, | |
x1, | |
y1 | |
if (node) quads.push(new Quad(node, this._x0, this._y0, this._x1, this._y1)) | |
while ((q = quads.pop())) { | |
if ( | |
!callback( | |
(node = q.node), | |
(x0 = q.x0), | |
(y0 = q.y0), | |
(x1 = q.x1), | |
(y1 = q.y1), | |
) && | |
node.length | |
) { | |
var xm = (x0 + x1) / 2, | |
ym = (y0 + y1) / 2 | |
if ((child = node[3])) quads.push(new Quad(child, xm, ym, x1, y1)) | |
if ((child = node[2])) quads.push(new Quad(child, x0, ym, xm, y1)) | |
if ((child = node[1])) quads.push(new Quad(child, xm, y0, x1, ym)) | |
if ((child = node[0])) quads.push(new Quad(child, x0, y0, xm, ym)) | |
} | |
} | |
return this | |
} | |
function tree_visitAfter(callback) { | |
var quads = [], | |
next = [], | |
q | |
if (this._root) | |
quads.push(new Quad(this._root, this._x0, this._y0, this._x1, this._y1)) | |
while ((q = quads.pop())) { | |
var node = q.node | |
if (node.length) { | |
var child, | |
x0 = q.x0, | |
y0 = q.y0, | |
x1 = q.x1, | |
y1 = q.y1, | |
xm = (x0 + x1) / 2, | |
ym = (y0 + y1) / 2 | |
if ((child = node[0])) quads.push(new Quad(child, x0, y0, xm, ym)) | |
if ((child = node[1])) quads.push(new Quad(child, xm, y0, x1, ym)) | |
if ((child = node[2])) quads.push(new Quad(child, x0, ym, xm, y1)) | |
if ((child = node[3])) quads.push(new Quad(child, xm, ym, x1, y1)) | |
} | |
next.push(q) | |
} | |
while ((q = next.pop())) { | |
callback(q.node, q.x0, q.y0, q.x1, q.y1) | |
} | |
return this | |
} | |
function defaultX(d) { | |
return d[0] | |
} | |
function tree_x(_) { | |
return arguments.length ? ((this._x = _), this) : this._x | |
} | |
function defaultY(d) { | |
return d[1] | |
} | |
function tree_y(_) { | |
return arguments.length ? ((this._y = _), this) : this._y | |
} | |
function quadtree(nodes, x, y) { | |
var tree = new Quadtree( | |
x == null ? defaultX : x, | |
y == null ? defaultY : y, | |
NaN, | |
NaN, | |
NaN, | |
NaN, | |
) | |
return nodes == null ? tree : tree.addAll(nodes) | |
} | |
function Quadtree(x, y, x0, y0, x1, y1) { | |
this._x = x | |
this._y = y | |
this._x0 = x0 | |
this._y0 = y0 | |
this._x1 = x1 | |
this._y1 = y1 | |
this._root = undefined | |
} | |
function leaf_copy(leaf) { | |
var copy = { data: leaf.data }, | |
next = copy | |
while ((leaf = leaf.next)) next = next.next = { data: leaf.data } | |
return copy | |
} | |
var treeProto = (quadtree.prototype = Quadtree.prototype) | |
treeProto.copy = function () { | |
var copy = new Quadtree( | |
this._x, | |
this._y, | |
this._x0, | |
this._y0, | |
this._x1, | |
this._y1, | |
), | |
node = this._root, | |
nodes, | |
child | |
if (!node) return copy | |
if (!node.length) return (copy._root = leaf_copy(node)), copy | |
nodes = [{ source: node, target: (copy._root = new Array(4)) }] | |
while ((node = nodes.pop())) { | |
for (var i = 0; i < 4; ++i) { | |
if ((child = node.source[i])) { | |
if (child.length) | |
nodes.push({ source: child, target: (node.target[i] = new Array(4)) }) | |
else node.target[i] = leaf_copy(child) | |
} | |
} | |
} | |
return copy | |
} | |
treeProto.add = tree_add | |
treeProto.addAll = addAll | |
treeProto.cover = tree_cover | |
treeProto.data = tree_data | |
treeProto.extent = tree_extent | |
treeProto.find = tree_find | |
treeProto.remove = tree_remove | |
treeProto.removeAll = removeAll | |
treeProto.root = tree_root | |
treeProto.size = tree_size | |
treeProto.visit = tree_visit | |
treeProto.visitAfter = tree_visitAfter | |
treeProto.x = tree_x | |
treeProto.y = tree_y | |
function constant(x) { | |
return function () { | |
return x | |
} | |
} | |
function jiggle(random) { | |
return (random() - 0.5) * 1e-6 | |
} | |
function index(d) { | |
return d.index | |
} | |
function find(nodeById, nodeId) { | |
var node = nodeById.get(nodeId) | |
if (!node) throw new Error('node not found: ' + nodeId) | |
return node | |
} | |
function link(links) { | |
var id = index, | |
strength = defaultStrength, | |
strengths, | |
distance = constant(30), | |
distances, | |
nodes, | |
count, | |
bias, | |
random, | |
iterations = 1 | |
if (links == null) links = [] | |
function defaultStrength(link) { | |
return 1 / Math.min(count[link.source.index], count[link.target.index]) | |
} | |
function force(alpha) { | |
for (var k = 0, n = links.length; k < iterations; ++k) { | |
for (var i = 0, link, source, target, x, y, l, b; i < n; ++i) { | |
;(link = links[i]), (source = link.source), (target = link.target) | |
x = target.x + target.vx - source.x - source.vx || jiggle(random) | |
y = target.y + target.vy - source.y - source.vy || jiggle(random) | |
l = Math.sqrt(x * x + y * y) | |
l = ((l - distances[i]) / l) * alpha * strengths[i] | |
;(x *= l), (y *= l) | |
target.vx -= x * (b = bias[i]) | |
target.vy -= y * b | |
source.vx += x * (b = 1 - b) | |
source.vy += y * b | |
} | |
} | |
} | |
function initialize() { | |
if (!nodes) return | |
var i, | |
n = nodes.length, | |
m = links.length, | |
nodeById = new Map(nodes.map((d, i) => [id(d, i, nodes), d])), | |
link | |
for (i = 0, count = new Array(n); i < m; ++i) { | |
;(link = links[i]), (link.index = i) | |
if (typeof link.source !== 'object') | |
link.source = find(nodeById, link.source) | |
if (typeof link.target !== 'object') | |
link.target = find(nodeById, link.target) | |
count[link.source.index] = (count[link.source.index] || 0) + 1 | |
count[link.target.index] = (count[link.target.index] || 0) + 1 | |
} | |
for (i = 0, bias = new Array(m); i < m; ++i) { | |
;(link = links[i]), | |
(bias[i] = | |
count[link.source.index] / | |
(count[link.source.index] + count[link.target.index])) | |
} | |
;(strengths = new Array(m)), initializeStrength() | |
;(distances = new Array(m)), initializeDistance() | |
} | |
function initializeStrength() { | |
if (!nodes) return | |
for (var i = 0, n = links.length; i < n; ++i) { | |
strengths[i] = +strength(links[i], i, links) | |
} | |
} | |
function initializeDistance() { | |
if (!nodes) return | |
for (var i = 0, n = links.length; i < n; ++i) { | |
distances[i] = +distance(links[i], i, links) | |
} | |
} | |
force.initialize = function (_nodes, _random) { | |
nodes = _nodes | |
random = _random | |
initialize() | |
} | |
force.links = function (_) { | |
return arguments.length ? ((links = _), initialize(), force) : links | |
} | |
force.id = function (_) { | |
return arguments.length ? ((id = _), force) : id | |
} | |
force.iterations = function (_) { | |
return arguments.length ? ((iterations = +_), force) : iterations | |
} | |
force.strength = function (_) { | |
return arguments.length | |
? ((strength = typeof _ === 'function' ? _ : constant(+_)), | |
initializeStrength(), | |
force) | |
: strength | |
} | |
force.distance = function (_) { | |
return arguments.length | |
? ((distance = typeof _ === 'function' ? _ : constant(+_)), | |
initializeDistance(), | |
force) | |
: distance | |
} | |
return force | |
} | |
// https://en.wikipedia.org/wiki/Linear_congruential_generator#Parameters_in_common_use | |
const a = 1664525 | |
const c = 1013904223 | |
const m = 4294967296 // 2^32 | |
function lcg() { | |
let s = 1 | |
return () => (s = (a * s + c) % m) / m | |
} | |
function x(d) { | |
return d.x | |
} | |
function y(d) { | |
return d.y | |
} | |
var initialRadius = 10, | |
initialAngle = Math.PI * (3 - Math.sqrt(5)) | |
function simulation(nodes) { | |
var simulation, | |
alpha = 1, | |
alphaMin = 0.001, | |
alphaDecay = 1 - Math.pow(alphaMin, 1 / 300), | |
alphaTarget = 0, | |
velocityDecay = 0.6, | |
forces = new Map(), | |
stepper = timer(step), | |
event = dispatch('tick', 'end'), | |
random = lcg() | |
if (nodes == null) nodes = [] | |
function step() { | |
tick() | |
event.call('tick', simulation) | |
if (alpha < alphaMin) { | |
stepper.stop() | |
event.call('end', simulation) | |
} | |
} | |
function tick(iterations) { | |
var i, | |
n = nodes.length, | |
node | |
if (iterations === undefined) iterations = 1 | |
for (var k = 0; k < iterations; ++k) { | |
alpha += (alphaTarget - alpha) * alphaDecay | |
forces.forEach(function (force) { | |
force(alpha) | |
}) | |
for (i = 0; i < n; ++i) { | |
node = nodes[i] | |
if (node.fx == null) node.x += node.vx *= velocityDecay | |
else (node.x = node.fx), (node.vx = 0) | |
if (node.fy == null) node.y += node.vy *= velocityDecay | |
else (node.y = node.fy), (node.vy = 0) | |
} | |
} | |
return simulation | |
} | |
function initializeNodes() { | |
for (var i = 0, n = nodes.length, node; i < n; ++i) { | |
;(node = nodes[i]), (node.index = i) | |
if (node.fx != null) node.x = node.fx | |
if (node.fy != null) node.y = node.fy | |
if (isNaN(node.x) || isNaN(node.y)) { | |
var radius = initialRadius * Math.sqrt(0.5 + i), | |
angle = i * initialAngle | |
node.x = radius * Math.cos(angle) | |
node.y = radius * Math.sin(angle) | |
} | |
if (isNaN(node.vx) || isNaN(node.vy)) { | |
node.vx = node.vy = 0 | |
} | |
} | |
} | |
function initializeForce(force) { | |
if (force.initialize) force.initialize(nodes, random) | |
return force | |
} | |
initializeNodes() | |
return (simulation = { | |
tick: tick, | |
restart: function () { | |
return stepper.restart(step), simulation | |
}, | |
stop: function () { | |
return stepper.stop(), simulation | |
}, | |
nodes: function (_) { | |
return arguments.length | |
? ((nodes = _), | |
initializeNodes(), | |
forces.forEach(initializeForce), | |
simulation) | |
: nodes | |
}, | |
alpha: function (_) { | |
return arguments.length ? ((alpha = +_), simulation) : alpha | |
}, | |
alphaMin: function (_) { | |
return arguments.length ? ((alphaMin = +_), simulation) : alphaMin | |
}, | |
alphaDecay: function (_) { | |
return arguments.length ? ((alphaDecay = +_), simulation) : +alphaDecay | |
}, | |
alphaTarget: function (_) { | |
return arguments.length ? ((alphaTarget = +_), simulation) : alphaTarget | |
}, | |
velocityDecay: function (_) { | |
return arguments.length | |
? ((velocityDecay = 1 - _), simulation) | |
: 1 - velocityDecay | |
}, | |
randomSource: function (_) { | |
return arguments.length | |
? ((random = _), forces.forEach(initializeForce), simulation) | |
: random | |
}, | |
force: function (name, _) { | |
return arguments.length > 1 | |
? (_ == null | |
? forces.delete(name) | |
: forces.set(name, initializeForce(_)), | |
simulation) | |
: forces.get(name) | |
}, | |
find: function (x, y, radius) { | |
var i = 0, | |
n = nodes.length, | |
dx, | |
dy, | |
d2, | |
node, | |
closest | |
if (radius == null) radius = Infinity | |
else radius *= radius | |
for (i = 0; i < n; ++i) { | |
node = nodes[i] | |
dx = x - node.x | |
dy = y - node.y | |
d2 = dx * dx + dy * dy | |
if (d2 < radius) (closest = node), (radius = d2) | |
} | |
return closest | |
}, | |
on: function (name, _) { | |
return arguments.length > 1 | |
? (event.on(name, _), simulation) | |
: event.on(name) | |
}, | |
}) | |
} | |
function manyBody() { | |
var nodes, | |
node, | |
random, | |
alpha, | |
strength = constant(-30), | |
strengths, | |
distanceMin2 = 1, | |
distanceMax2 = Infinity, | |
theta2 = 0.81 | |
function force(_) { | |
var i, | |
n = nodes.length, | |
tree = quadtree(nodes, x, y).visitAfter(accumulate) | |
for (alpha = _, i = 0; i < n; ++i) (node = nodes[i]), tree.visit(apply) | |
} | |
function initialize() { | |
if (!nodes) return | |
var i, | |
n = nodes.length, | |
node | |
strengths = new Array(n) | |
for (i = 0; i < n; ++i) | |
(node = nodes[i]), (strengths[node.index] = +strength(node, i, nodes)) | |
} | |
function accumulate(quad) { | |
var strength = 0, | |
q, | |
c, | |
weight = 0, | |
x, | |
y, | |
i | |
// For internal nodes, accumulate forces from child quadrants. | |
if (quad.length) { | |
for (x = y = i = 0; i < 4; ++i) { | |
if ((q = quad[i]) && (c = Math.abs(q.value))) { | |
;(strength += q.value), (weight += c), (x += c * q.x), (y += c * q.y) | |
} | |
} | |
quad.x = x / weight | |
quad.y = y / weight | |
} | |
// For leaf nodes, accumulate forces from coincident quadrants. | |
else { | |
q = quad | |
q.x = q.data.x | |
q.y = q.data.y | |
do strength += strengths[q.data.index] | |
while ((q = q.next)) | |
} | |
quad.value = strength | |
} | |
function apply(quad, x1, _, x2) { | |
if (!quad.value) return true | |
var x = quad.x - node.x, | |
y = quad.y - node.y, | |
w = x2 - x1, | |
l = x * x + y * y | |
// Apply the Barnes-Hut approximation if possible. | |
// Limit forces for very close nodes; randomize direction if coincident. | |
if ((w * w) / theta2 < l) { | |
if (l < distanceMax2) { | |
if (x === 0) (x = jiggle(random)), (l += x * x) | |
if (y === 0) (y = jiggle(random)), (l += y * y) | |
if (l < distanceMin2) l = Math.sqrt(distanceMin2 * l) | |
node.vx += (x * quad.value * alpha) / l | |
node.vy += (y * quad.value * alpha) / l | |
} | |
return true | |
} | |
// Otherwise, process points directly. | |
else if (quad.length || l >= distanceMax2) return | |
// Limit forces for very close nodes; randomize direction if coincident. | |
if (quad.data !== node || quad.next) { | |
if (x === 0) (x = jiggle(random)), (l += x * x) | |
if (y === 0) (y = jiggle(random)), (l += y * y) | |
if (l < distanceMin2) l = Math.sqrt(distanceMin2 * l) | |
} | |
do | |
if (quad.data !== node) { | |
w = (strengths[quad.data.index] * alpha) / l | |
node.vx += x * w | |
node.vy += y * w | |
} | |
while ((quad = quad.next)) | |
} | |
force.initialize = function (_nodes, _random) { | |
nodes = _nodes | |
random = _random | |
initialize() | |
} | |
force.strength = function (_) { | |
return arguments.length | |
? ((strength = typeof _ === 'function' ? _ : constant(+_)), | |
initialize(), | |
force) | |
: strength | |
} | |
force.distanceMin = function (_) { | |
return arguments.length | |
? ((distanceMin2 = _ * _), force) | |
: Math.sqrt(distanceMin2) | |
} | |
force.distanceMax = function (_) { | |
return arguments.length | |
? ((distanceMax2 = _ * _), force) | |
: Math.sqrt(distanceMax2) | |
} | |
force.theta = function (_) { | |
return arguments.length ? ((theta2 = _ * _), force) : Math.sqrt(theta2) | |
} | |
return force | |
} | |
function initRange(domain, range) { | |
switch (arguments.length) { | |
case 0: | |
break | |
case 1: | |
this.range(domain) | |
break | |
default: | |
this.range(range).domain(domain) | |
break | |
} | |
return this | |
} | |
const implicit = Symbol('implicit') | |
function ordinal() { | |
var index = new InternMap(), | |
domain = [], | |
range = [], | |
unknown = implicit | |
function scale(d) { | |
let i = index.get(d) | |
if (i === undefined) { | |
if (unknown !== implicit) return unknown | |
index.set(d, (i = domain.push(d) - 1)) | |
} | |
return range[i % range.length] | |
} | |
scale.domain = function (_) { | |
if (!arguments.length) return domain.slice() | |
;(domain = []), (index = new InternMap()) | |
for (const value of _) { | |
if (index.has(value)) continue | |
index.set(value, domain.push(value) - 1) | |
} | |
return scale | |
} | |
scale.range = function (_) { | |
return arguments.length ? ((range = Array.from(_)), scale) : range.slice() | |
} | |
scale.unknown = function (_) { | |
return arguments.length ? ((unknown = _), scale) : unknown | |
} | |
scale.copy = function () { | |
return ordinal(domain, range).unknown(unknown) | |
} | |
initRange.apply(scale, arguments) | |
return scale | |
} | |
function colors(specifier) { | |
var n = (specifier.length / 6) | 0, | |
colors = new Array(n), | |
i = 0 | |
while (i < n) colors[i] = '#' + specifier.slice(i * 6, ++i * 6) | |
return colors | |
} | |
var category10 = colors( | |
'1f77b4ff7f0e2ca02cd627289467bd8c564be377c27f7f7fbcbd2217becf', | |
) | |
var Dark2 = colors('1b9e77d95f027570b3e7298a66a61ee6ab02a6761d666666') | |
function Transform(k, x, y) { | |
this.k = k | |
this.x = x | |
this.y = y | |
} | |
Transform.prototype = { | |
constructor: Transform, | |
scale: function (k) { | |
return k === 1 ? this : new Transform(this.k * k, this.x, this.y) | |
}, | |
translate: function (x, y) { | |
return (x === 0) & (y === 0) | |
? this | |
: new Transform(this.k, this.x + this.k * x, this.y + this.k * y) | |
}, | |
apply: function (point) { | |
return [point[0] * this.k + this.x, point[1] * this.k + this.y] | |
}, | |
applyX: function (x) { | |
return x * this.k + this.x | |
}, | |
applyY: function (y) { | |
return y * this.k + this.y | |
}, | |
invert: function (location) { | |
return [(location[0] - this.x) / this.k, (location[1] - this.y) / this.k] | |
}, | |
invertX: function (x) { | |
return (x - this.x) / this.k | |
}, | |
invertY: function (y) { | |
return (y - this.y) / this.k | |
}, | |
rescaleX: function (x) { | |
return x.copy().domain(x.range().map(this.invertX, this).map(x.invert, x)) | |
}, | |
rescaleY: function (y) { | |
return y.copy().domain(y.range().map(this.invertY, this).map(y.invert, y)) | |
}, | |
toString: function () { | |
return 'translate(' + this.x + ',' + this.y + ') scale(' + this.k + ')' | |
}, | |
} | |
Transform.prototype | |
function _isPlaceholder(a) { | |
return ( | |
a != null && typeof a === 'object' && a['@@functional/placeholder'] === true | |
) | |
} | |
/** | |
* Optimized internal one-arity curry function. | |
* | |
* @private | |
* @category Function | |
* @param {Function} fn The function to curry. | |
* @return {Function} The curried function. | |
*/ | |
function _curry1(fn) { | |
return function f1(a) { | |
if (arguments.length === 0 || _isPlaceholder(a)) { | |
return f1 | |
} else { | |
return fn.apply(this, arguments) | |
} | |
} | |
} | |
/** | |
* Optimized internal two-arity curry function. | |
* | |
* @private | |
* @category Function | |
* @param {Function} fn The function to curry. | |
* @return {Function} The curried function. | |
*/ | |
function _curry2(fn) { | |
return function f2(a, b) { | |
switch (arguments.length) { | |
case 0: | |
return f2 | |
case 1: | |
return _isPlaceholder(a) | |
? f2 | |
: _curry1(function (_b) { | |
return fn(a, _b) | |
}) | |
default: | |
return _isPlaceholder(a) && _isPlaceholder(b) | |
? f2 | |
: _isPlaceholder(a) | |
? _curry1(function (_a) { | |
return fn(_a, b) | |
}) | |
: _isPlaceholder(b) | |
? _curry1(function (_b) { | |
return fn(a, _b) | |
}) | |
: fn(a, b) | |
} | |
} | |
} | |
function _arity(n, fn) { | |
/* eslint-disable no-unused-vars */ | |
switch (n) { | |
case 0: | |
return function () { | |
return fn.apply(this, arguments) | |
} | |
case 1: | |
return function (a0) { | |
return fn.apply(this, arguments) | |
} | |
case 2: | |
return function (a0, a1) { | |
return fn.apply(this, arguments) | |
} | |
case 3: | |
return function (a0, a1, a2) { | |
return fn.apply(this, arguments) | |
} | |
case 4: | |
return function (a0, a1, a2, a3) { | |
return fn.apply(this, arguments) | |
} | |
case 5: | |
return function (a0, a1, a2, a3, a4) { | |
return fn.apply(this, arguments) | |
} | |
case 6: | |
return function (a0, a1, a2, a3, a4, a5) { | |
return fn.apply(this, arguments) | |
} | |
case 7: | |
return function (a0, a1, a2, a3, a4, a5, a6) { | |
return fn.apply(this, arguments) | |
} | |
case 8: | |
return function (a0, a1, a2, a3, a4, a5, a6, a7) { | |
return fn.apply(this, arguments) | |
} | |
case 9: | |
return function (a0, a1, a2, a3, a4, a5, a6, a7, a8) { | |
return fn.apply(this, arguments) | |
} | |
case 10: | |
return function (a0, a1, a2, a3, a4, a5, a6, a7, a8, a9) { | |
return fn.apply(this, arguments) | |
} | |
default: | |
throw new Error( | |
'First argument to _arity must be a non-negative integer no greater than ten', | |
) | |
} | |
} | |
/** | |
* Internal curryN function. | |
* | |
* @private | |
* @category Function | |
* @param {Number} length The arity of the curried function. | |
* @param {Array} received An array of arguments received thus far. | |
* @param {Function} fn The function to curry. | |
* @return {Function} The curried function. | |
*/ | |
function _curryN(length, received, fn) { | |
return function () { | |
var combined = [] | |
var argsIdx = 0 | |
var left = length | |
var combinedIdx = 0 | |
var hasPlaceholder = false | |
while (combinedIdx < received.length || argsIdx < arguments.length) { | |
var result | |
if ( | |
combinedIdx < received.length && | |
(!_isPlaceholder(received[combinedIdx]) || argsIdx >= arguments.length) | |
) { | |
result = received[combinedIdx] | |
} else { | |
result = arguments[argsIdx] | |
argsIdx += 1 | |
} | |
combined[combinedIdx] = result | |
if (!_isPlaceholder(result)) { | |
left -= 1 | |
} else { | |
hasPlaceholder = true | |
} | |
combinedIdx += 1 | |
} | |
return !hasPlaceholder && left <= 0 | |
? fn.apply(this, combined) | |
: _arity(Math.max(0, left), _curryN(length, combined, fn)) | |
} | |
} | |
/** | |
* Returns a curried equivalent of the provided function, with the specified | |
* arity. The curried function has two unusual capabilities. First, its | |
* arguments needn't be provided one at a time. If `g` is `R.curryN(3, f)`, the | |
* following are equivalent: | |
* | |
* - `g(1)(2)(3)` | |
* - `g(1)(2, 3)` | |
* - `g(1, 2)(3)` | |
* - `g(1, 2, 3)` | |
* | |
* Secondly, the special placeholder value [`R.__`](#__) may be used to specify | |
* "gaps", allowing partial application of any combination of arguments, | |
* regardless of their positions. If `g` is as above and `_` is [`R.__`](#__), | |
* the following are equivalent: | |
* | |
* - `g(1, 2, 3)` | |
* - `g(_, 2, 3)(1)` | |
* - `g(_, _, 3)(1)(2)` | |
* - `g(_, _, 3)(1, 2)` | |
* - `g(_, 2)(1)(3)` | |
* - `g(_, 2)(1, 3)` | |
* - `g(_, 2)(_, 3)(1)` | |
* | |
* @func | |
* @memberOf R | |
* @since v0.5.0 | |
* @category Function | |
* @sig Number -> (* -> a) -> (* -> a) | |
* @param {Number} length The arity for the returned function. | |
* @param {Function} fn The function to curry. | |
* @return {Function} A new, curried function. | |
* @see R.curry | |
* @example | |
* | |
* const sumArgs = (...args) => R.sum(args); | |
* | |
* const curriedAddFourNumbers = R.curryN(4, sumArgs); | |
* const f = curriedAddFourNumbers(1, 2); | |
* const g = f(3); | |
* g(4); //=> 10 | |
*/ | |
var curryN = | |
/*#__PURE__*/ | |
_curry2(function curryN(length, fn) { | |
if (length === 1) { | |
return _curry1(fn) | |
} | |
return _arity(length, _curryN(length, [], fn)) | |
}) | |
/** | |
* Optimized internal three-arity curry function. | |
* | |
* @private | |
* @category Function | |
* @param {Function} fn The function to curry. | |
* @return {Function} The curried function. | |
*/ | |
function _curry3(fn) { | |
return function f3(a, b, c) { | |
switch (arguments.length) { | |
case 0: | |
return f3 | |
case 1: | |
return _isPlaceholder(a) | |
? f3 | |
: _curry2(function (_b, _c) { | |
return fn(a, _b, _c) | |
}) | |
case 2: | |
return _isPlaceholder(a) && _isPlaceholder(b) | |
? f3 | |
: _isPlaceholder(a) | |
? _curry2(function (_a, _c) { | |
return fn(_a, b, _c) | |
}) | |
: _isPlaceholder(b) | |
? _curry2(function (_b, _c) { | |
return fn(a, _b, _c) | |
}) | |
: _curry1(function (_c) { | |
return fn(a, b, _c) | |
}) | |
default: | |
return _isPlaceholder(a) && _isPlaceholder(b) && _isPlaceholder(c) | |
? f3 | |
: _isPlaceholder(a) && _isPlaceholder(b) | |
? _curry2(function (_a, _b) { | |
return fn(_a, _b, c) | |
}) | |
: _isPlaceholder(a) && _isPlaceholder(c) | |
? _curry2(function (_a, _c) { | |
return fn(_a, b, _c) | |
}) | |
: _isPlaceholder(b) && _isPlaceholder(c) | |
? _curry2(function (_b, _c) { | |
return fn(a, _b, _c) | |
}) | |
: _isPlaceholder(a) | |
? _curry1(function (_a) { | |
return fn(_a, b, c) | |
}) | |
: _isPlaceholder(b) | |
? _curry1(function (_b) { | |
return fn(a, _b, c) | |
}) | |
: _isPlaceholder(c) | |
? _curry1(function (_c) { | |
return fn(a, b, _c) | |
}) | |
: fn(a, b, c) | |
} | |
} | |
} | |
/** | |
* Tests whether or not an object is an array. | |
* | |
* @private | |
* @param {*} val The object to test. | |
* @return {Boolean} `true` if `val` is an array, `false` otherwise. | |
* @example | |
* | |
* _isArray([]); //=> true | |
* _isArray(null); //=> false | |
* _isArray({}); //=> false | |
*/ | |
var _isArray = | |
Array.isArray || | |
function _isArray(val) { | |
return ( | |
val != null && | |
val.length >= 0 && | |
Object.prototype.toString.call(val) === '[object Array]' | |
) | |
} | |
function _isTransformer(obj) { | |
return obj != null && typeof obj['@@transducer/step'] === 'function' | |
} | |
/** | |
* Returns a function that dispatches with different strategies based on the | |
* object in list position (last argument). If it is an array, executes [fn]. | |
* Otherwise, if it has a function with one of the given method names, it will | |
* execute that function (functor case). Otherwise, if it is a transformer, | |
* uses transducer created by [transducerCreator] to return a new transformer | |
* (transducer case). | |
* Otherwise, it will default to executing [fn]. | |
* | |
* @private | |
* @param {Array} methodNames properties to check for a custom implementation | |
* @param {Function} transducerCreator transducer factory if object is transformer | |
* @param {Function} fn default ramda implementation | |
* @return {Function} A function that dispatches on object in list position | |
*/ | |
function _dispatchable(methodNames, transducerCreator, fn) { | |
return function () { | |
if (arguments.length === 0) { | |
return fn() | |
} | |
var obj = arguments[arguments.length - 1] | |
if (!_isArray(obj)) { | |
var idx = 0 | |
while (idx < methodNames.length) { | |
if (typeof obj[methodNames[idx]] === 'function') { | |
return obj[methodNames[idx]].apply( | |
obj, | |
Array.prototype.slice.call(arguments, 0, -1), | |
) | |
} | |
idx += 1 | |
} | |
if (_isTransformer(obj)) { | |
var transducer = transducerCreator.apply( | |
null, | |
Array.prototype.slice.call(arguments, 0, -1), | |
) | |
return transducer(obj) | |
} | |
} | |
return fn.apply(this, arguments) | |
} | |
} | |
var _xfBase = { | |
init: function () { | |
return this.xf['@@transducer/init']() | |
}, | |
result: function (result) { | |
return this.xf['@@transducer/result'](result) | |
}, | |
} | |
function _arrayFromIterator(iter) { | |
var list = [] | |
var next | |
while (!(next = iter.next()).done) { | |
list.push(next.value) | |
} | |
return list | |
} | |
function _includesWith(pred, x, list) { | |
var idx = 0 | |
var len = list.length | |
while (idx < len) { | |
if (pred(x, list[idx])) { | |
return true | |
} | |
idx += 1 | |
} | |
return false | |
} | |
function _functionName(f) { | |
// String(x => x) evaluates to "x => x", so the pattern may not match. | |
var match = String(f).match(/^function (\w*)/) | |
return match == null ? '' : match[1] | |
} | |
function _has(prop, obj) { | |
return Object.prototype.hasOwnProperty.call(obj, prop) | |
} | |
// Based on https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/is | |
function _objectIs(a, b) { | |
// SameValue algorithm | |
if (a === b) { | |
// Steps 1-5, 7-10 | |
// Steps 6.b-6.e: +0 != -0 | |
return a !== 0 || 1 / a === 1 / b | |
} else { | |
// Step 6.a: NaN == NaN | |
return a !== a && b !== b | |
} | |
} | |
var _objectIs$1 = typeof Object.is === 'function' ? Object.is : _objectIs | |
var toString = Object.prototype.toString | |
var _isArguments = | |
/*#__PURE__*/ | |
(function () { | |
return toString.call(arguments) === '[object Arguments]' | |
? function _isArguments(x) { | |
return toString.call(x) === '[object Arguments]' | |
} | |
: function _isArguments(x) { | |
return _has('callee', x) | |
} | |
})() | |
var hasEnumBug = !( | |
/*#__PURE__*/ | |
{ | |
toString: null, | |
}.propertyIsEnumerable('toString') | |
) | |
var nonEnumerableProps = [ | |
'constructor', | |
'valueOf', | |
'isPrototypeOf', | |
'toString', | |
'propertyIsEnumerable', | |
'hasOwnProperty', | |
'toLocaleString', | |
] // Safari bug | |
var hasArgsEnumBug = | |
/*#__PURE__*/ | |
(function () { | |
return arguments.propertyIsEnumerable('length') | |
})() | |
var contains = function contains(list, item) { | |
var idx = 0 | |
while (idx < list.length) { | |
if (list[idx] === item) { | |
return true | |
} | |
idx += 1 | |
} | |
return false | |
} | |
/** | |
* Returns a list containing the names of all the enumerable own properties of | |
* the supplied object. | |
* Note that the order of the output array is not guaranteed to be consistent | |
* across different JS platforms. | |
* | |
* @func | |
* @memberOf R | |
* @since v0.1.0 | |
* @category Object | |
* @sig {k: v} -> [k] | |
* @param {Object} obj The object to extract properties from | |
* @return {Array} An array of the object's own properties. | |
* @see R.keysIn, R.values, R.toPairs | |
* @example | |
* | |
* R.keys({a: 1, b: 2, c: 3}); //=> ['a', 'b', 'c'] | |
*/ | |
var keys = | |
typeof Object.keys === 'function' && !hasArgsEnumBug | |
? /*#__PURE__*/ | |
_curry1(function keys(obj) { | |
return Object(obj) !== obj ? [] : Object.keys(obj) | |
}) | |
: /*#__PURE__*/ | |
_curry1(function keys(obj) { | |
if (Object(obj) !== obj) { | |
return [] | |
} | |
var prop, nIdx | |
var ks = [] | |
var checkArgsLength = hasArgsEnumBug && _isArguments(obj) | |
for (prop in obj) { | |
if (_has(prop, obj) && (!checkArgsLength || prop !== 'length')) { | |
ks[ks.length] = prop | |
} | |
} | |
if (hasEnumBug) { | |
nIdx = nonEnumerableProps.length - 1 | |
while (nIdx >= 0) { | |
prop = nonEnumerableProps[nIdx] | |
if (_has(prop, obj) && !contains(ks, prop)) { | |
ks[ks.length] = prop | |
} | |
nIdx -= 1 | |
} | |
} | |
return ks | |
}) | |
/** | |
* Gives a single-word string description of the (native) type of a value, | |
* returning such answers as 'Object', 'Number', 'Array', or 'Null'. Does not | |
* attempt to distinguish user Object types any further, reporting them all as | |
* 'Object'. | |
* | |
* @func | |
* @memberOf R | |
* @since v0.8.0 | |
* @category Type | |
* @sig * -> String | |
* @param {*} val The value to test | |
* @return {String} | |
* @example | |
* | |
* R.type({}); //=> "Object" | |
* R.type(1); //=> "Number" | |
* R.type(false); //=> "Boolean" | |
* R.type('s'); //=> "String" | |
* R.type(null); //=> "Null" | |
* R.type([]); //=> "Array" | |
* R.type(/[A-z]/); //=> "RegExp" | |
* R.type(() => {}); //=> "Function" | |
* R.type(async () => {}); //=> "AsyncFunction" | |
* R.type(undefined); //=> "Undefined" | |
*/ | |
var type = | |
/*#__PURE__*/ | |
_curry1(function type(val) { | |
return val === null | |
? 'Null' | |
: val === undefined | |
? 'Undefined' | |
: Object.prototype.toString.call(val).slice(8, -1) | |
}) | |
/** | |
* private _uniqContentEquals function. | |
* That function is checking equality of 2 iterator contents with 2 assumptions | |
* - iterators lengths are the same | |
* - iterators values are unique | |
* | |
* false-positive result will be returned for comparison of, e.g. | |
* - [1,2,3] and [1,2,3,4] | |
* - [1,1,1] and [1,2,3] | |
* */ | |
function _uniqContentEquals(aIterator, bIterator, stackA, stackB) { | |
var a = _arrayFromIterator(aIterator) | |
var b = _arrayFromIterator(bIterator) | |
function eq(_a, _b) { | |
return _equals(_a, _b, stackA.slice(), stackB.slice()) | |
} // if *a* array contains any element that is not included in *b* | |
return !_includesWith( | |
function (b, aItem) { | |
return !_includesWith(eq, aItem, b) | |
}, | |
b, | |
a, | |
) | |
} | |
function _equals(a, b, stackA, stackB) { | |
if (_objectIs$1(a, b)) { | |
return true | |
} | |
var typeA = type(a) | |
if (typeA !== type(b)) { | |
return false | |
} | |
if ( | |
typeof a['fantasy-land/equals'] === 'function' || | |
typeof b['fantasy-land/equals'] === 'function' | |
) { | |
return ( | |
typeof a['fantasy-land/equals'] === 'function' && | |
a['fantasy-land/equals'](b) && | |
typeof b['fantasy-land/equals'] === 'function' && | |
b['fantasy-land/equals'](a) | |
) | |
} | |
if (typeof a.equals === 'function' || typeof b.equals === 'function') { | |
return ( | |
typeof a.equals === 'function' && | |
a.equals(b) && | |
typeof b.equals === 'function' && | |
b.equals(a) | |
) | |
} | |
switch (typeA) { | |
case 'Arguments': | |
case 'Array': | |
case 'Object': | |
if ( | |
typeof a.constructor === 'function' && | |
_functionName(a.constructor) === 'Promise' | |
) { | |
return a === b | |
} | |
break | |
case 'Boolean': | |
case 'Number': | |
case 'String': | |
if (!(typeof a === typeof b && _objectIs$1(a.valueOf(), b.valueOf()))) { | |
return false | |
} | |
break | |
case 'Date': | |
if (!_objectIs$1(a.valueOf(), b.valueOf())) { | |
return false | |
} | |
break | |
case 'Error': | |
return a.name === b.name && a.message === b.message | |
case 'RegExp': | |
if ( | |
!( | |
a.source === b.source && | |
a.global === b.global && | |
a.ignoreCase === b.ignoreCase && | |
a.multiline === b.multiline && | |
a.sticky === b.sticky && | |
a.unicode === b.unicode | |
) | |
) { | |
return false | |
} | |
break | |
} | |
var idx = stackA.length - 1 | |
while (idx >= 0) { | |
if (stackA[idx] === a) { | |
return stackB[idx] === b | |
} | |
idx -= 1 | |
} | |
switch (typeA) { | |
case 'Map': | |
if (a.size !== b.size) { | |
return false | |
} | |
return _uniqContentEquals( | |
a.entries(), | |
b.entries(), | |
stackA.concat([a]), | |
stackB.concat([b]), | |
) | |
case 'Set': | |
if (a.size !== b.size) { | |
return false | |
} | |
return _uniqContentEquals( | |
a.values(), | |
b.values(), | |
stackA.concat([a]), | |
stackB.concat([b]), | |
) | |
case 'Arguments': | |
case 'Array': | |
case 'Object': | |
case 'Boolean': | |
case 'Number': | |
case 'String': | |
case 'Date': | |
case 'Error': | |
case 'RegExp': | |
case 'Int8Array': | |
case 'Uint8Array': | |
case 'Uint8ClampedArray': | |
case 'Int16Array': | |
case 'Uint16Array': | |
case 'Int32Array': | |
case 'Uint32Array': | |
case 'Float32Array': | |
case 'Float64Array': | |
case 'ArrayBuffer': | |
break | |
default: | |
// Values of other types are only equal if identical. | |
return false | |
} | |
var keysA = keys(a) | |
if (keysA.length !== keys(b).length) { | |
return false | |
} | |
var extendedStackA = stackA.concat([a]) | |
var extendedStackB = stackB.concat([b]) | |
idx = keysA.length - 1 | |
while (idx >= 0) { | |
var key = keysA[idx] | |
if ( | |
!(_has(key, b) && _equals(b[key], a[key], extendedStackA, extendedStackB)) | |
) { | |
return false | |
} | |
idx -= 1 | |
} | |
return true | |
} | |
/** | |
* Returns `true` if its arguments are equivalent, `false` otherwise. Handles | |
* cyclical data structures. | |
* | |
* Dispatches symmetrically to the `equals` methods of both arguments, if | |
* present. | |
* | |
* @func | |
* @memberOf R | |
* @since v0.15.0 | |
* @category Relation | |
* @sig a -> b -> Boolean | |
* @param {*} a | |
* @param {*} b | |
* @return {Boolean} | |
* @example | |
* | |
* R.equals(1, 1); //=> true | |
* R.equals(1, '1'); //=> false | |
* R.equals([1, 2, 3], [1, 2, 3]); //=> true | |
* | |
* const a = {}; a.v = a; | |
* const b = {}; b.v = b; | |
* R.equals(a, b); //=> true | |
*/ | |
var equals = | |
/*#__PURE__*/ | |
_curry2(function equals(a, b) { | |
return _equals(a, b, [], []) | |
}) | |
function _indexOf(list, a, idx) { | |
var inf, item // Array.prototype.indexOf doesn't exist below IE9 | |
if (typeof list.indexOf === 'function') { | |
switch (typeof a) { | |
case 'number': | |
if (a === 0) { | |
// manually crawl the list to distinguish between +0 and -0 | |
inf = 1 / a | |
while (idx < list.length) { | |
item = list[idx] | |
if (item === 0 && 1 / item === inf) { | |
return idx | |
} | |
idx += 1 | |
} | |
return -1 | |
} else if (a !== a) { | |
// NaN | |
while (idx < list.length) { | |
item = list[idx] | |
if (typeof item === 'number' && item !== item) { | |
return idx | |
} | |
idx += 1 | |
} | |
return -1 | |
} // non-zero numbers can utilise Set | |
return list.indexOf(a, idx) | |
// all these types can utilise Set | |
case 'string': | |
case 'boolean': | |
case 'function': | |
case 'undefined': | |
return list.indexOf(a, idx) | |
case 'object': | |
if (a === null) { | |
// null can utilise Set | |
return list.indexOf(a, idx) | |
} | |
} | |
} // anything else not covered above, defer to R.equals | |
while (idx < list.length) { | |
if (equals(list[idx], a)) { | |
return idx | |
} | |
idx += 1 | |
} | |
return -1 | |
} | |
function _includes(a, list) { | |
return _indexOf(list, a, 0) >= 0 | |
} | |
function _map(fn, functor) { | |
var idx = 0 | |
var len = functor.length | |
var result = Array(len) | |
while (idx < len) { | |
result[idx] = fn(functor[idx]) | |
idx += 1 | |
} | |
return result | |
} | |
function _arrayReduce(reducer, acc, list) { | |
var index = 0 | |
var length = list.length | |
while (index < length) { | |
acc = reducer(acc, list[index]) | |
index += 1 | |
} | |
return acc | |
} | |
function _filter(fn, list) { | |
var idx = 0 | |
var len = list.length | |
var result = [] | |
while (idx < len) { | |
if (fn(list[idx])) { | |
result[result.length] = list[idx] | |
} | |
idx += 1 | |
} | |
return result | |
} | |
function _isObject(x) { | |
return Object.prototype.toString.call(x) === '[object Object]' | |
} | |
var XFilter = | |
/*#__PURE__*/ | |
(function () { | |
function XFilter(f, xf) { | |
this.xf = xf | |
this.f = f | |
} | |
XFilter.prototype['@@transducer/init'] = _xfBase.init | |
XFilter.prototype['@@transducer/result'] = _xfBase.result | |
XFilter.prototype['@@transducer/step'] = function (result, input) { | |
return this.f(input) | |
? this.xf['@@transducer/step'](result, input) | |
: result | |
} | |
return XFilter | |
})() | |
function _xfilter(f) { | |
return function (xf) { | |
return new XFilter(f, xf) | |
} | |
} | |
/** | |
* Takes a predicate and a `Filterable`, and returns a new filterable of the | |
* same type containing the members of the given filterable which satisfy the | |
* given predicate. Filterable objects include plain objects or any object | |
* that has a filter method such as `Array`. | |
* | |
* Dispatches to the `filter` method of the second argument, if present. | |
* | |
* Acts as a transducer if a transformer is given in list position. | |
* | |
* @func | |
* @memberOf R | |
* @since v0.1.0 | |
* @category List | |
* @category Object | |
* @sig Filterable f => (a -> Boolean) -> f a -> f a | |
* @param {Function} pred | |
* @param {Array} filterable | |
* @return {Array} Filterable | |
* @see R.reject, R.transduce, R.addIndex | |
* @example | |
* | |
* const isEven = n => n % 2 === 0; | |
* | |
* R.filter(isEven, [1, 2, 3, 4]); //=> [2, 4] | |
* | |
* R.filter(isEven, {a: 1, b: 2, c: 3, d: 4}); //=> {b: 2, d: 4} | |
*/ | |
var filter = | |
/*#__PURE__*/ | |
_curry2( | |
/*#__PURE__*/ | |
_dispatchable( | |
['fantasy-land/filter', 'filter'], | |
_xfilter, | |
function (pred, filterable) { | |
return _isObject(filterable) | |
? _arrayReduce( | |
function (acc, key) { | |
if (pred(filterable[key])) { | |
acc[key] = filterable[key] | |
} | |
return acc | |
}, | |
{}, | |
keys(filterable), | |
) // else | |
: _filter(pred, filterable) | |
}, | |
), | |
) | |
var XMap = | |
/*#__PURE__*/ | |
(function () { | |
function XMap(f, xf) { | |
this.xf = xf | |
this.f = f | |
} | |
XMap.prototype['@@transducer/init'] = _xfBase.init | |
XMap.prototype['@@transducer/result'] = _xfBase.result | |
XMap.prototype['@@transducer/step'] = function (result, input) { | |
return this.xf['@@transducer/step'](result, this.f(input)) | |
} | |
return XMap | |
})() | |
var _xmap = function _xmap(f) { | |
return function (xf) { | |
return new XMap(f, xf) | |
} | |
} | |
var _xmap$1 = _xmap | |
/** | |
* Takes a function and | |
* a [functor](https://github.com/fantasyland/fantasy-land#functor), | |
* applies the function to each of the functor's values, and returns | |
* a functor of the same shape. | |
* | |
* Ramda provides suitable `map` implementations for `Array` and `Object`, | |
* so this function may be applied to `[1, 2, 3]` or `{x: 1, y: 2, z: 3}`. | |
* | |
* Dispatches to the `map` method of the second argument, if present. | |
* | |
* Acts as a transducer if a transformer is given in list position. | |
* | |
* Also treats functions as functors and will compose them together. | |
* | |
* @func | |
* @memberOf R | |
* @since v0.1.0 | |
* @category List | |
* @sig Functor f => (a -> b) -> f a -> f b | |
* @param {Function} fn The function to be called on every element of the input `list`. | |
* @param {Array} list The list to be iterated over. | |
* @return {Array} The new list. | |
* @see R.transduce, R.addIndex, R.pluck, R.project | |
* @example | |
* | |
* const double = x => x * 2; | |
* | |
* R.map(double, [1, 2, 3]); //=> [2, 4, 6] | |
* | |
* R.map(double, {x: 1, y: 2, z: 3}); //=> {x: 2, y: 4, z: 6} | |
* @symb R.map(f, [a, b]) = [f(a), f(b)] | |
* @symb R.map(f, { x: a, y: b }) = { x: f(a), y: f(b) } | |
* @symb R.map(f, functor_o) = functor_o.map(f) | |
*/ | |
var map = | |
/*#__PURE__*/ | |
_curry2( | |
/*#__PURE__*/ | |
_dispatchable( | |
['fantasy-land/map', 'map'], | |
_xmap$1, | |
function map(fn, functor) { | |
switch (Object.prototype.toString.call(functor)) { | |
case '[object Function]': | |
return curryN(functor.length, function () { | |
return fn.call(this, functor.apply(this, arguments)) | |
}) | |
case '[object Object]': | |
return _arrayReduce( | |
function (acc, key) { | |
acc[key] = fn(functor[key]) | |
return acc | |
}, | |
{}, | |
keys(functor), | |
) | |
default: | |
return _map(fn, functor) | |
} | |
}, | |
), | |
) | |
function _isString(x) { | |
return Object.prototype.toString.call(x) === '[object String]' | |
} | |
/** | |
* Tests whether or not an object is similar to an array. | |
* | |
* @private | |
* @category Type | |
* @category List | |
* @sig * -> Boolean | |
* @param {*} x The object to test. | |
* @return {Boolean} `true` if `x` has a numeric length property and extreme indices defined; `false` otherwise. | |
* @example | |
* | |
* _isArrayLike([]); //=> true | |
* _isArrayLike(true); //=> false | |
* _isArrayLike({}); //=> false | |
* _isArrayLike({length: 10}); //=> false | |
* _isArrayLike({0: 'zero', 9: 'nine', length: 10}); //=> true | |
* _isArrayLike({nodeType: 1, length: 1}) // => false | |
*/ | |
var _isArrayLike = | |
/*#__PURE__*/ | |
_curry1(function isArrayLike(x) { | |
if (_isArray(x)) { | |
return true | |
} | |
if (!x) { | |
return false | |
} | |
if (typeof x !== 'object') { | |
return false | |
} | |
if (_isString(x)) { | |
return false | |
} | |
if (x.length === 0) { | |
return true | |
} | |
if (x.length > 0) { | |
return x.hasOwnProperty(0) && x.hasOwnProperty(x.length - 1) | |
} | |
return false | |
}) | |
var symIterator = typeof Symbol !== 'undefined' ? Symbol.iterator : '@@iterator' | |
function _createReduce(arrayReduce, methodReduce, iterableReduce) { | |
return function _reduce(xf, acc, list) { | |
if (_isArrayLike(list)) { | |
return arrayReduce(xf, acc, list) | |
} | |
if (list == null) { | |
return acc | |
} | |
if (typeof list['fantasy-land/reduce'] === 'function') { | |
return methodReduce(xf, acc, list, 'fantasy-land/reduce') | |
} | |
if (list[symIterator] != null) { | |
return iterableReduce(xf, acc, list[symIterator]()) | |
} | |
if (typeof list.next === 'function') { | |
return iterableReduce(xf, acc, list) | |
} | |
if (typeof list.reduce === 'function') { | |
return methodReduce(xf, acc, list, 'reduce') | |
} | |
throw new TypeError('reduce: list must be array or iterable') | |
} | |
} | |
function _xArrayReduce(xf, acc, list) { | |
var idx = 0 | |
var len = list.length | |
while (idx < len) { | |
acc = xf['@@transducer/step'](acc, list[idx]) | |
if (acc && acc['@@transducer/reduced']) { | |
acc = acc['@@transducer/value'] | |
break | |
} | |
idx += 1 | |
} | |
return xf['@@transducer/result'](acc) | |
} | |
/** | |
* Creates a function that is bound to a context. | |
* Note: `R.bind` does not provide the additional argument-binding capabilities of | |
* [Function.prototype.bind](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/bind). | |
* | |
* @func | |
* @memberOf R | |
* @since v0.6.0 | |
* @category Function | |
* @category Object | |
* @sig (* -> *) -> {*} -> (* -> *) | |
* @param {Function} fn The function to bind to context | |
* @param {Object} thisObj The context to bind `fn` to | |
* @return {Function} A function that will execute in the context of `thisObj`. | |
* @see R.partial | |
* @example | |
* | |
* const log = R.bind(console.log, console); | |
* R.pipe(R.assoc('a', 2), R.tap(log), R.assoc('a', 3))({a: 1}); //=> {a: 3} | |
* // logs {a: 2} | |
* @symb R.bind(f, o)(a, b) = f.call(o, a, b) | |
*/ | |
var bind = | |
/*#__PURE__*/ | |
_curry2(function bind(fn, thisObj) { | |
return _arity(fn.length, function () { | |
return fn.apply(thisObj, arguments) | |
}) | |
}) | |
function _xIterableReduce(xf, acc, iter) { | |
var step = iter.next() | |
while (!step.done) { | |
acc = xf['@@transducer/step'](acc, step.value) | |
if (acc && acc['@@transducer/reduced']) { | |
acc = acc['@@transducer/value'] | |
break | |
} | |
step = iter.next() | |
} | |
return xf['@@transducer/result'](acc) | |
} | |
function _xMethodReduce(xf, acc, obj, methodName) { | |
return xf['@@transducer/result']( | |
obj[methodName](bind(xf['@@transducer/step'], xf), acc), | |
) | |
} | |
var _xReduce = | |
/*#__PURE__*/ | |
_createReduce(_xArrayReduce, _xMethodReduce, _xIterableReduce) | |
var XWrap = | |
/*#__PURE__*/ | |
(function () { | |
function XWrap(fn) { | |
this.f = fn | |
} | |
XWrap.prototype['@@transducer/init'] = function () { | |
throw new Error('init not implemented on XWrap') | |
} | |
XWrap.prototype['@@transducer/result'] = function (acc) { | |
return acc | |
} | |
XWrap.prototype['@@transducer/step'] = function (acc, x) { | |
return this.f(acc, x) | |
} | |
return XWrap | |
})() | |
function _xwrap(fn) { | |
return new XWrap(fn) | |
} | |
/** | |
* Returns a single item by iterating through the list, successively calling | |
* the iterator function and passing it an accumulator value and the current | |
* value from the array, and then passing the result to the next call. | |
* | |
* The iterator function receives two values: *(acc, value)*. It may use | |
* [`R.reduced`](#reduced) to shortcut the iteration. | |
* | |
* The arguments' order of [`reduceRight`](#reduceRight)'s iterator function | |
* is *(value, acc)*. | |
* | |
* Note: `R.reduce` does not skip deleted or unassigned indices (sparse | |
* arrays), unlike the native `Array.prototype.reduce` method. For more details | |
* on this behavior, see: | |
* https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/reduce#Description | |
* | |
* Be cautious of mutating and returning the accumulator. If you reuse it across | |
* invocations, it will continue to accumulate onto the same value. The general | |
* recommendation is to always return a new value. If you can't do so for | |
* performance reasons, then be sure to reinitialize the accumulator on each | |
* invocation. | |
* | |
* Dispatches to the `reduce` method of the third argument, if present. When | |
* doing so, it is up to the user to handle the [`R.reduced`](#reduced) | |
* shortcuting, as this is not implemented by `reduce`. | |
* | |
* @func | |
* @memberOf R | |
* @since v0.1.0 | |
* @category List | |
* @sig ((a, b) -> a) -> a -> [b] -> a | |
* @param {Function} fn The iterator function. Receives two values, the accumulator and the | |
* current element from the array. | |
* @param {*} acc The accumulator value. | |
* @param {Array} list The list to iterate over. | |
* @return {*} The final, accumulated value. | |
* @see R.reduced, R.addIndex, R.reduceRight | |
* @example | |
* | |
* R.reduce(R.subtract, 0, [1, 2, 3, 4]) // => ((((0 - 1) - 2) - 3) - 4) = -10 | |
* // - -10 | |
* // / \ / \ | |
* // - 4 -6 4 | |
* // / \ / \ | |
* // - 3 ==> -3 3 | |
* // / \ / \ | |
* // - 2 -1 2 | |
* // / \ / \ | |
* // 0 1 0 1 | |
* | |
* @symb R.reduce(f, a, [b, c, d]) = f(f(f(a, b), c), d) | |
*/ | |
var reduce = | |
/*#__PURE__*/ | |
_curry3(function (xf, acc, list) { | |
return _xReduce(typeof xf === 'function' ? _xwrap(xf) : xf, acc, list) | |
}) | |
/** | |
* `_makeFlat` is a helper function that returns a one-level or fully recursive | |
* function based on the flag passed in. | |
* | |
* @private | |
*/ | |
function _makeFlat(recursive) { | |
return function flatt(list) { | |
var value, jlen, j | |
var result = [] | |
var idx = 0 | |
var ilen = list.length | |
while (idx < ilen) { | |
if (_isArrayLike(list[idx])) { | |
value = recursive ? flatt(list[idx]) : list[idx] | |
j = 0 | |
jlen = value.length | |
while (j < jlen) { | |
result[result.length] = value[j] | |
j += 1 | |
} | |
} else { | |
result[result.length] = list[idx] | |
} | |
idx += 1 | |
} | |
return result | |
} | |
} | |
function _pipe(f, g) { | |
return function () { | |
return g.call(this, f.apply(this, arguments)) | |
} | |
} | |
/** | |
* This checks whether a function has a [methodname] function. If it isn't an | |
* array it will execute that function otherwise it will default to the ramda | |
* implementation. | |
* | |
* @private | |
* @param {Function} fn ramda implementation | |
* @param {String} methodname property to check for a custom implementation | |
* @return {Object} Whatever the return value of the method is. | |
*/ | |
function _checkForMethod(methodname, fn) { | |
return function () { | |
var length = arguments.length | |
if (length === 0) { | |
return fn() | |
} | |
var obj = arguments[length - 1] | |
return _isArray(obj) || typeof obj[methodname] !== 'function' | |
? fn.apply(this, arguments) | |
: obj[methodname].apply( | |
obj, | |
Array.prototype.slice.call(arguments, 0, length - 1), | |
) | |
} | |
} | |
/** | |
* Returns the elements of the given list or string (or object with a `slice` | |
* method) from `fromIndex` (inclusive) to `toIndex` (exclusive). | |
* | |
* Dispatches to the `slice` method of the third argument, if present. | |
* | |
* @func | |
* @memberOf R | |
* @since v0.1.4 | |
* @category List | |
* @sig Number -> Number -> [a] -> [a] | |
* @sig Number -> Number -> String -> String | |
* @param {Number} fromIndex The start index (inclusive). | |
* @param {Number} toIndex The end index (exclusive). | |
* @param {*} list | |
* @return {*} | |
* @example | |
* | |
* R.slice(1, 3, ['a', 'b', 'c', 'd']); //=> ['b', 'c'] | |
* R.slice(1, Infinity, ['a', 'b', 'c', 'd']); //=> ['b', 'c', 'd'] | |
* R.slice(0, -1, ['a', 'b', 'c', 'd']); //=> ['a', 'b', 'c'] | |
* R.slice(-3, -1, ['a', 'b', 'c', 'd']); //=> ['b', 'c'] | |
* R.slice(0, 3, 'ramda'); //=> 'ram' | |
*/ | |
var slice = | |
/*#__PURE__*/ | |
_curry3( | |
/*#__PURE__*/ | |
_checkForMethod('slice', function slice(fromIndex, toIndex, list) { | |
return Array.prototype.slice.call(list, fromIndex, toIndex) | |
}), | |
) | |
/** | |
* Returns all but the first element of the given list or string (or object | |
* with a `tail` method). | |
* | |
* Dispatches to the `slice` method of the first argument, if present. | |
* | |
* @func | |
* @memberOf R | |
* @since v0.1.0 | |
* @category List | |
* @sig [a] -> [a] | |
* @sig String -> String | |
* @param {*} list | |
* @return {*} | |
* @see R.head, R.init, R.last | |
* @example | |
* | |
* R.tail([1, 2, 3]); //=> [2, 3] | |
* R.tail([1, 2]); //=> [2] | |
* R.tail([1]); //=> [] | |
* R.tail([]); //=> [] | |
* | |
* R.tail('abc'); //=> 'bc' | |
* R.tail('ab'); //=> 'b' | |
* R.tail('a'); //=> '' | |
* R.tail(''); //=> '' | |
*/ | |
var tail = | |
/*#__PURE__*/ | |
_curry1( | |
/*#__PURE__*/ | |
_checkForMethod( | |
'tail', | |
/*#__PURE__*/ | |
slice(1, Infinity), | |
), | |
) | |
/** | |
* Performs left-to-right function composition. The first argument may have | |
* any arity; the remaining arguments must be unary. | |
* | |
* In some libraries this function is named `sequence`. | |
* | |
* **Note:** The result of pipe is not automatically curried. | |
* | |
* @func | |
* @memberOf R | |
* @since v0.1.0 | |
* @category Function | |
* @sig (((a, b, ..., n) -> o), (o -> p), ..., (x -> y), (y -> z)) -> ((a, b, ..., n) -> z) | |
* @param {...Function} functions | |
* @return {Function} | |
* @see R.compose | |
* @example | |
* | |
* const f = R.pipe(Math.pow, R.negate, R.inc); | |
* | |
* f(3, 4); // -(3^4) + 1 | |
* @symb R.pipe(f, g, h)(a, b) = h(g(f(a, b))) | |
* @symb R.pipe(f, g, h)(a)(b) = h(g(f(a)))(b) | |
*/ | |
function pipe() { | |
if (arguments.length === 0) { | |
throw new Error('pipe requires at least one argument') | |
} | |
return _arity( | |
arguments[0].length, | |
reduce(_pipe, arguments[0], tail(arguments)), | |
) | |
} | |
function _identity(x) { | |
return x | |
} | |
/** | |
* A function that does nothing but return the parameter supplied to it. Good | |
* as a default or placeholder function. | |
* | |
* @func | |
* @memberOf R | |
* @since v0.1.0 | |
* @category Function | |
* @sig a -> a | |
* @param {*} x The value to return. | |
* @return {*} The input value, `x`. | |
* @example | |
* | |
* R.identity(1); //=> 1 | |
* | |
* const obj = {}; | |
* R.identity(obj) === obj; //=> true | |
* @symb R.identity(a) = a | |
*/ | |
var identity = | |
/*#__PURE__*/ | |
_curry1(_identity) | |
var identity$1 = identity | |
var _Set = | |
/*#__PURE__*/ | |
(function () { | |
function _Set() { | |
/* globals Set */ | |
this._nativeSet = typeof Set === 'function' ? new Set() : null | |
this._items = {} | |
} | |
// until we figure out why jsdoc chokes on this | |
// @param item The item to add to the Set | |
// @returns {boolean} true if the item did not exist prior, otherwise false | |
// | |
_Set.prototype.add = function (item) { | |
return !hasOrAdd(item, true, this) | |
} // | |
// @param item The item to check for existence in the Set | |
// @returns {boolean} true if the item exists in the Set, otherwise false | |
// | |
_Set.prototype.has = function (item) { | |
return hasOrAdd(item, false, this) | |
} // | |
// Combines the logic for checking whether an item is a member of the set and | |
// for adding a new item to the set. | |
// | |
// @param item The item to check or add to the Set instance. | |
// @param shouldAdd If true, the item will be added to the set if it doesn't | |
// already exist. | |
// @param set The set instance to check or add to. | |
// @return {boolean} true if the item already existed, otherwise false. | |
// | |
return _Set | |
})() | |
function hasOrAdd(item, shouldAdd, set) { | |
var type = typeof item | |
var prevSize, newSize | |
switch (type) { | |
case 'string': | |
case 'number': | |
// distinguish between +0 and -0 | |
if (item === 0 && 1 / item === -Infinity) { | |
if (set._items['-0']) { | |
return true | |
} else { | |
if (shouldAdd) { | |
set._items['-0'] = true | |
} | |
return false | |
} | |
} // these types can all utilise the native Set | |
if (set._nativeSet !== null) { | |
if (shouldAdd) { | |
prevSize = set._nativeSet.size | |
set._nativeSet.add(item) | |
newSize = set._nativeSet.size | |
return newSize === prevSize | |
} else { | |
return set._nativeSet.has(item) | |
} | |
} else { | |
if (!(type in set._items)) { | |
if (shouldAdd) { | |
set._items[type] = {} | |
set._items[type][item] = true | |
} | |
return false | |
} else if (item in set._items[type]) { | |
return true | |
} else { | |
if (shouldAdd) { | |
set._items[type][item] = true | |
} | |
return false | |
} | |
} | |
case 'boolean': | |
// set._items['boolean'] holds a two element array | |
// representing [ falseExists, trueExists ] | |
if (type in set._items) { | |
var bIdx = item ? 1 : 0 | |
if (set._items[type][bIdx]) { | |
return true | |
} else { | |
if (shouldAdd) { | |
set._items[type][bIdx] = true | |
} | |
return false | |
} | |
} else { | |
if (shouldAdd) { | |
set._items[type] = item ? [false, true] : [true, false] | |
} | |
return false | |
} | |
case 'function': | |
// compare functions for reference equality | |
if (set._nativeSet !== null) { | |
if (shouldAdd) { | |
prevSize = set._nativeSet.size | |
set._nativeSet.add(item) | |
newSize = set._nativeSet.size | |
return newSize === prevSize | |
} else { | |
return set._nativeSet.has(item) | |
} | |
} else { | |
if (!(type in set._items)) { | |
if (shouldAdd) { | |
set._items[type] = [item] | |
} | |
return false | |
} | |
if (!_includes(item, set._items[type])) { | |
if (shouldAdd) { | |
set._items[type].push(item) | |
} | |
return false | |
} | |
return true | |
} | |
case 'undefined': | |
if (set._items[type]) { | |
return true | |
} else { | |
if (shouldAdd) { | |
set._items[type] = true | |
} | |
return false | |
} | |
case 'object': | |
if (item === null) { | |
if (!set._items['null']) { | |
if (shouldAdd) { | |
set._items['null'] = true | |
} | |
return false | |
} | |
return true | |
} | |
/* falls through */ | |
default: | |
// reduce the search size of heterogeneous sets by creating buckets | |
// for each type. | |
type = Object.prototype.toString.call(item) | |
if (!(type in set._items)) { | |
if (shouldAdd) { | |
set._items[type] = [item] | |
} | |
return false | |
} // scan through all previously applied items | |
if (!_includes(item, set._items[type])) { | |
if (shouldAdd) { | |
set._items[type].push(item) | |
} | |
return false | |
} | |
return true | |
} | |
} // A simple Set type that honours R.equals semantics | |
/** | |
* Returns a new list by pulling every item out of it (and all its sub-arrays) | |
* and putting them in a new array, depth-first. | |
* | |
* @func | |
* @memberOf R | |
* @since v0.1.0 | |
* @category List | |
* @sig [a] -> [b] | |
* @param {Array} list The array to consider. | |
* @return {Array} The flattened list. | |
* @see R.unnest | |
* @example | |
* | |
* R.flatten([1, 2, [3, 4], 5, [6, [7, 8, [9, [10, 11], 12]]]]); | |
* //=> [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12] | |
*/ | |
var flatten = | |
/*#__PURE__*/ | |
_curry1( | |
/*#__PURE__*/ | |
_makeFlat(true), | |
) | |
var XUniqBy = | |
/*#__PURE__*/ | |
(function () { | |
function XUniqBy(f, xf) { | |
this.xf = xf | |
this.f = f | |
this.set = new _Set() | |
} | |
XUniqBy.prototype['@@transducer/init'] = _xfBase.init | |
XUniqBy.prototype['@@transducer/result'] = _xfBase.result | |
XUniqBy.prototype['@@transducer/step'] = function (result, input) { | |
return this.set.add(this.f(input)) | |
? this.xf['@@transducer/step'](result, input) | |
: result | |
} | |
return XUniqBy | |
})() | |
function _xuniqBy(f) { | |
return function (xf) { | |
return new XUniqBy(f, xf) | |
} | |
} | |
/** | |
* Returns a new list containing only one copy of each element in the original | |
* list, based upon the value returned by applying the supplied function to | |
* each list element. Prefers the first item if the supplied function produces | |
* the same value on two items. [`R.equals`](#equals) is used for comparison. | |
* | |
* Acts as a transducer if a transformer is given in list position. | |
* | |
* @func | |
* @memberOf R | |
* @since v0.16.0 | |
* @category List | |
* @sig (a -> b) -> [a] -> [a] | |
* @param {Function} fn A function used to produce a value to use during comparisons. | |
* @param {Array} list The array to consider. | |
* @return {Array} The list of unique items. | |
* @example | |
* | |
* R.uniqBy(Math.abs, [-1, -5, 2, 10, 1, 2]); //=> [-1, -5, 2, 10] | |
*/ | |
var uniqBy = | |
/*#__PURE__*/ | |
_curry2( | |
/*#__PURE__*/ | |
_dispatchable([], _xuniqBy, function (fn, list) { | |
var set = new _Set() | |
var result = [] | |
var idx = 0 | |
var appliedItem, item | |
while (idx < list.length) { | |
item = list[idx] | |
appliedItem = fn(item) | |
if (set.add(appliedItem)) { | |
result.push(item) | |
} | |
idx += 1 | |
} | |
return result | |
}), | |
) | |
/** | |
* Returns a new list containing only one copy of each element in the original | |
* list. [`R.equals`](#equals) is used to determine equality. | |
* | |
* @func | |
* @memberOf R | |
* @since v0.1.0 | |
* @category List | |
* @sig [a] -> [a] | |
* @param {Array} list The array to consider. | |
* @return {Array} The list of unique items. | |
* @example | |
* | |
* R.uniq([1, 1, 2, 1]); //=> [1, 2] | |
* R.uniq([1, '1']); //=> [1, '1'] | |
* R.uniq([[42], [42]]); //=> [[42]] | |
*/ | |
var uniq = | |
/*#__PURE__*/ | |
uniqBy(identity$1) | |
var uniq$1 = uniq | |
/** | |
* Returns a partial copy of an object containing only the keys specified. If | |
* the key does not exist, the property is ignored. | |
* | |
* @func | |
* @memberOf R | |
* @since v0.1.0 | |
* @category Object | |
* @sig [k] -> {k: v} -> {k: v} | |
* @param {Array} names an array of String property names to copy onto a new object | |
* @param {Object} obj The object to copy from | |
* @return {Object} A new object with only properties from `names` on it. | |
* @see R.omit, R.props | |
* @example | |
* | |
* R.pick(['a', 'd'], {a: 1, b: 2, c: 3, d: 4}); //=> {a: 1, d: 4} | |
* R.pick(['a', 'e', 'f'], {a: 1, b: 2, c: 3, d: 4}); //=> {a: 1} | |
*/ | |
var pick = | |
/*#__PURE__*/ | |
_curry2(function pick(names, obj) { | |
var result = {} | |
var idx = 0 | |
while (idx < names.length) { | |
if (names[idx] in obj) { | |
result[names[idx]] = obj[names[idx]] | |
} | |
idx += 1 | |
} | |
return result | |
}) | |
var XTap = | |
/*#__PURE__*/ | |
(function () { | |
function XTap(f, xf) { | |
this.xf = xf | |
this.f = f | |
} | |
XTap.prototype['@@transducer/init'] = _xfBase.init | |
XTap.prototype['@@transducer/result'] = _xfBase.result | |
XTap.prototype['@@transducer/step'] = function (result, input) { | |
this.f(input) | |
return this.xf['@@transducer/step'](result, input) | |
} | |
return XTap | |
})() | |
function _xtap(f) { | |
return function (xf) { | |
return new XTap(f, xf) | |
} | |
} | |
/** | |
* Runs the given function with the supplied object, then returns the object. | |
* | |
* Acts as a transducer if a transformer is given as second parameter. | |
* | |
* @func | |
* @memberOf R | |
* @since v0.1.0 | |
* @category Function | |
* @sig (a -> *) -> a -> a | |
* @param {Function} fn The function to call with `x`. The return value of `fn` will be thrown away. | |
* @param {*} x | |
* @return {*} `x`. | |
* @example | |
* | |
* const sayX = x => console.log('x is ' + x); | |
* R.tap(sayX, 100); //=> 100 | |
* // logs 'x is 100' | |
* @symb R.tap(f, a) = (f(a), a) | |
*/ | |
var tap = | |
/*#__PURE__*/ | |
_curry2( | |
/*#__PURE__*/ | |
_dispatchable([], _xtap, function tap(fn, x) { | |
fn(x) | |
return x | |
}), | |
) | |
// Table.wc.svelte (Svelte v5.0.0-next.94) | |
// Note: compiler output will change before 5.0 is released! | |
var root$1 = template(`table`, 1) | |
function Table_wc($$anchor, $$props) { | |
push($$props, true) | |
var fragment = root$1() | |
append($$anchor, fragment) | |
pop() | |
} | |
customElements.define( | |
'db-table', | |
create_custom_element(Table_wc, {}, [], [], true), | |
) | |
// separator must only contain chars valid for .querySelector() | |
const labelSeparator = '_' | |
const idSeparator = labelSeparator + labelSeparator | |
const connectionId = ({ table_name, name, connectionTo: to }) => | |
`connection${idSeparator}${table_name}${labelSeparator}${name}${idSeparator}${to.table_name}${labelSeparator}${to.name}` | |
const anchorId = ({ table_name, name }) => | |
`anchor${idSeparator}${table_name}_${name}` | |
const d = tap(console.info) | |
const connections = (tables) => | |
flatten( | |
map( | |
pipe( | |
d, | |
({ table_name, schema }) => | |
pipe( | |
map( | |
pipe(d, ({ name, connectionTo }) => | |
connectionTo | |
? connectionId({ table_name, name, connectionTo }) | |
: null, | |
), | |
), | |
filter(identity$1), | |
)(schema), | |
d, | |
), | |
)(tables), | |
) | |
const getBoundingClientRect = (node) => (id) => | |
node.querySelector(id)?.getBoundingClientRect() | |
const getCoordinates = (node) => (prefix) => | |
map((segment) => | |
getBoundingClientRect(node)(`#${prefix}${idSeparator}${segment}`), | |
) | |
const coordinates = (node) => | |
map( | |
pipe((id) => { | |
// FIXME: we need coordinates of | |
// - source box | |
// - destination box | |
// plus determine left and right anchor point for connection path | |
const [prefix, source, destination] = id.split(idSeparator) | |
console.log({ prefix, source, destination }) | |
const [[src_table, src_column], [dst_table, dst_column]] = [ | |
source, | |
destination, | |
].map((s) => s.split(labelSeparator)) | |
const c = getCoordinates(node) | |
const th_els = c('th')([src_table, dst_table]) | |
const tr_els = c('anchor')([source, destination]) | |
console.log(tr_els, th_els) | |
return [{ id, source, destination }, ...tr_els, ...th_els] | |
}), | |
) | |
const transform = ({ tables, onlyConnected }) => { | |
if (!tables) { | |
return | |
} | |
const isConnected = uniq$1( | |
flatten( | |
map((con) => { | |
const [, s, d] = con.split(idSeparator) | |
return [s, d] | |
})(connections(tables)), | |
), | |
) | |
let counter = 0 | |
const nodes = flatten( | |
tables.map(({ table_name, schema }) => ({ | |
id: ++counter, | |
name: table_name, | |
columns: pipe( | |
filter(({ name }) => | |
onlyConnected | |
? isConnected?.includes(`${table_name}${labelSeparator}${name}`) | |
: true, | |
), | |
map(({ name, type, connectionTo: to }) => [ | |
name, | |
type, | |
to ? `${to.table_name}${labelSeparator}${to.name}` : undefined, | |
]), | |
)(schema), | |
})), | |
) | |
const indexedNodes = pipe( | |
map(({ id, name }) => ({ [name]: id })), | |
reduce((p, c) => ({ ...p, ...c }), {}), | |
)(nodes) | |
const links = pipe( | |
filter(({ columns }) => columns.filter((c) => c[2]).length > 0), | |
map(({ id, columns }) => | |
columns | |
.map(([, , link]) => | |
link | |
? { | |
source: id, | |
target: indexedNodes[link?.split(labelSeparator)[0]], | |
} | |
: undefined, | |
) | |
.filter(identity$1), | |
), | |
flatten, | |
)(nodes) | |
return { | |
links, | |
indexedNodes, | |
nodes: map(pick(['id', 'name', 'columns']))(nodes), | |
} | |
} | |
const chart = ( | |
tables, | |
dimensions = { | |
width: 1000, | |
height: 1000, | |
table_width: 160, | |
table_height: 100, | |
distanceFactor: 4, | |
}, | |
cb, | |
) => { | |
console.log('chart', tables) | |
if (!tables.tables) { | |
return | |
} | |
const { width, height, table_width, table_height, distanceFactor } = | |
dimensions | |
const data = transform({ tables: tables.tables, onlyConnected: true }) | |
// Specify the dimensions of the chart. | |
// Specify the color scale. | |
ordinal(category10) | |
// The force simulation mutates links and nodes, so create a copy | |
// so that re-evaluating this cell produces the same result. | |
const links = data.links.map((d) => ({ ...d })) | |
const nodes = data.nodes.map((d) => ({ ...d })) | |
// Create a simulation with several forces. | |
const simulation$1 = simulation(nodes) | |
.force( | |
'link', | |
link(links) | |
.id((d) => d.id) | |
.distance(width / (distanceFactor ?? 4)), | |
) | |
.force('charge', manyBody().distanceMin(300).strength(1)) | |
.force('center', center(width / 2, height / 2)) | |
.on('tick', ticked) | |
// Create the SVG container. | |
const svg = create$1('svg') | |
.attr('width', width) | |
.attr('height', height) | |
.attr('viewBox', [0, 0, width, height]) | |
.attr('style', 'max-width: 100%; height: auto; min-width 30vw;') | |
// Add a line for each link, and a circle for each node. | |
const link$1 = svg | |
.append('g') | |
.attr('id', 'links') | |
.attr('stroke', '#999') | |
.attr('stroke-opacity', 0) | |
.selectAll() | |
.data(links) | |
.join('line') | |
.attr('stroke-width', (d) => Math.sqrt(d.value)) | |
const node = svg | |
.append('g') | |
.attr('id', 'tables') | |
.attr('stroke', '#fff') | |
.attr('stroke-width', 1.5) | |
.selectAll() | |
.data(nodes) | |
.join('foreignObject') | |
// .attr('id', (d) => `foreignObject_table${idSeparator}${d.name}`) | |
.attr('width', (d) => Math.max(200, table_width)) | |
.attr('height', (d) => Math.max(d.columns.length * 27 + 20, table_height)) | |
// .attr('fill', (d) => color(d.group)) | |
node | |
.append('xhtml:section') | |
.attr('id', (d) => `section${idSeparator}${d.name}`) | |
.style('border-bottom', '2px solid #5a5e64') | |
.style('user-select', 'none') | |
.style('border-width', '3px') | |
.style('border-style', 'solid') | |
.style('border-color', '#5a5e64') | |
.style('border-radius', '0.5em') | |
// .style('margin', '0.5em') | |
.append('xhtml:table') | |
.style('border-collapse', 'collapse') | |
.attr('id', (d) => `table${idSeparator}${d.name}`) | |
.html( | |
(d) => | |
`<caption | |
style="border-bottom: 2px solid #5a5e64; | |
width: calc(100% - 1em); | |
caption-side: top; | |
text-align: left; | |
font-weight: bold; | |
padding: 0.2em 0.5em; | |
margin: 0; | |
">${d.name}</caption>`, | |
) | |
.append('xhtml:tbody') | |
.append('xhtml:th') | |
.attr('id', (d) => `th${idSeparator}${d.name}`) | |
.style('padding', 0) | |
// FIXME: iterate over columns from transformed nodes instead of schema | |
tables.tables.map(({ table_name, schema }, i) => { | |
console.log({ table_name }) | |
node.selectAll(`th#th${idSeparator}${table_name}`).each(function (th) { | |
console.log(th.name) | |
const that = this | |
schema | |
.map(({ name, type, constrains, connectionTo }) => [ | |
anchorId({ table_name, name }), | |
`<tr style="display: inline-block;"> | |
<td style=" | |
border-right: 2px solid rgba(127,127,127,0.9); | |
padding: 0.2em 0.5em; | |
text-overflow: ellipsis; | |
overflow: hidden; | |
max-width: 5em; | |
">${name}</td> | |
<td style="padding: 0.2em 0.5em;">${type}</td> | |
<td style="padding: 0.2em 0.5em; color: lightblue">${constrains ?? ''}</td> | |
${connectionTo === true ? `<td>(${connectionTo})</td>` : ''} | |
</tr> | |
`, | |
]) | |
.map(([id, html]) => { | |
// console.log({ html }) | |
const t = document.createElement('tr') | |
if (id) { | |
t.id = id | |
} | |
t.innerHTML = html | |
that.parentNode.appendChild(t, that.nextSibling) | |
console.debug( | |
`appendChild [${id}] here, we need the coordinates somehow... (this is before mounting??)`, | |
t.getBoundingClientRect(), | |
) | |
}) | |
}) | |
}) | |
node.call( | |
drag().on('start', dragstarted).on('drag', dragged).on('end', dragended), | |
) | |
// // TODO implement anchor points | |
// const anchor = svg.append('g').attr('id', 'anchors') | |
// Set the position attributes of links and nodes each time the simulation ticks. | |
function ticked() { | |
link$1 | |
.attr('x1', (d) => d.source.x + table_width / 2) | |
.attr('y1', (d) => d.source.y + table_height / 2) | |
.attr('x2', (d) => d.target.x + table_width / 2) | |
.attr('y2', (d) => d.target.y + table_height / 2) | |
node | |
.attr('x', (d) => { | |
// console.debug(`${d.id}: x ${d.x}`) | |
cb('move', { id: d.id, x: d.x, width: d.width }) | |
return d.x | |
}) | |
.attr('y', (d) => { | |
// console.debug(`${d.id}: y ${d.y}`) | |
cb('move', { id: d.id, y: d.y, height: d.height }) | |
return d.y | |
}) | |
// .dispatch('move', { detail: 'blub', bubbles: true }) | |
} | |
// Reheat the simulation when drag starts, and fix the subject position. | |
function dragstarted(event) { | |
if (!event.active) simulation$1.alphaTarget(0.3).restart() | |
event.subject.fx = event.subject.x | |
event.subject.fy = event.subject.y | |
} | |
// Update the subject (dragged node) position during drag. | |
function dragged(event) { | |
event.subject.fx = event.x | |
event.subject.fy = event.y | |
} | |
// Restore the target alpha so the simulation cools after dragging ends. | |
// Unfix the subject position now that it’s no longer being dragged. | |
function dragended(event) { | |
if (!event.active) simulation$1.alphaTarget(0) | |
event.subject.fx = null | |
event.subject.fy = null | |
} | |
// When this cell is re-run, stop the previous simulation. (This doesn’t | |
// really matter since the target alpha is zero and the simulation will | |
// stop naturally, but it’s a good practice.) | |
// invalidation.then(() => simulation.stop()); | |
return svg.node() | |
} | |
// Tables.wc.svelte (Svelte v5.0.0-next.94) | |
// Note: compiler output will change before 5.0 is released! | |
var root_4 = svg_template( | |
`<circle r="6"></circle><rect width="10" height="10" style="transform-origin: center; transform-box: fill-box;" transform="translate(-5, -5), rotate(45)"></rect><path></path>`, | |
1, | |
) | |
var root_5 = svg_template(`<line stroke-width="2"></line>`) | |
var root_7 = template(`<li> </li>`) | |
var root_6 = template( | |
`<ul style="list-style: none; color: rgba(20,127,127,0.6); font-weight: 100; font-family: arial"></ul>`, | |
) | |
var root_1 = template( | |
`<div class="overlay" style="width: 100%; max-width: 100%; height: 100%; | |
position: absolute; top: 0; left: 0;"><svg style="max-width: 100%; height: auto; min-width 30vw;"><g id="connectionprojections"></g></svg></div> <div class="baseline" style="width: 100%; | |
max-width: 100%; | |
height: 100%; | |
position: absolute; | |
top: 0; | |
left: 0;"></div> <!>`, | |
1, | |
) | |
var root = template( | |
`<div style="display: flex; background-color: #191b1f; color: #e4eaed;"><!></div>`, | |
) | |
function Tables_wc($$anchor, $$props) { | |
push($$props, true) | |
let data = prop($$props, 'data', 7), | |
curves = prop($$props, 'curves', 7), | |
debug = prop($$props, 'debug', 7) | |
let _props = JSON.parse(data()) || { tables: [] } | |
// TODO: pass in dimensions | |
const { tables, dimensions } = _props | |
const width = dimensions?.width ?? 1000 | |
/* Math.min( | |
1000, | |
// document.getRootNode().body.getBoundingClientRect().width, | |
) | |
*/ | |
const height = dimensions?.height ?? 1000 | |
/*Math.min( | |
1000, | |
// document.getRootNode().body.getBoundingClientRect().height, | |
) | |
*/ | |
const table_width = dimensions?.table_width ?? 260 | |
const table_height = dimensions?.table_height ?? 300 | |
const distanceFactor = dimensions?.distanceFactor ?? 4 | |
let table_position = source(proxy({})) | |
let label_position = source(proxy({})) | |
const color = ordinal(Dark2) | |
// get connections based on tables | |
const connections$1 = connections(tables) | |
// get a map of nodes for lookup | |
const { indexedNodes } = transform({ tables, onlyConnected: true }) | |
Object.entries(indexedNodes).reduce((p, [key, value]) => { | |
return { ...p, [value]: key } | |
}, []) | |
const convertRect = (box) => { | |
if (!box) return {} | |
const { x, y, width, height, top, right, bottom, left } = box | |
return { | |
x, | |
y, | |
width, | |
height, | |
top, | |
right, | |
bottom, | |
left, | |
} | |
} | |
const unkey = (id) => { | |
// FIXME: use a different and safe separator | |
const [table, label] = id.split(labelSeparator) | |
return { id, table, label } | |
} | |
function visualize(node) { | |
const msg = (key, { id, ...rest }) => { | |
get$2(table_position)[id] = { | |
id, | |
...get$2(table_position)[id], | |
...rest, | |
} | |
} | |
select(node).html(null) | |
// mount the force simulated tables into div | |
node.appendChild( | |
chart( | |
{ tables }, | |
{ | |
width, | |
height, | |
table_width, | |
table_height, | |
distanceFactor, | |
}, | |
msg, | |
), | |
) | |
coordinates(node)(connections$1).map( | |
tap(([{ source, destination }, srcRect, dstRect, srcTable, dstTable]) => { | |
get$2(label_position)[source] = { | |
...unkey(source), | |
...convertRect(srcRect), | |
thPos: convertRect(srcTable), | |
} | |
get$2(label_position)[destination] = { | |
...unkey(destination), | |
...convertRect(dstRect), | |
thPos: convertRect(dstTable), | |
} | |
}), | |
) | |
} | |
const posDebug = (o) => | |
o ? `${o.id}: ${Math.floor(o.x)} ${Math.floor(o.y)}` : {} | |
const controlPointString = (o) => { | |
//INFO: inspired by: https://github.com/open-source-labs/Svelvet/blob/main/src/lib/components/Edge/Edge.svelte#L107 | |
if (!o) { | |
return '' | |
} | |
const { source, target } = o | |
const rotateVector = ({ x, y }) => { | |
const angle = -45 * (Math.PI / 180) | |
return { | |
x: x * Math.cos(angle) - y * Math.sin(angle), | |
y: y * Math.sin(angle) + y * Math.cos(angle), | |
} | |
} | |
//Reactive declarations | |
const sourceX = source.x | |
const sourceY = source.y | |
const targetX = target.x | |
const targetY = target.y | |
// The distances between the points | |
const deltaX = targetX - sourceX | |
const deltaY = targetY - sourceY | |
const anchorWidth = Math.abs(deltaX) | |
const anchorHeight = Math.abs(deltaY) | |
// Strength of curvature | |
const maxCurveDisplaceX = Math.max(30, Math.min(600, anchorWidth / 2)) | |
const maxCurveDisplaceY = Math.max(30, Math.min(600, anchorHeight / 2)) | |
// Helper XY pair to offset the control points | |
const sourceControlVector = rotateVector({ | |
x: sourceX < targetX ? 1 : -1, | |
y: 0, | |
}) | |
const targetControlVector = rotateVector({ | |
x: sourceX > targetX ? 1 : -1, | |
y: 0, | |
}) | |
// Calculating the control points for the bezier curve | |
const sourceControlX = sourceX + sourceControlVector.x * maxCurveDisplaceX | |
const sourceControlY = sourceY + sourceControlVector.y * maxCurveDisplaceY | |
const targetControlX = targetX + targetControlVector.x * maxCurveDisplaceX | |
const targetControlY = targetY + targetControlVector.y * maxCurveDisplaceY | |
// Constructing the control point element of the path string | |
const controlPointString = `C ${sourceControlX}, ${sourceControlY} ${targetControlX}, ${targetControlY}` | |
return controlPointString | |
} | |
var div = root() | |
var node_1 = child(div) | |
if_block( | |
node_1, | |
() => tables, | |
($$anchor) => { | |
var fragment = root_1() | |
var div_1 = first_child(fragment) | |
var svg = child(div_1) | |
set_attribute(svg, 'viewBox', `0,0,${width},${height}`) | |
set_attribute(svg, 'width', width) | |
set_attribute(svg, 'height', height) | |
var g = child(svg) | |
each_indexed( | |
g, | |
73, | |
() => connections$1, | |
($$anchor, connection, i) => { | |
var fragment_1 = comment() | |
const computed_const = derived(() => { | |
const [prefix, source, destination] = | |
unwrap(connection).split(idSeparator) | |
return { prefix, source, destination } | |
}) | |
const computed_const_1 = derived(() => { | |
const [src_t, src_l, dst_t, dst_l] = flatten( | |
[ | |
get$2(computed_const).source, | |
get$2(computed_const).destination, | |
].map((s) => s.split(labelSeparator)), | |
) | |
return { src_t, src_l, dst_t, dst_l } | |
}) | |
var node_2 = first_child(fragment_1) | |
if_block( | |
node_2, | |
() => get$2(computed_const).prefix === 'connection', | |
($$anchor) => { | |
var fragment_2 = comment() | |
const computed_const_2 = derived(() => { | |
const [t_src, t_dst] = [ | |
get$2(computed_const_1).src_t, | |
get$2(computed_const_1).dst_t, | |
].map((id) => get$2(table_position)[indexedNodes[id]]) | |
return { t_src, t_dst } | |
}) | |
const computed_const_3 = derived(() => { | |
const [l_src, l_dst] = [ | |
`${get$2(computed_const_1).src_t}${labelSeparator}${get$2(computed_const_1).src_l}`, | |
`${get$2(computed_const_1).dst_t}${labelSeparator}${get$2(computed_const_1).dst_l}`, | |
].map((id) => { | |
if (!Object.keys(get$2(label_position)).includes(id)) return | |
return get$2(label_position)[id] | |
}) | |
return { l_src, l_dst } | |
}) | |
var node_3 = first_child(fragment_2) | |
if_block( | |
node_3, | |
() => | |
get$2(computed_const_2).t_src && | |
get$2(computed_const_2).t_dst && | |
get$2(computed_const_3).l_src && | |
get$2(computed_const_3).l_dst, | |
($$anchor) => { | |
var fragment_3 = comment() | |
const box_offset = derived(() => ({ x: -3, y: +3 })) | |
const computed_const_4 = derived(() => { | |
const [x1, x2, y1, y2] = [ | |
get$2(computed_const_3).l_src.x + | |
get$2(computed_const_2).t_src.x + | |
(get$2(computed_const_3).l_src.x + | |
get$2(computed_const_2).t_src.x < | |
get$2(computed_const_3).l_dst.x + | |
get$2(computed_const_2).t_dst.x | |
? table_width + get$2(box_offset).x | |
: 0), | |
get$2(computed_const_3).l_dst.x + | |
get$2(computed_const_2).t_dst.x + | |
(get$2(computed_const_3).l_src.x + | |
get$2(computed_const_2).t_src.x > | |
get$2(computed_const_3).l_dst.x + | |
get$2(computed_const_2).t_dst.x | |
? table_width + get$2(box_offset).x | |
: 0), | |
get$2(computed_const_3).l_src.y + | |
get$2(computed_const_3).l_src.height / 2 + | |
get$2(computed_const_2).t_src.y + | |
get$2(box_offset).y, | |
get$2(computed_const_3).l_dst.y + | |
get$2(computed_const_3).l_dst.height / 2 + | |
get$2(computed_const_2).t_dst.y + | |
get$2(box_offset).y, | |
] | |
return { x1, x2, y1, y2 } | |
}) | |
var node_4 = first_child(fragment_3) | |
if_block( | |
node_4, | |
() => [true.toString(), true].includes(curves()), | |
($$anchor) => { | |
var fragment_4 = root_4() | |
var circle = first_child(fragment_4) | |
render_effect(() => | |
set_attribute(circle, 'fill', color(unwrap(i))), | |
) | |
var rect = sibling(circle) | |
render_effect(() => | |
set_attribute(rect, 'fill', color(unwrap(i))), | |
) | |
var path = sibling(rect) | |
render_effect(() => | |
set_attribute( | |
path, | |
'd', | |
`M ${get$2(computed_const_4).x1}, ${get$2(computed_const_4).y1} ${controlPointString( | |
{ | |
source: { | |
x: get$2(computed_const_4).x1, | |
y: get$2(computed_const_4).y1, | |
}, | |
target: { | |
x: get$2(computed_const_4).x2, | |
y: get$2(computed_const_4).y2, | |
}, | |
}, | |
)} ${get$2(computed_const_4).x2}, ${get$2(computed_const_4).y2}`, | |
), | |
) | |
render_effect(() => | |
set_attribute(path, 'stroke', color(unwrap(i))), | |
) | |
render_effect(() => { | |
set_attribute(circle, 'cx', get$2(computed_const_4).x1) | |
set_attribute(circle, 'cy', get$2(computed_const_4).y1) | |
set_attribute(rect, 'x', get$2(computed_const_4).x2) | |
set_attribute(rect, 'y', get$2(computed_const_4).y2) | |
}) | |
append($$anchor, fragment_4) | |
}, | |
($$anchor) => { | |
var line = root_5() | |
render_effect(() => | |
set_attribute(line, 'stroke', color(unwrap(i))), | |
) | |
render_effect(() => { | |
set_attribute(line, 'x1', get$2(computed_const_4).x1) | |
set_attribute(line, 'x2', get$2(computed_const_4).x2) | |
set_attribute(line, 'y1', get$2(computed_const_4).y1) | |
set_attribute(line, 'y2', get$2(computed_const_4).y2) | |
}) | |
append($$anchor, line) | |
}, | |
) | |
append($$anchor, fragment_3) | |
}, | |
) | |
append($$anchor, fragment_2) | |
}, | |
) | |
append($$anchor, fragment_1) | |
}, | |
) | |
var div_2 = sibling(sibling(div_1, true)) | |
var node_5 = sibling(sibling(div_2, true)) | |
if_block(node_5, debug, ($$anchor) => { | |
var ul = root_6() | |
each_indexed( | |
ul, | |
73, | |
() => connections$1, | |
($$anchor, connection, $$index_1) => { | |
var fragment_5 = comment() | |
const computed_const_5 = derived(() => { | |
const [prefix, source, destination] = | |
unwrap(connection).split(idSeparator) | |
return { prefix, source, destination } | |
}) | |
const computed_const_6 = derived(() => { | |
const [src_t, src_l, dst_t, dst_l] = flatten( | |
[ | |
get$2(computed_const_5).source, | |
get$2(computed_const_5).destination, | |
].map((s) => s.split(labelSeparator)), | |
) | |
return { src_t, src_l, dst_t, dst_l } | |
}) | |
var node_6 = first_child(fragment_5) | |
if_block( | |
node_6, | |
() => get$2(computed_const_5).prefix === 'connection', | |
($$anchor) => { | |
var li = root_7() | |
const computed_const_7 = derived(() => { | |
const [t_src, t_dst] = [ | |
get$2(computed_const_6).src_t, | |
get$2(computed_const_6).dst_t, | |
].map((id) => get$2(table_position)[indexedNodes[id]]) | |
return { t_src, t_dst } | |
}) | |
const computed_const_8 = derived(() => { | |
const [l_src, l_dst] = [ | |
`${get$2(computed_const_6).src_t}${labelSeparator}${get$2(computed_const_6).src_l}`, | |
`${get$2(computed_const_6).dst_t}${labelSeparator}${get$2(computed_const_6).dst_l}`, | |
].map((id) => { | |
if (!Object.keys(get$2(label_position)).includes(id)) return | |
return get$2(label_position)[id] | |
}) | |
return { l_src, l_dst } | |
}) | |
var text = child(li) | |
render_effect(() => | |
set_text( | |
text, | |
`${stringify(get$2(computed_const_6).src_t)}_${stringify(get$2(computed_const_6).src_l)} (${stringify(posDebug(get$2(computed_const_7).t_src))}) + (${stringify(posDebug(get$2(computed_const_8).l_src))}) -> ${stringify(get$2(computed_const_6).dst_t)}_${stringify(get$2(computed_const_6).dst_l)} | |
(${stringify(posDebug(get$2(computed_const_7).t_dst))}) + (${stringify(posDebug(get$2(computed_const_8).l_dst))})`, | |
), | |
) | |
append($$anchor, li) | |
}, | |
) | |
append($$anchor, fragment_5) | |
}, | |
) | |
append($$anchor, ul) | |
}) | |
action(div_2, ($$node) => visualize($$node)) | |
append($$anchor, fragment) | |
}, | |
) | |
append($$anchor, div) | |
return pop({ | |
get data() { | |
return data() | |
}, | |
set data($$value) { | |
data($$value) | |
flush_sync() | |
}, | |
get curves() { | |
return curves() | |
}, | |
set curves($$value) { | |
curves($$value) | |
flush_sync() | |
}, | |
get debug() { | |
return debug() | |
}, | |
set debug($$value) { | |
debug($$value) | |
flush_sync() | |
}, | |
}) | |
} | |
customElements.define( | |
'visualize-tables', | |
create_custom_element( | |
Tables_wc, | |
{ data: {}, curves: {}, debug: {} }, | |
[], | |
[], | |
true, | |
), | |
) | |
export { Tables_wc as Tables } |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!doctype html> | |
<html> | |
<head> | |
<script src="/build.js" type="module"> | |
console.log('loading') | |
</script> | |
<style> | |
body { | |
margin: 0; | |
height: 100%; | |
background-color: grey; | |
color: white; | |
} | |
.dark { | |
background-color: blue; | |
} | |
</style> | |
</head> | |
<body> | |
test | |
<div class="dark"> | |
<visualize-tables | |
dark="true" | |
data='{"dimensions": {"distanceFactor": 2},"tables":[{"table_name":"a","schema":[{"name":"A1","type":"ID","connectionTo":{"table_name":"b","name":"B2"}}]},{"table_name":"b","schema":[{"name":"B1","type":"Int","constrains":"autoincrement","connectionTo":{"table_name":"a","name":"A1"}},{"name":"B2","type":"Int","connectionTo":{"table_name":"c","name":"C1"}},{"name":"B3","type":"ID"},{"name":"B4B4B4B4B4B4B4B4B4B4B4B4B4B4B4B4B4B4B4B4","type":"ID","connectionTo":{"table_name":"c","name":"C4"}}]},{"table_name":"c","schema":[{"name":"C1","type":"ID"},{"name":"C2","type":"ID","constrains":"not null","connectionTo":{"table_name":"a","name":"A1"}},{"name":"C3","type":"ID"},{"name":"C4","type":"String"},{"name":"C5C5C5C5C5C5C5C5C5C5C5C5","type":"Number","connectionTo":{"table_name":"b","name":"B1"}}]}]}' | |
curves="false" | |
/> | |
</div> | |
</body> | |
</html> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment