diff --git a/src/compiler/compile/nodes/Binding.ts b/src/compiler/compile/nodes/Binding.ts index 969232a17c30..eaf2709da29f 100644 --- a/src/compiler/compile/nodes/Binding.ts +++ b/src/compiler/compile/nodes/Binding.ts @@ -11,7 +11,9 @@ const read_only_media_attributes = new Set([ 'duration', 'buffered', 'seekable', - 'played' + 'played', + 'seeking', + 'ended' ]); export default class Binding extends Node { diff --git a/src/compiler/compile/nodes/Element.ts b/src/compiler/compile/nodes/Element.ts index 555c772f232a..ea0160e2b483 100644 --- a/src/compiler/compile/nodes/Element.ts +++ b/src/compiler/compile/nodes/Element.ts @@ -596,7 +596,9 @@ export default class Element extends Node { name === 'seekable' || name === 'played' || name === 'volume' || - name === 'playbackRate' + name === 'playbackRate' || + name === 'seeking' || + name === 'ended' ) { if (this.name !== 'audio' && this.name !== 'video') { component.error(binding, { diff --git a/src/compiler/compile/render_dom/wrappers/Element/Binding.ts b/src/compiler/compile/render_dom/wrappers/Element/Binding.ts index 90727743fa4a..9618ad3c2cc0 100644 --- a/src/compiler/compile/render_dom/wrappers/Element/Binding.ts +++ b/src/compiler/compile/render_dom/wrappers/Element/Binding.ts @@ -86,6 +86,7 @@ export default class BindingWrapper { const { parent } = this; const update_conditions: any[] = this.needs_lock ? [x`!${lock}`] : []; + const mount_conditions: any[] = []; const dependency_array = [...this.node.expression.dependencies]; @@ -103,6 +104,7 @@ export default class BindingWrapper { // model to view let update_dom = get_dom_updater(parent, this); + let mount_dom = update_dom; // special cases switch (this.node.name) { @@ -122,16 +124,23 @@ export default class BindingWrapper { case 'textContent': update_conditions.push(x`${this.snippet} !== ${parent.var}.textContent`); + mount_conditions.push(x`${this.snippet} !== void 0`); break; case 'innerHTML': update_conditions.push(x`${this.snippet} !== ${parent.var}.innerHTML`); + mount_conditions.push(x`${this.snippet} !== void 0`); break; case 'currentTime': + update_conditions.push(x`!@_isNaN(${this.snippet})`); + mount_dom = null; + break; + case 'playbackRate': case 'volume': update_conditions.push(x`!@_isNaN(${this.snippet})`); + mount_conditions.push(x`!@_isNaN(${this.snippet})`); break; case 'paused': @@ -142,12 +151,14 @@ export default class BindingWrapper { update_conditions.push(x`${last} !== (${last} = ${this.snippet})`); update_dom = b`${parent.var}[${last} ? "pause" : "play"]();`; + mount_dom = null; break; } case 'value': if (parent.node.get_static_attribute_value('type') === 'file') { update_dom = null; + mount_dom = null; } } @@ -165,13 +176,18 @@ export default class BindingWrapper { } } - if (this.node.name === 'innerHTML' || this.node.name === 'textContent') { - block.chunks.mount.push(b` - if (${this.snippet} !== void 0) { - ${update_dom} - }`); - } else if (!/(currentTime|paused)/.test(this.node.name)) { - block.chunks.mount.push(update_dom); + if (mount_dom) { + if (mount_conditions.length > 0) { + const condition = mount_conditions.reduce((lhs, rhs) => x`${lhs} && ${rhs}`); + + block.chunks.mount.push(b` + if (${condition}) { + ${mount_dom} + } + `); + } else { + block.chunks.mount.push(mount_dom); + } } } } diff --git a/src/compiler/compile/render_dom/wrappers/Element/index.ts b/src/compiler/compile/render_dom/wrappers/Element/index.ts index 75c2cc60969b..54d5a23932ed 100644 --- a/src/compiler/compile/render_dom/wrappers/Element/index.ts +++ b/src/compiler/compile/render_dom/wrappers/Element/index.ts @@ -62,7 +62,7 @@ const events = [ event_names: ['timeupdate'], filter: (node: Element, name: string) => node.is_media_node() && - (name === 'currentTime' || name === 'played') + (name === 'currentTime' || name === 'played' || name === 'ended') }, { event_names: ['durationchange'], @@ -100,6 +100,18 @@ const events = [ node.is_media_node() && name === 'playbackRate' }, + { + event_names: ['seeking', 'seeked'], + filter: (node: Element, name: string) => + node.is_media_node() && + (name === 'seeking') + }, + { + event_names: ['ended'], + filter: (node: Element, name: string) => + node.is_media_node() && + name === 'ended' + }, // details event { diff --git a/test/js/samples/media-bindings/expected.js b/test/js/samples/media-bindings/expected.js index 1d90e0849e43..0538f07055e4 100644 --- a/test/js/samples/media-bindings/expected.js +++ b/test/js/samples/media-bindings/expected.js @@ -35,10 +35,12 @@ function create_fragment(ctx) { return { c() { audio = element("audio"); - if (ctx.played === void 0 || ctx.currentTime === void 0) add_render_callback(audio_timeupdate_handler); + if (ctx.played === void 0 || ctx.currentTime === void 0 || ctx.ended === 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)); + if (ctx.seeking === void 0) add_render_callback(() => ctx.audio_seeking_seeked_handler.call(audio)); + if (ctx.ended === void 0) add_render_callback(() => ctx.audio_ended_handler.call(audio)); dispose = [ listen(audio, "timeupdate", audio_timeupdate_handler), @@ -48,13 +50,22 @@ function create_fragment(ctx) { listen(audio, "progress", ctx.audio_progress_handler), listen(audio, "loadedmetadata", ctx.audio_loadedmetadata_handler), listen(audio, "volumechange", ctx.audio_volumechange_handler), - listen(audio, "ratechange", ctx.audio_ratechange_handler) + listen(audio, "ratechange", ctx.audio_ratechange_handler), + listen(audio, "seeking", ctx.audio_seeking_seeked_handler), + listen(audio, "seeked", ctx.audio_seeking_seeked_handler), + listen(audio, "ended", ctx.audio_ended_handler) ]; }, m(target, anchor) { insert(target, audio, anchor); - audio.volume = ctx.volume; - audio.playbackRate = ctx.playbackRate; + + if (!isNaN(ctx.volume)) { + audio.volume = ctx.volume; + } + + if (!isNaN(ctx.playbackRate)) { + audio.playbackRate = ctx.playbackRate; + } }, p(changed, ctx) { if (!audio_updating && changed.currentTime && !isNaN(ctx.currentTime)) { @@ -93,12 +104,16 @@ function instance($$self, $$props, $$invalidate) { let { paused } = $$props; let { volume } = $$props; let { playbackRate } = $$props; + let { seeking } = $$props; + let { ended } = $$props; function audio_timeupdate_handler() { played = time_ranges_to_array(this.played); currentTime = this.currentTime; + ended = this.ended; $$invalidate("played", played); $$invalidate("currentTime", currentTime); + $$invalidate("ended", ended); } function audio_durationchange_handler() { @@ -133,6 +148,16 @@ function instance($$self, $$props, $$invalidate) { $$invalidate("playbackRate", playbackRate); } + function audio_seeking_seeked_handler() { + seeking = this.seeking; + $$invalidate("seeking", seeking); + } + + function audio_ended_handler() { + ended = this.ended; + $$invalidate("ended", ended); + } + $$self.$set = $$props => { if ("buffered" in $$props) $$invalidate("buffered", buffered = $$props.buffered); if ("seekable" in $$props) $$invalidate("seekable", seekable = $$props.seekable); @@ -142,6 +167,8 @@ function instance($$self, $$props, $$invalidate) { if ("paused" in $$props) $$invalidate("paused", paused = $$props.paused); if ("volume" in $$props) $$invalidate("volume", volume = $$props.volume); if ("playbackRate" in $$props) $$invalidate("playbackRate", playbackRate = $$props.playbackRate); + if ("seeking" in $$props) $$invalidate("seeking", seeking = $$props.seeking); + if ("ended" in $$props) $$invalidate("ended", ended = $$props.ended); }; return { @@ -153,13 +180,17 @@ function instance($$self, $$props, $$invalidate) { paused, volume, playbackRate, + seeking, + ended, audio_timeupdate_handler, audio_durationchange_handler, audio_play_pause_handler, audio_progress_handler, audio_loadedmetadata_handler, audio_volumechange_handler, - audio_ratechange_handler + audio_ratechange_handler, + audio_seeking_seeked_handler, + audio_ended_handler }; } @@ -175,7 +206,9 @@ class Component extends SvelteComponent { duration: 0, paused: 0, volume: 0, - playbackRate: 0 + playbackRate: 0, + seeking: 0, + ended: 0 }); } } diff --git a/test/js/samples/media-bindings/input.svelte b/test/js/samples/media-bindings/input.svelte index 4b5793ba9343..a079a2e0e580 100644 --- a/test/js/samples/media-bindings/input.svelte +++ b/test/js/samples/media-bindings/input.svelte @@ -7,6 +7,8 @@ export let paused; export let volume; export let playbackRate; + export let seeking; + export let ended; -