Compare commits
20 Commits
Author | SHA1 | Date | |
---|---|---|---|
494479ce1a | |||
98ebb160f9 | |||
d1cc406f05 | |||
687b6e1aa9 | |||
c960d48b72 | |||
c8c7506efc | |||
91edcee28c | |||
a425a786ef | |||
f023351acc | |||
c8610eee29 | |||
f7a70ec2ea | |||
69d8ffe8a5 | |||
71240ec1e8 | |||
a6f3c7a647 | |||
eab9631a11 | |||
9e29db911c | |||
8d1040115a | |||
3551545eae | |||
01001dc7b2 | |||
b4e6ab80d2 |
15
LICENSE.md
15
LICENSE.md
@ -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.
|
||||
|
@ -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).
|
||||
|
||||
|
@ -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
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
1170
_locales/es/messages.json
Normal file
File diff suppressed because it is too large
Load Diff
1178
_locales/fr/messages.json
Normal file
1178
_locales/fr/messages.json
Normal file
File diff suppressed because it is too large
Load Diff
1170
_locales/pl/messages.json
Executable file
1170
_locales/pl/messages.json
Executable file
File diff suppressed because it is too large
Load Diff
@ -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"
|
||||
|
@ -80,7 +80,11 @@
|
||||
"tif",
|
||||
"tiff",
|
||||
"wmf",
|
||||
"webp"
|
||||
"webp",
|
||||
"heic",
|
||||
"heif",
|
||||
"jxr",
|
||||
"wdp"
|
||||
],
|
||||
"video": [
|
||||
"3g2",
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -147,7 +147,6 @@ async function load(): Promise<Localization> {
|
||||
}
|
||||
|
||||
const custom = localStorage.getItem(CUSTOM_KEY);
|
||||
console.log("custom", custom);
|
||||
if (custom) {
|
||||
try {
|
||||
valid.push(JSON.parse(custom));
|
||||
|
@ -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
|
||||
|
@ -1,7 +1,7 @@
|
||||
{
|
||||
"manifest_version": 2,
|
||||
"name": "DownThemAll!",
|
||||
"version": "4.0.3",
|
||||
"version": "4.0.4",
|
||||
|
||||
"description": "__MSG_extensionDescription__",
|
||||
"homepage_url": "https://downthemall.org/",
|
||||
|
@ -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": {
|
||||
|
@ -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"],
|
||||
|
22
windows/contextmenu.ts
Normal file
22
windows/contextmenu.ts
Normal 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));
|
||||
});
|
@ -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 {
|
||||
|
@ -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";
|
||||
|
@ -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";
|
||||
|
@ -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>
|
||||
|
@ -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
|
||||
|
@ -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">
|
||||
|
@ -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() {
|
||||
|
@ -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>
|
||||
|
Reference in New Issue
Block a user