summaryrefslogtreecommitdiff
path: root/data/extensions/jid1-KtlZuoiikVfFew@jetpack/html
diff options
context:
space:
mode:
Diffstat (limited to 'data/extensions/jid1-KtlZuoiikVfFew@jetpack/html')
-rw-r--r--data/extensions/jid1-KtlZuoiikVfFew@jetpack/html/README21
-rw-r--r--data/extensions/jid1-KtlZuoiikVfFew@jetpack/html/background-panel.pngbin0 -> 14814 bytes
-rw-r--r--data/extensions/jid1-KtlZuoiikVfFew@jetpack/html/common.css29
-rw-r--r--data/extensions/jid1-KtlZuoiikVfFew@jetpack/html/display_panel/content/display-panel.html104
-rw-r--r--data/extensions/jid1-KtlZuoiikVfFew@jetpack/html/display_panel/content/main_panel.js177
-rw-r--r--data/extensions/jid1-KtlZuoiikVfFew@jetpack/html/display_panel/content/panel-styles.css157
-rw-r--r--data/extensions/jid1-KtlZuoiikVfFew@jetpack/html/librejs-title.pngbin0 -> 14123 bytes
-rw-r--r--data/extensions/jid1-KtlZuoiikVfFew@jetpack/html/preferences_panel/pref.js307
-rw-r--r--data/extensions/jid1-KtlZuoiikVfFew@jetpack/html/preferences_panel/preferences_panel.html86
-rw-r--r--data/extensions/jid1-KtlZuoiikVfFew@jetpack/html/preferences_panel/prefs.css91
10 files changed, 972 insertions, 0 deletions
diff --git a/data/extensions/jid1-KtlZuoiikVfFew@jetpack/html/README b/data/extensions/jid1-KtlZuoiikVfFew@jetpack/html/README
new file mode 100644
index 0000000..a56ea46
--- /dev/null
+++ b/data/extensions/jid1-KtlZuoiikVfFew@jetpack/html/README
@@ -0,0 +1,21 @@
+/**
+ * GNU LibreJS - A browser add-on to block nonfree nontrivial JavaScript.
+ * *
+ * Copyright (C) 2011, 2012, 2014 Loic J. Duros
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+All images in this directory are free, released under the GPLv3 or later. \ No newline at end of file
diff --git a/data/extensions/jid1-KtlZuoiikVfFew@jetpack/html/background-panel.png b/data/extensions/jid1-KtlZuoiikVfFew@jetpack/html/background-panel.png
new file mode 100644
index 0000000..022ffb3
--- /dev/null
+++ b/data/extensions/jid1-KtlZuoiikVfFew@jetpack/html/background-panel.png
Binary files differ
diff --git a/data/extensions/jid1-KtlZuoiikVfFew@jetpack/html/common.css b/data/extensions/jid1-KtlZuoiikVfFew@jetpack/html/common.css
new file mode 100644
index 0000000..cf2c5d1
--- /dev/null
+++ b/data/extensions/jid1-KtlZuoiikVfFew@jetpack/html/common.css
@@ -0,0 +1,29 @@
+html {
+ padding:0px;
+ margin:0px;
+ color:#000 !important;
+ background:url('background-panel.png') !important;
+}
+body {
+ padding:0;
+ margin:10px 30px 10px 20px;
+ color:#000;
+}
+
+div.libre {
+ position: relative;
+}
+
+.libre {
+ width:230px;
+ height:104px;
+ display:block;
+}
+h1.libre {
+ font-size:1.5em;
+ font-weight:normal;
+ padding:0;
+ font-weight:bold;
+ background:url('librejs-title.png') no-repeat top left;
+ text-indent:-1000px;
+}
diff --git a/data/extensions/jid1-KtlZuoiikVfFew@jetpack/html/display_panel/content/display-panel.html b/data/extensions/jid1-KtlZuoiikVfFew@jetpack/html/display_panel/content/display-panel.html
new file mode 100644
index 0000000..df153b3
--- /dev/null
+++ b/data/extensions/jid1-KtlZuoiikVfFew@jetpack/html/display_panel/content/display-panel.html
@@ -0,0 +1,104 @@
+<!doctype html>
+<html>
+<head>
+<meta charset="utf-8"/>
+<title>Display JS Monitoring Panel</title>
+<link rel="stylesheet" type="text/css" href="./panel-styles.css"/>
+
+<!-- /**
+ * GNU LibreJS - A browser add-on to block nonfree nontrivial JavaScript.
+ * *
+ * Copyright (C) 2011, 2012, 2014 Loic J. Duros
+ * Copyright (C) 2017, 2018 NateN1222 <nathannichols454@gmail.com>
+ * Copyright (C) 2018 Ruben Rodriguez <ruben@gnu.org>
+ * Copyright (C) 2018 Giorgio Maone <giorgio@maone.net>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+-->
+</head>
+
+<body>
+ <div id="header">
+ <div class="title-area">
+ <div>
+ <a class="libre"
+ id="ljs-settings"
+ href= href="https://www.gnu.org/software/librejs/"
+ title="LibreJS Page Settings">
+ <h1 class="libre">LibreJS</h1>
+ </a>
+ </div>
+ </div>
+ <div id="buttons" class="title-area">
+ <div>
+ <a target="_blank" href="https://www.gnu.org/software/librejs/"
+ id="librejs-web-link">gnu.org/software/librejs</a>
+ </div>
+ <div>
+ <strong>LibreJS <span id="version"></span></strong>
+ </div>
+ <button id="complain">Complain to site owner</button>
+ <button id="report-tab">Show this report in a new tab</button>
+ <button id="open-options">Settings...</button>
+ </div>
+ </div>
+ <div id="info">
+ <div id="site">
+ <h2 class="site">This whole site <span></span></h2>
+ <div class="buttons">
+ <button class="whitelist" name="*">Whitelist</button>
+ <button class="blacklist" name="*">Blacklist</button>
+ <button class="forget" name="*">Forget</button>
+ </div>
+ </div>
+ <div id="unknown" class="unknown-js">
+ <h2></h2>
+ <p id="must-reload">
+ LibreJS will decide whether blocking these scripts next time this page is loaded. <button id="reload">Reload it now</button>
+ </p>
+ <ul>
+ <li id="li-template">
+ <a class="script-url" href="#"></a>:
+ <p class="reason"></p>
+ <div class="buttons">
+ <button class="whitelist">Whitelist</button>
+ <button class="blacklist">Blacklist</button>
+ <button class="forget">Forget</button>
+ <button class="forget" name="*">Forget <span class="domain"></span></button>
+ </div>
+ </li>
+ </ul>
+ </div>
+ <div id="whitelisted" class="accepted-js">
+ <h2></h2>
+ <ul></ul>
+ </div>
+ <div id="accepted" class="accepted-js">
+ <h2></h2>
+ <ul></ul>
+ </div>
+ <div id="blocked" class="blocked-js">
+ <h2></h2>
+ <ul></ul>
+ </div>
+ <div id="blacklisted" class="blocked-js">
+ <h2></h2>
+ <ul></ul>
+ </div>
+ </div>
+</body>
+<script src="main_panel.js"></script>
+</html>
diff --git a/data/extensions/jid1-KtlZuoiikVfFew@jetpack/html/display_panel/content/main_panel.js b/data/extensions/jid1-KtlZuoiikVfFew@jetpack/html/display_panel/content/main_panel.js
new file mode 100644
index 0000000..c55b167
--- /dev/null
+++ b/data/extensions/jid1-KtlZuoiikVfFew@jetpack/html/display_panel/content/main_panel.js
@@ -0,0 +1,177 @@
+/**
+* GNU LibreJS - A browser add-on to block nonfree nontrivial JavaScript.
+* *
+* Copyright (C) 2017, 2018 NateN1222 <nathannichols454@gmail.com>
+* Copyright (C) 2018 Ruben Rodriguez <ruben@gnu.org>
+* Copyright (C) 2018 Giorgio Maone <giorgio@maone.net>
+*
+* This program is free software: you can redistribute it and/or modify
+* it under the terms of the GNU General Public License as published by
+* the Free Software Foundation, either version 3 of the License, or
+* (at your option) any later version.
+*
+* This program is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+* GNU General Public License for more details.
+*
+* You should have received a copy of the GNU General Public License
+* along with this program. If not, see <http://www.gnu.org/licenses/>.
+*
+*/
+
+var fromTab = window.location.hash.match(/^#fromTab=(\d+)/) && RegExp.$1;
+if (fromTab) {
+ let browserStyle = document.createElement("link");
+ browserStyle.rel = "stylesheet";
+ browserStyle.href = "chrome://browser/content/extension.css";
+ document.head.appendChild(browserStyle);
+ document.documentElement.classList.add("tab");
+}
+
+var myPort = browser.runtime.connect({name: "port-from-cs"});
+var currentReport;
+
+// Sends a message that tells the background script the window is open
+myPort.postMessage({"update": true, tabId: parseInt(currentReport && currentReport.tabId || fromTab) || ""});
+
+// Display the actual extension version Number
+document.querySelector("#version").textContent = browser.runtime.getManifest().version;
+
+var liTemplate = document.querySelector("#li-template");
+liTemplate.remove();
+
+document.querySelector("#info").addEventListener("click", e => {
+ let button = e.target;
+ if (!button.matches(".buttons > button")) return;
+ let li = button.closest("li");
+ let entry = li && li._scriptEntry || [currentReport.url, "Page's site"];
+ let action = button.className;
+ let site = button.name === "*";
+ if (site) {
+ ([action] = action.split("-"));
+ }
+ myPort.postMessage({[action]: entry, site, tabId: currentReport.tabId});
+});
+
+document.querySelector("#report-tab").onclick = e => {
+ myPort.postMessage({report_tab: currentReport});
+ close();
+}
+
+document.querySelector("#complain").onclick = e => {
+ myPort.postMessage({invoke_contact_finder: currentReport});
+ close();
+}
+
+document.querySelector("#open-options").onclick = e => {
+ browser.runtime.openOptionsPage();
+ close();
+}
+
+document.querySelector("#reload").onclick = async e => {
+ let {tabId} = currentReport;
+ if (tabId) {
+ await browser.tabs.reload(tabId);
+ myPort.postMessage({"update": true, tabId});
+ }
+};
+
+/*
+* Takes in the [[file_id, reason],...] array and the group name for one group
+* of scripts found in this tab, rendering it as a list with management buttons.
+* Groups are "unknown", "blacklisted", "whitelisted", "accepted", and "blocked".
+*/
+function createList(data, group){
+ var {url} = data;
+ let entries = data[group];
+ let container = document.getElementById(group);
+ let heading = container.querySelector("h2");
+ var list = container.querySelector("ul");
+ list.classList.toggle(group, true);
+ if (Array.isArray(entries) && entries.length) {
+ heading.innerHTML = `<span class="type-name">${group}</span> scripts in ${url}:`;
+ container.classList.remove("empty");
+ } else {
+ // default message
+ list.innerHTML = `<li>No <span class="type-name">${group}</span> scripts on this page.</li>`
+ entries = data[group] = [];
+ container.classList.add("empty");
+ }
+ // generate list
+ for (let entry of entries) {
+ let [scriptId, reason] = entry;
+ let li = liTemplate.cloneNode(true);
+ let a = li.querySelector("a");
+ a.href = scriptId.split("(")[0];
+ a.textContent = scriptId;
+ li.querySelector(".reason").textContent = reason;
+ let bySite = !!reason.match(/https?:\/\/[^/]+\/\*/);
+ li.classList.toggle("by-site", bySite);
+ if (bySite) {
+ let domain = li.querySelector(".forget .domain");
+ if (domain) domain.textContent = RegExp.lastMatch;
+ }
+ li._scriptEntry = entry;
+ list.appendChild(li);
+ }
+
+}
+
+/**
+* Updates scripts lists and buttons to act on them.
+* If return_HTML is true, it returns the HTML of the popup window without updating it.
+* example report argument:
+* {
+* "accepted": [["FILENAME 1","REASON 1"],["FILENAME 2","REASON 2"]],
+* "blocked": [["FILENAME 1","REASON 1"],["FILENAME 2","REASON 2"]],
+* "whitelisted": [["FILENAME 1","REASON 1"],["FILENAME 2","REASON 2"]],
+* "blacklisted": [["FILENAME 1","REASON 1"],["FILENAME 2","REASON 2"]],
+* "unknown": [["FILENAME 1","REASON 1"],["FILENAME 2","REASON 2"]],
+* "url":"example.com"
+* };
+*
+*/
+function refreshUI(report) {
+ currentReport = report;
+
+ document.querySelector("#site").className = report.siteStatus || "";
+ document.querySelector("#site h2").textContent =
+ `This site ${report.site}`;
+
+ for (let toBeErased of document.querySelectorAll("#info h2:not(.site) > *, #info ul > *")) {
+ toBeErased.remove();
+ }
+
+ let scriptsCount = 0;
+ for (let group of ["unknown", "accepted", "whitelisted", "blocked", "blacklisted"]) {
+ if (group in report) createList(report, group);
+ scriptsCount += report[group].length;
+ }
+
+ for (let b of document.querySelectorAll(`.forget, .whitelist, .blacklist`)) {
+ b.disabled = false;
+ }
+ for (let b of document.querySelectorAll(
+ `.unknown .forget, .accepted .forget, .blocked .forget,
+ .whitelisted .whitelist, .blacklisted .blacklist`
+ )) {
+ b.disabled = true;
+ }
+
+ let noscript = scriptsCount === 0;
+ document.body.classList.toggle("empty", noscript);
+}
+
+myPort.onMessage.addListener(m => {
+ if (m.show_info) {
+ refreshUI(m.show_info);
+ }
+});
+
+function print_local_storage(){
+ myPort.postMessage({"printlocalstorage": true});
+}
+function delete_local_storage(){
+ myPort.postMessage({"deletelocalstorage":true});
+}
diff --git a/data/extensions/jid1-KtlZuoiikVfFew@jetpack/html/display_panel/content/panel-styles.css b/data/extensions/jid1-KtlZuoiikVfFew@jetpack/html/display_panel/content/panel-styles.css
new file mode 100644
index 0000000..cbf5cf5
--- /dev/null
+++ b/data/extensions/jid1-KtlZuoiikVfFew@jetpack/html/display_panel/content/panel-styles.css
@@ -0,0 +1,157 @@
+/**
+ * GNU LibreJS - A browser add-on to block nonfree nontrivial JavaScript.
+ * *
+ * Copyright (C) 2011, 2012, 2014 Loic J. Duros
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+@import url("/html/common.css");
+
+body {
+ width:500px;
+}
+#header{
+display:block;
+width:500px;
+}
+
+h2 {
+ font-size:1.1em;
+ font-weight:bold;
+ font-family:arial;
+ border-bottom:4px solid #444;
+ padding-bottom:0;
+ margin:10px 0 0 0;
+ line-height:140%;
+}
+code {
+ font-size:1.2em;
+ margin:0;
+ padding:0;
+}
+ul {
+ margin:0;
+ padding:0;
+ list-style:none;
+}
+#info li {
+ padding:5px;
+ border-bottom:2px solid #CCC;
+ margin:0;
+ overflow: hidden;
+}
+
+#info ul ul {
+ margin:10px;
+ list-style:disc;
+}
+#info ul ul li {
+ padding:5px;
+ border-bottom:0;
+}
+#info {
+ clear:both;
+}
+
+#info .type-name {
+ text-transform: uppercase;
+ font-weight: bold;
+}
+
+#info .accepted-js .type-name {
+ color: #080;
+}
+
+#info .blocked-js .type-name {
+ color: #800;
+}
+
+#info .unknown-js .type-name {
+ color: #008;
+}
+
+#info .unknown-js .reason {
+ display: none;
+}
+
+.by-site button.forget, button.forget[name="*"] {
+ display: none;
+}
+
+.by-site button.forget[name="*"], #site .forget[name="*"] {
+ display: initial;
+}
+
+
+button.whitelist {
+ color: #080;
+}
+button.blacklist {
+ color: #800;
+}
+button.forget {
+ color: #008;
+}
+
+button:disabled {
+ color: #888 !important;
+}
+
+span.accepted, span.blocked {
+ color:#008e00;
+ font-size:145%;
+ font-variant:small-caps;
+ font-weight:bold;
+}
+
+span.blocked {
+ color:#8e0000;
+}
+
+.title-area {
+ width: 250px;
+ float:left !important;
+ text-align: center !important;
+}
+
+.title-area #librejs-web-link {
+ font-size: 18px;
+}
+
+#librejs-web-labels-pages>ul {
+ margin-top: 8px;
+ font-size: 14px;
+ list-style-type: disc;
+}
+
+
+.empty #site, .unknown-js.empty {
+ display: none;
+}
+
+.tab #must-reload, .tab #buttons, .empty #buttons {
+ display: none;
+}
+
+#buttons button {
+ width: 100%;
+ text-align: center;
+}
+
+
+
+#complain {
+ display: none; /* TODO: Complaint to owner UI */
+}
diff --git a/data/extensions/jid1-KtlZuoiikVfFew@jetpack/html/librejs-title.png b/data/extensions/jid1-KtlZuoiikVfFew@jetpack/html/librejs-title.png
new file mode 100644
index 0000000..c1a911c
--- /dev/null
+++ b/data/extensions/jid1-KtlZuoiikVfFew@jetpack/html/librejs-title.png
Binary files differ
diff --git a/data/extensions/jid1-KtlZuoiikVfFew@jetpack/html/preferences_panel/pref.js b/data/extensions/jid1-KtlZuoiikVfFew@jetpack/html/preferences_panel/pref.js
new file mode 100644
index 0000000..9cecbb6
--- /dev/null
+++ b/data/extensions/jid1-KtlZuoiikVfFew@jetpack/html/preferences_panel/pref.js
@@ -0,0 +1,307 @@
+/**
+* GNU LibreJS - A browser add-on to block nonfree nontrivial JavaScript.
+*
+* Copyright (C) 2017 Nathan Nichols
+* Copyright (C) 2018 Giorgio maone
+*
+* This file is part of GNU LibreJS.
+*
+* GNU LibreJS is free software: you can redistribute it and/or modify
+* it under the terms of the GNU General Public License as published by
+* the Free Software Foundation, either version 3 of the License, or
+* (at your option) any later version.
+*
+* GNU LibreJS is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+* GNU General Public License for more details.
+*
+* You should have received a copy of the GNU General Public License
+* along with GNU LibreJS. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+(() => {
+ "use strict";
+
+ const LIST_NAMES = ["white", "black"];
+
+ var Model = {
+ lists: {},
+ prefs: null,
+
+ malformedUrl(url) {
+ let error = null;
+ try {
+ let objUrl = new URL(url);
+ url = objUrl.href;
+ if (!objUrl.protocol.startsWith("http")) {
+ error = "Please enter http:// or https:// URLs only";
+ } else if (!/^[^*]+\*?$/.test(url)) {
+ error = "Only one single trailing path wildcard (/*) allowed";
+ }
+ } catch (e) {
+ error = "Invalid URL";
+ if (url && !url.includes("://")) error += ": missing protocol, either http:// or https://";
+ else if (url.endsWith("://")) error += ": missing domain name";
+ }
+ return error;
+ },
+
+ async save(prefs = this.prefs) {
+ if (prefs !== this.prefs) {
+ this.prefs = Object.assign(this.prefs, prefs);
+ }
+ this.saving = true;
+ try {
+ return await browser.storage.local.set(prefs);
+ } finally {
+ this.saving = false;
+ }
+ },
+
+ async addToList(list, ...items) {
+ let other = list === Model.lists.black ? Model.lists.white : Model.lists.black;
+ this.saving = true;
+ try {
+ await Promise.all([
+ other.remove(...items),
+ list.store(...items)
+ ]);
+ } finally {
+ this.saving = false;
+ }
+ }
+ };
+ Model.loading = (async () => {
+ let prefsNames = [
+ "whitelist",
+ "blacklist",
+ "subject",
+ "body"
+ ];
+ Model.prefs = await browser.storage.local.get(prefsNames.map(name => `pref_${name}`));
+
+ for (let listName of LIST_NAMES) {
+ let prefName = `pref_${listName}list`;
+ await (Model.lists[listName] = new ListStore(prefName, Storage.CSV))
+ .load(Model.prefs[prefName]);
+ }
+ })();
+
+ var Controller = {
+ init() {
+ let widgetsRoot = this.root = document.getElementById("widgets");
+ for (let widget of widgetsRoot.querySelectorAll('[id^="pref_"]')) {
+ if (widget.id in Model.lists) {
+ populateListUI(widget);
+ } else if (widget.id in Model.prefs) {
+ widget.value = Model.prefs[widget.id];
+ }
+ }
+
+ this.populateListUI();
+ this.syncAll();
+
+ for (let ev in Listeners) {
+ widgetsRoot.addEventListener(ev, Listeners[ev]);
+ }
+ document.getElementById("site").onfocus = e => {
+ if (!e.target.value.trim()) {
+ e.target.value = "https://";
+ }
+ };
+
+ browser.storage.onChanged.addListener(changes => {
+ if (!Model.saving &&
+ ("pref_whitelist" in changes || "pref_blacklist" in changes)) {
+ setTimeout(() => {
+ this.populateListUI();
+ this.syncAll();
+ }, 10);
+ }
+ });
+ },
+
+ async addSite(list) {
+ let url = document.getElementById("site").value.trim();
+
+ if (url && !Model.malformedUrl(url)) {
+ await this.addToList(list, url);
+ }
+ },
+ async addToList(list, ...items) {
+ await Model.addToList(list, ...items);
+ this.populateListUI();
+ this.syncAll();
+ },
+ async swapSelection(list) {
+ let origin = list === Model.lists.black ? "white" : "black";
+ await this.addToList(list, ...Array.map(
+ document.querySelectorAll(`select#${origin} option:checked`),
+ option => option.value)
+ );
+ },
+
+ syncAll() {
+ this.syncListsUI();
+ this.syncSiteUI();
+ },
+
+ syncSiteUI() {
+ let widget = document.getElementById("site");
+ let list2button = listName => document.getElementById(`cmd-${listName}list-site`);
+
+ for (let bi of LIST_NAMES.map(list2button)) {
+ bi.disabled = true;
+ }
+
+ let url = widget.value.trim();
+ let malformedUrl = url && Model.malformedUrl(url);
+ widget.classList.toggle("error", !!malformedUrl);
+ document.getElementById("site-error").textContent = malformedUrl || "";
+ if (!url) return;
+ if (url !== widget.value) {
+ widget.value = url;
+ }
+
+ for (let listName of LIST_NAMES) {
+ let list = Model.lists[listName];
+ if (!list.contains(url)) {
+ list2button(listName).disabled = false;
+ }
+ }
+ },
+
+ syncListsUI() {
+ let total = 0;
+ for (let id of ["black", "white"]) {
+ let selected = document.querySelectorAll(`select#${id} option:checked`).length;
+ let other = id === "black" ? "white" : "black";
+ document.getElementById(`cmd-${other}list`).disabled = selected === 0;
+ total += selected;
+ }
+ document.getElementById("cmd-delete").disabled = total === 0;
+ },
+
+ async deleteSelection() {
+ for (let id of ["black", "white"]) {
+ let selection = document.querySelectorAll(`select#${id} option:checked`);
+ await Model.lists[id].remove(...Array.map(selection, option => option.value));
+ }
+ this.populateListUI();
+ this.syncAll();
+ },
+
+ populateListUI(widget) {
+ if (!widget) {
+ for(let id of ["white", "black"]) {
+ this.populateListUI(document.getElementById(id));
+ }
+ return;
+ }
+ widget.innerHTML = "";
+ let items = [...Model.lists[widget.id].items].sort();
+ let options = new DocumentFragment();
+ for (let item of items) {
+ let option = document.createElement("option");
+ option.value = option.textContent = option.title = item;
+ options.appendChild(option);
+ }
+ widget.appendChild(options);
+ }
+ };
+
+ var KeyEvents = {
+ Delete(e) {
+ if (e.target.matches("#lists select")) {
+ Controller.deleteSelection();
+ }
+ },
+ Enter(e) {
+ if (e.target.id === "site") {
+ e.target.parentElement.querySelector("button[default]").click();
+ }
+ },
+ KeyA(e) {
+ if (e.target.matches("select") && e.ctrlKey) {
+ for (let o of e.target.options) {
+ o.selected = true;
+ }
+ Controller.syncListsUI();
+ }
+ }
+ }
+
+ var Listeners = {
+ async change(e) {
+ let {target} = e;
+ let {id} = target;
+
+ if (id in Model.lists) {
+ Controller.syncListsUI();
+ let selection = target.querySelectorAll("option:checked");
+ if (selection.length === 1) {
+ document.getElementById("site").value = selection[0].value;
+ }
+ return;
+ }
+ },
+
+ click(e) {
+ let {target} = e;
+
+ if (!/^cmd-(white|black|delete)(list-site)?/.test(target.id)) return;
+ e.preventDefault();
+ let cmd = RegExp.$1;
+ if (cmd === "delete") {
+ Controller.deleteSelection();
+ return;
+ }
+ let list = Model.lists[cmd];
+ if (list) {
+ Controller[RegExp.$2 ? "addSite" : "swapSelection"](list);
+ return;
+ }
+ },
+
+ keypress(e) {
+ let {code} = e;
+ if (code && typeof KeyEvents[code] === "function") {
+ if (KeyEvents[code](e) === false) {
+ e.preventDefault();
+ }
+ return;
+ }
+ },
+
+ async input(e) {
+ let {target} = e;
+ let {id} = target;
+ if (!id) return;
+
+ if (id === "site") {
+ Controller.syncSiteUI();
+ let url = target.value;
+ if (url) {
+ let o = document.querySelector(`#lists select option[value="${url}"]`);
+ if (o) {
+ o.scrollIntoView();
+ o.selected = true;
+ }
+ }
+ return;
+ }
+
+ if (id.startsWith("pref_")) {
+ await Model.save({[id]: target.value});
+ return;
+ }
+ }
+ };
+
+ window.addEventListener("DOMContentLoaded", async e => {
+ await Model.loading;
+ Controller.init();
+ });
+
+})();
diff --git a/data/extensions/jid1-KtlZuoiikVfFew@jetpack/html/preferences_panel/preferences_panel.html b/data/extensions/jid1-KtlZuoiikVfFew@jetpack/html/preferences_panel/preferences_panel.html
new file mode 100644
index 0000000..effb724
--- /dev/null
+++ b/data/extensions/jid1-KtlZuoiikVfFew@jetpack/html/preferences_panel/preferences_panel.html
@@ -0,0 +1,86 @@
+<!doctype html>
+<html>
+<head>
+<meta charset="utf-8"/>
+<!-- /**
+ * GNU LibreJS - A browser add-on to block nonfree nontrivial JavaScript.
+ * *
+ * Copyright (C) 2011, 2012, 2014 Loic J. Duros
+ * Copyright (C) 2014, 2015 Nik Nyby
+ * Copyright (C) 2018 Giorgio Maone
+ *
+ * This file is part of GNU LibreJS.
+ *
+ * GNU LibreJS is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * GNU LibreJS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with GNU LibreJS. If not, see <http://www.gnu.org/licenses/>.
+ */
+-->
+ <title>
+ LibreJS preferences
+ </title>
+ <link rel="stylesheet" type="text/css" href="./prefs.css"/>
+ <script type="text/javascript" src="/common/Storage.js"></script>
+ <script type="text/javascript" src="pref.js"></script>
+ </head>
+
+ <body>
+ <div class="libre">
+ <a class="libre"
+ id="ljs-settings"
+ href="https://www.gnu.org/software/librejs/"
+ title="LibreJS Settings">
+ <h1 class="libre">LibreJS</h1>
+ </a>
+ <h3>Settings</h3>
+ </div>
+ <div id="widgets">
+ <fieldset id="section-lists"><legend>Allow or block scripts matching the following URLs ("*" matches any path)</legend>
+ <label>Type a new whitelist / blacklist entry:</label>
+ <div id="new-site">
+ <input type="text" id="site" value="" placeholder="https://www.gnu.org/*">
+ <button id="cmd-whitelist-site" class="white" title="Whitelist this site" default>Whitelist</button>
+ <button id="cmd-blacklist-site" class="red" title="Blacklist this site">Blacklist</button>
+ </div>
+ <div id="site-error" class="error-msg"></div>
+ <div id="lists">
+ <div class="white list-container">
+ <label>Whitelist (always allow)</label>
+ <select id="white" multiple size="10"></select>
+ </div>
+ <div id="commands">
+ <button id="cmd-delete" title="Delete">x</button>
+ <button id="cmd-blacklist" title="Move to blacklist">&raquo;</button>
+ <button id="cmd-whitelist" title="Move to whitelist">&laquo;</button>
+ </div>
+ <div class="black list-container">
+ <label>Blacklist (always block)</label>
+ <select id="black" multiple size="10"></select>
+ </div>
+ </div>
+ </fieldset>
+
+ <fieldset id="section-complaint"><legend>Complaint email defaults</legend>
+ <label for="pref_subject">Subject</label>
+ <input id="pref_subject" type="text"
+ value="Issues with Javascript on your website"
+ />
+ <label for="pref_body">Body</label>
+ <textarea id="pref_body" rows="5"
+>Please consider using a free license for the Javascript on your website.
+
+[Message generated by LibreJS. See https://www.gnu.org/software/librejs/ for more information]
+</textarea>
+ </fieldset>
+ </div>
+ </body>
+</html>
diff --git a/data/extensions/jid1-KtlZuoiikVfFew@jetpack/html/preferences_panel/prefs.css b/data/extensions/jid1-KtlZuoiikVfFew@jetpack/html/preferences_panel/prefs.css
new file mode 100644
index 0000000..b52d6c5
--- /dev/null
+++ b/data/extensions/jid1-KtlZuoiikVfFew@jetpack/html/preferences_panel/prefs.css
@@ -0,0 +1,91 @@
+@import url("chrome://browser/content/extension.css");
+@import url("/html/common.css");
+h3 {
+ position: absolute;
+ bottom: 0px;
+ left: 240px;
+ font-size: 18px;
+}
+textarea {
+ width: 100%;
+}
+fieldset {
+ border: none;
+ padding: 0;
+ margin-top: 1em;
+ border-top: 1px solid #ccc;
+}
+legend {
+ font-weight: bold;
+ margin: 0;
+ padding: 0;
+}
+label, legend {
+ display: block;
+ font-size: 1.2em;
+}
+
+#lists {
+ display: flex;
+ flex-direction: row;
+}
+.list-container {
+ flex: 3;
+ flex-direction: row;
+}
+.list-container select {
+width: 100%
+}
+
+.black {
+ color: #600;
+}
+.white {
+ color: #060;
+}
+
+#commands {
+ display: flex;
+ justify-content: center;
+ flex: none;
+ flex-flow: column nowrap;
+}
+
+#commands button {
+ font-weight: bold;
+}
+input[type="text"] {
+ width: 100%;
+}
+
+#lists label {
+ font-weight: bold;
+}
+#lists select {
+ color: black;
+}
+#black {
+ background-color: #fcc;
+}
+#white {
+ background-color: #cfc;
+}
+
+#new-site {
+ display: flex;
+ flex 2;
+}
+.error-msg {
+ color: red;
+}
+.error-msg::after {
+ content: "\00A0";
+}
+.error {
+ background: #ffe;
+ color: #800;
+}
+
+#section-complaint {
+ display: none; /* TODO: Complaint to owner UI */
+}