less of any

This commit is contained in:
Nils Maier 2019-08-26 19:50:06 +02:00
parent 116d5b9b00
commit 544b7d522c
29 changed files with 479 additions and 252 deletions

View File

@ -5,7 +5,8 @@ import { TYPE_LINK, TYPE_MEDIA } from "./constants";
import { filters } from "./filters";
import { Prefs } from "./prefs";
import { lazy } from "./util";
import { Item, makeUniqueItems } from "./item";
// eslint-disable-next-line no-unused-vars
import { Item, makeUniqueItems, BaseItem } from "./item";
import { getManager } from "./manager/man";
import { select } from "./select";
import { single } from "./single";
@ -16,12 +17,17 @@ import { _ } from "./i18n";
const MAX_BATCH = 10000;
export interface QueueOptions {
mask?: string;
paused?: boolean;
}
export const API = new class APIImpl {
async filter(arr: any, type: number) {
async filter(arr: BaseItem[], type: number) {
return await (await filters()).filterItemsByType(arr, type);
}
async queue(items: any, options: any) {
async queue(items: BaseItem[], options: QueueOptions) {
await MASK.init();
const {mask = MASK.current} = options;
@ -36,12 +42,9 @@ export const API = new class APIImpl {
fileName: null,
title: "",
description: "",
fromMetalink: false,
startDate: new Date(),
hashes: [],
private: false,
postData: null,
cleanRequest: false,
mask,
date: Date.now(),
paused
@ -54,7 +57,7 @@ export const API = new class APIImpl {
}
return currentBatch;
});
items = items.map((i: any) => {
items = items.map(i => {
delete i.idx;
return new Item(i, defaults);
});
@ -79,7 +82,7 @@ export const API = new class APIImpl {
}
}
sanity(links: any[], media: any[]) {
sanity(links: BaseItem[], media: BaseItem[]) {
if (!links.length && !media.length) {
new Notification(null, _("no-links"));
return false;
@ -87,7 +90,7 @@ export const API = new class APIImpl {
return true;
}
async turbo(links: any[], media: any[]) {
async turbo(links: BaseItem[], media: BaseItem[]) {
if (!this.sanity(links, media)) {
return false;
}
@ -105,37 +108,36 @@ export const API = new class APIImpl {
return await this.queue(selected, {paused: await Prefs.get("add-paused")});
}
async regularInternal(selected: any) {
if (selected.mask && !selected.maskOnce) {
async regularInternal(selected: BaseItem[], options: any) {
if (options.mask && !options.maskOnce) {
await MASK.init();
await MASK.push(selected.mask);
await MASK.push(options.mask);
}
if (typeof selected.fast === "string" && !selected.fastOnce) {
if (typeof options.fast === "string" && !options.fastOnce) {
await FASTFILTER.init();
await FASTFILTER.push(selected.fast);
await FASTFILTER.push(options.fast);
}
if (typeof selected.type === "string") {
await Prefs.set("last-type", selected.type);
if (typeof options.type === "string") {
await Prefs.set("last-type", options.type);
}
const {items} = selected;
delete selected.items;
return await this.queue(items, selected);
return await this.queue(selected, options);
}
async regular(links: any[], media: any[]) {
async regular(links: BaseItem[], media: BaseItem[]) {
if (!this.sanity(links, media)) {
return false;
}
const selected = await select(links, media);
return this.regularInternal(selected);
const {items, options} = await select(links, media);
console.log(items, options);
return this.regularInternal(items, options);
}
async singleTurbo(item: any) {
async singleTurbo(item: BaseItem) {
return await this.queue([item], {paused: await Prefs.get("add-paused")});
}
async singleRegular(item: any) {
const selected = await single(item);
return this.regularInternal(selected);
async singleRegular(item: BaseItem | null) {
const {items, options} = await single(item);
return this.regularInternal(items, options);
}
}();

View File

@ -13,9 +13,14 @@ import {
browserAction as action,
menus as _menus, contextMenus as _cmenus,
tabs,
webNavigation as nav
webNavigation as nav,
// eslint-disable-next-line no-unused-vars
Tab,
// eslint-disable-next-line no-unused-vars
MenuClickInfo
} from "./browser";
import { Bus } from "./bus";
import { filterInSitu } from "./util";
const menus = typeof (_menus) !== "undefined" && _menus || _cmenus;
@ -23,7 +28,7 @@ const menus = typeof (_menus) !== "undefined" && _menus || _cmenus;
const GATHER = "/bundles/content-gather.js";
async function runContentJob(tab: any, file: string, msg: any) {
async function runContentJob(tab: Tab, file: string, msg: any) {
try {
const res = await tabs.executeScript(tab.id, {
file,
@ -56,7 +61,7 @@ type SelectionOptions = {
selectionOnly: boolean;
allTabs: boolean;
turbo: boolean;
tab: any;
tab: Tab;
};
@ -71,9 +76,8 @@ class Handler {
return makeUniqueItems(
results.filter(e => e[what]).map(e => {
const finisher = new Finisher(e);
return e[what].
map((item: any) => finisher.finish(item)).
filter((i: any) => i);
return filterInSitu(e[what].
map((item: any) => finisher.finish(item)), e => !!e);
}));
}
@ -348,7 +352,7 @@ locale.then(() => {
}
}
async findSingleItem(tab: any, url: string, turbo = false) {
async findSingleItem(tab: Tab, url: string, turbo = false) {
if (!url) {
return;
}
@ -368,7 +372,7 @@ locale.then(() => {
API[turbo ? "singleTurbo" : "singleRegular"](item);
}
onClicked(info: any, tab: any) {
onClicked(info: MenuClickInfo, tab: Tab) {
if (!tab.id) {
return;
}
@ -378,7 +382,7 @@ locale.then(() => {
console.error("Invalid Handler for", menuItemId);
return;
}
const rv = handler.call(this, info, tab);
const rv: Promise<void> | void = handler.call(this, info, tab);
if (rv && rv.catch) {
rv.catch(console.error);
}
@ -394,7 +398,7 @@ locale.then(() => {
}, tab[0]);
}
async onClickedDTARegular(info: any, tab: any) {
async onClickedDTARegular(info: MenuClickInfo, tab: Tab) {
return await this.performSelection({
selectionOnly: false,
allTabs: false,
@ -403,7 +407,7 @@ locale.then(() => {
});
}
async onClickedDTARegularAll(info: any, tab: any) {
async onClickedDTARegularAll(info: MenuClickInfo, tab: Tab) {
return await this.performSelection({
selectionOnly: false,
allTabs: true,
@ -412,7 +416,7 @@ locale.then(() => {
});
}
async onClickedDTARegularSelection(info: any, tab: any) {
async onClickedDTARegularSelection(info: MenuClickInfo, tab: Tab) {
return await this.performSelection({
selectionOnly: true,
allTabs: false,
@ -421,7 +425,7 @@ locale.then(() => {
});
}
async onClickedDTATurbo(info: any, tab: any) {
async onClickedDTATurbo(info: MenuClickInfo, tab: Tab) {
return await this.performSelection({
selectionOnly: false,
allTabs: false,
@ -430,7 +434,7 @@ locale.then(() => {
});
}
async onClickedDTATurboAll(info: any, tab: any) {
async onClickedDTATurboAll(info: MenuClickInfo, tab: Tab) {
return await this.performSelection({
selectionOnly: false,
allTabs: true,
@ -439,7 +443,7 @@ locale.then(() => {
});
}
async onClickedDTATurboSelection(info: any, tab: any) {
async onClickedDTATurboSelection(info: MenuClickInfo, tab: Tab) {
return await this.performSelection({
selectionOnly: true,
allTabs: false,
@ -448,28 +452,46 @@ locale.then(() => {
});
}
async onClickedDTARegularLink(info: any, tab: any) {
return await this.findSingleItem(tab, info.linkUrl, false);
async onClickedDTARegularLink(info: MenuClickInfo, tab: Tab) {
if (!info.linkUrl) {
return;
}
await this.findSingleItem(tab, info.linkUrl, false);
}
async onClickedDTATurboLink(info: any, tab: any) {
return await this.findSingleItem(tab, info.linkUrl, true);
async onClickedDTATurboLink(info: MenuClickInfo, tab: Tab) {
if (!info.linkUrl) {
return;
}
await this.findSingleItem(tab, info.linkUrl, true);
}
async onClickedDTARegularImage(info: any, tab: any) {
return await this.findSingleItem(tab, info.srcUrl, false);
async onClickedDTARegularImage(info: MenuClickInfo, tab: Tab) {
if (!info.srcUrl) {
return;
}
await this.findSingleItem(tab, info.srcUrl, false);
}
async onClickedDTATurboImage(info: any, tab: any) {
return await this.findSingleItem(tab, info.srcUrl, true);
async onClickedDTATurboImage(info: MenuClickInfo, tab: Tab) {
if (!info.srcUrl) {
return;
}
await this.findSingleItem(tab, info.srcUrl, true);
}
async onClickedDTARegularMedia(info: any, tab: any) {
return await this.findSingleItem(tab, info.srcUrl, false);
async onClickedDTARegularMedia(info: MenuClickInfo, tab: Tab) {
if (!info.srcUrl) {
return;
}
await this.findSingleItem(tab, info.srcUrl, false);
}
async onClickedDTATurboMedia(info: any, tab: any) {
return await this.findSingleItem(tab, info.srcUrl, true);
async onClickedDTATurboMedia(info: MenuClickInfo, tab: Tab) {
if (!info.srcUrl) {
return;
}
await this.findSingleItem(tab, info.srcUrl, true);
}
onClickedDTAAdd() {

View File

@ -98,9 +98,9 @@ export class BatchGenerator implements Generator {
public readonly hasInvalid: boolean;
public readonly length: any;
public readonly length: number;
public readonly preview: any;
public readonly preview: string;
constructor(str: string) {
this.gens = [];

View File

@ -3,6 +3,42 @@
// eslint-disable-next-line @typescript-eslint/no-var-requires
const polyfill = require("webextension-polyfill");
interface ExtensionListener {
addListener: (listener: Function) => void;
removeListener: (listener: Function) => void;
}
export interface MessageSender {
tab?: Tab;
frameId?: number;
id?: number;
url?: string;
tlsChannelId?: string;
}
export interface Tab {
id?: number;
}
export interface MenuClickInfo {
menuItemId: string | number;
button?: number;
linkUrl?: string;
srcUrl?: string;
}
export interface RawPort {
error: any;
name: string;
onDisconnect: ExtensionListener;
onMessage: ExtensionListener;
sender?: MessageSender;
disconnect: () => void;
postMessage: (message: any) => void;
}
export const {extension} = polyfill;
export const {notifications} = polyfill;
export const {browserAction} = polyfill;

View File

@ -2,22 +2,18 @@
// License: MIT
import { EventEmitter } from "./events";
import {runtime, tabs} from "./browser";
// eslint-disable-next-line no-unused-vars
import {runtime, tabs, RawPort, MessageSender} from "./browser";
export class Port extends EventEmitter {
private port: any;
private port: RawPort | null;
constructor(port: any) {
constructor(port: RawPort) {
super();
this.port = port;
let disconnected = false;
let tabListener: any;
const disconnect = () => {
if (tabListener) {
tabs.onRemoved.removeListener(tabListener);
tabListener = null;
}
if (disconnected) {
return;
}
@ -41,12 +37,17 @@ export class Port extends EventEmitter {
}
get name() {
if (!this.port) {
return null;
}
return this.port.name;
}
get id() {
return this.port.sender && (
this.port.sender.id || this.port.sender.extensionId);
if (!this.port || !this.port.sender) {
return null;
}
return this.port.sender.id;
}
get isSelf() {
@ -54,6 +55,9 @@ export class Port extends EventEmitter {
}
post(msg: string, ...data: any[]) {
if (!this.port) {
return;
}
if (!data) {
this.port.postMessage({msg});
return;
@ -65,14 +69,17 @@ export class Port extends EventEmitter {
}
onMessage(message: any) {
if (Object.keys(message).includes("msg")) {
this.emit(message.msg, message);
if (!this.port) {
return;
}
if (Array.isArray(message)) {
message.forEach(this.onMessage, this);
return;
}
if (Object.keys(message).includes("msg")) {
this.emit(message.msg, message);
return;
}
if (typeof message === "string") {
this.emit(message);
return;
@ -100,7 +107,7 @@ export const Bus = new class extends EventEmitter {
runtime.onConnect.addListener(this.onConnect.bind(this));
}
onMessage(msg: any, sender: any, callback: any) {
onMessage(msg: any, sender: MessageSender, callback: any) {
let {type = null} = msg;
if (!type) {
type = msg;
@ -108,7 +115,7 @@ export const Bus = new class extends EventEmitter {
this.emit(type, msg, callback);
}
onConnect(port: any) {
onConnect(port: RawPort) {
if (!port.name) {
port.disconnect();
return;

View File

@ -1,5 +1,8 @@
"use strict";
// eslint-disable-next-line no-unused-vars
import { BaseItem } from "./item";
// License: MIT
const VERSION = 1;
@ -40,12 +43,12 @@ export const DB = new class DB {
});
}
getAllInternal(resolve: (items: any[]) => void, reject: Function) {
getAllInternal(resolve: (items: BaseItem[]) => void, reject: Function) {
if (!this.db) {
reject(new Error("db closed"));
return;
}
const items: any[] = [];
const items: BaseItem[] = [];
const transaction = this.db.transaction(STORE, "readonly");
transaction.onerror = ex => reject(ex);
const store = transaction.objectStore(STORE);

View File

@ -12,6 +12,8 @@ import { Overlayable } from "./objectoverlay";
import * as DEFAULT_FILTERS from "../data/filters.json";
import { FASTFILTER } from "./recentlist";
import { _, locale } from "./i18n";
// eslint-disable-next-line no-unused-vars
import { BaseItem } from "./item";
const REG_ESCAPE = /[{}()[\]\\^$.]/g;
const REG_FNMATCH = /[*?]/;
@ -174,25 +176,37 @@ export class Matcher {
}
/* eslint-enable no-unused-vars */
matchItem(item: any) {
matchItem(item: BaseItem) {
const {usable = "", title = "", description = "", fileName = ""} = item;
return this.match(usable) || this.match(title) ||
this.match(description) || this.match(fileName);
}
}
interface RawFilter extends Object {
active: boolean;
type: number;
label: string;
expr: string;
icon?: string;
custom?: boolean;
isOverridden?: (prop: string) => boolean;
reset?: () => void;
toJSON?: () => any;
}
export class Filter {
private readonly owner: Filters;
public readonly id: any;
public readonly id: string | symbol;
private readonly raw: any;
private readonly raw: RawFilter;
private _label: string;
private _reg: Matcher;
constructor(owner: Filters, id: any, raw: any) {
constructor(owner: Filters, id: string | symbol, raw: RawFilter) {
if (!owner || !id || !raw) {
throw new Error("null argument");
}
@ -204,10 +218,12 @@ export class Filter {
init() {
this._label = this.raw.label;
if (this.id !== FAST && this.id.startsWith("deffilter-") &&
!this.raw.isOverridden("label")) {
if (typeof this.raw.isOverridden !== "undefined" &&
typeof this.id === "string") {
if (this.id.startsWith("deffilter-") && !this.raw.isOverridden("label")) {
this._label = _(this.id) || this._label;
}
}
this._reg = Matcher.fromExpression(this.expr);
Object.seal(this);
}
@ -282,7 +298,7 @@ export class Filter {
}
async reset() {
if (this.raw.custom) {
if (!this.raw.reset) {
throw Error("Cannot reset non-default filter");
}
this.raw.reset();
@ -292,7 +308,10 @@ export class Filter {
async "delete"() {
if (!this.raw.custom) {
throw Error("Cannot delete default filter");
throw new Error("Cannot delete default filter");
}
if (typeof this.id !== "string") {
throw new Error("Cannot delete symbolized");
}
await this.owner.delete(this.id);
}
@ -301,7 +320,7 @@ export class Filter {
return this._reg.match(str);
}
matchItem(item: any) {
matchItem(item: BaseItem) {
return this._reg.matchItem(item);
}
@ -316,8 +335,7 @@ class FastFilter extends Filter {
throw new Error("Invalid fast filter value");
}
super(owner, FAST, {
id: FAST,
label: FAST,
label: "fast",
type: TYPE_ALL,
active: true,
expr: value,
@ -409,11 +427,11 @@ class Filters extends EventEmitter {
await this.save();
}
"get"(id: any) {
"get"(id: string | symbol) {
return this.filters.find(e => e.id === id);
}
async "delete"(id: any) {
async "delete"(id: string) {
const idx = this.filters.findIndex(e => e.id === id);
if (idx < 0) {
return;
@ -517,7 +535,7 @@ class Filters extends EventEmitter {
this.regenerate();
}
async filterItemsByType(items: any[], type: number) {
async filterItemsByType(items: BaseItem[], type: number) {
const matcher = this.typeMatchers.get(type);
const fast = await this.getFastFilter();
return items.filter(function(item) {
@ -544,12 +562,14 @@ class Filters extends EventEmitter {
}
}
let _filters: any;
let _filters: Filters;
let _loader: Promise<void>;
export async function filters(): Promise<Filters> {
if (!_filters) {
if (!_loader) {
_filters = new Filters();
await _filters.load();
_loader = _filters.load();
}
await _loader;
return _filters;
}

View File

@ -4,18 +4,32 @@
import { ALLOWED_SCHEMES } from "./constants";
import { TRANSFERABLE_PROPERTIES } from "./constants";
export interface BaseItem {
url: string;
usable: string;
referrer?: string;
usableReferrer?: string;
description?: string;
title?: string;
fileName?: string;
batch?: number;
idx: number;
mask?: string;
startDate?: number;
private?: boolean;
postData?: string;
paused?: boolean;
}
const OPTIONPROPS = Object.freeze([
"referrer", "usableReferrer",
"description", "title",
"fileName",
"batch", "idx",
"mask",
"fromMetalink",
"startDate",
"hashes",
"private",
"postData",
"cleanRequest",
"paused"
]);
@ -34,7 +48,7 @@ function maybeAssign(options: any, what: any) {
this[what] = val;
}
export class Item {
export class Item implements BaseItem {
public url: string;
public usable: string;
@ -43,6 +57,8 @@ export class Item {
public usableReferrer: string;
public idx: number;
constructor(raw: any, options?: any) {
Object.assign(this, raw);
OPTIONPROPS.forEach(maybeAssign.bind(this, options || {}));

View File

@ -29,8 +29,6 @@ const SAVEDPROPS = [
"serverName",
// other options
"private",
"fromMetalink",
"cleanRequest",
// db
"manId",
"dbId",

View File

@ -10,10 +10,24 @@ import { donate, openPrefs, openUrls } from "./windowutils";
import { filters, FAST, Filter } from "./filters";
import { WindowStateTracker } from "./windowstatetracker";
import { windows } from "./browser";
// eslint-disable-next-line no-unused-vars
import { BaseItem } from "./item";
interface BaseMatchedItem extends BaseItem {
matched?: string | null;
prevMatched?: string | null;
}
function computeSelection(filters: any[], items: any[], onlyFast: boolean) {
let ws = items.map((item: any, idx: number) => {
export interface ItemDelta {
idx: number;
matched?: string | null;
}
function computeSelection(
filters: Filter[],
items: BaseMatchedItem[],
onlyFast: boolean): ItemDelta[] {
let ws = items.map((item, idx: number) => {
item.idx = idx;
const {matched = null} = item;
item.prevMatched = matched;
@ -23,9 +37,15 @@ function computeSelection(filters: any[], items: any[], onlyFast: boolean) {
for (const filter of filters) {
ws = ws.filter(item => {
if (filter.matchItem(item)) {
item.matched = filter.id === FAST ?
"fast" :
(onlyFast ? null : filter.id);
if (filter.id === FAST) {
item.matched = "fast";
}
else if (!onlyFast && typeof filter.id === "string") {
item.matched = filter.id;
}
else {
item.matched = null;
}
}
return !item.matched;
});
@ -41,6 +61,9 @@ function computeSelection(filters: any[], items: any[], onlyFast: boolean) {
function *computeActiveFiltersGen(
filters: Filter[], activeOverrides: Map<string, boolean>) {
for (const filter of filters) {
if (typeof filter.id !== "string") {
continue;
}
const override = activeOverrides.get(filter.id);
if (typeof override === "boolean") {
if (override) {
@ -59,11 +82,11 @@ function computeActiveFilters(
return Array.from(computeActiveFiltersGen(filters, activeOverrides));
}
function filtersToDescs(filters: any[]) {
function filtersToDescs(filters: Filter[]) {
return filters.map(f => f.descriptor);
}
export async function select(links: any[], media: any[]) {
export async function select(links: BaseItem[], media: BaseItem[]) {
const fm = await filters();
const tracker = new WindowStateTracker("select", {
minWidth: 700,
@ -85,7 +108,7 @@ export async function select(links: any[], media: any[]) {
tracker.track(window.id, port);
const overrides = new Map();
let fast: any = null;
let fast: Filter | null = null;
let onlyFast: false;
try {
fast = await fm.getFastFilter();
@ -95,16 +118,16 @@ export async function select(links: any[], media: any[]) {
}
const sendFilters = function(delta = false) {
let {linkFilters, mediaFilters} = fm;
const {linkFilters, mediaFilters} = fm;
const alink = computeActiveFilters(linkFilters, overrides);
const amedia = computeActiveFilters(mediaFilters, overrides);
const sactiveFilters = new Set<any>();
[alink, amedia].forEach(
a => a.forEach(filter => sactiveFilters.add(filter.id)));
const activeFilters = Array.from(sactiveFilters);
linkFilters = filtersToDescs(linkFilters);
mediaFilters = filtersToDescs(mediaFilters);
port.post("filters", {linkFilters, mediaFilters, activeFilters});
const linkFilterDescs = filtersToDescs(linkFilters);
const mediaFilterDescs = filtersToDescs(mediaFilters);
port.post("filters", {linkFilterDescs, mediaFilterDescs, activeFilters});
if (fast) {
alink.unshift(fast);
@ -128,9 +151,6 @@ export async function select(links: any[], media: any[]) {
});
port.on("queue", (msg: any) => {
const selected = new Set<number>(msg.items);
const items = (msg.type === "links" ? links : media);
msg.items = items.filter((item: any, idx: number) => selected.has(idx));
done.resolve(msg);
});
@ -175,7 +195,11 @@ export async function select(links: any[], media: any[]) {
sendFilters(false);
const type = await Prefs.get("last-type", "links");
port.post("items", {type, links, media});
const result = await done;
const {items, options} = await done;
const selectedIndexes = new Set<number>(items);
const selectedList = (options.type === "links" ? links : media);
const selectedItems = selectedList.filter(
(item: BaseItem, idx: number) => selectedIndexes.has(idx));
for (const [filter, override] of overrides) {
const f = fm.get(filter);
if (f) {
@ -183,7 +207,7 @@ export async function select(links: any[], media: any[]) {
}
}
await fm.save();
return result;
return {items: selectedItems, options};
}
finally {
fm.off("changed", sendFilters);

View File

@ -7,8 +7,10 @@ import { WindowStateTracker } from "./windowstatetracker";
import { Promised, timeout } from "./util";
import { donate } from "./windowutils";
import { windows } from "./browser";
// eslint-disable-next-line no-unused-vars
import { BaseItem } from "./item";
export async function single(item: any) {
export async function single(item: BaseItem | null) {
const tracker = new WindowStateTracker("single", {
minWidth: 700,
minHeight: 460
@ -46,7 +48,9 @@ export async function single(item: any) {
donate();
});
port.post("item", item);
if (item) {
port.post("item", {item});
}
return await done;
}
finally {

View File

@ -14,6 +14,8 @@ import {
} from "./tablesymbols";
import { InvalidatedSet, UpdateRecord } from "./tableutil";
import { addClass, clampUInt, IS_MAC } from "./util";
// eslint-disable-next-line no-unused-vars
import { TableConfig } from "./config";
const ROWS_SMALL_UPDATE = 5;
const PIXEL_PREC = 5;
@ -79,7 +81,7 @@ export class BaseTable extends AbstractTable {
[COLS]: Columns;
constructor(elem: any, config: any, version?: number) {
constructor(elem: any, config: TableConfig | null, version?: number) {
config = (config && config.version === version && config) || {};
super();
@ -121,9 +123,9 @@ export class BaseTable extends AbstractTable {
this.makeDOM(config);
}
makeDOM(config: any) {
makeDOM(config: TableConfig) {
const configColumns = "columns" in config ? config.columns : null;
const cols = this[COLS] = new Columns(this, configColumns);
const cols = this[COLS] = new Columns(this, configColumns || null);
const container = document.createElement("div");
const thead = document.createElement("div");
@ -241,7 +243,7 @@ export class BaseTable extends AbstractTable {
return new SelectionRange(firstIdx, lastIdx);
}
get config() {
get config(): TableConfig {
return {
version: this.version,
columns: this.columnConfig

View File

@ -1,14 +1,12 @@
"use strict";
// License: MIT
/* eslint-disable no-unused-vars */
import { TableEvents } from "./tableevents";
import {addClass, debounce, sum} from "./util";
import {EventEmitter} from "./events";
import {APOOL} from "./animationpool";
/* eslint-enable no-unused-vars */
// License: MIT
import { ColumnConfig, ColumnConfigs } from "./config";
const PIXLIT_WIDTH = 2;
const MIN_COL_WIDTH = 16;
@ -55,8 +53,7 @@ export class Column extends EventEmitter {
columns: Columns,
col: HTMLTableHeaderCellElement,
id: number,
config: any) {
config = config || {};
config: ColumnConfig | null) {
super();
this.columns = columns;
this.elem = col;
@ -89,7 +86,7 @@ export class Column extends EventEmitter {
this.elem.appendChild(containerElem);
if ("visible" in config) {
if (config) {
this.visible = config.visible;
}
this.initWidths(config);
@ -148,18 +145,18 @@ export class Column extends EventEmitter {
return Math.max(0, this.currentWidth - this.minWidth);
}
get config() {
get config(): ColumnConfig {
return {
visible: this.visible,
width: this.currentWidth,
};
}
initWidths(config: any) {
initWidths(config: ColumnConfig | null) {
const style = getComputedStyle(this.elem, null);
this.minWidth = toPixel(style.getPropertyValue("min-width"), MIN_COL_WIDTH);
this.maxWidth = toPixel(style.getPropertyValue("max-width"), 0);
const width = config.width || this.baseWidth;
const width = (config && config.width) || this.baseWidth;
this.setWidth(width);
}
@ -236,7 +233,7 @@ export class Columns extends EventEmitter {
public visible: Column[];
constructor(table: any, config: any) {
constructor(table: any, config: ColumnConfigs | null) {
config = config || {};
super();
this.table = table;
@ -247,7 +244,9 @@ export class Columns extends EventEmitter {
this.named = new Map<string, Column>();
this.cols = Array.from(table.elem.querySelectorAll("th")).
map((colEl: HTMLTableHeaderCellElement, colid: number) => {
const columnConfig = colEl.id in config ? config[colEl.id] : null;
const columnConfig = config && colEl.id in config ?
config[colEl.id] :
null;
const col = new Column(this, colEl, colid, columnConfig);
col.on("gripmoved", this.gripmoved);
this.named.set(colEl.id, col);
@ -261,7 +260,7 @@ export class Columns extends EventEmitter {
Object.seal(this);
}
get config() {
get config(): ColumnConfigs {
const rv: any = {};
for (const c of this.cols) {
rv[c.elem.id] = c.config;

13
uikit/lib/config.ts Normal file
View File

@ -0,0 +1,13 @@
"use strict";
// License: MIT
export interface ColumnConfig {
visible: boolean;
width: number;
}
export type ColumnConfigs ={ [name: string]: ColumnConfig };
export interface TableConfig {
version?: number;
columns?: ColumnConfigs;
}

View File

@ -33,6 +33,14 @@ export interface MenuPosition {
clientY: number;
}
interface MenuOptions {
disabled?: string;
allowClick?: string;
icon?: string;
key?: string;
autoHide?: string;
}
export class MenuItemBase {
public readonly owner: ContextMenu;
@ -44,7 +52,7 @@ export class MenuItemBase {
public readonly key: string;
public readonly autohide: boolean;
public readonly autoHide: boolean;
public readonly elem: HTMLLIElement;
@ -54,18 +62,16 @@ export class MenuItemBase {
public readonly keyElem: HTMLSpanElement;
constructor(owner: ContextMenu, id = "", text = "", {
key = "", icon = "", autohide = Object()
}) {
constructor(owner: ContextMenu, id = "", text = "", options: MenuOptions) {
this.owner = owner;
if (!id) {
id = `contextmenu-${++ids}`;
}
this.id = id;
this.text = text || "";
this.icon = icon || "";
this.key = key || "";
this.autohide = autohide !== "false" && autohide !== false;
this.icon = options.icon || "";
this.key = options.key || "";
this.autoHide = options.autoHide !== "false";
this.elem = document.createElement("li");
this.elem.id = this.id;
@ -96,13 +102,14 @@ export class MenuItemBase {
}
export class MenuItem extends MenuItemBase {
constructor(owner: ContextMenu, id = "", text = "", options: any = {}) {
constructor(
owner: ContextMenu, id = "", text = "", options: MenuOptions = {}) {
options = options || {};
super(owner, id, text, options);
this.disabled = !!options.disabled;
this.disabled = options.disabled === "true";
this.elem.setAttribute("aria-role", "menuitem");
this.elem.addEventListener(
"click", () => this.owner.emit("clicked", this.id, this.autohide));
"click", () => this.owner.emit("clicked", this.id, this.autoHide));
}
get disabled() {
@ -132,7 +139,8 @@ export class SubMenuItem extends MenuItemBase {
public readonly expandElem: HTMLSpanElement;
constructor(owner: ContextMenu, id = "", text = "", options: any = {}) {
constructor(
owner: ContextMenu, id = "", text = "", options: MenuOptions = {}) {
super(owner, id, text, options);
this.elem.setAttribute("aria-role", "menuitem");
this.elem.setAttribute("aria-haspopup", "true");
@ -145,8 +153,8 @@ export class SubMenuItem extends MenuItemBase {
this.expandElem.textContent = "►";
this.elem.appendChild(this.expandElem);
this.elem.addEventListener("click", event => {
if (options.allowClick) {
this.owner.emit("clicked", this.id, this.autohide);
if (options.allowClick === "true") {
this.owner.emit("clicked", this.id, this.autoHide);
}
event.stopPropagation();
event.preventDefault();
@ -160,7 +168,7 @@ export class SubMenuItem extends MenuItemBase {
this.owner.on("showing", () => {
this.menu.dismiss();
});
this.menu.on("clicked", (...args: any) => {
this.menu.on("clicked", (...args: any[]) => {
this.owner.emit("clicked", ...args);
});
}
@ -215,7 +223,7 @@ export class SubMenuItem extends MenuItemBase {
export class ContextMenu extends EventEmitter {
id: string;
items: any[];
items: MenuItemBase[];
itemMap: Map<string, MenuItemBase>;
@ -223,7 +231,7 @@ export class ContextMenu extends EventEmitter {
showing: boolean;
_maybeDismiss: any;
_maybeDismiss: (this: Window, ev: MouseEvent) => any;
constructor(el?: any) {
super();
@ -348,10 +356,12 @@ export class ContextMenu extends EventEmitter {
return this.itemMap.get(id);
}
add(item: MenuItemBase, before: any = "") {
add(item: MenuItemBase, before: MenuItemBase | string = "") {
let idx = this.items.length;
if (before) {
before = before.id || before;
if (typeof before !== "string") {
before = before.id;
}
const ni = this.items.findIndex(i => i.id === before);
if (ni >= 0) {
idx = ni;
@ -366,8 +376,8 @@ export class ContextMenu extends EventEmitter {
this.itemMap.set(item.id, item);
}
remove(item: any) {
const id = item.id || item;
remove(item: MenuItemBase | string) {
const id = typeof item === "string" ? item : item.id;
const idx = this.items.findIndex(i => i.id === id);
if (idx >= 0) {
this.items.splice(idx, 1);

View File

@ -1,15 +1,20 @@
"use strict";
// License: MIT
interface ModalButton {
export interface ModalButton {
title: string;
value: string;
default?: boolean;
dismiss?: boolean;
}
interface Promised {
resolve: Function;
reject: Function;
}
export default abstract class ModalDialog {
private _showing: any;
private _showing: Promised | null;
private _dismiss: HTMLButtonElement | null;
@ -107,7 +112,10 @@ export default abstract class ModalDialog {
];
}
done(button: any) {
done(button: ModalButton) {
if (!this._showing) {
return;
}
const value = this.convertValue(button.value);
if (button.dismiss) {
this._showing.reject(new Error(value));
@ -130,7 +138,7 @@ export default abstract class ModalDialog {
}
async show() {
async show(): Promise<any> {
if (this._showing) {
throw new Error("Double show");
}

View File

@ -38,7 +38,7 @@ class Hover {
private hovering: boolean;
private timer: any;
private timer: number | null;
constructor(row: Row) {
this.row = row;
@ -62,7 +62,7 @@ class Hover {
this.elem.addEventListener("mousemove", this.onmove, {passive: true});
this.x = evt.clientX;
this.y = evt.clientY;
this.timer = setTimeout(this.onhover, HOVER_TIME);
this.timer = window.setTimeout(this.onhover, HOVER_TIME);
}
onleave() {
@ -93,7 +93,7 @@ class Hover {
if (this.timer) {
clearTimeout(this.timer);
}
this.timer = setTimeout(this.onhover, HOVER_TIME);
this.timer = window.setTimeout(this.onhover, HOVER_TIME);
}
}

View File

@ -12,6 +12,8 @@ import {Row} from "./row";
import {APOOL} from "./animationpool";
import {COLS, ROWCACHE, VISIBLE} from "./tablesymbols";
import {ContextMenu, MenuItem} from "./contextmenu";
// eslint-disable-next-line no-unused-vars
import { TableConfig } from "./config";
const RESIZE_DEBOUNCE = 500;
const SCROLL_DEBOUNCE = 250;
@ -19,7 +21,7 @@ const SCROLL_DEBOUNCE = 250;
export class TableEvents extends BaseTable {
private oldVisibleTop: number;
constructor(elem: any, config?: any, version?: number) {
constructor(elem: any, config: TableConfig | null, version?: number) {
super(elem, config, version);
const {selection} = this;
selection.on("selection-added", this.selectionAdded.bind(this));
@ -172,7 +174,7 @@ export class TableEvents extends BaseTable {
ctx,
id,
col.spanElem.textContent || "",
{autohide: "false"});
{autoHide: "false"});
ctx.add(item);
item.iconElem.textContent = col.visible ? "✓" : " ";
ctx.on(id, async () => {

View File

@ -8,6 +8,8 @@ import { Row } from "./row";
import {APOOL} from "./animationpool";
import {ROW_CACHE_SIZE, ROW_REUSE_SIZE} from "./constants";
import {clampUInt} from "./util";
// eslint-disable-next-line no-unused-vars
import { BaseTable } from "./basetable";
export class InvalidatedSet<T> extends Set<T> {
@ -70,7 +72,7 @@ export class UpdateRecord {
bottom: number;
constructor(table: any, cols: Column[]) {
constructor(table: BaseTable, cols: Column[]) {
this.rowCount = table.rowCount;
this.scrollTop = table.visibleTop;
this.rowHeight = table.rowHeight;

View File

@ -13,14 +13,21 @@ export function addClass(elem: HTMLElement, ...cls: string[]) {
}
}
interface Timer {
args: any[];
}
export function debounce(fn: Function, to: number) {
let timer: any;
let timer: Timer | null;
return function(...args: any[]) {
if (timer) {
timer.args = args;
return;
}
setTimeout(function() {
if (!timer) {
return;
}
const {args} = timer;
timer = null;
try {
@ -38,7 +45,7 @@ function sumreduce(p: number, c: number) {
return p + c;
}
export function sum(arr: any[]) {
export function sum(arr: number[]) {
return arr.reduce(sumreduce, 0);
}

View File

@ -13,7 +13,7 @@ export class Dropdown extends EventEmitter {
select: HTMLSelectElement;
constructor(el: string, options: any[] = []) {
constructor(el: string, options: string[] = []) {
super();
let input = document.querySelector(el);
if (!input || !input.parentElement) {

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-autoHide="false">Invert</li>
<li id="ctx-menufilter-clear" data-autoHide="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

@ -41,7 +41,7 @@ export class TextFilter extends ItemFilter {
private box: HTMLInputElement;
private timer: any;
private timer: number | null;
private current: string;
@ -58,7 +58,7 @@ export class TextFilter extends ItemFilter {
if (this.timer) {
return;
}
this.timer = setTimeout(() => this.update(), TIMEOUT_SEARCH);
this.timer = window.setTimeout(() => this.update(), TIMEOUT_SEARCH);
});
this.box.addEventListener("keydown", e => {
if (e.key !== "Escape") {
@ -152,7 +152,7 @@ export class MenuFilter extends ItemFilter {
return;
}
const item = new MenuItem(this.menu, id, text, {
autohide: false,
autoHide: "false",
});
item.iconElem.textContent = checked ? "✓" : "";
this.items.set(id, {item, callback});
@ -202,16 +202,24 @@ export class MenuFilter extends ItemFilter {
}
}
type ChainedFunction = (item: DownloadItem) => boolean;
interface ChainedItem {
text: string;
fn: ChainedFunction;
}
class FixedMenuFilter extends MenuFilter {
collection: FilteredCollection;
selected: Set<any>;
selected: Set<ChainedItem>;
fixed: Set<any>;
fixed: Set<ChainedItem>;
chain: any;
chain: ChainedFunction | null;
constructor(id: string, collection: FilteredCollection, items: any[]) {
constructor(
id: string, collection: FilteredCollection, items: ChainedItem[]) {
super(id);
this.collection = collection;
this.selected = new Set();
@ -226,7 +234,7 @@ class FixedMenuFilter extends MenuFilter {
});
}
toggle(item: MenuItem) {
toggle(item: ChainedItem) {
if (this.selected.has(item)) {
this.selected.delete(item);
}
@ -241,14 +249,18 @@ class FixedMenuFilter extends MenuFilter {
this.collection.removeFilter(this);
return;
}
this.chain = Array.from(this.selected).reduce((prev, curr) => {
return (item: DownloadItem) => curr.fn(item) || (prev && prev(item));
}, null);
this.chain = null;
this.chain = Array.from(this.selected).reduce(
(prev: ChainedFunction | null, curr) => {
return (item: DownloadItem) => {
return curr.fn(item) || (prev !== null && prev(item));
};
}, this.chain);
this.collection.addFilter(this);
}
allow(item: DownloadItem) {
return this.chain(item);
return this.chain !== null && this.chain(item);
}
clear() {
@ -341,7 +353,7 @@ export class UrlMenuFilter extends MenuFilter {
});
}
toggleRegularFilter(filter: any) {
toggleRegularFilter(filter: Filter) {
if (this.filters.has(filter)) {
this.filters.delete(filter);
}

View File

@ -2,14 +2,18 @@
// License: MIT
import { EventEmitter } from "../../lib/events";
import { runtime } from "../../lib/browser";
// eslint-disable-next-line no-unused-vars
import { runtime, RawPort } from "../../lib/browser";
const PORT = new class Port extends EventEmitter {
port: any;
port: RawPort | null;
constructor() {
super();
this.port = runtime.connect(null, { name: "manager" });
if (!this.port) {
throw new Error("Could not connect");
}
this.port.onMessage.addListener((msg: any) => {
if (typeof msg === "string") {
this.emit(msg);
@ -23,10 +27,16 @@ const PORT = new class Port extends EventEmitter {
}
post(msg: string, data?: any) {
if (!this.port) {
return;
}
this.port.postMessage(Object.assign({msg}, data));
}
disconnect() {
if (!this.port) {
return;
}
this.port.disconnect();
this.port = null;
}

View File

@ -35,6 +35,8 @@ import "../../lib/util";
import { CellTypes } from "../../uikit/lib/constants";
import { downloads } from "../../lib/browser";
import { $ } from "../winutil";
// eslint-disable-next-line no-unused-vars
import { TableConfig } from "../../uikit/lib/config";
const TREE_CONFIG_VERSION = 2;
const RUNNING_TIMEOUT = 1000;
@ -311,9 +313,9 @@ export class DownloadTable extends VirtualTable {
public readonly showUrls: ShowUrlsWatcher;
private runningTimer: any;
private runningTimer: number | null;
private sizesTimer: any;
private sizesTimer: number | null;
private readonly globalStats: Stats;
@ -329,7 +331,7 @@ export class DownloadTable extends VirtualTable {
private readonly openFileAction: Broadcaster;
private readonly openDirectoryAction: any;
private readonly openDirectoryAction: Broadcaster;
private readonly moveTopAction: Broadcaster;
@ -339,11 +341,11 @@ export class DownloadTable extends VirtualTable {
private readonly moveBottomAction: Broadcaster;
private readonly disableSet: Set<any>;
private readonly disableSet: Set<Broadcaster>;
private tooltip: Tooltip | null;
constructor(treeConfig: any) {
constructor(treeConfig: TableConfig | null) {
super("#items", treeConfig, TREE_CONFIG_VERSION);
TEXT_SIZE_UNKNOWM = _("size-unknown");
@ -621,7 +623,7 @@ export class DownloadTable extends VirtualTable {
filter(e => e.startsWith(prefix)).
forEach(e => rem.remove(e));
for (const filt of filts.all) {
if (filt.id === "deffilter-all") {
if (typeof filt.id !== "string" || filt.id === "deffilter-all") {
continue;
}
const mi = new MenuItem(rem, `${prefix}-${filt.id}`, filt.label, {
@ -918,7 +920,7 @@ export class DownloadTable extends VirtualTable {
}
const filter = (await filters()).get(id);
if (!filter) {
if (!filter || typeof filter.id !== "string") {
return;
}
await new RemovalModalDialog(
@ -966,7 +968,7 @@ export class DownloadTable extends VirtualTable {
switch (oldState) {
case DownloadState.RUNNING:
this.running.delete(item);
if (!this.running.size && this.runningTimer) {
if (!this.running.size && this.runningTimer && this.sizesTimer) {
clearInterval(this.runningTimer);
this.runningTimer = null;
clearInterval(this.sizesTimer);
@ -983,9 +985,9 @@ export class DownloadTable extends VirtualTable {
case DownloadState.RUNNING:
this.running.add(item);
if (!this.runningTimer) {
this.runningTimer = setInterval(
this.runningTimer = window.setInterval(
this.updateRunning.bind(this), RUNNING_TIMEOUT);
this.sizesTimer = setInterval(
this.sizesTimer = window.setInterval(
this.updateSizes.bind(this), SIZES_TIMEOUT);
this.updateRunning();
this.updateSizes();

View File

@ -6,7 +6,8 @@ import { Prefs, PrefWatcher } from "../lib/prefs";
import { hostToDomain } from "../lib/util";
import { filters } from "../lib/filters";
import {Limits} from "../lib/manager/limits";
import ModalDialog from "../uikit/lib/modal";
// eslint-disable-next-line no-unused-vars
import ModalDialog, { ModalButton } from "../uikit/lib/modal";
import { TYPE_LINK, TYPE_MEDIA } from "../lib/constants";
import { iconForPath, visible } from "../lib/windowutils";
import { VirtualTable } from "../uikit/lib/table";
@ -138,7 +139,7 @@ class CreateFilterDialog extends ModalDialog {
this.label.focus();
}
done(b: any) {
done(b: ModalButton) {
if (!b || !b.default) {
return super.done(b);
}
@ -210,7 +211,7 @@ class FiltersUI extends VirtualTable {
ignoreNext: boolean;
constructor() {
super("#filters");
super("#filters", null);
this.filters = [];
this.icons = new Icons($("#icons"));
const filter: any = null;
@ -403,7 +404,7 @@ class LimitsUI extends VirtualTable {
};
constructor() {
super("#limits");
super("#limits", null);
this.limits = [];
Limits.on("changed", () => {
this.limits = Array.from(Limits);

View File

@ -15,10 +15,17 @@ import { Icons } from "./icons";
import { sort, naturalCaseCompare } from "../lib/sorting";
import { hookButton } from "../lib/manager/renamer";
import { CellTypes } from "../uikit/lib/constants";
import { runtime } from "../lib/browser";
// eslint-disable-next-line no-unused-vars
import { runtime, RawPort } from "../lib/browser";
import { $ } from "./winutil";
// eslint-disable-next-line no-unused-vars
import { BaseItem } from "../lib/item";
// eslint-disable-next-line no-unused-vars
import { ItemDelta } from "../lib/select";
// eslint-disable-next-line no-unused-vars
import { TableConfig } from "../uikit/lib/config";
const PORT = runtime.connect(null, { name: "select" });
const PORT: RawPort = runtime.connect(null, { name: "select" });
const TREE_CONFIG_VERSION = 1;
@ -37,7 +44,11 @@ let Mask: Dropdown;
let FastFilter: Dropdown;
type DELTAS = {deltaLinks: any[]; deltaMedia: any[]};
type DELTAS = {deltaLinks: ItemDelta[]; deltaMedia: ItemDelta[]};
interface BaseMatchedItem extends BaseItem {
matched?: string | null;
}
function cleaErrors() {
const not = $("#notification");
@ -46,7 +57,7 @@ function cleaErrors() {
}
function matched(item: any) {
function matched(item: BaseMatchedItem) {
return item && item.matched && item.matched !== "unmanual";
}
@ -113,18 +124,20 @@ class CheckClasser extends Map<string, string> {
}
}
type KeyFn = (item: BaseMatchedItem) => any;
class SelectionTable extends VirtualTable {
checkClasser: CheckClasser;
icons: Icons;
links: any[];
links: BaseMatchedItem[];
media: any[];
media: BaseMatchedItem[];
type: string;
items: any[];
items: BaseMatchedItem[];
status: HTMLElement;
@ -142,9 +155,11 @@ class SelectionTable extends VirtualTable {
sortasc: boolean;
keyfns: Map<string, (item: any) => any>;
keyfns: Map<string, KeyFn>;
constructor(treeConfig: any, type: string, links: any[], media: any[]) {
constructor(
treeConfig: TableConfig | null, type: string,
links: BaseMatchedItem[], media: BaseMatchedItem[]) {
if (type === "links" && !links.length) {
type = "media";
}
@ -187,7 +202,7 @@ class SelectionTable extends VirtualTable {
this.sortcol = null;
this.sortasc = true;
this.keyfns = new Map([
this.keyfns = new Map<string, KeyFn>([
["colDownload", item => item.usable],
["colTitle", item => [item.title, item.usable]],
["colDescription", item => [item.description, item.usable]],
@ -270,7 +285,7 @@ class SelectionTable extends VirtualTable {
oldmask = "";
break;
}
oldmask = m;
oldmask = m || oldmask;
}
try {
Keys.suppressed = true;
@ -374,7 +389,7 @@ class SelectionTable extends VirtualTable {
});
}
applyDeltaTo(delta: any[], items: any[]) {
applyDeltaTo(delta: ItemDelta[], items: BaseMatchedItem[]) {
const active = items === this.items;
for (const d of delta) {
const {idx = -1, matched = null} = d;
@ -432,7 +447,7 @@ class SelectionTable extends VirtualTable {
getRowClasses(rowid: number) {
const item = this.items[rowid];
if (!item || !matched(item)) {
if (!item || !matched(item) || !item.matched) {
return null;
}
return ["filtered", this.checkClasser.get(item.matched)];
@ -467,7 +482,7 @@ class SelectionTable extends VirtualTable {
}
getText(prop: string, idx: number) {
const item = this.items[idx];
const item: any = this.items[idx];
if (!item || !(prop in item) || !item[prop]) {
return "";
}
@ -506,7 +521,7 @@ class SelectionTable extends VirtualTable {
getCellCheck(rowid: number, colid: number) {
if (colid === COL_CHECK) {
return matched(this.items[rowid]);
return !!matched(this.items[rowid]);
}
return false;
}
@ -549,13 +564,15 @@ async function download(paused = false) {
}
PORT.postMessage({
msg: "queue",
type: Table.type,
items,
options: {
type: Table.type,
paused,
mask,
maskOnce: $<HTMLInputElement>("#maskOnceCheck").checked,
fast: FastFilter.value,
fastOnce: $<HTMLInputElement>("#fastOnceCheck").checked,
}
});
}
catch (ex) {
@ -567,17 +584,17 @@ async function download(paused = false) {
}
class Filter {
active: any;
container: any;
elem: HTMLLabelElement;
label: any;
active: boolean;
checkElem: HTMLInputElement;
id: any;
container: HTMLElement;
elem: HTMLLabelElement;
id: string;
label: string;
constructor(container: HTMLElement, raw: any, active = false) {
Object.assign(this, raw);

View File

@ -4,7 +4,8 @@
import ModalDialog from "../uikit/lib/modal";
import { _, localize } from "../lib/i18n";
import { Item } from "../lib/item";
// eslint-disable-next-line no-unused-vars
import { Item, BaseItem } from "../lib/item";
import { MASK } from "../lib/recentlist";
import { BatchGenerator } from "../lib/batches";
import { WindowState } from "./windowstate";
@ -16,7 +17,7 @@ import { $ } from "./winutil";
const PORT = runtime.connect(null, { name: "single" });
let ITEM: any;
let ITEM: BaseItem;
let Mask: Dropdown;
class BatchModalDialog extends ModalDialog {
@ -59,7 +60,7 @@ class BatchModalDialog extends ModalDialog {
}
}
function setItem(item: any) {
function setItem(item: BaseItem) {
if (!item) {
return;
}
@ -179,10 +180,12 @@ async function downloadInternal(paused: boolean) {
PORT.postMessage({
msg: "queue",
paused,
items,
options: {
paused,
mask,
maskOnce: $<HTMLInputElement>("#maskOnceCheck").checked,
}
});
return null;
}
@ -204,7 +207,27 @@ async function init() {
addEventListener("DOMContentLoaded", async function dom() {
removeEventListener("DOMContentLoaded", dom);
await init();
const inited = init();
PORT.onMessage.addListener(async (msg: any) => {
try {
switch (msg.msg) {
case "item": {
await inited;
setItem(msg.data.item);
return;
}
default:
throw Error("Unhandled message");
}
}
catch (ex) {
console.error("Failed to process message", msg, ex);
}
});
await inited;
$("#btnDownload").addEventListener("click", () => download(false));
$("#btnPaused").addEventListener("click", () => download(true));
@ -224,23 +247,6 @@ addEventListener("DOMContentLoaded", async function dom() {
return true;
});
PORT.onMessage.addListener((msg: any) => {
try {
switch (msg.msg) {
case "item": {
setItem(msg.data);
return;
}
default:
throw Error("Unhandled message");
}
}
catch (ex) {
console.error("Failed to process message", msg, ex);
}
});
hookButton($("#maskButton"));
});

View File

@ -1,10 +1,14 @@
"use strict";
// eslint-disable-next-line no-unused-vars
import { RawPort } from "../lib/browser";
// License: MIT
export class WindowState {
private readonly port: any;
private readonly port: RawPort;
constructor(port: any) {
constructor(port: RawPort) {
this.port = port;
this.update = this.update.bind(this);
addEventListener("resize", this.update);