move web content to www folder
authorMoritz Warning <moritzwarning@web.de>
Tue, 16 Jun 2020 20:02:05 +0000 (22:02 +0200)
committerMoritz Warning <moritzwarning@web.de>
Wed, 17 Jun 2020 12:00:30 +0000 (14:00 +0200)
This makes a better separation of actual content and meta data (readme, license, etc.) much better.

15 files changed:
README.md
config.js [deleted file]
i18n.js [deleted file]
index.css [deleted file]
index.html [deleted file]
index.js [deleted file]
logo.png [deleted file]
spinner.gif [deleted file]
www/config.js [new file with mode: 0644]
www/i18n.js [new file with mode: 0644]
www/index.css [new file with mode: 0644]
www/index.html [new file with mode: 0644]
www/index.js [new file with mode: 0644]
www/logo.png [new file with mode: 0644]
www/spinner.gif [new file with mode: 0644]

index 48224a9f3e76238f5ea35a9830664b4b3ef6df99..b071fd6fc2c089f2da825a42a147da3ec04c60d1 100644 (file)
--- a/README.md
+++ b/README.md
@@ -1,18 +1,18 @@
 # Yet Another Firmware Selector
 
 A simple OpenWrt firmware selector using autocompletion. Uses plain
-HTML/CSS/JavaScript. Checkout the [Demo](https://mwarning.github.io/yet_another_firmware_selector/).
+HTML/CSS/JavaScript. Checkout the [Demo](https://mwarning.github.io/yet-another-firmware-selector/www/).
 
 ![image](misc/screenshot.png)
 
 
 ## Run
 
-* Download repository and change directory
+* Checkput the repository and change to the project directory
 * Start webserver (e.g. `python3 -m http.server`)
-* Go to `http://localhost:8000` in your web browser
+* Go to `http://localhost:8000/www/` in your web browser
 
-Configure with [config.js](config.js).
+Configure with [config.js](www/config.js).
 
 ## Attended Sysupgrade Support
 
diff --git a/config.js b/config.js
deleted file mode 100644 (file)
index 6048dd6..0000000
--- a/config.js
+++ /dev/null
@@ -1,15 +0,0 @@
-var config = {
-  // Default language, see i18n.js
-  language: 'en',
-  // Show help text for images
-  showHelp: true,
-  // Image overview file or path to the ASU API
-  versions: {
-    'SNAPSHOT': 'misc/snapshot/overview.json',
-    '19.07.1': 'misc/19.07.1/overview.json',
-    '18.06.7': 'misc/18.06.7/overview.json'
-  },
-  // Build custom images
-  // See https://github.com/aparcar/asu
-  //asu_url: 'https://chef.libremesh.org'
-};
diff --git a/i18n.js b/i18n.js
deleted file mode 100644 (file)
index 56333bc..0000000
--- a/i18n.js
+++ /dev/null
@@ -1,240 +0,0 @@
-
-var translations = {
-       'ca': {
-               'tr-load': 'Descarregueu el microprogramari OpenWrt per al vostre dispositiu!',
-               'tr-title': 'Selector de microprogramari OpenWrt',
-               'tr-message': 'Feu servir el formulari de sota per seleccionar i descarregar el microprogramari per al vostre dispositiu!',
-               'tr-version-build': 'Compilació',
-               'tr-custom-build': 'Compilació personalitzada',
-               'tr-customize': 'Personalitzar',
-               'tr-request-build': 'Demanar la compilació',
-               'tr-model': 'Model:',
-               'tr-target': 'Plataforma:',
-               'tr-version': 'Versió:',
-               'tr-date': 'Data:',
-               'tr-downloads': 'Descàrregues',
-               'tr-custom-downloads': 'Descàrregues personalitzades',
-               'tr-factory-help': 'Les imatges Factory són per instal·lar OpenWrt als dispositius per primera vegada. Usualment, això es fa a través de la interfície web del microprogramari original.',
-               'tr-sysupgrade-help': 'Les imatges Sysupgrade són per instal·lar-les als dispositius que ja tenen OpenWrt. La imatge es pot instal·lar a través de la interfície web o del terminal.',
-               'tr-kernel-help': 'El nucli de Linux en una imatge separada.',
-               'tr-rootfs-help': 'El sistema de fitxers arrel en una imatge separada.',
-               'tr-sdcard-help': 'Una imatge feta per escriure-la a una targeta SD.',
-               'tr-tftp-help': 'Les imatges TFTP images es fan servir per instal·lar-les a un dispositiu mitjançant el mètode TFTP del carregador d\'arrencada.',
-               'tr-other-help': 'Un altre tipus d\'imatge.',
-               'tr-build-successful': 'La compilació ha tingut èxit',
-               'tr-build-failed': 'La compilació ha fallat',
-               'tr-request-image': 'Demanar la imatge',
-               'tr-check-again': 'Proveu de nou d\'aquí 5 segons...'
-       },
-       'en': {
-               'tr-load': 'Download OpenWrt firmware for your device!',
-               'tr-title': 'OpenWrt Firmware Selector',
-               'tr-message': 'Please use the input below to download firmware for your device!',
-               'tr-version-build': 'Build',
-               'tr-custom-build': 'Custom Build',
-               'tr-customize': 'Customize',
-               'tr-request-build': 'Request Build',
-               'tr-model': 'Model:',
-               'tr-target': 'Platform:',
-               'tr-version': 'Version:',
-               'tr-date': 'Date:',
-               'tr-downloads': 'Downloads',
-               'tr-custom-downloads': 'Custom Downloads',
-               'tr-factory-help': 'Factory images are for flashing routers with OpenWrt for the first time. Usually via the web interface of the original firmware.',
-               'tr-sysupgrade-help': 'Sysupgrade images are for flashing routers that already run OpenWrt. The image can be applied using the web interface or the terminal.',
-               'tr-kernel-help': 'Linux kernel as a separate image.',
-               'tr-rootfs-help': 'Root file system as a separate image.',
-               'tr-sdcard-help': 'Image that is meant to be flashed onto a SD-Card.',
-               'tr-tftp-help': 'TFTP images are used to flash a device via the TFTP method of the bootloader.',
-               'tr-other-help': 'Other image type.',
-               'tr-build-successful': 'Build successful',
-               'tr-build-failed': 'Build failed',
-               'tr-request-image': 'Request image',
-               'tr-check-again': 'Check again in 5 seconds...'
-       },
-       'es': {
-               'tr-load': 'Descargue el firmware OpenWrt para su dispositivo!',
-               'tr-title': 'Selector de firmware OpenWrt',
-               'tr-message': 'Utilice la entrada a continuación para descargar el firmware de su dispositivo!',
-               'tr-version-build': 'Compilar',
-               'tr-custom-build': 'Compilación personalizada',
-               'tr-customize': 'Personalizar',
-               'tr-request-build': 'Solicitar compilación',
-               'tr-model': 'Modelo:',
-               'tr-target': 'Plataforma:',
-               'tr-version': 'Versión:',
-               'tr-date': 'Fecha:',
-               'tr-downloads': 'Descargas',
-               'tr-custom-downloads': 'Descargas personalizadas',
-               'tr-factory-help': 'Las imágenes de fábrica son para enrutadores intermitentes con OpenWrt por primera vez. Generalmente a través de la interfaz web del firmware original.',
-               'tr-sysupgrade-help': 'Las imágenes de Sysupgrade son para enrutadores intermitentes que ya ejecutan OpenWrt. La imagen se puede aplicar utilizando la interfaz web o el terminal.',
-               'tr-kernel-help': 'Kernel de Linux como una imagen separada.',
-               'tr-rootfs-help': 'Sistema de archivos raíz como una imagen separada.',
-               'tr-sdcard-help': 'Imagen que debe ser mostrada en una tarjeta SD.',
-               'tr-tftp-help': 'Las imágenes TFTP se utilizan para flashear un dispositivo mediante el método TFTP del gestor de arranque.',
-               'tr-other-help': 'Otro tipo de imagen.',
-               'tr-build-successful': 'Compilación exitosa',
-               'tr-build-failed': 'Compilación fallida',
-               'tr-request-image': 'Solicitar imagen',
-               'tr-check-again': 'Verifique nuevamente en 5 segundos...'
-       },
-       'no': {
-               'tr-load': 'Last ned OpenWrt fastvare for din enhet!',
-               'tr-title': 'OpenWrt fastvare utvelger',
-               'tr-message': 'Bruk feltene nedenfor for å laste ned fastvare til enheten din!',
-               'tr-version-build': 'Sammensetning',
-               'tr-custom-build': 'Tilpasset sammensetning',
-               'tr-customize': 'Tilpasse',
-               'tr-request-build': 'Be om sammensetning',
-               'tr-model': 'Modell:',
-               'tr-target': 'Platform:',
-               'tr-version': 'Versjon:',
-               'tr-date': 'Dato:',
-               'tr-downloads': 'Nedlastninger',
-               'tr-custom-downloads': 'Tilpassede nedlastninger',
-               'tr-factory-help': 'Factory avbildningen er for å laste rutere med OpenWrt første gang. Vanligvis via webgrensesnittet til den originale fastvaren.',
-               'tr-sysupgrade-help': 'Sysupgrade avbildningen er for rutere som allerede benytter OpenWrt. Avbildningen innstaleres gjennom webgrensesnittet eller terminalen.',
-               'tr-kernel-help': 'Linux kjernen som en egen avbildning.',
-               'tr-rootfs-help': 'Rotfilsystem som en egen avbildning.',
-               'tr-sdcard-help': 'Avbildning som er ment for et SD-kort.',
-               'tr-tftp-help': 'TFTP avbildninger er for å laste enheter via TFTP metoden i oppstartsprosedyren.',
-               'tr-other-help': 'Andre avbildningstyper.',
-               'tr-build-successful': 'Vellykket sammensetning',
-               'tr-build-failed': 'Sammensetningen feilet',
-               'tr-request-image': 'Be om avbildning',
-               'tr-check-again': 'Sjekk pånytt om 5 sekunder...'
-       },
-       'de': {
-               'tr-load': 'Lade die OpenWrt Firmware für dein Gerät!',
-               'tr-title': 'OpenWrt Firmware Selector',
-               'tr-message': 'Bitte benutze die Eingabe um die passende Firmware zu finden!',
-               'tr-version-build': 'Release Build',
-               'tr-custom-build': 'Custom Build',
-               'tr-customize': 'Customize',
-               'tr-request-build': 'Request Build',
-               'tr-model': 'Model:',
-               'tr-target': 'Target',
-               'tr-version': 'Version:',
-               'tr-date': 'Datum:',
-               'tr-downloads': 'Downloads',
-               'tr-custom-downloads': 'Custom Downloads',
-               'tr-factory-help': 'Factory Abbilder werden über die Weboberfläche der originalen Firmware eingespielt.',
-               'tr-sysupgrade-help': 'Sysupgrade Abbilder werden für Geräte verwendet, die bereits OpenWrt laufen haben. Es ist möglich, existierende Einstellungen beizubehalten.',
-               'tr-kernel-help': 'Linux Kernel als separates Abbild.',
-               'tr-rootfs-help': 'Das Root Dateisystem als separates Abbild.',
-               'tr-sdcard-help': 'Image für SD Speicherkarten.',
-               'tr-tftp-help': 'TFTP Dateien können verwendet werden, um ein Gerät über die TFTP Method des Bootloader zu flashen.',
-               'tr-other-help': 'Sonstiger Imagetyp.',
-               'tr-build-successful': 'Build erfolgreich',
-               'tr-build-failed': 'Build fehlgeschlagen',
-               'tr-request-image': 'Frage nach image',
-               'tr-check-again': 'Nochmal nachfragen in 5 Sekunden...'
-       },
-       'fr': {
-               'tr-load': 'Télécharger le firmware OpenWrt de votre périphérique !',
-               'tr-title': 'Sélecteur de Firmware',
-               'tr-message': 'Utiliser les entrées ci-dessous pour télécharger le firmware de votre périphérique !',
-               'tr-version-build': 'Build',
-               'tr-custom-build': 'Build Personnalisé',
-               'tr-customize': 'Personnalisation',
-               'tr-request-build': 'Requête de Build',
-               'tr-model': 'Modèle:',
-               'tr-target': 'Platform:',
-               'tr-version': 'Version:',
-               'tr-date': 'Date:',
-               'tr-downloads': 'Téléchargements',
-               'tr-custom-downloads': 'Téléchargements Personnalusés',
-               'tr-factory-help': 'Les images Factory sont prévues pour flasher les routers avec OpenWrt pour la première fois. Habituellement à partir de l\'interface web du firmware d\'origine.',
-               'tr-sysupgrade-help': 'Les images Sysupgrade sont prévues pour flasher les routers fonctionnant déjà avec OpenWrt. L\'image peut être installée à travers l\'interface web ou par le terminal.',
-               'tr-kernel-help': 'Linux kernel comme image séparée.',
-               'tr-rootfs-help': 'Root file system comme image séparée.',
-               'tr-sdcard-help': 'Image prévue pour être flashée sur une carte SD.',
-               'tr-tftp-help': 'TFTP images prévues pour flasher le périphérique via le démarrage par méthode TFTP.',
-               'tr-other-help': 'Autre type d\'image.',
-               'tr-build-successful': 'Succès du Build',
-               'tr-build-failed': 'Échec du Build',
-               'tr-request-image': 'Demade d\'image',
-               'tr-check-again': 'Essayer à nouveau dans 5 secondes...'
-       },
-       'it': {
-               'tr-load': 'Scarica il firmware OpenWrt per il tuo dispositivo!',
-               'tr-title': 'OpenWrt Firmware Selector',
-               'tr-message': 'Usa la casella sottostante per scaricare il firmware per il tuo dispositivo!',
-               'tr-version-build': 'Build',
-               'tr-custom-build': 'Custom Build',
-               'tr-customize': 'Personalizza',
-               'tr-request-build': 'Richiedi Build',
-               'tr-model': 'Modell:',
-               'tr-target': 'Platform:',
-               'tr-version': 'Version:',
-               'tr-date': 'Data:',
-               'tr-downloads': 'Downloads',
-               'tr-custom-downloads': 'Download Personalizzati',
-               'tr-factory-help': 'Factory Image sono usate per installare OpenWrt su router per la prima volta. Di solito l\'immagine può essere applicata via l\'interfaccia web del firmware originale.',
-               'tr-sysupgrade-help': 'Sysupgrade Image sono usate per flashare router in cui OpenWrt è già installato. L\'immagine può essere applicata via interfaccia web o terminale.',
-               'tr-kernel-help': 'Linux kernel come immagine separata.',
-               'tr-rootfs-help': 'Root file system come immagine separata.',
-               'tr-sdcard-help': 'Immagine da flashare su scheda SD-Card separata.',
-               'tr-tftp-help': 'Immagini TFTP images sono usate per flashare un dispositivo con il metodo TFTP del bootloader.',
-               'tr-other-help': 'Other image type.',
-               'tr-build-successful': 'Build compilata con successo',
-               'tr-build-failed': 'Build fallita',
-               'tr-request-image': 'Richiedi immagine',
-               'tr-check-again': 'Prova di nuovo in 5 secondi...'
-       },
-       'pl': {
-               'tr-load': 'Pobieranie oprogramowania OpenWrt!',
-               'tr-title': 'OpenWrt Firmware Selector',
-               'tr-message': 'Użyj pola poniżej żeby znaleźć obraz dla swojego urządzenia!',
-               'tr-version-build': 'Informacje o obrazie',
-               'tr-custom-build': 'Informacje o zmodyfikowanym obrazie',
-               'tr-customize': 'Modyfikacja',
-               'tr-request-build': 'Żądanie budowy obrazu',
-               'tr-model': 'Model:',
-               'tr-target': 'Platforma:',
-               'tr-version': 'Wersja:',
-               'tr-date': 'Data:',
-               'tr-downloads': 'Obrazy do pobrania',
-               'tr-custom-downloads': 'Zmodyfikowane obrazy do pobrania',
-               'tr-factory-help': 'Obraz factory używany jest do pierwszej instalacji OpenWrt. Zwykle można go użyć wykorzystując interfejs graficzny oryginalnego oprogramowania.',
-               'tr-sysupgrade-help': 'Obraz sysuprade używany jest do aktualizacji routerów z zainstalowanym już OpenWrt. Obraz można użyć przez GUI lub konsolę.',
-               'tr-kernel-help': 'Osobny obraz z kernelem linuksowym.',
-               'tr-rootfs-help': 'Osobny obraz z system plików.',
-               'tr-sdcard-help': 'Obraz do wgrania na kartę SD.',
-               'tr-tftp-help': 'Obraz TFTP służący do aktualizacji urządzenia z wykorzystaniem TFTP i bootloadera.',
-               'tr-other-help': 'Inny typ obrazu.',
-               'tr-build-successful': 'Budowanie zakończone pomyślnie',
-               'tr-build-failed': 'Błąd budowania',
-               'tr-request-image': 'Żądanie obrazu',
-               'tr-check-again': 'Sprawdź ponownie za 5 sekund...'
-       },
-       'tr': {
-               'tr-load': 'Cihazınız için OpenWrt yazılımını indirin!',
-               'tr-title': 'OpenWrt Yazılım Seçicisi',
-               'tr-message': 'Cihazınız için yazılımı indirmek için lütfen aşağıdaki girişi kullanın!',
-               'tr-version-build': 'Sürüm',
-               'tr-custom-build': 'Özel Sürüm',
-               'tr-customize': 'Özelleştir',
-               'tr-request-build': 'Sürüm Oluştur',
-               'tr-model': 'Model:',
-               'tr-target': 'Platform:',
-               'tr-version': 'Versiyon:',
-               'tr-date': 'Tarih:',
-               'tr-downloads': 'İndirmeler',
-               'tr-custom-downloads': 'Özel İndirmeler',
-               'tr-factory-help': 'Fabrika imajları, ilk kez OpenWrt yüklenen cihazlar içindir. Genellikle orijinal ürün yazılımının web arayüzü üzerinden yüklenir.',
-               'tr-sysupgrade-help': 'Sysupgrade imajları, zaten OpenWrt kurulu cihazlar içindir. İmaj, web arayüzü veya terminal kullanılarak yüklenebilir.',
-               'tr-kernel-help': 'Linux kernel ayrı bir imaj olarak.',
-               'tr-rootfs-help': 'Kök Dosya Sistemi ayrı bir imaj olarak.',
-               'tr-sdcard-help': 'SD-Kart \'a kurulması planlanan imaj',
-               'tr-tftp-help': 'TFTP imajları, Bootloader \'ın TFTP yöntemi ile bir cihaza kurulması için kullanılır.',
-               'tr-other-help': 'Diğer imaj türü.',
-               'tr-build-successful': 'Oluşturma başarılı',
-               'tr-build-failed': 'Oluşturma başarısız',
-               'tr-request-image': 'Imaj oluştur',
-               'tr-check-again': '5 saniye icinde tekrar dene...'
-       }
-};
-
-// Complement translations based on other translations
-//translations['en'] = Object.assign({}, translations['de'], translations['en']);
diff --git a/index.css b/index.css
deleted file mode 100644 (file)
index e0e1885..0000000
--- a/index.css
+++ /dev/null
@@ -1,231 +0,0 @@
-
-body {
-  font-family: "Arial", sans-serif;
-  margin: 0px;
-}
-
-#buildstatus a {
-  color: inherit;
-}
-
-#models-autocomplete {
-  width: 20em;
-  display: inline-block;
-}
-
-.autocomplete {
-  position: relative;
-}
-
-.autocomplete > input {
-  border: 1px solid transparent;
-  background-color: #f1f1f1;
-  padding: 10px;
-  width: 100%;
-  border-radius: 4px;
-}
-
-.autocomplete-items {
-  position: absolute;
-  border: 1px solid #d4d4d4;
-  border-bottom: none;
-  border-top: none;
-  z-index: 99;
-  /*position the autocomplete items to be the same width as the container:*/
-  top: 100%;
-  left: 0;
-  right: 0;
-}
-
-.autocomplete-items div {
-  padding: 10px;
-  cursor: pointer;
-  background-color: #fff; 
-  border-bottom: 1px solid #d4d4d4; 
-}
-
-/*when hovering an item:*/
-.autocomplete-items div:hover {
-  background-color: #e9e9e9; 
-}
-
-/*when navigating through the items using the arrow keys:*/
-.autocomplete-active {
-  background-color: DodgerBlue !important; 
-  color: #ffffff; 
-}
-
-header {
-  font-weight: 500;
-  width: 100%;
-  z-index: 1100;
-  box-shadow: 0px 2px 4px -1px rgba(0,0,0,0.2),0px 4px 5px 0px rgba(0,0,0,0.14),0px 1px 10px 0px rgba(0,0,0,0.12);
-  background-color: #00A3E1;
-}
-
-h6 {
-  margin: 0;
-  font-size: 1.25rem;
-  font-weight: 500;
-  line-height: 1.6;
-  letter-spacing: 0.0075em;
-}
-
-header > div {
-  padding-left: 24px;
-  padding-right: 24px;
-  min-height: 64px;
-
-  display: flex;
-  position: relative;
-  align-items: center;
-  color: #fff;
-}
-
-.container {
-  max-width: 1280px;
-  padding-left: 32px;
-  padding-right: 32px;
-  width: 100%;
-  box-sizing: border-box;
-  margin-top: 30px;
-  margin-right: auto;
-  margin-left: auto;
-  margin-bottom: 100px;
-}
-
-.container > div {
-  padding: 10px 20px;
-  text-align: left;
-  box-shadow: 0px 1px 3px 0px rgba(0,0,0,0.2),0px 1px 1px 0px rgba(0,0,0,0.14),0px 2px 1px -1px rgba(0,0,0,0.12);
-  border-radius: 4px;
-  color: rgba(0, 0, 0, 0.87);
-  transition: box-shadow 300ms cubic-bezier(0.4, 0, 0.2, 1) 0ms;
-  background-color: #fff;
-}
-
-#versions {
-  border: 1px solid transparent;
-  background-color: #f1f1f1;
-  padding: 10px;
-  width: 10em;
-  border-radius: 4px;
-}
-
-#language-selection {
-  color: #fff;
-  background-color: #1084b2;
-  box-shadow: 0px 1px 5px 0px rgba(0,0,0,0.2),0px 2px 2px 0px rgba(0,0,0,0.14),0px 3px 1px -2px
-  rgba(0,0,0,0.12);
-  padding: 6px 16px;
-  border-radius: 4px;
-  border: 0;
-
-  /* hide arrow */
-  -webkit-appearance: none;
-  -moz-appearance: none;
-  text-indent: 1px;
-  text-overflow: '';
-}
-
-.download-link {
-  text-decoration: none;
-  border-radius: 4px;
-  padding: 12px 16px;
-  margin: 5px;
-  font-size: 16px;
-  cursor: pointer;
-  letter-spacing: 0.05em;
-  display: inline-flex;
-  align-items: center;
-  box-shadow: 0px 1px 5px 0px rgba(0,0,0,0.2),0px 2px 2px 0px rgba(0,0,0,0.14),0px 3px 1px -2px rgba(0,0,0,0.12);
-  color: #fff;
-  background-color: #00A3E1;
-}
-
-.download-link:hover {
-  background-color: #038fc6
-}
-
-.download-link :first-child {
-  width: 30px;
-  margin-right: 15px;
-  margin-top: -2px;
-  content: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath d='M19.35 10.04C18.67 6.59 15.64 4 12 4 9.11 4 6.6 5.64 5.35 8.04 2.34 8.36 0 10.91 0 14c0 3.31 2.69 6 6 6h13c2.76 0 5-2.24 5-5 0-2.64-2.05-4.78-4.65-4.96zM17 13l-5 5-5-5h3V9h4v4h3z' fill='%23fff'%3E%3C/path%3E%3Cpath d='M0 0h24v24H0z' fill='none'/%3E%3C/svg%3E");
-}
-
-#images .column {
-  width: 5em;
-  display: inline-block;
-  line-height: 1.5;
-}
-
-#images {
-  display: none;
-}
-
-#images > div {
-  padding-top: 20px;
-}
-
-#image-model {
-  font-weight: bold;
-}
-
-#custom {
-  display: none;
-}
-
-#custom textarea {
-  width: 500px;
-  height: 120px;
-  font-size: 16px;
-  display: block;
-}
-
-#custom a :first-child {
-  width: 30px;
-  margin-right: 10px;
-  margin-top: 0px;
-  font-size: 36px;
-}
-
-#custom a {
-  text-decoration: none;
-  border-radius: 4px;
-  padding: 2px 10px;
-  margin: 5px;
-  font-size: 16px;
-  cursor: pointer;
-  letter-spacing: 0.05em;
-  display: inline-flex;
-  align-items: center;
-  box-shadow: 0px 1px 5px 0px rgba(0,0,0,0.2),0px 2px 2px 0px rgba(0,0,0,0.14),0px 3px 1px -2px rgba(0,0,0,0.12);
-  color: #fff;
-  background-color: #00A3E1;
-}
-
-.download-help {
-  display: none;
-}
-
-#buildspinner {
-  float: left;
-  height: 40px;
-  padding-right: 12px;
-  display: none;
-}
-
-#buildstatus {
-  padding: 10px 0;
-  display: none;
-}
-
-#footer {
-  font-size: 0.8em;
-  text-align: right;
-}
-
-#footer a {
-  text-decoration: none;
-}
diff --git a/index.html b/index.html
deleted file mode 100644 (file)
index 2260f81..0000000
+++ /dev/null
@@ -1,96 +0,0 @@
-<!DOCTYPE html>
-
-<html lang="en">
-<head>
-       <meta charset="utf-8"/>
-       <title>OpenWrt Firmware Selector</title>
-       <link rel="stylesheet" href="index.css" />
-       <script src="i18n.js"></script>
-       <script src="config.js"></script>
-       <script src="index.js"></script>
-</head>
-<body onload="init()">
-
-<header>
-       <div>
-<!--
-               <h6 class="tr-title">OpenWrt Firmware Selector</h6>
--->
-               <img src="logo.png" alt="Logo">
-               <div style="flex-grow: 1;"></div>
-
-               <select id="language-selection" size="1">
-                       <option value="ca">Català</option>
-                       <option value="en">English</option>
-                       <option value="es">Español</option>
-                       <option value="de">Deutsch</option>
-                       <option value="fr">Français</option>
-                       <option value="it">Italiano</option>
-                       <option value="no">Norsk</option>
-                       <option value="pl">Polski</option>
-                       <option value="tr">Türkçe</option>
-               </select>
-       </div>
-</header>
-
-<div class="container">
-       <div>
-               <h2 class="tr-load">Download OpenWrt firmware for your device!</h2>
-               <p class="tr-message">Please use the input below to download firmware for your device!</p>
-               <br>
-
-               <select id="versions" size="1"></select>
-               <div id="models-autocomplete" class="autocomplete">
-                       <input id="models" type="text" placeholder="Model" spellcheck="false" autocapitalize="off" autocorrect="off">
-               </div>
-
-               <br />
-               <br />
-
-               <div>
-                       <img id="buildspinner" src="spinner.gif" alt="Logo">
-                       <div id="buildstatus"></div>
-               </div>
-
-               <div id="images">
-                       <div id="custom">
-                               <h3 class="tr-customize">Customize</h3>
-                               <div class="autocomplete">
-                                       <textarea id="packages" spellcheck="false" autocapitalize="off" autocorrect="off"></textarea>
-                               </div>
-                               <a href="javascript:build_asu_request()" class="custom-link">
-                                       <span>&#9881;</span><span class="tr-request-build">Request Build</span>
-                               </a>
-                       </div>
-
-                       <div>
-                               <h3 id="images-title" class="tr-version-build">Release Build</h3>
-                               <div><span class="column tr-model">Model:</span> <span id="image-model"></span></div>
-                               <div><span class="column tr-target">Target:</span> <span id="image-target"></span></div>
-                               <div><span class="column tr-version">Version:</span> <span id="image-version"></span> (<span id="image-code"></span>)</div>
-                               <div><span class="column tr-date">Date:</span> <span id="image-date"></span></div>
-                       </div>
-
-                       <div id="download-links">
-                               <h3 id="downloads-title" class="tr-downloads">Downloads</h3>
-                       </div>
-
-                       <div>
-                               <span id="factory-help" class="download-help tr-factory-help">Factory images are for flashing routers with OpenWrt for the first time using the web interface of the original firmware.</span>
-                               <span id="sysupgrade-help" class="download-help tr-sysupgrade-help">Sysupgrade images are for flashing routers that already run OpenWrt. The image can be applied using the web interface or the console.</span>
-                               <span id="kernel-help" class="download-help tr-kernel-help">Linux kernel as a separate image.</span>
-                               <span id="rootfs-help" class="download-help tr-rootfs-help">Root file system as a separate image.</span>
-                               <span id="sdcard-help" class="download-help tr-sdcard-help">Image that is meant to be flashed on an SD-Card.</span>
-                               <span id="tftp-help" class="download-help tr-tftp-help">Image that can be applied using the TFTP meachnism of the bootloader</span>
-                               <span id="other-help" class="download-help tr-other-help">Image of unknown purpose.</span>
-                       </div>
-               </div>
-
-               <div id="footer">
-                       <span><a href="https://github.com/mwarning/yet_another_firmware_selector">YAFS</a> v2.2.1</span>
-               </div>
-       </div>
-</div>
-
-</body>
-</html>
diff --git a/index.js b/index.js
deleted file mode 100644 (file)
index 7562da4..0000000
--- a/index.js
+++ /dev/null
@@ -1,451 +0,0 @@
-
-var current_model = {};
-
-function $(id) {
-  return document.getElementById(id);
-}
-
-function show(id) {
-  $(id).style.display = 'block';
-}
-
-function hide(id) {
-  $(id).style.display = 'none';
-}
-
-function split(str) {
-  return str.match(/[^\s,]+/g) || [];
-}
-
-function get_model_titles(titles) {
-  return titles.map(e => {
-    if (e.title) {
-      return e.title;
-    } else {
-      return ((e.vendor || '') + ' ' + (e.model || '') + ' ' + (e.variant || '')).trim();
-    }
-  }).join(' / ');
-}
-
-function build_asu_request() {
-  if (!current_model || !current_model.id) {
-    alert('bad profile');
-    return;
-  }
-
-  function showStatus(message, url) {
-    show('buildstatus');
-    var tr = message.startsWith('tr-') ? message : '';
-    if (url) {
-      $('buildstatus').innerHTML = '<a href="' + url + '" class="' + tr + '">' + message + '</a>';
-    } else {
-      $('buildstatus').innerHTML = '<span class="' + tr + '"></span>';
-    }
-    translate();
-  }
-
-  // hide image view
-  updateImages();
-
-  show('buildspinner');
-  showStatus('tr-request-image');
-
-  var request_data = {
-    'target': current_model.target,
-    'profile': current_model.id,
-    'packages': split($('packages').value),
-    'version': $('versions').value
-  }
-
-  fetch(config.asu_url + '/api/build', {
-     method: 'POST',
-     headers: { 'Content-Type': 'application/json' },
-     body: JSON.stringify(request_data)
-  })
-  .then(response => {
-    switch (response.status) {
-      case 200:
-        hide('buildspinner');
-        showStatus('tr-build-successful');
-
-        response.json()
-        .then(mobj => {
-          var download_url = config.asu_url + '/store/' + mobj.bin_dir;
-          showStatus('tr-build-successful', download_url + '/buildlog.txt');
-          updateImages(
-            mobj.version_number,
-            mobj.version_code,
-            mobj.build_at,
-            get_model_titles(mobj.titles),
-            download_url, mobj, true
-          );
-        });
-        break;
-      case 202:
-        showStatus('tr-check-again');
-        setTimeout(_ => { build_asu_request() }, 5000);
-        break;
-      case 400: // bad request
-      case 422: // bad package
-      case 500: // build failed
-        hide('buildspinner');
-        response.json()
-        .then(mobj => {
-          var message = mobj['message'] || 'tr-build-failed';
-          var url = mobj.buildlog ? (config.asu_url + '/store/' + mobj.bin_dir + '/buildlog.txt') : undefined;
-          showStatus(message, url);
-        })
-        break;
-    }
-  })
-  .catch(err => {
-    hide('buildspinner');
-    showStatus(err);
-  })
-}
-
-function setupSelectList(select, items, onselection) {
-  for (var i = 0; i < items.length; i += 1) {
-    var option = document.createElement('OPTION');
-    option.innerHTML = items[i];
-    select.appendChild(option);
-  }
-
-  select.addEventListener('change', e => {
-    onselection(items[select.selectedIndex]);
-  });
-
-  if (select.selectedIndex >= 0) {
-    onselection(items[select.selectedIndex]);
-  }
-}
-
-// Change the translation of the entire document
-function translate() {
-  var mapping = translations[config.language];
-  for (var tr in mapping) {
-    Array.from(document.getElementsByClassName(tr))
-      .forEach(e => { e.innerText = mapping[tr]; })
-  }
-}
-
-function setupAutocompleteList(input, items, as_list, onbegin, onend) {
-  var currentFocus = -1;
-
-  // sort numbers and other characters separately
-  var collator = new Intl.Collator(undefined, {numeric: true, sensitivity: 'base'});
-
-  items.sort(collator.compare);
-
-  input.oninput = function(e) {
-    onbegin();
-
-    var offset = 0;
-    var value = this.value;
-    var value_list = [];
-
-    if (as_list) {
-      // automcomplete last text item
-      offset = this.value.lastIndexOf(' ') + 1;
-      value = this.value.substr(offset);
-      value_list = split(this.value.substr(0, offset));
-    }
-
-    // close any already open lists of autocompleted values
-    closeAllLists();
-
-    if (!value) {
-      return false;
-    }
-
-    // create a DIV element that will contain the items (values):
-    var 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:
-    this.parentNode.appendChild(list);
-
-    var c = 0;
-    for (var i = 0; i < items.length; i += 1) {
-      var item = items[i];
-
-      // match
-      var j = item.toUpperCase().indexOf(value.toUpperCase());
-      if (j < 0) {
-        continue;
-      }
-
-      // do not offer a duplicate item
-      if (as_list && value_list.indexOf(item) != -1) {
-        continue;
-      }
-
-      c += 1;
-      if (c >= 15) {
-        var div = document.createElement('DIV');
-        div.innerHTML = '...';
-        list.appendChild(div);
-        break;
-      } else {
-        var div = document.createElement('DIV');
-        // make the matching letters bold:
-        div.innerHTML = item.substr(0, j)
-          + '<strong>' + item.substr(j, value.length) + '</strong>'
-          + item.substr(j + value.length)
-          + '<input type="hidden" value="' + item + '">';
-
-        div.addEventListener('click', function(e) {
-          // include selected value
-          var selected = this.getElementsByTagName('input')[0].value;
-          if (as_list) {
-            input.value = value_list.join(' ') + ' ' + selected;
-          } else {
-            input.value = selected;
-          }
-          // close the list of autocompleted values,
-          closeAllLists();
-          onend(input);
-        });
-
-        list.appendChild(div);
-      }
-    }
-  };
-
-  input.onkeydown = function(e) {
-      var x = document.getElementById(this.id + '-autocomplete-list');
-      if (x) x = x.getElementsByTagName('div');
-      if (e.keyCode == 40) {
-        // key down
-        currentFocus += 1;
-        // and and make the current item more visible:
-        setActive(x);
-      } else if (e.keyCode == 38) {
-        // key up
-        currentFocus -= 1;
-        // and and make the current item more visible:
-        setActive(x);
-      } else if (e.keyCode == 13) {
-        // If the ENTER key is pressed, prevent the form from being submitted,
-        e.preventDefault();
-        if (currentFocus > -1) {
-          // and simulate a click on the 'active' item:
-          if (x) x[currentFocus].click();
-        }
-      }
-  };
-
-  input.onfocus = function() {
-    onend(input);
-  }
-
-  // focus lost
-  input.onblur = function() {
-    onend(input);
-  }
-
-  function setActive(x) {
-    // a function to classify an item as 'active':
-    if (!x) 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');
-    }
-    if (currentFocus >= x.length) currentFocus = 0;
-    if (currentFocus < 0) currentFocus = (x.length - 1);
-    // add class 'autocomplete-active':
-    x[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]);
-      }
-    }
-  }
-
-  // execute a function when someone clicks in the document:
-  document.addEventListener('click', e => {
-      closeAllLists(e.target);
-  });
-}
-
-// for attended sysupgrade
-function updatePackageList(version, target) {
-  // set available packages
-  fetch(config.asu_url + '/' + config.versions[version] + '/' + target +  '/index.json')
-  .then(response => response.json())
-  .then(all_packages => {
-    setupAutocompleteList($('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(/^\-/, ''));
-          return (i === index) || (i < 0);
-        })
-        // limit to available packages, ignore minus
-        .filter((value, index) => all_packages.indexOf(value.replace(/^\-/, '')) !== -1)
-        .join(' ');
-    });
-  });
-}
-
-function updateImages(version, code, date, model, url, mobj, is_custom) {
-  // add download button for image
-  function addLink(type, file) {
-    var 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');
-    span.appendChild(document.createTextNode(''));
-    a.appendChild(span);
-    a.appendChild(document.createTextNode(type.toUpperCase()));
-
-    if (config.showHelp) {
-      a.onmouseover = function() {
-        // hide all help texts
-        Array.from(document.getElementsByClassName('download-help'))
-          .forEach(e => e.style.display = 'none');
-        var lc = type.toLowerCase();
-        if (lc.includes('sysupgrade')) {
-          show('sysupgrade-help');
-        } else if (lc.includes('factory') || lc == 'trx' || lc == 'chk') {
-          show('factory-help');
-        } else if (lc.includes('kernel') || lc.includes('zimage') || lc.includes('uimage')) {
-          show('kernel-help');
-        } else if (lc.includes('root')) {
-          show('rootfs-help');
-        } else if (lc.includes('sdcard')) {
-          show('sdcard-help');
-        } else if (lc.includes('tftp')) {
-          show('tftp-help');
-        } else {
-          show('other-help');
-        }
-      };
-    }
-
-    $('download-links').appendChild(a);
-  }
-
-  function switchClass(id, from_class, to_class) {
-    $(id).classList.remove(from_class);
-    $(id).classList.add(to_class);
-  }
-
-  // remove all download links
-  Array.from(document.getElementsByClassName('download-link'))
-    .forEach(e => e.remove());
-
-  // hide all help texts
-  Array.from(document.getElementsByClassName('download-help'))
-    .forEach(e => e.style.display = 'none');
-
-  if (version && code && date && model && url && mobj) {
-    var target = mobj.target;
-    var images = mobj.images;
-
-    // change between "version" and "custom" title
-    if (is_custom) {
-      switchClass('images-title', 'tr-version-build', 'tr-custom-build');
-      switchClass('downloads-title', 'tr-version-downloads', 'tr-custom-downloads');
-    } else {
-      switchClass('images-title', 'tr-custom-build', 'tr-version-build');
-      switchClass('downloads-title', 'tr-custom-downloads', 'tr-version-downloads');
-    }
-    // update title translation
-    translate();
-
-    // fill out build info
-    $('image-model').innerText = model;
-    $('image-target').innerText = target;
-    $('image-version').innerText = version;
-    $('image-code').innerText = code;
-    $('image-date').innerText = date;
-
-    images.sort((a, b) => a.name.localeCompare(b.name));
-
-    for (var i in images) {
-      addLink(images[i].type, images[i].name);
-    }
-
-    if (config.asu_url) {
-      updatePackageList(version, target);
-    }
-
-    show('images');
-  } else {
-    hide('images');
-  }
-}
-
-function init() {
-  var build_date = "unknown"
-  setupSelectList($('versions'), Object.keys(config.versions), version => {
-    var 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');
-      return obj.json();
-    })
-    .then(obj => {
-      // handle native openwrt json format
-      if ('profiles' in obj) {
-        obj['models'] = {}
-        for (const [key, value] of Object.entries(obj['profiles'])) {
-          obj['models'][get_model_titles(value.titles)] = value
-          obj['models'][get_model_titles(value.titles)]['id'] = key
-        }
-      }
-      return obj
-    })
-    .then(obj => {
-      setupAutocompleteList($('models'), Object.keys(obj['models']), false, updateImages, models => {
-        var model = models.value;
-        if (model in obj['models']) {
-          var url = obj.url || 'unknown';
-          var code = obj.version_code || 'unknown';
-          var mobj = obj['models'][model];
-          updateImages(version, code, build_date, model, url, mobj, false);
-          current_model = mobj;
-        } else {
-          updateImages();
-          current_model = {};
-        }
-      });
-
-      // trigger model update when selected version changes
-      $('models').onfocus();
-    });
-  });
-
-  if (config.asu_url) {
-    show('custom');
-  }
-
-  // hide fields
-  updateImages();
-
-  var user_lang = (navigator.language || navigator.userLanguage).split('-')[0];
-  if (user_lang in translations) {
-    config.language = user_lang;
-    $('language-selection').value = user_lang;
-  }
-
-  translate();
-
-  $('language-selection').onclick = function() {
-    config.language = this.children[this.selectedIndex].value;
-    translate();
-  }
-}
diff --git a/logo.png b/logo.png
deleted file mode 100644 (file)
index 8500f9b..0000000
Binary files a/logo.png and /dev/null differ
diff --git a/spinner.gif b/spinner.gif
deleted file mode 100644 (file)
index 7154314..0000000
Binary files a/spinner.gif and /dev/null differ
diff --git a/www/config.js b/www/config.js
new file mode 100644 (file)
index 0000000..78adfb0
--- /dev/null
@@ -0,0 +1,15 @@
+var config = {
+  // Default language, see i18n.js
+  language: 'en',
+  // Show help text for images
+  showHelp: true,
+  // Image overview file or path to the ASU API
+  versions: {
+    'SNAPSHOT': '../misc/snapshot/overview.json',
+    '19.07.1': '../misc/19.07.1/overview.json',
+    '18.06.7': '../misc/18.06.7/overview.json'
+  },
+  // Build custom images
+  // See https://github.com/aparcar/asu
+  //asu_url: 'https://chef.libremesh.org'
+};
diff --git a/www/i18n.js b/www/i18n.js
new file mode 100644 (file)
index 0000000..56333bc
--- /dev/null
@@ -0,0 +1,240 @@
+
+var translations = {
+       'ca': {
+               'tr-load': 'Descarregueu el microprogramari OpenWrt per al vostre dispositiu!',
+               'tr-title': 'Selector de microprogramari OpenWrt',
+               'tr-message': 'Feu servir el formulari de sota per seleccionar i descarregar el microprogramari per al vostre dispositiu!',
+               'tr-version-build': 'Compilació',
+               'tr-custom-build': 'Compilació personalitzada',
+               'tr-customize': 'Personalitzar',
+               'tr-request-build': 'Demanar la compilació',
+               'tr-model': 'Model:',
+               'tr-target': 'Plataforma:',
+               'tr-version': 'Versió:',
+               'tr-date': 'Data:',
+               'tr-downloads': 'Descàrregues',
+               'tr-custom-downloads': 'Descàrregues personalitzades',
+               'tr-factory-help': 'Les imatges Factory són per instal·lar OpenWrt als dispositius per primera vegada. Usualment, això es fa a través de la interfície web del microprogramari original.',
+               'tr-sysupgrade-help': 'Les imatges Sysupgrade són per instal·lar-les als dispositius que ja tenen OpenWrt. La imatge es pot instal·lar a través de la interfície web o del terminal.',
+               'tr-kernel-help': 'El nucli de Linux en una imatge separada.',
+               'tr-rootfs-help': 'El sistema de fitxers arrel en una imatge separada.',
+               'tr-sdcard-help': 'Una imatge feta per escriure-la a una targeta SD.',
+               'tr-tftp-help': 'Les imatges TFTP images es fan servir per instal·lar-les a un dispositiu mitjançant el mètode TFTP del carregador d\'arrencada.',
+               'tr-other-help': 'Un altre tipus d\'imatge.',
+               'tr-build-successful': 'La compilació ha tingut èxit',
+               'tr-build-failed': 'La compilació ha fallat',
+               'tr-request-image': 'Demanar la imatge',
+               'tr-check-again': 'Proveu de nou d\'aquí 5 segons...'
+       },
+       'en': {
+               'tr-load': 'Download OpenWrt firmware for your device!',
+               'tr-title': 'OpenWrt Firmware Selector',
+               'tr-message': 'Please use the input below to download firmware for your device!',
+               'tr-version-build': 'Build',
+               'tr-custom-build': 'Custom Build',
+               'tr-customize': 'Customize',
+               'tr-request-build': 'Request Build',
+               'tr-model': 'Model:',
+               'tr-target': 'Platform:',
+               'tr-version': 'Version:',
+               'tr-date': 'Date:',
+               'tr-downloads': 'Downloads',
+               'tr-custom-downloads': 'Custom Downloads',
+               'tr-factory-help': 'Factory images are for flashing routers with OpenWrt for the first time. Usually via the web interface of the original firmware.',
+               'tr-sysupgrade-help': 'Sysupgrade images are for flashing routers that already run OpenWrt. The image can be applied using the web interface or the terminal.',
+               'tr-kernel-help': 'Linux kernel as a separate image.',
+               'tr-rootfs-help': 'Root file system as a separate image.',
+               'tr-sdcard-help': 'Image that is meant to be flashed onto a SD-Card.',
+               'tr-tftp-help': 'TFTP images are used to flash a device via the TFTP method of the bootloader.',
+               'tr-other-help': 'Other image type.',
+               'tr-build-successful': 'Build successful',
+               'tr-build-failed': 'Build failed',
+               'tr-request-image': 'Request image',
+               'tr-check-again': 'Check again in 5 seconds...'
+       },
+       'es': {
+               'tr-load': 'Descargue el firmware OpenWrt para su dispositivo!',
+               'tr-title': 'Selector de firmware OpenWrt',
+               'tr-message': 'Utilice la entrada a continuación para descargar el firmware de su dispositivo!',
+               'tr-version-build': 'Compilar',
+               'tr-custom-build': 'Compilación personalizada',
+               'tr-customize': 'Personalizar',
+               'tr-request-build': 'Solicitar compilación',
+               'tr-model': 'Modelo:',
+               'tr-target': 'Plataforma:',
+               'tr-version': 'Versión:',
+               'tr-date': 'Fecha:',
+               'tr-downloads': 'Descargas',
+               'tr-custom-downloads': 'Descargas personalizadas',
+               'tr-factory-help': 'Las imágenes de fábrica son para enrutadores intermitentes con OpenWrt por primera vez. Generalmente a través de la interfaz web del firmware original.',
+               'tr-sysupgrade-help': 'Las imágenes de Sysupgrade son para enrutadores intermitentes que ya ejecutan OpenWrt. La imagen se puede aplicar utilizando la interfaz web o el terminal.',
+               'tr-kernel-help': 'Kernel de Linux como una imagen separada.',
+               'tr-rootfs-help': 'Sistema de archivos raíz como una imagen separada.',
+               'tr-sdcard-help': 'Imagen que debe ser mostrada en una tarjeta SD.',
+               'tr-tftp-help': 'Las imágenes TFTP se utilizan para flashear un dispositivo mediante el método TFTP del gestor de arranque.',
+               'tr-other-help': 'Otro tipo de imagen.',
+               'tr-build-successful': 'Compilación exitosa',
+               'tr-build-failed': 'Compilación fallida',
+               'tr-request-image': 'Solicitar imagen',
+               'tr-check-again': 'Verifique nuevamente en 5 segundos...'
+       },
+       'no': {
+               'tr-load': 'Last ned OpenWrt fastvare for din enhet!',
+               'tr-title': 'OpenWrt fastvare utvelger',
+               'tr-message': 'Bruk feltene nedenfor for å laste ned fastvare til enheten din!',
+               'tr-version-build': 'Sammensetning',
+               'tr-custom-build': 'Tilpasset sammensetning',
+               'tr-customize': 'Tilpasse',
+               'tr-request-build': 'Be om sammensetning',
+               'tr-model': 'Modell:',
+               'tr-target': 'Platform:',
+               'tr-version': 'Versjon:',
+               'tr-date': 'Dato:',
+               'tr-downloads': 'Nedlastninger',
+               'tr-custom-downloads': 'Tilpassede nedlastninger',
+               'tr-factory-help': 'Factory avbildningen er for å laste rutere med OpenWrt første gang. Vanligvis via webgrensesnittet til den originale fastvaren.',
+               'tr-sysupgrade-help': 'Sysupgrade avbildningen er for rutere som allerede benytter OpenWrt. Avbildningen innstaleres gjennom webgrensesnittet eller terminalen.',
+               'tr-kernel-help': 'Linux kjernen som en egen avbildning.',
+               'tr-rootfs-help': 'Rotfilsystem som en egen avbildning.',
+               'tr-sdcard-help': 'Avbildning som er ment for et SD-kort.',
+               'tr-tftp-help': 'TFTP avbildninger er for å laste enheter via TFTP metoden i oppstartsprosedyren.',
+               'tr-other-help': 'Andre avbildningstyper.',
+               'tr-build-successful': 'Vellykket sammensetning',
+               'tr-build-failed': 'Sammensetningen feilet',
+               'tr-request-image': 'Be om avbildning',
+               'tr-check-again': 'Sjekk pånytt om 5 sekunder...'
+       },
+       'de': {
+               'tr-load': 'Lade die OpenWrt Firmware für dein Gerät!',
+               'tr-title': 'OpenWrt Firmware Selector',
+               'tr-message': 'Bitte benutze die Eingabe um die passende Firmware zu finden!',
+               'tr-version-build': 'Release Build',
+               'tr-custom-build': 'Custom Build',
+               'tr-customize': 'Customize',
+               'tr-request-build': 'Request Build',
+               'tr-model': 'Model:',
+               'tr-target': 'Target',
+               'tr-version': 'Version:',
+               'tr-date': 'Datum:',
+               'tr-downloads': 'Downloads',
+               'tr-custom-downloads': 'Custom Downloads',
+               'tr-factory-help': 'Factory Abbilder werden über die Weboberfläche der originalen Firmware eingespielt.',
+               'tr-sysupgrade-help': 'Sysupgrade Abbilder werden für Geräte verwendet, die bereits OpenWrt laufen haben. Es ist möglich, existierende Einstellungen beizubehalten.',
+               'tr-kernel-help': 'Linux Kernel als separates Abbild.',
+               'tr-rootfs-help': 'Das Root Dateisystem als separates Abbild.',
+               'tr-sdcard-help': 'Image für SD Speicherkarten.',
+               'tr-tftp-help': 'TFTP Dateien können verwendet werden, um ein Gerät über die TFTP Method des Bootloader zu flashen.',
+               'tr-other-help': 'Sonstiger Imagetyp.',
+               'tr-build-successful': 'Build erfolgreich',
+               'tr-build-failed': 'Build fehlgeschlagen',
+               'tr-request-image': 'Frage nach image',
+               'tr-check-again': 'Nochmal nachfragen in 5 Sekunden...'
+       },
+       'fr': {
+               'tr-load': 'Télécharger le firmware OpenWrt de votre périphérique !',
+               'tr-title': 'Sélecteur de Firmware',
+               'tr-message': 'Utiliser les entrées ci-dessous pour télécharger le firmware de votre périphérique !',
+               'tr-version-build': 'Build',
+               'tr-custom-build': 'Build Personnalisé',
+               'tr-customize': 'Personnalisation',
+               'tr-request-build': 'Requête de Build',
+               'tr-model': 'Modèle:',
+               'tr-target': 'Platform:',
+               'tr-version': 'Version:',
+               'tr-date': 'Date:',
+               'tr-downloads': 'Téléchargements',
+               'tr-custom-downloads': 'Téléchargements Personnalusés',
+               'tr-factory-help': 'Les images Factory sont prévues pour flasher les routers avec OpenWrt pour la première fois. Habituellement à partir de l\'interface web du firmware d\'origine.',
+               'tr-sysupgrade-help': 'Les images Sysupgrade sont prévues pour flasher les routers fonctionnant déjà avec OpenWrt. L\'image peut être installée à travers l\'interface web ou par le terminal.',
+               'tr-kernel-help': 'Linux kernel comme image séparée.',
+               'tr-rootfs-help': 'Root file system comme image séparée.',
+               'tr-sdcard-help': 'Image prévue pour être flashée sur une carte SD.',
+               'tr-tftp-help': 'TFTP images prévues pour flasher le périphérique via le démarrage par méthode TFTP.',
+               'tr-other-help': 'Autre type d\'image.',
+               'tr-build-successful': 'Succès du Build',
+               'tr-build-failed': 'Échec du Build',
+               'tr-request-image': 'Demade d\'image',
+               'tr-check-again': 'Essayer à nouveau dans 5 secondes...'
+       },
+       'it': {
+               'tr-load': 'Scarica il firmware OpenWrt per il tuo dispositivo!',
+               'tr-title': 'OpenWrt Firmware Selector',
+               'tr-message': 'Usa la casella sottostante per scaricare il firmware per il tuo dispositivo!',
+               'tr-version-build': 'Build',
+               'tr-custom-build': 'Custom Build',
+               'tr-customize': 'Personalizza',
+               'tr-request-build': 'Richiedi Build',
+               'tr-model': 'Modell:',
+               'tr-target': 'Platform:',
+               'tr-version': 'Version:',
+               'tr-date': 'Data:',
+               'tr-downloads': 'Downloads',
+               'tr-custom-downloads': 'Download Personalizzati',
+               'tr-factory-help': 'Factory Image sono usate per installare OpenWrt su router per la prima volta. Di solito l\'immagine può essere applicata via l\'interfaccia web del firmware originale.',
+               'tr-sysupgrade-help': 'Sysupgrade Image sono usate per flashare router in cui OpenWrt è già installato. L\'immagine può essere applicata via interfaccia web o terminale.',
+               'tr-kernel-help': 'Linux kernel come immagine separata.',
+               'tr-rootfs-help': 'Root file system come immagine separata.',
+               'tr-sdcard-help': 'Immagine da flashare su scheda SD-Card separata.',
+               'tr-tftp-help': 'Immagini TFTP images sono usate per flashare un dispositivo con il metodo TFTP del bootloader.',
+               'tr-other-help': 'Other image type.',
+               'tr-build-successful': 'Build compilata con successo',
+               'tr-build-failed': 'Build fallita',
+               'tr-request-image': 'Richiedi immagine',
+               'tr-check-again': 'Prova di nuovo in 5 secondi...'
+       },
+       'pl': {
+               'tr-load': 'Pobieranie oprogramowania OpenWrt!',
+               'tr-title': 'OpenWrt Firmware Selector',
+               'tr-message': 'Użyj pola poniżej żeby znaleźć obraz dla swojego urządzenia!',
+               'tr-version-build': 'Informacje o obrazie',
+               'tr-custom-build': 'Informacje o zmodyfikowanym obrazie',
+               'tr-customize': 'Modyfikacja',
+               'tr-request-build': 'Żądanie budowy obrazu',
+               'tr-model': 'Model:',
+               'tr-target': 'Platforma:',
+               'tr-version': 'Wersja:',
+               'tr-date': 'Data:',
+               'tr-downloads': 'Obrazy do pobrania',
+               'tr-custom-downloads': 'Zmodyfikowane obrazy do pobrania',
+               'tr-factory-help': 'Obraz factory używany jest do pierwszej instalacji OpenWrt. Zwykle można go użyć wykorzystując interfejs graficzny oryginalnego oprogramowania.',
+               'tr-sysupgrade-help': 'Obraz sysuprade używany jest do aktualizacji routerów z zainstalowanym już OpenWrt. Obraz można użyć przez GUI lub konsolę.',
+               'tr-kernel-help': 'Osobny obraz z kernelem linuksowym.',
+               'tr-rootfs-help': 'Osobny obraz z system plików.',
+               'tr-sdcard-help': 'Obraz do wgrania na kartę SD.',
+               'tr-tftp-help': 'Obraz TFTP służący do aktualizacji urządzenia z wykorzystaniem TFTP i bootloadera.',
+               'tr-other-help': 'Inny typ obrazu.',
+               'tr-build-successful': 'Budowanie zakończone pomyślnie',
+               'tr-build-failed': 'Błąd budowania',
+               'tr-request-image': 'Żądanie obrazu',
+               'tr-check-again': 'Sprawdź ponownie za 5 sekund...'
+       },
+       'tr': {
+               'tr-load': 'Cihazınız için OpenWrt yazılımını indirin!',
+               'tr-title': 'OpenWrt Yazılım Seçicisi',
+               'tr-message': 'Cihazınız için yazılımı indirmek için lütfen aşağıdaki girişi kullanın!',
+               'tr-version-build': 'Sürüm',
+               'tr-custom-build': 'Özel Sürüm',
+               'tr-customize': 'Özelleştir',
+               'tr-request-build': 'Sürüm Oluştur',
+               'tr-model': 'Model:',
+               'tr-target': 'Platform:',
+               'tr-version': 'Versiyon:',
+               'tr-date': 'Tarih:',
+               'tr-downloads': 'İndirmeler',
+               'tr-custom-downloads': 'Özel İndirmeler',
+               'tr-factory-help': 'Fabrika imajları, ilk kez OpenWrt yüklenen cihazlar içindir. Genellikle orijinal ürün yazılımının web arayüzü üzerinden yüklenir.',
+               'tr-sysupgrade-help': 'Sysupgrade imajları, zaten OpenWrt kurulu cihazlar içindir. İmaj, web arayüzü veya terminal kullanılarak yüklenebilir.',
+               'tr-kernel-help': 'Linux kernel ayrı bir imaj olarak.',
+               'tr-rootfs-help': 'Kök Dosya Sistemi ayrı bir imaj olarak.',
+               'tr-sdcard-help': 'SD-Kart \'a kurulması planlanan imaj',
+               'tr-tftp-help': 'TFTP imajları, Bootloader \'ın TFTP yöntemi ile bir cihaza kurulması için kullanılır.',
+               'tr-other-help': 'Diğer imaj türü.',
+               'tr-build-successful': 'Oluşturma başarılı',
+               'tr-build-failed': 'Oluşturma başarısız',
+               'tr-request-image': 'Imaj oluştur',
+               'tr-check-again': '5 saniye icinde tekrar dene...'
+       }
+};
+
+// Complement translations based on other translations
+//translations['en'] = Object.assign({}, translations['de'], translations['en']);
diff --git a/www/index.css b/www/index.css
new file mode 100644 (file)
index 0000000..e0e1885
--- /dev/null
@@ -0,0 +1,231 @@
+
+body {
+  font-family: "Arial", sans-serif;
+  margin: 0px;
+}
+
+#buildstatus a {
+  color: inherit;
+}
+
+#models-autocomplete {
+  width: 20em;
+  display: inline-block;
+}
+
+.autocomplete {
+  position: relative;
+}
+
+.autocomplete > input {
+  border: 1px solid transparent;
+  background-color: #f1f1f1;
+  padding: 10px;
+  width: 100%;
+  border-radius: 4px;
+}
+
+.autocomplete-items {
+  position: absolute;
+  border: 1px solid #d4d4d4;
+  border-bottom: none;
+  border-top: none;
+  z-index: 99;
+  /*position the autocomplete items to be the same width as the container:*/
+  top: 100%;
+  left: 0;
+  right: 0;
+}
+
+.autocomplete-items div {
+  padding: 10px;
+  cursor: pointer;
+  background-color: #fff; 
+  border-bottom: 1px solid #d4d4d4; 
+}
+
+/*when hovering an item:*/
+.autocomplete-items div:hover {
+  background-color: #e9e9e9; 
+}
+
+/*when navigating through the items using the arrow keys:*/
+.autocomplete-active {
+  background-color: DodgerBlue !important; 
+  color: #ffffff; 
+}
+
+header {
+  font-weight: 500;
+  width: 100%;
+  z-index: 1100;
+  box-shadow: 0px 2px 4px -1px rgba(0,0,0,0.2),0px 4px 5px 0px rgba(0,0,0,0.14),0px 1px 10px 0px rgba(0,0,0,0.12);
+  background-color: #00A3E1;
+}
+
+h6 {
+  margin: 0;
+  font-size: 1.25rem;
+  font-weight: 500;
+  line-height: 1.6;
+  letter-spacing: 0.0075em;
+}
+
+header > div {
+  padding-left: 24px;
+  padding-right: 24px;
+  min-height: 64px;
+
+  display: flex;
+  position: relative;
+  align-items: center;
+  color: #fff;
+}
+
+.container {
+  max-width: 1280px;
+  padding-left: 32px;
+  padding-right: 32px;
+  width: 100%;
+  box-sizing: border-box;
+  margin-top: 30px;
+  margin-right: auto;
+  margin-left: auto;
+  margin-bottom: 100px;
+}
+
+.container > div {
+  padding: 10px 20px;
+  text-align: left;
+  box-shadow: 0px 1px 3px 0px rgba(0,0,0,0.2),0px 1px 1px 0px rgba(0,0,0,0.14),0px 2px 1px -1px rgba(0,0,0,0.12);
+  border-radius: 4px;
+  color: rgba(0, 0, 0, 0.87);
+  transition: box-shadow 300ms cubic-bezier(0.4, 0, 0.2, 1) 0ms;
+  background-color: #fff;
+}
+
+#versions {
+  border: 1px solid transparent;
+  background-color: #f1f1f1;
+  padding: 10px;
+  width: 10em;
+  border-radius: 4px;
+}
+
+#language-selection {
+  color: #fff;
+  background-color: #1084b2;
+  box-shadow: 0px 1px 5px 0px rgba(0,0,0,0.2),0px 2px 2px 0px rgba(0,0,0,0.14),0px 3px 1px -2px
+  rgba(0,0,0,0.12);
+  padding: 6px 16px;
+  border-radius: 4px;
+  border: 0;
+
+  /* hide arrow */
+  -webkit-appearance: none;
+  -moz-appearance: none;
+  text-indent: 1px;
+  text-overflow: '';
+}
+
+.download-link {
+  text-decoration: none;
+  border-radius: 4px;
+  padding: 12px 16px;
+  margin: 5px;
+  font-size: 16px;
+  cursor: pointer;
+  letter-spacing: 0.05em;
+  display: inline-flex;
+  align-items: center;
+  box-shadow: 0px 1px 5px 0px rgba(0,0,0,0.2),0px 2px 2px 0px rgba(0,0,0,0.14),0px 3px 1px -2px rgba(0,0,0,0.12);
+  color: #fff;
+  background-color: #00A3E1;
+}
+
+.download-link:hover {
+  background-color: #038fc6
+}
+
+.download-link :first-child {
+  width: 30px;
+  margin-right: 15px;
+  margin-top: -2px;
+  content: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath d='M19.35 10.04C18.67 6.59 15.64 4 12 4 9.11 4 6.6 5.64 5.35 8.04 2.34 8.36 0 10.91 0 14c0 3.31 2.69 6 6 6h13c2.76 0 5-2.24 5-5 0-2.64-2.05-4.78-4.65-4.96zM17 13l-5 5-5-5h3V9h4v4h3z' fill='%23fff'%3E%3C/path%3E%3Cpath d='M0 0h24v24H0z' fill='none'/%3E%3C/svg%3E");
+}
+
+#images .column {
+  width: 5em;
+  display: inline-block;
+  line-height: 1.5;
+}
+
+#images {
+  display: none;
+}
+
+#images > div {
+  padding-top: 20px;
+}
+
+#image-model {
+  font-weight: bold;
+}
+
+#custom {
+  display: none;
+}
+
+#custom textarea {
+  width: 500px;
+  height: 120px;
+  font-size: 16px;
+  display: block;
+}
+
+#custom a :first-child {
+  width: 30px;
+  margin-right: 10px;
+  margin-top: 0px;
+  font-size: 36px;
+}
+
+#custom a {
+  text-decoration: none;
+  border-radius: 4px;
+  padding: 2px 10px;
+  margin: 5px;
+  font-size: 16px;
+  cursor: pointer;
+  letter-spacing: 0.05em;
+  display: inline-flex;
+  align-items: center;
+  box-shadow: 0px 1px 5px 0px rgba(0,0,0,0.2),0px 2px 2px 0px rgba(0,0,0,0.14),0px 3px 1px -2px rgba(0,0,0,0.12);
+  color: #fff;
+  background-color: #00A3E1;
+}
+
+.download-help {
+  display: none;
+}
+
+#buildspinner {
+  float: left;
+  height: 40px;
+  padding-right: 12px;
+  display: none;
+}
+
+#buildstatus {
+  padding: 10px 0;
+  display: none;
+}
+
+#footer {
+  font-size: 0.8em;
+  text-align: right;
+}
+
+#footer a {
+  text-decoration: none;
+}
diff --git a/www/index.html b/www/index.html
new file mode 100644 (file)
index 0000000..2260f81
--- /dev/null
@@ -0,0 +1,96 @@
+<!DOCTYPE html>
+
+<html lang="en">
+<head>
+       <meta charset="utf-8"/>
+       <title>OpenWrt Firmware Selector</title>
+       <link rel="stylesheet" href="index.css" />
+       <script src="i18n.js"></script>
+       <script src="config.js"></script>
+       <script src="index.js"></script>
+</head>
+<body onload="init()">
+
+<header>
+       <div>
+<!--
+               <h6 class="tr-title">OpenWrt Firmware Selector</h6>
+-->
+               <img src="logo.png" alt="Logo">
+               <div style="flex-grow: 1;"></div>
+
+               <select id="language-selection" size="1">
+                       <option value="ca">Català</option>
+                       <option value="en">English</option>
+                       <option value="es">Español</option>
+                       <option value="de">Deutsch</option>
+                       <option value="fr">Français</option>
+                       <option value="it">Italiano</option>
+                       <option value="no">Norsk</option>
+                       <option value="pl">Polski</option>
+                       <option value="tr">Türkçe</option>
+               </select>
+       </div>
+</header>
+
+<div class="container">
+       <div>
+               <h2 class="tr-load">Download OpenWrt firmware for your device!</h2>
+               <p class="tr-message">Please use the input below to download firmware for your device!</p>
+               <br>
+
+               <select id="versions" size="1"></select>
+               <div id="models-autocomplete" class="autocomplete">
+                       <input id="models" type="text" placeholder="Model" spellcheck="false" autocapitalize="off" autocorrect="off">
+               </div>
+
+               <br />
+               <br />
+
+               <div>
+                       <img id="buildspinner" src="spinner.gif" alt="Logo">
+                       <div id="buildstatus"></div>
+               </div>
+
+               <div id="images">
+                       <div id="custom">
+                               <h3 class="tr-customize">Customize</h3>
+                               <div class="autocomplete">
+                                       <textarea id="packages" spellcheck="false" autocapitalize="off" autocorrect="off"></textarea>
+                               </div>
+                               <a href="javascript:build_asu_request()" class="custom-link">
+                                       <span>&#9881;</span><span class="tr-request-build">Request Build</span>
+                               </a>
+                       </div>
+
+                       <div>
+                               <h3 id="images-title" class="tr-version-build">Release Build</h3>
+                               <div><span class="column tr-model">Model:</span> <span id="image-model"></span></div>
+                               <div><span class="column tr-target">Target:</span> <span id="image-target"></span></div>
+                               <div><span class="column tr-version">Version:</span> <span id="image-version"></span> (<span id="image-code"></span>)</div>
+                               <div><span class="column tr-date">Date:</span> <span id="image-date"></span></div>
+                       </div>
+
+                       <div id="download-links">
+                               <h3 id="downloads-title" class="tr-downloads">Downloads</h3>
+                       </div>
+
+                       <div>
+                               <span id="factory-help" class="download-help tr-factory-help">Factory images are for flashing routers with OpenWrt for the first time using the web interface of the original firmware.</span>
+                               <span id="sysupgrade-help" class="download-help tr-sysupgrade-help">Sysupgrade images are for flashing routers that already run OpenWrt. The image can be applied using the web interface or the console.</span>
+                               <span id="kernel-help" class="download-help tr-kernel-help">Linux kernel as a separate image.</span>
+                               <span id="rootfs-help" class="download-help tr-rootfs-help">Root file system as a separate image.</span>
+                               <span id="sdcard-help" class="download-help tr-sdcard-help">Image that is meant to be flashed on an SD-Card.</span>
+                               <span id="tftp-help" class="download-help tr-tftp-help">Image that can be applied using the TFTP meachnism of the bootloader</span>
+                               <span id="other-help" class="download-help tr-other-help">Image of unknown purpose.</span>
+                       </div>
+               </div>
+
+               <div id="footer">
+                       <span><a href="https://github.com/mwarning/yet_another_firmware_selector">YAFS</a> v2.2.1</span>
+               </div>
+       </div>
+</div>
+
+</body>
+</html>
diff --git a/www/index.js b/www/index.js
new file mode 100644 (file)
index 0000000..7562da4
--- /dev/null
@@ -0,0 +1,451 @@
+
+var current_model = {};
+
+function $(id) {
+  return document.getElementById(id);
+}
+
+function show(id) {
+  $(id).style.display = 'block';
+}
+
+function hide(id) {
+  $(id).style.display = 'none';
+}
+
+function split(str) {
+  return str.match(/[^\s,]+/g) || [];
+}
+
+function get_model_titles(titles) {
+  return titles.map(e => {
+    if (e.title) {
+      return e.title;
+    } else {
+      return ((e.vendor || '') + ' ' + (e.model || '') + ' ' + (e.variant || '')).trim();
+    }
+  }).join(' / ');
+}
+
+function build_asu_request() {
+  if (!current_model || !current_model.id) {
+    alert('bad profile');
+    return;
+  }
+
+  function showStatus(message, url) {
+    show('buildstatus');
+    var tr = message.startsWith('tr-') ? message : '';
+    if (url) {
+      $('buildstatus').innerHTML = '<a href="' + url + '" class="' + tr + '">' + message + '</a>';
+    } else {
+      $('buildstatus').innerHTML = '<span class="' + tr + '"></span>';
+    }
+    translate();
+  }
+
+  // hide image view
+  updateImages();
+
+  show('buildspinner');
+  showStatus('tr-request-image');
+
+  var request_data = {
+    'target': current_model.target,
+    'profile': current_model.id,
+    'packages': split($('packages').value),
+    'version': $('versions').value
+  }
+
+  fetch(config.asu_url + '/api/build', {
+     method: 'POST',
+     headers: { 'Content-Type': 'application/json' },
+     body: JSON.stringify(request_data)
+  })
+  .then(response => {
+    switch (response.status) {
+      case 200:
+        hide('buildspinner');
+        showStatus('tr-build-successful');
+
+        response.json()
+        .then(mobj => {
+          var download_url = config.asu_url + '/store/' + mobj.bin_dir;
+          showStatus('tr-build-successful', download_url + '/buildlog.txt');
+          updateImages(
+            mobj.version_number,
+            mobj.version_code,
+            mobj.build_at,
+            get_model_titles(mobj.titles),
+            download_url, mobj, true
+          );
+        });
+        break;
+      case 202:
+        showStatus('tr-check-again');
+        setTimeout(_ => { build_asu_request() }, 5000);
+        break;
+      case 400: // bad request
+      case 422: // bad package
+      case 500: // build failed
+        hide('buildspinner');
+        response.json()
+        .then(mobj => {
+          var message = mobj['message'] || 'tr-build-failed';
+          var url = mobj.buildlog ? (config.asu_url + '/store/' + mobj.bin_dir + '/buildlog.txt') : undefined;
+          showStatus(message, url);
+        })
+        break;
+    }
+  })
+  .catch(err => {
+    hide('buildspinner');
+    showStatus(err);
+  })
+}
+
+function setupSelectList(select, items, onselection) {
+  for (var i = 0; i < items.length; i += 1) {
+    var option = document.createElement('OPTION');
+    option.innerHTML = items[i];
+    select.appendChild(option);
+  }
+
+  select.addEventListener('change', e => {
+    onselection(items[select.selectedIndex]);
+  });
+
+  if (select.selectedIndex >= 0) {
+    onselection(items[select.selectedIndex]);
+  }
+}
+
+// Change the translation of the entire document
+function translate() {
+  var mapping = translations[config.language];
+  for (var tr in mapping) {
+    Array.from(document.getElementsByClassName(tr))
+      .forEach(e => { e.innerText = mapping[tr]; })
+  }
+}
+
+function setupAutocompleteList(input, items, as_list, onbegin, onend) {
+  var currentFocus = -1;
+
+  // sort numbers and other characters separately
+  var collator = new Intl.Collator(undefined, {numeric: true, sensitivity: 'base'});
+
+  items.sort(collator.compare);
+
+  input.oninput = function(e) {
+    onbegin();
+
+    var offset = 0;
+    var value = this.value;
+    var value_list = [];
+
+    if (as_list) {
+      // automcomplete last text item
+      offset = this.value.lastIndexOf(' ') + 1;
+      value = this.value.substr(offset);
+      value_list = split(this.value.substr(0, offset));
+    }
+
+    // close any already open lists of autocompleted values
+    closeAllLists();
+
+    if (!value) {
+      return false;
+    }
+
+    // create a DIV element that will contain the items (values):
+    var 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:
+    this.parentNode.appendChild(list);
+
+    var c = 0;
+    for (var i = 0; i < items.length; i += 1) {
+      var item = items[i];
+
+      // match
+      var j = item.toUpperCase().indexOf(value.toUpperCase());
+      if (j < 0) {
+        continue;
+      }
+
+      // do not offer a duplicate item
+      if (as_list && value_list.indexOf(item) != -1) {
+        continue;
+      }
+
+      c += 1;
+      if (c >= 15) {
+        var div = document.createElement('DIV');
+        div.innerHTML = '...';
+        list.appendChild(div);
+        break;
+      } else {
+        var div = document.createElement('DIV');
+        // make the matching letters bold:
+        div.innerHTML = item.substr(0, j)
+          + '<strong>' + item.substr(j, value.length) + '</strong>'
+          + item.substr(j + value.length)
+          + '<input type="hidden" value="' + item + '">';
+
+        div.addEventListener('click', function(e) {
+          // include selected value
+          var selected = this.getElementsByTagName('input')[0].value;
+          if (as_list) {
+            input.value = value_list.join(' ') + ' ' + selected;
+          } else {
+            input.value = selected;
+          }
+          // close the list of autocompleted values,
+          closeAllLists();
+          onend(input);
+        });
+
+        list.appendChild(div);
+      }
+    }
+  };
+
+  input.onkeydown = function(e) {
+      var x = document.getElementById(this.id + '-autocomplete-list');
+      if (x) x = x.getElementsByTagName('div');
+      if (e.keyCode == 40) {
+        // key down
+        currentFocus += 1;
+        // and and make the current item more visible:
+        setActive(x);
+      } else if (e.keyCode == 38) {
+        // key up
+        currentFocus -= 1;
+        // and and make the current item more visible:
+        setActive(x);
+      } else if (e.keyCode == 13) {
+        // If the ENTER key is pressed, prevent the form from being submitted,
+        e.preventDefault();
+        if (currentFocus > -1) {
+          // and simulate a click on the 'active' item:
+          if (x) x[currentFocus].click();
+        }
+      }
+  };
+
+  input.onfocus = function() {
+    onend(input);
+  }
+
+  // focus lost
+  input.onblur = function() {
+    onend(input);
+  }
+
+  function setActive(x) {
+    // a function to classify an item as 'active':
+    if (!x) 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');
+    }
+    if (currentFocus >= x.length) currentFocus = 0;
+    if (currentFocus < 0) currentFocus = (x.length - 1);
+    // add class 'autocomplete-active':
+    x[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]);
+      }
+    }
+  }
+
+  // execute a function when someone clicks in the document:
+  document.addEventListener('click', e => {
+      closeAllLists(e.target);
+  });
+}
+
+// for attended sysupgrade
+function updatePackageList(version, target) {
+  // set available packages
+  fetch(config.asu_url + '/' + config.versions[version] + '/' + target +  '/index.json')
+  .then(response => response.json())
+  .then(all_packages => {
+    setupAutocompleteList($('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(/^\-/, ''));
+          return (i === index) || (i < 0);
+        })
+        // limit to available packages, ignore minus
+        .filter((value, index) => all_packages.indexOf(value.replace(/^\-/, '')) !== -1)
+        .join(' ');
+    });
+  });
+}
+
+function updateImages(version, code, date, model, url, mobj, is_custom) {
+  // add download button for image
+  function addLink(type, file) {
+    var 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');
+    span.appendChild(document.createTextNode(''));
+    a.appendChild(span);
+    a.appendChild(document.createTextNode(type.toUpperCase()));
+
+    if (config.showHelp) {
+      a.onmouseover = function() {
+        // hide all help texts
+        Array.from(document.getElementsByClassName('download-help'))
+          .forEach(e => e.style.display = 'none');
+        var lc = type.toLowerCase();
+        if (lc.includes('sysupgrade')) {
+          show('sysupgrade-help');
+        } else if (lc.includes('factory') || lc == 'trx' || lc == 'chk') {
+          show('factory-help');
+        } else if (lc.includes('kernel') || lc.includes('zimage') || lc.includes('uimage')) {
+          show('kernel-help');
+        } else if (lc.includes('root')) {
+          show('rootfs-help');
+        } else if (lc.includes('sdcard')) {
+          show('sdcard-help');
+        } else if (lc.includes('tftp')) {
+          show('tftp-help');
+        } else {
+          show('other-help');
+        }
+      };
+    }
+
+    $('download-links').appendChild(a);
+  }
+
+  function switchClass(id, from_class, to_class) {
+    $(id).classList.remove(from_class);
+    $(id).classList.add(to_class);
+  }
+
+  // remove all download links
+  Array.from(document.getElementsByClassName('download-link'))
+    .forEach(e => e.remove());
+
+  // hide all help texts
+  Array.from(document.getElementsByClassName('download-help'))
+    .forEach(e => e.style.display = 'none');
+
+  if (version && code && date && model && url && mobj) {
+    var target = mobj.target;
+    var images = mobj.images;
+
+    // change between "version" and "custom" title
+    if (is_custom) {
+      switchClass('images-title', 'tr-version-build', 'tr-custom-build');
+      switchClass('downloads-title', 'tr-version-downloads', 'tr-custom-downloads');
+    } else {
+      switchClass('images-title', 'tr-custom-build', 'tr-version-build');
+      switchClass('downloads-title', 'tr-custom-downloads', 'tr-version-downloads');
+    }
+    // update title translation
+    translate();
+
+    // fill out build info
+    $('image-model').innerText = model;
+    $('image-target').innerText = target;
+    $('image-version').innerText = version;
+    $('image-code').innerText = code;
+    $('image-date').innerText = date;
+
+    images.sort((a, b) => a.name.localeCompare(b.name));
+
+    for (var i in images) {
+      addLink(images[i].type, images[i].name);
+    }
+
+    if (config.asu_url) {
+      updatePackageList(version, target);
+    }
+
+    show('images');
+  } else {
+    hide('images');
+  }
+}
+
+function init() {
+  var build_date = "unknown"
+  setupSelectList($('versions'), Object.keys(config.versions), version => {
+    var 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');
+      return obj.json();
+    })
+    .then(obj => {
+      // handle native openwrt json format
+      if ('profiles' in obj) {
+        obj['models'] = {}
+        for (const [key, value] of Object.entries(obj['profiles'])) {
+          obj['models'][get_model_titles(value.titles)] = value
+          obj['models'][get_model_titles(value.titles)]['id'] = key
+        }
+      }
+      return obj
+    })
+    .then(obj => {
+      setupAutocompleteList($('models'), Object.keys(obj['models']), false, updateImages, models => {
+        var model = models.value;
+        if (model in obj['models']) {
+          var url = obj.url || 'unknown';
+          var code = obj.version_code || 'unknown';
+          var mobj = obj['models'][model];
+          updateImages(version, code, build_date, model, url, mobj, false);
+          current_model = mobj;
+        } else {
+          updateImages();
+          current_model = {};
+        }
+      });
+
+      // trigger model update when selected version changes
+      $('models').onfocus();
+    });
+  });
+
+  if (config.asu_url) {
+    show('custom');
+  }
+
+  // hide fields
+  updateImages();
+
+  var user_lang = (navigator.language || navigator.userLanguage).split('-')[0];
+  if (user_lang in translations) {
+    config.language = user_lang;
+    $('language-selection').value = user_lang;
+  }
+
+  translate();
+
+  $('language-selection').onclick = function() {
+    config.language = this.children[this.selectedIndex].value;
+    translate();
+  }
+}
diff --git a/www/logo.png b/www/logo.png
new file mode 100644 (file)
index 0000000..8500f9b
Binary files /dev/null and b/www/logo.png differ
diff --git a/www/spinner.gif b/www/spinner.gif
new file mode 100644 (file)
index 0000000..7154314
Binary files /dev/null and b/www/spinner.gif differ