2 function loadFile(url
, callback
) {
3 var xmlhttp
= new XMLHttpRequest();
4 xmlhttp
.onreadystatechange = function() {
5 if (xmlhttp
.readyState
== 4 && xmlhttp
.status
== 200) {
6 callback(JSON
.parse(xmlhttp
.responseText
), url
);
9 xmlhttp
.open('GET', url
, true);
13 function setupSelectList(select
, items
, onselection
) {
14 for (var i
= 0; i
< items
.length
; i
+= 1) {
15 var option
= document
.createElement("OPTION");
16 option
.innerHTML
= items
[i
];
17 select
.appendChild(option
);
20 select
.addEventListener("change", function(e
) {
21 onselection(items
[select
.selectedIndex
]);
24 if (select
.selectedIndex
>= 0) {
25 onselection(items
[select
.selectedIndex
]);
29 // Change the translation of the entire document
30 function changeLanguage(language
) {
31 var mapping
= translations
[language
];
33 for (var tr
in mapping
) {
34 Array
.from(document
.getElementsByClassName(tr
))
35 .forEach(function(e
) { e
.innerText
= mapping
[tr
]; })
40 function setupAutocompleteList(input
, items
, onselection
) {
41 // the setupAutocompleteList function takes two arguments,
42 // the text field element and an array of possible autocompleted values:
43 var currentFocus
= -1;
45 // sort numbers and other characters separately
46 var collator
= new Intl
.Collator(undefined, {numeric
: true, sensitivity
: 'base'});
48 items
.sort(collator
.compare
);
50 // execute a function when someone writes in the text field:
51 input
.oninput = function(e
) {
55 var value
= this.value
;
56 // close any already open lists of autocompleted values
63 // create a DIV element that will contain the items (values):
64 var list
= document
.createElement("DIV");
65 list
.setAttribute("id", this.id
+ "-autocomplete-list");
66 list
.setAttribute("class", "autocomplete-items");
67 // append the DIV element as a child of the autocomplete container:
68 this.parentNode
.appendChild(list
);
70 // for each item in the array...
72 for (var i
= 0; i
< items
.length
; i
+= 1) {
76 var j
= item
.toUpperCase().indexOf(value
.toUpperCase());
83 var div
= document
.createElement("DIV");
84 div
.innerHTML
= "...";
85 list
.appendChild(div
);
88 var div
= document
.createElement("DIV");
89 // make the matching letters bold:
90 div
.innerHTML
= item
.substr(0, j
)
91 + "<strong>" + item
.substr(j
, value
.length
) + "</strong>"
92 + item
.substr(j
+ value
.length
)
93 + "<input type='hidden' value='" + item
+ "'>";
95 div
.addEventListener("click", function(e
) {
96 // set text field to selected value
97 input
.value
= this.getElementsByTagName("input")[0].value
;
98 // close the list of autocompleted values,
99 // (or any other open lists of autocompleted values:
102 onselection(input
.value
);
105 list
.appendChild(div
);
110 input
.onkeydown = function(e
) {
111 var x
= document
.getElementById(this.id
+ "-autocomplete-list");
112 if (x
) x
= x
.getElementsByTagName("div");
113 if (e
.keyCode
== 40) {
116 // and and make the current item more visible:
118 } else if (e
.keyCode
== 38) {
121 // and and make the current item more visible:
123 } else if (e
.keyCode
== 13) {
124 // If the ENTER key is pressed, prevent the form from being submitted,
126 if (currentFocus
> -1) {
127 // and simulate a click on the "active" item:
128 if (x
) x
[currentFocus
].click();
133 input
.onfocus = function() {
134 onselection(input
.value
);
137 function setActive(x
) {
138 // a function to classify an item as "active":
139 if (!x
) return false;
140 // start by removing the "active" class on all items:
141 for (var i
= 0; i
< x
.length
; i
++) {
142 x
[i
].classList
.remove("autocomplete-active");
144 if (currentFocus
>= x
.length
) currentFocus
= 0;
145 if (currentFocus
< 0) currentFocus
= (x
.length
- 1);
146 // add class "autocomplete-active":
147 x
[currentFocus
].classList
.add("autocomplete-active");
150 function closeAllLists(elmnt
) {
151 // close all autocomplete lists in the document,
152 // except the one passed as an argument:
153 var x
= document
.getElementsByClassName("autocomplete-items");
154 for (var i
= 0; i
< x
.length
; i
++) {
155 if (elmnt
!= x
[i
] && elmnt
!= input
) {
156 x
[i
].parentNode
.removeChild(x
[i
]);
161 // execute a function when someone clicks in the document:
162 document
.addEventListener("click", function (e
) {
163 closeAllLists(e
.target
);
168 return document
.getElementById(id
);
171 function findCommonPrefix(files
) {
172 var A
= files
.concat().sort();
174 var last
= A
[A
.length
- 1];
175 var L
= first
.length
;
177 while (i
< L
&& first
.charAt(i
) === last
.charAt(i
)) {
180 return first
.substring(0, i
);
183 function updateImages(version
, commit
, model
, image_link
, mobj
) {
184 // add download button for image
185 function addLink(label
, tags
, file
, help_id
) {
186 var a
= document
.createElement('A');
187 a
.classList
.add('download-link');
189 .replace('%target', mobj
.target
)
190 .replace('%release', version
)
191 .replace('%file', file
);
192 var span
= document
.createElement('SPAN');
193 span
.appendChild(document
.createTextNode(''));
197 if (tags
.length
> 0) {
198 a
.appendChild(document
.createTextNode(label
+ ' (' + tags
.join(', ') + ')'));
200 a
.appendChild(document
.createTextNode(label
));
203 if (config
.showHelp
) {
204 a
.onmouseover = function() {
205 // hide all help texts
206 Array
.from(document
.getElementsByClassName('download-help'))
207 .forEach(function(e
) { e
.style
.display
= 'none'; });
208 $(help_id
).style
.display
= 'block';
212 $('download-links').appendChild(a
);
215 // remove all download links
216 Array
.from(document
.getElementsByClassName('download-link'))
217 .forEach(function(e
) { e
.remove(); });
219 // hide all help texts
220 Array
.from(document
.getElementsByClassName('download-help'))
221 .forEach(function(e
) { e
.style
.display
= 'none'; });
223 if (version
&& commit
&& model
&& image_link
&& mobj
) {
224 var target
= mobj
.target
;
225 var images
= mobj
.images
;
227 // fill out build info
228 $('image-model').innerText
= model
;
229 $('image-target').innerText
= target
;
230 $('image-release').innerText
= version
;
231 $('image-commit').innerText
= commit
;
233 var prefix
= findCommonPrefix(images
);
246 for (var i
in images
) {
247 var image
= images
[i
];
248 var lc
= image
.toLowerCase()
249 if (lc
.includes('factory')) {
250 entries
['FACTORY'].push(image
);
251 } else if (lc
.includes('sysupgrade')) {
252 entries
['SYSUPGRADE'].push(image
);
253 } else if (lc
.includes('kernel') || lc
.includes('zimage') || lc
.includes('uimage')) {
254 entries
['KERNEL'].push(image
);
255 } else if (lc
.includes('rootfs')) {
256 entries
['ROOTFS'].push(image
);
257 } else if (lc
.includes('sdcard')) {
258 entries
['SDCARD'].push(image
);
259 } else if (lc
.includes('tftp')) {
260 entries
['TFTP'].push(image
);
262 entries
['OTHER'].push(image
);
266 function extractTags(prefix
, image
) {
267 var all
= image
.substring(prefix
.length
).split('.')[0].split('-');
268 var ignore
= ['', 'kernel', 'zImage', 'uImage', 'factory', 'sysupgrade', 'rootfs', 'sdcard'];
269 return all
.filter(function (el
) { return !ignore
.includes(el
); });
272 for (var category
in entries
) {
273 var images
= entries
[category
];
274 for (var i
in images
) {
275 var image
= images
[i
];
276 var tags
= (images
.length
> 1) ? extractTags(prefix
, image
) : [];
277 addLink(category
, tags
, image
, category
.toLowerCase() + '-help');
281 $('images').style
.display
= 'block';
283 $('images').style
.display
= 'none';
289 changeLanguage(config
.language
);
291 setupSelectList($("releases"), Object
.keys(config
.versions
), function(version
) {
292 loadFile(config
.versions
[version
], function(obj
) {
293 setupAutocompleteList($("models"), Object
.keys(obj
['models']), function(model
) {
294 if (model
in obj
['models']) {
296 var commit
= obj
.commit
;
297 var mobj
= obj
['models'][model
];
298 updateImages(version
, commit
, model
, link
, mobj
);
304 // trigger model update when selected version changes
305 $("models").onfocus();