downthemall/windows/single.ts
2019-08-26 19:50:06 +02:00

273 lines
5.9 KiB
TypeScript

/* eslint-disable require-atomic-updates */
"use strict";
// License: MIT
import ModalDialog from "../uikit/lib/modal";
import { _, localize } from "../lib/i18n";
// 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";
import { Dropdown } from "./dropdown";
import { Keys } from "./keys";
import { hookButton } from "../lib/manager/renamer";
import { runtime } from "../lib/browser";
import { $ } from "./winutil";
const PORT = runtime.connect(null, { name: "single" });
let ITEM: BaseItem;
let Mask: Dropdown;
class BatchModalDialog extends ModalDialog {
private readonly gen: BatchGenerator;
constructor(gen: BatchGenerator) {
super();
this.gen = gen;
}
getContent() {
const tmpl = $("#batch-template") as HTMLTemplateElement;
const content = tmpl.content.cloneNode(true) as DocumentFragment;
$(".batch-items", content).textContent = this.gen.length.toLocaleString();
$(".batch-preview", content).textContent = this.gen.preview;
return content;
}
get buttons() {
return [
{
title: _("batch.batch"),
value: "ok",
default: true,
dismiss: false
},
{
title: _("batch.single"),
value: "single",
default: false,
dismiss: false
},
{
title: _("cancel"),
value: "cancel",
default: false,
dismiss: true,
}
];
}
}
function setItem(item: BaseItem) {
if (!item) {
return;
}
ITEM = item;
const {
usable = "",
fileName = "",
title = "",
description = "",
usableReferrer = "",
mask = ""
} = item;
$<HTMLInputElement>("#URL").value = usable;
$<HTMLInputElement>("#filename").value = fileName;
$<HTMLInputElement>("#title").value = title;
$<HTMLInputElement>("#description").value = description;
$<HTMLInputElement>("#referrer").value = usableReferrer;
if (mask) {
Mask.value = mask;
}
}
function displayError(err: string) {
const not = $("#notification");
not.textContent = _(err);
not.style.display = "block";
}
async function downloadInternal(paused: boolean) {
let usable = $<HTMLInputElement>("#URL").value.trim();
let url;
try {
url = new URL(usable).toString();
}
catch (ex) {
try {
url = new URL(`https://${usable}`).toString();
$<HTMLInputElement>("#URL").value = usable = `https://${usable}`;
}
catch (ex) {
return displayError("error.invalidURL");
}
}
const gen = new BatchGenerator(usable);
const usableReferrer = $<HTMLInputElement>("#referrer").value.trim();
let referrer;
try {
referrer = usableReferrer ? new URL(usableReferrer).toString() : "";
}
catch (ex) {
return displayError("error.invalidReferrer");
}
const fileName = $<HTMLInputElement>("#filename").value.trim();
const title = $<HTMLInputElement>("#title").value.trim();
const description = $<HTMLInputElement>("#description").value.trim();
const mask = Mask.value.trim();
if (!mask) {
return displayError("error.invalidMask");
}
const items = [];
if (!ITEM) {
ITEM = new Item({
url,
usable,
referrer,
usableReferrer,
fileName,
title,
description,
mask,
});
}
else {
ITEM.fileName = fileName;
ITEM.title = title;
ITEM.description = description;
ITEM.mask = mask;
if (usableReferrer !== ITEM.usableReferrer) {
ITEM.referrer = referrer;
ITEM.usableReferrer = usableReferrer;
}
}
let isBatch = gen.length > 1;
if (isBatch) {
try {
Keys.suppressed = true;
const rv = await new BatchModalDialog(gen).show();
isBatch = rv === "ok";
}
finally {
Keys.suppressed = false;
}
}
if (!isBatch) {
if (usable !== ITEM.usable) {
ITEM.url = url;
ITEM.usable = usable;
}
items.push(ITEM);
}
else {
for (const usable of gen) {
items.push(Object.assign(
{},
ITEM,
{ usable, url: new URL(usable).toString() }));
}
}
PORT.postMessage({
msg: "queue",
items,
options: {
paused,
mask,
maskOnce: $<HTMLInputElement>("#maskOnceCheck").checked,
}
});
return null;
}
function download(paused: boolean) {
downloadInternal(paused).catch(console.error);
}
function cancel() {
PORT.postMessage("cancel");
return true;
}
async function init() {
await localize(document.documentElement);
await Promise.all([MASK.init()]);
Mask = new Dropdown("#mask", MASK.values);
}
addEventListener("DOMContentLoaded", async function dom() {
removeEventListener("DOMContentLoaded", dom);
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));
$("#btnCancel").addEventListener(
"click", cancel);
Keys.on("Enter", "Return", () => {
download(false);
return true;
});
Keys.on("ACCEL-Enter", "ACCEL-Return", () => {
download(true);
return true;
});
Keys.on("Escape", () => {
cancel();
return true;
});
hookButton($("#maskButton"));
});
addEventListener("load", function () {
$("#URL").focus();
});
addEventListener("contextmenu", event => {
const target = event.target as HTMLElement;
if (target.localName === "input") {
return null;
}
event.preventDefault();
event.stopPropagation();
return false;
});
addEventListener("beforeunload", function () {
PORT.disconnect();
});
new WindowState(PORT);