Project

General

Profile

Download (7.27 KB) Statistics
| Branch: | Tag: | Revision:

haketilo / html / repo_query.js @ 003876d5

1
/**
2
 * This file is part of Haketilo.
3
 *
4
 * Function: Show available repositories and allow querying them for resources.
5
 *
6
 * Copyright (C) 2022 Wojtek Kosior
7
 *
8
 * This program is free software: you can redistribute it and/or modify
9
 * it under the terms of the GNU General Public License as published by
10
 * the Free Software Foundation, either version 3 of the License, or
11
 * (at your option) any later version.
12
 *
13
 * This program is distributed in the hope that it will be useful,
14
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16
 * GNU General Public License for more details.
17
 *
18
 * As additional permission under GNU GPL version 3 section 7, you
19
 * may distribute forms of that code without the copy of the GNU
20
 * GPL normally required by section 4, provided you include this
21
 * license notice and, in case of non-source distribution, a URL
22
 * through which recipients can access the Corresponding Source.
23
 * If you modify file(s) with this exception, you may extend this
24
 * exception to your version of the file(s), but you are not
25
 * obligated to do so. If you do not wish to do so, delete this
26
 * exception statement from your version.
27
 *
28
 * As a special exception to the GPL, any HTML file which merely
29
 * makes function calls to this code, and for that purpose
30
 * includes it by reference shall be deemed a separate work for
31
 * copyright law purposes. If you modify this code, you may extend
32
 * this exception to your version of the code, but you are not
33
 * obligated to do so. If you do not wish to do so, delete this
34
 * exception statement from your version.
35
 *
36
 * You should have received a copy of the GNU General Public License
37
 * along with this program.  If not, see <https://www.gnu.org/licenses/>.
38
 *
39
 * I, Wojtek Kosior, thereby promise not to sue for violation of this file's
40
 * license. Although I request that you do not make use of this code in a
41
 * proprietary program, I am not going to enforce this in court.
42
 */
43

    
44
#IMPORT common/indexeddb.js AS haketilodb
45

    
46
#FROM common/browser.js    IMPORT browser
47
#FROM html/DOM_helpers.js  IMPORT clone_template, Showable
48
#FROM common/entities.js   IMPORT item_id_string, version_string
49
#FROM html/install.js      IMPORT InstallView
50
#FROM common/jsonschema.js IMPORT haketilo_validator, haketilo_schemas
51

    
52
const coll = new Intl.Collator();
53

    
54
function ResultEntry(repo_entry, mapping_ref) {
55
    Object.assign(this, clone_template("repo_query_single_result"));
56
    Object.assign(this, {repo_entry, mapping_ref});
57

    
58
    this.mapping_name.innerText = mapping_ref.long_name;
59
    this.mapping_id.innerText = item_id_string(mapping_ref);
60

    
61
    const iv = repo_entry.query_view.install_view;
62

    
63
    function start_install() {
64
	iv.show(repo_entry.repo_url, "mapping",
65
		mapping_ref.identifier, mapping_ref.version);
66
    }
67

    
68
    const cb = repo_entry.query_view.install_view.when_hidden(start_install);
69
    this.install_but.addEventListener("click", cb);
70
}
71

    
72
function RepoEntry(query_view, repo_url) {
73
    Object.assign(this, clone_template("repo_query_single_repo"));
74
    Object.assign(this, {query_view, repo_url});
75
    this.results_shown_before = false;
76

    
77
    this.repo_url_label.innerText = repo_url;
78

    
79
    const query_results = async () => {
80
	const msg = [
81
	    "repo_query",
82
	    `${repo_url}query?url=${encodeURIComponent(query_view.url)}`
83
	];
84
	const response = await browser.tabs.sendMessage(query_view.tab_id, msg);
85

    
86
	if ("error" in response)
87
	    throw "Failure to communicate with repository :(";
88

    
89
	if (!response.ok)
90
	    throw `Repository sent HTTP code ${response.status} :(`;
91
	if ("error_json" in response)
92
	    throw "Repository's response is not valid JSON :(";
93

    
94
	const $id =
95
	      `https://hydrilla.koszko.org/schemas/api_query_result-1.0.1.schema.json`;
96
	const schema = haketilo_schemas[$id];
97
	const result = haketilo_validator.validate(response.json, schema);
98
	if (result.errors.length > 0) {
99
	    console.error("Haketilo:", result.errors);
100

    
101
	    const reg = new RegExp(schema.properties.$schema.pattern);
102
	    if (response.json.$schema && !reg.test(response.json.$schema))
103
		throw "Results were served using unsupported Hydrilla API version. You might need to update Haketilo.";
104

    
105
	    throw "Results were served using a nonconforming response format.";
106
	}
107

    
108
	return response.json.mappings;
109
    }
110

    
111
    const populate_results = async () => {
112
	this.results_shown_before = true;
113

    
114
	try {
115
	    var results = await query_results();
116
	} catch(e) {
117
	    this.info_div.innerText = e;
118
	    return;
119
	}
120

    
121
	this.result_entries = results.map(ref => new ResultEntry(this, ref));
122

    
123
	if (this.result_entries.length > 0) {
124
	    this.results_list.classList.remove("hide");
125
	    this.info_div.remove();
126

    
127
	    const to_append = this.result_entries.map(re => re.main_li);
128
	    this.results_list.append(...to_append);
129
	} else {
130
	    this.info_div.innerText = "No results :(";
131
	}
132
    }
133

    
134
    let show_results = () => {
135
	if (!query_view.shown)
136
	    return;
137

    
138
	if (!this.results_shown_before)
139
	    populate_results();
140

    
141
	this.list_container.classList.remove("hide");
142
	this.hide_results_but.classList.remove("hide");
143
	this.show_results_but.classList.add("hide");
144
    }
145
    show_results = query_view.install_view.when_hidden(show_results);
146

    
147
    let hide_results = () => {
148
	if (!query_view.shown)
149
	    return;
150

    
151
	this.list_container.classList.add("hide");
152
	this.hide_results_but.classList.add("hide");
153
	this.show_results_but.classList.remove("hide");
154
    }
155
    hide_results = query_view.install_view.when_hidden(hide_results);
156

    
157
    this.show_results_but.addEventListener("click", show_results);
158
    this.hide_results_but.addEventListener("click", hide_results);
159
}
160

    
161
const container_ids = ["repos_list_container", "install_view_container"];
162

    
163
function RepoQueryView(tab, on_view_show, on_view_hide) {
164
    Showable.call(this, on_view_show, on_view_hide);
165

    
166
    Object.assign(this, clone_template("repo_query"));
167
    this.tab_id = tab.id;
168
#IF MOZILLA
169
    this.incognito = tab.incognito;
170
    if (this.incognito) {
171
	[...this.top_text.childNodes].forEach(n => n.remove());
172
	this.top_text.append(
173
	    clone_template("repo_query_private_mode_error").main_span
174
	);
175
    }
176
#ENDIF
177

    
178
    const show_container = name => {
179
	for (const cid of container_ids) {
180
	    if (cid !== name)
181
		this[cid].classList.add("hide");
182
	}
183
	this[name].classList.remove("hide");
184
    }
185

    
186
    this.install_view = new InstallView(
187
	this.tab_id,
188
	() => show_container("install_view_container"),
189
	() => show_container("repos_list_container")
190
    );
191
    this.install_view_container.prepend(this.install_view.main_div);
192

    
193
    const show_super = this.show;
194
    this.show = async url => {
195
	if (!show_super())
196
	    return;
197

    
198
#IF MOZILLA
199
	if (this.incognito) {
200
	    this.repo_entries = [];
201
	    return;
202
	}
203
#ENDIF
204

    
205
	this.url = url;
206
	this.url_span.innerText = url;
207

    
208
	[...this.repos_list.children].forEach(c => c.remove());
209

    
210
	const repo_urls = await haketilodb.get_repos();
211
	repo_urls.sort((a, b) => coll.compare(a, b));
212
	this.repo_entries = repo_urls.map(ru => new RepoEntry(this, ru));
213

    
214
	if (repo_urls.length === 0) {
215
	    const info_li = document.createElement("li");
216
	    info_li.innerText = "You have no repositories configured :(";
217
	    this.repos_list.append(info_li);
218
	    return;
219
	}
220

    
221
	this.repos_list.append(...this.repo_entries.map(re => re.main_li));
222
    }
223

    
224
    this.cancel_but.addEventListener("click",
225
				     this.install_view.when_hidden(this.hide));
226
}
227
#EXPORT RepoQueryView
(21-21/27)