20 Commits

Author SHA1 Message Date
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
26 changed files with 5669 additions and 620 deletions

View File

@ -30,7 +30,7 @@ THE SOFTWARE.
## DownThemAll! uikit ## DownThemAll! uikit
Copyright © 2016-2019 by Nils Maier 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) ## 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 ## DownThemAll! icons, icon-font and graphic assets
Copyright (C) 2012-2019 by Nils Maier 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. The icon font contains icons from Font Awesome.
See: https://creativecommons.org/licenses/by-sa/4.0/legalcode 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 The DownThemAll! name and logo cannot be used without explicit permission
in any derivative work, except in credits and license-related notices. 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 modifications of the software and forks is permitted without explicit
permission. permission.
Distributing official DownThemAll! releases without any modifications is allowed without explicit permission.
## Font Awesome ## Font Awesome
Copyright (C) 2016 by Dave Gandy Copyright (C) 2016 by Dave Gandy
License: SIL () License: SIL ()
Homepage: http://fortawesome.github.com/Font-Awesome/ Homepage: http://fortawesome.github.com/Font-Awesome/
## webextension-polyfill ## webextension-polyfill
Lcensed under the Mozilla Public License 2.0. Licensed under the Mozilla Public License 2.0.
## PSL (public-suffix-list) ## PSL (public-suffix-list)
The list itself is licensed under the Mozilla Public License 2.0. 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 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 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). 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

@ -1,20 +1,33 @@
# Translations # 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. * 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. * 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 other. * Do not translate anything besides the "message" elements. Pay attention to the descriptions.
* Do not remove anything. * Do not remove anything.
* Do not translate `$PLACEHOLDERS$`. Placeholders should appear in your translation with the same spelling and all uppercase. * 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. 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"` * 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. * 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. ## Testing Your Translation
* Select your translated `messages.json`. (it doesn't have to be named exactly like that, but should have a `.json` extension)
* 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). * 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). * 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.
## 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

1178
_locales/fr/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

View File

@ -14,7 +14,7 @@
}, },
"deffilter-aud": { "deffilter-aud": {
"label": "Audio", "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, "type": 1,
"active": false, "active": false,
"icon": "mp3" "icon": "mp3"
@ -35,7 +35,7 @@
}, },
"deffilter-img": { "deffilter-img": {
"label": "Images", "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, "type": 3,
"active": false, "active": false,
"icon": "jpg" "icon": "jpg"
@ -63,7 +63,7 @@
}, },
"deffilter-vid": { "deffilter-vid": {
"label": "Videos", "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, "type": 3,
"active": true, "active": true,
"icon": "mkv" "icon": "mkv"

View File

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

View File

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

View File

@ -147,7 +147,6 @@ async function load(): Promise<Localization> {
} }
const custom = localStorage.getItem(CUSTOM_KEY); const custom = localStorage.getItem(CUSTOM_KEY);
console.log("custom", custom);
if (custom) { if (custom) {
try { try {
valid.push(JSON.parse(custom)); valid.push(JSON.parse(custom));

View File

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

View File

@ -1,7 +1,7 @@
{ {
"manifest_version": 2, "manifest_version": 2,
"name": "DownThemAll!", "name": "DownThemAll!",
"version": "4.0.3", "version": "4.0.4",
"description": "__MSG_extensionDescription__", "description": "__MSG_extensionDescription__",
"homepage_url": "https://downthemall.org/", "homepage_url": "https://downthemall.org/",

View File

@ -22,13 +22,13 @@
"@typescript-eslint/eslint-plugin": "^2.0.0", "@typescript-eslint/eslint-plugin": "^2.0.0",
"@typescript-eslint/parser": "^2.0.0", "@typescript-eslint/parser": "^2.0.0",
"chai": "^4.1.2", "chai": "^4.1.2",
"eslint": "^6.1.0", "eslint": "^6.2.2",
"mocha": "^6.2.0", "mocha": "^6.2.0",
"ts-loader": "^6.0.4", "ts-loader": "^6.0.4",
"ts-node": "^8.3.0", "ts-node": "^8.3.0",
"typescript": "^3.5.3", "typescript": "^3.5.3",
"webpack": "^4.39.2", "webpack": "^4.39.3",
"webpack-cli": "^3.3.6", "webpack-cli": "^3.3.7",
"xregexp": "^4.2.4" "xregexp": "^4.2.4"
}, },
"dependencies": { "dependencies": {

View File

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

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"; import {EventEmitter} from "../lib/events";
// eslint-disable-next-line no-unused-vars // eslint-disable-next-line no-unused-vars
import { ContextMenu } from "../uikit/lib/contextmenu"; import { ContextMenu } from "./contextmenu";
import { runtime } from "../lib/browser"; import { runtime } from "../lib/browser";
export const Keys = new class extends EventEmitter { export const Keys = new class extends EventEmitter {

View File

@ -10,7 +10,7 @@ import {
MenuItemBase, MenuItemBase,
// eslint-disable-next-line no-unused-vars // eslint-disable-next-line no-unused-vars
MenuPosition, MenuPosition,
} from "../../uikit/lib/contextmenu"; } from "../contextmenu";
import {EventEmitter} from "../../lib/events"; import {EventEmitter} from "../../lib/events";
// eslint-disable-next-line no-unused-vars // eslint-disable-next-line no-unused-vars
import {filters, Matcher, Filter} from "../../lib/filters"; import {filters, Matcher, Filter} from "../../lib/filters";

View File

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

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> <label><input type="checkbox" id="pref-remove-missing-on-init"> <span data-i18n="pref-remove-missing-on-init"></span></label>
</fieldset> </fieldset>
<fieldset id="pref-conflict-action"> <fieldset id="pref-conflict-action">
<legend data-i18="prefs.conflict">When a file exists</legend> <legend data-i18n="prefs.conflicts">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="overwrite"> <span data-i18n="conflict-overwrite">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="uniquify"> <span data-i18n="conflict-rename">Rename</span></label>
<!--<label><input type="radio" name="pref-conflict-action" value="prompt"> <span>Prompt</span></label>--> <!--<label><input type="radio" name="pref-conflict-action" value="prompt"> <span data-i18n="conflict-prompt">Prompt</span></label>-->
</fieldset> </fieldset>
<fieldset> <fieldset>
<legend>Translations</legend> <legend>Translations</legend>

View File

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

View File

@ -44,7 +44,7 @@
</table> </table>
</div> </div>
<section id="filters" class="collapsable"> <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="linksFilters"></div>
<div class="filters-container" id="mediaFilters"></div> <div class="filters-container" id="mediaFilters"></div>
</section> </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-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 id="ctx-select-invert" data-key="ACCEL-KeyI" data-i18n="invert-selection">Invert Selection</li>
<li>-</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> </ul>
</template> </template>
<template id="paused-template"> <template id="paused-template">

View File

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

View File

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

819
yarn.lock

File diff suppressed because it is too large Load Diff