From 2ce20021cac4411aa186d4bc76df022b04f4d763 Mon Sep 17 00:00:00 2001 From: slowe <299787+slowe@users.noreply.github.com> Date: Tue, 15 Oct 2024 15:47:21 +0100 Subject: [PATCH] Timeout fetch --- 2024-DFES/resources/config.js | 40 ++++++----- 2024-DFES/resources/dfes.js | 125 +++++++++++++++++++--------------- 2024-DFES/resources/style.css | 8 +-- 3 files changed, 96 insertions(+), 77 deletions(-) diff --git a/2024-DFES/resources/config.js b/2024-DFES/resources/config.js index abb1aa5..0ca2182 100644 --- a/2024-DFES/resources/config.js +++ b/2024-DFES/resources/config.js @@ -224,27 +224,31 @@ OI.ready(function(){ if(url.match("https://northernpowergrid.opendatasoft.com/api/explore/v2.1/")){ var rows = d.replace(/[\n\r]+$/,'').split(/\r\n/); var r,cols,c,head = {},header = [],orows = new Array(rows.length-1); - for(r = 0; r < rows.length; r++){ - cols = rows[r].split(/\;/); - if(r==0){ - header = [data.key]; - for(c = 0; c < cols.length; c++){ - head[cols[c]] = c; - if(cols[c]==parseInt(cols[c])) header.push(parseInt(cols[c])); - } - }else{ - orows[r-1] = new Array(header.length); - for(c = 0; c < header.length; c++){ - id = header[c]; - if(id==data.key){ - v = cols[head[id]].toUpperCase(); - }else{ - v = cols[head[id]]; - if(parseFloat(v)==v) v = parseFloat(v); + if(rows.length > 1){ + for(r = 0; r < rows.length; r++){ + cols = rows[r].split(/\;/); + if(r==0){ + header = [data.key]; + for(c = 0; c < cols.length; c++){ + head[cols[c]] = c; + if(cols[c]==parseInt(cols[c])) header.push(parseInt(cols[c])); + } + }else{ + orows[r-1] = new Array(header.length); + for(c = 0; c < header.length; c++){ + id = header[c]; + if(id==data.key){ + v = cols[head[id]].toUpperCase(); + }else{ + v = cols[head[id]]; + if(parseFloat(v)==v) v = parseFloat(v); + } + orows[r-1][c] = v; } - orows[r-1][c] = v; } } + }else{ + this.message('No data loaded from API',{'id':'error','type':'ERROR'}); } // We need to add a "raw" variable that consists of { header: [], rows: [] } data.raw = {'rows':orows,'header':header}; diff --git a/2024-DFES/resources/dfes.js b/2024-DFES/resources/dfes.js index d456108..574d114 100644 --- a/2024-DFES/resources/dfes.js +++ b/2024-DFES/resources/dfes.js @@ -80,35 +80,34 @@ if(!this.options.files.parameters) this.options.files.parameters = path+"data/scenarios/config.json"; if(!this.options.files.scenarios) this.options.files.scenarios = path+"data/scenarios/index.json"; - fetch(this.options.files.parameters,{}).then(response => { - if(!response.ok) throw new Error('Network response was not OK'); - return response.json(); - }).then(d => { - if(d.length){ - this.parameters = {}; - // New style (1.5.0) config is an array to preserve order - convert into object - for(var i = 0; i < d.length; i++){ - if(d[i].key) this.parameters[d[i].key] = d[i]; - } - }else this.parameters = d; - fetch(this.options.files.scenarios,{}).then(response => { - if(!response.ok) throw new Error('Network response was not OK'); - return response.json(); - }).then(d => { - this.log('MSG','Got '+this.options.files.scenarios); + this.fetch(this.options.files.parameters,{ + 'type':'json', + 'callback': function(d){ if(d.length){ - this.data.scenarios = {}; + this.parameters = {}; // New style (1.5.0) config is an array to preserve order - convert into object for(var i = 0; i < d.length; i++){ - if(d[i].key) this.data.scenarios[d[i].key] = d[i]; + if(d[i].key) this.parameters[d[i].key] = d[i]; } - }else this.data.scenarios = d; - this.init(); - }).catch(e => { - this.message('Unable to load scenarios from '+this.options.files.scenarios.replace(/\?.*/,""),{'id':'error','type':'ERROR'}); - }); - }).catch(e => { - this.message('Unable to load parameters from '+this.options.files.parameters.replace(/\?.*/,""),{'id':'error','type':'ERROR'}) + }else this.parameters = d; + + this.fetch(this.options.files.scenarios,{ + 'type':'json', + 'callback': function(d){ + this.log('MSG','Got '+this.options.files.scenarios); + if(d.length){ + this.data.scenarios = {}; + // New style (1.5.0) config is an array to preserve order - convert into object + for(var i = 0; i < d.length; i++){ + if(d[i].key) this.data.scenarios[d[i].key] = d[i]; + } + }else this.data.scenarios = d; + this.init(); + }, + 'error':'Unable to load scenarios from ' + }); + }, + 'error': 'Unable to load parameters from ' }); return this; @@ -270,14 +269,13 @@ var url = this.data.scenarios[this.options.scenario].data[this.options.parameter].file; if(!url.match(/^https/)) url = path+"data/scenarios/"+this.data.scenarios[this.options.scenario].data[this.options.parameter].file; this.log('INFO','Getting data from %c'+url.replace(/\%/g,'\\%')+'%c','font-style:italic;','') - fetch(url,{}).then(response => { - if(!response.ok) throw new Error('Network response was not OK'); - return response.text(); - }).then(d => { - this.log('MSG','Got '+url); - this.loadedData(d,this.options.scenario,this.options.parameter,callback,url); - }).catch(e => { - this.message('Unable to load data from '+url.replace(/\?.*/,""),{'id':'error','type':'ERROR'}); + this.fetch(url,{ + 'type':'text', + 'callback': function(d){ + this.log('MSG','Got '+url); + this.loadedData(d,this.options.scenario,this.options.parameter,callback,url); + }, + 'error':'Unable to load data from ' }); }; @@ -491,17 +489,16 @@ if(!this.mapping[data.dataBy][id].data && typeof this.mapping[data.dataBy][id].file==="string"){ // Load from JSON file var url = path+this.mapping[data.dataBy][id].file; - fetch(url,{}).then(response => { - if(!response.ok) throw new Error('Network response was not OK'); - return response.json(); - }).then(d => { - this.log('MSG','Got '+url); - this.mapping[data.dataBy][id].raw = d; - if(typeof this.mapping[data.dataBy][id].process==="function") d = this.mapping[data.dataBy][id].process.call(this,d); - this.mapping[data.dataBy][id].data = d; - this.mapData(); - }).catch(e => { - this.message('Unable to load '+url.replace(/\?.*/,""),{'id':'error','type':'ERROR'}); + this.fetch(url,{ + 'type':'json', + 'callback': function(d){ + this.log('MSG','Got '+url); + this.mapping[data.dataBy][id].raw = d; + if(typeof this.mapping[data.dataBy][id].process==="function") d = this.mapping[data.dataBy][id].process.call(this,d); + this.mapping[data.dataBy][id].data = d; + this.mapData(); + }, + 'error': 'Unable to load ' }); return this; @@ -774,15 +771,14 @@ document.querySelector('#map .spinner').style.display = ''; var url = path+this.layers[layer.id].geojson; - fetch(url,{}).then(response => { - if(!response.ok) throw new Error('Network response was not OK'); - return response.json(); - }).then(d => { - this.log('MSG','Got '+url); - this.layers[layer.id].geojson = d; - this.buildMap(); - }).catch(e => { - this.message('Unable to load GeoJSON '+url.replace(/\?.*/,""),{'id':'error','type':'ERROR'}); + this.fetch(url,{ + 'type':'json', + 'callback':function(d){ + this.log('MSG','Got '+url); + this.layers[layer.id].geojson = d; + this.buildMap(); + }, + 'error':'Unable to load GeoJSON ' }); return this; } @@ -953,6 +949,27 @@ return this; }; + FES.prototype.fetch = function(resource, options = {}) { + var { timeout = 3000 } = options; + var controller = new AbortController(); + var id = setTimeout(() => controller.abort(), timeout); + this.log('INFO','Get '+resource); + var response = fetch(resource,{ + ...options, + signal: controller.signal + }).then(response=>{ + if(!response.ok) throw new Error('Network response was not OK'); + if(options.type=="json") return response.json(); + else return response.text(); + }).then(d=>{ + if(typeof options.callback==="function") options.callback.call(this,d); + }).catch(e => { + this.message((options.error ? options.error : 'Unable to load from ')+resource.replace(/\?.*/,""),{'id':'error','type':'ERROR'}) + }); + clearTimeout(id); + return this; + } + function popuptext(feature,attr){ // does this feature have a property named popupContent? var popup,me,view,key,v,lid; @@ -1424,4 +1441,4 @@ function appendHTML(el,html){ else el.append(html); } return el; -} \ No newline at end of file +} diff --git a/2024-DFES/resources/style.css b/2024-DFES/resources/style.css index a6a002c..aeb82ae 100644 --- a/2024-DFES/resources/style.css +++ b/2024-DFES/resources/style.css @@ -35,11 +35,9 @@ .message { text-align: center; } -.warning { - color: rgb(95, 82, 7); - background-color: rgb(251, 245, 208); - filter: drop-shadow(0px 0px 1px rgb(95, 82, 7)); -} +.warning { color: rgb(95, 82, 7); background-color: rgb(251, 245, 208); filter: drop-shadow(0px 0px 1px rgb(95, 82, 7)); } +.error { color: #721c24; background-color: #f8d7da; filter: drop-shadow(0 0 1px #f5c6cb)); } + .holder > *:last-child { margin-bottom: 0; } ol { list-style: decimal; margin-left: 2em; margin-bottom: 1em; }