www/index.js: deterministic version order and introduce default_version
[web/firmware-selector-openwrt-org.git] / www / index.js
index 2f9afd660d93446239bec7682c4ad47dae309630..f5edb4b65717d079b633ad1da4a7a387ed3d1248 100644 (file)
@@ -1,4 +1,7 @@
-var current_model = {};
+/* global translations, config */
+/* exported build_asu_request, init */
+
+let current_model = {};
 
 function $(query) {
   if (typeof query === "string") {
@@ -46,7 +49,7 @@ function build_asu_request() {
 
   function showStatus(message, url) {
     show("#buildstatus");
-    var tr = message.startsWith("tr-") ? message : "";
+    const tr = message.startsWith("tr-") ? message : "";
     if (url) {
       $("#buildstatus").innerHTML =
         '<a href="' + url + '" class="' + tr + '">' + message + "</a>";
@@ -62,7 +65,7 @@ function build_asu_request() {
   show("#buildspinner");
   showStatus("tr-request-image");
 
-  var request_data = {
+  const request_data = {
     target: current_model.target,
     profile: current_model.id,
     packages: split($("#packages").value),
@@ -81,7 +84,7 @@ function build_asu_request() {
           showStatus("tr-build-successful");
 
           response.json().then((mobj) => {
-            var download_url = config.asu_url + "/store/" + mobj.bin_dir;
+            const download_url = config.asu_url + "/store/" + mobj.bin_dir;
             showStatus("tr-build-successful", download_url + "/buildlog.txt");
             updateImages(
               mobj.version_number,
@@ -96,7 +99,7 @@ function build_asu_request() {
           break;
         case 202:
           showStatus("tr-check-again");
-          setTimeout((_) => {
+          setTimeout(() => {
             build_asu_request();
           }, 5000);
           break;
@@ -105,8 +108,8 @@ function build_asu_request() {
         case 500: // build failed
           hide("#buildspinner");
           response.json().then((mobj) => {
-            var message = mobj["message"] || "tr-build-failed";
-            var url = mobj.buildlog
+            const message = mobj["message"] || "tr-build-failed";
+            const url = mobj.buildlog
               ? config.asu_url + "/store/" + mobj.bin_dir + "/buildlog.txt"
               : undefined;
             showStatus(message, url);
@@ -121,13 +124,19 @@ function build_asu_request() {
 }
 
 function setupSelectList(select, items, onselection) {
-  for (var i = 0; i < items.length; i += 1) {
-    var option = document.createElement("OPTION");
-    option.innerHTML = items[i];
+  for (const item of items.sort().reverse()) {
+    const option = document.createElement("OPTION");
+    option.innerHTML = item;
     select.appendChild(option);
   }
 
-  select.addEventListener("change", (e) => {
+  // pre-select version from config.json
+  const preselect = config.default_version;
+  if (preselect) {
+    $("#versions").value = preselect;
+  }
+
+  select.addEventListener("change", () => {
     onselection(items[select.selectedIndex]);
   });
 
@@ -138,8 +147,8 @@ function setupSelectList(select, items, onselection) {
 
 // Change the translation of the entire document
 function translate() {
-  var mapping = translations[config.language];
-  for (var tr in mapping) {
+  const mapping = translations[config.language];
+  for (const tr in mapping) {
     Array.from(document.getElementsByClassName(tr)).forEach((e) => {
       e.innerText = mapping[tr];
     });
@@ -147,22 +156,22 @@ function translate() {
 }
 
 function setupAutocompleteList(input, items, as_list, onbegin, onend) {
-  var currentFocus = -1;
+  let currentFocus = -1;
 
   // sort numbers and other characters separately
-  var collator = new Intl.Collator(undefined, {
+  const collator = new Intl.Collator(undefined, {
     numeric: true,
     sensitivity: "base",
   });
 
   items.sort(collator.compare);
 
-  input.oninput = function (e) {
+  input.oninput = function () {
     onbegin();
 
-    var offset = 0;
-    var value = this.value;
-    var value_list = [];
+    let offset = 0;
+    let value = this.value;
+    let value_list = [];
 
     if (as_list) {
       // automcomplete last text item
@@ -179,7 +188,7 @@ function setupAutocompleteList(input, items, as_list, onbegin, onend) {
     }
 
     // create a DIV element that will contain the items (values):
-    var list = document.createElement("DIV");
+    const list = document.createElement("DIV");
     list.setAttribute("id", this.id + "-autocomplete-list");
     list.setAttribute("class", "autocomplete-items");
     // append the DIV element as a child of the autocomplete container:
@@ -189,13 +198,11 @@ function setupAutocompleteList(input, items, as_list, onbegin, onend) {
       return s.toUpperCase().replace(/[-_.]/g, " ");
     }
 
-    var match = normalize(value);
-    var c = 0;
-    for (var i = 0; i < items.length; i += 1) {
-      var item = items[i];
-
+    const match = normalize(value);
+    let c = 0;
+    for (const item of items) {
       // match
-      var j = normalize(item).indexOf(match);
+      let j = normalize(item).indexOf(match);
       if (j < 0) {
         continue;
       }
@@ -207,12 +214,12 @@ function setupAutocompleteList(input, items, as_list, onbegin, onend) {
 
       c += 1;
       if (c >= 15) {
-        var div = document.createElement("DIV");
+        let div = document.createElement("DIV");
         div.innerHTML = "...";
         list.appendChild(div);
         break;
       } else {
-        var div = document.createElement("DIV");
+        let div = document.createElement("DIV");
         // make the matching letters bold:
         div.innerHTML =
           item.substr(0, j) +
@@ -224,9 +231,9 @@ function setupAutocompleteList(input, items, as_list, onbegin, onend) {
           item +
           '">';
 
-        div.addEventListener("click", function (e) {
+        div.addEventListener("click", function () {
           // include selected value
-          var selected = this.getElementsByTagName("input")[0].value;
+          const selected = this.getElementsByTagName("input")[0].value;
           if (as_list) {
             input.value = value_list.join(" ") + " " + selected;
           } else {
@@ -243,7 +250,7 @@ function setupAutocompleteList(input, items, as_list, onbegin, onend) {
   };
 
   input.onkeydown = function (e) {
-    var x = document.getElementById(this.id + "-autocomplete-list");
+    let x = document.getElementById(this.id + "-autocomplete-list");
     if (x) x = x.getElementsByTagName("div");
     if (e.keyCode == 40) {
       // key down
@@ -274,26 +281,26 @@ function setupAutocompleteList(input, items, as_list, onbegin, onend) {
     onend(input);
   };
 
-  function setActive(x) {
+  function setActive(xs) {
     // a function to classify an item as 'active':
-    if (!x) return false;
+    if (!xs) return false;
     // start by removing the 'active' class on all items:
-    for (var i = 0; i < x.length; i++) {
-      x[i].classList.remove("autocomplete-active");
+    for (const x of xs) {
+      x.classList.remove("autocomplete-active");
     }
-    if (currentFocus >= x.length) currentFocus = 0;
-    if (currentFocus < 0) currentFocus = x.length - 1;
+    if (currentFocus >= xs.length) currentFocus = 0;
+    if (currentFocus < 0) currentFocus = xs.length - 1;
     // add class 'autocomplete-active':
-    x[currentFocus].classList.add("autocomplete-active");
+    xs[currentFocus].classList.add("autocomplete-active");
   }
 
   function closeAllLists(elmnt) {
     // close all autocomplete lists in the document,
     // except the one passed as an argument:
-    var x = document.getElementsByClassName("autocomplete-items");
-    for (var i = 0; i < x.length; i++) {
-      if (elmnt != x[i] && elmnt != input) {
-        x[i].parentNode.removeChild(x[i]);
+    const xs = document.getElementsByClassName("autocomplete-items");
+    for (const x of xs) {
+      if (elmnt != x && elmnt != input) {
+        x.parentNode.removeChild(x);
       }
     }
   }
@@ -321,18 +328,17 @@ function updatePackageList(version, target) {
         $("#packages"),
         all_packages,
         true,
-        (_) => {},
+        () => {},
         (textarea) => {
           textarea.value = split(textarea.value)
             // make list unique, ignore minus
             .filter((value, index, self) => {
-              var i = self.indexOf(value.replace(/^\-/, ""));
+              const i = self.indexOf(value.replace(/^-/, ""));
               return i === index || i < 0;
             })
             // limit to available packages, ignore minus
             .filter(
-              (value, index) =>
-                all_packages.indexOf(value.replace(/^\-/, "")) !== -1
+              (value) => all_packages.indexOf(value.replace(/^-/, "")) !== -1
             )
             .join(" ");
         }
@@ -343,13 +349,13 @@ function updatePackageList(version, target) {
 function updateImages(version, code, date, model, url, mobj, is_custom) {
   // add download button for image
   function addLink(type, file) {
-    var a = document.createElement("A");
+    const a = document.createElement("A");
     a.classList.add("download-link");
     a.href =
       url.replace("{target}", mobj.target).replace("{version}", version) +
       "/" +
       file;
-    var span = document.createElement("SPAN");
+    const span = document.createElement("SPAN");
     span.appendChild(document.createTextNode(""));
     a.appendChild(span);
     a.appendChild(document.createTextNode(type.toUpperCase()));
@@ -360,7 +366,7 @@ function updateImages(version, code, date, model, url, mobj, is_custom) {
         Array.from(document.getElementsByClassName("download-help")).forEach(
           (e) => (e.style.display = "none")
         );
-        var lc = type.toLowerCase();
+        const lc = type.toLowerCase();
         if (lc.includes("sysupgrade")) {
           show("#sysupgrade-help");
         } else if (lc.includes("factory") || lc == "trx" || lc == "chk") {
@@ -402,8 +408,8 @@ function updateImages(version, code, date, model, url, mobj, is_custom) {
   );
 
   if (model && url && mobj) {
-    var target = mobj.target;
-    var images = mobj.images;
+    const target = mobj.target;
+    const images = mobj.images;
 
     // change between "version" and "custom" title
     if (is_custom) {
@@ -434,7 +440,7 @@ function updateImages(version, code, date, model, url, mobj, is_custom) {
 
     images.sort((a, b) => a.name.localeCompare(b.name));
 
-    for (var i in images) {
+    for (const i in images) {
       addLink(images[i].type, images[i].name);
     }
 
@@ -449,9 +455,9 @@ function updateImages(version, code, date, model, url, mobj, is_custom) {
 }
 
 function init() {
-  var build_date = "unknown";
+  let build_date = "unknown";
   setupSelectList($("#versions"), Object.keys(config.versions), (version) => {
-    var url = config.versions[version];
+    let url = config.versions[version];
     if (config.asu_url) {
       url = config.asu_url + "/" + url + "/profiles.json";
     }
@@ -478,11 +484,11 @@ function init() {
           false,
           updateImages,
           (models) => {
-            var model = models.value;
+            const model = models.value;
             if (model in obj["models"]) {
-              var url = obj.download_url || "unknown";
-              var code = obj.version_code || "unknown";
-              var mobj = obj["models"][model];
+              const url = obj.download_url || "unknown";
+              const code = obj.version_code || "unknown";
+              const mobj = obj["models"][model];
               updateImages(version, code, build_date, model, url, mobj, false);
               current_model = mobj;
             } else {
@@ -505,7 +511,9 @@ function init() {
   updateImages();
 
   // default to browser language
-  var user_lang = (navigator.language || navigator.userLanguage).split("-")[0];
+  const user_lang = (navigator.language || navigator.userLanguage).split(
+    "-"
+  )[0];
   if (user_lang in translations) {
     config.language = user_lang;
     $("#language-selection").value = user_lang;