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