From 4c6a2323d90e9321ec2b78e226167b3013ea69ab Mon Sep 17 00:00:00 2001 From: Wojtek Kosior Date: Sat, 29 Jan 2022 00:03:51 +0100 Subject: make Haketilo buildable again (for Mozilla) How cool it is to throw away 5755 lines of code... --- html/back_button.css | 71 ---- html/display_panel.html | 370 ------------------- html/display_panel.js | 586 ------------------------------- html/import_frame.html | 79 ----- html/import_frame.js | 185 ---------- html/mozilla_scrollbar_fix.css | 67 ---- html/options.html | 416 ---------------------- html/options_main.js | 781 ----------------------------------------- html/popup.html | 2 +- html/settings.html | 1 - html/table.css | 74 ---- 11 files changed, 1 insertion(+), 2631 deletions(-) delete mode 100644 html/back_button.css delete mode 100644 html/display_panel.html delete mode 100644 html/display_panel.js delete mode 100644 html/import_frame.html delete mode 100644 html/import_frame.js delete mode 100644 html/mozilla_scrollbar_fix.css delete mode 100644 html/options.html delete mode 100644 html/options_main.js delete mode 100644 html/table.css (limited to 'html') diff --git a/html/back_button.css b/html/back_button.css deleted file mode 100644 index 38740d7..0000000 --- a/html/back_button.css +++ /dev/null @@ -1,71 +0,0 @@ -/* - * SPDX-License-Identifier: GPL-3.0-or-later OR CC-BY-SA-4.0 - * - * Style for a "back" button with a CSS arrow image - * - * This file is part of Haketilo. - * - * Copyright (C) 2021 Wojtek Kosior - * - * File is dual-licensed. You can choose either GPLv3+, CC BY-SA or both. - * - * 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. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - * - * I, Wojtek Kosior, thereby promise not to sue for violation of this file's - * licenses. 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. - */ - -.back_button { - display: block; - width: auto; - height: auto; - background-color: white; - border: solid #454 0.4em; - border-left: none; - border-radius: 0 1.5em 1.5em 0; - cursor: pointer; -} - -.back_button:hover { - box-shadow: 0 6px 8px 0 rgba(0,0,0,0.24), 0 17px 50px 0 rgba(0,0,0,0.19); -} - -.back_button>div, .back_arrow { - width: 2em; - height: 0.5em; - background-color: #4CAF50; - border-radius: 0.3em; - margin: 1.15em 0.4em; -} - -.back_button>div::after, .back_arrow::after, -.back_button>div::before, .back_arrow::before { - content: ""; - display: block; - position: relative; - background-color: inherit; - width: 1.3em; - height: 0.5em; - transform: rotate(45deg); - border-radius: 0.3em; - top: 0.3em; - right: 0.2em; - margin: 0 -1.3em -0.5em 0; -} - -.back_button>div::before, .back_arrow::before { - transform: rotate(-45deg); - top: -0.3em; -} diff --git a/html/display_panel.html b/html/display_panel.html deleted file mode 100644 index 1468368..0000000 --- a/html/display_panel.html +++ /dev/null @@ -1,370 +0,0 @@ - - - - - - Haketilo - page settings -#LOADCSS html/reset.css -#LOADCSS html/base.css -#LOADCSS html/back_button.css -#LOADCSS html/table.css -#IF MOZILLA -#LOADCSS html/mozilla_scrollbar_fix.css -#ENDIF - - - - - - -
-

Site modifiers install

-
-#INCLUDE html/import_frame.html -
-
- - -
-

Injected scripts

-
- None -
-
- - -
-

Possible patterns for this page

-
- -
-
-
- - - -
-
-
-
- -#INCLUDE html/default_blocking_policy.html - -
-
- - -
-

Queried from repositories

-
-
-
- - -
-

-

Privileged page

- -
-
- -
-
- -
- - - - - - - - - - - - - - - - - - - - - -
Matched pattern:... - -
Scripts blocked:...
Injected payload:... - -
- -
- This is a non-HTML page. Chosen payload will not be injected. -
- -
-
-

- Connecting to content script... -

- -
-
-
- - -
- -
- - -#LOADJS html/display_panel.js - - diff --git a/html/display_panel.js b/html/display_panel.js deleted file mode 100644 index 1cd77e6..0000000 --- a/html/display_panel.js +++ /dev/null @@ -1,586 +0,0 @@ -/** - * This file is part of Haketilo. - * - * Function: Popup logic. - * - * Copyright (C) 2021 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 . - * - * 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/connection_types.js AS CONNECTION_TYPE - -#FROM common/browser.js IMPORT browser -/* - * Using remote storage here seems inefficient, we only resort to that - * temporarily, before all storage access gets reworked. - */ -#FROM common/storage_client.js IMPORT get_remote_storage -#FROM html/import_frame.js IMPORT get_import_frame -#FROM common/settings_query.js IMPORT query_all -#FROM common/misc.js IMPORT is_privileged_url, nice_name, \ - open_in_settings -#FROM common/stored_types.js IMPORT TYPE_PREFIX -#FROM common/patterns.js IMPORT each_url_pattern -#FROM html/DOM_helpers.js IMPORT by_id, clone_template - -let storage; -let tab_url; - -#IF MOZILLA -/* Force popup 's reflow on stupid Firefox. */ -const reflow_forcer = - () => document.documentElement.style.width = "-moz-fit-content"; -for (const radio of document.querySelectorAll('[name="current_view"]')) - radio.addEventListener("change", reflow_forcer); -#ENDIF - -const show_queried_view_radio = by_id("show_queried_view_radio"); - -const tab_query = {currentWindow: true, active: true}; - -async function get_current_tab() -{ -#IF CHROMIUM - const callback = (cb) => browser.tabs.query(tab_query, tab => cb(tab)); - const promise = new Promise(callback); -#ELIF MOZILLA - const promise = browser.tabs.query(tab_query); -#ENDIF - - try { - return (await promise)[0]; - } catch(e) { - console.log(e); - } -} - -const page_url_heading = by_id("page_url_heading"); -const privileged_notice = by_id("privileged_notice"); -const page_state = by_id("page_state"); - -/* Helper functions to convert string into a list of one-letter 's. */ -function char_to_span(char, doc) -{ - const span = document.createElement("span"); - span.textContent = char; - return span; -} - -function to_spans(string, doc=document) -{ - return string.split("").map(c => char_to_span(c, doc)); -} - -async function show_page_activity_info() -{ - const tab = await get_current_tab(); - - if (tab === undefined) { - page_url_heading.textContent = "unknown page"; - return; - } - - tab_url = /^([^?#]*)/.exec(tab.url)[1]; - to_spans(tab_url).forEach(s => page_url_heading.append(s)); - if (is_privileged_url(tab_url)) { - privileged_notice.classList.remove("hide"); - return; - } - - populate_possible_patterns_list(tab_url); - page_state.classList.remove("hide"); - - try_to_connect(tab.id); -} - -const possible_patterns_list = by_id("possible_patterns"); -const known_patterns = new Map(); - -function add_pattern_to_list(pattern) -{ - const template = clone_template("pattern_entry"); - template.name.textContent = pattern; - - const settings_opener = () => open_in_settings(TYPE_PREFIX.PAGE, pattern); - template.button.addEventListener("click", settings_opener); - - known_patterns.set(pattern, template); - possible_patterns_list.append(template.entry); - - return template; -} - -function style_possible_pattern_entry(pattern, exists_in_settings) -{ - const [text, class_action] = exists_in_settings ? - ["Edit", "add"] : ["Add", "remove"]; - const entry_object = known_patterns.get(pattern); - - if (entry_object) { - entry_object.button.textContent = `${text} setting`; - entry_object.entry.classList[class_action]("matched_pattern"); - } -} - -function handle_page_change(change) -{ - style_possible_pattern_entry(change.item, change.new_val !== undefined); -} - -function populate_possible_patterns_list(url) -{ - for (const pattern of each_url_pattern(url)) - add_pattern_to_list(pattern); - - for (const [pattern, settings] of query_all(storage, url)) - style_possible_pattern_entry(pattern, true); - - storage.add_change_listener(handle_page_change, [TYPE_PREFIX.PAGE]); -} - -const connected_chbx = by_id("connected_chbx"); -const query_pattern_but = by_id("query_pattern"); - -var content_script_port; - -function try_to_connect(tab_id) -{ - /* This won't connect to iframes. We'll add support for them later */ - const connect_info = {name: CONNECTION_TYPE.ACTIVITY_INFO, frameId: 0}; - content_script_port = browser.tabs.connect(tab_id, connect_info); - - const disconnect_cb = () => handle_disconnect(tab_id, start_querying_repos); - content_script_port.onDisconnect.addListener(disconnect_cb); - content_script_port.onMessage.addListener(handle_activity_report); - - query_pattern_but.addEventListener("click", start_querying_repos); - -#IF MOZILLA - setTimeout(() => monitor_connecting(tab_id), 1000); -#ENDIF -} - -function start_querying_repos() -{ - query_pattern_but.removeEventListener("click", start_querying_repos); - const repo_urls = storage.get_all_names(TYPE_PREFIX.REPO); - if (content_script_port) - content_script_port.postMessage([TYPE_PREFIX.URL, tab_url, repo_urls]); -} - -const loading_point = by_id("loading_point"); -const reload_notice = by_id("reload_notice"); - -function handle_disconnect(tab_id, button_cb) -{ - query_pattern_but.removeEventListener("click", button_cb); - content_script_port = null; - -#IF CHROMIUM - if (!browser.runtime.lastError) - return; -#ENDIF - - /* return if error was not during connection initialization */ - if (connected_chbx.checked) - return; - - loading_point.classList.toggle("camouflage"); - reload_notice.classList.remove("hide"); - - setTimeout(() => try_to_connect(tab_id), 1000); -} - -function monitor_connecting(tab_id) -{ - if (connected_chbx.checked) - return; - - if (content_script_port) - content_script_port.disconnect(); - else - return; - - loading_point.classList.toggle("camouflage"); - reload_notice.classList.remove("hide"); - try_to_connect(tab_id); -} - -const pattern_span = by_id("pattern"); -const view_pattern_but = by_id("view_pattern"); -const blocked_span = by_id("blocked"); -const payload_span = by_id("payload"); -const payload_buttons_div = by_id("payload_buttons"); -const view_payload_but = by_id("view_payload"); -const view_injected_but = by_id("view_injected"); -const container_for_injected = by_id("container_for_injected"); -const content_type_cell = by_id("content_type"); - -const queried_items = new Map(); - -let max_injected_script_id = 0; - -function handle_activity_report(message) -{ - connected_chbx.checked = true; - - const [type, data] = message; - - if (type === "settings") { - const settings = data; - - blocked_span.textContent = settings.allow ? "no" : "yes"; - - if (settings.pattern) { - pattern_span.textContent = settings.pattern; - const settings_opener = - () => open_in_settings(TYPE_PREFIX.PAGE, settings.pattern); - view_pattern_but.classList.remove("hide"); - view_pattern_but.addEventListener("click", settings_opener); - } else { - pattern_span.textContent = "none"; - blocked_span.textContent = blocked_span.textContent + " (default)"; - } - - if (settings.payload) { - payload_span.textContent = nice_name(...settings.payload); - payload_buttons_div.classList.remove("hide"); - const settings_opener = () => open_in_settings(...settings.payload); - view_payload_but.addEventListener("click", settings_opener); - } else { - payload_span.textContent = "none"; - } - } - if (type === "script") { - const template = clone_template("injected_script"); - const chbx_id = `injected_script_${max_injected_script_id++}`; - template.chbx.id = chbx_id; - template.lbl.setAttribute("for", chbx_id); - template.script_contents.textContent = data; - container_for_injected.appendChild(template.div); - } - if (type === "is_html") { - if (!data) - content_type_cell.classList.remove("hide"); - } - if (type === "repo_query_action") { - const key = data.prefix + data.item; - const results = queried_items.get(key) || {}; - Object.assign(results, data.results); - queried_items.set(key, results); - - const action = data.prefix === TYPE_PREFIX.URL ? - show_query_result : record_fetched_install_dep; - - for (const [repo_url, result] of Object.entries(data.results)) - action(data.prefix, data.item, repo_url, result); - } -} - -const container_for_repo_responses = by_id("container_for_repo_responses"); - -const results_lists = new Map(); - -function create_results_list(url) -{ - const cloned_template = clone_template("multi_repos_query_result"); - cloned_template.url_span.textContent = url; - container_for_repo_responses.appendChild(cloned_template.div); - - cloned_template.by_repo = new Map(); - results_lists.set(url, cloned_template); - - return cloned_template; -} - -function create_result_item(list_object, repo_url, result) -{ - const cloned_template = clone_template("single_repo_query_result"); - cloned_template.repo_url.textContent = repo_url; - cloned_template.appended = null; - - list_object.ul.appendChild(cloned_template.li); - list_object.by_repo.set(repo_url, cloned_template); - - return cloned_template; -} - -function set_appended(result_item, element) -{ - if (result_item.appended) - result_item.appended.remove(); - result_item.appended = element; - result_item.li.appendChild(element); -} - -function show_message(result_item, text) -{ - const div = document.createElement("div"); - div.textContent = text; - set_appended(result_item, div); -} - -function showcb(text) -{ - return item => show_message(item, text); -} - -function unroll_chbx_first_checked(entry_object) -{ - if (!entry_object.chbx.checked) - return; - - entry_object.chbx.removeEventListener("change", entry_object.unroll_cb); - delete entry_object.unroll_cb; - - entry_object.unroll.innerHTML = "preview not implemented...
(consider contributing)"; -} - -let import_frame; -let install_target = null; - -function install_abort(error_state) -{ - import_frame.show_error(`Error: ${error_state}`); - install_target = null; -} - -/* - * Translate objects from the format in which they are sent by Hydrilla to the - * format in which they are stored in settings. - */ - -function translate_script(script_object, repo_url) -{ - return { - [TYPE_PREFIX.SCRIPT + script_object.name]: { - hash: script_object.sha256, - url: `${repo_url}/content/${script_object.location}` - } - }; -} - -function translate_bag(bag_object) -{ - return { - [TYPE_PREFIX.BAG + bag_object.name]: bag_object.components - }; -} - -const format_translators = { - [TYPE_PREFIX.BAG]: translate_bag, - [TYPE_PREFIX.SCRIPT]: translate_script -}; - -function install_check_ready() -{ - if (install_target.to_fetch.size > 0) - return; - - const page_key = [TYPE_PREFIX.PAGE + install_target.pattern]; - const to_install = [{[page_key]: {components: install_target.payload}}]; - - for (const key of install_target.fetched) { - const old_object = - queried_items.get(key)[install_target.repo_url].response; - const new_object = - format_translators[key[0]](old_object, install_target.repo_url); - to_install.push(new_object); - } - - import_frame.show_selection(to_install); -} - -const possible_errors = ["connection_error", "parse_error"]; - -function fetch_install_deps(components) -{ - const needed = [...components]; - const processed = new Set(); - - while (needed.length > 0) { - const [prefix, item] = needed.pop(); - const key = prefix + item; - processed.add(key); - const results = queried_items.get(key); - let relevant_result = null; - - if (results) - relevant_result = results[install_target.repo_url]; - - if (!relevant_result) { - content_script_port.postMessage([prefix, item, - [install_target.repo_url]]); - install_target.to_fetch.add(key); - continue; - } - - if (possible_errors.includes(relevant_result.state)) { - install_abort(relevant_result.state); - return false; - } - - install_target.fetched.add(key); - - if (prefix !== TYPE_PREFIX.BAG) - continue; - - for (const dependency of relevant_result.response.components) { - if (processed.has(dependency.join(''))) - continue; - needed.push(dependency); - } - } -} - -function record_fetched_install_dep(prefix, item, repo_url, result) -{ - const key = prefix + item; - - if (!install_target || repo_url !== install_target.repo_url || - !install_target.to_fetch.has(key)) - return; - - if (possible_errors.includes(result.state)) { - install_abort(result.state); - return; - } - - if (result.state !== "completed") - return; - - install_target.to_fetch.delete(key); - install_target.fetched.add(key); - - if (prefix === TYPE_PREFIX.BAG && - fetch_install_deps(result.response.components) === false) - return; - - install_check_ready(); -} - -function install_clicked(entry_object) -{ - import_frame.show_loading(); - - install_target = { - repo_url: entry_object.repo_url, - pattern: entry_object.match_object.pattern, - payload: entry_object.match_object.payload, - fetched: new Set(), - to_fetch: new Set() - }; - - fetch_install_deps([install_target.payload]); - - install_check_ready(); -} - -var max_query_result_id = 0; - -function show_query_successful_result(result_item, repo_url, result) -{ - if (result.length === 0) { - show_message(result_item, "No results :("); - return; - } - - const cloned_ul_template = clone_template("result_patterns_list"); - set_appended(result_item, cloned_ul_template.ul); - - for (const match of result) { - const entry_object = clone_template("query_match_li"); - - entry_object.pattern.textContent = match.pattern; - - cloned_ul_template.ul.appendChild(entry_object.li); - - if (!match.payload) { - entry_object.payload.textContent = "(none)"; - for (const key of ["chbx", "triangle", "unroll"]) - entry_object[key].remove(); - continue; - } - - entry_object.payload.textContent = nice_name(...match.payload); - - const install_cb = () => install_clicked(entry_object); - entry_object.btn.addEventListener("click", install_cb); - - const chbx_id = `query_result_${max_query_result_id++}`; - entry_object.chbx.id = chbx_id; - entry_object.lbl.setAttribute("for", chbx_id); - - entry_object.unroll_cb = () => unroll_chbx_first_checked(entry_object); - entry_object.chbx.addEventListener("change", entry_object.unroll_cb); - - entry_object.component_object = match.payload; - entry_object.match_object = match; - entry_object.repo_url = repo_url; - } -} - -function show_query_result(url_prefix, url, repo_url, result) -{ - const results_list_object = results_lists.get(url) || - create_results_list(url); - const result_item = results_list_object.by_repo.get(repo_url) || - create_result_item(results_list_object, repo_url, result); - - const completed_cb = - item => show_query_successful_result(item, repo_url, result.response); - const possible_actions = { - completed: completed_cb, - started: showcb("loading..."), - connection_error: showcb("Error when querying repository."), - parse_error: showcb("Bad data format received.") - }; - possible_actions[result.state](result_item, repo_url); -} - -by_id("settings_but") - .addEventListener("click", (e) => browser.runtime.openOptionsPage()); - -async function main() -{ - storage = await get_remote_storage(); - import_frame = await get_import_frame(); - import_frame.onclose = () => show_queried_view_radio.checked = true; - show_page_activity_info(); -} - -main(); diff --git a/html/import_frame.html b/html/import_frame.html deleted file mode 100644 index bac98a8..0000000 --- a/html/import_frame.html +++ /dev/null @@ -1,79 +0,0 @@ - - - - - - - - - Loading... - - -
- - -

-  
-
- - -
- - - - -
-
- - - -
-
-
- - -
diff --git a/html/import_frame.js b/html/import_frame.js deleted file mode 100644 index 659d420..0000000 --- a/html/import_frame.js +++ /dev/null @@ -1,185 +0,0 @@ -/** - * This file is part of Haketilo. - * - * Function: Logic for the settings import frame. - * - * Copyright (C) 2021 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 . - * - * 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. - */ - -#FROM common/storage_client.js IMPORT get_remote_storage -#FROM html/DOM_helpers.js IMPORT by_id, clone_template -#FROM common/misc.js IMPORT nice_name -#FROM common/once.js IMPORT make_once - -let storage; - -let import_list = by_id("import_list"); -let import_chbxs_colliding = undefined; -let entry_objects = undefined; -let settings_import_map = undefined; - -function add_import_entry(prefix, name) -{ - const cloned_template = clone_template("import_entry"); - Object.assign(cloned_template, {prefix, name}); - - cloned_template.name_span.textContent = nice_name(prefix, name); - - if (storage.get(prefix, name) !== undefined) { - import_chbxs_colliding.push(cloned_template.chbx); - cloned_template.warning.textContent = "!"; - } - - import_list.appendChild(cloned_template.entry); - - return cloned_template; -} - -function check_all_imports() -{ - for (const entry_object of entry_objects) - entry_object.chbx.checked = true; -} - -function uncheck_all_imports() -{ - for (const entry_object of entry_objects) - entry_object.chbx.checked = false; -} - -function uncheck_colliding_imports() -{ - for (let chbx of import_chbxs_colliding) - chbx.checked = false; -} - -function commit_import() -{ - for (const entry_object of entry_objects) { - if (!entry_object.chbx.checked) - continue; - - const key = entry_object.prefix + entry_object.name; - const value = settings_import_map.get(key); - storage.set(entry_object.prefix, entry_object.name, value); - } - - deactivate(); -} - -const import_loading_radio = by_id("import_loading_radio"); - -function show_loading() -{ - import_loading_radio.checked = true; -} - -const import_failed_radio = by_id("import_failed_radio"); -const import_errormsg = by_id("import_errormsg"); -const import_errordetail_chbx = by_id("import_errordetail_chbx"); -const import_errordetail = by_id("import_errordetail"); - -function show_error(errormsg, errordetail) -{ - import_failed_radio.checked = true; - import_errormsg.textContent = errormsg; - import_errordetail_chbx.checked = errordetail; - import_errordetail.textContent = errordetail; -} - -const import_selection_radio = by_id("import_selection_radio"); -const existing_settings_note = by_id("existing_settings_note"); - -function show_selection(settings) -{ - import_selection_radio.checked = true; - - let old_children = import_list.children; - while (old_children[0] !== undefined) - import_list.removeChild(old_children[0]); - - import_chbxs_colliding = []; - entry_objects = []; - settings_import_map = new Map(); - - for (let setting of settings) { - let [key, value] = Object.entries(setting)[0]; - let prefix = key[0]; - let name = key.substring(1); - entry_objects.push(add_import_entry(prefix, name)); - settings_import_map.set(key, value); - } - - const op = import_chbxs_colliding.length > 0 ? "remove" : "add"; - existing_settings_note.classList[op]("hide"); -} - -function deactivate() -{ - /* Let GC free some memory */ - import_chbxs_colliding = undefined; - entry_objects = undefined; - settings_import_map = undefined; - - if (exports.onclose) - exports.onclose(); -} - -const wrapper = by_id("import_table_wrapper"); -const style_table = (...cls) => cls.forEach(c => wrapper.classList.add(c)); - -const exports = - {show_loading, show_error, show_selection, deactivate, style_table}; - -async function init() -{ - storage = await get_remote_storage(); - - by_id("commit_import_but").addEventListener("click", commit_import); - by_id("check_all_import_but").addEventListener("click", check_all_imports); - by_id("uncheck_all_import_but") - .addEventListener("click", uncheck_all_imports); - by_id("uncheck_colliding_import_but") - .addEventListener("click", uncheck_colliding_imports); - by_id("cancel_import_but").addEventListener("click", deactivate); - by_id("import_failok_but").addEventListener("click", deactivate); - - return exports; -} - -#EXPORT make_once(init) AS get_import_frame diff --git a/html/mozilla_scrollbar_fix.css b/html/mozilla_scrollbar_fix.css deleted file mode 100644 index 08cdc93..0000000 --- a/html/mozilla_scrollbar_fix.css +++ /dev/null @@ -1,67 +0,0 @@ -/* - * SPDX-License-Identifier: GPL-3.0-or-later OR CC-BY-SA-4.0 - * - * Hacky fix for vertical scrollbar width being included in child's width - * - * This file is part of Haketilo. - * - * Copyright (C) 2021 Wojtek Kosior - * - * File is dual-licensed. You can choose either GPLv3+, CC BY-SA or both. - * - * 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. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - * - * I, Wojtek Kosior, thereby promise not to sue for violation of this file's - * licenses. 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. - */ - -/* - * Under Mozilla browsers to avoid vertical scrollbar forcing horizontal - * scrollbar to appear in an element we add the `firefox_scrollbars_hacky_fix' - * class to an element for which width has to be reserved. - * - * This is a bit hacky and relies on some assumed width of Firefox scrollbar, I - * know. And must be excluded from Chromium builds. - * - * I came up with this hack when working on popup. Before that I had the - * scrollbar issue with tables in the options page and gave up there and made - * the scrollbal always visible. Now we could try applying this "fix" there, - * too! - */ - -.firefox_scrollbars_hacky_fix { - font-size: 0; -} - -.firefox_scrollbars_hacky_fix>div { - display: inline-block; - width: -moz-available; -} - -.firefox_scrollbars_hacky_fix>*>* { - font-size: initial; -} - -.firefox_scrollbars_hacky_fix::after { - content: ""; - display: inline-block; - visibility: hidden; - font-size: initial; - width: 14px; -} - -.firefox_scrollbars_hacky_fix.has_inline_content::after { - width: calc(14px - 0.3em); -} diff --git a/html/options.html b/html/options.html deleted file mode 100644 index e317032..0000000 --- a/html/options.html +++ /dev/null @@ -1,416 +0,0 @@ - - - - - - Haketilo options -#LOADCSS html/reset.css -#LOADCSS html/base.css -#LOADCSS html/table.css - - - - - - - - - - -
- - - - - -
-
-
-
- - - - - - -
-
- - -
- - -
-
-
-
-
- -
-
-
-
- - - - - - -
-
- - - - - - - -
- - - - (only possible when no payload is used) - -
-
- - -
-
-
-
-
- -
-#INCLUDE html/default_blocking_policy.html -
-
-
-
- - - - - - -
-
- - -
-
- - - - -
-
-
-
- -
-
- - -
-
-
-
-
- -
-
-
-
- - - - - - -
-
- - - - - - - - - -
- - -
-
-
-
-
- -
- - - - - - - - - -#LOADJS html/options_main.js - - diff --git a/html/options_main.js b/html/options_main.js deleted file mode 100644 index 4b828f0..0000000 --- a/html/options_main.js +++ /dev/null @@ -1,781 +0,0 @@ -/** - * This file is part of Haketilo. - * - * Function: Settings page logic. - * - * Copyright (C) 2021 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 . - * - * 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. - */ - -#FROM common/storage_client.js IMPORT get_remote_storage -#FROM common/stored_types.js IMPORT TYPE_PREFIX, TYPE_NAME, \ - list_prefixes -#FROM common/misc.js IMPORT nice_name, matchers -#FROM common/sanitize_JSON.js IMPORT parse_json_with_schema -#FROM html/DOM_helpers.js IMPORT get_template, by_id -#FROM html/import_frame.js IMPORT get_import_frame - -var storage; - -const item_li_template = get_template("item_li"); -const bag_component_li_template = get_template("bag_component_li"); -const chbx_component_li_template = get_template("chbx_component_li"); -const radio_component_li_template = get_template("radio_component_li"); -/* Make sure they are later cloned without id. */ -item_li_template.removeAttribute("id"); -bag_component_li_template.removeAttribute("id"); -chbx_component_li_template.removeAttribute("id"); -radio_component_li_template.removeAttribute("id"); - -function list_set_scrollbar(list_elem) -{ - const op = ((list_elem.children.length === 1 && - list_elem.children[0].classList.contains("hide")) || - list_elem.children.length < 1) ? "remove" : "add"; - while (!list_elem.classList.contains("table_wrapper")) - list_elem = list_elem.parentElement; - list_elem.classList[op]("always_scrollbar"); -} - -function item_li_id(prefix, item) -{ - return `li_${prefix}_${item}`; -} - -/* Insert into list of bags/pages/scripts/repos */ -function add_li(prefix, item, at_the_end=false) -{ - let ul = ul_by_prefix[prefix]; - let li = item_li_template.cloneNode(true); - li.id = item_li_id(prefix, item); - - let span = li.firstElementChild; - span.textContent = item; - - let edit_button = span.nextElementSibling; - edit_button.addEventListener("click", () => edit_item(prefix, item)); - - let remove_button = edit_button.nextElementSibling; - remove_button.addEventListener("click", - () => storage.remove(prefix, item)); - - let export_button = remove_button.nextElementSibling; - export_button.addEventListener("click", - () => export_item(prefix, item)); - if (prefix === TYPE_PREFIX.REPO) - export_button.remove(); - - if (!at_the_end) { - for (let element of ul.ul.children) { - if (element.id < li.id || element.id.startsWith("work_")) - continue; - - ul.ul.insertBefore(li, element); - break; - } - } - if (!li.parentElement) { - if (ul.work_li !== ul.ul.lastElementChild) - ul.ul.appendChild(li); - else - ul.work_li.before(li); - } - - list_set_scrollbar(ul.ul); -} - -const chbx_components_ul = by_id("chbx_components_ul"); -const radio_components_ul = by_id("radio_components_ul"); - -function chbx_li_id(prefix, item) -{ - return `cli_${prefix}_${item}`; -} - -function radio_li_id(prefix, item) -{ - return `rli_${prefix}_${item}`; -} - -//TODO: refactor the 2 functions below - -function add_chbx_li(prefix, name) -{ - if (![TYPE_PREFIX.BAG, TYPE_PREFIX.SCRIPT].includes(prefix)) - return; - - let li = chbx_component_li_template.cloneNode(true); - li.id = chbx_li_id(prefix, name); - li.setAttribute("data-prefix", prefix); - li.setAttribute("data-name", name); - - let chbx = li.firstElementChild.firstElementChild; - let span = chbx.nextElementSibling; - - span.textContent = nice_name(prefix, name); - - chbx_components_ul.appendChild(li); - list_set_scrollbar(chbx_components_ul); -} - -var radio_component_none_li = by_id("radio_component_none_li"); - -function add_radio_li(prefix, name) -{ - if (![TYPE_PREFIX.BAG, TYPE_PREFIX.SCRIPT].includes(prefix)) - return; - - let li = radio_component_li_template.cloneNode(true); - li.id = radio_li_id(prefix, name); - li.setAttribute("data-prefix", prefix); - li.setAttribute("data-name", name); - - let radio = li.firstElementChild.firstElementChild; - let span = radio.nextElementSibling; - - span.textContent = nice_name(prefix, name); - - radio_component_none_li.before(li); - list_set_scrollbar(radio_components_ul); -} - -/* Used to reset edited repo. */ -function reset_work_repo_li(ul, item, _) -{ - ul.work_name_input.value = maybe_string(item); -} - -/* Used to get repo data for saving */ -function work_repo_li_data(ul) -{ - return [ul.work_name_input.value, {}]; -} - -const allow_native_scripts_container = by_id("allow_native_scripts_container"); -const page_payload_span = by_id("page_payload"); - -function set_page_components(components) -{ - if (components === undefined) { - page_payload_span.setAttribute("data-payload", "no"); - page_payload_span.textContent = "(None)"; - allow_native_scripts_container.classList.remove("form_disabled"); - } else { - page_payload_span.setAttribute("data-payload", "yes"); - let [prefix, name] = components; - page_payload_span.setAttribute("data-prefix", prefix); - page_payload_span.setAttribute("data-name", name); - page_payload_span.textContent = nice_name(prefix, name); - allow_native_scripts_container.classList.add("form_disabled"); - } -} - -const page_allow_chbx = by_id("page_allow_chbx"); - -/* Used to reset edited page. */ -function reset_work_page_li(ul, item, settings) -{ - ul.work_name_input.value = maybe_string(item); - settings = settings || {allow: false, components: undefined}; - page_allow_chbx.checked = !!settings.allow; - - set_page_components(settings.components); -} - -function work_page_li_components() -{ - if (page_payload_span.getAttribute("data-payload") === "no") - return undefined; - - let prefix = page_payload_span.getAttribute("data-prefix"); - let name = page_payload_span.getAttribute("data-name"); - return [prefix, name]; -} - -/* Used to get edited page data for saving. */ -function work_page_li_data(ul) -{ - let url = ul.work_name_input.value; - let settings = { - components : work_page_li_components(), - allow : !!page_allow_chbx.checked - }; - - return [url, settings]; -} - -const empty_bag_component_li = by_id("empty_bag_component_li"); -var bag_components_ul = by_id("bag_components_ul"); - -function remove_bag_component_entry(entry) -{ - const list = entry.parentElement; - entry.remove(); - list_set_scrollbar(list); -} - -/* Used to construct and update components list of edited bag. */ -function add_bag_components(components) -{ - for (let component of components) { - let [prefix, name] = component; - let li = bag_component_li_template.cloneNode(true); - li.setAttribute("data-prefix", prefix); - li.setAttribute("data-name", name); - - let span = li.firstElementChild; - span.textContent = nice_name(prefix, name); - let remove_but = span.nextElementSibling; - remove_but.addEventListener("click", - () => remove_bag_component_entry(li)); - bag_components_ul.appendChild(li); - } - - bag_components_ul.appendChild(empty_bag_component_li); - list_set_scrollbar(bag_components_ul); -} - -/* Used to reset edited bag. */ -function reset_work_bag_li(ul, item, components) -{ - components = components || []; - - ul.work_name_input.value = maybe_string(item); - let old_components_ul = bag_components_ul; - bag_components_ul = old_components_ul.cloneNode(false); - - old_components_ul.replaceWith(bag_components_ul); - - add_bag_components(components); -} - -/* Used to get edited bag data for saving. */ -function work_bag_li_data(ul) -{ - let component_li = bag_components_ul.firstElementChild; - - let components = []; - - /* Last list element is empty li with id set. */ - while (component_li.id === '') { - components.push([component_li.getAttribute("data-prefix"), - component_li.getAttribute("data-name")]); - component_li = component_li.nextElementSibling; - } - - return [ul.work_name_input.value, components]; -} - -const script_url_input = by_id("script_url_field"); -const script_sha256_input = by_id("script_sha256_field"); -const script_contents_field = by_id("script_contents_field"); - -function maybe_string(maybe_defined) -{ - return maybe_defined === undefined ? "" : maybe_defined + ""; -} - -/* Used to reset edited script. */ -function reset_work_script_li(ul, name, data) -{ - ul.work_name_input.value = maybe_string(name); - if (data === undefined) - data = {}; - script_url_input.value = maybe_string(data.url); - script_sha256_input.value = maybe_string(data.hash); - script_contents_field.value = maybe_string(data.text); -} - -/* Used to get edited script data for saving. */ -function work_script_li_data(ul) -{ - return [ul.work_name_input.value, { - url : script_url_input.value, - hash : script_sha256_input.value, - text : script_contents_field.value - }]; -} - -function cancel_work(prefix) -{ - let ul = ul_by_prefix[prefix]; - - if (ul.state === UL_STATE.IDLE) - return; - - if (ul.state === UL_STATE.EDITING_ENTRY) { - add_li(prefix, ul.edited_item); - } - - ul.work_li.classList.add("hide"); - ul.ul.append(ul.work_li); - list_set_scrollbar(ul.ul); - ul.state = UL_STATE.IDLE; -} - -function save_work(prefix) -{ - let ul = ul_by_prefix[prefix]; - - if (ul.state === UL_STATE.IDLE) - return; - - let [item, data] = ul.get_work_li_data(ul); - - /* Here we fire promises and return without waiting. */ - - if (ul.state === UL_STATE.EDITING_ENTRY) - storage.replace(prefix, ul.edited_item, item, data); - if (ul.state === UL_STATE.ADDING_ENTRY) - storage.set(prefix, item, data); - - cancel_work(prefix); -} - -function edit_item(prefix, item) -{ - cancel_work(prefix); - - let ul = ul_by_prefix[prefix]; - let li = by_id(item_li_id(prefix, item)); - - if (li === null) { - add_new_item(prefix, item); - return; - } - - ul.reset_work_li(ul, item, storage.get(prefix, item)); - ul.ul.insertBefore(ul.work_li, li); - ul.ul.removeChild(li); - ul.work_li.classList.remove("hide"); - list_set_scrollbar(ul.ul); - - ul.state = UL_STATE.EDITING_ENTRY; - ul.edited_item = item; -} - -const file_downloader = by_id("file_downloader"); - -function recursively_export_item(prefix, name, added_items, items_data) -{ - let key = prefix + name; - - if (added_items.has(key)) - return; - - let data = storage.get(prefix, name); - if (data === undefined) { - console.log(`${TYPE_NAME[prefix]} '${name}' for export not found`); - return; - } - - if (prefix !== TYPE_PREFIX.SCRIPT) { - let components = prefix === TYPE_PREFIX.BAG ? - data : [data.components]; - - for (let [comp_prefix, comp_name] of components) { - recursively_export_item(comp_prefix, comp_name, - added_items, items_data); - } - } - - items_data.push({[key]: data}); - added_items.add(key); -} - -function export_item(prefix, name) -{ - let added_items = new Set(); - let items_data = []; - recursively_export_item(prefix, name, added_items, items_data); - let file = new Blob([JSON.stringify(items_data)], - {type: "application/json"}); - let url = URL.createObjectURL(file); - file_downloader.setAttribute("href", url); - file_downloader.setAttribute("download", prefix + name + ".json"); - file_downloader.click(); - file_downloader.removeAttribute("href"); - URL.revokeObjectURL(url); -} - -function add_new_item(prefix, name) -{ - cancel_work(prefix); - - let ul = ul_by_prefix[prefix]; - ul.reset_work_li(ul); - ul.work_li.classList.remove("hide"); - ul.ul.appendChild(ul.work_li); - list_set_scrollbar(ul.ul); - - if (name !== undefined) - ul.work_name_input.value = name; - ul.state = UL_STATE.ADDING_ENTRY; -} - -const chbx_components_window = by_id("chbx_components_window"); - -function bag_components() -{ - chbx_components_window.classList.remove("hide"); - radio_components_window.classList.add("hide"); - - for (let li of chbx_components_ul.children) { - let chbx = li.firstElementChild.firstElementChild; - chbx.checked = false; - } -} - -function commit_bag_components() -{ - let selected = []; - - for (let li of chbx_components_ul.children) { - let chbx = li.firstElementChild.firstElementChild; - if (!chbx.checked) - continue; - - selected.push([li.getAttribute("data-prefix"), - li.getAttribute("data-name")]); - } - - add_bag_components(selected); - cancel_components(); -} - -const radio_components_window = by_id("radio_components_window"); -var radio_component_none_input = by_id("radio_component_none_input"); - -function page_components() -{ - radio_components_window.classList.remove("hide"); - chbx_components_window.classList.add("hide"); - - radio_component_none_input.checked = true; - - let components = work_page_li_components(); - if (components === undefined) - return; - - let [prefix, item] = components; - let li = by_id(radio_li_id(prefix, item)); - - if (li === null) - radio_component_none_input.checked = false; - else - li.firstElementChild.firstElementChild.checked = true; -} - -function commit_page_components() -{ - let components = null; - - for (let li of radio_components_ul.children) { - let radio = li.firstElementChild.firstElementChild; - if (!radio.checked) - continue; - - components = [li.getAttribute("data-prefix"), - li.getAttribute("data-name")]; - - if (radio.id === "radio_component_none_input") - components = undefined; - - break; - } - - if (components !== null) - set_page_components(components); - cancel_components(); -} - -function cancel_components() -{ - chbx_components_window.classList.add("hide"); - radio_components_window.classList.add("hide"); -} - -const UL_STATE = { - EDITING_ENTRY : 0, - ADDING_ENTRY : 1, - IDLE : 2 -}; - -const ul_by_prefix = { - [TYPE_PREFIX.REPO] : { - ul : by_id("repos_ul"), - work_li : by_id("work_repo_li"), - work_name_input : by_id("repo_url_field"), - reset_work_li : reset_work_repo_li, - get_work_li_data : work_repo_li_data, - state : UL_STATE.IDLE, - edited_item : undefined, - }, - [TYPE_PREFIX.PAGE] : { - ul : by_id("pages_ul"), - work_li : by_id("work_page_li"), - work_name_input : by_id("page_url_field"), - reset_work_li : reset_work_page_li, - get_work_li_data : work_page_li_data, - select_components : page_components, - commit_components : commit_page_components, - state : UL_STATE.IDLE, - edited_item : undefined, - }, - [TYPE_PREFIX.BAG] : { - ul : by_id("bags_ul"), - work_li : by_id("work_bag_li"), - work_name_input : by_id("bag_name_field"), - reset_work_li : reset_work_bag_li, - get_work_li_data : work_bag_li_data, - select_components : bag_components, - commit_components : commit_bag_components, - state : UL_STATE.IDLE, - edited_item : undefined, - }, - [TYPE_PREFIX.SCRIPT] : { - ul : by_id("scripts_ul"), - work_li : by_id("work_script_li"), - work_name_input : by_id("script_name_field"), - reset_work_li : reset_work_script_li, - get_work_li_data : work_script_li_data, - state : UL_STATE.IDLE, - edited_item : undefined, - } -} - -/* - * Newer browsers could utilise `text' method of File objects. - * Older ones require FileReader. - */ - -function _read_file(file, resolve, reject) -{ - let reader = new FileReader(); - - reader.onload = () => resolve(reader.result); - reader.onerror = () => reject(reader.error); - reader.readAsText(file); -} - -function read_file(file) -{ - return new Promise((resolve, reject) => - _read_file(file, resolve, reject)); -} - -const url_regex = /^[a-z0-9]+:\/\/[^/]+\.[^/]{2,}(\/[^?#]*)?$/; -const empty_regex = /^$/; - -const settings_schema = [ - [{}, "matchentry", "minentries", 1, - new RegExp(`^${TYPE_PREFIX.SCRIPT}`), { - /* script data */ - "url": ["optional", url_regex, "or", empty_regex], - "sha256": ["optional", matchers.sha256, "or", empty_regex], - "text": ["optional", "string"] - }, - new RegExp(`^${TYPE_PREFIX.BAG}`), [ - "optional", - [matchers.component, "repeat"], - "default", undefined - ], - new RegExp(`^${TYPE_PREFIX.PAGE}`), { - /* page data */ - "components": ["optional", matchers.component] - }], "repeat" -]; - -const import_window = by_id("import_window"); -let import_frame; - -async function import_from_file(event) -{ - let files = event.target.files; - if (files.length < 1) - return; - - import_window.classList.remove("hide"); - import_frame.show_loading(); - - try { - const file = await read_file(files[0]); - var result = parse_json_with_schema(settings_schema, file); - } catch(e) { - import_frame.show_error("Bad file :(", "" + e); - return; - } - - import_frame.show_selection(result); -} - -const file_opener_form = by_id("file_opener_form"); - -function hide_import_window() -{ - import_window.classList.add("hide"); - - /* - * Reset file . Without this, a second attempt to import the same - * file would result in "change" event not happening on element. - */ - file_opener_form.reset(); -} - -async function initialize_import_facility() -{ - let import_but = by_id("import_but"); - let file_opener = by_id("file_opener"); - - import_but.addEventListener("click", () => file_opener.click()); - file_opener.addEventListener("change", import_from_file); - - import_frame = await get_import_frame(); - import_frame.onclose = hide_import_window; - import_frame.style_table("has_bottom_line", "always_scrollbar", - "has_upper_line", "tight_table"); -} - -/* - * If url has a target appended, e.g. - * chrome-extension://hnhmbnpohhlmhehionjgongbnfdnabdl/html/options.html#smyhax - * that target will be split into prefix and item name (e.g. "s" and "myhax") - * and editing of that respective item will be started. - * - * We don't need to worry about the state of the page (e.g. some editing being - * in progress) in jump_to_item() - this function is called at the beginning, - * together with callbacks being assigned to buttons, so it is safe to assume - * lists are initialized with items and page is in its virgin state with regard - * to everything else. - */ -function jump_to_item(url_with_item) -{ - const [dummy1, base_url, dummy2, target] = - /^([^#]*)(#(.*))?$/i.exec(url_with_item); - if (target === undefined) - return; - - const prefix = target.substring(0, 1); - - if (!list_prefixes.includes(prefix)) { - history.replaceState(null, "", base_url); - return; - } - - by_id(`show_${TYPE_NAME[prefix]}s`).checked = true; - edit_item(prefix, decodeURIComponent(target.substring(1))); -} - -async function main() -{ - storage = await get_remote_storage(); - - for (let prefix of list_prefixes) { - for (let item of storage.get_all_names(prefix).sort()) { - add_li(prefix, item, true); - add_chbx_li(prefix, item); - add_radio_li(prefix, item); - } - - let name = TYPE_NAME[prefix]; - - let add_but = by_id(`add_${name}_but`); - let discard_but = by_id(`discard_${name}_but`); - let save_but = by_id(`save_${name}_but`); - - add_but.addEventListener("click", () => add_new_item(prefix)); - discard_but.addEventListener("click", () => cancel_work(prefix)); - save_but.addEventListener("click", () => save_work(prefix)); - - if ([TYPE_PREFIX.REPO, TYPE_PREFIX.SCRIPT].includes(prefix)) - continue; - - let ul = ul_by_prefix[prefix]; - - let commit_components_but = by_id(`commit_${name}_components_but`); - let cancel_components_but = by_id(`cancel_${name}_components_but`); - let select_components_but = by_id(`select_${name}_components_but`); - - commit_components_but - .addEventListener("click", ul.commit_components); - select_components_but - .addEventListener("click", ul.select_components); - cancel_components_but.addEventListener("click", cancel_components); - } - - jump_to_item(document.URL); - - storage.add_change_listener(handle_change); - - await initialize_import_facility(); -} - -function handle_change(change) -{ - if (change.old_val === undefined) { - add_li(change.prefix, change.item); - add_chbx_li(change.prefix, change.item); - add_radio_li(change.prefix, change.item); - - return; - } - - if (change.new_val !== undefined) - return; - - let ul = ul_by_prefix[change.prefix]; - if (ul.state === UL_STATE.EDITING_ENTRY && - ul.edited_item === change.item) { - ul.state = UL_STATE.ADDING_ENTRY; - return; - } - - let uls_creators = [[ul.ul, item_li_id]]; - - if ([TYPE_PREFIX.BAG, TYPE_PREFIX.SCRIPT].includes(change.prefix)) { - uls_creators.push([chbx_components_ul, chbx_li_id]); - uls_creators.push([radio_components_ul, radio_li_id]); - } - - for (let [components_ul, id_creator] of uls_creators) { - let li = by_id(id_creator(change.prefix, change.item)); - components_ul.removeChild(li); - list_set_scrollbar(components_ul); - } -} - -main(); diff --git a/html/popup.html b/html/popup.html index ad6c258..bb30425 100644 --- a/html/popup.html +++ b/html/popup.html @@ -55,10 +55,10 @@ #info_form, #unprivileged_page_info { display: grid; grid-template-columns: auto; - text-align: center; } #info_form * { + text-align: center; white-space: nowrap; text-overflow: ellipsis; overflow-x: hidden; diff --git a/html/settings.html b/html/settings.html index ce19e55..7abb870 100644 --- a/html/settings.html +++ b/html/settings.html @@ -33,7 +33,6 @@ Haketilo options #LOADCSS html/reset.css #LOADCSS html/base.css -#LOADCSS html/table.css #LOADCSS html/grid.css