www/index.js: allow deep linking
[web/firmware-selector-openwrt-org.git] / www / index.js
index 9f7c40b4d14f8713e4e178889dce09bb3e9ab582..4258eccef251c5fe150569251d6bd06166295dbe 100644 (file)
@@ -2,6 +2,7 @@
 /* exported build_asu_request, init */
 
 let current_model = {};
+let url_params = undefined;
 
 function $(query) {
   if (typeof query === "string") {
@@ -124,12 +125,18 @@ function build_asu_request() {
 }
 
 function setupSelectList(select, items, onselection) {
-  for (let i = 0; i < items.length; i += 1) {
+  for (const item of items.sort().reverse()) {
     const option = document.createElement("OPTION");
-    option.innerHTML = items[i];
+    option.innerHTML = item;
     select.appendChild(option);
   }
 
+  // pre-select version from URL or config.json
+  const preselect = url_params.get("version") || config.default_version;
+  if (preselect) {
+    $("#versions").value = preselect;
+  }
+
   select.addEventListener("change", () => {
     onselection(items[select.selectedIndex]);
   });
@@ -194,9 +201,7 @@ function setupAutocompleteList(input, items, as_list, onbegin, onend) {
 
     const match = normalize(value);
     let c = 0;
-    for (let i = 0; i < items.length; i += 1) {
-      const item = items[i];
-
+    for (const item of items) {
       // match
       let j = normalize(item).indexOf(match);
       if (j < 0) {
@@ -277,26 +282,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 (let 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:
-    const x = document.getElementsByClassName("autocomplete-items");
-    for (let 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);
       }
     }
   }
@@ -444,19 +449,56 @@ function updateImages(version, code, date, model, url, mobj, is_custom) {
       updatePackageList(version, target);
     }
 
+    // set current selection in URL
+    history.pushState(
+      null,
+      null,
+      document.location.href.split("?")[0] +
+        "?version=" +
+        encodeURIComponent(version) +
+        "&id=" +
+        encodeURIComponent(mobj["id"])
+    );
+
     show("#images");
   } else {
     hide("#images");
   }
 }
 
+// Update model title in search box.
+// Device id and model title might change between releases.
+function setModel(obj, id, model) {
+  if (id) {
+    for (const mobj of Object.values(obj["models"])) {
+      if (mobj["id"] == id) {
+        $("#models").value = mobj["model"];
+        return;
+      }
+    }
+  }
+
+  if (model) {
+    for (const mobj of Object.values(obj["models"])) {
+      if (mobj["model"].toLowerCase() == model.toLowerCase()) {
+        $("#models").value = mobj["model"];
+        return;
+      }
+    }
+  }
+}
+
 function init() {
+  url_params = new URLSearchParams(window.location.search);
   let build_date = "unknown";
+
   setupSelectList($("#versions"), Object.keys(config.versions), (version) => {
+    // A new version was selected
     let url = config.versions[version];
     if (config.asu_url) {
       url = config.asu_url + "/" + url + "/profiles.json";
     }
+
     fetch(url)
       .then((obj) => {
         build_date = obj.headers.get("last-modified");
@@ -471,6 +513,12 @@ function init() {
             obj["models"][get_model_titles(value.titles)] = value;
           }
         }
+
+        // add key (title) to each model object
+        for (const [title, mobj] of Object.entries(obj["models"])) {
+          mobj["model"] = title;
+        }
+
         return obj;
       })
       .then((obj) => {
@@ -494,7 +542,14 @@ function init() {
           }
         );
 
-        // trigger model update when selected version changes
+        // set model when selected version changes
+        setModel(
+          obj,
+          current_model["id"] || url_params.get("id"),
+          current_model["model"] || url_params.get("model")
+        );
+
+        // trigger update of current selected model
         $("#models").onfocus();
       });
   });