2 var current_model
= {};
3 var current_language
= config
.language
;
6 return document
.getElementById(id
);
10 $(id
).style
.display
= 'block';
14 $(id
).style
.display
= 'none';
17 function build_asa_request() {
18 if (!current_model
|| !current_model
.id
) {
24 return str
.match(/[^\s,]+/g) || [];
27 function get_model_titles(titles
) {
28 return titles
.map(e
=> {
32 return ((e
.vendor
|| '') + (e
.model
|| '') + (e
.variant
|| '')).trim();
37 function showStatus(text
) {
39 $('buildstatus').innerHTML
= text
;
43 function handleError(response
) {
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>');
62 showStatus('<span class="tr-request-image"></span>');
65 'profile': current_model
.id
,
66 'packages': split($('packages').value
),
67 'version': $('versions').value
70 fetch(config
.asu_url
+ '/api/build', {
72 headers
: { 'Content-Type': 'application/json' },
73 body
: JSON
.stringify(request_data
)
76 switch (response
.status
) {
79 showStatus('<span class="tr-build-successful"></span>');
84 var download_url
= config
.asu_url
+ '/store/' + mobj
.bin_dir
;
89 get_model_titles(mobj
.titles
),
90 download_url
, mobj
, true
95 showStatus('<span class="tr-check-again"></span>');
96 setTimeout(_
=> { build_asa_request() }, 5000);
98 case 400: // bad request
99 case 422: // bad package
100 case 500: // build failed
101 handleError(response
);
106 hide('buildspinner');
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
);
118 xmlhttp
.open('GET', url
, true);
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
);
129 select
.addEventListener('change', e
=> {
130 onselection(items
[select
.selectedIndex
]);
133 if (select
.selectedIndex
>= 0) {
134 onselection(items
[select
.selectedIndex
]);
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
]; })
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;
152 // sort numbers and other characters separately
153 var collator
= new Intl
.Collator(undefined, {numeric
: true, sensitivity
: 'base'});
155 items
.sort(collator
.compare
);
157 // execute a function when someone writes in the text field:
158 input
.oninput = function(e
) {
162 var value
= this.value
;
163 // close any already open lists of autocompleted values
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
);
177 // for each item in the array...
179 for (var i
= 0; i
< items
.length
; i
+= 1) {
183 var j
= item
.toUpperCase().indexOf(value
.toUpperCase());
190 var div
= document
.createElement('DIV');
191 div
.innerHTML
= '...';
192 list
.appendChild(div
);
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
+ '">';
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:
209 onselection(input
.value
);
212 list
.appendChild(div
);
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) {
223 // and and make the current item more visible:
225 } else if (e
.keyCode
== 38) {
228 // and and make the current item more visible:
230 } else if (e
.keyCode
== 13) {
231 // If the ENTER key is pressed, prevent the form from being submitted,
233 if (currentFocus
> -1) {
234 // and simulate a click on the 'active' item:
235 if (x
) x
[currentFocus
].click();
240 input
.onfocus = function() {
241 onselection(input
.value
);
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');
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');
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
]);
268 // execute a function when someone clicks in the document:
269 document
.addEventListener('click', e
=> {
270 closeAllLists(e
.target
);
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');
280 .replace('{target}', mobj
.target
)
281 .replace('{version}', version
)
283 var span
= document
.createElement('SPAN');
284 span
.appendChild(document
.createTextNode(''));
286 a
.appendChild(document
.createTextNode(type
.toUpperCase()));
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')) {
300 } else if (lc
.includes('root')) {
302 } else if (lc
.includes('sdcard')) {
304 } else if (lc
.includes('tftp')) {
312 $('download-links').appendChild(a
);
315 function switchClass(id
, from_class
, to_class
) {
316 $(id
).classList
.remove(from_class
);
317 $(id
).classList
.add(to_class
);
320 // remove all download links
321 Array
.from(document
.getElementsByClassName('download-link'))
322 .forEach(e
=> e
.remove());
324 // hide all help texts
325 Array
.from(document
.getElementsByClassName('download-help'))
326 .forEach(e
=> e
.style
.display
= 'none');
328 if (version
&& code
&& date
&& model
&& url
&& mobj
) {
329 var target
= mobj
.target
;
330 var images
= mobj
.images
;
332 // change between "version" and "custom" title
334 switchClass('images-title', 'tr-version-build', 'tr-custom-build');
335 switchClass('downloads-title', 'tr-version-downloads', 'tr-custom-downloads');
337 switchClass('images-title', 'tr-custom-build', 'tr-version-build');
338 switchClass('downloads-title', 'tr-custom-downloads', 'tr-version-downloads');
340 // update title translation
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
;
350 images
.sort((a
, b
) => a
.name
.localeCompare(b
.name
));
352 for (var i
in images
) {
353 addLink(images
[i
].type
, images
[i
].name
);
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']) {
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
;
378 // trigger model update when selected version changes
379 $('models').onfocus();
383 if (config
.asu_url
) {