@@ -149,55 +149,112 @@ var tryExamplesGlobalMinHeight = 0;
149149 */
150150var tryExamplesConfigLoaded = false ;
151151
152- window . loadTryExamplesConfig = async ( configFilePath ) => {
153- if ( tryExamplesConfigLoaded ) {
154- return ;
155- }
156- try {
157- // Add a timestamp as query parameter to ensure a cached version of the
158- // file is not used.
159- const timestamp = new Date ( ) . getTime ( ) ;
160- const configFileUrl = `${ configFilePath } ?cb=${ timestamp } ` ;
161- const currentPageUrl = window . location . pathname ;
162-
163- const response = await fetch ( configFileUrl ) ;
164- if ( ! response . ok ) {
165- if ( response . status === 404 ) {
166- // Try examples ignore file is not present.
167- console . log ( "Optional try_examples config file not found." ) ;
168- return ;
169- }
170- throw new Error ( `Error fetching ${ configFilePath } ` ) ;
152+ // A config loader with imprved error handling + request deduplication
153+ const ConfigLoader = ( ( ) => {
154+ // setting a private state for managing requests and errors
155+ let configLoadPromise = null ;
156+ let lastErrorTimestamp = 0 ;
157+ const ERROR_THROTTLE_MS = 5000 ; // error messages at most every 5 seconds
158+ const failedRequestsCache = new Set ( ) ;
159+
160+ const shouldShowError = ( ) => {
161+ const now = Date . now ( ) ;
162+ if ( now - lastErrorTimestamp > ERROR_THROTTLE_MS ) {
163+ lastErrorTimestamp = now ;
164+ return true ;
171165 }
166+ return false ;
167+ } ;
172168
173- const data = await response . json ( ) ;
174- if ( ! data ) {
169+ const logError = ( message ) => {
170+ if ( shouldShowError ( ) ) {
171+ console . log ( message ) ;
172+ }
173+ } ;
174+
175+ const loadConfig = async ( configFilePath ) => {
176+ if ( tryExamplesConfigLoaded ) {
175177 return ;
176178 }
177179
178- // Set minimum iframe height based on value in config file
179- if ( data . global_min_height ) {
180- tryExamplesGlobalMinHeight = parseInt ( data . global_min_height ) ;
180+ if ( failedRequestsCache . has ( configFilePath ) ) {
181+ return ;
181182 }
182183
183- // Disable interactive examples if file matches one of the ignore patterns
184- // by hiding try_examples_buttons.
185- Patterns = data . ignore_patterns ;
186- for ( let pattern of Patterns ) {
187- let regex = new RegExp ( pattern ) ;
188- if ( regex . test ( currentPageUrl ) ) {
189- var buttons = document . getElementsByClassName ( "try_examples_button" ) ;
190- for ( var i = 0 ; i < buttons . length ; i ++ ) {
191- buttons [ i ] . classList . add ( "hidden" ) ;
184+ // Return the existing promise if the request is in progress, as we
185+ // don't want to make multiple requests for the same file. This
186+ // can happen if there are several try_examples directives on the
187+ // same page.
188+ if ( configLoadPromise ) {
189+ return configLoadPromise ;
190+ }
191+
192+ configLoadPromise = ( async ( ) => {
193+ try {
194+ // Add a timestamp as query parameter to ensure a cached version of the
195+ // file is not used.
196+ const timestamp = new Date ( ) . getTime ( ) ;
197+ const configFileUrl = `${ configFilePath } ?cb=${ timestamp } ` ;
198+ const currentPageUrl = window . location . pathname ;
199+
200+ const response = await fetch ( configFileUrl ) ;
201+ if ( ! response . ok ) {
202+ if ( response . status === 404 ) {
203+ failedRequestsCache . add ( configFilePath ) ;
204+ logError ( "Optional try_examples config file not found." ) ;
205+ return ;
206+ }
207+ throw new Error ( `Error fetching ${ configFilePath } ` ) ;
208+ }
209+
210+ const data = await response . json ( ) ;
211+ if ( ! data ) {
212+ return ;
192213 }
193- break ;
214+
215+ // Set minimum iframe height based on value in config file
216+ if ( data . global_min_height ) {
217+ tryExamplesGlobalMinHeight = parseInt ( data . global_min_height ) ;
218+ }
219+
220+ // Disable interactive examples if file matches one of the ignore patterns
221+ // by hiding try_examples_buttons.
222+ Patterns = data . ignore_patterns ;
223+ for ( let pattern of Patterns ) {
224+ let regex = new RegExp ( pattern ) ;
225+ if ( regex . test ( currentPageUrl ) ) {
226+ var buttons = document . getElementsByClassName (
227+ "try_examples_button" ,
228+ ) ;
229+ for ( var i = 0 ; i < buttons . length ; i ++ ) {
230+ buttons [ i ] . classList . add ( "hidden" ) ;
231+ }
232+ break ;
233+ }
234+ }
235+ } catch ( error ) {
236+ console . error ( error ) ;
237+ } finally {
238+ tryExamplesConfigLoaded = true ;
239+ configLoadPromise = null ;
194240 }
195- }
196- } catch ( error ) {
197- console . error ( error ) ;
198- }
199- tryExamplesConfigLoaded = true ;
200- } ;
241+ } ) ( ) ;
242+
243+ return configLoadPromise ;
244+ } ;
245+
246+ return {
247+ loadConfig,
248+ resetState : ( ) => {
249+ tryExamplesConfigLoaded = false ;
250+ configLoadPromise = null ;
251+ failedRequestsCache . clear ( ) ;
252+ lastErrorTimestamp = 0 ;
253+ } ,
254+ } ;
255+ } ) ( ) ;
256+
257+ window . loadTryExamplesConfig = ConfigLoader . loadConfig ;
201258
202259window . toggleTryExamplesButtons = ( ) => {
203260 /* Toggle visibility of TryExamples buttons. For use in console for debug
0 commit comments