Repository: sveltejs/svelte (JavaScript) — Bug Fix

Issue:
Possible issue with binding back on <:Window> variables
See this REPL: https://svelte.technology/repl?version=1.50.1&gist=a2de5017bab2f388b03e81e91b41fbd9

Have to use indirection to set variables back on the window object.

Discussion here: https://gitter.im/sveltejs/svelte?at=5a4ec8dc03838b2f2a70ce8d


Discussion:

Resolving patch:
diff --git a/src/generators/nodes/Window.ts b/src/generators/nodes/Window.ts
--- a/src/generators/nodes/Window.ts
+++ b/src/generators/nodes/Window.ts
@@ -1,3 +1,4 @@
+import CodeBuilder from '../../utils/CodeBuilder';
 import deindent from '../../utils/deindent';
 import { stringify } from '../../utils/stringify';
 import flattenReference from '../../utils/flattenReference';
@@ -106,6 +107,8 @@ export default class Window extends Node {
 		});
 
 		const lock = block.getUniqueName(`window_updating`);
+		const clear = block.getUniqueName(`clear_window_updating`);
+		const timeout = block.getUniqueName(`window_updating_timeout`);
 
 		Object.keys(events).forEach(event => {
 			const handlerName = block.getUniqueName(`onwindow${event}`);
@@ -114,10 +117,15 @@ export default class Window extends Node {
 			if (event === 'scroll') {
 				// TODO other bidirectional bindings...
 				block.addVariable(lock, 'false');
+				block.addVariable(clear, `function() { ${lock} = false; }`);
+				block.addVariable(timeout);
 			}
 
 			const handlerBody = deindent`
-				${event === 'scroll' && `${lock} = true;`}
+				${event === 'scroll' && deindent`
+					if (${lock}) return;
+					${lock} = true;
+				`}
 				${generator.options.dev && `component._updatingReadonlyProperty = true;`}
 
 				#component.set({
@@ -146,7 +154,8 @@ export default class Window extends Node {
 
 			block.builders.init.addBlock(deindent`
 				function ${observerCallback}() {
-					if (${lock}) return;
+					${lock} = true;
+					clearTimeout(${timeout});
 					var x = ${bindings.scrollX
 						? `#component.get("${bindings.scrollX}")`
 						: `window.scrollX`};
@@ -154,6 +163,7 @@ export default class Window extends Node {
 						? `#component.get("${bindings.scrollY}")`
 						: `window.scrollY`};
 					window.scrollTo(x, y);
+					${timeout} = setTimeout(${clear}, 100);
 				}
 			`);
 
@@ -170,8 +180,10 @@ export default class Window extends Node {
 
 			block.builders.init.addBlock(deindent`
 				#component.observe("${bindings.scrollX || bindings.scrollY}", function(${isX ? 'x' : 'y'}) {
-					if (${lock}) return;
+					${lock} = true;
+					clearTimeout(${timeout});
 					window.scrollTo(${isX ? 'x, window.scrollY' : 'window.scrollX, y'});
+					${timeout} = setTimeout(${clear}, 100);
 				});
 			`);
 		}


Test patch:
diff --git a/test/js/samples/window-binding-scroll/expected-bundle.js b/test/js/samples/window-binding-scroll/expected-bundle.js
--- a/test/js/samples/window-binding-scroll/expected-bundle.js
+++ b/test/js/samples/window-binding-scroll/expected-bundle.js
@@ -190,9 +190,10 @@ var proto = {
 
 /* generated by Svelte vX.Y.Z */
 function create_main_fragment(state, component) {
-	var window_updating = false, p, text, text_1;
+	var window_updating = false, clear_window_updating = function() { window_updating = false; }, window_updating_timeout, p, text, text_1;
 
 	function onwindowscroll(event) {
+		if (window_updating) return;
 		window_updating = true;
 
 		component.set({
@@ -203,8 +204,10 @@ function create_main_fragment(state, component) {
 	window.addEventListener("scroll", onwindowscroll);
 
 	component.observe("y", function(y) {
-		if (window_updating) return;
+		window_updating = true;
+		clearTimeout(window_updating_timeout);
 		window.scrollTo(window.scrollX, y);
+		window_updating_timeout = setTimeout(clear_window_updating, 100);
 	});
 
 	return {
diff --git a/test/js/samples/window-binding-scroll/expected.js b/test/js/samples/window-binding-scroll/expected.js
--- a/test/js/samples/window-binding-scroll/expected.js
+++ b/test/js/samples/window-binding-scroll/expected.js
@@ -2,9 +2,10 @@
 import { appendNode, assign, createElement, createText, detachNode, init, insertNode, proto } from "svelte/shared.js";
 
 function create_main_fragment(state, component) {
-	var window_updating = false, p, text, text_1;
+	var window_updating = false, clear_window_updating = function() { window_updating = false; }, window_updating_timeout, p, text, text_1;
 
 	function onwindowscroll(event) {
+		if (window_updating) return;
 		window_updating = true;
 
 		component.set({
@@ -15,8 +16,10 @@ function create_main_fragment(state, component) {
 	window.addEventListener("scroll", onwindowscroll);
 
 	component.observe("y", function(y) {
-		if (window_updating) return;
+		window_updating = true;
+		clearTimeout(window_updating_timeout);
 		window.scrollTo(window.scrollX, y);
+		window_updating_timeout = setTimeout(clear_window_updating, 100);
 	});
 
 	return {