diff options
Diffstat (limited to 'html')
-rw-r--r-- | html/DOM_helpers.js | 23 | ||||
-rw-r--r-- | html/MOZILLA_scrollbar_fix.css | 46 | ||||
-rw-r--r-- | html/back_button.css | 50 | ||||
-rw-r--r-- | html/base.css | 69 | ||||
-rw-r--r-- | html/default_blocking_policy.html | 18 | ||||
-rw-r--r-- | html/default_blocking_policy.js | 47 | ||||
-rw-r--r-- | html/display-panel.html | 374 | ||||
-rw-r--r-- | html/display-panel.js | 191 | ||||
-rw-r--r-- | html/import_frame.html | 45 | ||||
-rw-r--r-- | html/import_frame.js | 73 | ||||
-rw-r--r-- | html/options.html | 421 | ||||
-rw-r--r-- | html/options_main.js | 75 | ||||
-rw-r--r-- | html/reset.css | 49 | ||||
-rw-r--r-- | html/table.css | 45 |
14 files changed, 1152 insertions, 374 deletions
diff --git a/html/DOM_helpers.js b/html/DOM_helpers.js index 2bff966..01e2be9 100644 --- a/html/DOM_helpers.js +++ b/html/DOM_helpers.js @@ -10,9 +10,27 @@ function by_id(id) return document.getElementById(id); } +const known_templates = new Map(); + +function get_template(template_id) +{ + let template = known_templates.get(template_id) || null; + if (template) + return template; + + for (const template_node of document.getElementsByTagName("TEMPLATE")) { + template = template_node.content.getElementById(template_id); + if (template) + break; + } + + known_templates.set(template_id, template); + return template; +} + function clone_template(template_id) { - const clone = document.getElementById(template_id).cloneNode(true); + const clone = get_template(template_id).cloneNode(true); const result_object = {}; const to_process = [clone]; @@ -24,7 +42,7 @@ function clone_template(template_id) result_object[template_key] = element; element.removeAttribute("id"); - element.removeAttribute("template_key"); + element.removeAttribute("data-template"); for (const child of element.children) to_process.push(child); @@ -36,6 +54,7 @@ function clone_template(template_id) /* * EXPORTS_START * EXPORT by_id + * EXPORT get_template * EXPORT clone_template * EXPORTS_END */ diff --git a/html/MOZILLA_scrollbar_fix.css b/html/MOZILLA_scrollbar_fix.css new file mode 100644 index 0000000..5feb7c3 --- /dev/null +++ b/html/MOZILLA_scrollbar_fix.css @@ -0,0 +1,46 @@ +/** + * Hachette + * Hacky fix for vertical scrollbar width being included in child's width. + * + * Copyright (C) 2021 Wojtek Kosior + * Redistribution terms are gathered in the `copyright' file. + */ + +/* + * 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/back_button.css b/html/back_button.css new file mode 100644 index 0000000..1ddc5da --- /dev/null +++ b/html/back_button.css @@ -0,0 +1,50 @@ +/** + * part of Hachette + * Style for a "back" button with a CSS arrow image. + * + * Copyright (C) 2021 Wojtek Kosior + * Redistribution terms are gathered in the `copyright' file. + */ + +.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/base.css b/html/base.css index 2256833..df52f7c 100644 --- a/html/base.css +++ b/html/base.css @@ -6,10 +6,40 @@ * Redistribution terms are gathered in the `copyright' file. */ +body { + font-family: sans-serif; + background-color: #f0f0f0; + color: #555; + overflow: auto; +} + +.bold, h2 { + font-weight: bold; +} + +h2 { + margin: 8px; + font-size: 120%; +} + +h3 { + padding: 5px; + font-size: 108%; + text-shadow: 0 0 0 #454; +} + +textarea { + font-family: monospace; +} + input[type="checkbox"], input[type="radio"], .hide { display: none; } +.camouflage { + visibility: hidden; +} + .show_next:not(:checked)+* { display: none; } @@ -32,6 +62,10 @@ button, .button { display: inline-block; padding: 6px 12px; margin: 2px 0px; + -moz-user-select: none; + user-select: none; + cursor: pointer; + font: 400 15px sans-serif; } button.slimbutton, .button.slimbutton { @@ -42,3 +76,38 @@ button.slimbutton, .button.slimbutton { button:hover, .button:hover { box-shadow: 0 6px 8px 0 rgba(0,0,0,0.24), 0 17px 50px 0 rgba(0,0,0,0.19); } + +aside { + background: #3f8dc6; + margin: 5px 0; + padding: 0.3em 1em; + border-radius: 3px; + color: #fff; +} + +textarea: { + resize: none; +} + +.has_bottom_line::after, .has_upper_line::before { + content: ""; + display: block; + height: 8px; + background: linear-gradient(transparent, #555); +} + +.has_bottom_line::after { + background: linear-gradient(#555, transparent); +} + +.has_bottom_thin_line { + border-bottom: dashed #4CAF50 1px; +} + +.has_upper_thin_line { + border-top: dashed #4CAF50 1px; +} + +.nowrap { + white-space: nowrap; +} diff --git a/html/default_blocking_policy.html b/html/default_blocking_policy.html new file mode 100644 index 0000000..50c19ca --- /dev/null +++ b/html/default_blocking_policy.html @@ -0,0 +1,18 @@ +<!-- + Copyright (C) 2021 Wojtek Kosior + Redistribution terms are gathered in the `copyright' file. + + This is not a standalone page. This file is meant to be imported into other + HTML code. + --> +<style> + #blocking_policy_div { + line-height: 2em; + } +</style> +<span id="blocking_policy_span"> + Default policy for unmatched pages is to + <span id="current_policy_span" class="bold"></span> + their own scripts. + <button id="toggle_policy_but">Toggle policy</button> +</span> diff --git a/html/default_blocking_policy.js b/html/default_blocking_policy.js new file mode 100644 index 0000000..2f49bac --- /dev/null +++ b/html/default_blocking_policy.js @@ -0,0 +1,47 @@ +/** + * part of Hachette + * Default policy dialog logic. + * + * Copyright (C) 2021 Wojtek Kosior + * Redistribution terms are gathered in the `copyright' file. + */ + +/* + * IMPORTS_START + * IMPORT by_id + * IMPORT light_storage + * IMPORT observables + * IMPORTS_END + */ + +/* + * Used with `default_blocking_policy.html' to allow user to choose whether to + * block scripts globally or not. + */ + +const blocking_policy_span = by_id("blocking_policy_span"); +const current_policy_span = by_id("current_policy_span"); +const toggle_policy_but = by_id("toggle_policy_but"); + +let policy_observable; + +const update_policy = + allowed => current_policy_span.textContent = allowed ? "allow" : "block"; +const toggle_policy = + () => light_storage.set_var("default_allow", !policy_observable.value); + +async function init_default_policy_dialog() +{ + policy_observable = await light_storage.observe_var("default_allow"); + update_policy(policy_observable.value); + observables.subscribe(policy_observable, update_policy); + + toggle_policy_but.addEventListener("click", toggle_policy); + blocking_policy_span.classList.remove("hide"); +} + +/* + * EXPORTS_START + * EXPORT init_default_policy_dialog + * EXPORTS_END + */ diff --git a/html/display-panel.html b/html/display-panel.html index 1b9c77b..3ed1b7a 100644 --- a/html/display-panel.html +++ b/html/display-panel.html @@ -7,119 +7,337 @@ <head> <meta charset="utf-8"/> <title>Hachette - page settings</title> + <link type="text/css" rel="stylesheet" href="reset.css" /> <link type="text/css" rel="stylesheet" href="base.css" /> + <link type="text/css" rel="stylesheet" href="back_button.css" /> + <link type="text/css" rel="stylesheet" href="table.css" /> + <link type="text/css" rel="stylesheet" href="MOZILLA_scrollbar_fix.css" /> <style> body { - width: 300px; - height: 300px; + width: max-content; + width: -moz-fit-content; } - ul { - padding-inline-start: 15px; + .top>h2 { + padding-left: calc(0.8*3.2em - 8px); } - .bold { - font-weight: bold; + .top { + line-height: calc(0.8*3.6em - 16px); + } + + #main_view>.top>h2 { + padding-left: 0; + max-width: 550px } - .unroll_chbx:not(:checked)+*+label span.triangle:first-child+span.triangle, - .unroll_chbx:checked+*+label span.triangle:first-child, - .unroll_chbx:not(:checked)+*, - .unroll_chbx:not(:checked)+*+label+* { + .unroll_chbx:not(:checked)+div>:not(:first-child) { display: none; } + .unroll_triangle { + height: 1em; + width: 1em; + display: inline-block; + } + + .unroll_triangle::after { + content: ""; + width: 0.6em; + height: 0.6em; + background: linear-gradient(-45deg, currentColor 50%, transparent 50%); + display: block; + position: relative; + transform: rotate(-45deg); + top: 0.3em; + } + + .unroll_chbx:checked+div>:first-child .unroll_triangle::after { + transform: rotate(45deg); + left: 0.2em; + top: 0.2em; + } + + .unroll_chbx:checked+div>:first-child .unroll_block { + display: block; + } + + .unroll_chbx:checked+div>:first-child { + line-height: 1.4em; + } + + .l2_ul { + border-left: solid #454 5px; + } + + .l1_li { + margin-top: 0.3em; + margin-bottom: 0.3em; + } + + .l1_li>div { + padding: 0.3em 0.3em 0.3em 0; + } + + .l2_li { + padding: 0.3em; + } + + #container_for_injected>*:nth-child(odd), + .l2_li:nth-child(odd) { + background-color: #e5e5e5; + } + #container_for_injected>#none_injected:not(:last-child) { display: none; } + + #page_url_heading>span { + display: inline-block; + } + + .back_button { + position: fixed; + z-index: 1; + top: 0; + left: 0; + /* The following scales the entire button. */ + font-size: 80%; + } + + #show_main_view_radio:checked~.back_button { + margin-left: -3.2em; + } + + #show_main_view_radio:not(:checked)~.back_button { + transition: all 0.2s ease-out; + } + + pre { + font-family: monospace; + background-color: white; + padding: 1px 5px; + } + + .matched_pattern { + font-weight: bold; + } + + tr.matched_pattern~tr { + color: #777; + font-size: 90%; + } + + .padding_inline { + padding-left: 5px; + padding-right: 5px; + } + + .padding_top { + padding-top: 5px; + } + + .header { + padding-bottom: 0.3em; + margin-bottom: 0.5em; + text-align: center; + } + + .middle { + margin-top: 0.5em; + margin-bottom: 0.5em; + } + + .footer { + padding-top: 0.3em; + margin-top: 0.5em; + text-align: center; + } + + .active_setting_table { + margin-bottom: 0.5em; + } + + .active_setting_table td { + padding: 5px; + vertical-align: middle; + } </style> </head> <body> - <!-- The invisible div below is for elements that will be cloned. --> - <div class="hide"> - <li id="pattern_li_template"> - <span></span> - <button>View in settings</button> - </li> - <li id="query_match_li_template" class="queried_pattern_match" data-template="li"> + <template> + <tr id="pattern_entry" class="nowrap" data-template="entry"> + <td data-template="name"></td> + <td> + <div class="button" data-template="button">Add setting</div> + </td> + </tr> + + <li id="query_match_li" class="l2_li" data-template="li"> <div> <span>pattern:</span> <span class="bold" data-template="pattern"></span> - <button data-template="btn">Install</button> + <label class="button slimbutton" for="show_install_view_radio" data-template="btn"> + Install + </label> </div> - <div id="unrollable_component_template" data-template="unroll_container"> - <span data-template="component_label">payload:</span> + <div id="unrollable_component" data-template="unroll_container"> <input type="checkbox" class="unroll_chbx" data-template="chbx"></input> - <br data-template="br"/> - <label class="bold" data-template="lbl"> - <span data-template="triangle"> - <span class="triangle">⏵</span> - <span class="triangle">⏷</span> + <div> + <span>payload: + <label class="bold unroll_block" data-template="lbl"> + <div data-template="triangle" class="unroll_triangle"></div> + <span data-template="payload"></span> + </label> </span> - <span data-template="component"></span> + <div data-template="unroll"></div> + </div> + </div> + </li> + + <div id="injected_script" data-template="div"> + <input type="checkbox" class="unroll_chbx" data-template="chbx"></input> + <div> + <label data-template="lbl"> + <h3><div class="unroll_triangle"></div> script</h3> </label> - <div data-template="unroll"></div> + <pre class="has_bottom_thin_line has_upper_thin_line" data-template="script_contents"></pre> + </div> + </div> + + <div id="multi_repos_query_result" data-template="div"> + Results for <span class="bold" data-template="url_span"></span> + <ul class="l1_ul" data-template="ul"></ul> + </div> + + <li id="single_repo_query_result" class="l1_li" data-template="li"> + <div> + From <span class="bold" data-template="repo_url"></span> </div> </li> - </div> - <input id="show_install_view_chbx" type="checkbox" class="show_hide_next2"></input> + <ul id="result_patterns_list" class="l2_ul" data-template="ul"> + </ul> + </template> + + <input id="show_install_view_radio" type="radio" class="show_next" name="current_view"></input> <div id="install_view"> - <IMPORT html/import_frame.html /> - <!-- - <div id="install_status"></div> - <label for="show_install_chbx" class="bold">Cancel install</label> - <button id="commit_install_but">Commit install</button> - --> + <div class="top has_bottom_line"><h2> Site modifiers install </h2></div> + <div class="padding_inline"> + <IMPORT html/import_frame.html /> + </div> </div> - <div id="main_view"> - <h2 id="page_url_heading"></h2> - - <input id="show_privileged_notice_chbx" type="checkbox" class="show_next"></input> - <h3>Privileged page</h3> - - <input id="show_page_state_chbx" type="checkbox" class="show_next"></input> - <div> - <input id="possible_patterns_chbx" type="checkbox" class="unroll_chbx"></input> - <span></span> - <label for="possible_patterns_chbx"> - <h3> - <span class="triangle">⏵</span> - <span class="triangle">⏷</span> - Possible patterns - </h3> - </label> - <ul id="possible_patterns"></ul> - - <input id="connected_chbx" type="checkbox" class="show_hide_next2"></input> + + <input id="show_injected_view_radio" type="radio" class="show_next" name="current_view"></input> + <div id="injected_view"> + <div class="top has_bottom_line"><h2>Injected scripts</h2></div> + <div id="container_for_injected"> + <span id="none_injected">None</span> + </div> + </div> + + <input id="show_patterns_view_radio" type="radio" class="show_next" name="current_view"></input> + <div> + <div class="top has_bottom_line"><h2>Possible patterns for this page</h2></div> + <div class="padding_inline"> + <aside> + Patterns higher are more specific and override the ones below. + </aside> + </div> + <div class="table_wrapper firefox_scrollbars_hacky_fix"> <div> - Matched pattern: <span id="pattern" class="bold">...</span> - <button id="view_pattern" class="hide"> - View in settings - </button> - <br/> - Blocked: <span id="blocked" class="bold">...</span> - <br/> - Payload: <span id="payload" class="bold">...</span> - <button id="view_payload" class="hide"> - View in settings - </button> - <h3>Injected</h3> - <div id="container_for_injected"> - <span id="none_injected">None</span> + <table> + <tbody id="possible_patterns"> + </tbody> + </table> + </div> + </div> + <div class="padding_inline padding_top has_upper_thin_line firefox_scrollbars_hacky_fix has_inline_content"> + <span class="nowrap"> + <IMPORT html/default_blocking_policy.html /> + </span> + </div> + </div> + + <input id="show_queried_view_radio" type="radio" class="show_next" name="current_view"></input> + <div> + <div class="top has_bottom_line"><h2>Queried from repositories</h2></div> + <div id="container_for_repo_responses" class="padding_inline"> + </div> + </div> + + <input id="show_main_view_radio" type="radio" class="show_next" name="current_view" checked></input> + <div id="main_view"> + <div class="top has_bottom_line"><h2 id="page_url_heading"></h2></div> + <h3 id="privileged_notice" class="middle hide">Privileged page</h3> + + <div id="page_state" class="hide"> + <div class="header padding_inline has_bottom_thin_line"> + <label for="show_patterns_view_radio" class="button"> + Edit settings for this page + </label> + </div> + <div class="middle padding_inline"> + <input id="connected_chbx" type="checkbox" class="show_hide_next2"></input> + <div> + <table class="active_setting_table"> + <tbody> + <tr class="nowrap"> + <td>Matched pattern:</td> + <td id="pattern" class="bold">...</td> + <td> + <button id="view_pattern" class="hide"> + View in settings + </button> + </td> + </tr> + <tr class="nowrap"> + <td>Scripts blocked:</td> + <td id="blocked" class="bold">...</td> + <td></td> + </tr> + <tr class="nowrap"> + <td>Injected payload:</td> + <td id="payload" class="bold">...</td> + <td id="payload_buttons" class="hide"> + <button id="view_payload"> View in settings </button> + <br/> + <label id="view_injected" class="button" for="show_injected_view_radio"> + View injected scripts + </label> + </td> + </tr> + <tr> + <td id="content_type" colspan="3" class="hide"> + This is a non-HTML page. Chosen payload will not be injected. + </td> + </tr> + </tbody> + </table> + <label id="query_pattern" for="show_queried_view_radio" class="button"> + Install scripts for this page + </label> </div> - <input id="query_started_chbx" type="checkbox" class="show_hide_next2"></input> - <div id="container_for_repo_responses"> - <h3>Queried from repositories</h3> + <div> + <h3> + Connecting to content script..<span id="loading_point">.</span> + </h3> + <aside id="reload_notice"> + Try reloading the page. + </aside> </div> - <button id="query_pattern"> - Search for matching patterns - </button> </div> - <h3>Trying to connect..<input id="loading_chbx" type="checkbox" class="show_next"></input><span>.</span></h3> </div> - <button id="settings_but" type="button" style="margin-top: 20px;">Settings</button> - </div>_POPUPSCRIPTS_ + <div class="footer padding_inline has_upper_thin_line"> + <button id="settings_but" type="button"> + Open Hachette settings + </button> + </div> + </div> + + <div class="has_upper_line"></div> + + <label for="show_main_view_radio" class="back_button"><div></div></label>_POPUPSCRIPTS_ </body> </html> diff --git a/html/display-panel.js b/html/display-panel.js index 2539ded..66e51a6 100644 --- a/html/display-panel.js +++ b/html/display-panel.js @@ -14,13 +14,15 @@ *** temporarily, before all storage access gets reworked. * IMPORT get_remote_storage * IMPORT get_import_frame + * IMPORT init_default_policy_dialog * IMPORT query_all * IMPORT CONNECTION_TYPE * IMPORT is_privileged_url * IMPORT TYPE_PREFIX * IMPORT nice_name * IMPORT open_in_settings - * IMPORT for_each_possible_pattern + * IMPORT url_matches + * IMPORT each_url_pattern * IMPORT by_id * IMPORT clone_template * IMPORTS_END @@ -29,6 +31,16 @@ let storage; let tab_url; +/* Force popup <html>'s reflow on stupid Firefox. */ +if (is_mozilla) { + const reflow_forcer = + () => document.documentElement.style.width = "-moz-fit-content"; + for (const radio of document.querySelectorAll('[name="current_view"]')) + radio.addEventListener("change", reflow_forcer); +} + +const show_queried_view_radio = by_id("show_queried_view_radio"); + const tab_query = {currentWindow: true, active: true}; async function get_current_tab() @@ -47,8 +59,21 @@ async function get_current_tab() } const page_url_heading = by_id("page_url_heading"); -const show_privileged_notice_chbx = by_id("show_privileged_notice_chbx"); -const show_page_state_chbx = by_id("show_page_state_chbx"); +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 <span>'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() { @@ -60,79 +85,74 @@ async function show_page_activity_info() } tab_url = /^([^?#]*)/.exec(tab.url)[1]; - page_url_heading.textContent = tab_url; + to_spans(tab_url).forEach(s => page_url_heading.append(s)); if (is_privileged_url(tab_url)) { - show_privileged_notice_chbx.checked = true; + privileged_notice.classList.remove("hide"); return; } populate_possible_patterns_list(tab_url); - show_page_state_chbx.checked = true; + page_state.classList.remove("hide"); try_to_connect(tab.id); } -const possible_patterns_ul = by_id("possible_patterns"); -const pattern_li_template = by_id("pattern_li_template"); -pattern_li_template.removeAttribute("id"); +const possible_patterns_list = by_id("possible_patterns"); const known_patterns = new Map(); function add_pattern_to_list(pattern) { - const li = pattern_li_template.cloneNode(true); - li.id = `pattern_li_${known_patterns.size}`; - known_patterns.set(pattern, li.id); + const template = clone_template("pattern_entry"); + template.name.textContent = pattern; - const span = li.firstElementChild; - span.textContent = pattern; - - const button = span.nextElementSibling; const settings_opener = () => open_in_settings(TYPE_PREFIX.PAGE, pattern); - button.addEventListener("click", settings_opener); + template.button.addEventListener("click", settings_opener); - possible_patterns_ul.appendChild(li) + known_patterns.set(pattern, template); + possible_patterns_list.append(template.entry); - return li.id; + return template; } function ensure_pattern_exists(pattern) { - let id = known_patterns.get(pattern); + let entry_object = known_patterns.get(pattern); /* * As long as pattern computation works well, we should never get into this * conditional block. This is just a safety measure. To be removed as part * of a bigger rework when we start taking iframes into account. */ - if (id === undefined) { + if (entry_object === undefined) { console.log(`unknown pattern: ${pattern}`); - id = add_pattern_to_list(pattern); + entry_object = add_pattern_to_list(pattern); } - return id; + return entry_object; } -function set_pattern_li_button_text(li_id, text) +function style_possible_pattern_entry(pattern, exists_in_settings) { - by_id(li_id).firstElementChild.nextElementSibling.textContent = text; + const [text, class_action] = exists_in_settings ? + ["Edit", "add"] : ["Add", "remove"]; + const entry_object = ensure_pattern_exists(pattern); + + entry_object.button.textContent = `${text} setting`; + entry_object.entry.classList[class_action]("matched_pattern"); } function handle_page_change(change) { - const li_id = ensure_pattern_exists(change.item); - if (change.old_val === undefined) - set_pattern_li_button_text(li_id, "Edit in settings"); - if (change.new_val === undefined) - set_pattern_li_button_text(li_id, "Add setting"); + if (url_matches(tab_url, change.item)) + style_possible_pattern_entry(change.item, change.new_val !== undefined); } function populate_possible_patterns_list(url) { - for_each_possible_pattern(url, add_pattern_to_list); + for (const pattern of each_url_pattern(url)) + add_pattern_to_list(pattern); - for (const [pattern, settings] of query_all(storage, url)) { - set_pattern_li_button_text(ensure_pattern_exists(pattern), - "Edit in settings"); - } + 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]); } @@ -158,17 +178,16 @@ function try_to_connect(tab_id) setTimeout(() => monitor_connecting(tab_id), 1000); } -const query_started_chbx = by_id("query_started_chbx"); - -function start_querying_repos(port) +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]); - query_started_chbx.checked = true; } -const loading_chbx = by_id("loading_chbx"); +const loading_point = by_id("loading_point"); +const reload_notice = by_id("reload_notice"); function handle_disconnect(tab_id, button_cb) { @@ -182,7 +201,9 @@ function handle_disconnect(tab_id, button_cb) if (connected_chbx.checked) return; - loading_chbx.checked = !loading_chbx.checked; + loading_point.classList.toggle("camouflage"); + reload_notice.classList.remove("hide"); + setTimeout(() => try_to_connect(tab_id), 1000); } @@ -196,7 +217,8 @@ function monitor_connecting(tab_id) else return; - loading_chbx.checked = !loading_chbx.checked; + loading_point.classList.toggle("camouflage"); + reload_notice.classList.remove("hide"); try_to_connect(tab_id); } @@ -204,11 +226,16 @@ 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; @@ -218,7 +245,6 @@ function handle_activity_report(message) if (type === "settings") { let [pattern, settings] = data; - settings = settings || {}; blocked_span.textContent = settings.allow ? "no" : "yes"; if (pattern) { @@ -229,30 +255,32 @@ function handle_activity_report(message) view_pattern_but.addEventListener("click", settings_opener); } else { pattern_span.textContent = "none"; + blocked_span.textContent = blocked_span.textContent + " (default)"; } const components = settings.components; if (components) { payload_span.textContent = nice_name(...components); + payload_buttons_div.classList.remove("hide"); const settings_opener = () => open_in_settings(...components); - view_payload_but.classList.remove("hide"); view_payload_but.addEventListener("click", settings_opener); } else { payload_span.textContent = "none"; } } if (type === "script") { - const h4 = document.createElement("h4"); - const pre = document.createElement("pre"); - h4.textContent = "script"; - pre.textContent = data; - - container_for_injected.appendChild(h4); - container_for_injected.appendChild(pre); + 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 === "content_type") { + if (!/html/.test(data)) + content_type_cell.classList.remove("hide"); } if (type === "repo_query_action") { - query_started_chbx.checked = true; - const key = data.prefix + data.item; const results = queried_items.get(key) || {}; Object.assign(results, data.results); @@ -272,35 +300,26 @@ const results_lists = new Map(); function create_results_list(url) { - const list_div = document.createElement("div"); - const list_head = document.createElement("h4"); - const list = document.createElement("ul"); + const cloned_template = clone_template("multi_repos_query_result"); + cloned_template.url_span.textContent = url; + container_for_repo_responses.appendChild(cloned_template.div); - list_head.textContent = url; - list_div.appendChild(list_head); - list_div.appendChild(list); - container_for_repo_responses.appendChild(list_div); + cloned_template.by_repo = new Map(); + results_lists.set(url, cloned_template); - const list_object = {list, by_repo: new Map()}; - - results_lists.set(url, list_object); - - return list_object; + return cloned_template; } function create_result_item(list_object, repo_url, result) { - const result_li = document.createElement("li"); - const repo_url_span = document.createElement("span"); - const result_item = {result_li, appended: null}; - - repo_url_span.textContent = repo_url; - result_li.appendChild(repo_url_span); + const cloned_template = clone_template("single_repo_query_result"); + cloned_template.repo_url.textContent = repo_url; + cloned_template.appended = null; - list_object.list.appendChild(result_li); - list_object.by_repo.set(repo_url, result_item); + list_object.ul.appendChild(cloned_template.li); + list_object.by_repo.set(repo_url, cloned_template); - return result_item; + return cloned_template; } function set_appended(result_item, element) @@ -308,7 +327,7 @@ function set_appended(result_item, element) if (result_item.appended) result_item.appended.remove(); result_item.appended = element; - result_item.result_li.appendChild(element); + result_item.li.appendChild(element); } function show_message(result_item, text) @@ -331,11 +350,9 @@ function unroll_chbx_first_checked(entry_object) entry_object.chbx.removeEventListener("change", entry_object.unroll_cb); delete entry_object.unroll_cb; - entry_object.unroll.textContent = "preview not implemented..."; + entry_object.unroll.innerHTML = "preview not implemented...<br />(consider contributing)"; } -const show_install_chbx = by_id("show_install_view_chbx"); - let import_frame; let install_target = null; @@ -461,7 +478,6 @@ function record_fetched_install_dep(prefix, item, repo_url, result) function install_clicked(entry_object) { - show_install_chbx.checked = true; import_frame.show_loading(); install_target = { @@ -481,25 +497,24 @@ var max_query_result_id = 0; function show_query_successful_result(result_item, repo_url, result) { - const ul = document.createElement("ul"); - - set_appended(result_item, ul); + 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_template"); + const entry_object = clone_template("query_match_li"); entry_object.pattern.textContent = match.pattern; - ul.appendChild(entry_object.li); + cloned_ul_template.ul.appendChild(entry_object.li); if (!match.payload) { entry_object.payload.textContent = "(none)"; - for (const key of ["chbx", "br", "triangle", "unroll"]) + for (const key of ["chbx", "triangle", "unroll"]) entry_object[key].remove(); continue; } - entry_object.component.textContent = nice_name(...match.payload); + entry_object.payload.textContent = nice_name(...match.payload); const install_cb = () => install_clicked(entry_object); entry_object.btn.addEventListener("click", install_cb); @@ -540,9 +555,11 @@ by_id("settings_but") async function main() { + init_default_policy_dialog(); + storage = await get_remote_storage(); import_frame = await get_import_frame(); - import_frame.onclose = () => show_install_chbx.checked = false; + import_frame.onclose = () => show_queried_view_radio.checked = true; show_page_activity_info(); } diff --git a/html/import_frame.html b/html/import_frame.html index c86c3de..754b289 100644 --- a/html/import_frame.html +++ b/html/import_frame.html @@ -1,13 +1,28 @@ -<div style="display: none;"> - <li id="import_li_template"> - <span></span> - <input type="checkbox" style="display: inline;" checked></input> - <span></span> - </li> -</div> -<h2> Settings import </h2> +<!-- + Copyright (C) 2021 Wojtek Kosior + Redistribution terms are gathered in the `copyright' file. + + This is not a standalone page. This file is meant to be imported into other + HTML code. + --> +<style> + .padding_right { + padding-right: 0.3em; + } +</style> +<template> + <tr id="import_entry" class="nowrap" data-template="entry"> + <td> + <input type="checkbox" style="display: inline;" checked data-template="chbx"></input> + <span data-template="name_span"></span> + </td> + <td class="bold padding_right" data-template="warning"></td> + </tr> +</template> + <input id="import_loading_radio" type="radio" name="import_window_content" class="show_next"></input> <span> Loading... </span> + <input id="import_failed_radio" type="radio" name="import_window_content" class="show_next"></input> <div> <span id="import_errormsg"></span> @@ -15,13 +30,23 @@ <pre id="import_errordetail"></pre> <button id="import_failok_but"> OK </button> </div> + <input id="import_selection_radio" type="radio" name="import_window_content" class="show_next"></input> <div> <button id="check_all_import_but"> Check all </button> <button id="uncheck_all_import_but"> Uncheck all </button> <button id="uncheck_colliding_import_but"> Uncheck existing </button> - <ul id="import_ul"> - </ul> + <aside id="existing_settings_note"> + Settings that would owerwrite existing ones are marked "!". + </aside> + <div id="import_table_wrapper" class="table_wrapper"> + <div> + <table> + <tbody id="import_list"> + </tbody> + </table> + </div> + </div> <button id="commit_import_but"> OK </button> <button id="cancel_import_but"> Cancel </button> </div> diff --git a/html/import_frame.js b/html/import_frame.js index 4075433..c0eb2f0 100644 --- a/html/import_frame.js +++ b/html/import_frame.js @@ -9,6 +9,7 @@ * IMPORTS_START * IMPORT get_remote_storage * IMPORT by_id + * IMPORT clone_template * IMPORT nice_name * IMPORT make_once * IMPORTS_END @@ -16,48 +17,38 @@ let storage; -const import_li_template = by_id("import_li_template"); -import_li_template.removeAttribute("id"); - -function import_li_id(prefix, item) -{ - return `ili_${prefix}_${item}`; -} - -let import_ul = by_id("import_ul"); +let import_list = by_id("import_list"); let import_chbxs_colliding = undefined; +let entry_objects = undefined; let settings_import_map = undefined; -function add_import_li(prefix, name) +function add_import_entry(prefix, name) { - let li = import_li_template.cloneNode(true); - let name_span = li.firstElementChild; - let chbx = name_span.nextElementSibling; - let warning_span = chbx.nextElementSibling; + const cloned_template = clone_template("import_entry"); + Object.assign(cloned_template, {prefix, name}); - li.setAttribute("data-prefix", prefix); - li.setAttribute("data-name", name); - li.id = import_li_id(prefix, name); - name_span.textContent = nice_name(prefix, name); + cloned_template.name_span.textContent = nice_name(prefix, name); if (storage.get(prefix, name) !== undefined) { - import_chbxs_colliding.push(chbx); - warning_span.textContent = "(will overwrite existing setting!)"; + import_chbxs_colliding.push(cloned_template.chbx); + cloned_template.warning.textContent = "!"; } - import_ul.appendChild(li); + import_list.appendChild(cloned_template.entry); + + return cloned_template; } function check_all_imports() { - for (let li of import_ul.children) - li.firstElementChild.nextElementSibling.checked = true; + for (const entry_object of entry_objects) + entry_object.chbx.checked = true; } function uncheck_all_imports() { - for (let li of import_ul.children) - li.firstElementChild.nextElementSibling.checked = false; + for (const entry_object of entry_objects) + entry_object.chbx.checked = false; } function uncheck_colliding_imports() @@ -68,17 +59,13 @@ function uncheck_colliding_imports() function commit_import() { - for (let li of import_ul.children) { - let chbx = li.firstElementChild.nextElementSibling; - - if (!chbx.checked) + for (const entry_object of entry_objects) { + if (!entry_object.chbx.checked) continue; - let prefix = li.getAttribute("data-prefix"); - let name = li.getAttribute("data-name"); - let key = prefix + name; - let value = settings_import_map.get(key); - storage.set(prefix, name, value); + 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(); @@ -105,38 +92,48 @@ function show_error(errormsg, 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_ul.children; + let old_children = import_list.children; while (old_children[0] !== undefined) - import_ul.removeChild(old_children[0]); + 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); - add_import_li(prefix, name); + 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 exports = {show_loading, show_error, show_selection, deactivate}; +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() { diff --git a/html/options.html b/html/options.html index 2246f9a..f2a75e1 100644 --- a/html/options.html +++ b/html/options.html @@ -1,32 +1,18 @@ <!doctype html> <!-- Copyright (C) 2021 Wojtek Kosior - Copyright (C) 2021 Nicholas Johnson Redistribution terms are gathered in the `copyright' file. --> <html> <head> <meta charset="utf-8"/> <title>Hachette options</title> + <link type="text/css" rel="stylesheet" href="reset.css" /> <link type="text/css" rel="stylesheet" href="base.css" /> + <link type="text/css" rel="stylesheet" href="table.css" /> <style> - /* pages list */ - #page_components_ul { - max-height: 80vh; - overflow-y: auto; - } - #page_components_ul li.dragover_li { - border-top: 2px solid blue; - } - #page_components_ul li { - border-top: 2px solid white; - } - li[draggable=true] * { - pointer-events: none; - } - li[draggable=true] label, - li[draggable=true] button { - pointer-events: auto; + body { + width: 100%; } /* tabbed view */ @@ -37,20 +23,38 @@ display: none; } - #show_repos:checked ~ #repos_lbl, - #show_pages:checked ~ #pages_lbl, - #show_bags:checked ~ #bags_lbl, - #show_scripts:checked ~ #scripts_lbl { + #show_repos:checked ~ div #repos_lbl, + #show_pages:checked ~ div #pages_lbl, + #show_bags:checked ~ div #bags_lbl, + #show_scripts:checked ~ div #scripts_lbl { background: #4CAF50; color: white; } - body > div { - border-top: 6px solid #4CAF50; + #tab_heads>* { + font-size: 130%; + padding: 10px; + display: inline-block; + cursor: pointer; } - .tab_head { - display: inline-block; + #tab_heads { + -moz-user-select: none; + user-select: none; + } + + #import_but { + font: unset; + font-size: 130%; + float: right; + margin: 0; + border-radius: 0; + } + + div.tab { + min-width: 50vw; + width: fit-content; + padding-left: 6px; } /* popup window with list of selectable components */ @@ -60,7 +64,7 @@ height: 100vh; left: 0; top: 0; - background-color: rgba(0,0,0,0.4); + background-color: rgba(60,60,60,0.4); z-index: 1; overflow: auto; vertical-align: center; @@ -68,135 +72,264 @@ } .popup_frame { - background-color: white; - width: 50vw; + background-color: #f0f0f0; + margin: 5vh auto; + padding: 15px; + border: solid #333 4px; + border-radius: 15px; + width: -moz-fit-content; + width: fit-content; } - input[type="radio"]:not(:checked)+.import_window_content { - display: none; + .work_li .table_wrapper::before { + background: linear-gradient(#e0f0f0, #555); + } + + .work_li .table_wrapper::after { + background: linear-gradient(#555, #e0f0f0); + } + + .table_wrapper.always_scrollbar>* { + border-left: solid #454 8px; + max-height: 80vh; + overflow-y: scroll; + } + + .table_wrapper .table_wrapper.always_scrollbar>*, + .popup_frame .table_wrapper.always_scrollbar>* { + max-height: 60vh; + } + + .popup_frame .table_wrapper table { + min-width: 30vw; + } + + .popup_frame .table_wrapper { + margin: 0 auto; + } + + td:first-child { + max-width: 70vw; + overflow: hidden; + } + + tr.work_li>td:first-child { + padding-right: 0; + max-width: unset; + } + + tr.work_li>td>div { + background-color: #e0f0f0; + border-left: solid #454 5px; + border-right: solid #454 2px; + padding: 5px 10px; + } + + .form_grid { + display: grid; + grid-template-columns: auto auto; + } + + .form_grid>label { + grid-column: 1 / span 1; + margin-right: 10px; + } + + .form_grid label { + line-height: 34px; /* center text vertically */ + } + + .form_grid>input, .form_grid>span { + grid-column: 2 / span 1; + } + + .form_grid>label[for="script_contents_field"], + .form_grid>* { + grid-column: 1 / span 2; + } + + .form_grid>textarea { + min-width: 70vw; + resize: none; } </style> </head> <body> - <!-- The invisible div below is for elements that will be cloned. --> - <div class="hide"> - <li id="item_li_template"> - <span></span> - <button> Edit </button> - <button> Remove </button> - <button> Export </button> - </li> - <li id="bag_component_li_template"> - <span></span> - <button> Remove </button> - </li> - <li id="chbx_component_li_template"> - <input type="checkbox" style="display: inline;"></input> - <span></span> - </li> - <li id="radio_component_li_template"> - <input type="radio" style="display: inline;" name="page_components"></input> - <span></span> - </li> - </div> + <template> + <tr id="item_li" class="nowrap"> + <td></td> + <td><div class="button"> Edit </div></td> + <td><div class="button"> Remove </div></td> + <td><div class="button"> Export </div></td> + </tr> + <tr id="bag_component_li" class="nowrap"> + <td></td> + <td><div class="button"> Remove </div></td> + </tr> + <tr id="chbx_component_li" class="nowrap"> + <td> + <input type="checkbox" style="display: inline;"></input> + <span></span> + </td> + </tr> + <tr id="radio_component_li" class="nowrap"> + <td> + <input type="radio" style="display: inline;" name="page_components"></input> + <span></span> + </td> + </tr> + </template> <!-- Mind the show_*s ids below - their format is assumed in js code --> <input type="radio" name="tabs" id="show_repos"></input> <input type="radio" name="tabs" id="show_pages" checked></input> <input type="radio" name="tabs" id="show_bags"></input> <input type="radio" name="tabs" id="show_scripts"></input> - <label for="show_repos" id="repos_lbl" - class="tab_head"> Repos </label> - <label for="show_pages" id="pages_lbl" - class="tab_head"> Pages </label> - <label for="show_bags" id="bags_lbl" - class="tab_head"> Bags </label> - <label for="show_scripts" id="scripts_lbl" - class="tab_head"> Scripts </label> - - <div id="repos"> - <ul id="repos_ul"> - <li id="work_repo_li" class="hide"> - <label for="repo_url_field">URL: </label> - <input id="repo_url_field"></input> - <br/> - <button id="save_repo_but" type="button"> Save </button> - <button id="discard_repo_but" type="button"> Cancel </button> - </li> - </ul> + <div id="tab_heads" class="has_bottom_line"> + <label for="show_repos" id="repos_lbl"> Repos </label> + <label for="show_pages" id="pages_lbl"> Pages </label> + <label for="show_bags" id="bags_lbl"> Bags </label> + <label for="show_scripts" id="scripts_lbl"> Scripts </label> + <button id="import_but" style="margin-left: 40px;"> Import </button> + </div> + <div id="repos" class="tab"> + <div class="table_wrapper tight_table has_bottom_line has_upper_line"> + <div> + <table> + <tbody id="repos_ul"> + <tr id="work_repo_li" class="hide work_li"> + <td colspan="4"> + <div class="form_grid"> + <label for="repo_url_field">URL: </label> + <input id="repo_url_field"></input> + <div> + <button id="save_repo_but" type="button"> Save </button> + <button id="discard_repo_but" type="button"> Cancel </button> + </div> + </div> + </td> + </tr> + </tbody> + </table> + </div> + </div> <button id="add_repo_but" type="button"> Add repository </button> </div> - - <div id="pages"> - <ul id="pages_ul"> - <li id="work_page_li" class="hide"> - <label for="page_url_field">URL: </label> - <input id="page_url_field"></input> - <br/> - <label>Payload: </label> - <span id="page_payload"></span> - <button id="select_page_components_but"> - Choose payload - </button> - <br/> - <input id="page_allow_chbx" type="checkbox" style="display: inline;"></input> - <label for="page_allow_chbx">Allow native scripts</label> - <br/> - <button id="save_page_but" type="button"> Save </button> - <button id="discard_page_but" type="button"> Cancel </button> - </li> - </ul> + <div id="pages" class="tab"> + <div class="table_wrapper tight_table has_bottom_line has_upper_line"> + <div> + <table> + <tbody id="pages_ul"> + <tr id="work_page_li" class="hide work_li"> + <td colspan="4"> + <div class="form_grid"> + <label for="page_url_field">URL: </label> + <input id="page_url_field"></input> + <label>Payload: </label> + <span> + <span id="page_payload"></span> + <button id="select_page_components_but"> + Choose payload + </button> + </span> + <div> + <input id="page_allow_chbx" type="checkbox" style="display: inline;"></input> + <label for="page_allow_chbx">Allow native scripts</label> + </div> + <div> + <button id="save_page_but" type="button"> Save </button> + <button id="discard_page_but" type="button"> Cancel </button> + </div> + </div> + </td> + </tr> + </tbody> + </table> + </div> + </div> <button id="add_page_but" type="button"> Add page </button> + <IMPORT html/default_blocking_policy.html /> </div> - - <div id="bags"> - <ul id="bags_ul"> - <li id="work_bag_li" class="hide"> - <label for="bag_name_field"> Name: </label> - <input id="bag_name_field"></input> - <ul id="bag_components_ul"> - <li id="empty_bag_component_li" class="hide"></li> - </ul> - <button id="select_bag_components_but"> - Add scripts - </button> - <br/> - <button id="save_bag_but"> Save </button> - <button id="discard_bag_but"> Cancel </button> - </li> - </ul> + <div id="bags" class="tab"> + <div class="table_wrapper tight_table has_bottom_line has_upper_line"> + <div> + <table> + <tbody id="bags_ul"> + <tr id="work_bag_li" class="hide work_li"> + <td colspan="4"> + <div class="form_grid"> + <label for="bag_name_field"> Name: </label> + <input id="bag_name_field"></input> + <div class="table_wrapper tight_table has_bottom_line has_upper_line"> + <div> + <table> + <tbody id="bag_components_ul"> + <tr id="empty_bag_component_li" class="hide"></tr> + </tbody> + </table> + </div> + </div> + <div> + <button id="select_bag_components_but"> + Add scripts + </button> + </div> + <div> + <button id="save_bag_but"> Save </button> + <button id="discard_bag_but"> Cancel </button> + </div> + </div> + </td> + </tr> + </tbody> + </table> + </div> + </div> <button id="add_bag_but" type="button"> Add bag </button> </div> - - <div id="scripts"> - <ul id="scripts_ul"> - <li id="work_script_li" class="hide"> - <label for="script_name_field"> Name: </label> - <input id="script_name_field"></input> - <br/> - <label for="script_url_field"> URL: </label> - <input id="script_url_field"></input> - <br/> - <label for="script_sha256_field"> sha256: </label> - <input id="script_sha256_field"></input> - <br/> - <label for="script_contents_field"> contents: </label> - <textarea id="script_contents_field" rows="20" cols="80"></textarea> - <br/> - <button id="save_script_but"> Save </button> - <button id="discard_script_but"> Cancel </button> - </li> - </ul> + <div id="scripts" class="tab"> + <div class="table_wrapper tight_table has_bottom_line has_upper_line"> + <div> + <table> + <tbody id="scripts_ul"> + <tr id="work_script_li" class="hide work_li"> + <td colspan="4"> + <div class="form_grid"> + <label for="script_name_field"> Name: </label> + <input id="script_name_field"></input> + <label for="script_url_field"> URL: </label> + <input id="script_url_field"></input> + <label for="script_sha256_field"> SHA256: </label> + <input id="script_sha256_field"></input> + <aside> + Note: URL and SHA256 are ignored if script text is provided. + </aside> + <label for="script_contents_field"> contents: </label> + <textarea id="script_contents_field" rows="20" cols="80"></textarea> + <div> + <button id="save_script_but"> Save </button> + <button id="discard_script_but"> Cancel </button> + </div> + </div> + </td> + </tr> + </tbody> + </table> + </div> + </div> <button id="add_script_but" type="button"> Add script </button> </div> - <button id="import_but" style="margin-top: 40px;"> Import </button> - <div id="chbx_components_window" class="hide popup" position="absolute"> <div class="popup_frame"> - <ul id="chbx_components_ul"> - - </ul> + <div class="table_wrapper tight_table has_bottom_line has_upper_line"> + <div> + <table> + <tbody id="chbx_components_ul"> + </tbody> + </table> + </div> + </div> <button id="commit_bag_components_but"> Add </button> <button id="cancel_bag_components_but"> Cancel </button> </div> @@ -204,13 +337,20 @@ <div id="radio_components_window" class="hide popup" position="absolute"> <div class="popup_frame"> - <ul id="radio_components_ul"> - - <li id="radio_component_none_li"> - <input id="radio_component_none_input" type="radio" style="display: inline;" name="page_components"></input> - <span>(None)</span> - </li> - </ul> + <div class="table_wrapper tight_table always_scrollbar has_bottom_line has_upper_line"> + <div> + <table> + <tbody id="radio_components_ul"> + <tr id="radio_component_none_li"> + <td> + <input id="radio_component_none_input" type="radio" style="display: inline;" name="page_components"></input> + <span>(None)</span> + </td> + </tr> + </tbody> + </table> + </div> + </div> <button id="commit_page_components_but"> Choose </button> <button id="cancel_page_components_but"> Cancel </button> </div> @@ -218,6 +358,7 @@ <div id="import_window" class="hide popup" position="absolute"> <div class="popup_frame"> + <h2> Settings import </h2> <IMPORT html/import_frame.html /> </div> </div> diff --git a/html/options_main.js b/html/options_main.js index 830c860..2f4f154 100644 --- a/html/options_main.js +++ b/html/options_main.js @@ -13,24 +13,36 @@ * IMPORT list_prefixes * IMPORT nice_name * IMPORT parse_json_with_schema + * IMPORT get_template * IMPORT by_id * IMPORT matchers * IMPORT get_import_frame + * IMPORT init_default_policy_dialog * IMPORTS_END */ var storage; -const item_li_template = by_id("item_li_template"); -const bag_component_li_template = by_id("bag_component_li_template"); -const chbx_component_li_template = by_id("chbx_component_li_template"); -const radio_component_li_template = by_id("radio_component_li_template"); +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}`; @@ -65,11 +77,17 @@ function add_li(prefix, item, at_the_end=false) continue; ul.ul.insertBefore(li, element); - return; + break; } } + if (!li.parentElement) { + if (ul.work_li !== ul.ul.lastElementChild) + ul.ul.appendChild(li); + else + ul.work_li.before(li); + } - ul.ul.appendChild(li); + list_set_scrollbar(ul.ul); } const chbx_components_ul = by_id("chbx_components_ul"); @@ -97,12 +115,13 @@ function add_chbx_li(prefix, name) li.setAttribute("data-prefix", prefix); li.setAttribute("data-name", name); - let chbx = li.firstElementChild; + 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"); @@ -117,12 +136,13 @@ function add_radio_li(prefix, name) li.setAttribute("data-prefix", prefix); li.setAttribute("data-name", name); - let radio = li.firstElementChild; + let radio = li.firstElementChild.firstElementChild; let span = radio.nextElementSibling; span.textContent = nice_name(prefix, name); - radio_components_ul.insertBefore(li, radio_component_none_li); + radio_component_none_li.before(li); + list_set_scrollbar(radio_components_ul); } /* Used to reset edited repo. */ @@ -190,6 +210,13 @@ function work_page_li_data(ul) 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) { @@ -198,15 +225,17 @@ function add_bag_components(components) 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", () => - bag_components_ul.removeChild(li)); + 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. */ @@ -218,8 +247,7 @@ function reset_work_bag_li(ul, item, components) let old_components_ul = bag_components_ul; bag_components_ul = old_components_ul.cloneNode(false); - ul.work_li.insertBefore(bag_components_ul, old_components_ul); - ul.work_li.removeChild(old_components_ul); + old_components_ul.replaceWith(bag_components_ul); add_bag_components(components); } @@ -227,8 +255,7 @@ function reset_work_bag_li(ul, item, components) /* Used to get edited bag data for saving. */ function work_bag_li_data(ul) { - let components_ul = ul.work_name_input.nextElementSibling; - let component_li = components_ul.firstElementChild; + let component_li = bag_components_ul.firstElementChild; let components = []; @@ -284,6 +311,8 @@ function cancel_work(prefix) } ul.work_li.classList.add("hide"); + ul.ul.append(ul.work_li); + list_set_scrollbar(ul.ul); ul.state = UL_STATE.IDLE; } @@ -322,6 +351,7 @@ function edit_item(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; @@ -379,6 +409,7 @@ function add_new_item(prefix, name) 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; @@ -393,7 +424,7 @@ function bag_components() radio_components_window.classList.add("hide"); for (let li of chbx_components_ul.children) { - let chbx = li.firstElementChild; + let chbx = li.firstElementChild.firstElementChild; chbx.checked = false; } } @@ -403,7 +434,7 @@ function commit_bag_components() let selected = []; for (let li of chbx_components_ul.children) { - let chbx = li.firstElementChild; + let chbx = li.firstElementChild.firstElementChild; if (!chbx.checked) continue; @@ -431,10 +462,11 @@ function page_components() 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.checked = true; + li.firstElementChild.firstElementChild.checked = true; } function commit_page_components() @@ -442,7 +474,7 @@ function commit_page_components() let components = null; for (let li of radio_components_ul.children) { - let radio = li.firstElementChild; + let radio = li.firstElementChild.firstElementChild; if (!radio.checked) continue; @@ -603,6 +635,8 @@ async function initialize_import_facility() 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"); } /* @@ -637,6 +671,8 @@ function jump_to_item(url_with_item) async function main() { + init_default_policy_dialog(); + storage = await get_remote_storage(); for (let prefix of list_prefixes) { @@ -709,6 +745,7 @@ function handle_change(change) 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); } } diff --git a/html/reset.css b/html/reset.css new file mode 100644 index 0000000..dab51cd --- /dev/null +++ b/html/reset.css @@ -0,0 +1,49 @@ +/* http://meyerweb.com/eric/tools/css/reset/ + v2.0 | 20110126 + Copyright 2008,2011 Eric A. Meyer + Redistribution terms are gathered in the `copyright' file. +*/ + +html, body, div, span, applet, object, iframe, +h1, h2, h3, h4, h5, h6, p, blockquote, pre, +a, abbr, acronym, address, big, cite, code, +del, dfn, em, img, ins, kbd, q, s, samp, +small, strike, strong, sub, sup, tt, var, +b, u, i, center, +dl, dt, dd, ol, ul, li, +fieldset, form, label, legend, +table, caption, tbody, tfoot, thead, tr, th, td, +article, aside, canvas, details, embed, +figure, figcaption, footer, header, hgroup, +menu, nav, output, ruby, section, summary, +time, mark, audio, video { + margin: 0; + padding: 0; + border: 0; + font-size: 100%; + font: inherit; + vertical-align: baseline; +} +/* HTML5 display-role reset for older browsers */ +article, aside, details, figcaption, figure, +footer, header, hgroup, menu, nav, section { + display: block; +} +body { + line-height: 1; +} +ol, ul { + list-style: none; +} +blockquote, q { + quotes: none; +} +blockquote:before, blockquote:after, +q:before, q:after { + content: ''; + content: none; +} +table { + border-collapse: collapse; + border-spacing: 0; +} diff --git a/html/table.css b/html/table.css new file mode 100644 index 0000000..36f88bb --- /dev/null +++ b/html/table.css @@ -0,0 +1,45 @@ +.table_wrapper { + display: block; + background-color: #f0f0f0; + margin: 6px 0; +} + +.table_wrapper table { + border-collapse: unset; + width: 100%; +} + +.table_wrapper.tight_table, +.table_wrapper.tight_table>*, +.table_wrapper.tight_table>*>table { + width: -moz-min-content; + width: min-content; +} + +tr:nth-child(odd) { + background-color: #e5e5e5; +} + +td { + vertical-align: middle; + min-width: fit-content; + min-width: -moz-fit-content; +} + +.tight_table td { + width: 1%; +} + +td:first-child { + padding: 3px 10px 6px; +} + +.tight_table td:first-child { + width: 100%; +} + +td>div.button { + margin-right: 4px; + white-space: nowrap; + float: right; +} |