46 Commits

Author SHA1 Message Date
2ccc12de90 Version 4.0.6 2019-09-02 11:53:03 +02:00
28e6866db8 Use currentWindow instead of WINDOW_ID_CURRENT 2019-09-02 04:06:28 +02:00
7185964649 emulate must use correct window
Closes #41
2019-09-02 04:02:50 +02:00
cd1005823d Typo fixes 2019-09-02 03:57:21 +02:00
0afceb9850 Dutch translation
Closes #39
2019-09-02 00:30:45 +02:00
e6dc205b9d Small de improvement 2019-09-02 00:26:35 +02:00
8e473c778b Rename to zh-CN
Related to #38
2019-09-02 00:26:15 +02:00
7a71ae5f37 Also split ui locales by _ 2019-09-02 00:24:11 +02:00
d04f3db22f Add Simplified Chinese translation
Created this translation by using the online translation tool from https://downthemall.github.io/translate/ .
2019-09-02 00:23:51 +02:00
adc6b9dbb2 Add et locale 2019-09-02 00:03:29 +02:00
e8f09c80f3 Do not add empty separator in URLMenuFilter 2019-09-01 15:24:14 +02:00
46c4e66558 webpack config updates 2019-09-01 06:17:05 +02:00
a8ea416a67 Remove smartphone section from issue template 2019-09-01 05:06:30 +02:00
320c1ddafa Add a little margin to allow focus outlines 2019-09-01 05:01:49 +02:00
4c576ba720 Add content_security_policy 2019-09-01 04:52:57 +02:00
7b58779f9e Remove outdated todos 2019-09-01 00:37:10 +02:00
45d835fe19 use correct autoHide HTML spelling 2019-09-01 00:03:53 +02:00
2d2826d192 Correctly toggle URLMenuFilter 2019-09-01 00:03:34 +02:00
4c77ad0f1f Fix popup height flash bug 2019-08-31 23:16:46 +02:00
4d72ac4534 Notifications galore 2019-08-31 23:08:12 +02:00
cdda0835d8 Wrong translation 2019-08-31 18:12:37 +02:00
78e91304eb Version 4.0.5 2019-08-31 15:44:35 +02:00
0702631003 nag a little later 2019-08-31 13:40:54 +02:00
c45bf671fb fixes to polish translation (#34) 2019-08-31 13:38:47 +02:00
33a3e275fc Create messages.json 2019-08-31 08:16:29 +02:00
369514f155 Add lt locale 2019-08-30 17:02:08 +02:00
494479ce1a Version 4.0.4 2019-08-29 23:09:14 +02:00
98ebb160f9 Silence a console.log 2019-08-29 19:37:09 +02:00
d1cc406f05 select: frontend/backend mappings were out of sync
Closes #27
2019-08-29 19:36:06 +02:00
687b6e1aa9 pl locale
Closes #19
2019-08-29 09:52:26 +02:00
c960d48b72 es-ES locale
Closes #26
2019-08-29 08:24:11 +02:00
c8c7506efc Properly translate Filters heading
Reported in #26
2019-08-29 08:14:22 +02:00
91edcee28c FIx filters message names 2019-08-29 08:14:22 +02:00
a425a786ef Do not log "custom" 2019-08-29 08:14:22 +02:00
f023351acc Update Readme.md 2019-08-28 18:39:44 +02:00
c8610eee29 Add some additional file exts to the def filters 2019-08-28 17:59:29 +02:00
f7a70ec2ea Add fr-FR locale 2019-08-28 17:16:34 +02:00
69d8ffe8a5 Minor typos 2019-08-28 05:52:32 +02:00
71240ec1e8 More typos 2019-08-28 05:45:06 +02:00
a6f3c7a647 Some typos 2019-08-28 05:41:41 +02:00
eab9631a11 Fix typo 2019-08-28 04:10:24 +02:00
9e29db911c Update dependencies 2019-08-27 19:37:55 +02:00
8d1040115a Update information about translations 2019-08-27 19:32:11 +02:00
3551545eae add DE locale 2019-08-27 19:12:36 +02:00
01001dc7b2 Add missing i18n and fix some typos 2019-08-27 18:53:31 +02:00
b4e6ab80d2 Add locale descriptions 2019-08-27 16:43:49 +02:00
41 changed files with 11566 additions and 649 deletions

View File

@ -7,6 +7,11 @@ assignees: ''
---
**Desktop (please complete the following information):**
- OS: [e.g. iOS]
- Browser [e.g. chrome, safari]
- Version [e.g. 22]
**Describe the bug**
A clear and concise description of what the bug is.
@ -23,16 +28,6 @@ A clear and concise description of what you expected to happen.
**Screenshots**
If applicable, add screenshots to help explain your problem.
**Desktop (please complete the following information):**
- OS: [e.g. iOS]
- Browser [e.g. chrome, safari]
- Version [e.g. 22]
**Smartphone (please complete the following information):**
- Device: [e.g. iPhone6]
- OS: [e.g. iOS8.1]
- Browser [e.g. stock browser, safari]
- Version [e.g. 22]
**Additional context**
Add any other context about the problem here.

View File

@ -30,7 +30,7 @@ THE SOFTWARE.
## DownThemAll! uikit
Copyright © 2016-2019 by Nils Maier
The uikit libraries and assets are licened under the MIT license.
The uikit libraries and assets are licensed under the MIT license.
## DownThemAll! interface (.html, .css)
@ -41,7 +41,8 @@ Licensed under GPL2.0; see [LICENSE.gpl-2.0.txt](LICENSE.gpl-2.0.txt).
## DownThemAll! icons, icon-font and graphic assets
Copyright (C) 2012-2019 by Nils Maier
Licensed under Creative Commons Attribution-ShareAlike 4.0 International
Licensed under Creative Commons Attribution-ShareAlike 4.0 International.
The icon font contains icons from Font Awesome.
See: https://creativecommons.org/licenses/by-sa/4.0/legalcode
@ -54,21 +55,25 @@ Copyright © 2010-2019 by Nils Maier, Stefano Verna.
The DownThemAll! name and logo cannot be used without explicit permission
in any derivative work, except in credits and license-related notices.
Using the DownThemAll! logo in personal non-distributed non-commerical
Using the DownThemAll! logo in personal non-distributed non-commercial
modifications of the software and forks is permitted without explicit
permission.
Distributing official DownThemAll! releases without any modifications is allowed without explicit permission.
## Font Awesome
Copyright (C) 2016 by Dave Gandy
License: SIL ()
Homepage: http://fortawesome.github.com/Font-Awesome/
## webextension-polyfill
Lcensed under the Mozilla Public License 2.0.
Licensed under the Mozilla Public License 2.0.
## PSL (public-suffix-list)
The list itself is licensed under the Mozilla Public License 2.0.
The javascript library accessing it is licensed under the MIT license.
The javascript library accessing it is licensed under the MIT license.

View File

@ -36,7 +36,9 @@ You will want to `yarn` the development dependencies such as webpack first.
Afterwards there is two important commands to run
* `yarn watch` - This will run the webpack bundler in watch mode, updating bundles as you change the source.
* `yarn webext` - This will run the WebExtension in a development profile using the [`web-ext` tool from mozilla](https://www.npmjs.com/package/web-ext) (which you need to install separately).
* `yarn webext` - This will run the WebExtension in a development profile using the [`web-ext` tool from mozilla](https://www.npmjs.com/package/web-ext) (which you need to install separately). This will use the directory `../dtalite.p` to keep a development profile. You might need to create this directory before you use this command first.
Please note: You have to run `yarn watch` (at least once) as it builds the actual script bundles.
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).

View File

@ -15,10 +15,6 @@ Planned for later.
* Add downloads
* Chrome support
* vtable perf: cache column widths
* Localizations
* Settle on system
* Do the de-locale
* Enagage translators
* Download options
* This is a bit more limited, as we cannot modify options of downloads that have been started (and paused) or that are done.

View File

@ -1,20 +1,33 @@
# Translations
Right now we did not standardize on a tool to translate, so feel free to whip our your favorite text edits, JSON editor, special translation tool, what have you.
Right now we did not standardize on a tool/website/community use for translations
To make a translation of DownThemAll! in your language, please:
## Website-based Translation
Please go to [https://downthemall.github.io/translate/](https://downthemall.github.io/translate/) for a "good enough" tool to translate DownThemAll! for now. It will load the English locale as a base automatically.
Then you can translate (your progress will be saved in the browser). Once done, you can Download the `messages.json` and test it or submit it for inclusion.
You can also import your or other people's existing translations to modify. This will overwrite any progress you made so far, tho.
## Manual Translation
* Get the [`en/messages.json`](https://github.com/downthemall/downthemall/raw/master/_locales/en/messages.json) as a base.
* Translate the `"message"` items in that file only.
* Do not translate anything other.
* Translate the `"message"` items in that file only. Whip our your favorite text editor, JSON editor, special translation tool, what have you.
* Do not translate anything besides the "message" elements. Pay attention to the descriptions.
* Do not remove anything.
* Do not translate `$PLACEHOLDERS$`. Placeholders should appear in your translation with the same spelling and all uppercase.
They will be relaced at runtime with actual values.
* Make sure you save the file in an "utf-8" encoding. If you need double quotes, you need to escape the quotes with a backslash, e.g. `"some \"quoted\" text"`
* You should translate all strings. If you want to skip a string, set it to an empty `""` string. DTA will then use the English string.
* Once you are at a point you want to test things:
* Go to the DownThemAll! Preferences where you will find a "Load custom translation" button.
* Select your translated `messages.json`. (it doesn't have to be named exactly like that, but should have a `.json` extension)
* Make sure you save the file in an "utf-8" encoding. If you need double quotes, you need to escape the quotes with a backslash, e.g. `"some \"quoted\" text"`
* You should translate all strings. If you want to skip a string, set it to an empty `""` string. DTA will then use the English string.
## Testing Your Translation
* Go to the DownThemAll! Preferences where you will find a "Load custom translation" button.
* Select your translated `messages.json`. (it doesn't have to be named exactly like that, but should have a `.json` extension)
* If everything was OK, you will be asked to reload the extension (this will only reload DTA not the entire browser).
* See your strings in action once you reloaded DTA (either by answering OK when asked, or disable/enable the extension manually or restart your browser).
* If you're happy with the result and would like to contribute it back, you can either file a full Pull Request, or just file an issue and post a link to e.g. a [gist](https://gist.github.com/) or paste the translation in the issue text.
* See your strings in action once you reloaded DTA (either by answering OK when asked, or disable/enable the extension manually or restart your browser).
## Submitting Your Translation
If you're happy with the result and would like to contribute it back, you can either file a full Pull Request, or just file an issue and post a link to e.g. a [gist](https://gist.github.com/) or paste the translation in the issue text.

1170
_locales/de/messages.json Normal file

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

1170
_locales/es/messages.json Normal file

File diff suppressed because it is too large Load Diff

1170
_locales/et/messages.json Normal file

File diff suppressed because it is too large Load Diff

1178
_locales/fr/messages.json Normal file

File diff suppressed because it is too large Load Diff

1170
_locales/lt/messages.json Normal file

File diff suppressed because it is too large Load Diff

1170
_locales/nl/messages.json Normal file

File diff suppressed because it is too large Load Diff

1170
_locales/pl/messages.json Executable file

File diff suppressed because it is too large Load Diff

1160
_locales/pt/messages.json Normal file

File diff suppressed because it is too large Load Diff

1170
_locales/zh-CN/messages.json Normal file

File diff suppressed because it is too large Load Diff

View File

@ -14,7 +14,7 @@
},
"deffilter-aud": {
"label": "Audio",
"expr": "/\\.(?:mp3|wav|og(?:g|a)|flac|midi?|rm|aac|wma|mka|ape)$/i",
"expr": "/\\.(?:mp3|wav|og(?:g|a)|flac|midi?|rm|aac|wma|mka|ape|opus)$/i",
"type": 1,
"active": false,
"icon": "mp3"
@ -35,7 +35,7 @@
},
"deffilter-img": {
"label": "Images",
"expr": "/\\.(?:jp(?:e?g|e|2)|gif|png|tiff?|bmp|ico)$/i",
"expr": "/\\.(?:jp(?:e?g|e|2)|gif|png|tiff?|bmp|ico|heic|heif|webp|jxr|wdp|dng|cr2|arw)$/i",
"type": 3,
"active": false,
"icon": "jpg"
@ -63,7 +63,7 @@
},
"deffilter-vid": {
"label": "Videos",
"expr": "/\\.(?:mpeg|ra?m|avi|mp(?:g|e|4)|mov|divx|asf|qt|wmv|m\\dv|rv|vob|asx|ogm|ogv|webm|flv|mkv)$/i",
"expr": "/\\.(?:mpeg|ra?m|avi|mp(?:g|e|4)|mov|divx|asf|qt|wmv|m\\dv|rv|vob|asx|ogm|ogv|webm|flv|mkv|f4v|m4v)$/i",
"type": 3,
"active": true,
"icon": "mkv"

View File

@ -80,7 +80,11 @@
"tif",
"tiff",
"wmf",
"webp"
"webp",
"heic",
"heif",
"jxr",
"wdp"
],
"video": [
"3g2",

View File

@ -9,7 +9,7 @@
"hide-context": false,
"conflict-action": "uniquify",
"nagging": 0,
"nagging-next": 6,
"nagging-next": 7,
"tooltip": true,
"show-urls": false,
"remove-missing-on-init": false,

View File

@ -128,7 +128,6 @@ export const API = new class APIImpl {
return false;
}
const {items, options} = await select(links, media);
console.log(items, options);
return this.regularInternal(items, options);
}

View File

@ -17,7 +17,7 @@ import {
// eslint-disable-next-line no-unused-vars
Tab,
// eslint-disable-next-line no-unused-vars
MenuClickInfo
MenuClickInfo,
} from "./browser";
import { Bus } from "./bus";
import { filterInSitu } from "./util";
@ -389,7 +389,10 @@ locale.then(() => {
}
async enumulate(action: string) {
const tab = await tabs.query({active: true});
const tab = await tabs.query({
active: true,
currentWindow: true,
});
if (!tab || !tab.length) {
return;
}

View File

@ -123,9 +123,13 @@ async function loadRawLocales() {
const langs = new Set<string>(["en"]);
const ui = (browser.i18n || chrome.i18n).getUILanguage();
langs.add(ui);
// Try the base too
if (ui.includes("-")) {
// Try the base too
langs.add(ui.split(/[_-]+/)[0]);
langs.add(ui.split(/[-]+/)[0]);
}
else if (ui.includes("_")) {
langs.add(ui.split(/[_]+/)[0]);
}
const fetched = await Promise.all(Array.from(langs, fetchLanguage));
@ -147,7 +151,6 @@ async function load(): Promise<Localization> {
}
const custom = localStorage.getItem(CUSTOM_KEY);
console.log("custom", custom);
if (custom) {
try {
valid.push(JSON.parse(custom));

View File

@ -12,13 +12,20 @@ const DEFAULTS = {
message: "message",
};
const TIMEOUT = 4000;
let gid = 1;
export class Notification extends EventEmitter {
private notification: any;
private readonly generated: boolean;
constructor(id: string | null, options = {}) {
super();
id = id || "DownThemAll-notification";
this.generated = !id;
id = id || `DownThemAll-notification${++gid}`;
if (typeof options === "string") {
options = {message: options};
}
@ -39,11 +46,16 @@ export class Notification extends EventEmitter {
opened(notification: any) {
this.notification = notification;
this.emit("opened", this);
if (this.generated) {
setTimeout(() => {
notifications.clear(notification);
}, TIMEOUT);
}
}
clicked(notification: any, button?: number) {
// We can only be clicked, when we were opened, at which point the
// notification id is availablfalse
// notification id is available
if (notification !== this.notification) {
return;
}
@ -52,6 +64,7 @@ export class Notification extends EventEmitter {
return;
}
this.emit("clicked", this);
console.log("clicked", notification);
}
async closed(notification: any) {

View File

@ -50,7 +50,7 @@ function computeSelection(
return !item.matched;
});
}
return items.filter(item => item.prevMatched !== item.matched). map(item => {
return items.filter(item => item.prevMatched !== item.matched).map(item => {
return {
idx: item.idx,
matched: item.matched

View File

@ -1,7 +1,7 @@
{
"manifest_version": 2,
"name": "DownThemAll!",
"version": "4.0.3",
"version": "4.0.6",
"description": "__MSG_extensionDescription__",
"homepage_url": "https://downthemall.org/",
@ -9,6 +9,8 @@
"default_locale": "en",
"content_security_policy": "script-src 'self'; style-src 'self' 'unsafe-inline'; default-src 'self'",
"icons": {
"16": "style/icon16.png",
"32": "style/icon32.png",

View File

@ -22,13 +22,13 @@
"@typescript-eslint/eslint-plugin": "^2.0.0",
"@typescript-eslint/parser": "^2.0.0",
"chai": "^4.1.2",
"eslint": "^6.1.0",
"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.2",
"webpack-cli": "^3.3.6",
"webpack": "^4.39.3",
"webpack-cli": "^3.3.7",
"xregexp": "^4.2.4"
},
"dependencies": {

View File

@ -49,7 +49,7 @@ p.example {
}
#options > * {
margin: 0;
margin: 2px;
}
#options > input {

View File

@ -10,7 +10,7 @@ const MENU_OPEN_BOUNCE = 500;
let ids = 0;
const Keys = new Map([
export const Keys = new Map([
["ACCEL", IS_MAC ? "⌘" : "Ctrl"],
["CTRL", "Ctrl"],
["ALT", IS_MAC ? "⌥" : "Alt"],

View File

@ -42,6 +42,11 @@ module.exports = {
filename: "[name].js"
},
devtool: "source-map",
stats: {
hash: true,
timings: true,
maxModules: 2,
},
watchOptions: {
ignored: /node_modules|bundles/
},

22
windows/contextmenu.ts Normal file
View File

@ -0,0 +1,22 @@
"use strict";
// License: MIT
export * from "../uikit/lib/contextmenu";
import { Keys } from "../uikit/lib/contextmenu";
import { IS_MAC } from "../uikit/lib/util";
import { locale, _ } from "../lib/i18n";
locale.then(() => {
Keys.clear();
[
["ACCEL", IS_MAC ? "⌘" : _("key-ctrl")],
["CTRL", _("key-ctrl")],
["ALT", IS_MAC ? "⌥" : _("key-alt")],
["DELETE", _("key-delete")],
["PAGEUP", _("key-pageup")],
["PAGEDOWN", _("key-pagedown")],
["HOME", _("key-home")],
["END", _("key-end")],
["SHIFT", "⇧"],
].forEach(([k, v]) => Keys.set(k, v));
});

View File

@ -3,7 +3,7 @@
import {EventEmitter} from "../lib/events";
// eslint-disable-next-line no-unused-vars
import { ContextMenu } from "../uikit/lib/contextmenu";
import { ContextMenu } from "./contextmenu";
import { runtime } from "../lib/browser";
export const Keys = new class extends EventEmitter {

View File

@ -128,8 +128,8 @@
<template id="menufilter-template">
<ul>
<li id="ctx-menufilter-seperator">-</li>
<li id="ctx-menufilter-invert" data-autoHide="false">Invert</li>
<li id="ctx-menufilter-clear" data-autoHide="false">Clear</li>
<li id="ctx-menufilter-invert" data-auto-hide="false">Invert</li>
<li id="ctx-menufilter-clear" data-auto-hide="false">Clear</li>
<li>-</li>
<li id="ctx-menufilter-sort-ascending" data-icon="icon-sort-asc">Sort ascending</li>
<li id="ctx-menufilter-sort-descending" data-icon="icon-sort-desc">Sort descending</li>

View File

@ -44,7 +44,7 @@ addEventListener("DOMContentLoaded", function dom() {
const fullyloaded = Promise.all([LOADED, platformed, tabled, localized]);
fullyloaded.then(async () => {
const nag = await Prefs.get("nagging", 0);
const nagnext = await Prefs.get("nagging-next", 6);
const nagnext = await Prefs.get("nagging-next", 7);
const next = Math.ceil(Math.log2(Math.max(1, nag)));
const el = $("#nagging");
const remove = () => {

View File

@ -10,7 +10,7 @@ import {
MenuItemBase,
// eslint-disable-next-line no-unused-vars
MenuPosition,
} from "../../uikit/lib/contextmenu";
} from "../contextmenu";
import {EventEmitter} from "../../lib/events";
// eslint-disable-next-line no-unused-vars
import {filters, Matcher, Filter} from "../../lib/filters";
@ -340,14 +340,19 @@ export class UrlMenuFilter extends MenuFilter {
async populate() {
const filts = await filters();
for (const i of filts.all.filter(e => e.id !== "deffilter-all")) {
this.addItem(i.label, this.toggleRegularFilter.bind(this, i));
this.addItem(
i.label, this.toggleRegularFilter.bind(this, i), this.filters.has(i));
}
this.addItem("-");
sort(
const domains = sort(
Array.from(new Set(this.collection.items.map(e => e.domain))),
undefined,
naturalCaseCompare
).forEach(e => {
);
if (!domains.length) {
return;
}
this.addItem("-");
domains.forEach(e => {
this.addItem(
e, this.toggleDomainFilter.bind(this, e), this.domains.has(e));
});

View File

@ -7,7 +7,7 @@ import {
MenuItem,
// eslint-disable-next-line no-unused-vars
SubMenuItem
} from "../../uikit/lib/contextmenu";
} from "../contextmenu";
import { iconForPath } from "../../lib/windowutils";
import { formatSpeed, formatSize, formatTimeDelta } from "../../lib/formatters";
import { filters } from "../../lib/filters";

View File

@ -7,6 +7,10 @@
box-sizing: content-box !important;
}
html, body {
height: auto !important;
}
ul {
margin: 1.5ex;
margin-right: 2ex;
@ -25,6 +29,7 @@
vertical-align: center;
align-items: center;
border-radius: 4px;
white-space: nowrap;
}
li.sep {

View File

@ -65,10 +65,10 @@
<label><input type="checkbox" id="pref-remove-missing-on-init"> <span data-i18n="pref-remove-missing-on-init"></span></label>
</fieldset>
<fieldset id="pref-conflict-action">
<legend data-i18="prefs.conflict">When a file exists</legend>
<label><input type="radio" name="pref-conflict-action" value="overwrite"> <span>Overwrite</span></label>
<label><input type="radio" name="pref-conflict-action" value="uniquify"> <span>Rename</span></label>
<!--<label><input type="radio" name="pref-conflict-action" value="prompt"> <span>Prompt</span></label>-->
<legend data-i18n="prefs.conflicts">When a file exists</legend>
<label><input type="radio" name="pref-conflict-action" value="overwrite"> <span data-i18n="conflict-overwrite">Overwrite</span></label>
<label><input type="radio" name="pref-conflict-action" value="uniquify"> <span data-i18n="conflict-rename">Rename</span></label>
<!--<label><input type="radio" name="pref-conflict-action" value="prompt"> <span data-i18n="conflict-prompt">Prompt</span></label>-->
</fieldset>
<fieldset>
<legend>Translations</legend>

View File

@ -124,7 +124,7 @@ class CreateFilterDialog extends ModalDialog {
get buttons() {
return [
{
title: "Create",
title: _("create-filter"),
value: "ok",
default: true
},
@ -584,7 +584,7 @@ addEventListener("DOMContentLoaded", () => {
await Prefs.reset(k);
}
await ModalDialog.inform(
_("information.title"), _("reset-confirmations.done"), _("ok"));
_("information.title"), _("reset-layouts.done"), _("ok"));
});
// Filters

View File

@ -44,7 +44,7 @@
</table>
</div>
<section id="filters" class="collapsable">
<h2>Filters</h2>
<h2 data-i18n="options-filters"></h2>
<div class="filters-container" id="linksFilters"></div>
<div class="filters-container" id="mediaFilters"></div>
</section>
@ -89,7 +89,7 @@
<li id="ctx-select-filtered" data-key="ACCEL-KeyF" data-i18n="select-checked">Select Checked</li>
<li id="ctx-select-invert" data-key="ACCEL-KeyI" data-i18n="invert-selection">Invert Selection</li>
<li>-</li>
<li id="ctx-open-selected" data-icon="icon-launch" data-key="ACCEL-KeyO">Open</li>
<li id="ctx-open-selected" data-icon="icon-launch" data-key="ACCEL-KeyO" data-i18n="open-link">Open</li>
</ul>
</template>
<template id="paused-template">

View File

@ -3,7 +3,7 @@
import { VirtualTable } from "../uikit/lib/table";
import ModalDialog from "../uikit/lib/modal";
import { ContextMenu } from "../uikit/lib/contextmenu";
import { ContextMenu } from "./contextmenu";
import { iconForPath } from "../lib/windowutils";
import { _, localize } from "../lib/i18n";
import { Prefs } from "../lib/prefs";
@ -48,6 +48,7 @@ type DELTAS = {deltaLinks: ItemDelta[]; deltaMedia: ItemDelta[]};
interface BaseMatchedItem extends BaseItem {
matched?: string | null;
rowid: number;
}
function cleaErrors() {
@ -96,8 +97,6 @@ class PausedModalDialog extends ModalDialog {
}
}
class CheckClasser extends Map<string, string> {
gen: IterableIterator<string>;
@ -126,19 +125,82 @@ class CheckClasser extends Map<string, string> {
type KeyFn = (item: BaseMatchedItem) => any;
class ItemCollection {
private items: BaseMatchedItem[];
private indexes: Map<number, BaseMatchedItem>;
constructor(items: BaseMatchedItem[]) {
this.items = items;
this.assignRows();
this.indexes = new Map(items.map(i => [i.idx, i]));
}
assignRows() {
this.items.forEach((item, idx) => item.rowid = idx);
}
get length() {
return this.items.length;
}
get checked() {
const rv: number[] = [];
this.items.forEach(function (item, idx) {
if (item.matched && item.matched !== "unmanual") {
rv.push(idx);
}
});
return rv;
}
get checkedIndexes() {
const rv: number[] = [];
this.items.forEach(function (item) {
if (item.matched && item.matched !== "unmanual") {
rv.push(item.idx);
}
});
return rv;
}
at(idx: number) {
return this.items[idx];
}
byIndex(idx: number) {
return this.indexes.get(idx);
}
sort(keyFn: KeyFn) {
sort(this.items, keyFn, naturalCaseCompare);
this.assignRows();
}
reverse() {
this.items.reverse();
this.assignRows();
}
filter(fn: (item: BaseMatchedItem, idx: number) => boolean) {
return this.items.filter(fn);
}
}
class SelectionTable extends VirtualTable {
checkClasser: CheckClasser;
icons: Icons;
links: BaseMatchedItem[];
links: ItemCollection;
media: BaseMatchedItem[];
media: ItemCollection;
items: ItemCollection;
type: string;
items: BaseMatchedItem[];
status: HTMLElement;
linksTab: HTMLElement;
@ -170,10 +232,10 @@ class SelectionTable extends VirtualTable {
this.checkClasser = new CheckClasser(NUM_FILTER_CLASSES);
this.icons = new Icons($("#icons") as HTMLStyleElement);
this.links = links;
this.media = media;
this.links = new ItemCollection(links);
this.media = new ItemCollection(media);
this.type = type;
this.items = (this as any)[type];
this.items = type === "links" ? this.links : this.media;
this.status = $("#statusItems");
this.linksTab = $("#linksTab");
@ -217,8 +279,8 @@ class SelectionTable extends VirtualTable {
if (!keyfn) {
return false;
}
sort(this.links, keyfn, naturalCaseCompare);
sort(this.media, keyfn, naturalCaseCompare);
this.links.sort(keyfn);
this.media.sort(keyfn);
const elem = document.querySelector<HTMLElement>(`#${colid}`);
const oldelem = (this.sortcol && document.querySelector<HTMLElement>(`#${this.sortcol}`));
if (this.sortcol === colid && this.sortasc) {
@ -280,7 +342,7 @@ class SelectionTable extends VirtualTable {
}
let oldmask = "";
for (const r of this.selection) {
const m = this.items[r].mask;
const m = this.items.at(r).mask;
if (oldmask && m !== oldmask) {
oldmask = "";
break;
@ -292,7 +354,7 @@ class SelectionTable extends VirtualTable {
const newmask = await ModalDialog.prompt(
"Renaming mask", "Set new renaming mask", oldmask);
for (const r of this.selection) {
this.items[r].mask = newmask;
this.items.at(r).mask = newmask;
this.invalidateRow(r);
}
}
@ -317,16 +379,6 @@ class SelectionTable extends VirtualTable {
this.switchTab(type);
}
get checkedIndexes() {
const rv: number[] = [];
this.items.forEach(function (item, idx) {
if (item.matched && item.matched !== "unmanual") {
rv.push(idx);
}
});
return rv;
}
get rowCount() {
return this.items.length;
}
@ -336,7 +388,7 @@ class SelectionTable extends VirtualTable {
return false;
}
for (const rowid of this.selection) {
const item = this.items[rowid];
const item = this.items.at(rowid);
if (!state) {
state = matched(item) ? "unmanual" : "manual";
}
@ -362,7 +414,7 @@ class SelectionTable extends VirtualTable {
selectChecked() {
this.selection.clear();
let min = null;
for (const ci of this.checkedIndexes) {
for (const ci of this.items.checked) {
this.selection.add(ci);
min = min === null ? ci : Math.min(min, ci);
}
@ -381,7 +433,7 @@ class SelectionTable extends VirtualTable {
if (this.focusRow < 0) {
return;
}
items.push(this.items[this.focusRow]);
items.push(this.items.at(this.focusRow));
}
PORT.postMessage({
msg: "openUrls",
@ -389,14 +441,14 @@ class SelectionTable extends VirtualTable {
});
}
applyDeltaTo(delta: ItemDelta[], items: BaseMatchedItem[]) {
applyDeltaTo(delta: ItemDelta[], items: ItemCollection) {
const active = items === this.items;
for (const d of delta) {
const {idx = -1, matched = null} = d;
if (idx < 0) {
continue;
}
const item = items[idx];
const item = items.byIndex(idx);
if (!item) {
continue;
}
@ -410,7 +462,7 @@ class SelectionTable extends VirtualTable {
}
item.matched = matched;
if (active) {
this.invalidateRow(idx);
this.invalidateRow(item.rowid);
}
}
}
@ -435,7 +487,7 @@ class SelectionTable extends VirtualTable {
}
updateStatus() {
const selected = this.checkedIndexes.length;
const selected = this.items.checked.length;
if (!selected) {
this.status.textContent = _("noitems.label");
}
@ -446,7 +498,7 @@ class SelectionTable extends VirtualTable {
}
getRowClasses(rowid: number) {
const item = this.items[rowid];
const item = this.items.at(rowid);
if (!item || !matched(item) || !item.matched) {
return null;
}
@ -454,7 +506,7 @@ class SelectionTable extends VirtualTable {
}
getCellIcon(rowid: number, colid: number) {
const item = this.items[rowid];
const item = this.items.at(rowid);
if (item && colid === COL_DOWNLOAD) {
return this.icons.get(iconForPath(item.url, ICON_BASE_SIZE));
}
@ -471,7 +523,7 @@ class SelectionTable extends VirtualTable {
}
getDownloadText(idx: number) {
const item = this.items[idx];
const item = this.items.at(idx);
if (!item) {
return "";
}
@ -482,7 +534,7 @@ class SelectionTable extends VirtualTable {
}
getText(prop: string, idx: number) {
const item: any = this.items[idx];
const item: any = this.items.at(idx);
if (!item || !(prop in item) || !item[prop]) {
return "";
}
@ -490,7 +542,7 @@ class SelectionTable extends VirtualTable {
}
getMaskText(idx: number) {
const item = this.items[idx];
const item = this.items.at(idx);
if (item) {
return item.mask;
}
@ -521,13 +573,13 @@ class SelectionTable extends VirtualTable {
getCellCheck(rowid: number, colid: number) {
if (colid === COL_CHECK) {
return !!matched(this.items[rowid]);
return !!matched(this.items.at(rowid));
}
return false;
}
setCellCheck(rowid: number, colid: number, value: boolean) {
this.items[rowid].matched = value ? "manual" : "unmanual";
this.items.at(rowid).matched = value ? "manual" : "unmanual";
this.invalidateRow(rowid);
this.updateStatus();
}
@ -539,7 +591,7 @@ async function download(paused = false) {
if (!mask) {
throw new Error("error.invalidMask");
}
const items = Table.checkedIndexes;
const items = Table.items.checkedIndexes;
if (!items.length) {
throw new Error("error.noItemsSelected");
}
@ -631,10 +683,14 @@ function setFiltersInternal(
}
function setFilters(filters: any) {
const {linkFilters = [], mediaFilters = [], activeFilters = []} = filters;
const {
linkFilterDescs = [],
mediaFilterDescs = [],
activeFilters = []
} = filters;
const active: Set<string> = new Set(activeFilters);
setFiltersInternal("#linksFilters", linkFilters, active);
setFiltersInternal("#mediaFilters", mediaFilters, active);
setFiltersInternal("#linksFilters", linkFilterDescs, active);
setFiltersInternal("#mediaFilters", mediaFilterDescs, active);
}
function cancel() {

View File

@ -28,17 +28,17 @@
<p class="example">../mygallery[0:9]/photo[01:10][03:1:-1].jpg</p>
</section>
<section id="options">
<h2>Download</h2>
<h2 data-i18n="download">Download</h2>
<input type="text" id="URL">
<h2>Custom Filename</h2>
<h2 data-i18n="custom-filename">Custom Filename</h2>
<input type="text" id="filename">
<h3>Referring page</h3>
<h3 data-i18n="referrer">Referring page</h3>
<input type="text" id="referrer">
<h3>Title</h3>
<h3 data-i18n="title">Title</h3>
<input type="text" id="title">
<h3>Description</h3>
<h3 data-i18n="description">Description</h3>
<input type="text" id="description">
<h3>Mask</h3>
<h3 data-i18n="mask">Mask</h3>
<div id="maskOptions">
<input type="text" id="mask">
<button id="maskButton" class="icon-tags"></button>

819
yarn.lock

File diff suppressed because it is too large Load Diff