/**
* 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