11import createLogger from "debug" ;
2+ import System from "systemjs" ;
23const debug = createLogger ( "WebIO:Scope" ) ;
34
45import WebIONode , { WebIONodeDataBase , WebIONodeParams , WebIONodeType } from "./Node" ;
@@ -8,6 +9,7 @@ import {WebIOCommand, WebIOMessage} from "./message";
89import { createWebIOEventListener } from "./events" ;
910import WebIODomNode from "./DomNode" ;
1011import createNode from "./createNode" ;
12+ import { BlockImport , importBlock } from "./imports" ;
1113
1214/**
1315 * A map of observable names to arrays-of-listeners that should be
@@ -22,7 +24,7 @@ export interface ScopeListeners {
2224}
2325
2426export interface ScopePromises {
25- importsLoaded : Promise < void > ; // TODO
27+ importsLoaded : Promise < any [ ] | null > ;
2628 connected : Promise < WebIOScope > ;
2729}
2830
@@ -70,14 +72,26 @@ export interface ScopeNodeData extends WebIONodeDataBase {
7072 handlers ?: {
7173 [ observableName : string ] : string [ ] ;
7274 } ;
75+
76+ imports ?: BlockImport ;
7377 }
7478}
7579
80+ /**
81+ * Handlers that are associated with the scope promises.
82+ *
83+ * @todo This needs to be refactored.
84+ */
85+ export interface PromiseHandlers {
86+ importsLoaded ?: string ; // (imports: any[] | null) => void;
87+ // TODO: other promise handlers?
88+ }
89+
7690class WebIOScope extends WebIONode {
7791
7892 readonly id : string ;
7993 readonly element : HTMLDivElement ;
80- children : Array < WebIOScope | WebIODomNode | string > ;
94+ children : Array < WebIOScope | WebIODomNode | string > | null = null ;
8195 handlers : ScopeListeners ;
8296 observables : { [ observableName : string ] : WebIOObservable } ;
8397 promises : ScopePromises ;
@@ -96,40 +110,62 @@ class WebIOScope extends WebIONode {
96110 const { id, observables = { } , handlers = { } } = scopeData . instanceArgs ;
97111 this . id = id ;
98112
113+ // Create WebIOObservables.
99114 this . observables = { } ;
100115 Object . keys ( observables ) . forEach ( ( name ) => {
101116 this . observables [ name ] = new WebIOObservable ( name , observables [ name ] , this ) ;
102117 } ) ;
103118
104- // Map the function strings into handlers which have `this` bound to the scope's
105- // element and which have access to the _webIOScope variable (via closure).
106119 this . handlers = { } ;
107- Object . keys ( handlers ) . forEach ( ( observableName ) => {
120+
121+ // TODO: refactor registerScope as described elsewhere
122+ this . webIO . registerScope ( this ) ;
123+
124+ // TODO: refactor way initialization/import promises are done
125+ const initializationPromise = this . initialize ( scopeData ) ;
126+
127+ this . promises = {
128+ connected : this . webIO . connected . then ( ( ) => this ) ,
129+ importsLoaded : initializationPromise ,
130+ } ;
131+
132+ this . setupScope ( ) ;
133+ }
134+
135+ /**
136+ * Perform asynchronous initialization tasks.
137+ */
138+ private async initialize ( scopeData : ScopeNodeData ) {
139+ const { handlers = { } , imports} = scopeData . instanceArgs ;
140+
141+ // (Asynchronously) perform dependency initialization
142+ const { preDependencies = [ ] , _promises = { } , ...restHandlers } = handlers ;
143+ preDependencies
144+ . map ( ( functionString ) => createWebIOEventListener ( this . element , functionString , this ) )
145+ . forEach ( ( handler ) => handler . call ( this ) )
146+ ;
147+
148+ // Map the function strings into handlers which have `this` bound to the scope's
149+ // element and which have access to the _webIOScope resources variable (via closure).
150+ Object . keys ( restHandlers ) . forEach ( ( observableName ) => {
108151 this . handlers [ observableName ] = handlers [ observableName ] . map ( ( handlerString ) => {
109152 return createWebIOEventListener ( this . element , handlerString , this ) ;
110153 } ) ;
111154 } ) ;
112155
113- // TODO
114- this . promises = null as any ;
156+ const resources = imports ? await importBlock ( imports ) : null ;
115157
116- this . webIO . registerScope ( this ) ;
158+ // TypeScript hackery to deal with out promiseHandlers is a very special case
159+ const { importsLoaded : importsLoadedHandler } = _promises as any as PromiseHandlers ;
160+ if ( resources && importsLoadedHandler ) {
161+ // `as any` necessary because createWebIOEventListener normally returns
162+ // a function which is expected to be an event listener... but this is kind of a
163+ // special case of that.
164+ debug ( "Invoking importsLoaded Scope handler." , { importsLoadedHandler, resources} ) ;
165+ ( createWebIOEventListener ( this . element , importsLoadedHandler , this ) as any ) ( ...resources ) ;
166+ }
117167
118- // this.node = node;
119- // this.handlers = handlers;
120- // this.observables = observables;
121- //
122- // // (Asynchronously) perform dependency initialization
123- // const {preDependencies = []} = this.handlers;
124- // preDependencies.forEach((handler) => handler.call(this));
125- // const importsLoaded = Promise.resolve(); // TODO
126- //
127- // // (Asynchronously) connect to the WebIO Julia machinery
128- // const connected = this.connect().then(() => this);
129- //
130- // this.promises = {importsLoaded, connected};
131-
132- // Create children and append to this node's element.
168+ // Finally, create children.
133169 this . children = scopeData . children . map ( ( nodeData ) => {
134170 if ( typeof nodeData === "string" ) {
135171 return nodeData ;
@@ -144,7 +180,9 @@ class WebIOScope extends WebIONode {
144180 }
145181 }
146182
147- this . setupScope ( ) ;
183+ // This isn't super clean, but this function is used to create the
184+ // importsLoaded promise, so we need to return the promises.
185+ return resources ;
148186 }
149187
150188 getObservableValue ( observable : ObservableSpecifier ) {
0 commit comments