11const privateData = new WeakMap ( )
22
3- // Functional stand in for the W3 spec "queue a task" paradigm
4- function task ( ) : Promise < void > {
5- return new Promise ( resolve => setTimeout ( resolve , 0 ) )
6- }
7-
83function isWildcard ( accept : string | null ) {
94 return accept && ! ! accept . split ( ',' ) . find ( x => x . match ( / ^ \s * \* \/ \* / ) )
105}
@@ -133,28 +128,26 @@ export default class IncludeFragmentElement extends HTMLElement {
133128 }
134129 )
135130
136- #handleData( ) : Promise < void > {
137- if ( this . #busy) return Promise . resolve ( )
131+ async #handleData( ) : Promise < void > {
132+ if ( this . #busy) return
138133 this . #busy = true
139-
140134 this . #observer. unobserve ( this )
141- return this . #getData( ) . then (
142- ( html : string ) => {
143- const template = document . createElement ( 'template' )
144- // eslint-disable-next-line github/no-inner-html
145- template . innerHTML = html
146- const fragment = document . importNode ( template . content , true )
147- const canceled = ! this . dispatchEvent (
148- new CustomEvent ( 'include-fragment-replace' , { cancelable : true , detail : { fragment} } )
149- )
150- if ( canceled ) return
151- this . replaceWith ( fragment )
152- this . dispatchEvent ( new CustomEvent ( 'include-fragment-replaced' ) )
153- } ,
154- ( ) => {
155- this . classList . add ( 'is-error' )
156- }
157- )
135+ try {
136+ const html = await this . #getData( )
137+
138+ const template = document . createElement ( 'template' )
139+ // eslint-disable-next-line github/no-inner-html
140+ template . innerHTML = html
141+ const fragment = document . importNode ( template . content , true )
142+ const canceled = ! this . dispatchEvent (
143+ new CustomEvent ( 'include-fragment-replace' , { cancelable : true , detail : { fragment} } )
144+ )
145+ if ( canceled ) return
146+ this . replaceWith ( fragment )
147+ this . dispatchEvent ( new CustomEvent ( 'include-fragment-replaced' ) )
148+ } catch {
149+ this . classList . add ( 'is-error' )
150+ }
158151 }
159152
160153 #getData( ) : Promise < string > {
@@ -173,47 +166,43 @@ export default class IncludeFragmentElement extends HTMLElement {
173166 }
174167 }
175168
176- #fetchDataWithEvents( ) : Promise < string > {
169+ // Functional stand in for the W3 spec "queue a task" paradigm
170+ async #task( eventsToDispatch : string [ ] ) : Promise < void > {
171+ await new Promise ( resolve => setTimeout ( resolve , 0 ) )
172+ for ( const eventType of eventsToDispatch ) {
173+ this . dispatchEvent ( new Event ( eventType ) )
174+ }
175+ }
176+
177+ async #fetchDataWithEvents( ) : Promise < string > {
177178 // We mimic the same event order as <img>, including the spec
178179 // which states events must be dispatched after "queue a task".
179180 // https://www.w3.org/TR/html52/semantics-embedded-content.html#the-img-element
180- return task ( )
181- . then ( ( ) => {
182- this . dispatchEvent ( new Event ( 'loadstart' ) )
183- return this . fetch ( this . request ( ) )
184- } )
185- . then ( response => {
186- if ( response . status !== 200 ) {
187- throw new Error ( `Failed to load resource: the server responded with a status of ${ response . status } ` )
188- }
189- const ct = response . headers . get ( 'Content-Type' )
190- if ( ! isWildcard ( this . accept ) && ( ! ct || ! ct . includes ( this . accept ? this . accept : 'text/html' ) ) ) {
191- throw new Error ( `Failed to load resource: expected ${ this . accept || 'text/html' } but was ${ ct } ` )
192- }
193- return response . text ( )
194- } )
195- . then (
196- data => {
197- // Dispatch `load` and `loadend` async to allow
198- // the `load()` promise to resolve _before_ these
199- // events are fired.
200- task ( ) . then ( ( ) => {
201- this . dispatchEvent ( new Event ( 'load' ) )
202- this . dispatchEvent ( new Event ( 'loadend' ) )
203- } )
204- return data
205- } ,
206- error => {
207- // Dispatch `error` and `loadend` async to allow
208- // the `load()` promise to resolve _before_ these
209- // events are fired.
210- task ( ) . then ( ( ) => {
211- this . dispatchEvent ( new Event ( 'error' ) )
212- this . dispatchEvent ( new Event ( 'loadend' ) )
213- } )
214- throw error
215- }
216- )
181+
182+ await this . #task( [ 'loadstart' ] )
183+ const response = await this . fetch ( this . request ( ) )
184+ if ( response . status !== 200 ) {
185+ throw new Error ( `Failed to load resource: the server responded with a status of ${ response . status } ` )
186+ }
187+ const ct = response . headers . get ( 'Content-Type' )
188+ if ( ! isWildcard ( this . accept ) && ( ! ct || ! ct . includes ( this . accept ? this . accept : 'text/html' ) ) ) {
189+ throw new Error ( `Failed to load resource: expected ${ this . accept || 'text/html' } but was ${ ct } ` )
190+ }
191+ const data = await response . text ( )
192+
193+ try {
194+ // Dispatch `load` and `loadend` async to allow
195+ // the `load()` promise to resolve _before_ these
196+ // events are fired.
197+ this . #task( [ 'load' , 'loadend' ] )
198+ return data
199+ } catch ( error ) {
200+ // Dispatch `error` and `loadend` async to allow
201+ // the `load()` promise to resolve _before_ these
202+ // events are fired.
203+ this . #task( [ 'error' , 'loadend' ] )
204+ throw error
205+ }
217206 }
218207}
219208
0 commit comments