Fix media bindings, simplify others

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

@ -29,7 +29,7 @@ export default class Block {
dependencies: Set<string>; 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>; contextOwners: Map<string, EachBlockWrapper>;
builders: { builders: {

@ -97,15 +97,33 @@ export default class EachBlockWrapper extends Wrapper {
this.indexName = this.node.index || renderer.component.getUniqueName(`${this.node.context}_index`); 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 => { node.contexts.forEach(prop => {
this.block.contextOwners.set(prop.key.name, this); 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, object: this.vars.each_block_value,
property: this.indexName, property: this.indexName,
snippet: `${this.vars.each_block_value}[${this.indexName}]${prop.tail}` snippet: `${this.vars.each_block_value}[${this.indexName}]${prop.tail}`
})); });
}); });
if (this.node.index) { if (this.node.index) {
@ -147,30 +165,15 @@ export default class EachBlockWrapper extends Wrapper {
const { renderer } = this; const { renderer } = this;
const { component } = renderer; 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 const needsAnchor = this.next
? !this.next.isDomNode() : ? !this.next.isDomNode() :
!parentNode || !this.parent.isDomNode(); !parentNode || !this.parent.isDomNode();
this.vars = { this.vars.anchor = needsAnchor
anchor: needsAnchor ? block.getUniqueName(`${this.var}_anchor`)
? block.getUniqueName(`${this.var}_anchor`) : (this.next && this.next.var) || 'null';
: (this.next && this.next.var) || 'null',
create_each_block: this.block.name, this.vars.mountOrIntro = (this.block.hasIntroMethod || this.block.hasOutroMethod) ? 'i' : 'm';
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.contextProps = this.node.contexts.map(prop => `child_ctx.${prop.key.name} = list[i]${prop.tail};`); 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 // TODO neaten this up... will end up with an empty line in the block
block.builders.init.addBlock(deindent` 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} = ${this.else.block.name}(#component, ctx);
${each_block_else}.c(); ${each_block_else}.c();
} }
@ -228,9 +231,9 @@ export default class EachBlockWrapper extends Wrapper {
if (this.else.block.hasUpdateMethod) { if (this.else.block.hasUpdateMethod) {
block.builders.update.addBlock(deindent` 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); ${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} = ${this.else.block.name}(#component, ctx);
${each_block_else}.c(); ${each_block_else}.c();
${each_block_else}.${mountOrIntro}(${initialMountNode}, ${this.vars.anchor}); ${each_block_else}.${mountOrIntro}(${initialMountNode}, ${this.vars.anchor});
@ -241,7 +244,7 @@ export default class EachBlockWrapper extends Wrapper {
`); `);
} else { } else {
block.builders.update.addBlock(deindent` 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}) { if (${each_block_else}) {
${each_block_else}.d(1); ${each_block_else}.d(1);
${each_block_else} = null; ${each_block_else} = null;

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

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

@ -1,8 +1,8 @@
/* generated by Svelte vX.Y.Z */ /* 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) { function create_fragment(component, ctx) {
var input, input_updating = false, current, dispose; var input, current, dispose;
return { return {
c() { c() {
@ -14,15 +14,10 @@ function create_fragment(component, ctx) {
m(target, anchor) { m(target, anchor) {
insert(target, input, anchor); insert(target, input, anchor);
input.files = ctx.files;
current = true; current = true;
}, },
p(changed, ctx) { p: noop,
if (!input_updating && changed.files) input.files = ctx.files;
},
i(target, anchor) { i(target, anchor) {
if (current) return; 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"; 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) { 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 { return {
c() { c() {
audio = createElement("audio"); 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.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) 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)); if (ctx.buffered === void 0 || ctx.seekable === void 0) add_render_callback(() => ctx.audio_loadedmetadata_handler.call(audio));
dispose = [ dispose = [
addListener(audio, "timeupdate", ctx.audio_timeupdate_handler), addListener(audio, "timeupdate", audio_timeupdate_handler),
addListener(audio, "durationchange", ctx.audio_durationchange_handler), addListener(audio, "durationchange", ctx.audio_durationchange_handler),
addListener(audio, "play", ctx.audio_play_pause_handler), addListener(audio, "play", ctx.audio_play_pause_handler),
addListener(audio, "pause", 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) { p(changed, ctx) {
if (!audio_updating && !isNaN(ctx.currentTime) && changed.currentTime) audio.currentTime = ctx.currentTime; if (!audio_updating && changed.currentTime && !isNaN(ctx.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 (changed.paused && audio_is_paused !== (audio_is_paused = ctx.paused)) audio[audio_is_paused ? "pause" : "play"]();
if (!audio_updating && !isNaN(ctx.volume) && changed.volume) audio.volume = ctx.volume; if (changed.volume && !isNaN(ctx.volume)) audio.volume = ctx.volume;
audio_updating = false;
}, },
i(target, anchor) { i(target, anchor) {
@ -58,8 +66,6 @@ function instance($$self, $$props, $$invalidate) {
let { buffered, seekable, played, currentTime, duration, paused, volume } = $$props; let { buffered, seekable, played, currentTime, duration, paused, volume } = $$props;
function audio_timeupdate_handler() { function audio_timeupdate_handler() {
cancelAnimationFrame(audio_animationframe);
if (!audio.paused) audio_animationframe = requestAnimationFrame(audio_timeupdate_handler);
played = timeRangesToArray(this.played); played = timeRangesToArray(this.played);
currentTime = this.currentTime; currentTime = this.currentTime;
$$invalidate('played', played); $$invalidate('played', played);

Loading…
Cancel
Save