79 Commits

Author SHA1 Message Date
82e7361567 Version 4.2.6 2019-11-26 13:13:09 +01:00
5c84493a0f build: cleanup stale bundles 2019-11-26 13:05:16 +01:00
83cb8e32f1 Update Readme.md 2019-11-25 19:14:51 +01:00
c49af54532 Remove compresslevel from build.py
Makes python3.6 choke
2019-11-25 19:08:33 +01:00
eee8c4ea1a Version 4.2.5 2019-11-21 15:07:36 +01:00
858b8f277e Update dev dependencies 2019-11-21 13:25:20 +01:00
6841fdcfc8 New icon 2019-11-21 13:25:20 +01:00
1e8e7ad6ec Use correct grouping 2019-11-21 13:21:02 +01:00
6ed84b9560 Move landing pages into own repo 2019-11-21 12:03:29 +01:00
f739cb789c Adjust some darkmode colors 2019-11-21 11:40:39 +01:00
0d470a7ce0 Handle CRLF in imports
Closes #186
2019-11-21 11:40:39 +01:00
9ec1d46787 Update of Danish translation (#185) 2019-11-21 11:21:47 +01:00
ba283e9221 Version 4.2.4 2019-10-28 11:28:16 +01:00
e549886532 Do not mess up queue when sorting in select
Closes #181
2019-10-28 11:13:19 +01:00
d3b7032229 Version 4.2.3 2019-10-13 13:44:38 +02:00
5586bcb671 Update changelog 2019-10-13 13:44:05 +02:00
bd72c417d2 Remove sounds on Opera
See #125
2019-10-13 13:44:05 +02:00
52643e0bec Update feature_request.md 2019-10-11 17:38:14 +02:00
af59fb60ff Improve bug report template. 2019-10-11 17:37:04 +02:00
ac2bc8cdfd Add privacy notice 2019-10-11 17:26:17 +02:00
c5309a8923 Always import links in the right context
Closes #163
2019-10-11 15:47:16 +02:00
1370723e6d Push raw indexes 2019-10-11 15:47:16 +02:00
4ba827fc15 Make sure we're on background 2019-10-11 15:47:16 +02:00
b2e20b9875 Update zh_CN translation (#161) 2019-10-11 13:39:36 +02:00
ef9cff003d Update ru translation
Closes #160
2019-10-11 13:37:21 +02:00
74b3ce7eb1 Dark mode: improve display of disabled menu items 2019-10-11 13:28:41 +02:00
6528e2118e Show context when no selection
Closes #162
2019-10-11 13:28:18 +02:00
c901438216 Link landing from changelog 2019-10-09 20:56:02 +02:00
7a0718d9cc Version 4.2.2 2019-10-09 19:13:13 +02:00
a7cc3c7fff Fix landing title 2019-10-09 18:59:45 +02:00
9d313f319d Use the correct full path in tooltips 2019-10-09 18:32:22 +02:00
856044c88c Open landing page on installs 2019-10-09 18:26:36 +02:00
de1b13a50f Add landing page 2019-10-09 18:10:40 +02:00
a981b7b8c7 Update nl locale
Closes #157
2019-10-08 22:24:32 +02:00
abe9d82d03 Always notify all observers 2019-10-08 22:24:32 +02:00
49a3f08a9a Update Arabic translation (#158) 2019-10-08 15:12:38 +02:00
afaa75fcdc Update Bulgarian localization (#156) 2019-10-08 15:12:14 +02:00
a5c749412a Work around Firefox window.create({left, top}) issues 2019-10-07 14:55:01 +02:00
e64da40355 Track window state of manager windows
Closes #154
2019-10-07 14:55:00 +02:00
05e7283f9f Update messages.json (#155) 2019-10-07 14:27:19 +02:00
bea8e230fb Update pl translation
Closes #151
2019-10-06 23:04:53 +02:00
23c1ece807 4 more French strings (#152) 2019-10-06 23:02:11 +02:00
539d340f1a Make prefwatcher not stop event chain
See #148
2019-10-06 22:33:23 +02:00
876486bbf5 Update hungarian translation (#149) 2019-10-06 19:34:58 +02:00
9179851c85 Update zh_TW translation (#147) 2019-10-06 08:44:31 +02:00
1e96d7e787 Version 4.2.1 Beta
For translators
2019-10-06 08:12:27 +02:00
612478bcc7 Add license to changelog 2019-10-06 08:12:00 +02:00
23f84fbde0 Use both .currentSrc and .src for image gathering
Works around some lazy-loader issues and make it at least as compatible as v3

Closes #145
2019-10-06 08:00:50 +02:00
b7b4c57034 Update de 2019-10-06 08:00:50 +02:00
872b058d4c Update et (#146) 2019-10-06 07:56:38 +02:00
93ad3e71db Update of lt locale (#144) 2019-10-05 22:39:52 +02:00
7d824bf61e Update Japanese translation 5th (#143) 2019-10-05 22:39:25 +02:00
06228d9ec9 Update messages.json (#141) 2019-10-03 17:04:27 +02:00
f9232ffd96 Update messages.json (#140) 2019-10-03 14:30:31 +02:00
ab3c335bf1 Polish pref window a bit 2019-10-03 09:04:00 +02:00
9142cc023f Add a basic editorconfig 2019-10-03 08:30:45 +02:00
3133a8d8ad Dark theme modals 2019-10-03 08:26:22 +02:00
312f39f7f6 Dark mode scrollbar colors 2019-10-02 22:09:04 +02:00
4ba7bb530d Close unhandled ports 2019-10-02 21:47:38 +02:00
9caad6b3a5 Detect browser theme on Firefox (#139) 2019-10-02 14:54:51 +02:00
5e323db2f0 Make theme configurable (#139) 2019-10-02 14:24:36 +02:00
18daa28cea Initial dark theme (#139) 2019-10-02 13:57:27 +02:00
65c358c01b Update Readme.md 2019-10-02 10:49:09 +02:00
2d14432efe Update TODO.md 2019-10-02 04:43:45 +02:00
19b1cc8856 Add Character batches
Closes #118
2019-10-02 04:37:19 +02:00
883f9a6f0b Add Changelog 2019-10-02 04:05:32 +02:00
e969ba237a Update packages 2019-10-02 04:04:39 +02:00
207248e706 Update ar translation (#90) 2019-10-02 01:55:26 +02:00
9925dec0f4 Use onDeterminingFilename when supported
Closes #132
2019-09-26 08:46:57 +02:00
09b2b4be10 4.1.2 2019-09-26 07:19:43 +02:00
f331de4134 Add Cmd+F shortcut to focus filter box 2019-09-26 07:08:26 +02:00
7760072e8e Avoid the filter box getting an outline in chrome 2019-09-26 07:02:12 +02:00
626bf592de List da locale in all.json 2019-09-26 06:38:49 +02:00
d550de0e27 Add Danish locale
Close #133
2019-09-26 06:37:49 +02:00
367efb4b53 Fix domain parsing in particular IPs
Closes #135
2019-09-26 06:34:23 +02:00
4a82c62958 Update zh_TW translation (#129) 2019-09-23 07:27:39 +02:00
891aa66bff Update zh_CN translation (#126) 2019-09-23 07:24:50 +02:00
2513b60c1b Update ko locale
Closes #127
2019-09-22 17:22:40 +02:00
31cca481f9 Update nl locale
Closes #128
2019-09-22 17:17:24 +02:00
80 changed files with 5480 additions and 2514 deletions

View File

@ -1,6 +1,6 @@
---
name: Bug report
about: Create a report to help us improve
about: Create a report to help us improve DownThemAll!
title: ''
labels: ''
assignees: ''
@ -8,9 +8,9 @@ assignees: ''
---
**Desktop (please complete the following information):**
- OS: [e.g. iOS]
- Browser [e.g. chrome, safari]
- Version [e.g. 22]
- OS: [e.g. Windows 10, macOS, Linux (distribution, desktop environment)]
- Browser and version: [e.g. Firefox 42, Chrome 70, Opera 15, Seamonkey 2.16]
- DownThemAll! version: [e.g. 4.2, 3.0, latest]
**Describe the bug**
A clear and concise description of what the bug is.

View File

@ -1,6 +1,6 @@
---
name: Feature request
about: Suggest an idea for this project
about: Suggest a feature or an idea for DownThemAll!
title: ''
labels: ''
assignees: ''

View File

@ -1,3 +1,7 @@
![DownThemAll!](https://raw.githubusercontent.com/downthemall/downthemall/master/style/icon128.png)
# DownThemAll! WE
The DownThemAll! WebExtension.
@ -27,28 +31,59 @@ If you would like to help out translating DTA, please see our [translation guide
## Development
### Requirements
You will want to `yarn` the development dependencies such as webpack first.
- [node](https://nodejs.org/en/)
- [yarn](https://yarnpkg.com/)
- [python3](https://www.python.org/) >= 3.6 (to build zips)
- [web-ext](https://www.npmjs.com/package/web-ext) (for development ease)
Afterwards, you will want to run`yarn watch`.
### Setup
You will want to run `yarn` to install the development dependencies such as webpack first.
### Making changes
Just use your favorite text editor to edit the files.
You will want to run`yarn watch`.
This will run the webpack bundler in watch mode, transpiling the TypeScript to Javascript and updating bundles as you change the source.
Please note: You have to run `yarn watch` (at least once) as it builds the actual script bundles.
Please note: You have to run `yarn watch` or `yarn build` (at least once) as it builds the actual script bundles.
### Firefox
### Running in Firefox
I recommend you install the [`web-ext`](https://www.npmjs.com/package/web-ext) tools from mozilla. It is not listed as a dependency by design at it causes problems with dependency resolution in yarn right now if installed in the same location as the rest of the dependencies.
If you did, then running `yarn webext` (additionally to `yarn watch`) will run the WebExtension in a development profile. This will use the directory `../dtalite.p` to keep a development profile. You might need to create this directory before you use this command. Furthermore `yarn webext` will watch for changes to the sources and automatically reload the extension.
Alternative, you can also `yarn build`, which then builds an *unsigned* zip that you can then install permanently in a browser that does not enforce signing (i.e. Nightly or the Unbranded Firefox with the right about:config preferences).
Alternatively, you can also `yarn build`, which then builds an *unsigned* zip that you can then install permanently in a browser that does not enforce signing (i.e. Nightly or the Unbranded Firefox with the right about:config preferences).
### Chrome
### Running in Chrome/Chromium/etc
You have to build the bundles first, of course.
Then put your Chrome into Developement Mode on the Extensions page, and Load Unpacked the directory of your downthemall clone.
### Making release zips
To get a basic unofficial set of zips for Firefox and chrome, run `yarn build`.
If you want to generate release builds like the ones that are eventually released in the extension stores, use `python3 util/build.py --mode=release`.
The output is located in `web-ext-artifacts`.
- `-fx.zip` are Firefox builds
- `-crx.zip` are Chrome/Chromium builds
- `-opr.zip` are Opera builds (essentially like the Chrome one, but without sounds)
### The AMO Editors tl;dr guide
1. Install the requirements.
2. `yarn && python3 build/util.py --mode=release`
3. Have a look in `web-ext-artifacts/dta-*-fx.zip`
### Patches
Before submitting patches, please make sure you run eslint (if this isn't done automatically in your text editor/IDE), and eslint does not report any open issues. Code contributions should favor typescript code over javascript code. External dependencies that would ship with the final product (including all npm/yarn packages) should be kept to a bare minimum and need justification.

View File

@ -1,8 +1,6 @@
TODO
---
aka a lot
P2
===
@ -26,11 +24,7 @@ Nice-to-haves.
* Manipulate downloads (e.g. rewrite URLs)
* Native context menus?
* Would require massive reworks incl the need for new icon formats, but potentially feasible.
* Import/Export
* Download priorities (manual scheduling overrides)
* Dark Theme support
* os/browser define be default
* overwritable
* Remove `any` types as possible, and generally improve typescript (new language to me)
P4

View File

@ -2,6 +2,7 @@
"ar": "العربية [ar]",
"bg": "Български [bg]",
"cs": "Čeština (CZ) [cs]",
"da": "Dansk [da]",
"de": "Deutsch [de]",
"el": "Ελληνικά [el]",
"en": "English (US) [en]",

View File

@ -191,6 +191,22 @@
"message": "احذف",
"description": "button text"
},
"deletefiles": {
"message": "احذف الملفات",
"description": "menu action"
},
"deletefiles_button": {
"message": "احذف",
"description": "button text"
},
"deletefiles_text": {
"message": "أتريد إزالة الملفات الآتية؟",
"description": "messagebox text"
},
"deletefiles_title": {
"message": "حذف الملفات",
"description": "messagebox title"
},
"description": {
"message": "الوصف",
"description": "Description (keep it short); e.g. the description column in select"
@ -275,10 +291,34 @@
"message": "العنوان غير صالح",
"description": "Error message; single window"
},
"error_noabsolutepath": {
"message": "لا تدعم المتصفّحات أي مسار مُطلق للمجلدات الفرعية",
"description": "Error Message; select/single window"
},
"error_nodotsinpath": {
"message": "لا تدعم المتصفّحات النقط (.) في المجلدات الفرعية",
"description": "Error Message; select/single window"
},
"error_noItemsSelected": {
"message": "لم تحدّد عناصر",
"description": "Error Message; select window"
},
"export": {
"message": "صدّر إلى ملف",
"description": "menu text"
},
"export_aria2": {
"message": "صدّر بنسق قائمة aria2",
"description": "menu text"
},
"export_metalink": {
"message": "صدّر بنسق Metalink",
"description": "menu text"
},
"export_text": {
"message": "صدّر كنصّ",
"description": "menu text"
},
"extensionDescription": {
"message": "أداة التنزيل لبحور من الروابط، داخل متصفحك",
"description": "DownThemAll! tagline, displayed in about:addons; Please do NOT refer to a specific browser such as firefox, as we will probably support more than one"
@ -331,6 +371,10 @@
"message": "يُجبر البدء",
"description": "Menu text"
},
"import": {
"message": "استورِد من نصّ",
"description": "menu text"
},
"information_title": {
"message": "معلومات",
"description": "Used in message boxes"
@ -529,6 +573,26 @@
"message": "أضفِ التنزيلات الجديدة ملبثة، بدل بدئها مباشرة عند إضافتها",
"description": "Preferences/General"
},
"pref_button_type": {
"message": "زر ”نزّلها كلها!“:",
"description": "label"
},
"pref_button_type_dta": {
"message": "تحديد ”نزّلها كلها“",
"description": "label"
},
"pref_button_type_manager": {
"message": "يفتح المدير",
"description": "label"
},
"pref_button_type_popup": {
"message": "يعرض القائمة",
"description": "label"
},
"pref_button_type_turbo": {
"message": "ينفّذ ”بنقرة واحدة!“",
"description": "label"
},
"pref_concurrent_downloads": {
"message": "التنزيلات المتزامنة",
"description": "Preferences/Network"
@ -537,10 +601,6 @@
"message": "اعرض إخطارًا حين ينتهي تنزيل طابور التنزيلات",
"description": "Preferences/General"
},
"pref_global_turbo": {
"message": "ليكن زر المتصفح ”بنقرة واحدة!“",
"description": "Preferences/General"
},
"pref_hide_context": {
"message": "لا تعرض عناصر القائمة السياقية العامة",
"description": "Preferences/General"
@ -549,6 +609,10 @@
"message": "المدير",
"description": "Preferences/General; group text"
},
"pref_manager_in_popup": {
"message": "افتح المدير في نافذة منبثقة جديدة",
"description": "checkbox text"
},
"pref_manager_tooltip": {
"message": "اعرض التلميحات في ألسنة المدير",
"description": "Preferences/General"
@ -573,14 +637,42 @@
"message": "أزِل التنزيلات الناقصة بعد إعادة التشغيل",
"description": "Preferences/General"
},
"pref_retries": {
"message": "عدد مرات إعادة التنزيل في حال الأخطاء المؤقتة",
"description": "pref text"
},
"pref_retry_time": {
"message": "أعِد كل (بالدقيقة)",
"description": "pref text"
},
"pref_show_urls": {
"message": "اعرض العناوين بدل الأسماء",
"description": "Preferences/General"
},
"pref_sounds": {
"message": "شغّل الأصوات",
"description": "checkbox text"
},
"pref_text_links": {
"message": "حاوِل البحث عن الروابط في نص موقع الوِب (أبطأ)",
"description": "Preferences/General"
},
"pref_theme": {
"message": "السمة:",
"description": "label text"
},
"pref_theme_dark": {
"message": "داكنة",
"description": "option text"
},
"pref_theme_default": {
"message": "النظام/المتصفح",
"description": "option text"
},
"pref_theme_light": {
"message": "فاتحة",
"description": "option text"
},
"pref_ui": {
"message": "واجهة المستخدم",
"description": "Preferences/General; group text"
@ -895,6 +987,20 @@
"message": "استأنف",
"description": "Action for resuming a download"
},
"retrying": {
"message": "يُعيد",
"description": "Status text"
},
"retrying_error": {
"message": "يُعيد -$ERROR$",
"description": "status text",
"placeholders": {
"error": {
"content": "$1",
"example": "Server Error"
}
}
},
"running": {
"message": "يعمل",
"description": "Status text"
@ -943,6 +1049,18 @@
"message": "اضبط قناع تغيير الاسم",
"description": "Menu text; select window"
},
"set_mask_text": {
"message": "اضبط قناع تغيير اسم جديد",
"description": "dialog text"
},
"set_referrer": {
"message": "اضبط المُعيد",
"description": "menu text"
},
"set_referrer_text": {
"message": "اضبط مُعيدًا جديدًا",
"description": "dialog text"
},
"single_batchexamples": {
"message": "الدفعات مدعومة، مثل:",
"description": "Header text; single window"
@ -1123,6 +1241,14 @@
"message": "لن تبدأ أي تنزيلات جديدة",
"description": "Status bar tooltip; manager network icon"
},
"subfolder": {
"message": "المجلد الفرعي:",
"description": "label text"
},
"subfolder_placeholder": {
"message": "ضَع الملفات في هذا المجلد الفرعي لدليل المجلدات في الجهاز",
"description": "placeholder text within an input box"
},
"title": {
"message": "العنوان",
"description": "Column text; Title label (short)"
@ -1166,5 +1292,9 @@
"useonlyonce": {
"message": "استعمله مرة واحدة",
"description": "Label for Use-Once checkboxes"
},
"USER_CANCELED": {
"message": "ألغاها المستخدم",
"description": "Error message"
}
}

View File

@ -642,7 +642,7 @@
"description": "pref text"
},
"pref_retry_time": {
"message": "Опит на всеки (в минути)",
"message": "Нов опит на всеки (в минути)",
"description": "pref text"
},
"pref_show_urls": {
@ -657,6 +657,22 @@
"message": "Опит за намиране на връзки в текста на уебсайта (по-бавно)",
"description": "Preferences/General"
},
"pref_theme": {
"message": "Тема:",
"description": "label text"
},
"pref_theme_dark": {
"message": "Тъмна",
"description": "option text"
},
"pref_theme_default": {
"message": "Системна/Браузър",
"description": "option text"
},
"pref_theme_light": {
"message": "Светла",
"description": "option text"
},
"pref_ui": {
"message": "Потребителски интерфейс",
"description": "Preferences/General; group text"

1300
_locales/da/messages.json Normal file

File diff suppressed because it is too large Load Diff

View File

@ -657,6 +657,22 @@
"message": "Versuche Text-Links in Webseiten zu finden (langsamer)",
"description": "Preferences/General"
},
"pref_theme": {
"message": "Theme:",
"description": "label text"
},
"pref_theme_dark": {
"message": "Dunkel",
"description": "option text"
},
"pref_theme_default": {
"message": "System/Browser",
"description": "option text"
},
"pref_theme_light": {
"message": "Hell",
"description": "option text"
},
"pref_ui": {
"message": "Benutzeroberfläche",
"description": "Preferences/General; group text"

View File

@ -665,6 +665,22 @@
"description": "Preferences/General",
"message": "Try to find links in the website text (slower)"
},
"pref_theme": {
"description": "label text",
"message": "Theme:"
},
"pref_theme_dark": {
"description": "option text",
"message": "Dark"
},
"pref_theme_default": {
"description": "option text",
"message": "System/Browser"
},
"pref_theme_light": {
"description": "option text",
"message": "Light"
},
"pref_ui": {
"description": "Preferences/General; group text",
"message": "User Interface"

View File

@ -657,6 +657,22 @@
"message": "Intentar buscar enlaces en el texto del sitio web (más lento)",
"description": "Preferences/General"
},
"pref_theme": {
"message": "Tema:",
"description": "label text"
},
"pref_theme_dark": {
"message": "Oscuro",
"description": "option text"
},
"pref_theme_default": {
"message": "Sistema/Navegador",
"description": "option text"
},
"pref_theme_light": {
"message": "Claro",
"description": "option text"
},
"pref_ui": {
"message": "Interfaz de usuario",
"description": "Preferences/General; group text"

View File

@ -657,6 +657,22 @@
"message": "Ürita leida linke veebisaidi tekstist (aeglasem)",
"description": "Preferences/General"
},
"pref_theme": {
"message": "Teema:",
"description": "label text"
},
"pref_theme_dark": {
"message": "Tume",
"description": "option text"
},
"pref_theme_default": {
"message": "Süsteem/Veebilehitseja",
"description": "option text"
},
"pref_theme_light": {
"message": "Hele",
"description": "option text"
},
"pref_ui": {
"message": "Kasutajaliides",
"description": "Preferences/General; group text"

View File

@ -657,6 +657,22 @@
"message": "Tenter de trouver des liens dans le texte du site (lent)",
"description": "Preferences/General"
},
"pref_theme": {
"message": "Thème:",
"description": "label text"
},
"pref_theme_dark": {
"message": "Sombre",
"description": "option text"
},
"pref_theme_default": {
"message": "Système/Navigateur",
"description": "option text"
},
"pref_theme_light": {
"message": "Clair",
"description": "option text"
},
"pref_ui": {
"message": "Interface utilisateur",
"description": "Preferences/General; group text"

View File

@ -657,6 +657,22 @@
"message": "Keress hivatkozásokat a honlap szövegében (lassabb)",
"description": "Preferences/General"
},
"pref_theme": {
"message": "Téma:",
"description": "label text"
},
"pref_theme_dark": {
"message": "Sötét",
"description": "option text"
},
"pref_theme_default": {
"message": "Rendszer/Böngésző",
"description": "option text"
},
"pref_theme_light": {
"message": "Világos",
"description": "option text"
},
"pref_ui": {
"message": "Felhasználói felület",
"description": "Preferences/General; group text"

View File

@ -8,7 +8,7 @@
"description": "Language code the locale will use, e.g. de or en-GB or pt-BR"
},
"addpaused": {
"message": "一時停止状態で追加",
"message": "待機状態で追加",
"description": "Action: Add paused"
},
"add_download": {
@ -20,15 +20,15 @@
"description": "Button text (adding filters, limits and such)"
},
"add_paused_once": {
"message": "今回は一時停止で追加",
"message": "今回は待機状態で追加",
"description": "Checkbox label"
},
"add_paused_question": {
"message": "この決定を覚えて、今後新しいダウンロードを常に一時停止で追加しますか?",
"message": "この決定を覚えて、今後新しいダウンロードを常に待機状態で追加しますか?",
"description": "Messagebox text"
},
"add_paused_title": {
"message": "常に一時停止で追加しますか?",
"message": "常に待機状態で追加しますか?",
"description": "Title for the add-paused dialog"
},
"ask_again_later": {
@ -570,27 +570,27 @@
"description": "Window/tab title; Preferences"
},
"pref_add_paused": {
"message": "すぐに開始せず、新しいダウンロードを一時停止状態で追加する",
"message": "すぐに開始せず、新しいダウンロードを待機状態で追加する",
"description": "Preferences/General"
},
"pref_button_type": {
"message": "DownThemAll! アイコンクリック時:",
"message": "DownThemAll! アイコンクリック時:",
"description": "label"
},
"pref_button_type_dta": {
"message": "DownThemAll! 選択画面",
"message": "ダウンロード選択画面",
"description": "label"
},
"pref_button_type_manager": {
"message": "マネージャを開く",
"message": "マネージャを開く",
"description": "label"
},
"pref_button_type_popup": {
"message": "メニュー展開",
"message": "メニュー展開",
"description": "label"
},
"pref_button_type_turbo": {
"message": "OneClick! ダウンロード",
"message": "OneClick! ダウンロード",
"description": "label"
},
"pref_concurrent_downloads": {
@ -654,9 +654,25 @@
"description": "checkbox text"
},
"pref_text_links": {
"message": "Webサイトの文字列リンクを見つけてみる (但し、遅い)",
"message": "Webサイトの文字列からリンクを抽出する (但し、遅い)",
"description": "Preferences/General"
},
"pref_theme": {
"message": "テーマ:",
"description": "label text"
},
"pref_theme_dark": {
"message": "ダーク",
"description": "option text"
},
"pref_theme_default": {
"message": "システム/ブラウザのテーマ",
"description": "option text"
},
"pref_theme_light": {
"message": "ライト",
"description": "option text"
},
"pref_ui": {
"message": "ユーザーインターフェイス",
"description": "Preferences/General; group text"
@ -820,7 +836,7 @@
"description": "Messagebox text"
},
"remove_selected_downloads": {
"message": "選択を削除",
"message": "選択した項目を削除",
"description": "Menu text"
},
"rename": {

View File

@ -191,6 +191,22 @@
"message": "삭제",
"description": "button text"
},
"deletefiles": {
"message": "파일 삭제",
"description": "menu action"
},
"deletefiles_button": {
"message": "삭제",
"description": "button text"
},
"deletefiles_text": {
"message": "정말 다음 파일(들)을 삭제하시겠습니까?",
"description": "messagebox text"
},
"deletefiles_title": {
"message": "파일 삭제",
"description": "messagebox title"
},
"description": {
"message": "설명",
"description": "Description (keep it short); e.g. the description column in select"
@ -275,10 +291,34 @@
"message": "잘못된 URL",
"description": "Error message; single window"
},
"error_noabsolutepath": {
"message": "하위 폴더의 절대 경로는 브라우저에서 지원되지 않습니다",
"description": "Error Message; select/single window"
},
"error_nodotsinpath": {
"message": "하위 폴더의 점(.)은 브라우저에서 지원되지 않습니다",
"description": "Error Message; select/single window"
},
"error_noItemsSelected": {
"message": "선택된 항목 없음",
"description": "Error Message; select window"
},
"export": {
"message": "파일로 내보내기",
"description": "menu text"
},
"export_aria2": {
"message": "aria2 리스트로 내보내기",
"description": "menu text"
},
"export_metalink": {
"message": "메타링크로 내보내기",
"description": "menu text"
},
"export_text": {
"message": "텍스트로 내보내기",
"description": "menu text"
},
"extensionDescription": {
"message": "브라우저용 대량 다운로더",
"description": "DownThemAll! tagline, displayed in about:addons; Please do NOT refer to a specific browser such as firefox, as we will probably support more than one"
@ -331,6 +371,10 @@
"message": "강제 시작",
"description": "Menu text"
},
"import": {
"message": "파일에서 가져오기",
"description": "menu text"
},
"information_title": {
"message": "정보",
"description": "Used in message boxes"
@ -529,6 +573,26 @@
"message": "새 다운로드를 즉시 시작하는 대신 일시중지로 추가",
"description": "Preferences/General"
},
"pref_button_type": {
"message": "DownThemAll! 버튼:",
"description": "label"
},
"pref_button_type_dta": {
"message": "DownThemAll! 선택",
"description": "label"
},
"pref_button_type_manager": {
"message": "관리자 열기",
"description": "label"
},
"pref_button_type_popup": {
"message": "팝업 메뉴",
"description": "label"
},
"pref_button_type_turbo": {
"message": "OneClick!",
"description": "label"
},
"pref_concurrent_downloads": {
"message": "동시 다운로드",
"description": "Preferences/Network"
@ -537,10 +601,6 @@
"message": "다운로드 대기열이 끝나면 알림 표시",
"description": "Preferences/General"
},
"pref_global_turbo": {
"message": "브라우저 버튼을 OneClick!으로 작동",
"description": "Preferences/General"
},
"pref_hide_context": {
"message": "일반 컨텍스트 메뉴 항목 표시 안 함",
"description": "Preferences/General"
@ -549,6 +609,10 @@
"message": "관리자",
"description": "Preferences/General; group text"
},
"pref_manager_in_popup": {
"message": "새 팝업 창에 관리자 열기",
"description": "checkbox text"
},
"pref_manager_tooltip": {
"message": "관리자 탭에서 툴팁 표시",
"description": "Preferences/General"
@ -573,10 +637,22 @@
"message": "다시 시작한 후 누락된 다운로드 제거",
"description": "Preferences/General"
},
"pref_retries": {
"message": "일시적인 오류에 대한 다운로드 재시도 횟수",
"description": "pref text"
},
"pref_retry_time": {
"message": "재시도 간격 (분)",
"description": "pref text"
},
"pref_show_urls": {
"message": "이름 대신 URL 표시",
"description": "Preferences/General"
},
"pref_sounds": {
"message": "소리 재생",
"description": "checkbox text"
},
"pref_text_links": {
"message": "웹 사이트 텍스트에서 링크를 찾도록 시도 (느림)",
"description": "Preferences/General"
@ -895,6 +971,20 @@
"message": "계속",
"description": "Action for resuming a download"
},
"retrying": {
"message": "재시도중",
"description": "Status text"
},
"retrying_error": {
"message": "재시도중 - $ERROR$",
"description": "status text",
"placeholders": {
"error": {
"content": "$1",
"example": "Server Error"
}
}
},
"running": {
"message": "실행중",
"description": "Status text"
@ -943,6 +1033,18 @@
"message": "이름 바꾸기 마스크 설정",
"description": "Menu text; select window"
},
"set_mask_text": {
"message": "새 이름 바꾸기 마스크 설정",
"description": "dialog text"
},
"set_referrer": {
"message": "참조 페이지 설정",
"description": "menu text"
},
"set_referrer_text": {
"message": "새 참조 페이지 설정",
"description": "dialog text"
},
"single_batchexamples": {
"message": "일괄 처리가 지원됩니다, 예:",
"description": "Header text; single window"
@ -1123,6 +1225,14 @@
"message": "새 다운로드가 시작되지 않습니다",
"description": "Status bar tooltip; manager network icon"
},
"subfolder": {
"message": "하위 폴더",
"description": "label text"
},
"subfolder_placeholder": {
"message": "다운로드 디렉토리 내의 이 하위 폴더에 파일을 저장합니다",
"description": "placeholder text within an input box"
},
"title": {
"message": "제목",
"description": "Column text; Title label (short)"
@ -1166,5 +1276,9 @@
"useonlyonce": {
"message": "한번만",
"description": "Label for Use-Once checkboxes"
},
"USER_CANCELED": {
"message": "사용자 취소",
"description": "Error message"
}
}

View File

@ -657,6 +657,22 @@
"message": "Bandyti tinklalapio tekste rasti nuorodas (lėčiau)",
"description": "Preferences/General"
},
"pref_theme": {
"message": "Tema:",
"description": "label text"
},
"pref_theme_dark": {
"message": "Tamsi",
"description": "option text"
},
"pref_theme_default": {
"message": "Sistemos/Naršyklės",
"description": "option text"
},
"pref_theme_light": {
"message": "Šviesi",
"description": "option text"
},
"pref_ui": {
"message": "Naudotojo sąsaja",
"description": "Preferences/General; group text"

File diff suppressed because it is too large Load Diff

View File

@ -156,7 +156,7 @@
"description": "Filter label for the Archives filter"
},
"deffilter_aud": {
"message": "Audio (mp3, flac, wav, …)",
"message": "Dźwięk (mp3, flac, wav, …)",
"description": "Filter label for the Audio filter"
},
"deffilter_bin": {
@ -657,6 +657,22 @@
"message": "Spróbuj znaleźć linki w treści strony internetowej (wolniejsze)",
"description": "Preferences/General"
},
"pref_theme": {
"message": "Motyw:",
"description": "label text"
},
"pref_theme_dark": {
"message": "Ciemny",
"description": "option text"
},
"pref_theme_default": {
"message": "Systemowy/Przeglądarki",
"description": "option text"
},
"pref_theme_light": {
"message": "Jasny",
"description": "option text"
},
"pref_ui": {
"message": "Interfejs Użytkownika",
"description": "Preferences/General; group text"

View File

@ -657,6 +657,22 @@
"message": "Tentar encontrar links no texto do site (mais lento)",
"description": "Preferences/General"
},
"pref_theme": {
"message": "Tema:",
"description": "label text"
},
"pref_theme_dark": {
"message": "Escuro",
"description": "option text"
},
"pref_theme_default": {
"message": "Sistema/Navegador",
"description": "option text"
},
"pref_theme_light": {
"message": "Claro",
"description": "option text"
},
"pref_ui": {
"message": "Interface de usuário",
"description": "Preferences/General; group text"

View File

@ -657,6 +657,22 @@
"message": "Пытаться обнаружить ссылки в тексте на сайте",
"description": "Preferences/General"
},
"pref_theme": {
"message": "Тема:",
"description": "label text"
},
"pref_theme_dark": {
"message": "Тёмная",
"description": "option text"
},
"pref_theme_default": {
"message": "Система/Браузер",
"description": "option text"
},
"pref_theme_light": {
"message": "Светлая",
"description": "option text"
},
"pref_ui": {
"message": "Интерфейс пользователя",
"description": "Preferences/General; group text"

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -17,6 +17,7 @@
"remove-missing-on-init": false,
"retries": 5,
"retry-time": 10,
"theme": "default",
"limits": [
{
"domain": "*",

View File

@ -22,6 +22,8 @@ import {
runtime,
history,
sessions,
// eslint-disable-next-line no-unused-vars
OnInstalled,
} from "./browser";
import { Bus } from "./bus";
import { filterInSitu } from "./util";
@ -135,6 +137,33 @@ class Handler {
}
}
function getMajor(version?: string) {
if (!version) {
return "";
}
const match = version.match(/^\d+\.\d+/);
if (!match) {
return "";
}
return match[0];
}
runtime.onInstalled.addListener(({reason, previousVersion}: OnInstalled) => {
const {version} = runtime.getManifest();
const major = getMajor(version);
const prevMajor = getMajor(previousVersion);
if (reason === "update" && major !== prevMajor) {
tabs.create({
url: `https://about.downthemall.org/changelog/?cur=${major}&prev=${prevMajor}`,
});
}
else if (reason === "install") {
tabs.create({
url: `https://about.downthemall.org/4.0/?cur=${major}`,
});
}
});
locale.then(() => {
const menuHandler = new class Menus extends Handler {
constructor() {
@ -537,7 +566,6 @@ locale.then(() => {
32: "/style/icon32.png",
48: "/style/icon48.png",
64: "/style/icon64.png",
96: "/style/icon96.png",
128: "/style/icon128.png",
256: "/style/icon256.png"
};

View File

@ -73,7 +73,7 @@ class Numeral implements Generator {
this.digits = dir ? rawpieces[0].length : rawpieces[1].length;
this.length = Math.floor(
(this.stop - this.start + (dir ? 1 : -1)) / this.step);
this.preview = this[Symbol.iterator]().next().value;
this.preview = this[Symbol.iterator]().next().value as string;
Object.freeze(this);
}
@ -93,6 +93,60 @@ class Numeral implements Generator {
}
}
class Character implements Generator {
public readonly start: number;
public readonly stop: number;
public readonly step: number;
public readonly length: number;
public readonly preview: string;
constructor(str: string) {
const rawpieces = str.split(":").map(e => e.trim());
const pieces = rawpieces.map((e, i) => {
if (i === 2) {
return reallyParseInt(e);
}
if (e.length > 1) {
throw new Error("Malformed Character sequence");
}
return e.charCodeAt(0);
});
if (pieces.length < 2) {
throw new Error("Invalid input");
}
const [start, stop, step] = pieces;
if (step === 0) {
throw new Error("Invalid step");
}
this.step = !step ? 1 : step;
const dir = this.step > 0;
if (dir && start > stop) {
throw new Error("Invalid sequence");
}
else if (!dir && start < stop) {
throw new Error("Invalid sequence");
}
this.start = start;
this.stop = stop;
this.length = Math.floor(
(this.stop - this.start + (dir ? 1 : -1)) / this.step);
this.preview = this[Symbol.iterator]().next().value as string;
Object.freeze(this);
}
*[Symbol.iterator]() {
const {start, stop, step} = this;
const dir = step > 0;
for (let i = start; (dir ? i <= stop : i >= stop); i += step) {
yield String.fromCharCode(i);
}
}
}
export class BatchGenerator implements Generator {
private readonly gens: Generator[];
@ -120,11 +174,16 @@ export class BatchGenerator implements Generator {
try {
this.gens.push(new Numeral(tok));
}
catch (ex) {
catch {
try {
this.gens.push(new Character(tok));
}
catch {
this.gens.push(new Literal(`[${tok}]`));
this.hasInvalid = true;
}
}
}
if (str) {
this.gens.push(new Literal(str));
}

View File

@ -9,33 +9,33 @@ interface ExtensionListener {
}
export interface MessageSender {
tab?: Tab;
frameId?: number;
id?: number;
url?: string;
tlsChannelId?: string;
readonly tab?: Tab;
readonly frameId?: number;
readonly id?: number;
readonly url?: string;
readonly tlsChannelId?: string;
}
export interface Tab {
id?: number;
incognito?: boolean;
readonly id?: number;
readonly incognito?: boolean;
}
export interface MenuClickInfo {
menuItemId: string | number;
button?: number;
linkUrl?: string;
srcUrl?: string;
readonly menuItemId: string | number;
readonly button?: number;
readonly linkUrl?: string;
readonly srcUrl?: string;
}
export interface RawPort {
error: any;
name: string;
onDisconnect: ExtensionListener;
onMessage: ExtensionListener;
sender?: MessageSender;
readonly error: any;
readonly name: string;
readonly sender?: MessageSender;
readonly onDisconnect: ExtensionListener;
readonly onMessage: ExtensionListener;
disconnect: () => void;
postMessage: (message: any) => void;
}
@ -57,7 +57,7 @@ type Header = {name: string; value: string};
export interface DownloadOptions {
conflictAction: string;
filename: string;
filename?: string;
saveAs: boolean;
url: string;
method?: string;
@ -82,15 +82,22 @@ interface Downloads {
getFileIcon(id: number, options?: any): Promise<string>;
setShelfEnabled(state: boolean): void;
removeFile(manId: number): Promise<void>;
onCreated: ExtensionListener;
onChanged: ExtensionListener;
onErased: ExtensionListener;
readonly onCreated: ExtensionListener;
readonly onChanged: ExtensionListener;
readonly onErased: ExtensionListener;
readonly onDeterminingFilename?: ExtensionListener;
}
interface WebRequest {
onBeforeSendHeaders: WebRequestListener;
onSendHeaders: WebRequestListener;
onHeadersReceived: WebRequestListener;
readonly onBeforeSendHeaders: WebRequestListener;
readonly onSendHeaders: WebRequestListener;
readonly onHeadersReceived: WebRequestListener;
}
export interface OnInstalled {
readonly reason: string;
readonly previousVersion?: string;
readonly temporary: boolean;
}
export const {browserAction} = polyfill;
@ -107,5 +114,7 @@ export const {tabs} = polyfill;
export const {webNavigation} = polyfill;
export const {webRequest}: {webRequest: WebRequest} = polyfill;
export const {windows} = polyfill;
export const {theme} = polyfill;
export const CHROME = navigator.appVersion.includes("Chrome/");
export const OPERA = navigator.appVersion.includes("OPR/");

View File

@ -8,32 +8,35 @@ import {runtime, tabs, RawPort, MessageSender} from "./browser";
export class Port extends EventEmitter {
private port: RawPort | null;
private disconnected = false;
constructor(port: RawPort) {
super();
this.port = port;
let disconnected = false;
const disconnect = () => {
if (disconnected) {
return;
}
disconnected = true;
this.port = null; // Break the cycle
this.emit("disconnect", this, port);
};
// Nasty firefox bug, thus listen for tab removal explicitly
if (port.sender && port.sender.tab && port.sender.tab.id) {
const otherTabId = port.sender.tab.id;
const tabListener = function(tabId: number) {
const tabListener = (tabId: number) => {
if (tabId !== otherTabId) {
return;
}
disconnect();
this.disconnect();
};
tabs.onRemoved.addListener(tabListener);
}
port.onMessage.addListener(this.onMessage.bind(this));
port.onDisconnect.addListener(disconnect);
port.onDisconnect.addListener(this.disconnect.bind(this));
}
disconnect() {
if (this.disconnected) {
return;
}
this.disconnected = true;
const {port} = this;
this.port = null; // Break the cycle
this.emit("disconnect", this, port);
}
get name() {
@ -120,6 +123,9 @@ export const Bus = new class extends EventEmitter {
port.disconnect();
return;
}
this.ports.emit(port.name, new Port(port));
const wrapped = new Port(port);
if (!this.ports.emit(port.name, wrapped)) {
wrapped.disconnect();
}
}
}();

View File

@ -108,7 +108,7 @@ export function importText(data: string) {
if (data.includes(NS_METALINK_RFC5854)) {
return importMeta4(data);
}
const splitter = /(.+)\n|(.+)$/g;
const splitter = /((?:.|\r)+)\n|(.+)$/g;
const spacer = /^\s+/;
let match;
let current: BaseItem | undefined = undefined;

4
lib/ipreg.ts Normal file
View File

@ -0,0 +1,4 @@
"use strict";
// License: MIT
export const IPReg = /^(?:(?:[0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])[.]){3}(?:[0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])$|^(?:(?:(?:[0-9a-fA-F]{1,4}):){7}(?:(?:[0-9a-fA-F]{1,4})|:)|(?:(?:[0-9a-fA-F]{1,4}):){6}(?:((?:[0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])[.]){3}(?:[0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])|:(?:[0-9a-fA-F]{1,4})|:)|(?:(?:[0-9a-fA-F]{1,4}):){5}(?::((?:[0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])[.]){3}(?:[0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])|(:(?:[0-9a-fA-F]{1,4})){1,2}|:)|(?:(?:[0-9a-fA-F]{1,4}):){4}(?:(:(?:[0-9a-fA-F]{1,4})){0,1}:((?:[0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])[.]){3}(?:[0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])|(:(?:[0-9a-fA-F]{1,4})){1,3}|:)|(?:(?:[0-9a-fA-F]{1,4}):){3}(?:(:(?:[0-9a-fA-F]{1,4})){0,2}:((?:[0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])[.]){3}(?:[0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])|(:(?:[0-9a-fA-F]{1,4})){1,4}|:)|(?:(?:[0-9a-fA-F]{1,4}):){2}(?:(:(?:[0-9a-fA-F]{1,4})){0,3}:((?:[0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])[.]){3}(?:[0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])|(:(?:[0-9a-fA-F]{1,4})){1,5}|:)|(?:(?:[0-9a-fA-F]{1,4}):){1}(?:(:(?:[0-9a-fA-F]{1,4})){0,4}:((?:[0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])[.]){3}(?:[0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])|(:(?:[0-9a-fA-F]{1,4})){1,6}|:)|(?::((?::(?:[0-9a-fA-F]{1,4})){0,5}:((?:[0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])[.]){3}(?:[0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])|(?::(?:[0-9a-fA-F]{1,4})){1,7}|:)))(%[0-9a-zA-Z]{1,})?$/;

View File

@ -191,6 +191,7 @@ export class BaseDownload {
rv.destPath = dest.path;
rv.destFull = dest.full;
rv.currentName = this.browserName || rv.destName || rv.finalName;
rv.currentFull = `${dest.path}/${rv.currentName}`;
rv.error = this.error;
rv.ext = this.renamer.p_ext;
rv.retries = this.retries;

View File

@ -22,7 +22,8 @@ import {
RUNNING,
RETRYING
} from "./state";
import { Preroller } from "./preroller";
// eslint-disable-next-line no-unused-vars
import { Preroller, PrerollResults } from "./preroller";
function isRecoverable(error: string) {
switch (error) {
@ -130,11 +131,13 @@ export class Download extends BaseDownload {
}
const options: DownloadOptions = {
conflictAction: await Prefs.get("conflict-action"),
filename: this.dest.full,
saveAs: false,
url: this.url,
headers: [],
};
if (!CHROME) {
options.filename = this.dest.full;
}
if (!CHROME && this.private) {
options.incognito = true;
}
@ -194,15 +197,7 @@ export class Download extends BaseDownload {
if (!res) {
return;
}
if (res.mime) {
this.mime = res.mime;
}
if (res.name) {
this.serverName = res.name;
}
if (res.error) {
this.cancelAccordingToError(res.error);
}
this.adoptPrerollResults(res);
}
catch (ex) {
console.error("Failed to preroll", this, ex.toString(), ex.stack, ex);
@ -215,6 +210,18 @@ export class Download extends BaseDownload {
}
}
adoptPrerollResults(res: PrerollResults) {
if (res.mime) {
this.mime = res.mime;
}
if (res.name) {
this.serverName = res.name;
}
if (res.error) {
this.cancelAccordingToError(res.error);
}
}
resume(forced = false) {
if (!(FORCABLE & this.state)) {
return;
@ -391,4 +398,27 @@ export class Download extends BaseDownload {
this.setMissing();
}
}
updatefromSuggestion(state: any) {
const res: PrerollResults = {};
if (state.mime) {
res.mime = state.mime;
}
if (state.filename) {
res.name = state.filename;
}
if (state.finalUrl) {
res.finalURL = state.finalUrl;
const detected = Preroller.maybeFindNameFromSearchParams(this, res);
if (detected) {
res.name = detected;
}
}
try {
this.adoptPrerollResults(res);
}
finally {
this.markDirty();
}
}
}

View File

@ -16,7 +16,7 @@ import { Download } from "./download";
import { ManagerPort } from "./port";
import { Scheduler } from "./scheduler";
import { Limits } from "./limits";
import { downloads, runtime, webRequest, CHROME } from "../browser";
import { downloads, runtime, webRequest, CHROME, OPERA } from "../browser";
const US = runtime.getURL("");
@ -61,6 +61,9 @@ export class Manager extends EventEmitter {
private deadlineTimer: number;
constructor() {
if (!document.location.href.includes("background")) {
throw new Error("Not on background");
}
super();
this.active = true;
this.shouldReload = false;
@ -82,6 +85,10 @@ export class Manager extends EventEmitter {
downloads.onChanged.addListener(this.onChanged.bind(this));
downloads.onErased.addListener(this.onErased.bind(this));
if (CHROME && downloads.onDeterminingFilename) {
downloads.onDeterminingFilename.addListener(
this.onDeterminingFilename.bind(this));
}
Bus.onPort("manager", (port: Port) => {
const mport = new ManagerPort(this, port);
@ -89,6 +96,7 @@ export class Manager extends EventEmitter {
this.ports.delete(mport);
});
this.ports.add(mport);
return true;
});
Limits.on("changed", () => {
this.resetScheduler();
@ -157,6 +165,20 @@ export class Manager extends EventEmitter {
this.manIds.delete(downloadId);
}
onDeterminingFilename(state: any, suggest: Function) {
const download = this.manIds.get(state.id);
if (!download) {
return;
}
try {
download.updatefromSuggestion(state);
}
finally {
const suggestion = {filename: download.dest.full};
suggest(suggestion);
}
}
async resetScheduler() {
this.scheduler = null;
await this.startNext();
@ -218,7 +240,7 @@ export class Manager extends EventEmitter {
if (this.notifiedFinished || this.running.size || this.retrying.size) {
return;
}
if (SOUNDS.value) {
if (SOUNDS.value && !OPERA) {
const audio = new Audio(runtime.getURL("/style/done.opus"));
audio.addEventListener("canplaythrough", () => audio.play());
audio.addEventListener("ended", () => document.body.removeChild(audio));

View File

@ -9,6 +9,8 @@ import { BaseDownload } from "./basedownload";
import { Manager } from "./man";
// eslint-disable-next-line no-unused-vars
import { Port } from "../bus";
// eslint-disable-next-line no-unused-vars
import { BaseItem } from "../item";
type SID = {sid: number};
type SIDS = {
@ -42,6 +44,9 @@ export class ManagerPort {
port.on("prefs", () => {
openPrefs();
});
port.on("import", ({items}: {items: BaseItem[]}) => {
API.regular(items, []);
});
port.on("all", () => this.sendAll());
port.on("removeSids", this.onMsgRemoveSids);
port.on("showSingle", async () => {

View File

@ -54,6 +54,9 @@ export class Preroller {
}
get shouldPreroll() {
if (CHROME) {
return false;
}
const {uURL, renamer} = this.download;
const {pathname, search, host} = uURL;
if (PREROLL_NOPE.has(host)) {
@ -167,39 +170,15 @@ export class Preroller {
rv.mime = type.essence;
}
const {p_ext: ext} = this.download.renamer;
const dispHeader = headers.get("content-disposition");
if (dispHeader) {
const file = CDPARSER.parse(dispHeader);
// Sanitize
rv.name = sanitizePath(file.replace(/[/\\]+/g, "-"));
}
else if (!ext || PREROLL_SEARCHEXTS.has(ext.toLocaleLowerCase())) {
const {searchParams} = this.download.uURL;
let detected = "";
for (const [, value] of searchParams) {
if (!NAME_TESTER.test(value)) {
continue;
}
const p = parsePath(value);
if (!p.base || !p.ext) {
continue;
}
if (!MimeDB.hasExtension(p.ext)) {
continue;
}
if (rv.mime) {
const mime = MimeDB.getMime(rv.mime);
if (mime && !mime.extensions.has(p.ext.toLowerCase())) {
continue;
}
}
const sanitized = sanitizePath(p.name);
if (sanitized.length <= detected.length) {
continue;
}
detected = sanitized;
}
else {
const detected = Preroller.maybeFindNameFromSearchParams(
this.download, rv);
if (detected) {
rv.name = detected;
}
@ -231,4 +210,43 @@ export class Preroller {
return rv;
}
static maybeFindNameFromSearchParams(
download: Download, res: PrerollResults) {
const {p_ext: ext} = download.renamer;
if (ext && !PREROLL_SEARCHEXTS.has(ext.toLocaleLowerCase())) {
return undefined;
}
return Preroller.findNameFromSearchParams(download.uURL, res.mime);
}
static findNameFromSearchParams(url: URL, mimetype?: string) {
const {searchParams} = url;
let detected = "";
for (const [, value] of searchParams) {
if (!NAME_TESTER.test(value)) {
continue;
}
const p = parsePath(value);
if (!p.base || !p.ext) {
continue;
}
if (!MimeDB.hasExtension(p.ext)) {
continue;
}
if (mimetype) {
const mime = MimeDB.getMime(mimetype);
if (mime && !mime.extensions.has(p.ext.toLowerCase())) {
continue;
}
}
const sanitized = sanitizePath(p.name);
if (sanitized.length <= detected.length) {
continue;
}
detected = sanitized;
}
return detected;
}
}

View File

@ -99,6 +99,5 @@ export class PrefWatcher {
changed(prefs: any, key: string, value: any) {
this.value = value;
return true;
}
}

View File

@ -9,11 +9,12 @@ import { donate, openPrefs, openUrls } from "./windowutils";
// eslint-disable-next-line no-unused-vars
import { filters, FAST, Filter } from "./filters";
import { WindowStateTracker } from "./windowstatetracker";
import { windows } from "./browser";
import { windows, CHROME } from "./browser";
// eslint-disable-next-line no-unused-vars
import { BaseItem } from "./item";
interface BaseMatchedItem extends BaseItem {
sidx?: number;
matched?: string | null;
prevMatched?: string | null;
}
@ -29,6 +30,7 @@ function computeSelection(
onlyFast: boolean): ItemDelta[] {
let ws = items.map((item, idx: number) => {
item.idx = item.idx || idx;
item.sidx = item.sidx || idx;
const {matched = null} = item;
item.prevMatched = matched;
item.matched = null;
@ -52,7 +54,7 @@ function computeSelection(
}
return items.filter(item => item.prevMatched !== item.matched).map(item => {
return {
idx: item.idx,
idx: item.sidx as number,
matched: item.matched
};
});
@ -98,10 +100,16 @@ export async function select(links: BaseItem[], media: BaseItem[]) {
type: "popup",
});
const window = await windows.create(windowOptions);
tracker.track(window.id, null);
tracker.track(window.id);
try {
if (!CHROME) {
windows.update(window.id, tracker.getOptions({}));
}
const port = await Promise.race<Port>([
new Promise<Port>(resolve => Bus.oncePort("select", resolve)),
new Promise<Port>(resolve => Bus.oncePort("select", port => {
resolve(port);
return true;
})),
timeout<Port>(5 * 1000)]);
if (!port.isSelf) {
throw Error("Invalid sender connected");

View File

@ -6,7 +6,7 @@ import { Bus, Port } from "./bus";
import { WindowStateTracker } from "./windowstatetracker";
import { Promised, timeout } from "./util";
import { donate } from "./windowutils";
import { windows } from "./browser";
import { windows, CHROME } from "./browser";
// eslint-disable-next-line no-unused-vars
import { BaseItem } from "./item";
@ -21,10 +21,16 @@ export async function single(item: BaseItem | null) {
type: "popup",
});
const window = await windows.create(windowOptions);
tracker.track(window.id, null);
tracker.track(window.id);
try {
if (!CHROME) {
windows.update(window.id, tracker.getOptions({}));
}
const port: Port = await Promise.race<Port>([
new Promise<Port>(resolve => Bus.oncePort("single", resolve)),
new Promise<Port>(resolve => Bus.oncePort("single", port => {
resolve(port);
return true;
})),
timeout<Port>(5 * 1000)]);
if (!port.isSelf) {
throw Error("Invalid sender connected");

View File

@ -3,6 +3,7 @@
import * as psl from "psl";
import { identity, memoize } from "./memoize";
import { IPReg } from "./ipreg";
export { debounce } from "../uikit/lib/util";
export class Promised {
@ -237,7 +238,10 @@ export interface URLd extends URL {
Object.defineProperty(URL.prototype, "domain", {
get() {
try {
return hostToDomain(this.host) || this.host;
const {hostname} = this;
return IPReg.test(hostname) ?
hostname :
hostToDomain(hostname) || hostname;
}
catch (ex) {
console.error(ex);

View File

@ -3,6 +3,8 @@
import { Prefs } from "./prefs";
import { windows } from "./browser";
// eslint-disable-next-line no-unused-vars
import { Port } from "./bus";
const VALID_WINDOW_STATES = Object.freeze(new Set(["normal", "maximized"]));
@ -80,6 +82,7 @@ export class WindowStateTracker {
if (!this.windowId) {
return;
}
try {
const window = await windows.get(this.windowId);
if (!VALID_WINDOW_STATES.has(window.state)) {
return;
@ -97,17 +100,30 @@ export class WindowStateTracker {
}
await this.save();
}
catch {
// ignored
}
}
track(windowId: number, port: any) {
track(windowId: number, port?: Port) {
if (port) {
port.on("resized", this.update);
port.on("unload", e => this.finalize(e));
port.on("disconnect", this.finalize.bind(this));
}
this.windowId = windowId;
}
async finalize() {
async finalize(state?: any) {
if (state) {
this.left = state.left;
this.top = state.top;
}
await this.update();
this.windowId = 0;
if (state) {
await this.save();
}
}
async save() {

View File

@ -1,11 +1,15 @@
"use strict";
// License: MIT
import { windows, tabs, runtime } from "../lib/browser";
import { windows, tabs, runtime, CHROME } from "../lib/browser";
import { getManager } from "./manager/man";
import DEFAULT_ICONS from "../data/icons.json";
import { Prefs } from "./prefs";
import { _ } from "./i18n";
import { WindowStateTracker } from "./windowstatetracker";
// eslint-disable-next-line no-unused-vars
import { Port, Bus } from "./bus";
import { timeout } from "./util";
const DONATE_URL = "https://www.downthemall.org/howto/donate/";
const DONATE_LANG_URLS = Object.freeze(new Map([
@ -114,11 +118,37 @@ export async function openManager(focus = true) {
await windows.update(tab.windowId, {focused: true});
return;
}
const windowOptions = {
const tracker = new WindowStateTracker("manager", {
minWidth: 700,
minHeight: 500,
});
await tracker.init();
const windowOptions = tracker.getOptions({
url,
type: "popup",
};
await windows.create(windowOptions);
});
const window = await windows.create(windowOptions);
tracker.track(window.id);
try {
if (!CHROME) {
windows.update(window.id, tracker.getOptions({}));
}
const port = await Promise.race<Port>([
new Promise<Port>(resolve => Bus.oncePort("manager", port => {
resolve(port);
return true;
})),
timeout<Port>(5 * 1000)]);
if (!port.isSelf) {
throw Error("Invalid sender connected");
}
tracker.track(window.id, port);
}
catch (ex) {
console.error("couldn't track manager", ex);
}
return;
}
if (focus) {

View File

@ -1,7 +1,7 @@
{
"manifest_version": 2,
"name": "DownThemAll!",
"version": "4.1.1",
"version": "4.2.6",
"description": "__MSG_extensionDescription__",
"homepage_url": "https://downthemall.org/",
@ -16,7 +16,6 @@
"32": "style/icon32.png",
"48": "style/icon48.png",
"64": "style/icon64.png",
"96": "style/icon96.png",
"128": "style/icon128.png",
"256": "style/icon256.png"
},
@ -33,6 +32,7 @@
"sessions",
"storage",
"tabs",
"theme",
"webNavigation",
"webRequest",
"webRequestBlocking"
@ -53,7 +53,6 @@
"32": "style/icon32.png",
"48": "style/icon48.png",
"64": "style/icon64.png",
"96": "style/icon96.png",
"128": "style/icon128.png",
"256": "style/icon256.png"
},

View File

@ -8,6 +8,7 @@
},
"scripts": {
"build": "util/build.py",
"build:cleanup": "rm -rf bundles",
"build:bundles": "webpack",
"build:regexps": "node util/makexregexps.js > data/xregexps.json",
"stats": "cloc --vcs=git --exclude-lang=Markdown,SVG",
@ -18,24 +19,24 @@
"author": "Nils Maier",
"license": "MIT",
"devDependencies": {
"@types/node": "^12.7.2",
"@typescript-eslint/eslint-plugin": "^2.0.0",
"@typescript-eslint/parser": "^2.0.0",
"@types/node": "^12.7.8",
"@typescript-eslint/eslint-plugin": "^2.3.2",
"@typescript-eslint/parser": "^2.3.2",
"chai": "^4.1.2",
"eslint": "^6.2.2",
"mocha": "^6.2.0",
"ts-loader": "^6.0.4",
"ts-node": "^8.3.0",
"typescript": "^3.5.3",
"webpack": "^4.39.3",
"webpack-cli": "^3.3.7",
"eslint": "^6.5.1",
"mocha": "^6.2.1",
"ts-loader": "^6.2.0",
"ts-node": "^8.4.1",
"typescript": "^3.6.3",
"webpack": "^4.41.0",
"webpack-cli": "^3.3.9",
"xregexp": "^4.2.4"
},
"dependencies": {
"@types/psl": "^1.1.0",
"@types/whatwg-mimetype": "^2.1.0",
"psl": "^1.3.0",
"webextension-polyfill": "^0.4.0",
"psl": "^1.4.0",
"webextension-polyfill": "^0.5.0",
"whatwg-mimetype": "^2.3.0"
}
}

View File

@ -121,14 +121,26 @@ class Gatherer {
*collectImageInternal(img: HTMLImageElement) {
try {
const src = img.currentSrc || img.src;
{
const {src} = img;
const item = this.makeItem(src, img);
if (item) {
item.fileName = "";
item.description = item.title;
yield item;
}
}
{
const {currentSrc} = img;
const item = this.makeItem(currentSrc, img);
if (item) {
item.fileName = "";
item.description = item.title;
yield item;
}
}
{
const {srcset} = img;
if (!srcset) {
return;
@ -146,6 +158,7 @@ class Gatherer {
}
}
}
}
catch (ex) {
console.error("oops image", ex.toString(), ex.stack, ex);
}

View File

@ -2,11 +2,25 @@
/* License: gpl-v2 */
:root {
--general-color: #2a2a2e;
--general-bgcolor: rgb(249, 249, 250);
--general-border-color: lightgray;
--general-input-color: black;
--general-input-bgcolor: white;
--general-button-color: black;
--general-button-bgcolor: rgb(246, 246, 246);
--general-button-bgcolor-hover: white;
--general-button-shadow: 0px 0px 5px 1px rgba(128, 128, 128, 0.5);
--menu-bgcolor: white;
--menu-bgcolor-hover: #2283fb;
--table-bgcolor: white;
--table-head-bgcolor: white;
--toolbar-bg-color: rgb(248, 134, 6);
--toolbar-active-border-color: #478de7;
--toolbar-hover-border-color: red;
--toolbar-hover-background: rgb(247, 149, 37);
--toolbar-border-width: 2px;
--toolbar-border: 1px solid rgba(255, 255, 255, 0.5);
--add-color: navy;
--queue-color: gray;
--pause-color: #ffa318;
@ -20,24 +34,81 @@
--maskbutton-color: rgb(236, 185, 16);
--missing-color: rgb(0, 82, 204);
--open-color: rgba(236, 185, 16, 0.8);
--status-icon-color: #363636;
--status-icon-color-hover: #6e6d6d;
--tile-url: url(tile.png);
--file-icon-image-color: rgb(17, 107, 163);
--popup-bgcolor: #fff;
--popup-color: #0c0c0d;
--modal-color: black;
--modal-bgcolor: white;
}
html.dark {
--add-color: lightblue;
--error-color: rgb(130, 3, 22);
--running-color: #67a041;
--finishing-color: #4bb111;
--done-color: #006f00;
--pause-color: #cf9308;
--general-bgcolor: #2a2a2e;
--general-border-color: rgb(85, 85, 85);
--general-button-bgcolor-hover: black;
--general-button-bgcolor: rgb(36, 36, 36);
--general-button-color: white;
--general-color: rgb(249, 249, 250);
--menu-bgcolor: black;
--menu-bgcolor-hover: #1a6bce;
--table-bgcolor: #1a1a1e;
--table-head-bgcolor: #3a3a3e;
--toolbar-bg-color: rgb(202, 108, 0);
--status-icon-color: #b9b9b9;
--status-icon-color-hover: #e2e2e2;
--tile-url: url(tile-dark.png?3);
--toolbar-border: 1px solid rgba(30, 30, 30, 0.5);
--file-icon-image-color: rgb(21, 130, 197);
--popup-bgcolor: #4a4a4f;
--popup-color: rgb(249, 249, 250);
--general-button-shadow: 0px 0px 7px 1px rgba(128, 128, 128, 0.8);
--modal-color: white;
--modal-bgcolor: #333;
scrollbar-color: rgba(249, 249, 250, 0.4) rgba(20, 20, 25, 0.3);
}
html.dark a {
color: lightblue;
}
html.dark ::-webkit-scrollbar {
background: rgba(20, 20, 25, 0.3);
}
html.dark ::-webkit-scrollbar-thumb {
background: rgba(249, 249, 250, 0.4);
}
html.dark ::-webkit-scrollbar-corner {
background: #000;
}
html[data-platform="mac"] {
--folder-color: rgb(4, 102, 214);
}
html, body {
html,
body {
font-size: 10pt !important;
}
@font-face {
font-family: 'downthemall';
src: url('downthemall.woff2?75791791') format('woff2');
font-family: "downthemall";
src: url("downthemall.woff2?75791791") format("woff2");
font-weight: normal;
font-style: normal;
}
[class^="icon-"]:before, [class*=" icon-"]:before {
[class^="icon-"]:before,
[class*=" icon-"]:before {
font-family: "downthemall";
font-style: normal;
font-weight: normal;
@ -54,72 +125,187 @@ html, body {
-moz-osx-font-smoothing: grayscale;
}
.icon-add:before { content: '\e800'; } /* '' */
.icon-addsegment:before { content: '\e801'; } /* '' */
.icon-bottom:before { content: '\e802'; } /* '' */
.icon-picture:before { content: '\e803'; } /* '' */
.icon-circle:before { content: '\e804'; } /* '' */
.icon-delete:before { content: '\e805'; } /* '' */
.icon-done:before { content: '\e806'; } /* '' */
.icon-down:before { content: '\e807'; } /* '' */
.icon-download:before { content: '\e808'; } /* '' */
.icon-dupe:before { content: '\e809'; } /* '' */
.icon-error:before { content: '\e80a'; } /* '' */
.icon-failed:before { content: '\e80b'; } /* '' */
.icon-file:before { content: '\e80c'; } /* '' */
.icon-find:before { content: '\e80d'; } /* '' */
.icon-folder:before { content: '\e80e'; } /* '' */
.icon-force:before { content: '\e80f'; } /* '' */
.icon-go:before { content: '\e810'; } /* '' */
.icon-import:before { content: '\e811'; } /* '' */
.icon-info:before { content: '\e812'; } /* '' */
.icon-launch:before { content: '\e813'; } /* '' */
.icon-missing:before { content: '\e814'; } /* '' */
.icon-network-off:before { content: '\e815'; } /* '' */
.icon-network-on:before { content: '\e816'; } /* '' */
.icon-pause:before { content: '\e817'; } /* '' */
.icon-remsegment:before { content: '\e818'; } /* '' */
.icon-rename:before { content: '\e819'; } /* '' */
.icon-save:before { content: '\e81a'; } /* '' */
.icon-settings:before { content: '\e81b'; } /* '' */
.icon-top:before { content: '\e81c'; } /* '' */
.icon-unchecked:before { content: '\e81d'; } /* '' */
.icon-unlimited:before { content: '\e81e'; } /* '' */
.icon-link:before { content: '\e81f'; } /* '' */
.icon-up:before { content: '\e820'; } /* '' */
.icon-privacy:before { content: '\e821'; } /* '' */
.icon-tags:before { content: '\e822'; } /* '' */
.icon-attention:before { content: '\e823'; } /* '' */
.icon-notification:before { content: '\e824'; } /* '' */
.icon-file-video:before { content: '\e825'; } /* '' */
.icon-file-generic:before { content: '\e826'; } /* '' */
.icon-question-dark:before { content: '\e827'; } /* '' */
.icon-forward:before { content: '\e828'; } /* '' */
.icon-filter:before { content: '\f0b0'; } /* '' */
.icon-donate:before { content: '\f0d6'; } /* '' */
.icon-file-doc:before { content: '\f0f6'; } /* '' */
.icon-interface:before { content: '\f108'; } /* '' */
.icon-folder-1:before { content: '\f115'; } /* '' */
.icon-sort-asc:before { content: '\f15d'; } /* '' */
.icon-sort-desc:before { content: '\f15e'; } /* '' */
.icon-file-pdf:before { content: '\f1c1'; } /* '' */
.icon-file-word:before { content: '\f1c2'; } /* '' */
.icon-file-image:before { content: '\f1c5'; } /* '' */
.icon-file-archive:before { content: '\f1c6'; } /* '' */
.icon-file-audio:before { content: '\f1c7'; } /* '' */
.icon-toggle-off:before { content: '\f204'; } /* '' */
.icon-toggle-on:before { content: '\f205'; } /* '' */
.icon-server:before { content: '\f233'; } /* '' */
.icon-question-light:before { content: '\f29c'; } /* '' */
.icon-add:before {
content: "\e800";
} /* '' */
.icon-addsegment:before {
content: "\e801";
} /* '' */
.icon-bottom:before {
content: "\e802";
} /* '' */
.icon-picture:before {
content: "\e803";
} /* '' */
.icon-circle:before {
content: "\e804";
} /* '' */
.icon-delete:before {
content: "\e805";
} /* '' */
.icon-done:before {
content: "\e806";
} /* '' */
.icon-down:before {
content: "\e807";
} /* '' */
.icon-download:before {
content: "\e808";
} /* '' */
.icon-dupe:before {
content: "\e809";
} /* '' */
.icon-error:before {
content: "\e80a";
} /* '' */
.icon-failed:before {
content: "\e80b";
} /* '' */
.icon-file:before {
content: "\e80c";
} /* '' */
.icon-find:before {
content: "\e80d";
} /* '' */
.icon-folder:before {
content: "\e80e";
} /* '' */
.icon-force:before {
content: "\e80f";
} /* '' */
.icon-go:before {
content: "\e810";
} /* '' */
.icon-import:before {
content: "\e811";
} /* '' */
.icon-info:before {
content: "\e812";
} /* '' */
.icon-launch:before {
content: "\e813";
} /* '' */
.icon-missing:before {
content: "\e814";
} /* '' */
.icon-network-off:before {
content: "\e815";
} /* '' */
.icon-network-on:before {
content: "\e816";
} /* '' */
.icon-pause:before {
content: "\e817";
} /* '' */
.icon-remsegment:before {
content: "\e818";
} /* '' */
.icon-rename:before {
content: "\e819";
} /* '' */
.icon-save:before {
content: "\e81a";
} /* '' */
.icon-settings:before {
content: "\e81b";
} /* '' */
.icon-top:before {
content: "\e81c";
} /* '' */
.icon-unchecked:before {
content: "\e81d";
} /* '' */
.icon-unlimited:before {
content: "\e81e";
} /* '' */
.icon-link:before {
content: "\e81f";
} /* '' */
.icon-up:before {
content: "\e820";
} /* '' */
.icon-privacy:before {
content: "\e821";
} /* '' */
.icon-tags:before {
content: "\e822";
} /* '' */
.icon-attention:before {
content: "\e823";
} /* '' */
.icon-notification:before {
content: "\e824";
} /* '' */
.icon-file-video:before {
content: "\e825";
} /* '' */
.icon-file-generic:before {
content: "\e826";
} /* '' */
.icon-question-dark:before {
content: "\e827";
} /* '' */
.icon-forward:before {
content: "\e828";
} /* '' */
.icon-filter:before {
content: "\f0b0";
} /* '' */
.icon-donate:before {
content: "\f0d6";
} /* '' */
.icon-file-doc:before {
content: "\f0f6";
} /* '' */
.icon-interface:before {
content: "\f108";
} /* '' */
.icon-folder-1:before {
content: "\f115";
} /* '' */
.icon-sort-asc:before {
content: "\f15d";
} /* '' */
.icon-sort-desc:before {
content: "\f15e";
} /* '' */
.icon-file-pdf:before {
content: "\f1c1";
} /* '' */
.icon-file-word:before {
content: "\f1c2";
} /* '' */
.icon-file-image:before {
content: "\f1c5";
} /* '' */
.icon-file-archive:before {
content: "\f1c6";
} /* '' */
.icon-file-audio:before {
content: "\f1c7";
} /* '' */
.icon-toggle-off:before {
content: "\f204";
} /* '' */
.icon-toggle-on:before {
content: "\f205";
} /* '' */
.icon-server:before {
content: "\f233";
} /* '' */
.icon-question-light:before {
content: "\f29c";
} /* '' */
@media (min-resolution: 144dpi) {
[class^="icon-file-"]:before, [class*=" icon-file-"]:before {
[class^="icon-file-"]:before,
[class*=" icon-file-"]:before {
font-weight: bold !important;
}
}
.icon-file-image {
color: rgb(17, 107, 163);
color: var(--file-icon-image-color);
}
.icon-file-pdf,
@ -140,18 +326,29 @@ html, body {
color: rgb(202, 81, 198);
}
body, html {
background: #F6F6F8;
color: #0C0C0D;
body,
html {
font: message-box;
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Ubuntu', 'Helvetica Neue', sans-serif;
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Ubuntu",
"Helvetica Neue", sans-serif;
height: 100%;
margin: 0;
padding: 0;
width: 100%;
color: var(--general-color);
background: var(--general-bgcolor);
}
h1, h2, h3, h4 {
html#popup,
html#popup > body {
color: var(--popup-color);
background: var(--popup-bgcolor);
}
h1,
h2,
h3,
h4 {
font: caption;
font-weight: bold;
}
@ -191,7 +388,11 @@ section {
}
.virtualtable-column:active {
background-image: linear-gradient(to top, rgba(0,0,0,0.03), rgba(128,128,128,0.1));
background-image: linear-gradient(
to top,
rgba(0, 0, 0, 0.03),
rgba(128, 128, 128, 0.1)
);
}
th.virtualtable {
@ -218,8 +419,12 @@ td.virtualtable {
font-size: 12px;
align-items: stretch;
justify-items: center;
background: linear-gradient(to bottom, rgba(128,128,128,0.1) 0%,rgba(0,0,0,0) 100%);
border-top: 1px solid rgba(128,128,128,0.6);
background: linear-gradient(
to bottom,
rgba(128, 128, 128, 0.1) 0%,
rgba(0, 0, 0, 0) 100%
);
border-top: 1px solid rgba(128, 128, 128, 0.6);
display: flex;
margin-bottom: 1ex;
overflow: auto;
@ -246,15 +451,15 @@ td.virtualtable {
flex-grow: 3;
margin-right: 2ex;
padding-right: 1ex;
border-right: 1px dotted rgba(128,128,128,0.6);
border-right: 1px dotted rgba(128, 128, 128, 0.6);
}
#statusPrefs {
cursor: pointer;
color: #363636;
color: var(--status-icon-color);
}
#statusPrefs:hover {
color: #6e6d6d;
color: var(--status-icon-color-hover);
}
.dropdown {
@ -272,13 +477,14 @@ td.virtualtable {
outline: none;
position: absolute;
top: 0;
width:100%;
width: 100%;
}
.dropdown input {
-moz-appearance: none;
-webkit-appearance: none;
appearance: none;
color: black;
background: white;
border: none;
bottom: 2px;
@ -301,7 +507,6 @@ td.virtualtable {
padding-bottom: 1ex;
}
@supports (not (-moz-appearance: none)) {
.dropdown select {
background: white;
@ -371,3 +576,54 @@ td.virtualtable {
#maskButton {
color: var(--maskbutton-color);
}
table.virtualtable,
.virtualtable-body {
color: var(--general-color);
background: var(--table-bgcolor);
}
.virtualtable-head,
.virtualtable-head > table {
background: var(--table-head-bgcolor) !important;
}
.virtualtable-column {
border-right: 1px solid var(--general-border-color);
}
.virtualtable-cell {
border-right: 1px dotted var(--general-border-color);
}
.virtualtable-head,
.virtualtable-body {
border-bottom: 1px solid var(--general-border-color);
}
ul.context-menu,
ul.context-menu ul {
color: var(--general-color);
background: var(--menu-bgcolor);
}
.context-menu-item:hover:not(.context-menu-seperator),
.context-menu-item:hover:not(.context-menu-seperator) > * {
background: var(--menu-bgcolor-hover);
}
html.dark .context-menu-item.disabled,
html.dark .context-menu-item.disabled > * {
opacity: 0.7;
}
input {
color: var(--general-input-color);
background: var(--general-input-bgcolor);
border: inherit;
padding: 2px;
}
.modal-dialog {
color: var(--modal-color);
background: var(--modal-bgcolor);
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 164 KiB

After

Width:  |  Height:  |  Size: 75 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.6 KiB

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 673 B

After

Width:  |  Height:  |  Size: 785 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 KiB

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 20 KiB

After

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.6 KiB

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.8 KiB

After

Width:  |  Height:  |  Size: 3.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.8 KiB

After

Width:  |  Height:  |  Size: 5.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.3 KiB

View File

@ -16,7 +16,7 @@ body > * {
#toolbar {
display: flex;
margin: 0;
background: var(--toolbar-bg-color) url(tile.png) repeat-x;
background: var(--toolbar-bg-color) var(--tile-url) repeat-x;
}
#toolbar .spacer {
@ -42,9 +42,9 @@ body > * {
justify-content: center;
align-items: center;
cursor: pointer;
box-shadow: 0px 0px 5px 1px rgba(128,128,128,0.5);
background: rgb(246,246,246);
color: black;
box-shadow: var(--general-button-shadow);
background: var(--general-button-bgcolor);
color: var(--general-button-color);
transition: box-shadow 0.5s, background 1s;
font-size: 24px;
line-height: 24px;
@ -60,7 +60,7 @@ body > * {
}
#toolbar > .button:hover:not(.disabled) {
background: white;
background: var(--general-button-bgcolor-hover);
box-shadow: 0px 0px 7px 2px rgba(70,70,70,0.75);
}
@ -88,14 +88,14 @@ body > * {
flex: 1 1 auto;
overflow: hidden;
display: grid;
background: white;
background: var(--general-bgcolor);
}
#loading {
display: flex;
justify-content: center;
align-items: center;
background: rgba(255,255,255,0.9);
background: var(--general-button-bgcolor);
font-weight: bolder;
font-size: 200%;
z-index: 10;
@ -311,7 +311,7 @@ body > * {
color: crimson;
}
#statusNetwork.icon-network-on {
color: navy;
color: var(--add-color);
}
#statusFilter {
@ -344,6 +344,7 @@ body > * {
height: 16px;
-moz-appearance: none;
border: 0;
outline: 0;
background: transparent;
width: calc(100% - 28px);
}
@ -403,7 +404,7 @@ body > * {
font-size: 10pt !important;
}
#nagging {
border-top: 1px solid lightgray;
border-top: 1px solid var(--general-border-color);
display: grid;
grid-template-columns: 1fr auto auto auto;
align-content: center;

View File

@ -1,8 +1,8 @@
/* License: gpl-v2 */
@import 'common.css';
@import "common.css";
html, body {
background: transparent !important;
html,
body {
width: 100%;
height: 100%;
}
@ -19,9 +19,10 @@ article {
#tabs {
display: flex;
background: url(icon64.png) 1em 50%/32px 32px no-repeat, url(tile.png) repeat-x, var(--toolbar-bg-color);
background: url(icon64.png) 1em 50%/32px 32px no-repeat,
var(--tile-url) repeat-x, var(--toolbar-bg-color);
padding-left: calc(2em + 32px);
color: white;
color: var(--general-bgcolor);
}
input.tab {
@ -54,9 +55,10 @@ input.tab {
#tabsel-general:checked ~ #tabs #tabel-general,
#tabsel-filters:checked ~ #tabs #tabel-filters,
#tabsel-network:checked ~ #tabs #tabel-network {
color: black !important;
background: white;
border-top: var(--toolbar-border-width) solid var(--toolbar-active-border-color);
color: var(--general-color) !important;
background: var(--general-bgcolor);
border-top: var(--toolbar-border-width) solid
var(--toolbar-active-border-color);
}
#tabs > label {
@ -64,13 +66,14 @@ input.tab {
border-top: var(--toolbar-border-width) solid transparent;
border-left: 1px solid transparent;
border-right: 1px solid transparent;
border-left: 1px solid rgba(255, 255, 255, 0.5);
border-right: 1px solid rgba(255, 255, 255, 0.5);
border-left: var(--toolbar-border);
border-right: var(--toolbar-border);
background: var(--toolbar-bg-color);
}
#tabs > label:hover:not(:checked) {
border-top: var(--toolbar-border-width) solid var(--toolbar-hover-border-color);
border-top: var(--toolbar-border-width) solid
var(--toolbar-hover-border-color);
background: var(--toolbar-hover-background);
}
@ -102,7 +105,7 @@ input.tab {
text-align: center;
}
.buttons > button{
.buttons > button {
margin: 0 2em;
}
@ -113,15 +116,27 @@ input.tab {
fieldset {
display: flex;
margin-bottom: 1em;
border: 1px solid lightgray;
border: 1px solid var(--general-border-color);
border-radius: 6px;
box-shadow: 1px 1px 6px lightgray;
box-shadow: 1px 1px 6px var(--general-border-color);
background: rgba(128, 128, 128, 0.05);
flex-direction: column;
max-width: 60em;
padding: 1.2em;
}
.optiongroups,
fieldset > label {
display: flex;
align-items: center;
}
fieldset > label > input,
fieldset > label > select {
margin-left: 1ex;
margin-right: 1ex;
}
legend {
font-weight: bold;
font-size: 120%;
@ -134,10 +149,10 @@ legend {
}
.virtualtable-container {
border: 1px solid lightgray;
border: 1px solid var(--general-border-color);
border-radius: 6px;
background: rgba(128, 128, 128, 0.05);
box-shadow: 1px 1px 6px lightgray;
box-shadow: 1px 1px 6px var(--general-border-color);
}
#network-general {
@ -146,3 +161,24 @@ legend {
grid-column-gap: 1em;
grid-row-gap: 1ex;
}
.optiongroups {
display: grid;
grid-template-columns: auto 1fr;
grid-column-gap: 1em;
grid-row-gap: 1ex;
}
.optiongroups > div,
.optiongroups > div > label {
display: flex;
align-items: center;
}
.optiongroups input {
margin-left: 1em;
margin-right: 0.7ex;
}
hr {
width: 100%;
}

View File

@ -107,7 +107,7 @@ body > * {
padding: 0;
padding-left: calc(2em + 32px);
color: black;
background: url(icon32.png) 1em 0/32px 32px no-repeat, url(tile.png) repeat-x, var(--toolbar-bg-color);
background: url(icon32.png) 1em 0/32px 32px no-repeat, var(--tile-url) repeat-x, var(--toolbar-bg-color);
font: caption;
font-size: 150%;
font-weight: bold;
@ -117,7 +117,7 @@ body > * {
}
@media (-webkit-min-device-pixel-ratio: 1.3), (min-resolution: 124.8dpi) {
#tabs {
background: url(icon64.png) 1em 50%/32px 32px no-repeat, url(tile.png) repeat-x, var(--toolbar-bg-color);
background: url(icon64.png) 1em 50%/32px 32px no-repeat, var(--tile-url) repeat-x, var(--toolbar-bg-color);
}
}
@ -145,27 +145,27 @@ body > * {
-webkit-user-select: none;
user-select: none;
background: var(--toolbar-bg-color);
color: white;
color: var(--general-color);
min-width: 10em;
padding: 1ex;
padding-left: 1em;
cursor: pointer;
border: 0;
border-top: var(--toolbar-border-width) solid transparent;
border-left: 1px solid rgba(255,255,255,0.3);
border-right: 1px solid rgba(255,255,255,0.3);
border-left: var(--toolbar-border);
border-right: var(--toolbar-border);
transition: border 1s;
}
.tab:not(.active):not(.disabled):hover {
border-top: var(--toolbar-border-width) solid var(--toolbar-hover-border-color);
color: rgb(255, 226, 167);
color: var(--general-color);
background: var(--toolbar-hover-background);
}
.tab.active {
color: black;
background: white;
color: var(--general-color);
background: var(--table-head-bgcolor);
border-top: var(--toolbar-border-width) solid var(--toolbar-active-border-color);
border-left: 1px solid transparent;
border-right: 1px solid transparent;

BIN
style/tile-dark.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.6 KiB

After

Width:  |  Height:  |  Size: 2.3 KiB

10
tests/.editorconfig Normal file
View File

@ -0,0 +1,10 @@
root = true
[*]
charset = utf-8
end_of_line = lf
indent_size = 2
indent_style = space
insert_final_newline = true
tab_width = 2
trim_trailing_whitespace = true

View File

@ -113,4 +113,29 @@ describe("BatchGenerator", function() {
expect(items[0]).to.equal(gen.preview);
expect(gen.hasInvalid).to.be.true;
});
it("characters", function() {
const gen = new BatchGenerator("abc[a:c].lol[1].b");
const items = Array.from(gen);
expect(items).to.deep.equal([
"abca.lol[1].b",
"abcb.lol[1].b",
"abcc.lol[1].b",
]);
expect(items.length).to.equal(gen.length);
expect(items[0]).to.equal(gen.preview);
});
it("characters two", function() {
const gen = new BatchGenerator("abc[D:G].lol[1].b");
const items = Array.from(gen);
expect(items).to.deep.equal([
"abcD.lol[1].b",
"abcE.lol[1].b",
"abcF.lol[1].b",
"abcG.lol[1].b",
]);
expect(items.length).to.equal(gen.length);
expect(items[0]).to.equal(gen.preview);
});
});

43
tests/test_urld.js Normal file
View File

@ -0,0 +1,43 @@
/* eslint-env node */
/* eslint-disable @typescript-eslint/no-var-requires */
"use strict";
// License: CC0 1.0
require("../lib/util");
describe("URLd", function() {
it("basic domain", function() {
let u = new URL("https://www.google.de");
expect(u.domain).to.equal("google.de");
u = new URL("https://www.google.de:8443");
expect(u.domain).to.equal("google.de");
});
it("plain basic domain", function() {
const u = new URL("https://google.de");
expect(u.domain).to.equal("google.de");
});
it("special domain", function() {
let u = new URL("https://www.google.co.uk");
expect(u.domain).to.equal("google.co.uk");
u = new URL("https://google.co.uk");
expect(u.domain).to.equal("google.co.uk");
u = new URL("https://www.google.co.uk:8443");
expect(u.domain).to.equal("google.co.uk");
});
it("ipv4", function() {
let u = new URL("https://127.0.0.1:8443");
expect(u.domain).to.equal("127.0.0.1");
u = new URL("https://0.0.0.0:8443");
expect(u.domain).to.equal("0.0.0.0");
});
it("ipv6", function() {
let u = new URL("https://[::1]:8443");
expect(u.domain).to.equal("[::1]");
u = new URL("https://[2a00:1450:4005:800::2003]:8443");
expect(u.domain).to.equal("[2a00:1450:4005:800::2003]");
});
});

View File

@ -104,7 +104,7 @@ export class EventEmitter {
for (const e of Array.from(handlers)) {
try {
// eslint-disable-next-line prefer-spread
handled = handled || !!e.apply(null, args);
handled = !!e.apply(null, args) || handled;
}
catch (ex) {
console.error(`Event handler ${e} for ${event} failed`, ex.toString(), ex.stack, ex);

View File

@ -45,10 +45,10 @@ export class TableEvents extends BaseTable {
"scroll", debounce(this.scrolled.bind(this), SCROLL_DEBOUNCE), {
passive: true
});
body.addEventListener("contextmenu", this.contextmenu.bind(this), true);
table.addEventListener("keypress", this.keypressed.bind(this), true);
table.addEventListener("keydown", this.keypressed.bind(this), true);
table.addEventListener("contextmenu", this.contextmenu.bind(this), true);
selectionGrippy.addEventListener("click", this.grippyClicked.bind(this));
}

View File

@ -25,11 +25,14 @@ RELEASE_ID = "{DDC359D1-844A-42a7-9AA1-88A850A938A8}"
UNCOMPRESSABLE = set((".png", ".jpg", ".zip", ".woff2"))
LICENSED = set((".css", ".html", ".js", "*.ts"))
IGNORED = set((".DS_Store", "Thumbs.db"))
# XXX: #125
IGNORED_OPERA = set(("done.opus", "error.opus"))
PERM_IGNORED_FX = set(("downloads.shelf", "webRequest", "webRequestBlocking"))
PERM_IGNORED_CHROME = set(("menus", "sessions"))
PERM_IGNORED_CHROME = set(("menus", "sessions", "theme"))
SCRIPTS = [
"yarn build:cleanup",
"yarn build:regexps",
"yarn build:bundles",
]
@ -45,17 +48,17 @@ def check_licenses():
raise Exception(f"No license in {file}")
def files():
def files(additional_ignored):
p = Path("")
for pattern in FILES:
for file in sorted(p.glob(pattern)):
if file.name in IGNORED or not file.is_file():
if file.name in IGNORED or file.name in additional_ignored or not file.is_file():
continue
yield file
def build(out, manifest):
with ZipFile(out, "w", compression=ZIP_DEFLATED, allowZip64=False, compresslevel=2) as zp:
for file in files():
def build(out, manifest, additional_ignored=set()):
with ZipFile(out, "w", compression=ZIP_DEFLATED, allowZip64=False) as zp:
for file in files(additional_ignored):
if str(file) == "manifest.json":
buf = manifest
else:
@ -68,7 +71,7 @@ def build(out, manifest):
if file.suffix in UNCOMPRESSABLE:
zp.writestr(zinfo, buf, compress_type=ZIP_STORED)
else:
zp.writestr(zinfo, buf, compress_type=ZIP_DEFLATED, compresslevel=2)
zp.writestr(zinfo, buf, compress_type=ZIP_DEFLATED)
print(file)
@ -101,7 +104,7 @@ def build_firefox(args):
build(out, json.dumps(infos, indent=2).encode("utf-8"))
def build_chrome(args):
def build_chromium(args, pkg, additional_ignored=set()):
now = datetime.now().strftime("%Y%m%d%H%M%S")
with open("manifest.json") as manip:
infos = json.load(manip, object_pairs_hook=OrderedDict)
@ -119,13 +122,13 @@ def build_chrome(args):
infos["name"] = f"{infos.get('name')} {args.mode}"
infos["permissions"] = [p for p in infos.get("permissions") if not p in PERM_IGNORED_CHROME]
out = Path("web-ext-artifacts") / f"dta-{version}-{args.mode}-crx.zip"
out = Path("web-ext-artifacts") / f"dta-{version}-{args.mode}-{pkg}.zip"
if not out.parent.exists():
out.parent.mkdir()
if out.exists():
out.unlink()
print("Output", out)
build(out, json.dumps(infos, indent=2).encode("utf-8"))
build(out, json.dumps(infos, indent=2).encode("utf-8"), additional_ignored=additional_ignored)
def main():
from argparse import ArgumentParser
@ -140,7 +143,8 @@ def main():
else:
run([script], shell=True)
build_firefox(args)
build_chrome(args)
build_chromium(args, "crx")
build_chromium(args, "opr", IGNORED_OPERA)
print("DONE.")
if __name__ == "__main__":

View File

@ -13,8 +13,8 @@
<link rel="icon" href="/style/icon32.png">
<link rel="icon" sizes="16x16" href="/style/icon16.png">
<link rel="icon" sizes="32x32" href="/style/icon32.png">
<link rel="icon" sizes="48x48" href="/style/icon48.png">
<link rel="icon" sizes="64x64" href="/style/icon64.png">
<link rel="icon" sizes="96x96" href="/style/icon96.png">
<link rel="icon" sizes="128x128" href="/style/icon128.png">
<link rel="icon" sizes="256x256" href="/style/icon256.png">
<script defer src="/bundles/common.js"></script>

View File

@ -8,6 +8,8 @@ import PORT from "./manager/port";
import { runtime } from "../lib/browser";
import { Promised } from "../lib/util";
import { PromiseSerializer } from "../lib/pserializer";
import { Keys } from "./keys";
import "./theme";
const $ = document.querySelector.bind(document);
@ -120,6 +122,11 @@ addEventListener("DOMContentLoaded", function dom() {
statusNetwork.setAttribute("title", _("statusNetwork-inactive.title"));
}
});
Keys.on("ACCEL-KeyF", () => {
$("#filter").focus();
return true;
});
});
addEventListener("contextmenu", event => {

View File

@ -4,6 +4,7 @@
import { EventEmitter } from "../../lib/events";
// eslint-disable-next-line no-unused-vars
import { runtime, RawPort } from "../../lib/browser";
import { WindowState } from "../windowstate";
const PORT = new class Port extends EventEmitter {
port: RawPort | null;
@ -14,6 +15,17 @@ const PORT = new class Port extends EventEmitter {
if (!this.port) {
throw new Error("Could not connect");
}
new WindowState(this.port);
addEventListener("beforeunload", () => {
if (this.port) {
this.port.postMessage({
msg: "unload",
left: window.screenX,
top: window.screenY
});
}
});
this.port.onMessage.addListener((msg: any) => {
if (typeof msg === "string") {
this.emit(msg);

View File

@ -41,7 +41,6 @@ import { IconCache } from "../../lib/iconcache";
import * as imex from "../../lib/imex";
// eslint-disable-next-line no-unused-vars
import { BaseItem } from "../../lib/item";
import { API } from "../../lib/api";
const TREE_CONFIG_VERSION = 2;
const RUNNING_TIMEOUT = 1000;
@ -122,6 +121,8 @@ export class DownloadItem extends EventEmitter {
public currentName: string;
public currentFull: string;
public ext?: string;
public position: number;
@ -569,9 +570,7 @@ export class DownloadTable extends VirtualTable {
ctx.on("dismissed", () => this.table.focus());
this.on("contextmenu", (tree, event) => {
if (!this.selection.empty) {
this.showContextMenu(event);
}
return true;
});
@ -1204,7 +1203,7 @@ export class DownloadTable extends VirtualTable {
if (!items || !items.length) {
return;
}
API.regular(items, []);
PORT.post("import", {items});
};
reader.readAsText(picker.files[0], "utf-8");
};

View File

@ -182,7 +182,7 @@ export class Tooltip {
}
const icon = item.largeIcon;
this.icon.className = icon;
this.name.textContent = item.destFull;
this.name.textContent = item.currentFull;
this.from.textContent = item.usable;
this.size.textContent = item.fmtSize;
this.date.textContent = new Date(item.startDate).toLocaleString();

View File

@ -1,4 +1,6 @@
<!doctype html>
<html id="popup">
<head>
<!-- License: GPL-v2 -->
<meta charset="utf-8">
@ -11,6 +13,8 @@
height: auto !important;
-webkit-user-select: none;
user-select: none;
margin: 0;
padding: 0;
}
ul {
@ -106,3 +110,5 @@
</li>
</ul>
</body>
</html>

View File

@ -2,6 +2,7 @@
// License: MIT
import { localize } from "../lib/i18n";
import "./theme";
declare let browser: any;
declare let chrome: any;

View File

@ -20,8 +20,8 @@
<link rel="icon" href="/style/icon32.png">
<link rel="icon" sizes="16x16" href="/style/icon16.png">
<link rel="icon" sizes="32x32" href="/style/icon32.png">
<link rel="icon" sizes="48x48" href="/style/icon48.png">
<link rel="icon" sizes="64x64" href="/style/icon64.png">
<link rel="icon" sizes="96x96" href="/style/icon96.png">
<link rel="icon" sizes="128x128" href="/style/icon128.png">
<link rel="icon" sizes="256x256" href="/style/icon256.png">
<script defer src="/bundles/common.js"></script>
@ -43,13 +43,21 @@
<article id="tab-general" class="tab">
<fieldset>
<legend data-i18n="pref.ui">UI</legend>
<div id="pref-button-type">
<div class="optiongroups">
<label data-i18n="pref-button-type"></label>
<div id="pref-button-type">
<label><input type="radio" name="pref-button-type" value="popup"> <span data-i18n="pref-button-type-popup"></span></label>
<label><input type="radio" name="pref-button-type" value="dta"> <span data-i18n="pref-button-type-dta"></span></label>
<label><input type="radio" name="pref-button-type" value="turbo"> <span data-i18n="pref-button-type-turbo"></span></label>
<label><input type="radio" name="pref-button-type" value="manager"> <span data-i18n="pref-button-type-manager"></span></label>
</div>
<label data-i18n="pref-theme"></label>
<div id="pref-theme">
<label><input type="radio" name="pref-theme" value="default"> <span data-i18n="pref-theme-default"></span></label>
<label><input type="radio" name="pref-theme" value="light"> <span data-i18n="pref-theme-light"></span></label>
<label><input type="radio" name="pref-theme" value="dark"> <span data-i18n="pref-theme-dark"></span></label>
</div>
</div>
<hr>
<label><input type="checkbox" id="pref-manager-in-popup"> <span data-i18n="pref-manager-in-popup"></span></label>
<label><input type="checkbox" id="pref-finish-notification"> <span data-i18n="pref-finish-notification"></span></label>
@ -81,7 +89,8 @@
</fieldset>
<fieldset>
<legend>Translations</legend>
<label>Language:
<label>
<span>Language:</span>
<select id="languages">
<option value="default">Browser default</option>
</select>
@ -123,14 +132,16 @@
</article>
<article id="tab-network" class="tab">
<fieldset id="network-general">
<fieldset>
<legend data-i18n="pref.netglobal"></legend>
<div id="network-general">
<label data-i18n="pref-concurrent-downloads">Concurrent downloads</label>
<input id="pref-concurrent-downloads" type="number" min="1" max="10">
<label data-i18n="pref-retries"></label>
<input id="pref-retries" type="number" min="0" max="100">
<label data-i18n="pref-retry-time"></label>
<input id="pref-retry-time" type="number" min="1" max="600">
</div>
</fieldset>
<table id="limits" data-singleselect="true">
<tr>

View File

@ -19,7 +19,8 @@ import { iconForPath, visible } from "../lib/windowutils";
import { VirtualTable } from "../uikit/lib/table";
import { Icons } from "./icons";
import { $ } from "./winutil";
import { runtime, storage } from "../lib/browser";
import { runtime, storage, OPERA } from "../lib/browser";
import "./theme";
const ICON_BASE_SIZE = 16;
@ -557,7 +558,14 @@ addEventListener("DOMContentLoaded", async () => {
new BoolPref("pref-manager-in-popup", "manager-in-popup");
new BoolPref("pref-queue-notification", "queue-notification");
new BoolPref("pref-finish-notification", "finish-notification");
new BoolPref("pref-sounds", "sounds");
// XXX: #125
const sounds = new BoolPref("pref-sounds", "sounds");
if (OPERA) {
const sp = sounds.elem.parentElement;
if (sp) {
sp.style.display = "none";
}
}
new BoolPref("pref-hide-context", "hide-context");
new BoolPref("pref-tooltip", "tooltip");
new BoolPref("pref-open-manager-on-queue", "open-manager-on-queue");
@ -566,6 +574,7 @@ addEventListener("DOMContentLoaded", async () => {
new BoolPref("pref-show-urls", "show-urls");
new BoolPref("pref-remove-missing-on-init", "remove-missing-on-init");
new OptionPref("pref-button-type", "button-type");
new OptionPref("pref-theme", "theme");
new OptionPref("pref-conflict-action", "conflict-action");
$("#reset-confirmations").addEventListener("click", async () => {

View File

@ -14,8 +14,8 @@
<link rel="icon" href="/style/icon32.png">
<link rel="icon" sizes="16x16" href="/style/icon16.png">
<link rel="icon" sizes="32x32" href="/style/icon32.png">
<link rel="icon" sizes="48x48" href="/style/icon48.png">
<link rel="icon" sizes="64x64" href="/style/icon64.png">
<link rel="icon" sizes="96x96" href="/style/icon96.png">
<link rel="icon" sizes="128x128" href="/style/icon128.png">
<link rel="icon" sizes="256x256" href="/style/icon256.png">
<script defer src="/bundles/common.js"></script>

View File

@ -25,6 +25,7 @@ import { ItemDelta } from "../lib/select";
// eslint-disable-next-line no-unused-vars
import { TableConfig } from "../uikit/lib/config";
import { validateSubFolder as validateSubfolder } from "../lib/util";
import "./theme";
const PORT: RawPort = runtime.connect(null, { name: "select" });
@ -49,6 +50,7 @@ let Subfolder: Dropdown;
type DELTAS = {deltaLinks: ItemDelta[]; deltaMedia: ItemDelta[]};
interface BaseMatchedItem extends BaseItem {
backIdx: number;
matched?: string | null;
rowid: number;
}
@ -137,7 +139,8 @@ class ItemCollection {
constructor(items: BaseMatchedItem[]) {
this.items = items;
this.assignRows();
this.indexes = new Map(items.map(i => [i.idx, i]));
this.items.forEach((item, idx) => item.backIdx = idx);
this.indexes = new Map(items.map((i, idx) => [idx, i]));
}
assignRows() {
@ -158,11 +161,11 @@ class ItemCollection {
return rv;
}
get checkedIndexes() {
get checkedBackIndexes() {
const rv: number[] = [];
this.items.forEach(function (item) {
this.items.forEach(function(item) {
if (item.matched && item.matched !== "unmanual") {
rv.push(item.idx);
rv.push(item.backIdx);
}
});
return rv;
@ -678,7 +681,7 @@ async function download(paused = false) {
const subfolder = Subfolder.value;
validateSubfolder(subfolder);
const items = Table.items.checkedIndexes;
const items = Table.items.checkedBackIndexes;
if (!items.length) {
throw new Error("error.noItemsSelected");
}
@ -897,4 +900,3 @@ addEventListener("beforeunload", function() {
});
new WindowState(PORT);

View File

@ -13,8 +13,8 @@
<link rel="icon" href="/style/icon32.png">
<link rel="icon" sizes="16x16" href="/style/icon16.png">
<link rel="icon" sizes="32x32" href="/style/icon32.png">
<link rel="icon" sizes="48x48" href="/style/icon48.png">
<link rel="icon" sizes="64x64" href="/style/icon64.png">
<link rel="icon" sizes="96x96" href="/style/icon96.png">
<link rel="icon" sizes="128x128" href="/style/icon128.png">
<link rel="icon" sizes="256x256" href="/style/icon256.png">
<script defer src="/bundles/common.js"></script>

View File

@ -15,6 +15,7 @@ import { hookButton } from "../lib/manager/renamer";
import { runtime } from "../lib/browser";
import { $ } from "./winutil";
import { validateSubFolder } from "../lib/util";
import "./theme";
const PORT = runtime.connect(null, { name: "single" });

112
windows/theme.ts Normal file
View File

@ -0,0 +1,112 @@
/* eslint-disable no-magic-numbers */
"use strict";
// License: MIT
import { PrefWatcher } from "../lib/prefs";
import { theme } from "../lib/browser";
import { memoize } from "../lib/memoize";
const resolveColor = memoize(function(color) {
try {
const el = document.createElement("div");
el.style.backgroundColor = color;
el.style.display = "none";
document.body.appendChild(el);
try {
const resolved = window.getComputedStyle(el, null).backgroundColor;
return resolved;
}
finally {
document.body.removeChild(el);
}
}
catch {
return undefined;
}
}, 10, 1);
export const THEME = new class Theme extends PrefWatcher {
public systemDark: boolean;
public themeDark?: boolean;
constructor() {
super("theme", "default");
if (theme && theme.onUpdated) {
theme.onUpdated.addListener(this.onThemeUpdated.bind(this));
theme.getCurrent().then((theme: any) => this.onThemeUpdated({theme}));
}
this.themeDark = undefined;
const query = window.matchMedia("(prefers-color-scheme: dark)");
this.systemDark = query.matches;
query.addListener(e => {
this.systemDark = e.matches;
this.recalculate();
});
this.recalculate();
}
get dark() {
if (this.value === "dark") {
return true;
}
if (this.value === "light") {
return false;
}
if (typeof this.themeDark === "undefined") {
return this.systemDark;
}
return this.themeDark;
}
changed(prefs: any, key: string, value: any) {
const rv = super.changed(prefs, key, value);
this.recalculate();
return rv;
}
onThemeUpdated({theme}: {theme: any}) {
try {
if (!theme) {
this.themeDark = undefined;
return;
}
const {colors} = theme;
if (!colors) {
this.themeDark = undefined;
return;
}
const color = resolveColor(
colors.toolbar || colors.popup || colors.ntp_background);
if (!color) {
this.themeDark = undefined;
return;
}
const pieces = color.match(/^rgba?\((\d+),\s*(\d+),\s*(\d+)(?:,\s*(\d+(?:\.\d+)?))?\)$/);
if (!pieces) {
this.themeDark = undefined;
return;
}
const r = parseInt(pieces[1], 10);
const g = parseInt(pieces[2], 10);
const b = parseInt(pieces[3], 10);
// HSP (Highly Sensitive Poo) equation from
// http://alienryderflex.com/hsp.html
const hsp = Math.sqrt(
0.299 * (r * r) +
0.587 * (g * g) +
0.114 * (b * b)
);
this.themeDark = hsp < 128;
}
finally {
this.recalculate();
}
}
recalculate() {
document.documentElement.classList[this.dark ? "add" : "remove"]("dark");
}
}();

736
yarn.lock

File diff suppressed because it is too large Load Diff