Project

General

Profile

« Previous | Next » 

Revision 453ba039

Added by koszko about 2 years ago

add styling for popup page\n\nThis does not include styling for contents of the import dialog

View differences:

copyright
31 31
Copyright: 2021 Wojtek Kosior <koszko@koszko.org>
32 32
License: GPL-3+ or Alicense-1.0 or CC-BY-SA-4.0
33 33

  
34
Files: html/*.css
35
Copyright: 2021 Wojtek Kosior <koszko@koszko.org>
36
License: GPL-3+ or Alicense-1.0 or CC-BY-SA-4.0
37

  
34 38
Files: html/base.css
35 39
Copyright: 2021 Wojtek Kosior <koszko@koszko.org>
36 40
   2021 Nicholas Johnson <nicholasjohnson@posteo.org>
html/DOM_helpers.js
42 42
	    result_object[template_key] = element;
43 43

  
44 44
	element.removeAttribute("id");
45
	element.removeAttribute("template_key");
45
	element.removeAttribute("data-template");
46 46

  
47 47
	for (const child of element.children)
48 48
	    to_process.push(child);
html/back_button.css
1
/**
2
 * part of Hachette
3
 * Style for a "back" button with a CSS arrow image.
4
 *
5
 * Copyright (C) 2021 Wojtek Kosior
6
 * Redistribution terms are gathered in the `copyright' file.
7
 */
8

  
9
.back_button {
10
    display: block;
11
    width: auto;
12
    height: auto;
13
    background-color: white;
14
    border: solid #454 0.4em;
15
    border-left: none;
16
    border-radius: 0 1.5em 1.5em 0;
17
    cursor: pointer;
18
}
19

  
20
.back_button:hover {
21
    box-shadow: 0 6px 8px 0 rgba(0,0,0,0.24), 0 17px 50px 0 rgba(0,0,0,0.19);
22
}
23

  
24
.back_button>div, .back_arrow {
25
    width: 2em;
26
    height: 0.5em;
27
    background-color: #4CAF50;
28
    border-radius: 0.3em;
29
    margin: 1.15em 0.4em;
30
}
31

  
32
.back_button>div::after, .back_arrow::after,
33
.back_button>div::before, .back_arrow::before {
34
    content: "";
35
    display: block;
36
    position: relative;
37
    background-color: inherit;
38
    width: 1.3em;
39
    height: 0.5em;
40
    transform: rotate(45deg);
41
    border-radius: 0.3em;
42
    top: 0.3em;
43
    right: 0.2em;
44
    margin: 0 -1.3em -0.5em 0;
45
}
46

  
47
.back_button>div::before, .back_arrow::before {
48
    transform: rotate(-45deg);
49
    top: -0.3em;
50
}
html/base.css
6 6
 * Redistribution terms are gathered in the `copyright' file.
7 7
 */
8 8

  
9
html {
9
body {
10 10
    font-family: sans-serif;
11
    background-color: #f0f0f0;
12
    color: #555;
13
    overflow: auto;
11 14
}
12 15

  
13 16
textarea {
......
18 21
    display: none;
19 22
}
20 23

  
24
.camouflage {
25
    visibility: hidden;
26
}
27

  
21 28
.show_next:not(:checked)+* {
22 29
    display: none;
23 30
}
......
42 49
    margin: 2px 0px;
43 50
    -moz-user-select: none;
44 51
    user-select: none;
52
    cursor: pointer;
53
    font: 400 15px sans-serif;
45 54
}
46 55

  
47 56
button.slimbutton, .button.slimbutton {
......
64 73
textarea: {
65 74
    resize: none;
66 75
}
76

  
77
.has_bottom_line::after, .has_upper_line::before {
78
    content: "";
79
    display: block;
80
    height: 8px;
81
    background: linear-gradient(transparent, #555);
82
}
83

  
84
.has_bottom_line::after {
85
    background: linear-gradient(#555, transparent);
86
}
html/display-panel.html
9 9
    <title>Hachette - page settings</title>
10 10
    <link type="text/css" rel="stylesheet" href="reset.css" />
11 11
    <link type="text/css" rel="stylesheet" href="base.css" />
12
    <link type="text/css" rel="stylesheet" href="back_button.css" />
13
    <link type="text/css" rel="stylesheet" href="table.css" />
12 14
    <style>
13 15
      body {
14
	  width: 300px;
15
	  height: 300px;
16
	  width: max-content;
17
	  width: -moz-max-content;
16 18
      }
17 19

  
18
      ul {
19
	  padding-inline-start: 15px;
20
      .bold, h2 {
21
	  font-weight: bold;
20 22
      }
21 23

  
22
      .bold {
23
	  font-weight: bold;
24
      h2 {
25
	  margin: 8px;
26
	  font-size: 120%;
27
      }
28

  
29
      .top>h2 {
30
	  padding-left: calc(0.8*3.2em - 8px);
31
      }
32

  
33
      .top {
34
	  line-height: calc(0.8*3.6em - 16px);
24 35
      }
25 36

  
26
      .unroll_chbx:not(:checked)+*+label span.triangle:first-child+span.triangle,
27
      .unroll_chbx:checked+*+label span.triangle:first-child,
28
      .unroll_chbx:not(:checked)+*,
29
      .unroll_chbx:not(:checked)+*+label+* {
37
      #main_view>.top>h2 {
38
	  padding-left: 0;
39
      }
40

  
41
      h3 {
42
	  padding: 5px;
43
	  font-size: 108%;
44
	  text-shadow: 0 0 0 #454;
45
      }
46

  
47
      .unroll_chbx:not(:checked)+div>:not(:first-child) {
30 48
	  display: none;
31 49
      }
32 50

  
51
      .unroll_triangle {
52
	  height: 1em;
53
	  width: 1em;
54
	  display: inline-block;
55
      }
56

  
57
      .unroll_triangle::after {
58
	  content: "";
59
	  width: 0.6em;
60
	  height: 0.6em;
61
	  background: linear-gradient(-45deg, currentColor 50%, transparent 50%);
62
	  display: block;
63
	  position: relative;
64
	  transform: rotate(-45deg);
65
	  top: 0.3em;
66
      }
67

  
68
      .unroll_chbx:checked+div>:first-child .unroll_triangle::after {
69
	  transform: rotate(45deg);
70
	  left: 0.2em;
71
	  top: 0.2em;
72
      }
73

  
74
      .unroll_chbx:checked+div>:first-child .unroll_block {
75
	  display: block;
76
      }
77

  
78
      .unroll_chbx:checked+div>:first-child {
79
	  line-height: 1.4em;
80
      }
81

  
82
      .l2_ul {
83
	  border-left: solid #454 5px;
84
      }
85

  
86
      .l1_li {
87
	  margin-top: 0.3em;
88
	  margin-bottom: 0.3em;
89
      }
90

  
91
      .l1_li>div {
92
	  padding: 0.3em 0.3em 0.3em 0;
93
      }
94

  
95
      .l2_li {
96
	  padding: 0.3em;
97
      }
98

  
99
      #container_for_injected>*:nth-child(odd),
100
      .l2_li:nth-child(odd) {
101
	  background-color: #e5e5e5;
102
      }
103

  
33 104
      #container_for_injected>#none_injected:not(:last-child) {
34 105
	  display: none;
35 106
      }
107

  
108
      #page_url_heading>span {
109
	  display: inline-block;
110
      }
111

  
112
      .back_button {
113
	  position: fixed;
114
	  z-index: 1;
115
	  top: 0;
116
	  left: 0;
117
	  /* The following scales the entire button. */
118
	  font-size: 80%;
119
      }
120

  
121
      #show_main_view_radio:checked~.back_button {
122
	  margin-left: -3.2em;
123
      }
124

  
125
      #show_main_view_radio:not(:checked)~.back_button {
126
	  transition: all 0.2s ease-out;
127
      }
128

  
129
      pre {
130
	  font-family: monospace;
131
	  background-color: white;
132
	  border-top: dashed #4CAF50 1px;
133
	  border-bottom: dashed #4CAF50 1px;
134
	  padding: 1px 5px;
135
      }
136

  
137
      .matched_pattern {
138
	  font-weight: bold;
139
      }
140

  
141
      tr.matched_pattern~tr {
142
	  color: #777;
143
	  font-size: 90%;
144
      }
145

  
146
      .padding_inline {
147
	  padding-left: 5px;
148
	  padding-right: 5px;
149
      }
150

  
151
      .header {
152
	  border-bottom: dashed #4CAF50 1px;
153
	  padding-bottom: 0.3em;
154
	  margin-bottom: 0.5em;
155
	  text-align: center;
156
      }
157

  
158
      .middle {
159
	  margin-top: 0.5em;
160
	  margin-bottom: 0.5em;
161
      }
162

  
163
      .footer {
164
	  border-top: dashed #4CAF50 1px;
165
	  padding-top: 0.3em;
166
	  margin-top: 0.5em;
167
	  text-align: center;
168
      }
169

  
170
      .active_setting_table {
171
	  margin-bottom: 0.5em;
172
      }
173

  
174
      .active_setting_table td {
175
	  padding: 5px;
176
	  vertical-align: middle;
177
      }
36 178
    </style>
37 179
  </head>
38 180
  <body>
39 181
    <template>
40
      <li id="pattern_li">
41
	<span></span>
42
	<button>View in settings</button>
43
      </li>
44
      <li id="query_match_li" class="queried_pattern_match" data-template="li">
182
      <tr id="pattern_entry" data-template="entry">
183
	<td data-template="name"></td>
184
	<td>
185
	  <div class="button" data-template="button">Add setting</div>
186
	</td>
187
      </tr>
188

  
189
      <li id="query_match_li" class="l2_li" data-template="li">
45 190
	<div>
46 191
	  <span>pattern:</span>
47 192
	  <span class="bold" data-template="pattern"></span>
48
	  <button data-template="btn">Install</button>
193
	  <label class="button slimbutton" for="show_install_view_radio" data-template="btn">
194
	    Install
195
	  </label>
49 196
	</div>
50 197
	<div id="unrollable_component" data-template="unroll_container">
51
	  <span data-template="component_label">payload:</span>
52 198
	  <input type="checkbox" class="unroll_chbx" data-template="chbx"></input>
53
	  <br data-template="br"/>
54
	  <label class="bold" data-template="lbl">
55
	    <span data-template="triangle">
56
	      <span class="triangle">&#x23F5;</span>
57
	      <span class="triangle">&#x23F7;</span>
199
	  <div>
200
	    <span>payload:
201
	      <label class="bold unroll_block" data-template="lbl">
202
		<div data-template="triangle" class="unroll_triangle"></div>
203
		<span data-template="payload"></span>
204
	      </label>
58 205
	    </span>
59
	    <span data-template="component"></span>
206
	    <div data-template="unroll"></div>
207
	  </div>
208
	</div>
209
      </li>
210

  
211
      <div id="injected_script" data-template="div">
212
	<input type="checkbox" class="unroll_chbx" data-template="chbx"></input>
213
	<div>
214
	  <label data-template="lbl">
215
	    <h3><div class="unroll_triangle"></div> script</h3>
60 216
	  </label>
61
	  <div data-template="unroll"></div>
217
	  <pre data-template="script_contents"></pre>
218
	</div>
219
      </div>
220

  
221
      <div id="multi_repos_query_result" data-template="div">
222
	Results for <span class="bold" data-template="url_span"></span>
223
	<ul class="l1_ul" data-template="ul"></ul>
224
      </div>
225

  
226
      <li id="single_repo_query_result" class="l1_li" data-template="li">
227
	<div>
228
	  From <span class="bold" data-template="repo_url"></span>
62 229
	</div>
63 230
      </li>
231

  
232
      <ul id="result_patterns_list" class="l2_ul" data-template="ul">
233
      </ul>
64 234
    </template>
65 235

  
66
    <input id="show_install_view_chbx" type="checkbox" class="show_hide_next2"></input>
236
    <input id="show_install_view_radio" type="radio" class="show_next" name="current_view"></input>
67 237
    <div id="install_view">
238
      <div class="top has_bottom_line"><h2> Site modifiers install </h2></div>
68 239
      <IMPORT html/import_frame.html />
69
      <!--
70
	  <div id="install_status"></div>
71
	  <label for="show_install_chbx" class="bold">Cancel install</label>
72
	  <button id="commit_install_but">Commit install</button>
73
      -->
74 240
    </div>
75
    <div id="main_view">
76
      <h2 id="page_url_heading"></h2>
77

  
78
      <input id="show_privileged_notice_chbx" type="checkbox" class="show_next"></input>
79
      <h3>Privileged page</h3>
80

  
81
      <input id="show_page_state_chbx" type="checkbox" class="show_next"></input>
82
      <div>
83
	<input id="possible_patterns_chbx" type="checkbox" class="unroll_chbx"></input>
84
	<span></span>
85
	<label for="possible_patterns_chbx">
86
	  <h3>
87
	    <span class="triangle">&#x23F5;</span>
88
	    <span class="triangle">&#x23F7;</span>
89
	    Possible patterns
90
	  </h3>
91
	</label>
92
	<ul id="possible_patterns"></ul>
93

  
94
	<input id="connected_chbx" type="checkbox" class="show_hide_next2"></input>
241

  
242
    <input id="show_injected_view_radio" type="radio" class="show_next" name="current_view"></input>
243
    <div id="injected_view">
244
      <div class="top has_bottom_line"><h2>Injected scripts</h2></div>
245
      <div id="container_for_injected">
246
	<span id="none_injected">None</span>
247
      </div>
248
    </div>
249

  
250
    <input id="show_patterns_view_radio" type="radio" class="show_next" name="current_view"></input>
251
    <div>
252
      <div class="top has_bottom_line"><h2>Possible patterns for this page</h2></div>
253
      <div class="padding_inline">
254
	<aside>
255
	  Patterns higher are more specific and override the ones below.
256
	</aside>
257
      </div>
258
      <div class="table_wrapper">
95 259
	<div>
96
	  Matched pattern: <span id="pattern" class="bold">...</span>
97
	  <button id="view_pattern" class="hide">
98
	    View in settings
99
	  </button>
100
	  <br/>
101
	  Blocked: <span id="blocked" class="bold">...</span>
102
	  <br/>
103
	  Payload: <span id="payload" class="bold">...</span>
104
	  <button id="view_payload" class="hide">
105
	    View in settings
106
	  </button>
107
	  <h3>Injected</h3>
108
	  <div id="container_for_injected">
109
	    <span id="none_injected">None</span>
260
	  <table>
261
	    <tbody id="possible_patterns">
262
	    </tbody>
263
	  </table>
264
	</div>
265
      </div>
266
    </div>
267

  
268
    <input id="show_queried_view_radio" type="radio" class="show_next" name="current_view"></input>
269
    <div>
270
      <div class="top has_bottom_line"><h2>Queried from repositories</h2></div>
271
      <div id="container_for_repo_responses" class="padding_inline">
272
      </div>
273
    </div>
274

  
275
    <input id="show_main_view_radio" type="radio" class="show_next" name="current_view" checked></input>
276
    <div id="main_view">
277
      <div class="top has_bottom_line"><h2 id="page_url_heading"></h2></div>
278
      <h3 id="privileged_notice" class="middle hide">Privileged page</h3>
279

  
280
      <div id="page_state" class="hide">
281
	<div class="header padding_inline">
282
	  <label for="show_patterns_view_radio" class="button">
283
	    Edit settings for this page
284
	  </label>
285
	</div>
286
	<div class="middle padding_inline">
287
	  <input id="connected_chbx" type="checkbox" class="show_hide_next2"></input>
288
	  <div>
289
	    <table class="active_setting_table">
290
	      <tbody>
291
		<tr>
292
		  <td>Matched pattern:</td>
293
		  <td id="pattern" class="bold">...</td>
294
		  <td>
295
		    <button id="view_pattern" class="hide">
296
		      View in settings
297
		    </button>
298
		  </td>
299
		</tr>
300
		<tr>
301
		  <td>Scripts blocked:</td>
302
		  <td id="blocked" class="bold">...</td>
303
		  <td></td>
304
		</tr>
305
		<tr>
306
		  <td>Injected payload:</td>
307
		  <td id="payload" class="bold">...</td>
308
		  <td id="payload_buttons" class="hide">
309
		    <button id="view_payload"> View in settings </button>
310
		    <br/>
311
		    <label id="view_injected" class="button" for="show_injected_view_radio">
312
		      View injected scripts
313
		    </label>
314
		  </td>
315
		</tr>
316
	      </tbody>
317
	    </table>
318
	    <label id="query_pattern" for="show_queried_view_radio" class="button">
319
	      Install scripts for this page
320
	    </label>
110 321
	  </div>
111
	  <input id="query_started_chbx" type="checkbox" class="show_hide_next2"></input>
112
	  <div id="container_for_repo_responses">
113
	    <h3>Queried from repositories</h3>
322
	  <div>
323
	    <h3>
324
	      Connecting to content script..<span id="loading_point">.</span>
325
	    </h3>
326
	    <aside id="reload_notice">
327
	      Try reloading the page.
328
	    </aside>
114 329
	  </div>
115
	  <button id="query_pattern">
116
	    Search for matching patterns
117
	  </button>
118 330
	</div>
119
	<h3>Trying to connect..<input id="loading_chbx" type="checkbox" class="show_next"></input><span>.</span></h3>
120 331
      </div>
121 332

  
122
      <button id="settings_but" type="button" style="margin-top: 20px;">Settings</button>
123
    </div>_POPUPSCRIPTS_
333
      <div class="footer padding_inline">
334
	<button id="settings_but" type="button">
335
	  Open Hachette settings
336
	</button>
337
      </div>
338
    </div>
339

  
340
    <div class="has_upper_line"></div>
341

  
342
    <label for="show_main_view_radio" class="back_button"><div></div></label>_POPUPSCRIPTS_
124 343
  </body>
125 344
</html>
html/display-panel.js
20 20
 * IMPORT TYPE_PREFIX
21 21
 * IMPORT nice_name
22 22
 * IMPORT open_in_settings
23
 * IMPORT url_matches
23 24
 * IMPORT each_url_pattern
24 25
 * IMPORT by_id
25 26
 * IMPORT get_template
......
30 31
let storage;
31 32
let tab_url;
32 33

  
34
/* Force popup <html>'s reflow on stupid Firefox. */
35
if (is_mozilla) {
36
    const reflow_forcer =
37
	  () => document.documentElement.style.width = "-moz-fit-content";
38
    for (const radio of document.querySelectorAll('[name="current_view"]'))
39
	radio.addEventListener("change", reflow_forcer);
40
}
41

  
42
const show_queried_view_radio = by_id("show_queried_view_radio");
43

  
33 44
const tab_query = {currentWindow: true, active: true};
34 45

  
35 46
async function get_current_tab()
......
48 59
}
49 60

  
50 61
const page_url_heading = by_id("page_url_heading");
51
const show_privileged_notice_chbx = by_id("show_privileged_notice_chbx");
52
const show_page_state_chbx = by_id("show_page_state_chbx");
62
const privileged_notice = by_id("privileged_notice");
63
const page_state = by_id("page_state");
64

  
65
/* Helper functions to convert string into a list of one-letter <span>'s. */
66
function char_to_span(char, doc)
67
{
68
    const span = document.createElement("span");
69
    span.textContent = char;
70
    return span;
71
}
72

  
73
function to_spans(string, doc=document)
74
{
75
    return string.split("").map(c => char_to_span(c, doc));
76
}
53 77

  
54 78
async function show_page_activity_info()
55 79
{
......
61 85
    }
62 86

  
63 87
    tab_url = /^([^?#]*)/.exec(tab.url)[1];
64
    page_url_heading.textContent = tab_url;
88
    to_spans(tab_url).forEach(s => page_url_heading.append(s));
65 89
    if (is_privileged_url(tab_url)) {
66
	show_privileged_notice_chbx.checked = true;
90
	privileged_notice.classList.remove("hide");
67 91
	return;
68 92
    }
69 93

  
70 94
    populate_possible_patterns_list(tab_url);
71
    show_page_state_chbx.checked = true;
95
    page_state.classList.remove("hide");
72 96

  
73 97
    try_to_connect(tab.id);
74 98
}
75 99

  
76
const possible_patterns_ul = by_id("possible_patterns");
77
const pattern_li_template = get_template("pattern_li");
78
pattern_li_template.removeAttribute("id");
100
const possible_patterns_list = by_id("possible_patterns");
79 101
const known_patterns = new Map();
80 102

  
81 103
function add_pattern_to_list(pattern)
82 104
{
83
    const li = pattern_li_template.cloneNode(true);
84
    li.id = `pattern_li_${known_patterns.size}`;
85
    known_patterns.set(pattern, li.id);
105
    const template = clone_template("pattern_entry");
106
    template.name.textContent = pattern;
86 107

  
87
    const span = li.firstElementChild;
88
    span.textContent = pattern;
89

  
90
    const button = span.nextElementSibling;
91 108
    const settings_opener = () => open_in_settings(TYPE_PREFIX.PAGE, pattern);
92
    button.addEventListener("click", settings_opener);
109
    template.button.addEventListener("click", settings_opener);
93 110

  
94
    possible_patterns_ul.appendChild(li)
111
    known_patterns.set(pattern, template);
112
    possible_patterns_list.append(template.entry);
95 113

  
96
    return li.id;
114
    return template;
97 115
}
98 116

  
99 117
function ensure_pattern_exists(pattern)
100 118
{
101
    let id = known_patterns.get(pattern);
119
    let entry_object = known_patterns.get(pattern);
102 120
    /*
103 121
     * As long as pattern computation works well, we should never get into this
104 122
     * conditional block. This is just a safety measure. To be removed as part
105 123
     * of a bigger rework when we start taking iframes into account.
106 124
     */
107
    if (id === undefined) {
125
    if (entry_object === undefined) {
108 126
	console.log(`unknown pattern: ${pattern}`);
109
	id = add_pattern_to_list(pattern);
127
	entry_object = add_pattern_to_list(pattern);
110 128
    }
111 129

  
112
    return id;
130
    return entry_object;
113 131
}
114 132

  
115
function set_pattern_li_button_text(li_id, text)
133
function style_possible_pattern_entry(pattern, exists_in_settings)
116 134
{
117
    by_id(li_id).firstElementChild.nextElementSibling.textContent = text;
135
    const [text, class_action] = exists_in_settings ?
136
	  ["Edit", "add"] : ["Add", "remove"];
137
    const entry_object = ensure_pattern_exists(pattern);
138

  
139
    entry_object.button.textContent = `${text} setting`;
140
    entry_object.entry.classList[class_action]("matched_pattern");
118 141
}
119 142

  
120 143
function handle_page_change(change)
121 144
{
122
    const li_id = ensure_pattern_exists(change.item);
123
    if (change.old_val === undefined)
124
	set_pattern_li_button_text(li_id, "Edit in settings");
125
    if (change.new_val === undefined)
126
	set_pattern_li_button_text(li_id, "Add setting");
145
    if (url_matches(tab_url, change.item))
146
        style_possible_pattern_entry(change.item, change.new_val !== undefined);
127 147
}
128 148

  
129 149
function populate_possible_patterns_list(url)
......
131 151
    for (const pattern of each_url_pattern(url))
132 152
	add_pattern_to_list(pattern);
133 153

  
134
    for (const [pattern, settings] of query_all(storage, url)) {
135
	set_pattern_li_button_text(ensure_pattern_exists(pattern),
136
				   "Edit in settings");
137
    }
154
    for (const [pattern, settings] of query_all(storage, url))
155
	style_possible_pattern_entry(pattern, true);
138 156

  
139 157
    storage.add_change_listener(handle_page_change, [TYPE_PREFIX.PAGE]);
140 158
}
......
160 178
	setTimeout(() => monitor_connecting(tab_id), 1000);
161 179
}
162 180

  
163
const query_started_chbx = by_id("query_started_chbx");
164

  
165
function start_querying_repos(port)
181
function start_querying_repos()
166 182
{
183
    query_pattern_but.removeEventListener("click", start_querying_repos);
167 184
    const repo_urls = storage.get_all_names(TYPE_PREFIX.REPO);
168 185
    if (content_script_port)
169 186
	content_script_port.postMessage([TYPE_PREFIX.URL, tab_url, repo_urls]);
170
    query_started_chbx.checked = true;
171 187
}
172 188

  
173
const loading_chbx = by_id("loading_chbx");
189
const loading_point = by_id("loading_point");
190
const reload_notice = by_id("reload_notice");
174 191

  
175 192
function handle_disconnect(tab_id, button_cb)
176 193
{
......
184 201
    if (connected_chbx.checked)
185 202
	return;
186 203

  
187
    loading_chbx.checked = !loading_chbx.checked;
204
    loading_point.classList.toggle("camouflage");
205
    reload_notice.classList.remove("hide");
206

  
188 207
    setTimeout(() => try_to_connect(tab_id), 1000);
189 208
}
190 209

  
......
198 217
    else
199 218
	return;
200 219

  
201
    loading_chbx.checked = !loading_chbx.checked;
220
    loading_point.classList.toggle("camouflage");
221
    reload_notice.classList.remove("hide");
202 222
    try_to_connect(tab_id);
203 223
}
204 224

  
......
206 226
const view_pattern_but = by_id("view_pattern");
207 227
const blocked_span = by_id("blocked");
208 228
const payload_span = by_id("payload");
229
const payload_buttons_div = by_id("payload_buttons");
209 230
const view_payload_but = by_id("view_payload");
231
const view_injected_but = by_id("view_injected");
210 232
const container_for_injected = by_id("container_for_injected");
211 233

  
212 234
const queried_items = new Map();
213 235

  
236
let max_injected_script_id = 0;
237

  
214 238
function handle_activity_report(message)
215 239
{
216 240
    connected_chbx.checked = true;
......
236 260
	const components = settings.components;
237 261
	if (components) {
238 262
	    payload_span.textContent = nice_name(...components);
263
	    payload_buttons_div.classList.remove("hide");
239 264
	    const settings_opener = () => open_in_settings(...components);
240
	    view_payload_but.classList.remove("hide");
241 265
	    view_payload_but.addEventListener("click", settings_opener);
242 266
	} else {
243 267
	    payload_span.textContent = "none";
244 268
	}
245 269
    }
246 270
    if (type === "script") {
247
	const h4 = document.createElement("h4");
248
	const pre = document.createElement("pre");
249
	h4.textContent = "script";
250
	pre.textContent = data;
251

  
252
	container_for_injected.appendChild(h4);
253
	container_for_injected.appendChild(pre);
271
	const template = clone_template("injected_script");
272
	const chbx_id = `injected_script_${max_injected_script_id++}`;
273
	template.chbx.id = chbx_id;
274
	template.lbl.setAttribute("for", chbx_id);
275
	template.script_contents.textContent = data;
276
	container_for_injected.appendChild(template.div);
254 277
    }
255 278
    if (type === "repo_query_action") {
256
	query_started_chbx.checked = true;
257

  
258 279
	const key = data.prefix + data.item;
259 280
	const results = queried_items.get(key) || {};
260 281
	Object.assign(results, data.results);
......
274 295

  
275 296
function create_results_list(url)
276 297
{
277
    const list_div = document.createElement("div");
278
    const list_head = document.createElement("h4");
279
    const list = document.createElement("ul");
298
    const cloned_template = clone_template("multi_repos_query_result");
299
    cloned_template.url_span.textContent = url;
300
    container_for_repo_responses.appendChild(cloned_template.div);
280 301

  
281
    list_head.textContent = url;
282
    list_div.appendChild(list_head);
283
    list_div.appendChild(list);
284
    container_for_repo_responses.appendChild(list_div);
302
    cloned_template.by_repo = new Map();
303
    results_lists.set(url, cloned_template);
285 304

  
286
    const list_object = {list, by_repo: new Map()};
287

  
288
    results_lists.set(url, list_object);
289

  
290
    return list_object;
305
    return cloned_template;
291 306
}
292 307

  
293 308
function create_result_item(list_object, repo_url, result)
294 309
{
295
    const result_li = document.createElement("li");
296
    const repo_url_span = document.createElement("span");
297
    const result_item = {result_li, appended: null};
298

  
299
    repo_url_span.textContent = repo_url;
300
    result_li.appendChild(repo_url_span);
310
    const cloned_template = clone_template("single_repo_query_result");
311
    cloned_template.repo_url.textContent = repo_url;
312
    cloned_template.appended = null;
301 313

  
302
    list_object.list.appendChild(result_li);
303
    list_object.by_repo.set(repo_url, result_item);
314
    list_object.ul.appendChild(cloned_template.li);
315
    list_object.by_repo.set(repo_url, cloned_template);
304 316

  
305
    return result_item;
317
    return cloned_template;
306 318
}
307 319

  
308 320
function set_appended(result_item, element)
......
310 322
    if (result_item.appended)
311 323
	result_item.appended.remove();
312 324
    result_item.appended = element;
313
    result_item.result_li.appendChild(element);
325
    result_item.li.appendChild(element);
314 326
}
315 327

  
316 328
function show_message(result_item, text)
......
333 345
    entry_object.chbx.removeEventListener("change", entry_object.unroll_cb);
334 346
    delete entry_object.unroll_cb;
335 347

  
336
    entry_object.unroll.textContent = "preview not implemented...";
348
    entry_object.unroll.innerHTML = "preview not implemented...<br />(consider contributing)";
337 349
}
338 350

  
339
const show_install_chbx = by_id("show_install_view_chbx");
340

  
341 351
let import_frame;
342 352
let install_target = null;
343 353

  
......
463 473

  
464 474
function install_clicked(entry_object)
465 475
{
466
    show_install_chbx.checked = true;
467 476
    import_frame.show_loading();
468 477

  
469 478
    install_target = {
......
483 492

  
484 493
function show_query_successful_result(result_item, repo_url, result)
485 494
{
486
    const ul = document.createElement("ul");
487

  
488
    set_appended(result_item, ul);
495
    const cloned_ul_template = clone_template("result_patterns_list");
496
    set_appended(result_item, cloned_ul_template.ul);
489 497

  
490 498
    for (const match of result) {
491 499
	const entry_object = clone_template("query_match_li");
492 500

  
493 501
	entry_object.pattern.textContent = match.pattern;
494 502

  
495
	ul.appendChild(entry_object.li);
503
	cloned_ul_template.ul.appendChild(entry_object.li);
496 504

  
497 505
	if (!match.payload) {
498 506
	    entry_object.payload.textContent = "(none)";
499
	    for (const key of ["chbx", "br", "triangle", "unroll"])
507
	    for (const key of ["chbx", "triangle", "unroll"])
500 508
		entry_object[key].remove();
501 509
	    continue;
502 510
	}
503 511

  
504
	entry_object.component.textContent = nice_name(...match.payload);
512
	entry_object.payload.textContent = nice_name(...match.payload);
505 513

  
506 514
	const install_cb = () => install_clicked(entry_object);
507 515
	entry_object.btn.addEventListener("click", install_cb);
......
544 552
{
545 553
    storage = await get_remote_storage();
546 554
    import_frame = await get_import_frame();
547
    import_frame.onclose = () => show_install_chbx.checked = false;
555
    import_frame.onclose = () => show_queried_view_radio.checked = true;
548 556
    show_page_activity_info();
549 557
}
550 558

  
html/import_frame.html
5 5
    <span></span>
6 6
  </li>
7 7
</template>
8
<h2> Settings import </h2>
9 8
<input id="import_loading_radio" type="radio" name="import_window_content" class="show_next"></input>
10 9
<span> Loading... </span>
11 10
<input id="import_failed_radio" type="radio" name="import_window_content" class="show_next"></input>
html/options.html
9 9
    <title>Hachette options</title>
10 10
    <link type="text/css" rel="stylesheet" href="reset.css" />
11 11
    <link type="text/css" rel="stylesheet" href="base.css" />
12
    <link type="text/css" rel="stylesheet" href="table.css" />
12 13
    <style>
13 14
      body {
14
	  background-color: #f0f0f0;
15
	  color: #555;
16
	  overflow: auto;
17 15
	  width: 100%;
18 16
      }
19 17

  
......
37 35
	  font-size: 130%;
38 36
	  padding: 10px;
39 37
	  display: inline-block;
38
	  cursor: pointer;
39
      }
40

  
41
      #tab_heads {
40 42
	  -moz-user-select: none;
41 43
	  user-select: none;
42 44
      }
......
49 51
	  border-radius: 0;
50 52
      }
51 53

  
52
      #tab_heads::after {
53
	  display: block;
54
	  height: 8px;
55
	  content: "";
56
	  background: linear-gradient(#555, transparent);
57
      }
58

  
59 54
      div.tab {
60 55
	  min-width: 50vw;
61 56
	  width: fit-content;
......
86 81
	  width: fit-content;
87 82
      }
88 83

  
89
      div.ul_container {
90
	  background-color: #f0f0f0;
91
	  width: -moz-fit-content;
92
	  width: fit-content;
93
	  margin: 6px 0;
94
      }
95

  
96
      div.ul_container::after, div.ul_container::before {
97
	  display: block;
98
	  height: 6px;
99
	  content: "";
100
	  background: linear-gradient(transparent, #555);
101
      }
102

  
103
      div.ul_container::after {
104
	  background: linear-gradient(#555, transparent);
105
      }
106

  
107
      .work_li div.ul_container::before {
84
      .work_li .table_wrapper::before {
108 85
	  background: linear-gradient(#e0f0f0, #555);
109 86
      }
110 87

  
111
      .work_li div.ul_container::after {
88
      .work_li .table_wrapper::after {
112 89
	  background: linear-gradient(#555, #e0f0f0);
113 90
      }
114 91

  
115
      div.ul_container>.always_scrollbar{
92
      .table_wrapper>.always_scrollbar{
116 93
	  border-left: solid #454 8px;
117 94
	  max-height: 80vh;
118 95
	  overflow-y: scroll;
119 96
      }
120 97

  
121
      div.ul_container table {
122
	  border-collapse: unset;
123
      }
124

  
125
      div.ul_container, div.ul_container>*, div.ul_container table>tbody {
126
	  width: -moz-min-content;
127
	  width: min-content;
128
      }
129

  
130
      div.ul_container div.ul_container>.always_scrollbar,
131
      .popup_frame div.ul_container>.always_scrollbar {
98
      .table_wrapper .table_wrapper>.always_scrollbar,
99
      .popup_frame .table_wrapper>.always_scrollbar {
132 100
	  max-height: 60vh;
133 101
      }
134 102

  
135
      .popup_frame div.ul_container table {
103
      .popup_frame .table_wrapper table {
136 104
	  min-width: 30vw;
137 105
      }
138 106

  
139
      .popup_frame div.ul_container {
107
      .popup_frame .table_wrapper {
140 108
	  margin: 0 auto;
141 109
      }
142 110

  
143
      tr:nth-child(odd) {
144
	  background-color: #e5e5e5;
145
      }
146

  
147
      td {
148
	  vertical-align: middle;
149
	  min-width: fit-content;
150
	  min-width: -moz-fit-content;
151
	  width: 1%;
152
      }
153

  
154 111
      tr>td:first-child {
155
	  padding: 3px 10px 6px;
156 112
	  max-width: 70vw;
157 113
	  overflow: hidden;
158
	  width: 100%;
159
	  white-space: nowrap;
160 114
      }
161 115

  
162 116
      tr.work_li>td:first-child {
......
198 152
	  min-width: 70vw;
199 153
	  resize: none;
200 154
      }
201

  
202
      td>div.button {
203
	  margin-right: 4px;
204
      }
205

  
206
      input[type="radio"]:not(:checked)+.import_window_content {
207
	  display: none;
208
      }
209 155
    </style>
210 156
  </head>
211 157
  <body>
......
239 185
    <input type="radio" name="tabs" id="show_pages" checked></input>
240 186
    <input type="radio" name="tabs" id="show_bags"></input>
241 187
    <input type="radio" name="tabs" id="show_scripts"></input>
242
    <div id="tab_heads">
188
    <div id="tab_heads" class="has_bottom_line">
243 189
      <label for="show_repos" id="repos_lbl"> Repos </label>
244 190
      <label for="show_pages" id="pages_lbl"> Pages </label>
245 191
      <label for="show_bags" id="bags_lbl"> Bags </label>
......
247 193
      <button id="import_but" style="margin-left: 40px;"> Import </button>
248 194
    </div>
249 195
    <div id="repos" class="tab">
250
      <div class="ul_container">
196
      <div class="table_wrapper tight_table has_bottom_line has_upper_line">
251 197
	<div>
252 198
	  <table>
253 199
	    <tbody id="repos_ul">
......
270 216
      <button id="add_repo_but" type="button"> Add repository </button>
271 217
    </div>
272 218
    <div id="pages" class="tab">
273
      <div class="ul_container">
219
      <div class="table_wrapper tight_table has_bottom_line has_upper_line">
274 220
	<div>
275 221
	  <table>
276 222
	    <tbody id="pages_ul">
......
304 250
      <button id="add_page_but" type="button"> Add page </button>
305 251
    </div>
306 252
    <div id="bags" class="tab">
307
      <div class="ul_container">
253
      <div class="table_wrapper tight_table has_bottom_line has_upper_line">
308 254
	<div>
309 255
	  <table>
310 256
	    <tbody id="bags_ul">
......
313 259
		  <div class="form_grid">
314 260
		    <label for="bag_name_field"> Name: </label>
315 261
		    <input id="bag_name_field"></input>
316
		    <div class="ul_container">
262
		    <div class="table_wrapper tight_table has_bottom_line has_upper_line">
317 263
		      <div>
318 264
			<table>
319 265
			  <tbody id="bag_components_ul">
......
341 287
      <button id="add_bag_but" type="button"> Add bag </button>
342 288
    </div>
343 289
    <div id="scripts" class="tab">
344
      <div class="ul_container">
290
      <div class="table_wrapper tight_table has_bottom_line has_upper_line">
345 291
	<div>
346 292
	  <table>
347 293
	    <tbody id="scripts_ul">
......
375 321

  
376 322
    <div id="chbx_components_window" class="hide popup" position="absolute">
377 323
      <div class="popup_frame">
378
	<div class="ul_container">
324
	<div class="table_wrapper tight_table has_bottom_line has_upper_line">
379 325
	  <div>
380 326
	    <table>
381 327
	      <tbody id="chbx_components_ul">
......
390 336

  
391 337
    <div id="radio_components_window" class="hide popup" position="absolute">
392 338
      <div class="popup_frame">
393
	<div class="ul_container">
339
	<div class="table_wrapper tight_table has_bottom_line has_upper_line">
394 340
	  <div class="always_scrollbar">
395 341
	    <table>
396 342
	      <tbody id="radio_components_ul">
......
411 357

  
412 358
    <div id="import_window" class="hide popup" position="absolute">
413 359
      <div class="popup_frame">
360
	<h2> Settings import </h2>
414 361
	<IMPORT html/import_frame.html />
415 362
      </div>
416 363
    </div>
html/options_main.js
77 77
	    break;
78 78
	}
79 79
    }
80
    if (!li.parentElement)
81
	ul.ul.appendChild(li);
80
    if (!li.parentElement) {
81
	if (ul.work_li !== ul.ul.lastElementChild)
82
	    ul.ul.appendChild(li);
83
	else
84
	    ul.work_li.before(li);
85
    }
82 86

  
83 87
    list_set_scrollbar(ul.ul);
84 88
}
html/table.css
1
.table_wrapper {
2
    display: block;
3
    background-color: #f0f0f0;
4
    margin: 6px 0;
5
}
6

  
7
.table_wrapper table {
8
    border-collapse: unset;
9
    width: 100%;
10
}
11

  
12
.table_wrapper.tight_table,
13
.table_wrapper.tight_table>*,
14
.table_wrapper.tight_table>*>table {
15
    width: -moz-min-content;
16
    width: min-content;
17
}
18

  
19
tr:nth-child(odd) {
20
    background-color: #e5e5e5;
21
}
22

  
23
td {
24
    vertical-align: middle;
25
    min-width: fit-content;
26
    min-width: -moz-fit-content;
27
}
28

  
29
.tight_table td {
30
    width: 1%;
31
}
32

  
33
td:first-child {
34
    padding: 3px 10px 6px;
35
    white-space: nowrap;
36
}
37

  
38
.tight_table td:first-child {
39
    width: 100%;
40
}
41

  
42
td>div.button {
43
    margin-right: 4px;
44
    white-space: nowrap;
45
    float: right;
46
}

Also available in: Unified diff