function get_model_titles(titles) { var title = [] for (var i in titles) { if (titles[i].title) { title.push(titles[i].title) } else { var title_fragments = [] if (titles[i].vendor) { title_fragments.push(titles[i].vendor) } title_fragments.push(titles[i].model) if (titles[i].variant) { title_fragments.push(titles[i].variant) } title.push(title_fragments.join(" ")) } } return title.join("/") } function build_request(request_data) { console.log("fire") fetch(config.asu_url, { method: "POST", headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(request_data) }) .then(function(response) { switch (response.status) { case 200: console.log("image found"); response.json() .then(function(mobj) { console.log(mobj) updateImages( mobj.version_number, mobj.version_commit, get_model_titles(mobj.titles), mobj.url, mobj) }); break; case 202: // show some spinning animation console.log("check again in 5 seconds"); setTimeout(function() { build_request(request_data) }, 5000); break; case 400: console.log("bad request"); // see message break; case 422: console.log("bad package"); // see message break; case 500: console.log("build failed"); break; } }) } function example_request() { build_request({ "version": "SNAPSHOT", "packages": ["bmon", "vim", "ip-full"], "profile": "tplink_tl-wdr4300-v1" }) } function loadFile(url, callback) { var xmlhttp = new XMLHttpRequest(); xmlhttp.onreadystatechange = function() { if (xmlhttp.readyState == 4 && xmlhttp.status == 200) { callback(JSON.parse(xmlhttp.responseText), url); } }; xmlhttp.open('GET', url, true); xmlhttp.send(); } 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", function(e) { onselection(items[select.selectedIndex]); }); if (select.selectedIndex >= 0) { onselection(items[select.selectedIndex]); } } // Change the translation of the entire document function changeLanguage(language) { var mapping = translations[language]; if (mapping) { for (var tr in mapping) { Array.from(document.getElementsByClassName(tr)) .forEach(function(e) { e.innerText = mapping[tr]; }) } } } function setupAutocompleteList(input, items, onselection) { // the setupAutocompleteList function takes two arguments, // the text field element and an array of possible autocompleted values: var currentFocus = -1; // sort numbers and other characters separately var collator = new Intl.Collator(undefined, {numeric: true, sensitivity: 'base'}); items.sort(collator.compare); // execute a function when someone writes in the text field: input.oninput = function(e) { // clear images updateImages(); var value = this.value; // 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); // for each item in the array... 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; } 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) + "" + item.substr(j, value.length) + "" + item.substr(j + value.length) + ""; div.addEventListener("click", function(e) { // set text field to selected value input.value = this.getElementsByTagName("input")[0].value; // close the list of autocompleted values, // (or any other open lists of autocompleted values: closeAllLists(); // callback onselection(input.value); }); 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() { onselection(input.value); } 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", function (e) { closeAllLists(e.target); }); } function $(id) { return document.getElementById(id); } function findCommonPrefix(images) { var files = images.map(image => image.name) var A = files.concat().sort(); var first = A[0]; var last = A[A.length - 1]; var L = first.length; var i = 0; while (i < L && first.charAt(i) === last.charAt(i)) { i += 1; } return first.substring(0, i); } function updateImages(version, commit, model, url, mobj) { // add download button for image function addLink(label, tags, file, help_id) { var a = document.createElement('A'); a.classList.add('download-link'); a.href = url .replace('{target}', mobj.target) .replace('{release}', version) + '/' + file; var span = document.createElement('SPAN'); span.appendChild(document.createTextNode('')); a.appendChild(span); // add sub label if (tags.length > 0) { a.appendChild(document.createTextNode(label + ' (' + tags.join(', ') + ')')); } else { a.appendChild(document.createTextNode(label)); } if (config.showHelp) { a.onmouseover = function() { // hide all help texts Array.from(document.getElementsByClassName('download-help')) .forEach(function(e) { e.style.display = 'none'; }); $(help_id).style.display = 'block'; }; } $('download-links').appendChild(a); } // remove all download links Array.from(document.getElementsByClassName('download-link')) .forEach(function(e) { e.remove(); }); // hide all help texts Array.from(document.getElementsByClassName('download-help')) .forEach(function(e) { e.style.display = 'none'; }); if (version && commit && model && url && mobj) { var target = mobj.target; var images = mobj.images; // fill out build info $('image-model').innerText = model; $('image-target').innerText = target; $('image-release').innerText = version; $('image-commit').innerText = commit; var prefix = findCommonPrefix(images); var entries = { 'FACTORY': [], 'SYSUPGRADE': [], 'KERNEL': [], 'ROOTFS': [], 'SDCARD': [], 'TFTP': [], 'OTHER': [] }; images.sort(); for (var i in images) { var image = images[i].name; var lc = image.toLowerCase() if (lc.includes('factory')) { entries['FACTORY'].push(image); } else if (lc.includes('sysupgrade')) { entries['SYSUPGRADE'].push(image); } else if (lc.includes('kernel') || lc.includes('zimage') || lc.includes('uimage')) { entries['KERNEL'].push(image); } else if (lc.includes('rootfs')) { entries['ROOTFS'].push(image); } else if (lc.includes('sdcard')) { entries['SDCARD'].push(image); } else if (lc.includes('tftp')) { entries['TFTP'].push(image); } else { entries['OTHER'].push(image); } } function extractTags(prefix, image) { var all = image.substring(prefix.length).split('.')[0].split('-'); var ignore = ['', 'kernel', 'zImage', 'uImage', 'factory', 'sysupgrade', 'rootfs', 'sdcard']; return all.filter(function (el) { return !ignore.includes(el); }); } for (var category in entries) { var images = entries[category]; for (var i in images) { var image = images[i]; var tags = (images.length > 1) ? extractTags(prefix, image) : []; addLink(category, tags, image, category.toLowerCase() + '-help'); } } $('images').style.display = 'block'; } else { $('images').style.display = 'none'; } } // hide fields updateImages(); changeLanguage(config.language); setupSelectList($("releases"), Object.keys(config.versions), function(version) { loadFile(config.versions[version], function(obj) { setupAutocompleteList($("models"), Object.keys(obj['models']), function(model) { if (model in obj['models']) { var url = obj.url; var commit = obj.version_commit; var mobj = obj['models'][model]; updateImages(version, commit, model, url, mobj); } else { updateImages(); } }); // trigger model update when selected version changes $("models").onfocus(); }); });