Add resources download button + click to download picker in replay (#469)
Fixes #467 - Add a download button on the resource page for each entry to allow downloading each resource no matter what type. - Add a new 'click to download' image or media picker that allows highlighting + clicking on image/media to download on the page (to get around 'Save Image As...) not working. Also supports inline svgs + background images - should work better than Save Image As... - Location bar shows a popup indicating in 'click to download' mode, which can be canceled. Otherwise, mode remains until user downloads something. - Type fixes: Untangle Page and URLResource types for better type checking. - Deps: bump to wabac.js 2.25.1 for new download mode support --------- Co-authored-by: Tessa Walsh <tessa@bitarchivist.net>
This commit is contained in:
co-authored by
Tessa Walsh
parent
d2524ab26f
commit
916b154688
@@ -2,6 +2,12 @@
|
|||||||
|
|
||||||
## CHANGES
|
## CHANGES
|
||||||
|
|
||||||
|
*From release 2.4.0+, changes will now be listed in the release notes on GitHub.*
|
||||||
|
*Please see [the list of releases](https://github.com/webrecorder/replayweb.page/releases) for an up-to-date
|
||||||
|
list of changes for each release.*
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
v2.3.23
|
v2.3.23
|
||||||
|
|
||||||
- Fidelity: Fix possible regression from different JS parser in wabac.js, additional XHR improvements (viw abac.js 2.24.5)
|
- Fidelity: Fix possible regression from different JS parser in wabac.js, additional XHR improvements (viw abac.js 2.24.5)
|
||||||
|
|||||||
+2
-2
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "replaywebpage",
|
"name": "replaywebpage",
|
||||||
"productName": "ReplayWeb.page",
|
"productName": "ReplayWeb.page",
|
||||||
"version": "2.3.23",
|
"version": "2.4.0",
|
||||||
"description": "Serverless Web Archive Replay",
|
"description": "Serverless Web Archive Replay",
|
||||||
"repository": "https://github.com/webrecorder/replayweb.page",
|
"repository": "https://github.com/webrecorder/replayweb.page",
|
||||||
"homepage": "https://replayweb.page/",
|
"homepage": "https://replayweb.page/",
|
||||||
@@ -22,7 +22,7 @@
|
|||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@fortawesome/fontawesome-free": "^5.15.4",
|
"@fortawesome/fontawesome-free": "^5.15.4",
|
||||||
"@shoelace-style/shoelace": "~2.15.1",
|
"@shoelace-style/shoelace": "~2.15.1",
|
||||||
"@webrecorder/wabac": "^2.24.5",
|
"@webrecorder/wabac": "^2.25.1",
|
||||||
"bulma": "^0.9.3",
|
"bulma": "^0.9.3",
|
||||||
"electron-log": "^4.4.1",
|
"electron-log": "^4.4.1",
|
||||||
"electron-updater": "^6.6.2",
|
"electron-updater": "^6.6.2",
|
||||||
|
|||||||
+90
-3
@@ -28,6 +28,7 @@ import fasTriangleExclamation from "@fortawesome/fontawesome-free/svgs/solid/exc
|
|||||||
import fasBook from "@fortawesome/fontawesome-free/svgs/solid/book.svg";
|
import fasBook from "@fortawesome/fontawesome-free/svgs/solid/book.svg";
|
||||||
|
|
||||||
import fasDownload from "@fortawesome/fontawesome-free/svgs/solid/download.svg";
|
import fasDownload from "@fortawesome/fontawesome-free/svgs/solid/download.svg";
|
||||||
|
import fasFileDownload from "@fortawesome/fontawesome-free/svgs/regular/arrow-alt-circle-down.svg";
|
||||||
|
|
||||||
import farListAlt from "@fortawesome/fontawesome-free/svgs/regular/list-alt.svg";
|
import farListAlt from "@fortawesome/fontawesome-free/svgs/regular/list-alt.svg";
|
||||||
import farResources from "@fortawesome/fontawesome-free/svgs/solid/puzzle-piece.svg";
|
import farResources from "@fortawesome/fontawesome-free/svgs/solid/puzzle-piece.svg";
|
||||||
@@ -190,6 +191,9 @@ class Item extends LitElement {
|
|||||||
@property({ type: Array })
|
@property({ type: Array })
|
||||||
multiTs?: string[] = [];
|
multiTs?: string[] = [];
|
||||||
|
|
||||||
|
@property({ type: Boolean })
|
||||||
|
clickToDownloadMode = false;
|
||||||
|
|
||||||
private splitter: Split.Instance | null = null;
|
private splitter: Split.Instance | null = null;
|
||||||
|
|
||||||
private _replaceLoc = false;
|
private _replaceLoc = false;
|
||||||
@@ -743,8 +747,31 @@ class Item extends LitElement {
|
|||||||
line-height: 2;
|
line-height: 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#click-download-msg {
|
||||||
|
position: absolute;
|
||||||
|
right: 0.5rem;
|
||||||
|
z-index: 10;
|
||||||
|
background: #09c1ff;
|
||||||
|
top: 5px;
|
||||||
|
bottom: 5px;
|
||||||
|
margin-right: -3px;
|
||||||
|
border-radius: 8px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
line-height: 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
#hilite-sample {
|
||||||
|
width: 20px;
|
||||||
|
height: 20px;
|
||||||
|
background-color: rgba(0, 0, 255, 0.5);
|
||||||
|
border: solid 4px blue;
|
||||||
|
margin: 2px;
|
||||||
|
display: inline-flex;
|
||||||
|
}
|
||||||
|
|
||||||
/* Gradient to indicate URL clipping */
|
/* Gradient to indicate URL clipping */
|
||||||
#datetime:before {
|
.loc-overlay:before {
|
||||||
content: "";
|
content: "";
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: 0;
|
top: 0;
|
||||||
@@ -988,6 +1015,8 @@ class Item extends LitElement {
|
|||||||
id="replay"
|
id="replay"
|
||||||
@replay-loading="${this.onReplayLoading}"
|
@replay-loading="${this.onReplayLoading}"
|
||||||
@replay-favicons="${this.onFavIcons}"
|
@replay-favicons="${this.onFavIcons}"
|
||||||
|
@cancel-click-download="${() =>
|
||||||
|
(this.clickToDownloadMode = false)}"
|
||||||
>
|
>
|
||||||
</wr-coll-replay>
|
</wr-coll-replay>
|
||||||
`
|
`
|
||||||
@@ -1244,7 +1273,11 @@ class Item extends LitElement {
|
|||||||
.value="${this.url}"
|
.value="${this.url}"
|
||||||
placeholder="Enter text to search or a URL to replay"
|
placeholder="Enter text to search or a URL to replay"
|
||||||
/>
|
/>
|
||||||
${isReplay ? this.renderTimestamp() : ""}
|
${isReplay
|
||||||
|
? this.clickToDownloadMode
|
||||||
|
? this.renderClickToDownloadNotify()
|
||||||
|
: this.renderTimestamp()
|
||||||
|
: ""}
|
||||||
${showFavIcon
|
${showFavIcon
|
||||||
? html` <span class="favicon icon is-small is-left">
|
? html` <span class="favicon icon is-small is-left">
|
||||||
<img src="${this.favIconUrl}" />
|
<img src="${this.favIconUrl}" />
|
||||||
@@ -1258,6 +1291,23 @@ class Item extends LitElement {
|
|||||||
<p id="skip-replay-target" tabindex="-1" class="is-sr-only">Skipped</p>`;
|
<p id="skip-replay-target" tabindex="-1" class="is-sr-only">Skipped</p>`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected renderClickToDownloadNotify() {
|
||||||
|
return html`<article
|
||||||
|
id="click-download-msg"
|
||||||
|
class="loc-overlay has-background-link-light is-size-7"
|
||||||
|
>
|
||||||
|
<div class="ml-4 is-flex">
|
||||||
|
Select image or media highlighted with 🟦 (blue box) on hover to
|
||||||
|
download.
|
||||||
|
</div>
|
||||||
|
<button
|
||||||
|
class="mx-4 delete"
|
||||||
|
aria-label="delete"
|
||||||
|
@click="${this.cancelClickToDownload}"
|
||||||
|
></button>
|
||||||
|
</article>`;
|
||||||
|
}
|
||||||
|
|
||||||
protected renderToolbarRight() {
|
protected renderToolbarRight() {
|
||||||
const isReplay = !!this.tabData.url;
|
const isReplay = !!this.tabData.url;
|
||||||
|
|
||||||
@@ -1367,6 +1417,25 @@ class Item extends LitElement {
|
|||||||
<span>Purge Cache + Full Reload</span>
|
<span>Purge Cache + Full Reload</span>
|
||||||
</a>`
|
</a>`
|
||||||
: html``}
|
: html``}
|
||||||
|
${isReplay
|
||||||
|
? html`<hr class="dropdown-divider" />
|
||||||
|
<a
|
||||||
|
@click="${this.clickToDownload}"
|
||||||
|
role="button"
|
||||||
|
class="dropdown-item"
|
||||||
|
@keyup="${clickOnSpacebarPress}"
|
||||||
|
>
|
||||||
|
<span class="icon is-small">
|
||||||
|
<fa-icon
|
||||||
|
size="1.0em"
|
||||||
|
class="has-text-grey"
|
||||||
|
aria-hidden="true"
|
||||||
|
.svg="${fasFileDownload}"
|
||||||
|
></fa-icon>
|
||||||
|
</span>
|
||||||
|
<span>Select Media to Download</span>
|
||||||
|
</a>`
|
||||||
|
: html``}
|
||||||
${(!this.editable && this.downloadUrl?.startsWith("http://")) ||
|
${(!this.editable && this.downloadUrl?.startsWith("http://")) ||
|
||||||
this.downloadUrl?.startsWith("https://")
|
this.downloadUrl?.startsWith("https://")
|
||||||
? html` <hr class="dropdown-divider" />
|
? html` <hr class="dropdown-divider" />
|
||||||
@@ -1452,7 +1521,7 @@ class Item extends LitElement {
|
|||||||
const currDateStr = this.ts
|
const currDateStr = this.ts
|
||||||
? dateTimeFormatter.format(tsToDate(this.ts) as Date)
|
? dateTimeFormatter.format(tsToDate(this.ts) as Date)
|
||||||
: "";
|
: "";
|
||||||
return html`<div id="datetime" class="control is-hidden-mobile">
|
return html`<div id="datetime" class="control is-hidden-mobile loc-overlay">
|
||||||
${timestampStrs.length > 1
|
${timestampStrs.length > 1
|
||||||
? html`
|
? html`
|
||||||
<sl-dropdown placement="top-end" hoist>
|
<sl-dropdown placement="top-end" hoist>
|
||||||
@@ -1513,6 +1582,24 @@ class Item extends LitElement {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
clickToDownload() {
|
||||||
|
const replay = this.renderRoot.querySelector<Replay>("wr-coll-replay");
|
||||||
|
if (replay) {
|
||||||
|
this.clickToDownloadMode = true;
|
||||||
|
replay.setClickToDownload();
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
cancelClickToDownload() {
|
||||||
|
const replay = this.renderRoot.querySelector<Replay>("wr-coll-replay");
|
||||||
|
if (replay) {
|
||||||
|
this.clickToDownloadMode = false;
|
||||||
|
replay.clearHilite(true);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
renderItemInfo() {
|
renderItemInfo() {
|
||||||
if (!this.itemInfo)
|
if (!this.itemInfo)
|
||||||
return html`<sl-alert open variant="warning">
|
return html`<sl-alert open variant="warning">
|
||||||
|
|||||||
+6
-5
@@ -8,7 +8,7 @@ import "keyword-mark-element/lib/keyword-mark.js";
|
|||||||
import { getPageDateTS, getReplayLink } from "./pageutils";
|
import { getPageDateTS, getReplayLink } from "./pageutils";
|
||||||
|
|
||||||
import { wrapCss } from "./misc";
|
import { wrapCss } from "./misc";
|
||||||
import type { URLResource } from "./types";
|
import type { Page, URLTsChange } from "./types";
|
||||||
import {
|
import {
|
||||||
dateFormatter,
|
dateFormatter,
|
||||||
dateTimeFormatter,
|
dateTimeFormatter,
|
||||||
@@ -24,7 +24,7 @@ class PageEntry extends LitElement {
|
|||||||
textSnippet: string | null = "";
|
textSnippet: string | null = "";
|
||||||
|
|
||||||
@property({ type: Object })
|
@property({ type: Object })
|
||||||
page: URLResource | null = null;
|
page: Page | null = null;
|
||||||
|
|
||||||
@property({ type: String })
|
@property({ type: String })
|
||||||
replayPrefix = "";
|
replayPrefix = "";
|
||||||
@@ -241,7 +241,7 @@ class PageEntry extends LitElement {
|
|||||||
href="${getReplayLink(
|
href="${getReplayLink(
|
||||||
"pages",
|
"pages",
|
||||||
this.page!.url,
|
this.page!.url,
|
||||||
this.page!.timestamp!,
|
this.page!.timestamp,
|
||||||
this.page!.waczhash,
|
this.page!.waczhash,
|
||||||
)}"
|
)}"
|
||||||
>
|
>
|
||||||
@@ -294,7 +294,7 @@ class PageEntry extends LitElement {
|
|||||||
|
|
||||||
private getReplayPrefix() {
|
private getReplayPrefix() {
|
||||||
const waczhash = this.page!.waczhash ? `:${this.page!.waczhash}/` : "";
|
const waczhash = this.page!.waczhash ? `:${this.page!.waczhash}/` : "";
|
||||||
const ts = this.page!.timestamp || "";
|
const ts = this.page!.ts || "";
|
||||||
return this.replayPrefix + "/" + waczhash + ts + "id_";
|
return this.replayPrefix + "/" + waczhash + ts + "id_";
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -370,11 +370,12 @@ class PageEntry extends LitElement {
|
|||||||
onReplay(event: Event, reload = false) {
|
onReplay(event: Event, reload = false) {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
|
|
||||||
const data = {
|
const data: URLTsChange = {
|
||||||
url: this.page!.url,
|
url: this.page!.url,
|
||||||
ts: this.page!.timestamp,
|
ts: this.page!.timestamp,
|
||||||
waczhash: this.page!.waczhash,
|
waczhash: this.page!.waczhash,
|
||||||
};
|
};
|
||||||
|
|
||||||
this.sendChangeEvent(data, reload);
|
this.sendChangeEvent(data, reload);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|||||||
+21
-15
@@ -18,17 +18,17 @@ import fasEdit from "@fortawesome/fontawesome-free/svgs/solid/edit.svg";
|
|||||||
import type { Sorter } from "./sorter";
|
import type { Sorter } from "./sorter";
|
||||||
import type { PageEntry } from "./pageentry";
|
import type { PageEntry } from "./pageentry";
|
||||||
import type { Id, Index } from "flexsearch";
|
import type { Id, Index } from "flexsearch";
|
||||||
import type { ItemType, URLResource } from "./types";
|
import type { ItemType, Page } from "./types";
|
||||||
|
|
||||||
const DYNAMIC_PAGE_SIZE = 25;
|
const DYNAMIC_PAGE_SIZE = 25;
|
||||||
|
|
||||||
// ===========================================================================
|
// ===========================================================================
|
||||||
class Pages extends LitElement {
|
class Pages extends LitElement {
|
||||||
@property({ type: Array })
|
@property({ type: Array })
|
||||||
filteredPages: URLResource[] = [];
|
filteredPages: Page[] = [];
|
||||||
|
|
||||||
@property({ type: Array })
|
@property({ type: Array })
|
||||||
sortedPages: URLResource[] = [];
|
sortedPages: Page[] = [];
|
||||||
|
|
||||||
@property({ type: String })
|
@property({ type: String })
|
||||||
query = "";
|
query = "";
|
||||||
@@ -37,7 +37,7 @@ class Pages extends LitElement {
|
|||||||
flex: Index | null = null;
|
flex: Index | null = null;
|
||||||
|
|
||||||
@property({ attribute: false })
|
@property({ attribute: false })
|
||||||
textPages: URLResource[] | null = null;
|
textPages: Page[] | null = null;
|
||||||
|
|
||||||
@property()
|
@property()
|
||||||
newQuery: string | null = null;
|
newQuery: string | null = null;
|
||||||
@@ -94,7 +94,7 @@ class Pages extends LitElement {
|
|||||||
toDeletePages: Set<number> | number[] | null = null;
|
toDeletePages: Set<number> | number[] | null = null;
|
||||||
|
|
||||||
@property({ type: Object })
|
@property({ type: Object })
|
||||||
toDeletePage: URLResource | null = null;
|
toDeletePage: Page | null = null;
|
||||||
|
|
||||||
@property({ type: Object })
|
@property({ type: Object })
|
||||||
collInfo: ItemType | Record<string, never> | null = null;
|
collInfo: ItemType | Record<string, never> | null = null;
|
||||||
@@ -181,8 +181,7 @@ class Pages extends LitElement {
|
|||||||
this.sortKey = "date";
|
this.sortKey = "date";
|
||||||
this.sortDesc = true;
|
this.sortDesc = true;
|
||||||
}
|
}
|
||||||
const sorter =
|
const sorter = this.renderRoot.querySelector<Sorter<Page>>("wr-sorter");
|
||||||
this.renderRoot.querySelector<Sorter<URLResource>>("wr-sorter");
|
|
||||||
if (sorter) {
|
if (sorter) {
|
||||||
sorter.sortKey = this.sortKey;
|
sorter.sortKey = this.sortKey;
|
||||||
sorter.sortDesc = this.sortDesc;
|
sorter.sortDesc = this.sortDesc;
|
||||||
@@ -236,7 +235,9 @@ class Pages extends LitElement {
|
|||||||
|
|
||||||
if (this.dynamicPagesQuery) {
|
if (this.dynamicPagesQuery) {
|
||||||
if (!this.query) {
|
if (!this.query) {
|
||||||
const seedPages = this.collInfo!.pages.filter((x) => x.isSeed);
|
const seedPages = this.collInfo!.pages.filter(
|
||||||
|
(x) => x.isSeed || x.seed,
|
||||||
|
);
|
||||||
this.filteredPages =
|
this.filteredPages =
|
||||||
this.showAllPages || !seedPages.length
|
this.showAllPages || !seedPages.length
|
||||||
? [...this.collInfo!.pages]
|
? [...this.collInfo!.pages]
|
||||||
@@ -256,9 +257,6 @@ class Pages extends LitElement {
|
|||||||
// normalize the date
|
// normalize the date
|
||||||
for (const page of this.filteredPages) {
|
for (const page of this.filteredPages) {
|
||||||
const { timestamp, date } = getPageDateTS(page);
|
const { timestamp, date } = getPageDateTS(page);
|
||||||
if (date == null) {
|
|
||||||
throw new Error("Page date is null");
|
|
||||||
}
|
|
||||||
page.timestamp = timestamp;
|
page.timestamp = timestamp;
|
||||||
page.date = date;
|
page.date = date;
|
||||||
}
|
}
|
||||||
@@ -292,7 +290,7 @@ class Pages extends LitElement {
|
|||||||
const knownPages = new Set();
|
const knownPages = new Set();
|
||||||
this.filteredPages.forEach((x) => knownPages.add(x.id));
|
this.filteredPages.forEach((x) => knownPages.add(x.id));
|
||||||
|
|
||||||
const newPages = [];
|
const newPages: Page[] = [];
|
||||||
|
|
||||||
for (const {
|
for (const {
|
||||||
id,
|
id,
|
||||||
@@ -304,6 +302,7 @@ class Pages extends LitElement {
|
|||||||
favIconUrl,
|
favIconUrl,
|
||||||
waczhash,
|
waczhash,
|
||||||
isSeed,
|
isSeed,
|
||||||
|
size,
|
||||||
} of json.pages) {
|
} of json.pages) {
|
||||||
if (knownPages.has(id)) {
|
if (knownPages.has(id)) {
|
||||||
continue;
|
continue;
|
||||||
@@ -313,16 +312,20 @@ class Pages extends LitElement {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
let tsActual;
|
let tsActual: number;
|
||||||
|
let date: Date;
|
||||||
|
|
||||||
if (typeof ts === "string") {
|
if (typeof ts === "string") {
|
||||||
|
date = new Date(ts);
|
||||||
tsActual = new Date(ts).getTime();
|
tsActual = new Date(ts).getTime();
|
||||||
} else {
|
} else {
|
||||||
tsActual = ts;
|
tsActual = ts;
|
||||||
|
date = new Date(tsActual);
|
||||||
}
|
}
|
||||||
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
const timestamp = getTS(date.toISOString());
|
||||||
const newPage: any = {
|
|
||||||
|
const newPage: Page = {
|
||||||
id,
|
id,
|
||||||
url,
|
url,
|
||||||
title,
|
title,
|
||||||
@@ -331,6 +334,9 @@ class Pages extends LitElement {
|
|||||||
ts: tsActual,
|
ts: tsActual,
|
||||||
favIconUrl,
|
favIconUrl,
|
||||||
waczhash,
|
waczhash,
|
||||||
|
timestamp,
|
||||||
|
size,
|
||||||
|
date,
|
||||||
};
|
};
|
||||||
|
|
||||||
newPages.push(newPage);
|
newPages.push(newPage);
|
||||||
|
|||||||
+20
-6
@@ -1,4 +1,4 @@
|
|||||||
import type { URLResource } from "./types";
|
import type { Page } from "./types";
|
||||||
|
|
||||||
// ===========================================================================
|
// ===========================================================================
|
||||||
async function digestMessage(message: string, hashtype: AlgorithmIdentifier) {
|
async function digestMessage(message: string, hashtype: AlgorithmIdentifier) {
|
||||||
@@ -50,16 +50,17 @@ function getDateFromTS(ts: string | number) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// ===========================================================================
|
// ===========================================================================
|
||||||
function getPageDateTS(page: URLResource) {
|
function getPageDateTS(page: Page) {
|
||||||
let date: Date | null = null;
|
let date: Date | null = null;
|
||||||
try {
|
try {
|
||||||
date = new Date(page.ts || page.date);
|
if (page.ts > 0) {
|
||||||
|
date = new Date(page.ts);
|
||||||
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
// leave date unchanged in case of error
|
// ignore
|
||||||
}
|
}
|
||||||
|
|
||||||
const timestamp =
|
const timestamp = date ? getTS(date.toISOString()) : "";
|
||||||
date && date instanceof Date ? getTS(date.toISOString()) : "";
|
|
||||||
return { date, timestamp };
|
return { date, timestamp };
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -85,6 +86,18 @@ function getReplayLink(
|
|||||||
return "#" + params.toString();
|
return "#" + params.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ===========================================================================
|
||||||
|
function getDownloadLink(
|
||||||
|
replayPrefix: string,
|
||||||
|
url: string,
|
||||||
|
ts: string,
|
||||||
|
waczhash?: string,
|
||||||
|
) {
|
||||||
|
return `${replayPrefix}/${waczhash ? `:${waczhash}/` : ""}${
|
||||||
|
ts || ""
|
||||||
|
}dl_/${url}`;
|
||||||
|
}
|
||||||
|
|
||||||
// ===========================================================================
|
// ===========================================================================
|
||||||
async function sourceToId(url: string) {
|
async function sourceToId(url: string) {
|
||||||
try {
|
try {
|
||||||
@@ -137,6 +150,7 @@ export {
|
|||||||
getPageDateTS,
|
getPageDateTS,
|
||||||
getDateFromTS,
|
getDateFromTS,
|
||||||
getReplayLink,
|
getReplayLink,
|
||||||
|
getDownloadLink,
|
||||||
sourceToId,
|
sourceToId,
|
||||||
parseURLSchemeHostPath,
|
parseURLSchemeHostPath,
|
||||||
};
|
};
|
||||||
|
|||||||
+282
-6
@@ -1,9 +1,10 @@
|
|||||||
import { LitElement, html, css, type PropertyValues } from "lit";
|
import { LitElement, html, css, type PropertyValues } from "lit";
|
||||||
|
import { ifDefined } from "lit/directives/if-defined.js";
|
||||||
import { property } from "lit/decorators.js";
|
import { property } from "lit/decorators.js";
|
||||||
|
|
||||||
import { wrapCss } from "./misc";
|
import { wrapCss } from "./misc";
|
||||||
import rwpLogo from "~assets/brand/replaywebpage-icon-color.svg";
|
import rwpLogo from "~assets/brand/replaywebpage-icon-color.svg";
|
||||||
import type { ItemType } from "./types";
|
import type { ItemType, URLTsChange } from "./types";
|
||||||
|
|
||||||
// ===========================================================================
|
// ===========================================================================
|
||||||
class Replay extends LitElement {
|
class Replay extends LitElement {
|
||||||
@@ -49,10 +50,15 @@ class Replay extends LitElement {
|
|||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- requestPermission() type mismatch
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- requestPermission() type mismatch
|
||||||
authFileHandle: any = null;
|
authFileHandle: any = null;
|
||||||
|
|
||||||
|
@property({ type: String })
|
||||||
|
downloadResUrl = "";
|
||||||
|
|
||||||
private reauthWait: null | Promise<void> = null;
|
private reauthWait: null | Promise<void> = null;
|
||||||
|
|
||||||
private _loadPoll: null | number = null;
|
private _loadPoll: null | number = null;
|
||||||
|
|
||||||
|
private hiliter: HoverHiliter | null = null;
|
||||||
|
|
||||||
firstUpdated() {
|
firstUpdated() {
|
||||||
window.addEventListener("message", (event) => this.onReplayMessage(event));
|
window.addEventListener("message", (event) => this.onReplayMessage(event));
|
||||||
// TODO: Fix this the next time the file is edited.
|
// TODO: Fix this the next time the file is edited.
|
||||||
@@ -143,9 +149,10 @@ class Replay extends LitElement {
|
|||||||
(this.replayUrl && changedProperties.has("replayUrl")) ||
|
(this.replayUrl && changedProperties.has("replayUrl")) ||
|
||||||
(this.replayTS && changedProperties.has("replayTS"))
|
(this.replayTS && changedProperties.has("replayTS"))
|
||||||
) {
|
) {
|
||||||
const data = {
|
const data: URLTsChange = {
|
||||||
url: this.replayUrl,
|
url: this.replayUrl,
|
||||||
ts: this.replayTS,
|
ts: this.replayTS,
|
||||||
|
waczhash: this.waczhash,
|
||||||
};
|
};
|
||||||
|
|
||||||
this.dispatchEvent(
|
this.dispatchEvent(
|
||||||
@@ -203,7 +210,7 @@ class Replay extends LitElement {
|
|||||||
this.replayUrl = event.data.url;
|
this.replayUrl = event.data.url;
|
||||||
this.title = event.data.title || this.title;
|
this.title = event.data.title || this.title;
|
||||||
this.replayNotFoundError = event.data.wb_type === "archive-not-found";
|
this.replayNotFoundError = event.data.wb_type === "archive-not-found";
|
||||||
this.clearLoading(iframe.contentWindow);
|
this.clearLoading(iframe);
|
||||||
|
|
||||||
if (event.data.icons) {
|
if (event.data.icons) {
|
||||||
const icons = event.data.icons;
|
const icons = event.data.icons;
|
||||||
@@ -265,13 +272,12 @@ class Replay extends LitElement {
|
|||||||
(iframe.contentDocument.readyState === "complete" &&
|
(iframe.contentDocument.readyState === "complete" &&
|
||||||
!(iframe.contentWindow as Window & { _WBWombat: unknown })._WBWombat)
|
!(iframe.contentWindow as Window & { _WBWombat: unknown })._WBWombat)
|
||||||
) {
|
) {
|
||||||
this.clearLoading(iframe?.contentWindow);
|
this.clearLoading(iframe);
|
||||||
}
|
}
|
||||||
}, 5000);
|
}, 5000);
|
||||||
}
|
}
|
||||||
|
|
||||||
// @ts-expect-error [// TODO: Fix this the next time the file is edited.] - TS7006 - Parameter 'iframeWin' implicitly has an 'any' type.
|
clearLoading(iframe: HTMLIFrameElement | null) {
|
||||||
clearLoading(iframeWin) {
|
|
||||||
this.dispatchEvent(
|
this.dispatchEvent(
|
||||||
new CustomEvent("replay-loading", { detail: { loading: false } }),
|
new CustomEvent("replay-loading", { detail: { loading: false } }),
|
||||||
);
|
);
|
||||||
@@ -281,6 +287,8 @@ class Replay extends LitElement {
|
|||||||
this._loadPoll = null;
|
this._loadPoll = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const iframeWin = iframe?.contentWindow;
|
||||||
|
|
||||||
if (iframeWin) {
|
if (iframeWin) {
|
||||||
try {
|
try {
|
||||||
iframeWin.addEventListener("beforeunload", () => {
|
iframeWin.addEventListener("beforeunload", () => {
|
||||||
@@ -293,6 +301,7 @@ class Replay extends LitElement {
|
|||||||
}
|
}
|
||||||
|
|
||||||
setLoading() {
|
setLoading() {
|
||||||
|
this.clearHilite(true);
|
||||||
this.dispatchEvent(
|
this.dispatchEvent(
|
||||||
new CustomEvent("replay-loading", { detail: { loading: true } }),
|
new CustomEvent("replay-loading", { detail: { loading: true } }),
|
||||||
);
|
);
|
||||||
@@ -367,6 +376,15 @@ class Replay extends LitElement {
|
|||||||
margin: 3em;
|
margin: 3em;
|
||||||
background-color: white;
|
background-color: white;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.hilite-overlay {
|
||||||
|
display: none;
|
||||||
|
position: absolute;
|
||||||
|
z-index: 9999;
|
||||||
|
background-color: rgba(0, 0, 255, 0.5);
|
||||||
|
border: solid 10px blue;
|
||||||
|
cursor: crosshair;
|
||||||
|
}
|
||||||
`);
|
`);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -388,6 +406,16 @@ class Replay extends LitElement {
|
|||||||
</div>
|
</div>
|
||||||
</div>`
|
</div>`
|
||||||
: html`
|
: html`
|
||||||
|
<a
|
||||||
|
href="${this.downloadResUrl}"
|
||||||
|
@click="${this.hiliteClicked}"
|
||||||
|
class="hilite-overlay"
|
||||||
|
download="${ifDefined(
|
||||||
|
this.downloadResUrl.startsWith("blob:")
|
||||||
|
? "image.svg"
|
||||||
|
: undefined,
|
||||||
|
)}"
|
||||||
|
></a>
|
||||||
<div class="iframe-container">
|
<div class="iframe-container">
|
||||||
<iframe
|
<iframe
|
||||||
class="iframe-main"
|
class="iframe-main"
|
||||||
@@ -444,6 +472,254 @@ class Replay extends LitElement {
|
|||||||
</div>
|
</div>
|
||||||
`}`;
|
`}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
clearHilite(removeListeners = false) {
|
||||||
|
if (this.hiliter) {
|
||||||
|
this.hiliter.clearHilite(removeListeners);
|
||||||
|
this.hiliter = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (removeListeners) {
|
||||||
|
this.removeEventListener(
|
||||||
|
"update-download-res-url",
|
||||||
|
this.onUpdateDownloadResUrl,
|
||||||
|
);
|
||||||
|
this.dispatchEvent(new CustomEvent("cancel-click-download"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
hiliteClicked() {
|
||||||
|
this.clearHilite(true);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
onUpdateDownloadResUrl(e: Event) {
|
||||||
|
const { url } = (e as CustomEvent).detail;
|
||||||
|
this.downloadResUrl = url;
|
||||||
|
}
|
||||||
|
|
||||||
|
setClickToDownload() {
|
||||||
|
if (this.hiliter) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.addEventListener(
|
||||||
|
"update-download-res-url",
|
||||||
|
this.onUpdateDownloadResUrl,
|
||||||
|
);
|
||||||
|
|
||||||
|
try {
|
||||||
|
this.hiliter = new HoverHiliter(this);
|
||||||
|
} catch (e) {
|
||||||
|
this.hiliter = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ===========================================================================
|
||||||
|
class HoverHiliter {
|
||||||
|
doc: Document;
|
||||||
|
replay: Replay;
|
||||||
|
|
||||||
|
hiliteElem: HTMLElement | null = null;
|
||||||
|
hiliteOverlay: HTMLElement;
|
||||||
|
iframe: HTMLIFrameElement;
|
||||||
|
|
||||||
|
onMove: (event: MouseEvent) => void;
|
||||||
|
onRecompute: () => void;
|
||||||
|
|
||||||
|
constructor(replay: Replay) {
|
||||||
|
this.replay = replay;
|
||||||
|
|
||||||
|
const iframe = this.replay.renderRoot.querySelector("iframe");
|
||||||
|
const hiliteOverlay =
|
||||||
|
this.replay.renderRoot.querySelector(".hilite-overlay");
|
||||||
|
|
||||||
|
if (!hiliteOverlay || !iframe?.contentDocument) {
|
||||||
|
throw new Error("missing elements");
|
||||||
|
}
|
||||||
|
|
||||||
|
this.iframe = iframe;
|
||||||
|
this.hiliteOverlay = hiliteOverlay as HTMLElement;
|
||||||
|
|
||||||
|
this.onMove = (event: MouseEvent) => this.hiliteOnMove(event);
|
||||||
|
this.onRecompute = () => this.hiliteRecompute();
|
||||||
|
|
||||||
|
const doc = iframe.contentDocument;
|
||||||
|
|
||||||
|
this.doc = doc;
|
||||||
|
|
||||||
|
doc.addEventListener("mousemove", this.onMove);
|
||||||
|
|
||||||
|
doc.addEventListener("scroll", this.onRecompute);
|
||||||
|
|
||||||
|
doc.defaultView?.addEventListener("resize", this.onRecompute);
|
||||||
|
}
|
||||||
|
|
||||||
|
clearHilite(removeListeners = false) {
|
||||||
|
if (removeListeners) {
|
||||||
|
this.doc.removeEventListener("mousemove", this.onMove);
|
||||||
|
|
||||||
|
this.doc.removeEventListener("scroll", this.onRecompute);
|
||||||
|
|
||||||
|
this.doc.defaultView?.removeEventListener("scroll", this.onRecompute);
|
||||||
|
}
|
||||||
|
this.hiliteElem = null;
|
||||||
|
|
||||||
|
this.hiliteOverlay.style.display = "none";
|
||||||
|
}
|
||||||
|
|
||||||
|
hiliteRecompute() {
|
||||||
|
if (!this.hiliteElem) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const iframeRect = this.iframe.getBoundingClientRect();
|
||||||
|
const elemRect = this.hiliteElem.getBoundingClientRect();
|
||||||
|
|
||||||
|
const offset = 10;
|
||||||
|
|
||||||
|
const leftX = iframeRect.left + window.scrollX + elemRect.left - offset;
|
||||||
|
const topY = iframeRect.top + window.scrollY + elemRect.top - offset;
|
||||||
|
|
||||||
|
const hilite = this.hiliteOverlay;
|
||||||
|
|
||||||
|
hilite.style.left = leftX + "px";
|
||||||
|
hilite.style.top = topY + "px";
|
||||||
|
hilite.style.width = elemRect.width + offset * 2 + "px";
|
||||||
|
hilite.style.height = elemRect.height + offset * 2 + "px";
|
||||||
|
hilite.style.display = "block";
|
||||||
|
}
|
||||||
|
|
||||||
|
hiliteOnMove(event: MouseEvent) {
|
||||||
|
const elem = this.hiliteFindBestElement(event.clientX, event.clientY);
|
||||||
|
|
||||||
|
if (elem && this.hiliteElem === elem) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const getSrc = (elem: HTMLElement | null): string => {
|
||||||
|
if (!elem) {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((elem as HTMLImageElement).currentSrc) {
|
||||||
|
return (elem as HTMLImageElement).currentSrc;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (elem.tagName === "image") {
|
||||||
|
const href = elem.getAttribute("href");
|
||||||
|
if (href) {
|
||||||
|
return href;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (elem.tagName === "svg") {
|
||||||
|
if (!elem.getAttribute("xmlns")) {
|
||||||
|
elem.setAttribute("xmlns", "http://www.w3.org/2000/svg");
|
||||||
|
}
|
||||||
|
const buff = new TextEncoder().encode(elem.outerHTML);
|
||||||
|
const blob = new Blob([buff], { type: "image/svg+xml" });
|
||||||
|
return URL.createObjectURL(blob);
|
||||||
|
}
|
||||||
|
|
||||||
|
const src = HTMLElement.prototype.getAttribute.call(elem, "src");
|
||||||
|
if (src) {
|
||||||
|
return src;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (elem.style.backgroundImage) {
|
||||||
|
return elem.style.backgroundImage.replace(
|
||||||
|
/(url\s*\(\s*[\\"']*)([^)'"]+)([\\"']*\s*\)?)/i,
|
||||||
|
"$2",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return "";
|
||||||
|
};
|
||||||
|
|
||||||
|
const src = getSrc(elem);
|
||||||
|
|
||||||
|
if (src) {
|
||||||
|
const newSrc = src.replace(
|
||||||
|
/([\d]*)([\w][\w]_)(\/(https:|http:)?\/)/,
|
||||||
|
"$1dl_$3",
|
||||||
|
);
|
||||||
|
this.replay.dispatchEvent(
|
||||||
|
new CustomEvent("update-download-res-url", { detail: { url: newSrc } }),
|
||||||
|
);
|
||||||
|
this.hiliteElem = elem;
|
||||||
|
this.hiliteRecompute();
|
||||||
|
} else {
|
||||||
|
this.clearHilite();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
hiliteFindBestElement(x: number, y: number): HTMLElement | null {
|
||||||
|
const elems = this.doc.elementsFromPoint(x, y) as HTMLElement[];
|
||||||
|
|
||||||
|
if (!elems.length) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const firstElem = elems[0];
|
||||||
|
|
||||||
|
const containsRect = (rect: DOMRect) => {
|
||||||
|
return (
|
||||||
|
rect.x >= firstRect.x &&
|
||||||
|
rect.y >= firstRect.y &&
|
||||||
|
rect.width <= firstRect.width &&
|
||||||
|
rect.height <= firstRect.height
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
// allow other elements only if they're within same bounding rect
|
||||||
|
// as the deepest element
|
||||||
|
const firstRect = firstElem.getBoundingClientRect();
|
||||||
|
let isLast = false;
|
||||||
|
for (const elem of elems) {
|
||||||
|
const rect = elem.getBoundingClientRect();
|
||||||
|
if (elem === firstElem || containsRect(rect)) {
|
||||||
|
if (
|
||||||
|
(elem as HTMLImageElement).currentSrc ||
|
||||||
|
(elem.hasAttribute("src") && !(elem instanceof HTMLIFrameElement))
|
||||||
|
) {
|
||||||
|
return elem;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (elem.tagName === "image" && elem.hasAttribute("href")) {
|
||||||
|
return elem;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (elem.tagName === "svg") {
|
||||||
|
return elem;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
isLast = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
const subelem =
|
||||||
|
elem.querySelector("[src]:not(script)") ||
|
||||||
|
elem.querySelector(`div[style*="background-image: url("]`);
|
||||||
|
if (subelem) {
|
||||||
|
const subRect = subelem.getBoundingClientRect();
|
||||||
|
if (
|
||||||
|
containsRect(subRect) &&
|
||||||
|
x >= subRect.left &&
|
||||||
|
y >= subRect.top &&
|
||||||
|
x < subRect.right &&
|
||||||
|
y <= subRect.bottom
|
||||||
|
) {
|
||||||
|
return subelem as HTMLElement | null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isLast) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
customElements.define("wr-coll-replay", Replay);
|
customElements.define("wr-coll-replay", Replay);
|
||||||
|
|||||||
+26
-12
@@ -1,21 +1,35 @@
|
|||||||
export type URLResource = {
|
export type URLResource = {
|
||||||
id: number;
|
|
||||||
title: string;
|
|
||||||
url: string;
|
|
||||||
mime: string;
|
mime: string;
|
||||||
status: string;
|
status: string;
|
||||||
date: Date;
|
date: Date;
|
||||||
ts: string;
|
ts: string;
|
||||||
desc?: string;
|
url: string;
|
||||||
timestamp?: string;
|
|
||||||
size: number;
|
|
||||||
favIconUrl: string;
|
|
||||||
text?: string;
|
|
||||||
waczhash?: string;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export type Page = URLResource & {
|
export type Page = {
|
||||||
|
id: number;
|
||||||
|
title: string;
|
||||||
|
text?: string;
|
||||||
|
url: string;
|
||||||
|
mime: string;
|
||||||
|
status: string;
|
||||||
|
size: number;
|
||||||
|
|
||||||
|
ts: number;
|
||||||
|
timestamp: string;
|
||||||
|
date: Date | null;
|
||||||
|
|
||||||
|
favIconUrl?: string;
|
||||||
|
waczhash?: string;
|
||||||
|
seed?: boolean;
|
||||||
isSeed?: boolean;
|
isSeed?: boolean;
|
||||||
|
desc?: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type URLTsChange = {
|
||||||
|
url: string;
|
||||||
|
ts: string;
|
||||||
|
waczhash?: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type ItemType = {
|
export type ItemType = {
|
||||||
@@ -48,8 +62,8 @@ export type ItemType = {
|
|||||||
};
|
};
|
||||||
onDemand?: boolean;
|
onDemand?: boolean;
|
||||||
pages: Page[];
|
pages: Page[];
|
||||||
curatedPages: (URLResource & { list: string })[];
|
curatedPages: (Page & { list: string })[];
|
||||||
lists: URLResource[];
|
lists: Page[];
|
||||||
ctime?: string;
|
ctime?: string;
|
||||||
totalSize?: unknown;
|
totalSize?: unknown;
|
||||||
size?: number | string;
|
size?: number | string;
|
||||||
|
|||||||
+20
-1
@@ -1,9 +1,10 @@
|
|||||||
import { LitElement, html, css, type PropertyValues } from "lit";
|
import { LitElement, html, css, type PropertyValues } from "lit";
|
||||||
import { wrapCss, clickOnSpacebarPress } from "./misc";
|
import { wrapCss, clickOnSpacebarPress } from "./misc";
|
||||||
|
|
||||||
import { getReplayLink } from "./pageutils";
|
import { getReplayLink, getDownloadLink } from "./pageutils";
|
||||||
|
|
||||||
import fasSearch from "@fortawesome/fontawesome-free/svgs/solid/search.svg";
|
import fasSearch from "@fortawesome/fontawesome-free/svgs/solid/search.svg";
|
||||||
|
import fasDownload from "@fortawesome/fontawesome-free/svgs/solid/download.svg";
|
||||||
|
|
||||||
import "keyword-mark-element/lib/keyword-mark.js";
|
import "keyword-mark-element/lib/keyword-mark.js";
|
||||||
import { type ItemType } from "./types";
|
import { type ItemType } from "./types";
|
||||||
@@ -315,6 +316,9 @@ class URLResources extends LitElement {
|
|||||||
.all-results .column {
|
.all-results .column {
|
||||||
word-break: break-word;
|
word-break: break-word;
|
||||||
}
|
}
|
||||||
|
.dl-button {
|
||||||
|
padding: 4px;
|
||||||
|
}
|
||||||
div.sort-header {
|
div.sort-header {
|
||||||
padding: 10px;
|
padding: 10px;
|
||||||
margin-bottom: 0px !important;
|
margin-bottom: 0px !important;
|
||||||
@@ -528,6 +532,21 @@ class URLResources extends LitElement {
|
|||||||
<tr class="columns result">
|
<tr class="columns result">
|
||||||
<td class="column col-url is-6">
|
<td class="column col-url is-6">
|
||||||
<p class="minihead is-hidden-tablet">URL</p>
|
<p class="minihead is-hidden-tablet">URL</p>
|
||||||
|
<a
|
||||||
|
class="dl-button"
|
||||||
|
href="${getDownloadLink(
|
||||||
|
this.collInfo!.replayPrefix,
|
||||||
|
result.url,
|
||||||
|
result.ts,
|
||||||
|
)}"
|
||||||
|
><fa-icon
|
||||||
|
size="1.0em"
|
||||||
|
class="has-text-black"
|
||||||
|
aria-hidden="true"
|
||||||
|
title="Download Resource"
|
||||||
|
.svg="${fasDownload}"
|
||||||
|
></fa-icon>
|
||||||
|
</a>
|
||||||
<a
|
<a
|
||||||
@click="${this.onReplay}"
|
@click="${this.onReplay}"
|
||||||
data-url="${result.url}"
|
data-url="${result.url}"
|
||||||
|
|||||||
@@ -1075,17 +1075,17 @@
|
|||||||
resolved "https://registry.yarnpkg.com/@webpack-cli/serve/-/serve-2.0.5.tgz#325db42395cd49fe6c14057f9a900e427df8810e"
|
resolved "https://registry.yarnpkg.com/@webpack-cli/serve/-/serve-2.0.5.tgz#325db42395cd49fe6c14057f9a900e427df8810e"
|
||||||
integrity sha512-lqaoKnRYBdo1UgDX8uF24AfGMifWK19TxPmM5FHc2vAGxrJ/qtyUyFBWoY1tISZdelsQ5fBcOusifo5o5wSJxQ==
|
integrity sha512-lqaoKnRYBdo1UgDX8uF24AfGMifWK19TxPmM5FHc2vAGxrJ/qtyUyFBWoY1tISZdelsQ5fBcOusifo5o5wSJxQ==
|
||||||
|
|
||||||
"@webrecorder/wabac@^2.24.5":
|
"@webrecorder/wabac@^2.25.1":
|
||||||
version "2.24.5"
|
version "2.25.1"
|
||||||
resolved "https://registry.yarnpkg.com/@webrecorder/wabac/-/wabac-2.24.5.tgz#253b0d5a9b7691af475c7a158c6483df33f2d3ff"
|
resolved "https://registry.yarnpkg.com/@webrecorder/wabac/-/wabac-2.25.1.tgz#aaa99727bb2ef80746bb74ebc8d5148e03917ee9"
|
||||||
integrity sha512-Wt9bdZMYrl+DYRkUdRfYAPSUfygHBQGIiZ9oMZ/DRLIeqsqeN36UBGuvbj6kmUawLNWRYVQj6kNl/ndtRZSa6w==
|
integrity sha512-5MvG7DTfMHYzpfFfSx/z5SDVEVTVSeyx6Nlzjt+T7nJjxh7hGmrKss86hfXorS0BaeUELrtuPL/N3yGPfCnJXw==
|
||||||
dependencies:
|
dependencies:
|
||||||
"@peculiar/asn1-ecc" "^2.3.4"
|
"@peculiar/asn1-ecc" "^2.3.4"
|
||||||
"@peculiar/asn1-schema" "^2.3.3"
|
"@peculiar/asn1-schema" "^2.3.3"
|
||||||
"@peculiar/x509" "^1.9.2"
|
"@peculiar/x509" "^1.9.2"
|
||||||
"@types/js-levenshtein" "^1.1.3"
|
"@types/js-levenshtein" "^1.1.3"
|
||||||
"@webrecorder/wombat" "^3.10.1"
|
"@webrecorder/wombat" "^3.10.2"
|
||||||
acorn-loose "^8.5.2"
|
acorn "^8.15.0"
|
||||||
auto-js-ipfs "^2.1.1"
|
auto-js-ipfs "^2.1.1"
|
||||||
base64-js "^1.5.1"
|
base64-js "^1.5.1"
|
||||||
brotli "^1.3.3"
|
brotli "^1.3.3"
|
||||||
@@ -1095,7 +1095,8 @@
|
|||||||
http-status-codes "^2.1.4"
|
http-status-codes "^2.1.4"
|
||||||
idb "^7.1.1"
|
idb "^7.1.1"
|
||||||
js-levenshtein "^1.1.6"
|
js-levenshtein "^1.1.6"
|
||||||
js-yaml "^4.1.0"
|
js-yaml "^4.1.1"
|
||||||
|
mime "^4.1.0"
|
||||||
pako "^1.0.11"
|
pako "^1.0.11"
|
||||||
parse5-html-rewriting-stream "^7.0.0"
|
parse5-html-rewriting-stream "^7.0.0"
|
||||||
parse5-sax-parser "^7.0.0"
|
parse5-sax-parser "^7.0.0"
|
||||||
@@ -1104,10 +1105,10 @@
|
|||||||
stream-browserify "^3.0.0"
|
stream-browserify "^3.0.0"
|
||||||
warcio "^2.4.7"
|
warcio "^2.4.7"
|
||||||
|
|
||||||
"@webrecorder/wombat@^3.10.1":
|
"@webrecorder/wombat@^3.10.2":
|
||||||
version "3.10.1"
|
version "3.10.2"
|
||||||
resolved "https://registry.yarnpkg.com/@webrecorder/wombat/-/wombat-3.10.1.tgz#d45417f9ef61c9f02357bacf1ba930f6e09dd7fa"
|
resolved "https://registry.yarnpkg.com/@webrecorder/wombat/-/wombat-3.10.2.tgz#0d8e4057be66e4b67a522b605f8be907f912d5e0"
|
||||||
integrity sha512-OzYrv3iqC1AdgjG+QD/tNQi6+yv+1izzr2PoN+40AvleL+RLsIk/AwOmdYJpz9r+L7UIOdRrK4v/JrUWPwUDzw==
|
integrity sha512-01vr0mf+ecggorX6nyxTyf8gYrFichu1UE8RQi71Ckz8DAf+mxym96ozecq9bcS80+4Bkb7vFLAGyb4h6ZRXAw==
|
||||||
dependencies:
|
dependencies:
|
||||||
warcio "^2.4.7"
|
warcio "^2.4.7"
|
||||||
|
|
||||||
@@ -1162,13 +1163,6 @@ acorn-jsx@^5.3.2:
|
|||||||
resolved "https://registry.yarnpkg.com/acorn-jsx/-/acorn-jsx-5.3.2.tgz#7ed5bb55908b3b2f1bc55c6af1653bada7f07937"
|
resolved "https://registry.yarnpkg.com/acorn-jsx/-/acorn-jsx-5.3.2.tgz#7ed5bb55908b3b2f1bc55c6af1653bada7f07937"
|
||||||
integrity sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==
|
integrity sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==
|
||||||
|
|
||||||
acorn-loose@^8.5.2:
|
|
||||||
version "8.5.2"
|
|
||||||
resolved "https://registry.yarnpkg.com/acorn-loose/-/acorn-loose-8.5.2.tgz#a7cc7dfbb7c8f3c2e55b055db640dc657e278d26"
|
|
||||||
integrity sha512-PPvV6g8UGMGgjrMu+n/f9E/tCSkNQ2Y97eFvuVdJfG11+xdIeDcLyNdC8SHcrHbRqkfwLASdplyR6B6sKM1U4A==
|
|
||||||
dependencies:
|
|
||||||
acorn "^8.15.0"
|
|
||||||
|
|
||||||
acorn@^8.15.0:
|
acorn@^8.15.0:
|
||||||
version "8.15.0"
|
version "8.15.0"
|
||||||
resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.15.0.tgz#a360898bc415edaac46c8241f6383975b930b816"
|
resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.15.0.tgz#a360898bc415edaac46c8241f6383975b930b816"
|
||||||
@@ -3782,6 +3776,13 @@ js-yaml@^4.1.0:
|
|||||||
dependencies:
|
dependencies:
|
||||||
argparse "^2.0.1"
|
argparse "^2.0.1"
|
||||||
|
|
||||||
|
js-yaml@^4.1.1:
|
||||||
|
version "4.1.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-4.1.1.tgz#854c292467705b699476e1a2decc0c8a3458806b"
|
||||||
|
integrity sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==
|
||||||
|
dependencies:
|
||||||
|
argparse "^2.0.1"
|
||||||
|
|
||||||
jsbn@1.1.0:
|
jsbn@1.1.0:
|
||||||
version "1.1.0"
|
version "1.1.0"
|
||||||
resolved "https://registry.yarnpkg.com/jsbn/-/jsbn-1.1.0.tgz#b01307cb29b618a1ed26ec79e911f803c4da0040"
|
resolved "https://registry.yarnpkg.com/jsbn/-/jsbn-1.1.0.tgz#b01307cb29b618a1ed26ec79e911f803c4da0040"
|
||||||
@@ -4389,6 +4390,11 @@ mime@^2.5.2:
|
|||||||
resolved "https://registry.yarnpkg.com/mime/-/mime-2.5.2.tgz#6e3dc6cc2b9510643830e5f19d5cb753da5eeabe"
|
resolved "https://registry.yarnpkg.com/mime/-/mime-2.5.2.tgz#6e3dc6cc2b9510643830e5f19d5cb753da5eeabe"
|
||||||
integrity sha512-tqkh47FzKeCPD2PUiPB6pkbMzsCasjxAfC62/Wap5qrUWcb+sFasXUC5I3gYM5iBM8v/Qpn4UK0x+j0iHyFPDg==
|
integrity sha512-tqkh47FzKeCPD2PUiPB6pkbMzsCasjxAfC62/Wap5qrUWcb+sFasXUC5I3gYM5iBM8v/Qpn4UK0x+j0iHyFPDg==
|
||||||
|
|
||||||
|
mime@^4.1.0:
|
||||||
|
version "4.1.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/mime/-/mime-4.1.0.tgz#ec55df7aa21832a36d44f0bbee5c04639b27802f"
|
||||||
|
integrity sha512-X5ju04+cAzsojXKes0B/S4tcYtFAJ6tTMuSPBEn9CPGlrWr8Fiw7qYeLT0XyH80HSoAoqWCaz+MWKh22P7G1cw==
|
||||||
|
|
||||||
mimic-fn@^2.1.0:
|
mimic-fn@^2.1.0:
|
||||||
version "2.1.0"
|
version "2.1.0"
|
||||||
resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-2.1.0.tgz#7ed2c2ccccaf84d3ffcb7a69b57711fc2083401b"
|
resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-2.1.0.tgz#7ed2c2ccccaf84d3ffcb7a69b57711fc2083401b"
|
||||||
|
|||||||
Reference in New Issue
Block a user