/** * This file is part of Haketilo. * * Function: Show available repositories and allow querying them for resources. * * Copyright (C) 2022 Wojtek Kosior * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * As additional permission under GNU GPL version 3 section 7, you * may distribute forms of that code without the copy of the GNU * GPL normally required by section 4, provided you include this * license notice and, in case of non-source distribution, a URL * through which recipients can access the Corresponding Source. * If you modify file(s) with this exception, you may extend this * exception to your version of the file(s), but you are not * obligated to do so. If you do not wish to do so, delete this * exception statement from your version. * * As a special exception to the GPL, any HTML file which merely * makes function calls to this code, and for that purpose * includes it by reference shall be deemed a separate work for * copyright law purposes. If you modify this code, you may extend * this exception to your version of the code, but you are not * obligated to do so. If you do not wish to do so, delete this * exception statement from your version. * * You should have received a copy of the GNU General Public License * along with this program. If not, see <https://www.gnu.org/licenses/>. * * I, Wojtek Kosior, thereby promise not to sue for violation of this file's * license. Although I request that you do not make use of this code in a * proprietary program, I am not going to enforce this in court. */ #IMPORT common/indexeddb.js AS haketilodb #FROM common/browser.js IMPORT browser #FROM html/DOM_helpers.js IMPORT clone_template, Showable #FROM common/entities.js IMPORT item_id_string, version_string #FROM html/install.js IMPORT InstallView #FROM common/jsonschema.js IMPORT haketilo_validator, haketilo_schemas #FROM html/repo_query_cacher_client.js IMPORT indirect_fetch const coll = new Intl.Collator(); function ResultEntry(repo_entry, mapping_ref) { Object.assign(this, clone_template("repo_query_single_result")); Object.assign(this, {repo_entry, mapping_ref}); this.mapping_name.innerText = mapping_ref.long_name; this.mapping_id.innerText = item_id_string(mapping_ref); const iv = repo_entry.query_view.install_view; function start_install() { iv.show(repo_entry.repo_url, "mapping", mapping_ref.identifier, mapping_ref.version); } const cb = repo_entry.query_view.install_view.when_hidden(start_install); this.install_but.addEventListener("click", cb); } function RepoEntry(query_view, repo_url) { Object.assign(this, clone_template("repo_query_single_repo")); Object.assign(this, {query_view, repo_url}); this.results_shown_before = false; this.repo_url_label.innerText = repo_url; const encoded_queried_url = encodeURIComponent(query_view.url); const query_url = `${repo_url}query?url=${encoded_queried_url}`; const query_results = async () => { try { var response = await indirect_fetch(query_view.tab_id, query_url); } catch(e) { console.error("Haketilo:", e); throw "Failure to communicate with repository :("; } if (!response.ok) throw `Repository sent HTTP code ${response.status} :(`; try { var json = await response.json(); } catch(e) { console.error("Haketilo:", e); throw "Repository's response is not valid JSON :("; } const $id = `https://hydrilla.koszko.org/schemas/api_query_result-1.0.1.schema.json`; const schema = haketilo_schemas[$id]; const result = haketilo_validator.validate(json, schema); if (result.errors.length > 0) { console.error("Haketilo:", result.errors); const reg = new RegExp(schema.properties.$schema.pattern); if (json.$schema && !reg.test(json.$schema)) throw "Results were served using unsupported Hydrilla API version. You might need to update Haketilo."; throw "Results were served using a nonconforming response format."; } return json.mappings; } const populate_results = async () => { this.results_shown_before = true; try { var results = await query_results(); } catch(e) { this.info_div.innerText = e; return; } this.result_entries = results.map(ref => new ResultEntry(this, ref)); if (this.result_entries.length > 0) { this.results_list.classList.remove("hide"); this.info_div.remove(); const to_append = this.result_entries.map(re => re.main_li); this.results_list.append(...to_append); } else { this.info_div.innerText = "No results :("; } } let show_results = () => { if (!query_view.shown) return; if (!this.results_shown_before) populate_results(); this.list_container.classList.remove("hide"); this.hide_results_but.classList.remove("hide"); this.show_results_but.classList.add("hide"); } show_results = query_view.install_view.when_hidden(show_results); let hide_results = () => { if (!query_view.shown) return; this.list_container.classList.add("hide"); this.hide_results_but.classList.add("hide"); this.show_results_but.classList.remove("hide"); } hide_results = query_view.install_view.when_hidden(hide_results); this.show_results_but.addEventListener("click", show_results); this.hide_results_but.addEventListener("click", hide_results); } const container_ids = ["repos_list_container", "install_view_container"]; function RepoQueryView(tab, on_view_show, on_view_hide) { Showable.call(this, on_view_show, on_view_hide); Object.assign(this, clone_template("repo_query")); this.tab_id = tab.id; #IF MOZILLA this.incognito = tab.incognito; if (this.incognito) { [...this.top_text.childNodes].forEach(n => n.remove()); this.top_text.append( clone_template("repo_query_private_mode_error").main_span ); } #ENDIF const show_container = name => { for (const cid of container_ids) { if (cid !== name) this[cid].classList.add("hide"); } this[name].classList.remove("hide"); } this.install_view = new InstallView( this.tab_id, () => show_container("install_view_container"), () => show_container("repos_list_container") ); this.install_view_container.prepend(this.install_view.main_div); const show_super = this.show; this.show = async url => { if (!show_super()) return; #IF MOZILLA if (this.incognito) { this.repo_entries = []; return; } #ENDIF this.url = url; this.url_span.innerText = url; [...this.repos_list.children].forEach(c => c.remove()); const repo_urls = await haketilodb.get_repos(); repo_urls.sort((a, b) => coll.compare(a, b)); this.repo_entries = repo_urls.map(ru => new RepoEntry(this, ru)); if (repo_urls.length === 0) { const info_li = document.createElement("li"); info_li.innerText = "You have no repositories configured :("; this.repos_list.append(info_li); return; } this.repos_list.append(...this.repo_entries.map(re => re.main_li)); } this.cancel_but.addEventListener("click", this.install_view.when_hidden(this.hide)); } #EXPORT RepoQueryView