Fix media bindings, simplify others

pull/1935/head
Rich Harris 6 years ago committed by GitHub
parent f8517edba9
commit c0a8e630e3
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -7,7 +7,6 @@ export default class Binding extends Node {
name: string;
expression: Expression;
isContextual: boolean;
usesContext: boolean;
obj: string;
prop: string;
@ -34,13 +33,9 @@ export default class Binding extends Node {
prop = `[✂${this.expression.node.property.start}-${this.expression.node.property.end}✂]`;
if (!this.expression.node.computed) prop = `'${prop}'`;
obj = `[✂${this.expression.node.object.start}-${this.expression.node.object.end}✂]`;
this.usesContext = true;
} else {
obj = 'ctx';
prop = `'${name}'`;
this.usesContext = scope.names.has(name);
}
this.obj = obj;

@ -29,7 +29,7 @@ export default class Block {
dependencies: Set<string>;
bindings: Map<string, () => { object: string, property: string, snippet: string }>;
bindings: Map<string, { object: string, property: string, snippet: string }>;
contextOwners: Map<string, EachBlockWrapper>;
builders: {

@ -97,15 +97,33 @@ export default class EachBlockWrapper extends Wrapper {
this.indexName = this.node.index || renderer.component.getUniqueName(`${this.node.context}_index`);
// hack the sourcemap, so that if data is missing the bug
// is easy to find
let c = this.node.start + 2;
while (renderer.component.source[c] !== 'e') c += 1;
renderer.component.code.overwrite(c, c + 4, 'length');
const length = `[✂${c}-${c+4}✂]`;
this.vars = {
create_each_block: this.block.name,
each_block_value: renderer.component.getUniqueName(`${this.var}_value`),
get_each_context: renderer.component.getUniqueName(`get_${this.var}_context`),
iterations: block.getUniqueName(`${this.var}_blocks`),
length: `[✂${c}-${c+4}✂]`,
// filled out later
anchor: null,
mountOrIntro: null
};
node.contexts.forEach(prop => {
this.block.contextOwners.set(prop.key.name, this);
// TODO this doesn't feel great
this.block.bindings.set(prop.key.name, () => ({
this.block.bindings.set(prop.key.name, {
object: this.vars.each_block_value,
property: this.indexName,
snippet: `${this.vars.each_block_value}[${this.indexName}]${prop.tail}`
}));
});
});
if (this.node.index) {
@ -147,30 +165,15 @@ export default class EachBlockWrapper extends Wrapper {
const { renderer } = this;
const { component } = renderer;
// hack the sourcemap, so that if data is missing the bug
// is easy to find
let c = this.node.start + 2;
while (component.source[c] !== 'e') c += 1;
component.code.overwrite(c, c + 4, 'length');
const length = `[✂${c}-${c+4}✂]`;
const needsAnchor = this.next
? !this.next.isDomNode() :
!parentNode || !this.parent.isDomNode();
this.vars = {
anchor: needsAnchor
this.vars.anchor = needsAnchor
? block.getUniqueName(`${this.var}_anchor`)
: (this.next && this.next.var) || 'null',
create_each_block: this.block.name,
each_block_value: renderer.component.getUniqueName(`${this.var}_value`),
get_each_context: renderer.component.getUniqueName(`get_${this.var}_context`),
iterations: block.getUniqueName(`${this.var}_blocks`),
length: `[✂${c}-${c+4}✂]`,
mountOrIntro: (this.block.hasIntroMethod || this.block.hasOutroMethod)
? 'i'
: 'm'
};
: (this.next && this.next.var) || 'null';
this.vars.mountOrIntro = (this.block.hasIntroMethod || this.block.hasOutroMethod) ? 'i' : 'm';
this.contextProps = this.node.contexts.map(prop => `child_ctx.${prop.key.name} = list[i]${prop.tail};`);
@ -212,7 +215,7 @@ export default class EachBlockWrapper extends Wrapper {
// TODO neaten this up... will end up with an empty line in the block
block.builders.init.addBlock(deindent`
if (!${this.vars.each_block_value}.${length}) {
if (!${this.vars.each_block_value}.${this.vars.length}) {
${each_block_else} = ${this.else.block.name}(#component, ctx);
${each_block_else}.c();
}
@ -228,9 +231,9 @@ export default class EachBlockWrapper extends Wrapper {
if (this.else.block.hasUpdateMethod) {
block.builders.update.addBlock(deindent`
if (!${this.vars.each_block_value}.${length} && ${each_block_else}) {
if (!${this.vars.each_block_value}.${this.vars.length} && ${each_block_else}) {
${each_block_else}.p(changed, ctx);
} else if (!${this.vars.each_block_value}.${length}) {
} else if (!${this.vars.each_block_value}.${this.vars.length}) {
${each_block_else} = ${this.else.block.name}(#component, ctx);
${each_block_else}.c();
${each_block_else}.${mountOrIntro}(${initialMountNode}, ${this.vars.anchor});
@ -241,7 +244,7 @@ export default class EachBlockWrapper extends Wrapper {
`);
} else {
block.builders.update.addBlock(deindent`
if (${this.vars.each_block_value}.${length}) {
if (${this.vars.each_block_value}.${this.vars.length}) {
if (${each_block_else}) {
${each_block_else}.d(1);
${each_block_else} = null;

@ -21,11 +21,15 @@ export default class BindingWrapper {
parent: ElementWrapper;
object: string;
handler: any; // TODO
updateDom: string;
handler: {
usesContext: boolean;
mutation: string;
contextual_dependencies: Set<string>
};
snippet: string;
initialUpdate: string;
isReadOnly: boolean;
needsLock: boolean;
updateCondition: string;
constructor(block: Block, node: Binding, parent: ElementWrapper) {
this.node = node;
@ -51,45 +55,34 @@ export default class BindingWrapper {
eachBlock.hasBinding = true;
}
}
isReadOnlyMediaAttribute() {
return readOnlyMediaAttributes.has(this.node.name);
}
munge(block: Block) {
const { parent } = this;
const { renderer } = parent;
this.object = getObject(this.node.expression.node).name;
const needsLock = (
parent.node.name !== 'input' ||
!/radio|checkbox|range|color/.test(parent.node.getStaticAttributeValue('type'))
);
// TODO unfortunate code is necessary because we need to use `ctx`
// inside the fragment, but not inside the <script>
const contextless_snippet = this.parent.renderer.component.source.slice(this.node.expression.node.start, this.node.expression.node.end);
const isReadOnly = (
(parent.node.isMediaNode() && readOnlyMediaAttributes.has(this.node.name)) ||
dimensions.test(this.node.name)
);
// view to model
this.handler = getEventHandler(this, parent.renderer, block, this.object, contextless_snippet);
let updateConditions: string[] = [];
this.snippet = this.node.expression.render();
const { name } = getObject(this.node.expression.node);
const type = parent.node.getStaticAttributeValue('type');
const snippet = this.node.expression.render();
this.isReadOnly = (
dimensions.test(this.node.name) ||
(parent.node.isMediaNode() && readOnlyMediaAttributes.has(this.node.name)) ||
(parent.node.name === 'input' && type === 'file') // TODO others?
);
// TODO unfortunate code is necessary because we need to use `ctx`
// inside the fragment, but not inside the <script>
const contextless_snippet = this.parent.renderer.component.source.slice(this.node.expression.node.start, this.node.expression.node.end);
this.needsLock = this.node.name === 'currentTime'; // TODO others?
}
// special case: if you have e.g. `<input type=checkbox bind:checked=selected.done>`
// and `selected` is an object chosen with a <select>, then when `checked` changes,
// we need to tell the component to update all the values `selected` might be
// pointing to
// TODO should this happen in preprocess?
get_dependencies() {
const dependencies = new Set(this.node.expression.dependencies);
this.node.expression.dependencies.forEach((prop: string) => {
const indirectDependencies = renderer.component.indirectDependencies.get(prop);
const indirectDependencies = this.parent.renderer.component.indirectDependencies.get(prop);
if (indirectDependencies) {
indirectDependencies.forEach(indirectDependency => {
dependencies.add(indirectDependency);
@ -97,17 +90,37 @@ export default class BindingWrapper {
}
});
// view to model
const valueFromDom = getValueFromDom(renderer, this.parent, this);
const handler = getEventHandler(this, renderer, block, name, contextless_snippet, valueFromDom);
return dependencies;
}
isReadOnlyMediaAttribute() {
return readOnlyMediaAttributes.has(this.node.name);
}
render(block: Block, lock: string) {
if (this.isReadOnly) return;
const { parent } = this;
let updateConditions: string[] = this.needsLock ? [`!${lock}`] : [];
const dependencyArray = [...this.node.expression.dynamic_dependencies]
if (dependencyArray.length === 1) {
updateConditions.push(`changed.${dependencyArray[0]}`)
} else if (dependencyArray.length > 1) {
updateConditions.push(
`(${dependencyArray.map(prop => `changed.${prop}`).join(' || ')})`
)
}
// model to view
let updateDom = getDomUpdater(parent, this, snippet);
let initialUpdate = updateDom;
let updateDom = getDomUpdater(parent, this);
// special cases
if (this.node.name === 'group') {
const bindingGroup = getBindingGroup(renderer, this.node.expression.node);
switch (this.node.name) {
case 'group':
const bindingGroup = getBindingGroup(parent.renderer, this.node.expression.node);
block.builders.hydrate.addLine(
`(#component.$$.binding_groups[${bindingGroup}] || (#component.$$.binding_groups[${bindingGroup}] = [])).push(${parent.var});`
@ -116,61 +129,43 @@ export default class BindingWrapper {
block.builders.destroy.addLine(
`#component.$$.binding_groups[${bindingGroup}].splice(#component.$$.binding_groups[${bindingGroup}].indexOf(${parent.var}), 1);`
);
}
break;
if (this.node.name === 'currentTime' || this.node.name === 'volume') {
updateConditions.push(`!isNaN(${snippet})`);
if (this.node.name === 'currentTime') initialUpdate = null;
}
case 'currentTime':
case 'volume':
updateConditions.push(`!isNaN(${this.snippet})`);
break;
if (this.node.name === 'paused') {
case 'paused':
// this is necessary to prevent audio restarting by itself
const last = block.getUniqueName(`${parent.var}_is_paused`);
block.addVariable(last, 'true');
updateConditions.push(`${last} !== (${last} = ${snippet})`);
updateConditions.push(`${last} !== (${last} = ${this.snippet})`);
updateDom = `${parent.var}[${last} ? "pause" : "play"]();`;
initialUpdate = null;
}
break;
// bind:offsetWidth and bind:offsetHeight
if (dimensions.test(this.node.name)) {
initialUpdate = null;
case 'value':
if (parent.getStaticAttributeValue('type') === 'file') {
updateDom = null;
}
}
const dependencyArray = [...this.node.expression.dynamic_dependencies]
if (dependencyArray.length === 1) {
updateConditions.push(`changed.${dependencyArray[0]}`)
} else if (dependencyArray.length > 1) {
updateConditions.push(
`(${dependencyArray.map(prop => `changed.${prop}`).join(' || ')})`
)
if (updateDom) {
block.builders.update.addLine(
updateConditions.length ? `if (${updateConditions.join(' && ')}) ${updateDom}` : updateDom
);
}
return {
name: this.node.name,
object: name,
handler,
snippet,
usesContext: handler.usesContext,
updateDom: updateDom,
initialUpdate: initialUpdate,
needsLock: !isReadOnly && needsLock,
updateCondition: updateConditions.length ? updateConditions.join(' && ') : undefined,
isReadOnlyMediaAttribute: this.isReadOnlyMediaAttribute(),
dependencies,
contextual_dependencies: this.node.expression.contextual_dependencies
};
if (!/(currentTime|paused)/.test(this.node.name)) {
block.builders.mount.addBlock(updateDom);
}
}
}
function getDomUpdater(
element: ElementWrapper,
binding: BindingWrapper,
snippet: string
binding: BindingWrapper
) {
const { node } = element;
@ -184,21 +179,21 @@ function getDomUpdater(
if (node.name === 'select') {
return node.getStaticAttributeValue('multiple') === true ?
`@selectOptions(${element.var}, ${snippet})` :
`@selectOption(${element.var}, ${snippet})`;
`@selectOptions(${element.var}, ${binding.snippet})` :
`@selectOption(${element.var}, ${binding.snippet})`;
}
if (binding.node.name === 'group') {
const type = node.getStaticAttributeValue('type');
const condition = type === 'checkbox'
? `~${snippet}.indexOf(${element.var}.__value)`
: `${element.var}.__value === ${snippet}`;
? `~${binding.snippet}.indexOf(${element.var}.__value)`
: `${element.var}.__value === ${binding.snippet}`;
return `${element.var}.checked = ${condition};`
}
return `${element.var}.${binding.node.name} = ${snippet};`;
return `${element.var}.${binding.node.name} = ${binding.snippet};`;
}
function getBindingGroup(renderer: Renderer, value: Node) {
@ -221,9 +216,10 @@ function getEventHandler(
renderer: Renderer,
block: Block,
name: string,
snippet: string,
value: string
snippet: string
) {
const value = getValueFromDom(renderer, binding.parent, binding);
if (binding.node.isContextual) {
let tail = '';
if (binding.node.expression.node.type === 'MemberExpression') {
@ -231,7 +227,7 @@ function getEventHandler(
tail = renderer.component.source.slice(start, end);
}
const { object, property, snippet } = block.bindings.get(name)();
const { object, property, snippet } = block.bindings.get(name);
return {
usesContext: true,

@ -90,7 +90,6 @@ export default class ElementWrapper extends Wrapper {
attributes: AttributeWrapper[];
bindings: Binding[];
classDependencies: string[];
initialUpdate: string;
slotOwner?: InlineComponentWrapper;
selectBindingDependencies?: Set<string>;
@ -137,25 +136,11 @@ export default class ElementWrapper extends Wrapper {
return new AttributeWrapper(this, block, attribute);
});
let has_bindings;
const binding_lookup = {};
this.node.bindings.forEach(binding => {
binding_lookup[binding.name] = binding;
has_bindings = true;
});
const type = this.node.getStaticAttributeValue('type');
// ordinarily, there'll only be one... but we need to handle
// the rare case where an element can have multiple bindings,
// e.g. <audio bind:paused bind:currentTime>
this.bindings = this.node.bindings.map(binding => new Binding(block, binding, this));
// TODO remove this, it's just useful during refactoring
if (has_bindings && !this.bindings.length) {
throw new Error(`no binding was created`);
}
if (node.intro || node.outro) {
if (node.intro) block.addIntro();
if (node.outro) block.addOutro();
@ -305,10 +290,6 @@ export default class ElementWrapper extends Wrapper {
this.addActions(block);
this.addClasses(block);
if (this.initialUpdate) {
block.builders.mount.addBlock(this.initialUpdate);
}
if (nodes && this.renderer.options.hydratable) {
block.builders.claim.addLine(
`${nodes}.forEach(@detachNode);`
@ -389,12 +370,7 @@ export default class ElementWrapper extends Wrapper {
renderer.component.has_reactive_assignments = true;
const needsLock = this.node.name !== 'input' || !/radio|checkbox|range|color/.test(this.getStaticAttributeValue('type'));
// TODO munge in constructor
const mungedBindings = this.bindings.map(binding => binding.munge(block));
const lock = mungedBindings.some(binding => binding.needsLock) ?
const lock = this.bindings.some(binding => binding.needsLock) ?
block.getUniqueName(`${this.var}_updating`) :
null;
@ -403,9 +379,9 @@ export default class ElementWrapper extends Wrapper {
const groups = events
.map(event => ({
events: event.eventNames,
bindings: mungedBindings
.filter(binding => binding.name !== 'this')
.filter(binding => event.filter(this.node, binding.name))
bindings: this.bindings
.filter(binding => binding.node.name !== 'this')
.filter(binding => event.filter(this.node, binding.node.name))
}))
.filter(group => group.bindings.length);
@ -414,6 +390,7 @@ export default class ElementWrapper extends Wrapper {
renderer.component.declarations.push(handler);
renderer.component.template_references.add(handler);
// TODO figure out how to handle locks
const needsLock = group.bindings.some(binding => binding.needsLock);
const dependencies = new Set();
@ -421,22 +398,13 @@ export default class ElementWrapper extends Wrapper {
group.bindings.forEach(binding => {
// TODO this is a mess
addToSet(dependencies, binding.dependencies);
addToSet(contextual_dependencies, binding.contextual_dependencies);
addToSet(dependencies, binding.get_dependencies());
addToSet(contextual_dependencies, binding.node.expression.contextual_dependencies);
addToSet(contextual_dependencies, binding.handler.contextual_dependencies);
if (!binding.updateDom) return;
const updateConditions = needsLock ? [`!${lock}`] : [];
if (binding.updateCondition) updateConditions.push(binding.updateCondition);
block.builders.update.addLine(
updateConditions.length ? `if (${updateConditions.join(' && ')}) ${binding.updateDom}` : binding.updateDom
);
binding.render(block, lock);
});
const mutations = group.bindings.map(binding => binding.handler.mutation).filter(Boolean).join('\n');
// media bindings — awkward special case. The native timeupdate events
// fire too infrequently, so we need to take matters into our
// own hands
@ -446,49 +414,35 @@ export default class ElementWrapper extends Wrapper {
block.addVariable(animation_frame);
}
// TODO figure out how to handle locks
const has_local_function = contextual_dependencies.size > 0 || needsLock || animation_frame;
let callee;
// TODO dry this out — similar code for event handlers and component bindings
if (contextual_dependencies.size > 0) {
const deps = Array.from(contextual_dependencies);
if (has_local_function) {
// need to create a block-local function that calls an instance-level function
block.builders.init.addBlock(deindent`
function ${handler}() {
ctx.${handler}.call(this, ctx);
}
`);
this.renderer.component.partly_hoisted.push(deindent`
function ${handler}({ ${deps.join(', ')} }) {
${
animation_frame && deindent`
${animation_frame && deindent`
cancelAnimationFrame(${animation_frame});
if (!${this.var}.paused) ${animation_frame} = requestAnimationFrame(${handler});`
}
${mutations.length > 0 && mutations}
${Array.from(dependencies).map(dep => `$$invalidate('${dep}', ${dep});`)}
if (!${this.var}.paused) ${animation_frame} = requestAnimationFrame(${handler});`}
${needsLock && `${lock} = true;`}
ctx.${handler}.call(${this.var}${contextual_dependencies.size > 0 ? ', ctx' : ''});
}
`);
callee = handler;
} else {
this.renderer.component.partly_hoisted.push(deindent`
function ${handler}() {
${
animation_frame && deindent`
cancelAnimationFrame(${animation_frame});
if (!${this.var}.paused) ${animation_frame} = requestAnimationFrame(${handler});`
callee = `ctx.${handler}`;
}
${mutations.length > 0 && mutations}
this.renderer.component.partly_hoisted.push(deindent`
function ${handler}(${contextual_dependencies.size > 0 ? `{ ${[...contextual_dependencies].join(', ')} }` : ``}) {
${group.bindings.map(b => b.handler.mutation)}
${Array.from(dependencies).map(dep => `$$invalidate('${dep}', ${dep});`)}
}
`);
callee = `ctx.${handler}`;
}
group.events.forEach(name => {
if (name === 'resize') {
// special case
@ -513,9 +467,10 @@ export default class ElementWrapper extends Wrapper {
.map(binding => `${binding.snippet} === void 0`)
.join(' || ');
if (this.node.name === 'select' || group.bindings.find(binding => binding.name === 'indeterminate' || binding.isReadOnlyMediaAttribute)) {
if (this.node.name === 'select' || group.bindings.find(binding => binding.node.name === 'indeterminate' || binding.isReadOnlyMediaAttribute())) {
const callback = has_local_function ? handler : `() => ${callee}.call(${this.var})`;
block.builders.hydrate.addLine(
`if (${someInitialStateIsUndefined}) @add_render_callback(() => ${callee}.call(${this.var}));`
`if (${someInitialStateIsUndefined}) @add_render_callback(${callback});`
);
}
@ -526,7 +481,9 @@ export default class ElementWrapper extends Wrapper {
}
});
this.initialUpdate = mungedBindings.map(binding => binding.initialUpdate).filter(Boolean).join('\n');
if (lock) {
block.builders.update.addLine(`${lock} = false;`);
}
const this_binding = this.bindings.find(b => b.node.name === 'this');
if (this_binding) {
@ -534,7 +491,7 @@ export default class ElementWrapper extends Wrapper {
renderer.component.declarations.push(name);
renderer.component.template_references.add(name);
const { handler, object } = this_binding.munge(block);
const { handler, object } = this_binding;
renderer.component.partly_hoisted.push(deindent`
function ${name}($$node) {
@ -785,10 +742,6 @@ export default class ElementWrapper extends Wrapper {
return null;
}
isMediaNode() {
return this.node.name === 'audio' || this.node.name === 'video';
}
remount(name: string) {
const slot = this.attributes.find(attribute => attribute.node.name === 'slot');
if (slot) {

@ -211,7 +211,7 @@ export default class InlineComponentWrapper extends Wrapper {
// bind:x={y} — we can't just do `y = x`, we need to
// to `array[index] = x;
const { name } = binding.expression.node;
const { object, property, snippet } = block.bindings.get(name)();
const { object, property, snippet } = block.bindings.get(name);
lhs = snippet;
// TODO we need to invalidate... something
@ -263,7 +263,7 @@ export default class InlineComponentWrapper extends Wrapper {
// bind:x={y} — we can't just do `y = x`, we need to
// to `array[index] = x;
const { name } = binding.expression.node;
const { object, property, snippet } = block.bindings.get(name)();
const { object, property, snippet } = block.bindings.get(name);
lhs = snippet;
contextual_dependencies.push(object, property);
}

@ -1,8 +1,8 @@
/* generated by Svelte vX.Y.Z */
import { SvelteComponent as SvelteComponent_1, addListener, createElement, detachNode, flush, init, insert, run, safe_not_equal, setAttribute } from "svelte/internal";
import { SvelteComponent as SvelteComponent_1, addListener, createElement, detachNode, flush, init, insert, noop, run, safe_not_equal, setAttribute } from "svelte/internal";
function create_fragment(component, ctx) {
var input, input_updating = false, current, dispose;
var input, current, dispose;
return {
c() {
@ -14,15 +14,10 @@ function create_fragment(component, ctx) {
m(target, anchor) {
insert(target, input, anchor);
input.files = ctx.files;
current = true;
},
p(changed, ctx) {
if (!input_updating && changed.files) input.files = ctx.files;
},
p: noop,
i(target, anchor) {
if (current) return;

@ -2,18 +2,25 @@
import { SvelteComponent as SvelteComponent_1, addListener, add_render_callback, createElement, detachNode, flush, init, insert, run, run_all, safe_not_equal, timeRangesToArray } from "svelte/internal";
function create_fragment(component, ctx) {
var audio, audio_is_paused = true, audio_updating = false, audio_animationframe, current, dispose;
var audio, audio_updating = false, audio_animationframe, audio_is_paused = true, current, dispose;
function audio_timeupdate_handler() {
cancelAnimationFrame(audio_animationframe);
if (!audio.paused) audio_animationframe = requestAnimationFrame(audio_timeupdate_handler);
audio_updating = true;
ctx.audio_timeupdate_handler.call(audio);
}
return {
c() {
audio = createElement("audio");
if (ctx.played === void 0 || ctx.currentTime === void 0) add_render_callback(() => ctx.audio_timeupdate_handler.call(audio));
if (ctx.played === void 0 || ctx.currentTime === void 0) add_render_callback(audio_timeupdate_handler);
if (ctx.duration === void 0) add_render_callback(() => ctx.audio_durationchange_handler.call(audio));
if (ctx.buffered === void 0) add_render_callback(() => ctx.audio_progress_handler.call(audio));
if (ctx.buffered === void 0 || ctx.seekable === void 0) add_render_callback(() => ctx.audio_loadedmetadata_handler.call(audio));
dispose = [
addListener(audio, "timeupdate", ctx.audio_timeupdate_handler),
addListener(audio, "timeupdate", audio_timeupdate_handler),
addListener(audio, "durationchange", ctx.audio_durationchange_handler),
addListener(audio, "play", ctx.audio_play_pause_handler),
addListener(audio, "pause", ctx.audio_play_pause_handler),
@ -32,9 +39,10 @@ function create_fragment(component, ctx) {
},
p(changed, ctx) {
if (!audio_updating && !isNaN(ctx.currentTime) && changed.currentTime) audio.currentTime = ctx.currentTime;
if (!audio_updating && audio_is_paused !== (audio_is_paused = ctx.paused) && changed.paused) audio[audio_is_paused ? "pause" : "play"]();
if (!audio_updating && !isNaN(ctx.volume) && changed.volume) audio.volume = ctx.volume;
if (!audio_updating && changed.currentTime && !isNaN(ctx.currentTime)) audio.currentTime = ctx.currentTime;
if (changed.paused && audio_is_paused !== (audio_is_paused = ctx.paused)) audio[audio_is_paused ? "pause" : "play"]();
if (changed.volume && !isNaN(ctx.volume)) audio.volume = ctx.volume;
audio_updating = false;
},
i(target, anchor) {
@ -58,8 +66,6 @@ function instance($$self, $$props, $$invalidate) {
let { buffered, seekable, played, currentTime, duration, paused, volume } = $$props;
function audio_timeupdate_handler() {
cancelAnimationFrame(audio_animationframe);
if (!audio.paused) audio_animationframe = requestAnimationFrame(audio_timeupdate_handler);
played = timeRangesToArray(this.played);
currentTime = this.currentTime;
$$invalidate('played', played);

Loading…
Cancel
Save