@@ -5,11 +5,13 @@ import namespaces from '../../utils/namespaces.js';
5
5
import processCss from '../shared/css/process.js' ;
6
6
import visitors from './visitors/index.js' ;
7
7
import Generator from '../Generator.js' ;
8
+ import * as shared from '../../shared/index.js' ;
8
9
9
10
class DomGenerator extends Generator {
10
11
constructor ( parsed , source , names , visitors ) {
11
12
super ( parsed , source , names , visitors ) ;
12
13
this . renderers = [ ] ;
14
+ this . uses = { } ;
13
15
}
14
16
15
17
addElement ( name , renderStatement , needsIdentifier = false ) {
@@ -21,15 +23,13 @@ class DomGenerator extends Generator {
21
23
22
24
this . createMountStatement ( name ) ;
23
25
} else {
24
- this . current . builders . init . addLine (
25
- `${ this . current . target } .appendChild( ${ renderStatement } );`
26
- ) ;
26
+ this . uses . appendNode = true ;
27
+ this . current . builders . init . addLine ( `appendNode( ${ renderStatement } , ${ this . current . target } );` ) ;
27
28
}
28
29
29
30
if ( isToplevel ) {
30
- this . current . builders . detach . addLine (
31
- `${ name } .parentNode.removeChild( ${ name } );`
32
- ) ;
31
+ this . uses . detachNode = true ;
32
+ this . current . builders . detach . addLine ( `detachNode( ${ name } );` ) ;
33
33
}
34
34
}
35
35
@@ -54,19 +54,38 @@ class DomGenerator extends Generator {
54
54
55
55
if ( fragment . key ) properties . addBlock ( `key: key,` ) ;
56
56
57
- properties . addBlock ( deindent `
58
- mount: function ( target, anchor ) {
59
- ${ fragment . builders . mount }
60
- },
57
+ if ( fragment . builders . mount . isEmpty ( ) ) {
58
+ this . uses . noop = true ;
59
+ properties . addBlock ( `mount: noop,` ) ;
60
+ } else {
61
+ properties . addBlock ( deindent `
62
+ mount: function ( target, anchor ) {
63
+ ${ fragment . builders . mount }
64
+ },
65
+ ` ) ;
66
+ }
61
67
62
- update: function ( changed, ${ fragment . params } ) {
63
- ${ fragment . builders . update }
64
- },
68
+ if ( fragment . builders . update . isEmpty ( ) ) {
69
+ this . uses . noop = true ;
70
+ properties . addBlock ( `update: noop,` ) ;
71
+ } else {
72
+ properties . addBlock ( deindent `
73
+ update: function ( changed, ${ fragment . params } ) {
74
+ ${ fragment . builders . update }
75
+ },
76
+ ` ) ;
77
+ }
65
78
66
- teardown: function ( detach ) {
67
- ${ fragment . builders . teardown }
68
- }
69
- ` ) ;
79
+ if ( fragment . builders . teardown . isEmpty ( ) ) {
80
+ this . uses . noop = true ;
81
+ properties . addBlock ( `teardown: noop,` ) ;
82
+ } else {
83
+ properties . addBlock ( deindent `
84
+ teardown: function ( detach ) {
85
+ ${ fragment . builders . teardown }
86
+ },
87
+ ` ) ;
88
+ }
70
89
71
90
this . renderers . push ( deindent `
72
91
function ${ fragment . name } ( ${ fragment . params } , component${ fragment . key ? `, key` : '' } ) {
@@ -80,18 +99,18 @@ class DomGenerator extends Generator {
80
99
}
81
100
82
101
createAnchor ( name , description = '' ) {
83
- const renderStatement = `document.createComment( ${ JSON . stringify ( description ) } )` ;
102
+ this . uses . createComment = true ;
103
+ const renderStatement = `createComment( ${ JSON . stringify ( description ) } )` ;
84
104
this . addElement ( name , renderStatement , true ) ;
85
105
}
86
106
87
107
createMountStatement ( name ) {
88
108
if ( this . current . target === 'target' ) {
89
- this . current . builders . mount . addLine (
90
- `target.insertBefore( ${ name } , anchor );`
91
- ) ;
109
+ this . uses . insertNode = true ;
110
+ this . current . builders . mount . addLine ( `insertNode( ${ name } , target, anchor );` ) ;
92
111
} else {
93
- this . current . builders . init . addLine (
94
- ` ${ this . current . target } .appendChild( ${ name } );` ) ;
112
+ this . uses . appendNode = true ;
113
+ this . current . builders . init . addLine ( `appendNode( ${ name } , ${ this . current . target } );` ) ;
95
114
}
96
115
}
97
116
@@ -158,8 +177,8 @@ export default function dom ( parsed, source, options, names ) {
158
177
set : new CodeBuilder ( )
159
178
} ;
160
179
161
- builders . set . addLine ( 'var oldState = state ;' ) ;
162
- builders . set . addLine ( 'state = Object.assign( {}, oldState, newState );' ) ;
180
+ builders . set . addLine ( 'var oldState = this._state ;' ) ;
181
+ builders . set . addLine ( 'this._state = Object.assign( {}, oldState, newState );' ) ;
163
182
164
183
if ( computations . length ) {
165
184
const builder = new CodeBuilder ( ) ;
@@ -178,26 +197,30 @@ export default function dom ( parsed, source, options, names ) {
178
197
}
179
198
` ) ;
180
199
181
- builders . set . addLine ( `applyComputations( state , newState, oldState )` ) ;
200
+ builders . set . addLine ( `applyComputations( this._state , newState, oldState )` ) ;
182
201
}
183
202
203
+ // TODO is the `if` necessary?
184
204
builders . set . addBlock ( deindent `
185
- dispatchObservers( observers.immediate , newState, oldState );
186
- if ( mainFragment ) mainFragment. update( newState, state );
187
- dispatchObservers( observers.deferred , newState, oldState );
205
+ dispatchObservers( this, this._observers.pre , newState, oldState );
206
+ if ( this._fragment ) this._fragment. update( newState, this._state );
207
+ dispatchObservers( this, this._observers.post , newState, oldState );
188
208
` ) ;
189
209
190
210
if ( parsed . js ) {
191
211
builders . main . addBlock ( `[✂${ parsed . js . content . start } -${ parsed . js . content . end } ✂]` ) ;
192
212
}
193
213
194
214
if ( parsed . css && options . css !== false ) {
215
+ generator . uses . appendNode = true ;
216
+ generator . uses . createElement = true ;
217
+
195
218
builders . main . addBlock ( deindent `
196
219
let addedCss = false;
197
220
function addCss () {
198
- var style = document. createElement( 'style' );
221
+ var style = createElement( 'style' );
199
222
style.textContent = ${ JSON . stringify ( processCss ( parsed ) ) } ;
200
- document.head.appendChild ( style );
223
+ appendNode ( style, document.head );
201
224
202
225
addedCss = true;
203
226
}
@@ -212,29 +235,29 @@ export default function dom ( parsed, source, options, names ) {
212
235
}
213
236
214
237
if ( generator . hasComponents ) {
215
- builders . init . addLine ( `this.__renderHooks = [];` ) ;
238
+ builders . init . addLine ( `this._renderHooks = [];` ) ;
216
239
}
217
240
218
241
if ( generator . hasComplexBindings ) {
219
242
builders . init . addBlock ( deindent `
220
- this.__bindings = [];
221
- var mainFragment = renderMainFragment( state , this );
222
- if ( options.target ) this._mount ( options.target );
223
- while ( this.__bindings .length ) this.__bindings .pop()();
243
+ this._bindings = [];
244
+ this._fragment = renderMainFragment( this._state , this );
245
+ if ( options.target ) this._fragment.mount ( options.target, null );
246
+ while ( this._bindings .length ) this._bindings .pop()();
224
247
` ) ;
225
248
226
- builders . set . addLine ( `while ( this.__bindings .length ) this.__bindings .pop()();` ) ;
249
+ builders . set . addLine ( `while ( this._bindings .length ) this._bindings .pop()();` ) ;
227
250
} else {
228
251
builders . init . addBlock ( deindent `
229
- var mainFragment = renderMainFragment( state , this );
230
- if ( options.target ) this._mount ( options.target );
252
+ this._fragment = renderMainFragment( this._state , this );
253
+ if ( options.target ) this._fragment.mount ( options.target, null );
231
254
` ) ;
232
255
}
233
256
234
257
if ( generator . hasComponents ) {
235
258
const statement = deindent `
236
- while ( this.__renderHooks .length ) {
237
- var hook = this.__renderHooks .pop();
259
+ while ( this._renderHooks .length ) {
260
+ var hook = this._renderHooks .pop();
238
261
hook.fn.call( hook.context );
239
262
}
240
263
` ;
@@ -245,8 +268,8 @@ export default function dom ( parsed, source, options, names ) {
245
268
246
269
if ( templateProperties . onrender ) {
247
270
builders . init . addBlock ( deindent `
248
- if ( options.root ) {
249
- options.root.__renderHooks .push({ fn: template.onrender, context: this });
271
+ if ( options._root ) {
272
+ options._root._renderHooks .push({ fn: template.onrender, context: this });
250
273
} else {
251
274
template.onrender.call( this );
252
275
}
@@ -258,111 +281,56 @@ export default function dom ( parsed, source, options, names ) {
258
281
builders . main . addBlock ( deindent `
259
282
function ${ name } ( options ) {
260
283
options = options || {};
284
+ ${ generator . usesRefs ? `\nthis.refs = {}` : `` }
285
+ this._state = ${ initialState } ;${ templateProperties . computed ? `\napplyComputations( this._state, this._state, {} );` : `` }
261
286
262
- var component = this;${ generator . usesRefs ? `\nthis.refs = {}` : `` }
263
- var state = ${ initialState } ;${ templateProperties . computed ? `\napplyComputations( state, state, {} );` : `` }
264
-
265
- var observers = {
266
- immediate: Object.create( null ),
267
- deferred: Object.create( null )
268
- };
269
-
270
- var callbacks = Object.create( null );
271
-
272
- function dispatchObservers ( group, newState, oldState ) {
273
- for ( var key in group ) {
274
- if ( !( key in newState ) ) continue;
275
-
276
- var newValue = newState[ key ];
277
- var oldValue = oldState[ key ];
278
-
279
- if ( newValue === oldValue && typeof newValue !== 'object' ) continue;
280
-
281
- var callbacks = group[ key ];
282
- if ( !callbacks ) continue;
283
-
284
- for ( var i = 0; i < callbacks.length; i += 1 ) {
285
- var callback = callbacks[i];
286
- if ( callback.__calling ) continue;
287
-
288
- callback.__calling = true;
289
- callback.call( component, newValue, oldValue );
290
- callback.__calling = false;
291
- }
292
- }
293
- }
294
-
295
- this.fire = function fire ( eventName, data ) {
296
- var handlers = eventName in callbacks && callbacks[ eventName ].slice();
297
- if ( !handlers ) return;
298
-
299
- for ( var i = 0; i < handlers.length; i += 1 ) {
300
- handlers[i].call( this, data );
301
- }
302
- };
303
-
304
- this.get = function get ( key ) {
305
- return key ? state[ key ] : state;
287
+ this._observers = {
288
+ pre: Object.create( null ),
289
+ post: Object.create( null )
306
290
};
307
291
308
- this.set = function set ( newState ) {
309
- ${ builders . set }
310
- };
311
-
312
- this._mount = function mount ( target, anchor ) {
313
- mainFragment.mount( target, anchor );
314
- }
292
+ this._handlers = Object.create( null );
315
293
316
- this.observe = function ( key, callback, options ) {
317
- var group = ( options && options.defer ) ? observers.deferred : observers.immediate ;
294
+ this._root = options._root;
295
+ this._yield = options._yield ;
318
296
319
- ( group[ key ] || ( group[ key ] = [] ) ).push( callback );
297
+ ${ builders . init }
298
+ }
299
+ ` ) ;
320
300
321
- if ( !options || options.init !== false ) {
322
- callback.__calling = true;
323
- callback.call( component, state[ key ] );
324
- callback.__calling = false;
325
- }
301
+ if ( templateProperties . methods ) {
302
+ builders . main . addBlock ( `${ name } .prototype = template.methods;` ) ;
303
+ }
326
304
327
- return {
328
- cancel: function () {
329
- var index = group[ key ].indexOf( callback );
330
- if ( ~index ) group[ key ].splice( index, 1 );
331
- }
332
- };
333
- };
305
+ builders . main . addBlock ( deindent `
306
+ ${ name } .prototype.get = ${ shared . get } ;
334
307
335
- this.on = function on ( eventName, handler ) {
336
- var handlers = callbacks[ eventName ] || ( callbacks[ eventName ] = [] );
337
- handlers.push( handler );
308
+ ${ name } .prototype.fire = ${ shared . fire } ;
338
309
339
- return {
340
- cancel: function () {
341
- var index = handlers.indexOf( handler );
342
- if ( ~index ) handlers.splice( index, 1 );
343
- }
344
- };
345
- };
310
+ ${ name } .prototype.observe = ${ shared . observe } ;
346
311
347
- this.teardown = function teardown ( detach ) {
348
- this.fire( 'teardown' );${ templateProperties . onteardown ? `\ntemplate.onteardown.call( this );` : `` }
312
+ ${ name } .prototype.on = ${ shared . on } ;
349
313
350
- mainFragment.teardown( detach !== false );
351
- mainFragment = null;
314
+ ${ name } .prototype.set = function set ( newState ) {
315
+ ${ builders . set }
316
+ };
352
317
353
- state = {};
354
- };
318
+ ${ name } .prototype.teardown = function teardown ( detach ) {
319
+ this.fire( 'teardown' ); ${ templateProperties . onteardown ? `\ntemplate.onteardown.call( this );` : `` }
355
320
356
- this.root = options.root ;
357
- this.yield = options.yield ;
321
+ this._fragment.teardown( detach !== false ) ;
322
+ this._fragment = null ;
358
323
359
- ${ builders . init }
360
- }
324
+ this._state = {};
325
+ };
361
326
` ) ;
362
327
363
- if ( templateProperties . methods ) {
364
- builders . main . addBlock ( `${ name } .prototype = template.methods;` ) ;
365
- }
328
+ builders . main . addBlock ( shared . dispatchObservers . toString ( ) ) ;
329
+
330
+ Object . keys ( generator . uses ) . forEach ( key => {
331
+ const fn = shared [ key ] ; // eslint-disable-line import/namespace
332
+ builders . main . addBlock ( fn . toString ( ) ) ;
333
+ } ) ;
366
334
367
335
return generator . generate ( builders . main . toString ( ) , options , { name, format } ) ;
368
336
}
0 commit comments