Skip to content

Commit 850f60a

Browse files
Michael MaierMichael Maier
Michael Maier
authored and
Michael Maier
committed
rewrite of fetch into redundant version
1 parent 5cdff9b commit 850f60a

File tree

4 files changed

+158
-156
lines changed

4 files changed

+158
-156
lines changed

Q5-fallback.json

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
{"entities":{"Q5":{"pageid":62,"ns":120,"title":"Item:Q5","lastrevid":3393,"modified":"2016-09-26T08:48:05Z","type":"item","id":"Q5","labels":{"en":{"language":"en","value":"Taxonomy category"},"de":{"language":"de","value":"Taxonomie-Kategorie"},"fa":{"language":"fa","value":"\u0637\u0628\u0642\u0647 \u0628\u0646\u062f\u06cc"},"ro":{"language":"ro","value":"Categorie taxonometric\u0103"},"ga":{"language":"ga","value":"Tacsan\u00f3im\u00edocht Catag\u00f3ir"},"hr":{"language":"hr","value":"Kategorija taksonomije"},"pl":{"language":"pl","value":"Kategoria taksonomii"},"el":{"language":"el","value":"\u039a\u03b1\u03c4\u03b7\u03b3\u03bf\u03c1\u03af\u03b1 \u03a4\u03b1\u03be\u03b9\u03bd\u03bf\u03bc\u03af\u03b1\u03c2"},"es":{"language":"es","value":"Categor\u00eda taxon\u00f3mica"},"lv":{"language":"lv","value":"Taksonomijas kategorija"},"cs":{"language":"cs","value":"Kategorie taxonomie"},"bg":{"language":"bg","value":"\u0422\u0430\u043a\u0441\u043e\u043d\u043e\u043c\u0438\u0447\u043d\u0430 \u043a\u0430\u0442\u0435\u0433\u043e\u0440\u0438\u044f"},"et":{"language":"et","value":"Taksonoomia kategooria"},"pt":{"language":"pt","value":"Categoria de sistematiza\u00e7\u00e3o"},"fi":{"language":"fi","value":"Luokittelukategoria"}},"descriptions":{"de":{"language":"de","value":"Taxonomie-Kategorie"}},"aliases":[],"claims":{"P9":[{"mainsnak":{"snaktype":"value","property":"P9","datavalue":{"value":{"entity-type":"item","numeric-id":3},"type":"wikibase-entityid"},"datatype":"wikibase-item"},"type":"statement","id":"Q5$98e7621c-44a8-dd92-3072-7622f3fdc72d","rank":"normal"}]},"sitelinks":[]}}}

index.html

+1
Original file line numberDiff line numberDiff line change
@@ -167,6 +167,7 @@ <h3><%- T("type_of_initiative") %></h3>
167167
<script defer="defer" src="bower_components/leaflet/dist/leaflet.js"></script>
168168
<script defer="defer" src="scripts/leaflet-hash.js"></script>
169169
<script defer="defer" src="bower_components/PruneCluster/dist/PruneCluster.min.js"></script>
170+
<script defer="defer" src="scripts/red_fetch.js"></script>
170171
<script defer="defer" src="scripts/map.js"></script>
171172
</body>
172173
</html>

scripts/map.js

+14-156
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@
77
var data_url = "https://data.transformap.co/raw/5d6b9d3d32097fd6832200874402cfc3";
88
var fallback_data_url = "susydata-fallback.json";
99

10+
var redundant_data_urls = [ "https://data.transformap.co/raw/5d6b9d3d32097fd6832200874402cfc3", "susydata-fallback.json" ];
11+
1012
/* fix for leaflet scroll on devices that fire scroll too fast, e.g. Macs
1113
see https://github.com/Leaflet/Leaflet/issues/4410#issuecomment-234133427
1214
@@ -176,153 +178,7 @@ function addPOIsToMap(geoJSONfeatureCollection) {
176178
return true;
177179
}
178180

179-
/* new version of getting map data with promises
180-
should fetch data_url, and in case it doesn't respond in a timeout, fetch fallback_data_url
181-
182-
taken from https://blog.hospodarets.com/fetch_in_action
183-
*/
184-
185-
var processStatus = function (response) {
186-
// status "0" to handle local files fetching (e.g. Cordova/Phonegap etc.)
187-
if (response.status === 200 || response.status === 0) {
188-
return Promise.resolve(response)
189-
} else {
190-
return Promise.reject(new Error(response.statusText))
191-
}
192-
};
193-
194-
var parseJson = function (response) {
195-
return response.json();
196-
};
197-
198-
/* @returns {wrapped Promise} with .resolve/.reject/.catch methods */
199-
// It goes against Promise concept to not have external access to .resolve/.reject methods, but provides more flexibility
200-
var getWrappedPromise = function () {
201-
var wrappedPromise = {},
202-
promise = new Promise(function (resolve, reject) {
203-
wrappedPromise.resolve = resolve;
204-
wrappedPromise.reject = reject;
205-
});
206-
wrappedPromise.then = promise.then.bind(promise);
207-
wrappedPromise.catch = promise.catch.bind(promise);
208-
wrappedPromise.promise = promise;// e.g. if you want to provide somewhere only promise, without .resolve/.reject/.catch methods
209-
return wrappedPromise;
210-
};
211-
212-
/* @returns {wrapped Promise} with .resolve/.reject/.catch methods */
213-
var getWrappedFetch = function () {
214-
var wrappedPromise = getWrappedPromise();
215-
var args = Array.prototype.slice.call(arguments);// arguments to Array
216-
217-
fetch.apply(null, args)// calling original fetch() method
218-
.then(function (response) {
219-
wrappedPromise.resolve(response);
220-
}, function (error) {
221-
wrappedPromise.reject(error);
222-
})
223-
.catch(function (error) {
224-
wrappedPromise.catch(error);
225-
});
226-
return wrappedPromise;
227-
};
228-
229-
/**
230-
* Fetch JSON by url
231-
* @param { {
232-
* url: {String},
233-
* [cacheBusting]: {Boolean}
234-
* } } params
235-
* @returns {Promise}
236-
*/
237-
238-
var MAX_WAITING_TIME = 5000;// in ms
239-
240-
var getJSON = function (params) {
241-
var wrappedFetch = getWrappedFetch(
242-
params.cacheBusting ? params.url + '?' + new Date().getTime() : params.url,
243-
{
244-
method: 'get',// optional, "GET" is default value
245-
headers: {
246-
'Accept': 'application/json'
247-
}
248-
});
249-
250-
var timeoutId = setTimeout(function () {
251-
wrappedFetch.reject(new Error('Load timeout for resource: ' + params.url));// reject on timeout
252-
}, MAX_WAITING_TIME);
253-
254-
return wrappedFetch.promise// getting clear promise from wrapped
255-
.then(function (response) {
256-
clearTimeout(timeoutId);
257-
return response;
258-
})
259-
.then(processStatus)
260-
.then(parseJson);
261-
};
262-
263-
/*--- TEST --*/
264-
var onComplete = function () {
265-
console.log('I\'m invoked in any case after success/error');
266-
};
267-
268-
/*
269-
getJSON({
270-
url: data_url,
271-
cacheBusting: true
272-
}).then(function (data) {// on success
273-
console.log('JSON parsed successfully!');
274-
console.log(data);
275-
addPOIsToMap(data);
276-
// onComplete(data);
277-
}, function (error) {// on reject
278-
console.error('An error occured!');
279-
console.error(error.message ? error.message : error);
280-
// onComplete(error);
281-
});*/
282-
283-
function myGetJSON(url,success_function,error_function) {
284-
var getJSONparams = { url: url, cacheBusting: true };
285-
286-
getJSON(getJSONparams).then(
287-
function (data) { success_function(data) },
288-
function (error) { error_function(error) }
289-
);
290-
}
291-
292-
myGetJSON( data_url,
293-
function(data) {
294-
var adding_pois_successful = addPOIsToMap(data);
295-
if(adding_pois_successful) {
296-
console.log("1st try to fetch POI data from API ("+data_url+") was successful");
297-
return;
298-
}
299-
console.log("API didn't return useful data from "+data_url+", try static file");
300-
myGetJSON( fallback_data_url,
301-
function(data) {
302-
if(!addPOIsToMap(data))
303-
console.error("2nd try to fetch POI data from " + fallback_data_url + " failed too");
304-
},
305-
function (error) {
306-
console.error("2nd try to fetch POI data from " + fallback_data_url + " failed too");
307-
console.error(error.message ? error.message : error);
308-
});
309-
},
310-
function(error) {
311-
console.log("1st try to fetch POI data from API ("+data_url+") failed, try fallback");
312-
myGetJSON( fallback_data_url,
313-
function(data) {
314-
var adding_pois_successful = addPOIsToMap(data);
315-
if(adding_pois_successful)
316-
console.log("2nd try to fetch POI data from " + fallback_data_url + " successful.");
317-
else
318-
console.error("2nd try to fetch POI data from " + fallback_data_url + " failed too");
319-
},
320-
function (error) {
321-
console.error("2nd try to fetch POI data from " + fallback_data_url + " failed too");
322-
console.error(error.message ? error.message : error);
323-
});
324-
});
325-
181+
redundantFetch( redundant_data_urls ,addPOIsToMap, function(error) { console.error("none of the POI data urls available"); } );
326182

327183
/* get taxonomy stuff */
328184
var taxonomy_url = "http://viewer.transformap.co/taxonomy.json";
@@ -364,8 +220,7 @@ function getLangTaxURL(lang) {
364220
'SERVICE wikibase:label {bd:serviceParam wikibase:language "'+lang+'" }' +
365221
'}';
366222

367-
// return 'https://query.base.transformap.co/bigdata/namespace/transformap/sparql?query=' +encodeURIComponent(tax_query) + "&format=json"; // server not CORS ready yet
368-
return "https://raw.githubusercontent.com/TransforMap/transformap-viewer-translations/master/taxonomy-backup/susy/taxonomy."+lang+".json";
223+
return 'https://query.base.transformap.co/bigdata/namespace/transformap/sparql?query=' +encodeURIComponent(tax_query) + "&format=json"; // server not CORS ready yet
369224
}
370225

371226
function setFilterLang(lang) {
@@ -377,11 +232,9 @@ function setFilterLang(lang) {
377232
if(multilang_taxonomies[lang]) {
378233
setTaxonomy(multilang_taxonomies[lang]);
379234
} else {
380-
$.ajax({
381-
url: getLangTaxURL(lang),
382-
dataType: "json",
383-
success: applyOrAddTaxonomyLang,
384-
});
235+
redundantFetch( [ getLangTaxURL(lang), "https://raw.githubusercontent.com/TransforMap/transformap-viewer-translations/master/taxonomy-backup/susy/taxonomy."+lang+".json" ],
236+
applyOrAddTaxonomyLang,
237+
function(error) { console.error("none of the taxonomy data urls available") } );
385238
}
386239
}
387240

@@ -1169,7 +1022,8 @@ setFallbackLangs();
11691022

11701023

11711024
/* get languages for UI from our Wikibase, and pick languages that are translated there */
1172-
$.getJSON("https://base.transformap.co/wiki/Special:EntityData/Q5.json", function (returned_data){
1025+
1026+
function initializeLanguageSwitcher(returned_data){
11731027
for(lang in returned_data.entities.Q5.labels) { //Q5 is arbitrary. Choose one that gets translated for sure.
11741028
supported_languages.push(lang);
11751029
}
@@ -1210,7 +1064,11 @@ $.getJSON("https://base.transformap.co/wiki/Special:EntityData/Q5.json", functio
12101064
$("#languageSelector ul").append("<li targetlang=" + langcode + is_default + " onClick='switchToLang(\""+langcode+"\");'>"+item+"</li>");
12111065
});
12121066
});
1213-
});
1067+
}
1068+
redundantFetch( [ "https://base.transformap.co/wiki/Special:EntityData/Q5.json", "https://raw.githubusercontent.com/TransforMap/transformap-viewer/Q5-fallback.json", "Q5-fallback.json" ],
1069+
initializeLanguageSwitcher,
1070+
function(error) { console.error("none of the lang init data urls available") } );
1071+
12141072

12151073
function switchToLang(lang) {
12161074
$("#languageSelector li.default").removeClass("default");

scripts/red_fetch.js

+142
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,142 @@
1+
/*
2+
* This library provides a 'fetch', where you can use fallback URIs, to build on redundant servers.
3+
*
4+
* Mon 3 Oct 15:07:12 CEST 2016
5+
* Michael Maier (species@github), WTFPL
6+
*
7+
* Give it an array of resources, that can be fetched from different urls.
8+
* Will try to fetch them in the provided order.
9+
* Execute success_function on the first successful fetch, and error_function only if all resources fail to fetch.
10+
*/
11+
12+
/* This program is free software. It comes without any warranty, to
13+
* the extent permitted by applicable law. You can redistribute it
14+
* and/or modify it under the terms of the Do What The Fuck You Want
15+
* To Public License, Version 2, as published by Sam Hocevar. See
16+
* http://www.wtfpl.net/ for more details. */
17+
18+
/* new version of getting map data with promises
19+
20+
taken from https://blog.hospodarets.com/fetch_in_action
21+
*/
22+
23+
var processStatus = function (response) {
24+
// status "0" to handle local files fetching (e.g. Cordova/Phonegap etc.)
25+
if (response.status === 200 || response.status === 0) {
26+
return Promise.resolve(response)
27+
} else {
28+
return Promise.reject(new Error(response.statusText))
29+
}
30+
};
31+
32+
var parseJson = function (response) {
33+
return response.json();
34+
};
35+
36+
/* @returns {wrapped Promise} with .resolve/.reject/.catch methods */
37+
// It goes against Promise concept to not have external access to .resolve/.reject methods, but provides more flexibility
38+
var getWrappedPromise = function () {
39+
var wrappedPromise = {},
40+
promise = new Promise(function (resolve, reject) {
41+
wrappedPromise.resolve = resolve;
42+
wrappedPromise.reject = reject;
43+
});
44+
wrappedPromise.then = promise.then.bind(promise);
45+
wrappedPromise.catch = promise.catch.bind(promise);
46+
wrappedPromise.promise = promise;// e.g. if you want to provide somewhere only promise, without .resolve/.reject/.catch methods
47+
return wrappedPromise;
48+
};
49+
50+
/* @returns {wrapped Promise} with .resolve/.reject/.catch methods */
51+
var getWrappedFetch = function () {
52+
var wrappedPromise = getWrappedPromise();
53+
var args = Array.prototype.slice.call(arguments);// arguments to Array
54+
55+
fetch.apply(null, args)// calling original fetch() method
56+
.then(function (response) {
57+
wrappedPromise.resolve(response);
58+
}, function (error) {
59+
wrappedPromise.reject(error);
60+
})
61+
.catch(function (error) {
62+
wrappedPromise.catch(error);
63+
});
64+
return wrappedPromise;
65+
};
66+
67+
/**
68+
* Fetch JSON by url
69+
* @param { {
70+
* url: {String},
71+
* [cacheBusting]: {Boolean}
72+
* } } params
73+
* @returns {Promise}
74+
*/var MAX_WAITING_TIME = 5000;// in ms
75+
76+
var getJSON = function (params) {
77+
var wrappedFetch = getWrappedFetch(
78+
params.cacheBusting ? params.url + '?' + new Date().getTime() : params.url,
79+
{
80+
method: 'get',// optional, "GET" is default value
81+
headers: {
82+
'Accept': 'application/json'
83+
}
84+
});
85+
86+
var timeoutId = setTimeout(function () {
87+
wrappedFetch.reject(new Error('Load timeout for resource: ' + params.url));// reject on timeout
88+
}, MAX_WAITING_TIME);
89+
90+
return wrappedFetch.promise// getting clear promise from wrapped
91+
.then(function (response) {
92+
clearTimeout(timeoutId);
93+
return response;
94+
})
95+
.then(processStatus)
96+
.then(parseJson);
97+
};
98+
99+
function myGetJSON(url,success_function,error_function) {
100+
var getJSONparams = { url: url, cacheBusting: true };
101+
102+
getJSON(getJSONparams).then(
103+
function (data) { success_function(data) },
104+
function (error) { error_function(error) }
105+
);
106+
}
107+
108+
function redundantFetch (data_url_array, success_function, error_function) {
109+
if( ! (!!data_url_array && Array === data_url_array.constructor ) ) {
110+
console.error("redundantFetch: argument is no array");
111+
console.error(data_url_array);
112+
return false;
113+
}
114+
var current_url = data_url_array[0];
115+
if(typeof(current_url) !== 'string') {
116+
console.error("redundantFetch: url is no string");
117+
return false;
118+
}
119+
120+
console.log("redundantFetch called, urls:");
121+
console.log(data_url_array);
122+
123+
data_url_array.shift();
124+
125+
var local_error_function;
126+
var local_success_function;
127+
if(data_url_array.length == 0) { //last iteration
128+
local_success_function = success_function;
129+
local_error_function = error_function;
130+
} else {
131+
local_success_function = success_function;
132+
local_error_function = function (error) {
133+
redundantFetch(data_url_array, success_function, error_function);
134+
}
135+
}
136+
137+
var getJSONparams = { url: current_url, cacheBusting: true };
138+
getJSON(getJSONparams).then(
139+
function (data) { local_success_function(data); console.log("rfetch: success on "); console.log(data); },
140+
function (error) { local_error_function(error); console.log("rfetch: fail on "); console.log(error); }
141+
);
142+
}

0 commit comments

Comments
 (0)