import { html, css, wrapCss, IS_APP, apiPrefix } from "replaywebpage"; // replaywebpage imports import { ReplayWebApp, Embed, Loader } from "replaywebpage"; import { SWManager } from "replaywebpage"; import fasHelp from "@fortawesome/fontawesome-free/svgs/solid/question-circle.svg"; import fasPlus from "@fortawesome/fontawesome-free/svgs/solid/plus.svg"; import fasUpload from "@fortawesome/fontawesome-free/svgs/solid/upload.svg"; import fasCog from "@fortawesome/fontawesome-free/svgs/solid/cog.svg"; import "./coll"; import "./coll-info"; import "./recordembed"; import "./coll-index"; import { BtrixClient } from "./upload"; import wrRec from "../assets/icons/recLogo.svg"; import awpLogo from "../assets/brand/archivewebpage-icon-color.svg"; import awpBrandLockupColor from "../assets/brand/archivewebpage-lockup-color.svg"; import prettyBytes from "pretty-bytes"; import { create as createAutoIpfs, DaemonAPI, Web3StorageAPI, // @ts-expect-error - TS7016 - Could not find a declaration file for module 'auto-js-ipfs'. '/Users/emma/Work/Webrecorder/archiveweb.page/node_modules/auto-js-ipfs/index.js' implicitly has an 'any' type. } from "auto-js-ipfs"; import { getLocalOption, setLocalOption } from "../localstorage"; import { type BtrixOpts } from "../types"; const VERSION = __AWP_VERSION__; const DEFAULT_GATEWAY_URL = "https://w3s.link/ipfs/"; const DEFAULT_BTRIX_URL = "https://app.browsertrix.com"; //============================================================================ class ArchiveWebApp extends ReplayWebApp { showCollDrop: boolean; colls: { id: string; title?: string; loadUrl?: string }[]; autorun: boolean; settingsError: string; settingsTab: string; ipfsOpts: { daemonUrl: string; message?: string; useCustom: boolean; autoDetect: boolean; gatewayUrl: string; }; btrixOpts: BtrixOpts | null; loadedCollId?: string | null; showImport?: boolean; archiveCookies: boolean | null = null; archiveStorage: boolean | null = null; archiveFlash: boolean | null = null; archiveScreenshots: boolean | null = null; archivePDF: boolean | null = null; disableMSE: boolean | null = null; disablePerf: boolean | null = null; showIpfsShareFailed = false; showSettings = false; constructor() { super(); this.navMenuShown = false; this.showCollDrop = false; this.colls = []; this.autorun = false; this.settingsError = ""; this.settingsTab = localStorage.getItem("settingsTab") || "prefs"; try { const res = localStorage.getItem("ipfsOpts"); this.ipfsOpts = JSON.parse(res!); } catch (e) { // ignore empty } this.ipfsOpts ||= { daemonUrl: "", message: "", useCustom: false, autoDetect: false, gatewayUrl: DEFAULT_GATEWAY_URL, }; try { const res = localStorage.getItem("btrixOpts"); // @ts-expect-error - TS2339 - Property 'btrixOpts' does not exist on type 'ArchiveWebApp'. | TS2345 - Argument of type 'string | null' is not assignable to parameter of type 'string'. this.btrixOpts = JSON.parse(res); this.doBtrixLogin(); } catch (e) { this.btrixOpts = null; } if (window.archivewebpage) { // @ts-expect-error - TS7006 - Parameter 'progress' implicitly has an 'any' type. window.archivewebpage.setDownloadCallback((progress) => this.onDownloadProgress(progress), ); } void this.initOpts(); } async initOpts() { this.autorun = (await getLocalOption("autorunBehaviors")) === "1"; const archiveCookies = await getLocalOption("archiveCookies"); // default to true if unset to match existing behavior if (archiveCookies === null || archiveCookies === undefined) { await setLocalOption("archiveCookies", "1"); this.archiveCookies = true; } else { this.archiveCookies = archiveCookies === "1"; } this.archiveStorage = (await getLocalOption("archiveStorage")) === "1"; this.archiveFlash = (await getLocalOption("archiveFlash")) === "1"; this.disableMSE = (await getLocalOption("disableMSE")) === "1"; this.disablePerf = (await getLocalOption("disablePerf")) === "1"; const archiveScreenshots = await getLocalOption("archiveScreenshots"); // default to true if unset to enable screenshots! if (archiveScreenshots === null || archiveScreenshots === undefined) { await setLocalOption("archiveScreenshots", "1"); this.archiveScreenshots = true; } else { this.archiveScreenshots = archiveScreenshots === "1"; } this.archivePDF = (await getLocalOption("archivePDF")) === "1"; } async doBtrixLogin() { try { // @ts-expect-error - TS2531 - Object is possibly 'null'. | TS2345 - Argument of type 'BtrixOpts | null' is not assignable to parameter of type '{ url: any; username: any; password: any; orgName: any; }'. this.btrixOpts.client = await BtrixClient.login(this.btrixOpts); } catch (e) { this.btrixOpts = null; } } get appName() { return "ArchiveWeb.page"; } static get properties() { return { ...ReplayWebApp.properties, showStartRecord: { type: Boolean }, showCollDrop: { type: Boolean }, colls: { type: Array }, selCollId: { type: String }, selCollTitle: { type: String }, recordUrl: { type: String }, autorun: { type: Boolean }, showNew: { type: String }, showImport: { type: Boolean }, isImportExisting: { type: Boolean }, loadedCollId: { type: String }, showDownloadProgress: { type: Boolean }, download: { type: Object }, ipfsOpts: { type: Object }, btrixOpts: { type: Object }, uploadCollOpts: { type: Object }, showSettings: { type: Boolean }, settingsTab: { type: String }, settingsError: { type: String }, showIpfsShareFailed: { type: Boolean }, }; } initRoute() { const pageParams = new URLSearchParams(window.location.search); if (pageParams.has("config")) { super.initRoute(); this.handleMessages(); } else { this.inited = true; this.sourceUrl = pageParams.get("source") || ""; } if (!this.embed) { this.checkIPFS(); } } async checkSW() { const regs = await navigator.serviceWorker.getRegistrations(); // Remove double SW for (const reg of regs) { if (reg.active && reg.active.scriptURL.endsWith("/replay/sw.js")) { if (await reg.unregister()) { self.location.reload(); } } } // For App: If no SW, register here if (IS_APP && !regs.length) { const qp = new URLSearchParams(); qp.set("injectScripts", "ruffle/ruffle.js"); this.swmanager = new SWManager({ name: this.swName + "?" + qp.toString(), appName: this.appName, }); this.swmanager .register() .catch( () => (this.swErrorMsg = this.swmanager?.renderErrorReport(this.mainLogo) || ""), ); } } firstUpdated() { this.embed = this.pageParams.get("embed") || ""; if (this.embed) { return super.firstUpdated(); } this.checkSW(); this.initRoute(); window.addEventListener("popstate", () => { this.initRoute(); }); } handleMessages() { // support upload window.addEventListener("message", async (event) => { if ( this.embed && this.loadedCollId && typeof event.data === "object" && event.data.msg_type === "downloadToBlob" ) { const download = await fetch( `${apiPrefix}/c/${this.loadedCollId}/dl?format=wacz&pages=all`, ); const blob = await download.blob(); event.source?.postMessage({ msg_type: "downloadedBlob", coll: this.loadedCollId, url: URL.createObjectURL(blob), }); } }); } // @ts-expect-error - TS7006 - Parameter 'event' implicitly has an 'any' type. onStartLoad(event) { if (this.embed) { return; } this.showImport = false; this.sourceUrl = event.detail.sourceUrl; this.loadInfo = event.detail; // @ts-expect-error - TS2339 - Property 'isImportExisting' does not exist on type 'ArchiveWebApp'. | TS2339 - Property 'selCollId' does not exist on type 'ArchiveWebApp'. if (this.isImportExisting && this.selCollId) { // @ts-expect-error - TS2339 - Property 'loadInfo' does not exist on type 'ArchiveWebApp'. | TS2339 - Property 'selCollId' does not exist on type 'ArchiveWebApp'. this.loadInfo.importCollId = this.selCollId; } } // @ts-expect-error - TS7006 - Parameter 'event' implicitly has an 'any' type. onCollLoaded(event) { if (this.loadInfo?.importCollId) { if (navigator.serviceWorker.controller) { const msg = { msg_type: "reload", full: true, name: this.loadInfo.importCollId, }; navigator.serviceWorker.controller.postMessage(msg); } } if (this.embed) { this.loadedCollId = event.detail.collInfo?.coll; } super.onCollLoaded(event); if ( !event.detail.alreadyLoaded && event.detail.sourceUrl && event.detail.sourceUrl !== this.sourceUrl ) { this.sourceUrl = event.detail.sourceUrl; } } // eslint-disable-next-line @typescript-eslint/no-explicit-any getLoadInfo(sourceUrl: string): any { this.disableCSP(); if (this.loadInfo) { return this.loadInfo; } const customColl = sourceUrl.startsWith("local://") ? sourceUrl.slice("local://".length) : sourceUrl; return { customColl }; } async disableCSP() { // necessary for chrome 94> up due to new bug introduced // // @ts-expect-error - TS2339 - Property 'embed' does not exist on type 'ArchiveWebApp'. | TS2339 - Property 'chrome' does not exist on type 'Window & typeof globalThis'. | TS2339 - Property 'chrome' does not exist on type 'Window & typeof globalThis'. if (this.embed || !self.chrome?.runtime) { return; } const m = navigator.userAgent.match(/Chrome\/([\d]+)/); if (!m || Number(m[1]) < 94) { return; } console.log("attempt to disable CSP to ensure replay works"); const tabId = await new Promise((resolve) => { // @ts-expect-error - TS7006 - Parameter 'msg' implicitly has an 'any' type. chrome.tabs.getCurrent((msg) => resolve(msg.id)); }); chrome.runtime.sendMessage({ msg: "disableCSP", tabId, }); } static get styles() { return wrapCss(ArchiveWebApp.appStyles); } static get appStyles() { return wrapCss(css` :host { font-size: initial; overflow: auto; } wr-rec-coll { height: 100%; width: 100%; } .recorder .modal-background { background-color: rgba(10, 10, 10, 0.5); } .recorder .modal-card-head { background-color: #97a1ff; } .extra-padding { padding: 1em; } .less-padding { padding-top: 1em; padding-bottom: 1em; } div.field.has-addons { flex: auto; } form { flex-grow: 1; flex-shrink: 0; margin: 0px; } .dropdown-row { display: flex; align-items: center; margin-bottom: 0.5em; } .infomsg { max-width: 300px; padding-right: 8px; } .rightbar { margin-left: auto; display: flex; } .dl-progress { display: flex; flex-direction: column; } @media screen and (max-width: 768px) { #url { border-bottom-right-radius: 4px; border-top-right-radius: 4px; } .no-pad-mobile { padding-right: 2px; } } ${ReplayWebApp.appStyles} `); } // HACK: returns the logo requested by ReplayWeb.page's nav as nothing now that the new logo includes both graphics and text. Probably best to refactor this behavior. get mainLogo() { return ""; } renderNavEnd() { return html`  User Guide About `; } renderNavBrand() { return html` `; } renderHomeIndex() { return html`
The ArchiveWeb.page ${IS_APP ? "App" : "Extension"} allows you to archive webpages directly in your browser!
(this.showIpfsShareFailed = true)} @do-upload=${ // @ts-expect-error - TS2339 - Property 'uploadCollOpts' does not exist on type 'ArchiveWebApp'. (e) => (this.uploadCollOpts = e.detail) } style="overflow: visible" > `; } render() { // @ts-expect-error - TS2551 - Property 'showStartRecord' does not exist on type 'ArchiveWebApp'. Did you mean 'onStartRecord'? return html` ${this.showStartRecord ? this.renderStartModal() : ""} ${ // @ts-expect-error - TS2339 - Property 'showNew' does not exist on type 'ArchiveWebApp'. this.showNew ? this.renderNewCollModal() : "" } ${this.showImport ? this.renderImportModal() : ""} ${ // @ts-expect-error - TS2551 - Property 'showDownloadProgress' does not exist on type 'ArchiveWebApp'. Did you mean 'onDownloadProgress'? | TS2339 - Property 'download' does not exist on type 'ArchiveWebApp'. this.showDownloadProgress && this.download ? this.renderDownloadModal() : "" } ${this.showSettings ? this.renderSettingsModal() : ""} ${this.showIpfsShareFailed ? this.renderIPFSShareFailedModal() : ""} ${ // @ts-expect-error - TS2339 - Property 'uploadCollOpts' does not exist on type 'ArchiveWebApp'. | TS2339 - Property 'btrixOpts' does not exist on type 'ArchiveWebApp'. this.uploadCollOpts && this.btrixOpts ? this.renderBtrixUploadModal() : "" } ${super.render()}`; } renderColl() { return html` (this.uploadCollOpts = e.detail) } @about-show=${() => (this.showAbout = true)} >`; } renderCollList(text = "") { return html` `; } renderStartModal() { return html` ${this.renderCollList("Save To:")}

${IS_APP ? html` ` : ""}
`; } renderNewCollModal() { return html`

`; } renderImportModal() { return html`
${ // @ts-expect-error - TS2339 - Property 'isImportExisting' does not exist on type 'ArchiveWebApp'. this.isImportExisting ? this.renderCollList() : "" }
`; } renderIPFSShareFailedModal() { return html`

Sorry, IPFS sharing / unsharing failed as IPFS could not be reached.

(Check the IPFS settings and try again.)

`; } renderBtrixUploadModal() { return html` `; } renderDownloadModal() { const renderDLStatus = () => { // @ts-expect-error - TS2339 - Property 'download' does not exist on type 'ArchiveWebApp'. switch (this.download.state) { case "progressing": return html` `; case "interrupted": return html`

The download was interrupted

`; case "cancelled": return html`

The download was canceled

`; case "completed": return html`

Download Completed!

`; } }; return html`
Downloading to: ${ // @ts-expect-error - TS2339 - Property 'download' does not exist on type 'ArchiveWebApp'. this.download.filename }
Size Downloaded: ${ // @ts-expect-error - TS2339 - Property 'download' does not exist on type 'ArchiveWebApp'. prettyBytes(this.download.currSize) }
Time Elapsed: ${ // @ts-expect-error - TS2339 - Property 'download' does not exist on type 'ArchiveWebApp'. Math.round(Date.now() / 1000 - this.download.startTime) } seconds
${renderDLStatus()}
`; } // @ts-expect-error - TS7006 - Parameter 'progress' implicitly has an 'any' type. onDownloadProgress(progress) { if (progress.filename) { // @ts-expect-error - TS2551 - Property 'showDownloadProgress' does not exist on type 'ArchiveWebApp'. Did you mean 'onDownloadProgress'? this.showDownloadProgress = true; // @ts-expect-error - TS2339 - Property 'download' does not exist on type 'ArchiveWebApp'. this.download = progress; // @ts-expect-error - TS2339 - Property 'download' does not exist on type 'ArchiveWebApp'. } else if (this.download) { // @ts-expect-error - TS2339 - Property 'download' does not exist on type 'ArchiveWebApp'. | TS2339 - Property 'download' does not exist on type 'ArchiveWebApp'. this.download = { ...this.download, state: progress.state }; } } onDownloadCancel() { if (window.archivewebpage) { // @ts-expect-error - TS2339 - Property 'download' does not exist on type 'ArchiveWebApp'. | TS2339 - Property 'download' does not exist on type 'ArchiveWebApp'. if (this.download && this.download.state === "progressing") { // @ts-expect-error - TS2339 - Property 'archivewebpage' does not exist on type 'Window & typeof globalThis'. | TS2339 - Property 'download' does not exist on type 'ArchiveWebApp'. window.archivewebpage.downloadCancel(this.download); } else { // @ts-expect-error - TS2551 - Property 'showDownloadProgress' does not exist on type 'ArchiveWebApp'. Did you mean 'onDownloadProgress'? this.showDownloadProgress = false; } } } getDeployType() { if (IS_APP) { return "App"; } if (this.embed) { return "Embedded"; } return "Extension"; } renderAbout() { return html`
${this.getDeployType()} v${VERSION}
${IS_APP ? html`

ArchiveWeb.page App is a standalone app for Mac, Windows and Linux that allows users to archive webpages as they browse

` : html`

ArchiveWeb.page allows users to archive webpages directly in your browser!

`}

See the ArchiveWeb.page Guide for more info on how to use this tool.

Full source code is available at: https://github.com/webrecorder/archiveweb.page

ArchiveWeb.page is part of the Webrecorder Project.

Privacy Policy

ArchiveWeb.page allows users to archive what they browse, storing captured data directly in the browser. Users can downloaded this data as files to their hard drive. Users can also delete any and all archived data at any time. ArchiveWeb.page does not collect any usage or tracking data.

ArchiveWeb.page includes an experimental sharing option for each archive collection. Users can choose to share select archives on a peer-to-peer network (IPFS) via a unique id. Once shared on this network, the data may become accessible to others. All archived items are private and not shared by default, unless explicitly opted-in by the user. (A warning is displayed when sharing via IPFS.)

Disclaimer of Warranties

The application is provided "as is" without any guarantees.

Legal

DISCLAIMER OF SOFTWARE WARRANTY. WEBRECORDER SOFTWARE PROVIDES THIS SOFTWARE TO YOU "AS AVAILABLE" AND WITHOUT WARRANTY OF ANY KIND, EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION ANY WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.

Close
`; } renderSettingsModal() { return html`
${this.settingsTab === "prefs" ? html`
Optional archiving features:
Save Screenshots

Save screenshot + thumbnail of every page on load. Screenshot will be saved as soon as page is done loading.

Save PDFs

Save PDF of each page after page loads (experimental).

Enable Ruffle for Flash

Enables archiving Flash content via injecting the Ruffle emulator into the page. May cause issues with some pages, enable only when archiving websites that contain Flash.


Page override settings:
Disable Media Source Extensions

If set, will likely disable dynamic streaming for many websites, but may result in better video/audio capture.

Disable Performance Detection

If set, will disable performance detection features (such as current HTTP version), which can fix some replay issues, result in more accurate replay.


Privacy related settings:
Archive cookies

Archiving cookies may expose private information that is normally only shared with the site. When enabled, users should exercise caution about sharing these archived items publicly.

Archive local storage

Archiving local storage will archive information that is generally always private. Archiving local storage may be required for certain paywalled sites but should be avoided where possible.

Sharing content created with this setting enabled may compromise your login credentials.
Archived items created with this settings should generally be kept private!

` : ``} ${this.settingsTab === "ipfs" ? html`

Configure settings for sharing archived items to IPFS.

Auto-Detect IPFS

IPFS Daemon URL (leave blank to auto-detect IPFS):

IPFS Gateway URL:

` : ""} ${this.settingsTab === "browsertrix" ? html`

Configure your credentials to upload archived items to Browsertrix: Webrecorder's cloud-based crawling service.

Don't have a Browsertrix account? Sign up today!

Browsertrix URL:

Username

Password

Organization Name or Slug (Optional)

` : ""}
${this.settingsError}
${this.settingsTab !== "prefs" ? html`
` : ``}
`; } // @ts-expect-error - TS7006 - Parameter 'event' implicitly has an 'any' type. async onNewColl(event) { // @ts-expect-error - TS2339 - Property 'showNew' does not exist on type 'ArchiveWebApp'. this.showNew = "loading"; event.preventDefault(); // @ts-expect-error - TS2339 - Property 'renderRoot' does not exist on type 'ArchiveWebApp'. const title = this.renderRoot.querySelector("#new-title").value; const method = "POST"; const body = JSON.stringify({ metadata: { title } }); const resp = await fetch(`${apiPrefix}/c/create`, { method, body }); await resp.json(); const index = this.renderRoot.querySelector("wr-rec-coll-index")!; if (index) { // eslint-disable-next-line @typescript-eslint/no-explicit-any (index as any).loadItems(); } // @ts-expect-error - TS2339 - Property 'showNew' does not exist on type 'ArchiveWebApp'. this.showNew = null; } // @ts-expect-error - TS7006 - Parameter 'event' implicitly has an 'any' type. onSelectColl(event) { //this.selCollId = event.currentTarget.getAttribute("data-id"); //this.selCollTitle = event.currentTarget.getAttribute("data-title"); //this.showCollDrop = false; // @ts-expect-error - TS2339 - Property 'selCollId' does not exist on type 'ArchiveWebApp'. this.selCollId = event.currentTarget.value; } async setDefaultColl() { // @ts-expect-error - TS2339 - Property 'selCollId' does not exist on type 'ArchiveWebApp'. if (!this.selCollId) { // @ts-expect-error - TS2339 - Property 'selCollId' does not exist on type 'ArchiveWebApp'. this.selCollId = await getLocalOption("defaultCollId"); } // @ts-expect-error - TS2339 - Property 'selCollId' does not exist on type 'ArchiveWebApp'. | TS2339 - Property 'colls' does not exist on type 'ArchiveWebApp'. | TS2339 - Property 'colls' does not exist on type 'ArchiveWebApp'. if (!this.selCollId && this.colls?.length) { // @ts-expect-error - TS2339 - Property 'selCollId' does not exist on type 'ArchiveWebApp'. | TS2339 - Property 'colls' does not exist on type 'ArchiveWebApp'. this.selCollId = this.colls[0].id; } // copy from localStorage to chrome.storage if ( // @ts-expect-error - TS2339 - Property 'chrome' does not exist on type 'Window & typeof globalThis'. self.chrome?.storage?.local && self.localStorage ) { await setLocalOption( "index:sortKey", localStorage.getItem("index:sortKey"), ); await setLocalOption( "index:sortDesc", localStorage.getItem("index:sortDesc"), ); } } // @ts-expect-error - TS7006 - Parameter 'event' implicitly has an 'any' type. _setCurrColl(event) { if (!(event instanceof CustomEvent)) { this.setDefaultColl(); return; } const { detail } = event; // @ts-expect-error - TS2339 - Property 'selCollId' does not exist on type 'ArchiveWebApp'. this.selCollId = detail.coll; //this.selCollTitle = event.detail.title; if (!this.colls?.length) { this.colls = [ { id: detail.coll, title: detail.title, }, ]; } } // @ts-expect-error - TS7006 - Parameter 'event' implicitly has an 'any' type. onShowStart(event) { this._setCurrColl(event); // @ts-expect-error - TS2339 - Property 'recordUrl' does not exist on type 'ArchiveWebApp'. this.recordUrl = event.detail.url || "https://example.com/"; // @ts-expect-error - TS2551 - Property 'showStartRecord' does not exist on type 'ArchiveWebApp'. Did you mean 'onStartRecord'? this.showStartRecord = true; } // @ts-expect-error - TS7006 - Parameter 'event' implicitly has an 'any' type. onShowImport(event) { this._setCurrColl(event); this.showImport = true; // @ts-expect-error - TS2339 - Property 'isImportExisting' does not exist on type 'ArchiveWebApp'. this.isImportExisting = true; } // @ts-expect-error - TS7006 - Parameter 'event' implicitly has an 'any' type. onCollsLoaded(event) { this.colls = event.detail.colls; //this.selCollId = this.colls && this.colls.length ? this.colls[0].id: null; this.setDefaultColl(); } // @ts-expect-error - TS7006 - Parameter 'event' implicitly has an 'any' type. async onStartRecord(event) { event.preventDefault(); // @ts-expect-error - TS2339 - Property 'renderRoot' does not exist on type 'ArchiveWebApp'. const url = this.renderRoot.querySelector("#url").value; const previewCheckbox = this.renderRoot.querySelector("#preview"); // @ts-expect-error - TS2339 - Property 'checked' does not exist on type 'Element'. const isPreview = previewCheckbox?.checked; // @ts-expect-error - TS2551 - Property 'showStartRecord' does not exist on type 'ArchiveWebApp'. Did you mean 'onStartRecord'? this.showStartRecord = false; const autorun = this.autorun; // @ts-expect-error - TS2339 - Property 'selCollId' does not exist on type 'ArchiveWebApp'. const collId = this.selCollId; await setLocalOption("defaultCollId", collId); await setLocalOption("autorunBehaviors", autorun ? "1" : "0"); // @ts-expect-error - TS2339 - Property 'chrome' does not exist on type 'Window & typeof globalThis'. | TS2339 - Property 'chrome' does not exist on type 'Window & typeof globalThis'. if (self.chrome?.runtime) { chrome.runtime.sendMessage({ msg: "startNew", url, collId, autorun, }); } else if (window.archivewebpage?.record) { const startRec = !isPreview; window.archivewebpage.record({ url, collId, startRec, autorun }); } return false; } // @ts-expect-error - TS7006 - Parameter 'event' implicitly has an 'any' type. override async onTitle(event): void { super.onTitle(event); if ( this.embed && this.loadedCollId && event.detail.replayTitle && event.detail.title ) { try { await fetch(`${apiPrefix}/c/${this.loadedCollId}/pageTitle`, { method: "POST", body: JSON.stringify(event.detail), }); } catch (e) { console.warn(e); } } } // @ts-expect-error - TS7006 - Parameter 'event' implicitly has an 'any' type. async onSaveSettings(event) { event.preventDefault(); // IPFS settings const daemonUrlText = this.renderRoot.querySelector("#ipfsDaemonUrl"); const gatewayUrlText = this.renderRoot.querySelector("#ipfsGatewayUrl"); const autodetectCheck = this.renderRoot.querySelector("#ipfsAutoDetect"); if (daemonUrlText && gatewayUrlText) { // @ts-expect-error - TS2339 - Property 'value' does not exist on type 'Element'. const daemonUrl = daemonUrlText.value; // @ts-expect-error - TS2339 - Property 'value' does not exist on type 'Element'. const gatewayUrl = gatewayUrlText.value; // @ts-expect-error - TS2339 - Property 'checked' does not exist on type 'Element'. const autoDetect = autodetectCheck?.checked; this.ipfsOpts = { daemonUrl, useCustom: !!daemonUrl, gatewayUrl, autoDetect, }; await this.checkIPFS(); localStorage.setItem("ipfsOpts", JSON.stringify(this.ipfsOpts)); } // Browsertrix Settings const btrixUrl = this.renderRoot.querySelector("#btrixUrl"); const btrixUsername = this.renderRoot.querySelector("#btrixUsername"); const btrixPassword = this.renderRoot.querySelector("#btrixPassword"); const btrixOrgName = this.renderRoot.querySelector("#btrixOrgName"); if (btrixUrl && btrixUsername && btrixPassword) { // @ts-expect-error - TS2339 - Property 'value' does not exist on type 'Element'. const url = btrixUrl.value; // @ts-expect-error - TS2339 - Property 'value' does not exist on type 'Element'. const username = btrixUsername.value; // @ts-expect-error - TS2339 - Property 'value' does not exist on type 'Element'. const password = btrixPassword.value; // @ts-expect-error - TS2339 - Property 'value' does not exist on type 'Element'. const orgName = btrixOrgName?.value || ""; if (url && username && password) { const btrixOpts = { url, username, password, orgName }; let client; try { client = await BtrixClient.login(btrixOpts); this.settingsError = ""; } catch (e) { this.settingsError = "Unable to log in to Browsertrix. Check your credentials."; return false; } localStorage.setItem("btrixOpts", JSON.stringify(btrixOpts)); this.btrixOpts = { ...btrixOpts, client }; } else { this.btrixOpts = null; localStorage.removeItem("btrixOpts"); } } localStorage.setItem("settingsTab", this.settingsTab); this.showSettings = false; return false; } onUpdatePrefsOption(event: Event) { const target = event.currentTarget as HTMLInputElement; const name = target.id; // eslint-disable-next-line @typescript-eslint/no-explicit-any (this as any)[name] = target.checked; void setLocalOption(name, target.checked ? "1" : "0"); } onCancelSettings() { this.settingsError = ""; localStorage.setItem("settingsTab", this.settingsTab); this.showSettings = false; } async checkIPFS() { const ipfsOpts = this.ipfsOpts; // use auto-js-ipfs to get possible local daemon url (eg. for Brave) // if so, send it to the service worker if (ipfsOpts.useCustom && ipfsOpts.daemonUrl) { ipfsOpts.message = "IPFS Access -- Custom IPFS Daemon"; return; } if (!ipfsOpts.daemonUrl && ipfsOpts.autoDetect) { const autoipfs = await createAutoIpfs({ web3StorageToken: __WEB3_STORAGE_TOKEN__, }); if (autoipfs instanceof DaemonAPI) { ipfsOpts.daemonUrl = autoipfs.url; } ipfsOpts.useCustom = false; if (autoipfs instanceof Web3StorageAPI) { ipfsOpts.message = "Sharing via remote web3.storage"; } else if (!ipfsOpts.daemonUrl) { ipfsOpts.message = "IPFS Access Unknown - Sharing Not Available"; } else if (ipfsOpts.daemonUrl.startsWith("http://localhost:45")) { ipfsOpts.message = "Sharing via Brave IPFS node"; } else if (ipfsOpts.daemonUrl.startsWith("http://localhost")) { ipfsOpts.message = "Sharing via local IPFS node"; } else { ipfsOpts.message = ""; } } } } customElements.define("archive-web-page-app", ArchiveWebApp); export { ArchiveWebApp, Loader, Embed };