2 var current_model
= {};
5 return document
.getElementById(id
);
9 $(id
).style
.display
= 'block';
13 $(id
).style
.display
= 'none';
16 function build_asa_request() {
17 if (!current_model
|| !current_model
.id
) {
23 return str
.match(/[^\s,]+/g) || [];
26 function get_model_titles(titles
) {
27 return titles
.map(e
=> {
31 return ((e
.vendor
|| '') + (e
.model
|| '') + (e
.variant
|| '')).trim();
36 function showStatus(text
) {
38 $('buildstatus').innerHTML
= text
;
42 function handleError(response
) {
47 var message
= mobj
['message'] || '<span class="tr-build-failed"></span>';
48 if (mobj
.buildlog
== true) {
49 var url
= config
.asu_url
+ '/store/' + mobj
.bin_dir
+ '/buildlog.txt';
50 showStatus('<a href="' + url
+ '">' + message
+ '</a>');
61 showStatus('<span class="tr-request-image"></span>');
64 'profile': current_model
.id
,
65 'packages': split($('packages').value
),
66 'version': $('versions').value
69 fetch(config
.asu_url
+ '/api/build', {
71 headers
: { 'Content-Type': 'application/json' },
72 body
: JSON
.stringify(request_data
)
75 switch (response
.status
) {
78 showStatus('<span class="tr-build-successful"></span>');
82 var download_url
= config
.asu_url
+ '/store/' + mobj
.bin_dir
;
87 get_model_titles(mobj
.titles
),
88 download_url
, mobj
, true
93 showStatus('<span class="tr-check-again"></span>');
94 setTimeout(_
=> { build_asa_request() }, 5000);
96 case 400: // bad request
97 case 422: // bad package
98 case 500: // build failed
99 handleError(response
);
104 hide('buildspinner');
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
);
116 select
.addEventListener('change', e
=> {
117 onselection(items
[select
.selectedIndex
]);
120 if (select
.selectedIndex
>= 0) {
121 onselection(items
[select
.selectedIndex
]);
125 // Change the translation of the entire document
126 function translate() {
127 var mapping
= translations
[config
.language
];
128 for (var tr
in mapping
) {
129 Array
.from(document
.getElementsByClassName(tr
))
130 .forEach(e
=> { e
.innerText
= mapping
[tr
]; })
134 function setupAutocompleteList(input
, items
, onselection
) {
135 // the setupAutocompleteList function takes two arguments,
136 // the text field element and an array of possible autocompleted values:
137 var currentFocus
= -1;
139 // sort numbers and other characters separately
140 var collator
= new Intl
.Collator(undefined, {numeric
: true, sensitivity
: 'base'});
142 items
.sort(collator
.compare
);
144 // execute a function when someone writes in the text field:
145 input
.oninput = function(e
) {
149 var value
= this.value
;
150 // close any already open lists of autocompleted values
157 // create a DIV element that will contain the items (values):
158 var list
= document
.createElement('DIV');
159 list
.setAttribute('id', this.id
+ '-autocomplete-list');
160 list
.setAttribute('class', 'autocomplete-items');
161 // append the DIV element as a child of the autocomplete container:
162 this.parentNode
.appendChild(list
);
164 // for each item in the array...
166 for (var i
= 0; i
< items
.length
; i
+= 1) {
170 var j
= item
.toUpperCase().indexOf(value
.toUpperCase());
177 var div
= document
.createElement('DIV');
178 div
.innerHTML
= '...';
179 list
.appendChild(div
);
182 var div
= document
.createElement('DIV');
183 // make the matching letters bold:
184 div
.innerHTML
= item
.substr(0, j
)
185 + '<strong>' + item
.substr(j
, value
.length
) + '</strong>'
186 + item
.substr(j
+ value
.length
)
187 + '<input type="hidden" value="' + item
+ '">';
189 div
.addEventListener('click', function(e
) {
190 // set text field to selected value
191 input
.value
= this.getElementsByTagName('input')[0].value
;
192 // close the list of autocompleted values,
193 // (or any other open lists of autocompleted values:
196 onselection(input
.value
);
199 list
.appendChild(div
);
204 input
.onkeydown = function(e
) {
205 var x
= document
.getElementById(this.id
+ '-autocomplete-list');
206 if (x
) x
= x
.getElementsByTagName('div');
207 if (e
.keyCode
== 40) {
210 // and and make the current item more visible:
212 } else if (e
.keyCode
== 38) {
215 // and and make the current item more visible:
217 } else if (e
.keyCode
== 13) {
218 // If the ENTER key is pressed, prevent the form from being submitted,
220 if (currentFocus
> -1) {
221 // and simulate a click on the 'active' item:
222 if (x
) x
[currentFocus
].click();
227 input
.onfocus = function() {
228 onselection(input
.value
);
231 function setActive(x
) {
232 // a function to classify an item as 'active':
233 if (!x
) return false;
234 // start by removing the 'active' class on all items:
235 for (var i
= 0; i
< x
.length
; i
++) {
236 x
[i
].classList
.remove('autocomplete-active');
238 if (currentFocus
>= x
.length
) currentFocus
= 0;
239 if (currentFocus
< 0) currentFocus
= (x
.length
- 1);
240 // add class 'autocomplete-active':
241 x
[currentFocus
].classList
.add('autocomplete-active');
244 function closeAllLists(elmnt
) {
245 // close all autocomplete lists in the document,
246 // except the one passed as an argument:
247 var x
= document
.getElementsByClassName('autocomplete-items');
248 for (var i
= 0; i
< x
.length
; i
++) {
249 if (elmnt
!= x
[i
] && elmnt
!= input
) {
250 x
[i
].parentNode
.removeChild(x
[i
]);
255 // execute a function when someone clicks in the document:
256 document
.addEventListener('click', e
=> {
257 closeAllLists(e
.target
);
261 function updateImages(version
, code
, date
, model
, url
, mobj
, is_custom
) {
262 // add download button for image
263 function addLink(type
, file
) {
264 var a
= document
.createElement('A');
265 a
.classList
.add('download-link');
267 .replace('{target}', mobj
.target
)
268 .replace('{version}', version
)
270 var span
= document
.createElement('SPAN');
271 span
.appendChild(document
.createTextNode(''));
273 a
.appendChild(document
.createTextNode(type
.toUpperCase()));
275 if (config
.showHelp
) {
276 a
.onmouseover = function() {
277 // hide all help texts
278 Array
.from(document
.getElementsByClassName('download-help'))
279 .forEach(e
=> e
.style
.display
= 'none');
280 var lc
= type
.toLowerCase();
281 if (lc
.includes('sysupgrade')) {
282 show('sysupgrade-help');
283 } else if (lc
.includes('factory') || lc
== 'trx' || lc
== 'chk') {
284 show('factory-help');
285 } else if (lc
.includes('kernel') || lc
.includes('zimage') || lc
.includes('uimage')) {
287 } else if (lc
.includes('root')) {
289 } else if (lc
.includes('sdcard')) {
291 } else if (lc
.includes('tftp')) {
299 $('download-links').appendChild(a
);
302 function switchClass(id
, from_class
, to_class
) {
303 $(id
).classList
.remove(from_class
);
304 $(id
).classList
.add(to_class
);
307 // remove all download links
308 Array
.from(document
.getElementsByClassName('download-link'))
309 .forEach(e
=> e
.remove());
311 // hide all help texts
312 Array
.from(document
.getElementsByClassName('download-help'))
313 .forEach(e
=> e
.style
.display
= 'none');
315 if (version
&& code
&& date
&& model
&& url
&& mobj
) {
316 var target
= mobj
.target
;
317 var images
= mobj
.images
;
319 // change between "version" and "custom" title
321 switchClass('images-title', 'tr-version-build', 'tr-custom-build');
322 switchClass('downloads-title', 'tr-version-downloads', 'tr-custom-downloads');
324 switchClass('images-title', 'tr-custom-build', 'tr-version-build');
325 switchClass('downloads-title', 'tr-custom-downloads', 'tr-version-downloads');
327 // update title translation
330 // fill out build info
331 $('image-model').innerText
= model
;
332 $('image-target').innerText
= target
;
333 $('image-version').innerText
= version
;
334 $('image-code').innerText
= code
;
335 $('image-date').innerText
= date
;
337 images
.sort((a
, b
) => a
.name
.localeCompare(b
.name
));
339 for (var i
in images
) {
340 addLink(images
[i
].type
, images
[i
].name
);
349 setupSelectList($('versions'), Object
.keys(config
.versions
), version
=> {
350 fetch(config
.versions
[version
]).then(data
=> {
351 data
.json().then(obj
=> {
352 setupAutocompleteList($('models'), Object
.keys(obj
['models']), model
=> {
353 if (model
in obj
['models']) {
355 var code
= obj
.version_code
;
356 var date
= obj
.build_data
|| 'unknown';
357 var mobj
= obj
['models'][model
];
358 updateImages(version
, code
, date
, model
, url
, mobj
, false);
359 current_model
= mobj
;
366 // trigger model update when selected version changes
367 $('models').onfocus();
372 if (config
.asu_url
) {
379 var user_lang
= (navigator
.language
|| navigator
.userLanguage
).split('-')[0];
380 if (user_lang
in translations
) {
381 config
.language
= user_lang
;
382 $('language-selection').value
= user_lang
;