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