2023-11-01 21:11:11 +00:00
|
|
|
/* @license This file Copyright © Mnemosyne LLC.
|
2022-02-07 16:25:02 +00:00
|
|
|
It may be used under GPLv2 (SPDX: GPL-2.0-only), GPLv3 (SPDX: GPL-3.0-only),
|
2022-01-20 18:27:56 +00:00
|
|
|
or any future license endorsed by Mnemosyne LLC.
|
|
|
|
License text can be found in the licenses/ folder. */
|
2020-10-24 01:04:25 +00:00
|
|
|
|
|
|
|
import { Formatter } from './formatter.js';
|
|
|
|
import {
|
|
|
|
OutsideClickListener,
|
2022-10-21 17:22:59 +00:00
|
|
|
createTextualTabsContainer,
|
2020-10-24 01:04:25 +00:00
|
|
|
makeUUID,
|
|
|
|
setEnabled,
|
|
|
|
setTextContent,
|
|
|
|
} from './utils.js';
|
|
|
|
|
|
|
|
export class PrefsDialog extends EventTarget {
|
|
|
|
static _initTimeDropDown(e) {
|
|
|
|
for (let index = 0; index < 24 * 4; ++index) {
|
|
|
|
const hour = Number.parseInt(index / 4, 10);
|
|
|
|
const mins = (index % 4) * 15;
|
|
|
|
const value = index * 15;
|
|
|
|
const content = `${hour}:${mins || '00'}`;
|
|
|
|
e.options[index] = new Option(content, value);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static _initDayDropDown(e) {
|
|
|
|
const options = [
|
|
|
|
['Everyday', '127'],
|
|
|
|
['Weekdays', '62'],
|
|
|
|
['Weekends', '65'],
|
|
|
|
['Sunday', '1'],
|
|
|
|
['Monday', '2'],
|
|
|
|
['Tuesday', '4'],
|
|
|
|
['Wednesday', '8'],
|
|
|
|
['Thursday', '16'],
|
|
|
|
['Friday', '32'],
|
|
|
|
['Saturday', '64'],
|
|
|
|
];
|
|
|
|
for (let index = 0; options[index]; ++index) {
|
|
|
|
const [text, value] = options[index];
|
|
|
|
e.options[index] = new Option(text, value);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
_checkPort() {
|
2023-10-24 13:27:28 +00:00
|
|
|
for (const [key, element] of Object.entries(
|
|
|
|
this.elements.network.port_status_label,
|
|
|
|
)) {
|
|
|
|
delete element.dataset.open;
|
|
|
|
setTextContent(element, 'Checking...');
|
2024-02-16 05:20:23 +00:00
|
|
|
this.remote.checkPort(
|
|
|
|
key,
|
|
|
|
(response) => this._onPortChecked(key, response),
|
|
|
|
this,
|
|
|
|
);
|
2023-10-24 13:27:28 +00:00
|
|
|
}
|
2020-10-24 01:04:25 +00:00
|
|
|
}
|
|
|
|
|
2024-02-16 05:20:23 +00:00
|
|
|
_onPortChecked(ipProtocol, response) {
|
2023-09-01 22:52:17 +00:00
|
|
|
if (this.closed) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2024-02-16 05:20:23 +00:00
|
|
|
const element = this.elements.network.port_status_label[ipProtocol];
|
2023-10-24 13:27:28 +00:00
|
|
|
const is_open = response.arguments['port-is-open'] || false;
|
2020-10-24 01:04:25 +00:00
|
|
|
element.dataset.open = is_open;
|
2024-02-16 05:20:23 +00:00
|
|
|
if ('port-is-open' in response.arguments) {
|
|
|
|
setTextContent(element, is_open ? 'Open' : 'Closed');
|
|
|
|
} else {
|
|
|
|
setTextContent(element, 'Error');
|
|
|
|
}
|
2020-10-24 01:04:25 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
_setBlocklistButtonEnabled(b) {
|
|
|
|
const e = this.elements.peers.blocklist_update_button;
|
|
|
|
setEnabled(e, b);
|
|
|
|
e.value = b ? 'Update' : 'Updating...';
|
|
|
|
}
|
|
|
|
|
|
|
|
static _getValue(e) {
|
2022-02-20 17:54:20 +00:00
|
|
|
if (e.tagName === 'TEXTAREA') {
|
|
|
|
return e.value;
|
|
|
|
}
|
|
|
|
|
2020-10-24 01:04:25 +00:00
|
|
|
switch (e.type) {
|
|
|
|
case 'checkbox':
|
|
|
|
case 'radio':
|
|
|
|
return e.checked;
|
|
|
|
|
|
|
|
case 'number':
|
2023-02-24 22:09:50 +00:00
|
|
|
case 'select-one':
|
2020-10-24 01:04:25 +00:00
|
|
|
case 'text':
|
|
|
|
case 'url': {
|
|
|
|
const string = e.value;
|
|
|
|
if (Number.parseInt(string, 10).toString() === string) {
|
|
|
|
return Number.parseInt(string, 10);
|
|
|
|
}
|
|
|
|
if (Number.parseFloat(string).toString() === string) {
|
|
|
|
return Number.parseFloat(string);
|
|
|
|
}
|
|
|
|
return string;
|
|
|
|
}
|
|
|
|
|
|
|
|
default:
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-09-12 21:03:08 +00:00
|
|
|
_onMaybePortChanged(key) {
|
|
|
|
if (key === 'peer-port' || key === 'port-forwarding-enabled') {
|
|
|
|
this._checkPort();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-10-24 01:04:25 +00:00
|
|
|
// this callback is for controls whose changes can be applied
|
|
|
|
// immediately, like checkboxs, radioboxes, and selects
|
|
|
|
_onControlChanged(event_) {
|
|
|
|
const { key } = event_.target.dataset;
|
|
|
|
this.remote.savePrefs({
|
|
|
|
[key]: PrefsDialog._getValue(event_.target),
|
|
|
|
});
|
2023-09-12 21:03:08 +00:00
|
|
|
this._onMaybePortChanged(key);
|
2020-10-24 01:04:25 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
_onDialogClosed() {
|
|
|
|
this.dispatchEvent(new Event('closed'));
|
|
|
|
}
|
|
|
|
|
|
|
|
// update the dialog's controls
|
2023-09-12 21:03:08 +00:00
|
|
|
_update() {
|
2020-10-24 01:04:25 +00:00
|
|
|
this._setBlocklistButtonEnabled(true);
|
|
|
|
|
2023-09-12 21:03:08 +00:00
|
|
|
for (const [key, value] of Object.entries(
|
|
|
|
this.session_manager.session_properties,
|
|
|
|
)) {
|
2020-10-24 01:04:25 +00:00
|
|
|
for (const element of this.elements.root.querySelectorAll(
|
2023-07-15 00:26:48 +00:00
|
|
|
`[data-key="${key}"]`,
|
2020-10-24 01:04:25 +00:00
|
|
|
)) {
|
|
|
|
if (key === 'blocklist-size') {
|
|
|
|
const n = Formatter.number(value);
|
|
|
|
element.innerHTML = `Blocklist has <span class="blocklist-size-number">${n}</span> rules`;
|
|
|
|
setTextContent(this.elements.peers.blocklist_update_button, 'Update');
|
|
|
|
} else {
|
|
|
|
switch (element.type) {
|
|
|
|
case 'checkbox':
|
|
|
|
case 'radio':
|
2023-09-12 21:03:08 +00:00
|
|
|
element.checked = value;
|
2020-10-24 01:04:25 +00:00
|
|
|
break;
|
|
|
|
case 'text':
|
2022-02-20 19:33:53 +00:00
|
|
|
case 'textarea':
|
2020-10-24 01:04:25 +00:00
|
|
|
case 'url':
|
|
|
|
case 'email':
|
|
|
|
case 'number':
|
|
|
|
case 'search':
|
|
|
|
// don't change the text if the user's editing it.
|
|
|
|
// it's very annoying when that happens!
|
2023-09-12 21:03:08 +00:00
|
|
|
if (element !== document.activeElement) {
|
2020-10-24 01:04:25 +00:00
|
|
|
// eslint-disable-next-line eqeqeq
|
2023-09-12 21:03:08 +00:00
|
|
|
if (element.value != value) {
|
|
|
|
this._onMaybePortChanged(key);
|
|
|
|
}
|
2020-10-24 01:04:25 +00:00
|
|
|
element.value = value;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case 'select-one':
|
2023-09-12 21:03:08 +00:00
|
|
|
element.value = value;
|
2020-10-24 01:04:25 +00:00
|
|
|
break;
|
|
|
|
default:
|
2022-02-20 19:33:53 +00:00
|
|
|
console.log(element.type);
|
2020-10-24 01:04:25 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
shouldAddedTorrentsStart() {
|
|
|
|
return this.data.elements.root.find('#start-added-torrents')[0].checked;
|
|
|
|
}
|
|
|
|
|
|
|
|
static _createCheckAndLabel(id, text) {
|
|
|
|
const root = document.createElement('div');
|
|
|
|
root.id = id;
|
|
|
|
|
|
|
|
const check = document.createElement('input');
|
|
|
|
check.id = makeUUID();
|
|
|
|
check.type = 'checkbox';
|
|
|
|
root.append(check);
|
|
|
|
|
|
|
|
const label = document.createElement('label');
|
|
|
|
label.textContent = text;
|
|
|
|
label.setAttribute('for', check.id);
|
|
|
|
root.append(label);
|
|
|
|
|
|
|
|
return { check, label, root };
|
|
|
|
}
|
|
|
|
|
|
|
|
static _enableIfChecked(element, check) {
|
|
|
|
const callback = () => {
|
|
|
|
if (element.tagName === 'INPUT') {
|
|
|
|
setEnabled(element, check.checked);
|
|
|
|
} else {
|
|
|
|
element.classList.toggle('disabled', !check.checked);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
check.addEventListener('change', callback);
|
|
|
|
callback();
|
|
|
|
}
|
|
|
|
|
2022-05-24 04:55:33 +00:00
|
|
|
static _getProtocolHandlerRegistered() {
|
|
|
|
return localStorage.getItem('protocol-handler-registered') === 'true';
|
|
|
|
}
|
|
|
|
|
|
|
|
static _updateProtocolHandlerButton(button) {
|
|
|
|
button.removeAttribute('disabled');
|
|
|
|
button.removeAttribute('title');
|
|
|
|
|
|
|
|
if (PrefsDialog._getProtocolHandlerRegistered()) {
|
|
|
|
button.textContent = 'Remove Browser Handler';
|
|
|
|
if (!('unregisterProtocolHandler' in navigator)) {
|
|
|
|
button.setAttribute(
|
|
|
|
'title',
|
2023-07-15 00:26:48 +00:00
|
|
|
'Your browser does not support removing protocol handlers. This button only allows you to re-register a handler.',
|
2022-05-24 04:55:33 +00:00
|
|
|
);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
button.textContent = 'Add Browser Handler';
|
|
|
|
button.removeAttribute('title');
|
|
|
|
if (!('registerProtocolHandler' in navigator)) {
|
|
|
|
button.setAttribute('disabled', true);
|
|
|
|
button.setAttribute(
|
|
|
|
'title',
|
2023-07-15 00:26:48 +00:00
|
|
|
'Your browser does not support protocol handlers',
|
2022-05-24 04:55:33 +00:00
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static _toggleProtocolHandler(button) {
|
|
|
|
const handlerUrl = new URL(window.location.href);
|
|
|
|
handlerUrl.search = 'addtorrent=%s';
|
|
|
|
if (this._getProtocolHandlerRegistered()) {
|
|
|
|
navigator.unregisterProtocolHandler?.('magnet', handlerUrl.toString());
|
|
|
|
localStorage.removeItem('protocol-handler-registered');
|
|
|
|
PrefsDialog._updateProtocolHandlerButton(button);
|
|
|
|
} else {
|
|
|
|
navigator.registerProtocolHandler(
|
|
|
|
'magnet',
|
|
|
|
handlerUrl.toString(),
|
2023-07-15 00:26:48 +00:00
|
|
|
'Transmission Web',
|
2022-05-24 04:55:33 +00:00
|
|
|
);
|
|
|
|
localStorage.setItem('protocol-handler-registered', 'true');
|
|
|
|
PrefsDialog._updateProtocolHandlerButton(button);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-10-24 01:04:25 +00:00
|
|
|
static _createTorrentsPage() {
|
|
|
|
const root = document.createElement('div');
|
|
|
|
root.classList.add('prefs-torrents-page');
|
|
|
|
|
|
|
|
let label = document.createElement('div');
|
|
|
|
label.textContent = 'Downloading';
|
|
|
|
label.classList.add('section-label');
|
|
|
|
root.append(label);
|
|
|
|
|
|
|
|
label = document.createElement('label');
|
|
|
|
label.textContent = 'Download to:';
|
|
|
|
root.append(label);
|
|
|
|
|
|
|
|
let input = document.createElement('input');
|
|
|
|
input.type = 'text';
|
|
|
|
input.id = makeUUID();
|
|
|
|
input.dataset.key = 'download-dir';
|
|
|
|
label.setAttribute('for', input.id);
|
|
|
|
root.append(input);
|
|
|
|
const download_dir = input;
|
|
|
|
|
|
|
|
let cal = PrefsDialog._createCheckAndLabel(
|
2022-01-27 04:31:02 +00:00
|
|
|
'incomplete-dir-div',
|
2023-07-15 00:26:48 +00:00
|
|
|
'Use temporary folder:',
|
2022-01-27 04:31:02 +00:00
|
|
|
);
|
|
|
|
cal.check.title =
|
|
|
|
'Separate folder to temporarily store downloads until they are complete.';
|
|
|
|
cal.check.dataset.key = 'incomplete-dir-enabled';
|
|
|
|
cal.label.title = cal.check.title;
|
|
|
|
root.append(cal.root);
|
|
|
|
const incomplete_dir_check = cal.check;
|
|
|
|
|
|
|
|
input = document.createElement('input');
|
|
|
|
input.type = 'text';
|
|
|
|
input.dataset.key = 'incomplete-dir';
|
|
|
|
root.append(input);
|
|
|
|
PrefsDialog._enableIfChecked(input, cal.check);
|
|
|
|
const incomplete_dir_input = input;
|
|
|
|
|
2022-01-27 04:31:57 +00:00
|
|
|
cal = PrefsDialog._createCheckAndLabel('autostart-div', 'Start when added');
|
2020-10-24 01:04:25 +00:00
|
|
|
cal.check.dataset.key = 'start-added-torrents';
|
|
|
|
root.append(cal.root);
|
|
|
|
const autostart_check = cal.check;
|
|
|
|
|
|
|
|
cal = PrefsDialog._createCheckAndLabel(
|
|
|
|
'suffix-div',
|
2023-07-15 00:26:48 +00:00
|
|
|
`Append "part" to incomplete files' names`,
|
2020-10-24 01:04:25 +00:00
|
|
|
);
|
|
|
|
cal.check.dataset.key = 'rename-partial-files';
|
|
|
|
root.append(cal.root);
|
|
|
|
const suffix_check = cal.check;
|
|
|
|
|
2021-11-15 16:20:07 +00:00
|
|
|
cal = PrefsDialog._createCheckAndLabel(
|
|
|
|
'download-queue-div',
|
2023-07-15 00:26:48 +00:00
|
|
|
'Download queue size:',
|
2021-11-15 16:20:07 +00:00
|
|
|
);
|
|
|
|
cal.check.dataset.key = 'download-queue-enabled';
|
|
|
|
root.append(cal.root);
|
|
|
|
const download_queue_check = cal.check;
|
|
|
|
|
|
|
|
input = document.createElement('input');
|
|
|
|
input.type = 'number';
|
|
|
|
input.dataset.key = 'download-queue-size';
|
|
|
|
root.append(input);
|
|
|
|
PrefsDialog._enableIfChecked(input, cal.check);
|
|
|
|
const download_queue_input = input;
|
|
|
|
|
2020-10-24 01:04:25 +00:00
|
|
|
label = document.createElement('div');
|
|
|
|
label.textContent = 'Seeding';
|
|
|
|
label.classList.add('section-label');
|
|
|
|
root.append(label);
|
|
|
|
|
|
|
|
cal = PrefsDialog._createCheckAndLabel(
|
|
|
|
'stop-ratio-div',
|
2023-07-15 00:26:48 +00:00
|
|
|
'Stop seeding at ratio:',
|
2020-10-24 01:04:25 +00:00
|
|
|
);
|
|
|
|
cal.check.dataset.key = 'seedRatioLimited';
|
|
|
|
root.append(cal.root);
|
|
|
|
const stop_ratio_check = cal.check;
|
|
|
|
|
|
|
|
input = document.createElement('input');
|
|
|
|
input.type = 'number';
|
2022-02-13 15:58:59 +00:00
|
|
|
input.min = '0.1';
|
|
|
|
input.step = 'any';
|
2020-10-24 01:04:25 +00:00
|
|
|
input.dataset.key = 'seedRatioLimit';
|
|
|
|
root.append(input);
|
|
|
|
PrefsDialog._enableIfChecked(input, cal.check);
|
|
|
|
const stop_ratio_input = input;
|
|
|
|
|
|
|
|
cal = PrefsDialog._createCheckAndLabel(
|
|
|
|
'stop-idle-div',
|
2023-07-15 00:26:48 +00:00
|
|
|
'Stop seeding if idle for N mins:',
|
2020-10-24 01:04:25 +00:00
|
|
|
);
|
|
|
|
cal.check.dataset.key = 'idle-seeding-limit-enabled';
|
|
|
|
root.append(cal.root);
|
|
|
|
const stop_idle_check = cal.check;
|
|
|
|
|
|
|
|
input = document.createElement('input');
|
|
|
|
input.type = 'number';
|
2022-02-13 15:58:59 +00:00
|
|
|
input.min = '0.1';
|
|
|
|
input.step = 'any';
|
2020-10-24 01:04:25 +00:00
|
|
|
input.dataset.key = 'idle-seeding-limit';
|
|
|
|
root.append(input);
|
|
|
|
PrefsDialog._enableIfChecked(input, cal.check);
|
|
|
|
const stop_idle_input = input;
|
|
|
|
|
2022-05-24 04:55:33 +00:00
|
|
|
label = document.createElement('div');
|
|
|
|
label.textContent = 'Magnet Protocol Handler';
|
|
|
|
label.classList.add('section-label');
|
|
|
|
root.append(label);
|
|
|
|
|
|
|
|
const button = document.createElement('button');
|
|
|
|
button.classList.add('register-handler-button');
|
|
|
|
PrefsDialog._updateProtocolHandlerButton(button);
|
|
|
|
root.append(button);
|
|
|
|
const register_handler_button = button;
|
|
|
|
|
2020-10-24 01:04:25 +00:00
|
|
|
return {
|
|
|
|
autostart_check,
|
|
|
|
download_dir,
|
2021-11-15 16:20:07 +00:00
|
|
|
download_queue_check,
|
|
|
|
download_queue_input,
|
2022-01-27 04:31:02 +00:00
|
|
|
incomplete_dir_check,
|
|
|
|
incomplete_dir_input,
|
2022-05-24 04:55:33 +00:00
|
|
|
register_handler_button,
|
2020-10-24 01:04:25 +00:00
|
|
|
root,
|
|
|
|
stop_idle_check,
|
|
|
|
stop_idle_input,
|
|
|
|
stop_ratio_check,
|
|
|
|
stop_ratio_input,
|
|
|
|
suffix_check,
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
static _createSpeedPage() {
|
|
|
|
const root = document.createElement('div');
|
|
|
|
root.classList.add('prefs-speed-page');
|
|
|
|
|
|
|
|
let label = document.createElement('div');
|
|
|
|
label.textContent = 'Speed Limits';
|
|
|
|
label.classList.add('section-label');
|
|
|
|
root.append(label);
|
|
|
|
|
|
|
|
let cal = PrefsDialog._createCheckAndLabel(
|
|
|
|
'upload-speed-div',
|
2023-07-15 00:26:48 +00:00
|
|
|
'Upload (kB/s):',
|
2020-10-24 01:04:25 +00:00
|
|
|
);
|
|
|
|
cal.check.dataset.key = 'speed-limit-up-enabled';
|
|
|
|
root.append(cal.root);
|
|
|
|
const upload_speed_check = cal.check;
|
|
|
|
|
|
|
|
let input = document.createElement('input');
|
|
|
|
input.type = 'number';
|
|
|
|
input.dataset.key = 'speed-limit-up';
|
|
|
|
root.append(input);
|
|
|
|
PrefsDialog._enableIfChecked(input, cal.check);
|
|
|
|
const upload_speed_input = input;
|
|
|
|
|
|
|
|
cal = PrefsDialog._createCheckAndLabel(
|
|
|
|
'download-speed-div',
|
2023-07-15 00:26:48 +00:00
|
|
|
'Download (kB/s):',
|
2020-10-24 01:04:25 +00:00
|
|
|
);
|
|
|
|
cal.check.dataset.key = 'speed-limit-down-enabled';
|
|
|
|
root.append(cal.root);
|
|
|
|
const download_speed_check = cal.check;
|
|
|
|
|
|
|
|
input = document.createElement('input');
|
|
|
|
input.type = 'number';
|
|
|
|
input.dataset.key = 'speed-limit-down';
|
|
|
|
root.append(input);
|
|
|
|
PrefsDialog._enableIfChecked(input, cal.check);
|
|
|
|
const download_speed_input = input;
|
|
|
|
|
|
|
|
label = document.createElement('div');
|
|
|
|
label.textContent = 'Alternative Speed Limits';
|
|
|
|
label.classList.add('section-label', 'alt-speed-section-label');
|
|
|
|
root.append(label);
|
|
|
|
|
|
|
|
label = document.createElement('div');
|
|
|
|
label.textContent =
|
|
|
|
'Override normal speed limits manually or at scheduled times';
|
|
|
|
label.classList.add('alt-speed-label');
|
|
|
|
root.append(label);
|
|
|
|
|
|
|
|
label = document.createElement('label');
|
|
|
|
label.textContent = 'Upload (kB/s):';
|
|
|
|
root.append(label);
|
|
|
|
|
|
|
|
input = document.createElement('input');
|
|
|
|
input.type = 'number';
|
|
|
|
input.dataset.key = 'alt-speed-up';
|
|
|
|
input.id = makeUUID();
|
|
|
|
label.setAttribute('for', input.id);
|
|
|
|
root.append(input);
|
|
|
|
const alt_upload_speed_input = input;
|
|
|
|
|
|
|
|
label = document.createElement('label');
|
|
|
|
label.textContent = 'Download (kB/s):';
|
|
|
|
root.append(label);
|
|
|
|
|
|
|
|
input = document.createElement('input');
|
|
|
|
input.type = 'number';
|
|
|
|
input.dataset.key = 'alt-speed-down';
|
|
|
|
input.id = makeUUID();
|
|
|
|
label.setAttribute('for', input.id);
|
|
|
|
root.append(input);
|
|
|
|
const alt_download_speed_input = input;
|
|
|
|
|
|
|
|
cal = PrefsDialog._createCheckAndLabel('alt-times-div', 'Scheduled times');
|
|
|
|
cal.check.dataset.key = 'alt-speed-time-enabled';
|
|
|
|
root.append(cal.root);
|
|
|
|
const alt_times_check = cal.check;
|
|
|
|
|
|
|
|
label = document.createElement('label');
|
|
|
|
label.textContent = 'From:';
|
|
|
|
PrefsDialog._enableIfChecked(label, cal.check);
|
|
|
|
root.append(label);
|
|
|
|
|
|
|
|
let select = document.createElement('select');
|
|
|
|
select.id = makeUUID();
|
|
|
|
select.dataset.key = 'alt-speed-time-begin';
|
|
|
|
PrefsDialog._initTimeDropDown(select);
|
|
|
|
label.setAttribute('for', select.id);
|
|
|
|
root.append(select);
|
|
|
|
PrefsDialog._enableIfChecked(select, cal.check);
|
|
|
|
const alt_from_select = select;
|
|
|
|
|
|
|
|
label = document.createElement('label');
|
|
|
|
label.textContent = 'To:';
|
|
|
|
PrefsDialog._enableIfChecked(label, cal.check);
|
|
|
|
root.append(label);
|
|
|
|
|
|
|
|
select = document.createElement('select');
|
|
|
|
select.id = makeUUID();
|
|
|
|
select.dataset.key = 'alt-speed-time-end';
|
|
|
|
PrefsDialog._initTimeDropDown(select);
|
|
|
|
label.setAttribute('for', select.id);
|
|
|
|
root.append(select);
|
|
|
|
PrefsDialog._enableIfChecked(select, cal.check);
|
|
|
|
const alt_to_select = select;
|
|
|
|
|
|
|
|
label = document.createElement('label');
|
|
|
|
label.textContent = 'On days:';
|
|
|
|
PrefsDialog._enableIfChecked(label, cal.check);
|
|
|
|
root.append(label);
|
|
|
|
|
|
|
|
select = document.createElement('select');
|
|
|
|
select.id = makeUUID();
|
|
|
|
select.dataset.key = 'alt-speed-time-day';
|
|
|
|
PrefsDialog._initDayDropDown(select);
|
|
|
|
label.setAttribute('for', select.id);
|
|
|
|
root.append(select);
|
|
|
|
PrefsDialog._enableIfChecked(select, cal.check);
|
|
|
|
const alt_days_select = select;
|
|
|
|
|
|
|
|
return {
|
|
|
|
alt_days_select,
|
|
|
|
alt_download_speed_input,
|
|
|
|
alt_from_select,
|
|
|
|
alt_times_check,
|
|
|
|
alt_to_select,
|
|
|
|
alt_upload_speed_input,
|
|
|
|
download_speed_check,
|
|
|
|
download_speed_input,
|
|
|
|
root,
|
|
|
|
upload_speed_check,
|
|
|
|
upload_speed_input,
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
static _createPeersPage() {
|
|
|
|
const root = document.createElement('div');
|
|
|
|
root.classList.add('prefs-peers-page');
|
|
|
|
|
|
|
|
let label = document.createElement('div');
|
|
|
|
label.textContent = 'Connections';
|
|
|
|
label.classList.add('section-label');
|
|
|
|
root.append(label);
|
|
|
|
|
2023-03-15 15:46:28 +00:00
|
|
|
label = document.createElement('label');
|
|
|
|
label.textContent = 'Max peers per torrent:';
|
|
|
|
root.append(label);
|
2020-10-24 01:04:25 +00:00
|
|
|
|
|
|
|
let input = document.createElement('input');
|
|
|
|
input.type = 'number';
|
|
|
|
input.dataset.key = 'peer-limit-per-torrent';
|
2023-03-15 15:46:28 +00:00
|
|
|
input.id = makeUUID();
|
|
|
|
label.setAttribute('for', input.id);
|
2020-10-24 01:04:25 +00:00
|
|
|
root.append(input);
|
|
|
|
const max_peers_per_torrent_input = input;
|
|
|
|
|
2023-03-15 15:46:28 +00:00
|
|
|
label = document.createElement('label');
|
|
|
|
label.textContent = 'Max peers overall:';
|
|
|
|
root.append(label);
|
2020-10-24 01:04:25 +00:00
|
|
|
|
|
|
|
input = document.createElement('input');
|
|
|
|
input.type = 'number';
|
|
|
|
input.dataset.key = 'peer-limit-global';
|
2023-03-15 15:46:28 +00:00
|
|
|
input.id = makeUUID();
|
|
|
|
label.setAttribute('for', input.id);
|
2020-10-24 01:04:25 +00:00
|
|
|
root.append(input);
|
|
|
|
const max_peers_overall_input = input;
|
|
|
|
|
|
|
|
label = document.createElement('div');
|
|
|
|
label.textContent = 'Options';
|
|
|
|
label.classList.add('section-label');
|
|
|
|
root.append(label);
|
|
|
|
|
|
|
|
label = document.createElement('label');
|
|
|
|
label.textContent = 'Encryption mode:';
|
|
|
|
root.append(label);
|
|
|
|
|
|
|
|
const select = document.createElement('select');
|
|
|
|
select.id = makeUUID();
|
|
|
|
select.dataset.key = 'encryption';
|
|
|
|
select.options[0] = new Option('Prefer encryption', 'preferred');
|
|
|
|
select.options[1] = new Option('Allow encryption', 'tolerated');
|
|
|
|
select.options[2] = new Option('Require encryption', 'required');
|
|
|
|
root.append(select);
|
|
|
|
const encryption_select = select;
|
|
|
|
|
2023-03-15 15:46:28 +00:00
|
|
|
let cal = PrefsDialog._createCheckAndLabel(
|
2020-10-24 01:04:25 +00:00
|
|
|
'use-pex-div',
|
2023-07-15 00:26:48 +00:00
|
|
|
'Use PEX to find more peers',
|
2020-10-24 01:04:25 +00:00
|
|
|
);
|
|
|
|
cal.check.title =
|
|
|
|
"PEX is a tool for exchanging peer lists with the peers you're connected to.";
|
|
|
|
cal.check.dataset.key = 'pex-enabled';
|
|
|
|
cal.label.title = cal.check.title;
|
|
|
|
root.append(cal.root);
|
|
|
|
const pex_check = cal.check;
|
|
|
|
|
|
|
|
cal = PrefsDialog._createCheckAndLabel(
|
|
|
|
'use-dht-div',
|
2023-07-15 00:26:48 +00:00
|
|
|
'Use DHT to find more peers',
|
2020-10-24 01:04:25 +00:00
|
|
|
);
|
|
|
|
cal.check.title = 'DHT is a tool for finding peers without a tracker.';
|
|
|
|
cal.check.dataset.key = 'dht-enabled';
|
|
|
|
cal.label.title = cal.check.title;
|
|
|
|
root.append(cal.root);
|
|
|
|
const dht_check = cal.check;
|
|
|
|
|
|
|
|
cal = PrefsDialog._createCheckAndLabel(
|
|
|
|
'use-lpd-div',
|
2023-07-15 00:26:48 +00:00
|
|
|
'Use LPD to find more peers',
|
2020-10-24 01:04:25 +00:00
|
|
|
);
|
|
|
|
cal.check.title = 'LPD is a tool for finding peers on your local network.';
|
|
|
|
cal.check.dataset.key = 'lpd-enabled';
|
|
|
|
cal.label.title = cal.check.title;
|
|
|
|
root.append(cal.root);
|
|
|
|
const lpd_check = cal.check;
|
|
|
|
|
|
|
|
label = document.createElement('div');
|
|
|
|
label.textContent = 'Blocklist';
|
|
|
|
label.classList.add('section-label');
|
|
|
|
root.append(label);
|
|
|
|
|
|
|
|
cal = PrefsDialog._createCheckAndLabel(
|
|
|
|
'blocklist-enabled-div',
|
2023-07-15 00:26:48 +00:00
|
|
|
'Enable blocklist:',
|
2020-10-24 01:04:25 +00:00
|
|
|
);
|
|
|
|
cal.check.dataset.key = 'blocklist-enabled';
|
|
|
|
root.append(cal.root);
|
|
|
|
const blocklist_enabled_check = cal.check;
|
|
|
|
|
|
|
|
input = document.createElement('input');
|
|
|
|
input.type = 'url';
|
|
|
|
input.value = 'http://www.example.com/blocklist';
|
|
|
|
input.dataset.key = 'blocklist-url';
|
|
|
|
root.append(input);
|
|
|
|
PrefsDialog._enableIfChecked(input, cal.check);
|
|
|
|
const blocklist_url_input = input;
|
|
|
|
|
|
|
|
label = document.createElement('label');
|
|
|
|
label.textContent = 'Blocklist has {n} rules';
|
|
|
|
label.dataset.key = 'blocklist-size';
|
|
|
|
label.classList.add('blocklist-size-label');
|
|
|
|
PrefsDialog._enableIfChecked(label, cal.check);
|
|
|
|
root.append(label);
|
|
|
|
|
|
|
|
const button = document.createElement('button');
|
|
|
|
button.classList.add('blocklist-update-button');
|
|
|
|
button.textContent = 'Update';
|
|
|
|
root.append(button);
|
|
|
|
PrefsDialog._enableIfChecked(button, cal.check);
|
|
|
|
const blocklist_update_button = button;
|
|
|
|
|
|
|
|
return {
|
|
|
|
blocklist_enabled_check,
|
|
|
|
blocklist_update_button,
|
|
|
|
blocklist_url_input,
|
|
|
|
dht_check,
|
|
|
|
encryption_select,
|
|
|
|
lpd_check,
|
|
|
|
max_peers_overall_input,
|
|
|
|
max_peers_per_torrent_input,
|
|
|
|
pex_check,
|
|
|
|
root,
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
static _createNetworkPage() {
|
|
|
|
const root = document.createElement('div');
|
|
|
|
root.classList.add('prefs-network-page');
|
|
|
|
|
|
|
|
let label = document.createElement('div');
|
|
|
|
label.textContent = 'Listening Port';
|
|
|
|
label.classList.add('section-label');
|
|
|
|
root.append(label);
|
|
|
|
|
|
|
|
label = document.createElement('label');
|
|
|
|
label.textContent = 'Peer listening port:';
|
|
|
|
root.append(label);
|
|
|
|
|
|
|
|
const input = document.createElement('input');
|
|
|
|
input.type = 'number';
|
|
|
|
input.dataset.key = 'peer-port';
|
|
|
|
input.id = makeUUID();
|
|
|
|
label.setAttribute('for', input.id);
|
|
|
|
root.append(input);
|
|
|
|
const port_input = input;
|
|
|
|
|
|
|
|
const port_status_div = document.createElement('div');
|
|
|
|
port_status_div.classList.add('port-status');
|
|
|
|
label = document.createElement('label');
|
2023-10-24 13:27:28 +00:00
|
|
|
label.textContent = 'IPv4 port is';
|
|
|
|
port_status_div.append(label);
|
|
|
|
const port_status_label_ipv4 = document.createElement('label');
|
|
|
|
port_status_label_ipv4.textContent = '?';
|
|
|
|
port_status_label_ipv4.classList.add('port-status-label');
|
|
|
|
port_status_div.append(port_status_label_ipv4);
|
|
|
|
port_status_div.append(document.createElement('br'));
|
|
|
|
label = document.createElement('label');
|
|
|
|
label.textContent = 'IPv6 port is';
|
2020-10-24 01:04:25 +00:00
|
|
|
port_status_div.append(label);
|
2023-10-24 13:27:28 +00:00
|
|
|
const port_status_label_ipv6 = document.createElement('label');
|
|
|
|
port_status_label_ipv6.textContent = '?';
|
|
|
|
port_status_label_ipv6.classList.add('port-status-label');
|
|
|
|
port_status_div.append(port_status_label_ipv6);
|
2020-10-24 01:04:25 +00:00
|
|
|
root.append(port_status_div);
|
|
|
|
|
|
|
|
let cal = PrefsDialog._createCheckAndLabel(
|
|
|
|
'randomize-port',
|
2023-07-15 00:26:48 +00:00
|
|
|
'Randomize port on launch',
|
2020-10-24 01:04:25 +00:00
|
|
|
);
|
|
|
|
cal.check.dataset.key = 'peer-port-random-on-start';
|
|
|
|
root.append(cal.root);
|
|
|
|
const random_port_check = cal.check;
|
|
|
|
|
|
|
|
cal = PrefsDialog._createCheckAndLabel(
|
|
|
|
'port-forwarding',
|
2023-07-15 00:26:48 +00:00
|
|
|
'Use port forwarding from my router',
|
2020-10-24 01:04:25 +00:00
|
|
|
);
|
|
|
|
cal.check.dataset.key = 'port-forwarding-enabled';
|
|
|
|
root.append(cal.root);
|
|
|
|
const port_forwarding_check = cal.check;
|
|
|
|
|
|
|
|
label = document.createElement('div');
|
|
|
|
label.textContent = 'Options';
|
|
|
|
label.classList.add('section-label');
|
|
|
|
root.append(label);
|
|
|
|
|
|
|
|
cal = PrefsDialog._createCheckAndLabel(
|
|
|
|
'utp-enabled',
|
2023-07-15 00:26:48 +00:00
|
|
|
'Enable uTP for peer communication',
|
2020-10-24 01:04:25 +00:00
|
|
|
);
|
|
|
|
cal.check.dataset.key = 'utp-enabled';
|
|
|
|
root.append(cal.root);
|
|
|
|
const utp_check = cal.check;
|
|
|
|
|
2022-02-20 17:54:20 +00:00
|
|
|
label = document.createElement('div');
|
2022-02-20 19:33:53 +00:00
|
|
|
label.textContent = 'Default Public Trackers';
|
2022-02-20 17:54:20 +00:00
|
|
|
label.classList.add('section-label');
|
|
|
|
root.append(label);
|
|
|
|
|
2022-02-20 19:33:53 +00:00
|
|
|
const tracker_labels = [
|
|
|
|
'Trackers to use on all public torrents.',
|
|
|
|
'To add a backup URL, add it on the next line after a primary URL.',
|
|
|
|
'To add a new primary URL, add it after a blank line.',
|
|
|
|
];
|
|
|
|
for (const text of tracker_labels) {
|
|
|
|
label = document.createElement('label');
|
|
|
|
label.classList.add('default-trackers-label');
|
|
|
|
label.textContent = text;
|
|
|
|
label.setAttribute('for', 'default-trackers');
|
|
|
|
root.append(label);
|
|
|
|
}
|
2022-02-20 17:54:20 +00:00
|
|
|
|
|
|
|
const textarea = document.createElement('textarea');
|
|
|
|
textarea.dataset.key = 'default-trackers';
|
|
|
|
textarea.id = 'default-trackers';
|
|
|
|
root.append(textarea);
|
|
|
|
const default_trackers_textarea = textarea;
|
|
|
|
|
2020-10-24 01:04:25 +00:00
|
|
|
return {
|
2022-02-20 17:54:20 +00:00
|
|
|
default_trackers_textarea,
|
2020-10-24 01:04:25 +00:00
|
|
|
port_forwarding_check,
|
|
|
|
port_input,
|
2023-10-24 13:27:28 +00:00
|
|
|
port_status_label: {
|
|
|
|
ipv4: port_status_label_ipv4,
|
|
|
|
ipv6: port_status_label_ipv6,
|
|
|
|
},
|
2020-10-24 01:04:25 +00:00
|
|
|
random_port_check,
|
|
|
|
root,
|
|
|
|
utp_check,
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
static _create() {
|
|
|
|
const pages = {
|
|
|
|
network: PrefsDialog._createNetworkPage(),
|
|
|
|
peers: PrefsDialog._createPeersPage(),
|
|
|
|
speed: PrefsDialog._createSpeedPage(),
|
|
|
|
torrents: PrefsDialog._createTorrentsPage(),
|
|
|
|
};
|
|
|
|
|
2022-10-21 17:22:59 +00:00
|
|
|
const elements = createTextualTabsContainer('prefs-dialog', [
|
|
|
|
['prefs-tab-torrent', pages.torrents.root, 'Torrents'],
|
|
|
|
['prefs-tab-speed', pages.speed.root, 'Speed'],
|
|
|
|
['prefs-tab-peers', pages.peers.root, 'Peers'],
|
|
|
|
['prefs-tab-network', pages.network.root, 'Network'],
|
2020-10-24 01:04:25 +00:00
|
|
|
]);
|
|
|
|
|
|
|
|
return { ...elements, ...pages };
|
|
|
|
}
|
|
|
|
|
|
|
|
constructor(session_manager, remote) {
|
|
|
|
super();
|
|
|
|
|
|
|
|
this.closed = false;
|
|
|
|
this.session_manager = session_manager;
|
|
|
|
this.remote = remote;
|
2023-09-12 21:03:08 +00:00
|
|
|
this.update_from_session = () => this._update();
|
2020-10-24 01:04:25 +00:00
|
|
|
|
|
|
|
this.elements = PrefsDialog._create();
|
|
|
|
this.elements.peers.blocklist_update_button.addEventListener(
|
|
|
|
'click',
|
|
|
|
(event_) => {
|
|
|
|
setTextContent(event_.target, 'Updating blocklist...');
|
|
|
|
this.remote.updateBlocklist();
|
|
|
|
this._setBlocklistButtonEnabled(false);
|
2023-07-15 00:26:48 +00:00
|
|
|
},
|
2020-10-24 01:04:25 +00:00
|
|
|
);
|
2022-05-24 04:55:33 +00:00
|
|
|
this.elements.torrents.register_handler_button.addEventListener(
|
|
|
|
'click',
|
|
|
|
(event_) => {
|
|
|
|
PrefsDialog._toggleProtocolHandler(event_.currentTarget);
|
2023-07-15 00:26:48 +00:00
|
|
|
},
|
2022-05-24 04:55:33 +00:00
|
|
|
);
|
2023-09-01 22:52:17 +00:00
|
|
|
this.elements.dismiss.addEventListener('click', () => this.close());
|
2020-10-24 01:04:25 +00:00
|
|
|
this.outside = new OutsideClickListener(this.elements.root);
|
|
|
|
this.outside.addEventListener('click', () => this.close());
|
|
|
|
|
|
|
|
Object.seal(this);
|
|
|
|
|
|
|
|
// listen for user input
|
|
|
|
const on_change = this._onControlChanged.bind(this);
|
|
|
|
const walk = (o) => {
|
|
|
|
for (const element of Object.values(o)) {
|
|
|
|
if (element.tagName === 'INPUT') {
|
|
|
|
switch (element.type) {
|
|
|
|
case 'checkbox':
|
|
|
|
case 'radio':
|
|
|
|
case 'number':
|
|
|
|
case 'text':
|
|
|
|
case 'url':
|
|
|
|
element.addEventListener('change', on_change);
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
console.trace(`unhandled input: ${element.type}`);
|
|
|
|
break;
|
|
|
|
}
|
2023-02-24 22:09:50 +00:00
|
|
|
} else if (
|
|
|
|
element.tagName === 'TEXTAREA' ||
|
|
|
|
element.tagName === 'SELECT'
|
|
|
|
) {
|
2022-02-20 17:54:20 +00:00
|
|
|
element.addEventListener('change', on_change);
|
2020-10-24 01:04:25 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
};
|
|
|
|
walk(this.elements.network);
|
|
|
|
walk(this.elements.peers);
|
|
|
|
walk(this.elements.speed);
|
|
|
|
walk(this.elements.torrents);
|
|
|
|
|
2023-09-12 21:03:08 +00:00
|
|
|
this.session_manager.addEventListener(
|
|
|
|
'session-change',
|
|
|
|
this.update_from_session,
|
|
|
|
);
|
|
|
|
this.update_from_session();
|
2020-10-24 01:04:25 +00:00
|
|
|
|
|
|
|
document.body.append(this.elements.root);
|
|
|
|
}
|
|
|
|
|
|
|
|
close() {
|
|
|
|
if (!this.closed) {
|
|
|
|
this.outside.stop();
|
|
|
|
this.session_manager.removeEventListener(
|
|
|
|
'session-change',
|
2023-09-12 21:03:08 +00:00
|
|
|
this.update_from_session,
|
2020-10-24 01:04:25 +00:00
|
|
|
);
|
|
|
|
this.elements.root.remove();
|
|
|
|
dispatchEvent(new Event('close'));
|
|
|
|
for (const key of Object.keys(this)) {
|
|
|
|
this[key] = null;
|
|
|
|
}
|
|
|
|
this.closed = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|