hide asu feature when not enabled
[web/firmware-selector-openwrt-org.git] / index.js
1 data = {}
2
3 function get_model_titles(titles) {
4 var title = []
5 for (var i in titles) {
6 if (titles[i].title) {
7 title.push(titles[i].title)
8 } else {
9 var title_fragments = []
10 if (titles[i].vendor) { title_fragments.push(titles[i].vendor) }
11 title_fragments.push(titles[i].model)
12 if (titles[i].variant) { title_fragments.push(titles[i].variant) }
13 title.push(title_fragments.join(" "))
14 }
15 }
16 return title.join("/")
17 }
18
19 function build_request() {
20 var profile = data["models"][$("models").value]
21 console.log(profile)
22 if (profile === undefined) {
23 alert("bad profile");
24 return;
25 }
26
27 updateImages()
28 $("loading").style.display = 'block';
29 $("custom").style.display = 'none';
30
31
32 request_data = {
33 "profile": profile.id,
34 "packages": $("packages").value.trim().split(" "),
35 "version": $("releases").value
36 }
37
38 console.log("disable request button / show loading spinner")
39
40 fetch(config.asu_url, {
41 method: "POST",
42 headers: { 'Content-Type': 'application/json' },
43 body: JSON.stringify(request_data)
44 })
45 .then(function(response) {
46 switch (response.status) {
47 case 200:
48 $("loading").style.display = 'none';
49 $("custom").style.display = 'block';
50 console.log("image found");
51 response.json()
52 .then(function(mobj) {
53 console.log(mobj)
54 updateImages(
55 mobj.version_number,
56 mobj.version_commit,
57 get_model_titles(mobj.titles),
58 mobj.url,
59 mobj)
60 });
61 break;
62 case 202:
63 // show some spinning animation
64 console.log("check again in 5 seconds");
65 setTimeout(function() { build_request(request_data) }, 5000);
66 break;
67 case 400:
68 $("loading").style.display = 'none';
69 $("custom").style.display = 'block';
70 console.log("bad request"); // see message
71 response.json()
72 .then(function(mobj) {
73 alert(mobj.message)
74 });
75 break;
76 case 422:
77 $("loading").style.display = 'none';
78 $("custom").style.display = 'block';
79 console.log("bad package"); // see message
80 response.json()
81 .then(function(mobj) {
82 alert(mobj.message)
83 });
84 break;
85 case 500:
86 $("loading").style.display = 'none';
87 $("custom").style.display = 'block';
88 console.log("build failed");
89 response.json()
90 .then(function(mobj) {
91 alert(mobj.message)
92 });
93 break;
94 }
95 })
96 }
97
98 function loadFile(url, callback) {
99 var xmlhttp = new XMLHttpRequest();
100 xmlhttp.onreadystatechange = function() {
101 if (xmlhttp.readyState == 4 && xmlhttp.status == 200) {
102 callback(JSON.parse(xmlhttp.responseText), url);
103 }
104 };
105 xmlhttp.open('GET', url, true);
106 xmlhttp.send();
107 }
108
109 function setupSelectList(select, items, onselection) {
110 for (var i = 0; i < items.length; i += 1) {
111 var option = document.createElement("OPTION");
112 option.innerHTML = items[i];
113 select.appendChild(option);
114 }
115
116 select.addEventListener("change", function(e) {
117 onselection(items[select.selectedIndex]);
118 });
119
120 if (select.selectedIndex >= 0) {
121 onselection(items[select.selectedIndex]);
122 }
123 }
124
125 // Change the translation of the entire document
126 function changeLanguage(language) {
127 var mapping = translations[language];
128 if (mapping) {
129 for (var tr in mapping) {
130 Array.from(document.getElementsByClassName(tr))
131 .forEach(function(e) { e.innerText = mapping[tr]; })
132 }
133 }
134 }
135
136 function setupAutocompleteList(input, items, onselection) {
137 // the setupAutocompleteList function takes two arguments,
138 // the text field element and an array of possible autocompleted values:
139 var currentFocus = -1;
140
141 // sort numbers and other characters separately
142 var collator = new Intl.Collator(undefined, {numeric: true, sensitivity: 'base'});
143
144 items.sort(collator.compare);
145
146 // execute a function when someone writes in the text field:
147 input.oninput = function(e) {
148 // clear images
149 updateImages();
150
151 var value = this.value;
152 // close any already open lists of autocompleted values
153 closeAllLists();
154
155 if (!value) {
156 return false;
157 }
158
159 // create a DIV element that will contain the items (values):
160 var list = document.createElement("DIV");
161 list.setAttribute("id", this.id + "-autocomplete-list");
162 list.setAttribute("class", "autocomplete-items");
163 // append the DIV element as a child of the autocomplete container:
164 this.parentNode.appendChild(list);
165
166 // for each item in the array...
167 var c = 0;
168 for (var i = 0; i < items.length; i += 1) {
169 var item = items[i];
170
171 // match
172 var j = item.toUpperCase().indexOf(value.toUpperCase());
173 if (j < 0) {
174 continue;
175 }
176
177 c += 1;
178 if (c >= 15) {
179 var div = document.createElement("DIV");
180 div.innerHTML = "...";
181 list.appendChild(div);
182 break;
183 } else {
184 var div = document.createElement("DIV");
185 // make the matching letters bold:
186 div.innerHTML = item.substr(0, j)
187 + "<strong>" + item.substr(j, value.length) + "</strong>"
188 + item.substr(j + value.length)
189 + "<input type='hidden' value='" + item + "'>";
190
191 div.addEventListener("click", function(e) {
192 // set text field to selected value
193 input.value = this.getElementsByTagName("input")[0].value;
194 // close the list of autocompleted values,
195 // (or any other open lists of autocompleted values:
196 closeAllLists();
197 // callback
198 onselection(input.value);
199 });
200
201 list.appendChild(div);
202 }
203 }
204 };
205
206 input.onkeydown = function(e) {
207 var x = document.getElementById(this.id + "-autocomplete-list");
208 if (x) x = x.getElementsByTagName("div");
209 if (e.keyCode == 40) {
210 // key down
211 currentFocus += 1;
212 // and and make the current item more visible:
213 setActive(x);
214 } else if (e.keyCode == 38) {
215 // key up
216 currentFocus -= 1;
217 // and and make the current item more visible:
218 setActive(x);
219 } else if (e.keyCode == 13) {
220 // If the ENTER key is pressed, prevent the form from being submitted,
221 e.preventDefault();
222 if (currentFocus > -1) {
223 // and simulate a click on the "active" item:
224 if (x) x[currentFocus].click();
225 }
226 }
227 };
228
229 input.onfocus = function() {
230 onselection(input.value);
231 }
232
233 function setActive(x) {
234 // a function to classify an item as "active":
235 if (!x) return false;
236 // start by removing the "active" class on all items:
237 for (var i = 0; i < x.length; i++) {
238 x[i].classList.remove("autocomplete-active");
239 }
240 if (currentFocus >= x.length) currentFocus = 0;
241 if (currentFocus < 0) currentFocus = (x.length - 1);
242 // add class "autocomplete-active":
243 x[currentFocus].classList.add("autocomplete-active");
244 }
245
246 function closeAllLists(elmnt) {
247 // close all autocomplete lists in the document,
248 // except the one passed as an argument:
249 var x = document.getElementsByClassName("autocomplete-items");
250 for (var i = 0; i < x.length; i++) {
251 if (elmnt != x[i] && elmnt != input) {
252 x[i].parentNode.removeChild(x[i]);
253 }
254 }
255 }
256
257 // execute a function when someone clicks in the document:
258 document.addEventListener("click", function (e) {
259 closeAllLists(e.target);
260 });
261 }
262
263 function $(id) {
264 return document.getElementById(id);
265 }
266
267 function findCommonPrefix(images) {
268 var files = images.map(image => image.name)
269 var A = files.concat().sort();
270 var first = A[0];
271 var last = A[A.length - 1];
272 var L = first.length;
273 var i = 0;
274 while (i < L && first.charAt(i) === last.charAt(i)) {
275 i += 1;
276 }
277 return first.substring(0, i);
278 }
279
280 function updateImages(version, commit, model, url, mobj) {
281 // add download button for image
282 function addLink(label, tags, file, help_id) {
283 var a = document.createElement('A');
284 a.classList.add('download-link');
285 a.href = url
286 .replace('{target}', mobj.target)
287 .replace('{release}', version)
288 + '/' + file;
289 var span = document.createElement('SPAN');
290 span.appendChild(document.createTextNode(''));
291 a.appendChild(span);
292
293 // add sub label
294 if (tags.length > 0) {
295 a.appendChild(document.createTextNode(label + ' (' + tags.join(', ') + ')'));
296 } else {
297 a.appendChild(document.createTextNode(label));
298 }
299
300 if (config.showHelp) {
301 a.onmouseover = function() {
302 // hide all help texts
303 Array.from(document.getElementsByClassName('download-help'))
304 .forEach(function(e) { e.style.display = 'none'; });
305 $(help_id).style.display = 'block';
306 };
307 }
308
309 $('download-links').appendChild(a);
310 }
311
312 // remove all download links
313 Array.from(document.getElementsByClassName('download-link'))
314 .forEach(function(e) { e.remove(); });
315
316 // hide all help texts
317 Array.from(document.getElementsByClassName('download-help'))
318 .forEach(function(e) { e.style.display = 'none'; });
319
320 if (version && commit && model && url && mobj) {
321 var target = mobj.target;
322 var images = mobj.images;
323
324 // fill out build info
325 $('image-model').innerText = model;
326 $('image-target').innerText = target;
327 $('image-release').innerText = version;
328 $('image-commit').innerText = commit;
329
330 var prefix = findCommonPrefix(images);
331 var entries = {
332 'FACTORY': [],
333 'SYSUPGRADE': [],
334 'KERNEL': [],
335 'ROOTFS': [],
336 'SDCARD': [],
337 'TFTP': [],
338 'OTHER': []
339 };
340
341 images.sort();
342
343 for (var i in images) {
344 var image = images[i].name;
345 var lc = image.toLowerCase()
346 if (lc.includes('factory')) {
347 entries['FACTORY'].push(image);
348 } else if (lc.includes('sysupgrade')) {
349 entries['SYSUPGRADE'].push(image);
350 } else if (lc.includes('kernel') || lc.includes('zimage') || lc.includes('uimage')) {
351 entries['KERNEL'].push(image);
352 } else if (lc.includes('rootfs')) {
353 entries['ROOTFS'].push(image);
354 } else if (lc.includes('sdcard')) {
355 entries['SDCARD'].push(image);
356 } else if (lc.includes('tftp')) {
357 entries['TFTP'].push(image);
358 } else {
359 entries['OTHER'].push(image);
360 }
361 }
362
363 function extractTags(prefix, image) {
364 var all = image.substring(prefix.length).split('.')[0].split('-');
365 var ignore = ['', 'kernel', 'zImage', 'uImage', 'factory', 'sysupgrade', 'rootfs', 'sdcard'];
366 return all.filter(function (el) { return !ignore.includes(el); });
367 }
368
369 for (var category in entries) {
370 var images = entries[category];
371 for (var i in images) {
372 var image = images[i];
373 var tags = (images.length > 1) ? extractTags(prefix, image) : [];
374 addLink(category, tags, image, category.toLowerCase() + '-help');
375 }
376 }
377
378 $('images').style.display = 'block';
379 } else {
380 $('images').style.display = 'none';
381 }
382 }
383
384 // hide fields
385 updateImages();
386 changeLanguage(config.language);
387
388 if (config.asu_url) {
389 $('custom').style.display = 'block';
390 }
391
392 setupSelectList($("releases"), Object.keys(config.versions), function(version) {
393 loadFile(config.versions[version], function(obj) {
394 data = obj
395 setupAutocompleteList($("models"), Object.keys(obj['models']), function(model) {
396 if (model in obj['models']) {
397 var url = obj.url;
398 var commit = obj.version_commit;
399 var mobj = obj['models'][model];
400 updateImages(version, commit, model, url, mobj);
401 } else {
402 updateImages();
403 }
404 });
405
406 // trigger model update when selected version changes
407 $("models").onfocus();
408 });
409 });