1
1
import flattenReference from '../../../../../utils/flattenReference.js' ;
2
2
import deindent from '../../../../../utils/deindent.js' ;
3
+ import CodeBuilder from '../../../../../utils/CodeBuilder.js' ;
3
4
4
5
const associatedEvents = {
5
6
innerWidth : 'resize' ,
@@ -13,6 +14,7 @@ const associatedEvents = {
13
14
14
15
export default function visitWindow ( generator , block , node ) {
15
16
const events = { } ;
17
+ const bindings = { } ;
16
18
17
19
node . attributes . forEach ( attribute => {
18
20
if ( attribute . type === 'EventHandler' ) {
@@ -40,17 +42,22 @@ export default function visitWindow ( generator, block, node ) {
40
42
}
41
43
42
44
if ( attribute . type === 'Binding' ) {
45
+ if ( attribute . value . type !== 'Identifier' ) {
46
+ const { parts, keypath } = flattenReference ( attribute . value ) ;
47
+ throw new Error ( `Bindings on <:Window/> must be to top-level properties, e.g. '${ parts . pop ( ) } ' rather than '${ keypath } '` ) ;
48
+ }
49
+
50
+ bindings [ attribute . name ] = attribute . value . name ;
51
+
52
+ // bind:online is a special case, we need to listen for two separate events
53
+ if ( attribute . name === 'online' ) return ;
54
+
43
55
const associatedEvent = associatedEvents [ attribute . name ] ;
44
56
45
57
if ( ! associatedEvent ) {
46
58
throw new Error ( `Cannot bind to ${ attribute . name } on <:Window>` ) ;
47
59
}
48
60
49
- if ( attribute . value . type !== 'Identifier' ) {
50
- const { parts, keypath } = flattenReference ( attribute . value ) ;
51
- throw new Error ( `Bindings on <:Window/> must be to top-level properties, e.g. '${ parts . pop ( ) } ' rather than '${ keypath } '` ) ;
52
- }
53
-
54
61
if ( ! events [ associatedEvent ] ) events [ associatedEvent ] = [ ] ;
55
62
events [ associatedEvent ] . push ( `${ attribute . value . name } : this.${ attribute . name } ` ) ;
56
63
@@ -61,16 +68,31 @@ export default function visitWindow ( generator, block, node ) {
61
68
}
62
69
} ) ;
63
70
71
+ const lock = block . getUniqueName ( `window_updating` ) ;
72
+
64
73
Object . keys ( events ) . forEach ( event => {
65
74
const handlerName = block . getUniqueName ( `onwindow${ event } ` ) ;
66
-
67
75
const props = events [ event ] . join ( ',\n' ) ;
68
76
77
+ const handlerBody = new CodeBuilder ( ) ;
78
+ if ( event === 'scroll' ) { // TODO other bidirectional bindings...
79
+ block . builders . create . addLine ( `var ${ lock } = false;` ) ;
80
+ handlerBody . addLine ( `${ lock } = true;` ) ;
81
+ }
82
+
83
+ handlerBody . addBlock ( deindent `
84
+ ${ block . component } .set({
85
+ ${ props }
86
+ });
87
+ ` ) ;
88
+
89
+ if ( event === 'scroll' ) {
90
+ handlerBody . addLine ( `${ lock } = false;` ) ;
91
+ }
92
+
69
93
block . builders . create . addBlock ( deindent `
70
- var ${ handlerName } = function ( event ) {
71
- ${ block . component } .set({
72
- ${ props }
73
- });
94
+ function ${ handlerName } ( event ) {
95
+ ${ handlerBody }
74
96
};
75
97
window.addEventListener( '${ event } ', ${ handlerName } );
76
98
` ) ;
@@ -79,4 +101,52 @@ export default function visitWindow ( generator, block, node ) {
79
101
window.removeEventListener( '${ event } ', ${ handlerName } );
80
102
` ) ;
81
103
} ) ;
104
+
105
+ // special case... might need to abstract this out if we add more special cases
106
+ if ( bindings . scrollX && bindings . scrollY ) {
107
+ const observerCallback = block . getUniqueName ( `scrollobserver` ) ;
108
+
109
+ block . builders . create . addBlock ( deindent `
110
+ function ${ observerCallback } () {
111
+ if ( ${ lock } ) return;
112
+ var x = ${ bindings . scrollX ? `component.get( '${ bindings . scrollX } ' )` : `window.scrollX` } ;
113
+ var y = ${ bindings . scrollY ? `component.get( '${ bindings . scrollY } ' )` : `window.scrollY` } ;
114
+ window.scrollTo( x, y );
115
+ };
116
+ ` ) ;
117
+
118
+ if ( bindings . scrollX ) block . builders . create . addLine ( `component.observe( '${ bindings . scrollX } ', ${ observerCallback } );` ) ;
119
+ if ( bindings . scrollY ) block . builders . create . addLine ( `component.observe( '${ bindings . scrollY } ', ${ observerCallback } );` ) ;
120
+ } else if ( bindings . scrollX || bindings . scrollY ) {
121
+ const isX = ! ! bindings . scrollX ;
122
+
123
+ block . builders . create . addBlock ( deindent `
124
+ component.observe( '${ bindings . scrollX || bindings . scrollY } ', function ( ${ isX ? 'x' : 'y' } ) {
125
+ if ( ${ lock } ) return;
126
+ window.scrollTo( ${ isX ? 'x, window.scrollY' : 'window.scrollX, y' } );
127
+ });
128
+ ` ) ;
129
+ }
130
+
131
+ // another special case. (I'm starting to think these are all special cases.)
132
+ if ( bindings . online ) {
133
+ const handlerName = block . getUniqueName ( `onlinestatuschanged` ) ;
134
+ block . builders . create . addBlock ( deindent `
135
+ function ${ handlerName } ( event ) {
136
+ component.set({ ${ bindings . online } : navigator.onLine });
137
+ };
138
+ window.addEventListener( 'online', ${ handlerName } );
139
+ window.addEventListener( 'offline', ${ handlerName } );
140
+ ` ) ;
141
+
142
+ // add initial value
143
+ generator . builders . metaBindings . addLine (
144
+ `this._state.${ bindings . online } = navigator.onLine;`
145
+ ) ;
146
+
147
+ block . builders . destroy . addBlock ( deindent `
148
+ window.removeEventListener( 'online', ${ handlerName } );
149
+ window.removeEventListener( 'offline', ${ handlerName } );
150
+ ` ) ;
151
+ }
82
152
}
0 commit comments