import { LitElement, html, css } from "lit"; import { property } from "lit/decorators.js"; import { ref, createRef, type Ref } from "lit/directives/ref.js"; import type { SlDialog, SlDropdown, SlSelectEvent, } from "@shoelace-style/shoelace"; import "@shoelace-style/shoelace/dist/components/alert/alert.js"; import "@shoelace-style/shoelace/dist/components/button/button.js"; import "@shoelace-style/shoelace/dist/components/dialog/dialog.js"; import { wrapCss, rwpLogo, IS_APP, VERSION, clickOnSpacebarPress, apiPrefix, replayPrefix, } from "./misc"; import { sourceToId, tsToDate, getPageDateTS, getDateFromTS, } from "./pageutils"; import fasBook from "@fortawesome/fontawesome-free/svgs/solid/book.svg"; import fasDownload from "@fortawesome/fontawesome-free/svgs/solid/download.svg"; import farListAlt from "@fortawesome/fontawesome-free/svgs/regular/list-alt.svg"; import farResources from "@fortawesome/fontawesome-free/svgs/solid/puzzle-piece.svg"; import farPages from "@fortawesome/fontawesome-free/svgs/regular/file-image.svg"; import fasInfoIcon from "@fortawesome/fontawesome-free/svgs/solid/info-circle.svg"; import fasSync from "@fortawesome/fontawesome-free/svgs/solid/sync-alt.svg"; import fasRefresh from "@fortawesome/fontawesome-free/svgs/solid/redo-alt.svg"; import fasFullscreen from "@fortawesome/fontawesome-free/svgs/solid/desktop.svg"; import fasUnfullscreen from "@fortawesome/fontawesome-free/svgs/solid/compress-arrows-alt.svg"; import fasLeft from "@fortawesome/fontawesome-free/svgs/solid/arrow-left.svg"; import fasRight from "@fortawesome/fontawesome-free/svgs/solid/arrow-right.svg"; import fasMenuV from "@fortawesome/fontawesome-free/svgs/solid/ellipsis-v.svg"; import fasAngleLeft from "@fortawesome/fontawesome-free/svgs/solid/angle-left.svg"; import fasAngleRight from "@fortawesome/fontawesome-free/svgs/solid/angle-right.svg"; import fasCaretDown from "@fortawesome/fontawesome-free/svgs/solid/caret-down.svg"; import { RWPEmbedReceipt } from "./embed-receipt"; import Split from "split.js"; import type { Item as ItemInfo } from "./types"; import type { Replay } from "./replay"; import { ifDefined } from "lit/directives/if-defined.js"; import "./item-info"; const RWP_SCHEME = "search://"; export type LoadInfo = { extraConfig?: { baseUrlSourcePrefix?: unknown; baseUrl?: unknown; headers?: unknown; recording?: unknown; }; customColl?: string | null; noWebWorker?: boolean; noCache?: boolean; hideOffscreen?: boolean; loadEager?: boolean; sourceUrl?: string; loadUrl?: string; swError?: string; newFullImport?: unknown; name?: string; }; // =========================================================================== class Item extends LitElement { @property({ type: Boolean }) inited = false; @property({ type: String }) sourceUrl: string | null = null; @property({ type: Object, attribute: false }) loadInfo: LoadInfo | null = null; @property({ type: Boolean }) showSidebar: boolean | null = null; @property({ type: Object, attribute: false }) itemInfo: ItemInfo | Record | null = null; @property({ type: String }) item = ""; @property({ type: Boolean }) hasStory = false; @property({ type: Boolean }) isLoading = false; @property({ type: Object, attribute: false }) tabData: { view?: "story" | "pages" | "resources"; url?: string; ts?: string; multiTs?: string[]; currList?: number; query?: string; urlSearchType?: string; currMime?: string; } = {}; @property({ type: String }) url = ""; @property({ type: String }) ts = ""; @property({ type: Boolean }) isFullscreen: boolean | null = null; @property({ type: Boolean }) menuActive = false; @property({ type: String }) embed: string | null = null; @property({ type: Boolean }) embedDropdownActive = false; @property({ type: Boolean }) editable = false; @property({ type: Boolean }) browsable = true; @property({ type: Boolean }) clearable = true; @property({ type: Boolean }) isVisible = true; @property({ type: String }) favIconUrl = ""; @property({ type: String }) appName = "ReplayWeb.page"; @property({ type: String }) appVersion = VERSION; @property({ type: String }) appLogo = rwpLogo; @property({ type: Number }) autoUpdateInterval = 10; @property({ type: String }) swName: string | null = null; private splitter: Split.Instance | null = null; private _replaceLoc = false; private _locUpdateNeeded = false; private _locationHash = ""; private _autoUpdater: null | Promise = null; private observer?: IntersectionObserver; private archiveInfoDialog: Ref = createRef(); private readonly tabNames = ["pages", "story", "resources", "info"]; private readonly tabLabels = { pages: "Pages", story: "Story", resources: "URLs", info: "Archive Info", }; constructor() { super(); this.showSidebar = localStorage.getItem("pages:showSidebar") === "1"; } firstUpdated() { this.inited = true; window.addEventListener("hashchange", () => this.onHashChange()); this.addEventListener("fullscreenchange", () => { this.isFullscreen = !!document.fullscreenElement; }); if (this.embed && this.loadInfo && this.loadInfo.hideOffscreen) { this.observer = new IntersectionObserver((entries /*, observer*/) => { this.isVisible = entries[0].isIntersecting; }); this.observer.observe(this); } } async runUpdateLoop() { try { // only autoupdate if interval is set, and number of pages < 100 to avoid messing up scrolling while ( this.editable && this.autoUpdateInterval && (!this.itemInfo || !this.itemInfo.pages || this.itemInfo.pages.length < 100) ) { await new Promise((resolve) => setTimeout(resolve, this.autoUpdateInterval * 1000), ); await this.doUpdateInfo(true); } } finally { this._autoUpdater = null; } } async getMultiTimestamps(): Promise { if (!this.tabData.url) return; const resp = await fetch( apiPrefix + "/c/" + this.item + "/ts/?url=" + window.encodeURIComponent(this.tabData.url), ); if (resp.status !== 200) { return; } const json = await resp.json(); this.updateTabData({ multiTs: json.timestamps }); } willUpdate(changedProperties: Map>) { if (changedProperties.has("tabData") && this.tabData) { // Format tab data from URL query params const tabData = {}; Object.entries(this.tabData).forEach(([key, value]) => { if (!value) return; if (key === "multiTs" && typeof value === "string") { tabData[key] = value.split(","); } else { tabData[key] = value; } }); this.tabData = tabData; const prevTabData = changedProperties.get("tabData"); if (this.tabData.url && this.tabData.url !== prevTabData?.url) { this.getMultiTimestamps(); } } } updated(changedProperties) { // if (changedProperties.has("url") || changedProperties.has("ts")) { // if (this.url.startsWith("rwp?")) { // this.tabData = Object.fromEntries(new URLSearchParams(this.url.slice(4)).entries()); // } else { // this.tabData = {view: "replay", url: this.url, ts: this.ts}; // } // } if (changedProperties.has("sourceUrl")) { this.doUpdateInfo(); } if (changedProperties.has("editable")) { if (this.editable && this.autoUpdateInterval && !this._autoUpdater) { this._autoUpdater = this.runUpdateLoop(); } } if (changedProperties.has("tabData")) { if (!this.itemInfo || !this.itemInfo.coll) { return; } const newHash = "#" + new URLSearchParams(this.tabData as Record).toString(); if (!this.tabData.url) { this.url = RWP_SCHEME + decodeURIComponent(this._paramsToString(this.tabData)); } if (newHash !== this._locationHash) { this._locationHash = newHash; if ( this._replaceLoc || Object.keys(changedProperties.get("tabData")).length === 0 ) { const newLoc = new URL(window.location.href); newLoc.hash = this._locationHash; window.history.replaceState({}, "", newLoc.href); this._replaceLoc = false; } else { window.location.hash = this._locationHash; if (!this.showSidebar) { const replay = this.renderRoot.querySelector( "wr-coll-replay", ) as Replay; if (replay) { replay.focus(); } } } if (this.embed && window.parent !== window) { window.parent.postMessage(this.tabData, "*"); } } this._locUpdateNeeded = false; } if (changedProperties.has("showSidebar")) { if (!this.embed) { localStorage.setItem("pages:showSidebar", this.showSidebar ? "1" : "0"); } } if ( changedProperties.has("tabData") || changedProperties.has("showSidebar") ) { this.configureSplitter(); } } configureSplitter() { if (this.tabData.url && this.showSidebar) { const contents = this.renderRoot.querySelector( "#contents", ) as HTMLElement; const replay = this.renderRoot.querySelector("wr-coll-replay") as Replay; if (contents && replay && !this.splitter) { const opts = { sizes: [30, 70], minSize: [300, 300], gutterSize: 4, onDragStart() { replay.setDisablePointer(true); }, onDragEnd() { replay.setDisablePointer(false); }, }; this.splitter = Split([contents, replay], opts); } } else if (this.splitter) { try { this.splitter.destroy(); } catch (e) { // ignore, remove splitter } this.splitter = null; } } async doUpdateInfo(autorefresh = false) { // if auto-refresh, and replay and no sidebar, than skip update if (autorefresh && this.tabData.url && !this.showSidebar) { return; } let item = this.loadInfo && this.loadInfo.customColl; // @todo(2023-11-06) update customColl -> customItem if (!item) { const res = await sourceToId(this.sourceUrl); item = res.coll; } this.item = item; const itemApiPrefix = apiPrefix + "/c/" + item; const itemReplayPrefix = replayPrefix + "/" + item; const resp = await fetch(itemApiPrefix + "?all=1"); if (resp.status != 200) { this.itemInfo = {}; return; } const json = await resp.json(); this.itemInfo = { apiPrefix: itemApiPrefix, replayPrefix: itemReplayPrefix, coll: item, ...json, }; if ( this.loadInfo && this.loadInfo.extraConfig && this.loadInfo.extraConfig.headers ) { const headers = this.loadInfo.extraConfig.headers; await fetch(`${itemApiPrefix}/updateAuth`, { method: "POST", body: JSON.stringify({ headers }), }); } if (!this.itemInfo!.title) { this.itemInfo!.title = this.itemInfo!.filename; } if (this.embed === "replayonly" || this.embed === "replay-with-info") { this.showSidebar = false; } this.hasStory = Boolean( this.itemInfo!.desc || this.itemInfo!.lists?.length, ); this.dispatchEvent( new CustomEvent("coll-loaded", { detail: { collInfo: this.itemInfo, alreadyLoaded: true, }, }), ); this.onHashChange(); } onItemLoaded(event) { this.doUpdateInfo(); this.loadInfo = null; if (event.detail.sourceUrl) { this.sourceUrl = event.detail.sourceUrl; } this.dispatchEvent( new CustomEvent("coll-loaded", { detail: { sourceUrl: this.sourceUrl, collInfo: this.itemInfo, }, }), ); } onItemUpdate(event) { if (!this.editable) { return; } this.itemInfo = { ...this.itemInfo, ...event.detail }; } onHashChange() { const hash = window.location.hash; if (hash && hash !== this._locationHash) { this.tabData = Object.fromEntries( new URLSearchParams(hash.slice(1)).entries(), ); this._locationHash = hash; } if ( this.itemInfo?.coll && (!this.tabData.view || !this.tabNames.includes(this.tabData.view)) ) { const view = this.hasStory ? "story" : this.editable || this.itemInfo.pages?.length ? "pages" : "resources"; this.tabData = { ...this.tabData, view, }; } if (this.tabData.url && this.tabData.url.startsWith("page:")) { const pageIndex = Number(this.tabData.url.slice("page:".length)); if ( this.itemInfo?.pages && !isNaN(pageIndex) && pageIndex < this.itemInfo.pages.length ) { const page = this.itemInfo.pages[pageIndex]; this.tabData.url = page.url; this.tabData.ts = getPageDateTS(page).timestamp; } } if (!this.hasStory && this.tabData.view === "story") { this.tabData.view = "pages"; } if (this.tabData.url && this.tabData.query) { this.showSidebar = true; } } onTabClick(event) { event.preventDefault(); const hash = event.currentTarget.getAttribute("href"); this.tabData = { ...this.tabData, view: hash.slice(1) }; //this.tabData = {view: hash.slice(1)}; return false; } onItemTabNav(event) { if (event.detail.reload) { this.onRefresh(null, true); return; } if ( event.target.id === this.tabData.view || (event.target.id === "replay" && this.tabData.url) ) { this.updateTabData( event.detail.data, event.detail.replaceLoc /*, false */, ); } else if (this.showSidebar && this.tabData.url) { this.updateTabData( event.detail.data, event.detail.replaceLoc /*, true */, ); } } updateTabData(data, replaceLoc = false /*, merge = false*/) { this.tabData = { ...this.tabData, ...data }; if (this.tabData.url) { this.url = this.tabData.url || ""; } if (this.tabData.ts) { this.ts = this.tabData.ts || ""; } this._replaceLoc = !this._locUpdateNeeded && replaceLoc; this._locUpdateNeeded = true; } static get styles() { return wrapCss(Item.compStyles); } static get compStyles() { return css` :host { display: flex; flex-direction: column; height: 100%; min-width: 0px; } .icon { vertical-align: text-top; } .back fa-icon { width: 1.5em; vertical-align: bottom; line-height: 0.5em; } li.is-active { font-weight: bold; } .tab-label { display: inline; } @media screen and (max-width: ${!IS_APP ? css`1053px` : css`1163px`}) { .tab-label { display: none; } .main.tabs span.icon { margin: 0px; } } .main.tabs { display: flex; flex-direction: row; margin-bottom: 0px; } .main.tabs ul { position: relative; } .main.tabs li { line-height: 1.5; padding: 8px 0 6px 0; } @media screen and (max-width: 319px) { .main.tabs li a { padding-right: 4px; padding-left: 4px; } } .sidebar.main.tabs li a { padding-right: 6px; padding-left: 6px; } #contents { height: 100%; width: 100%; display: flex; flex-direction: column; min-height: 0px; flex: auto; background-color: white; } #tabContents { height: 100%; width: 100%; display: flex; flex-direction: row; min-height: 0px; flex: auto; } rwp-embed-receipt { flex-direction: column; display: flex; } ${RWPEmbedReceipt.embedStyles} ${Item.replayBarStyles} `; } static get replayBarStyles() { return css` .breadbar { display: flex; align-items: center; height: 35px; width: 100%; background-color: aliceblue; padding: 0.5em; } .replay-bar { padding: 0.5em 0em 0.5em 0.5em; max-width: none; border-bottom: solid 0.1rem #97989a; width: 100%; background-color: white; } input#url { border-radius: 4px; } .favicon img { width: 20px; height: 20px; margin: 8px; /*filter: drop-shadow(1px 1px 2px grey);*/ } #datetime { position: absolute; right: 0.5rem; z-index: 10; background: #fff; top: 1px; bottom: 1px; display: flex; align-items: center; line-height: 2; } /* Gradient to indicate URL clipping */ #datetime:before { content: ""; position: absolute; top: 0; width: 2em; height: 100%; transform: translateX(-100%); background: linear-gradient( 90deg, rgba(255, 255, 255, 0), #fff 50%, #fff ); pointer-events: none; } .timestamp-dropdown-btn { all: unset; cursor: pointer; display: flex; gap: var(--sl-spacing-x-small); align-items: center; transition: background-color var(--sl-transition-fast); color: var(--sl-color-neutral-600); } .timestamp-dropdown-btn:hover { color: var(--sl-color-neutral-900); } .timestamp-dropdown-btn:hover .timestamp-count-badge { background-color: var(--sl-color-blue-600); } .timestamp-count-badge { display: inline-flex; gap: var(--sl-spacing-2x-small); background-color: var(--sl-color-blue-500); color: var(--sl-color-neutral-0); line-height: 1; padding: var(--sl-spacing-3x-small) var(--sl-spacing-x-small); border-radius: var(--sl-border-radius-small); transition: background-color var(--sl-transition-fast); } .timestamp-count { font-weight: 600; transform: translateY(0.075em); } .timestamp-menu-item[aria-selected="true"]::part(label) { color: var(--sl-color-blue-600); } .menu-head { font-size: 10px; font-weight: bold; display: block; } .menu-logo { vertical-align: middle; } .menu-version { font-size: 10px; } .dropdown-item.info { font-style: italic; } input:focus + #datetime { display: none; } .grey-disabled { --fa-icon-fill-color: lightgrey; color: lightgrey; } .replay-bar .button:focus { box-shadow: none; } .is-borderless { border: 0px; } .narrow { padding: calc(0.5em - 1px) 0.8em; } form { width: 100%; margin: 0 0 0 0.5em; } .gutter.gutter-horizontal { cursor: col-resize; float: left; background-color: rgb(151, 152, 154); } .gutter.gutter-horizontal:hover { cursor: col-resize; } main, wr-coll-replay { width: 100%; } .is-list { height: fit-content; } #contents.full-pages { width: 100% !important; } .sidebar-nav { position: absolute; vertical-align: middle; } .sidebar-nav a { display: inline-block; border-bottom: 0px; } .sidebar-nav span.nav-hover { font-size: smaller; display: none; } .sidebar-nav:hover span.nav-hover, .sidebar-nav:focus-within span.nav-hover { display: initial; color: rgb(72, 118, 255); } .sidebar-nav fa-icon { vertical-align: bottom; } .sidebar-nav:hover fa-icon { color: rgb(72, 118, 255); } .sidebar-nav.left { left: 8px; } .sidebar-nav.right { right: 8px; } /* Since the replay sometimes programmatically receives keyboard focus, and that is visually unexpected for mouse-users, and since this won't particularly trip up keyboard users, just remove the focus style. */ wr-coll-replay:focus { outline: none; } /* Some keyboard-users may see this replacement style */ wr-coll-replay:focus-visible { outline: 1px solid rgb(72, 118, 255); } `; } render() { if (!this.inited) return html``; const isReplay = !!this.tabData.url; const isSidebar = isReplay && this.showSidebar; if (!isReplay && this.tabData && this.tabData.view) { const detail = { // eslint-disable-next-line @typescript-eslint/no-explicit-any -- TODO fixme title: (this.tabLabels as any)[this.tabData.view], replayTitle: false, }; this.dispatchEvent( new CustomEvent("update-title", { bubbles: true, composed: true, detail, }), ); } if (this.itemInfo && !this.itemInfo.coll) { return html` `; } else if (this.itemInfo) { return html` ${this.renderLocationBar()} ${this.renderVerifyInfo()} ${this.renderItemInfo()} Close
${this.renderTabHeader(isSidebar)} ${isSidebar || !isReplay ? this.renderItemTabs(isSidebar) : html``}
${isReplay && this.isVisible ? html` ` : ""}
`; } else { return html``; } } renderTabHeader(isSidebar) { // if (this.tabData.view === "replay") { // return ""; // } return html`
    ${isSidebar ? html`
  • Hide Hide Sidebar
  • ` : ""} ${this.hasStory ? html`
  • Story
  • ` : ""}
  • Pages
  • URLs
  • ${isSidebar ? html`
  • Expand Expand Sidebar to Full View
  • ` : ""}
`; } renderLocationBar() { if (this.embed === "replayonly" || this.embed == "replay-with-info") { return ""; } const dateStr = tsToDate(this.ts).toLocaleString(); const isReplay = !!this.tabData.url; const showFavIcon = isReplay && this.favIconUrl; return html` Skip replay navigation
${!this.isLoading ? html` ` : ""} ${this.browsable ? html` ` : ""} ${this.renderExtraToolbar()}
${isReplay ? this.renderTimestamp() : ""} ${showFavIcon ? html` ` : html``}
Full Screen Back Forward Reload ${this.browsable ? html` Browse Contents ` : ""} ${this.renderExtraToolbar()} ${this.clearable ? html`
Purge Cache + Full Reload ` : html``} ${(!this.editable && this.sourceUrl?.startsWith("http://")) || this.sourceUrl?.startsWith("https://") ? html`
Download Archive ` : html``} ${dateStr ? html`
Capture Date${dateStr}
` : ""} Archive Info
 About ${this.appName} (${this.appVersion})

Skipped

`; } private renderTimestamp() { const timestampStrs: { date: string; label: string }[] = []; this.tabData.multiTs?.forEach((ts) => { // Filter out invalid dates try { const date = getDateFromTS(+ts); const dateStr = tsToDate(date).toLocaleString(); timestampStrs.push({ date, label: dateStr, }); } catch { /* empty */ } }); const currDateStr = tsToDate(this.ts).toLocaleString(); return html`
${timestampStrs.length > 1 ? html` ${timestampStrs.map(({ date: ts, label }) => { const selected = this.ts === ts; return html` ${label}`; })} ` : currDateStr}
`; } renderVerifyInfo() { if (this.embed !== "replay-with-info") { return ""; } return html` `; } dragStart() { const replay = this.renderRoot.querySelector("wr-coll-replay") as Replay; if (replay) { replay.setDisablePointer(true); } } dragEnd() { const replay = this.renderRoot.querySelector("wr-coll-replay") as Replay; if (replay) { replay.setDisablePointer(false); } } renderItemInfo() { if (!this.itemInfo) return html` Archive info is not available
Please reload and try again.
`; return html``; } renderExtraToolbar(/*isDropdown = false*/) { return ""; } renderItemTabs(isSidebar) { const isStory = this.hasStory && this.tabData.view === "story"; const isPages = this.tabData.view === "pages"; const isResources = this.tabData.view === "resources"; return html` ${isStory ? html` ` : ""} ${isResources ? html` ` : ""} ${isPages ? html` ` : ""} `; } skipMenu(event) { // This is a workaround, since this app's routing doesn't permit normal // following of in-page anchors. event.preventDefault(); ( this.renderRoot.querySelector("#skip-replay-target") as HTMLElement ).focus(); } onKeyDown(event) { if (event.key === "Esc" || event.key === "Escape") { event.preventDefault(); event.currentTarget.value = this.url; } } onMenu(event) { event.stopPropagation(); this.menuActive = !this.menuActive; if (this.menuActive) { document.addEventListener( "click", () => { this.menuActive = false; }, { once: true }, ); } } onFullscreenToggle(event) { event.preventDefault(); this.menuActive = false; if (!this.isFullscreen) { this.requestFullscreen(); } else { document.exitFullscreen(); } } onGoBack(event) { event.preventDefault(); this.menuActive = false; window.history.back(); } onGoForward(event) { event.preventDefault(); this.menuActive = false; window.history.forward(); } onShowPages(event) { event.preventDefault(); // show sidebar for tablet or wider, or hide sidebar if (this.showSidebar || document.documentElement.clientWidth >= 769) { this.showSidebar = !this.showSidebar; } else { // otherwise, just go to full pages view this.showSidebar = false; this.updateTabData({ url: "", ts: "" }); } } onFullPageView(event) { event.preventDefault(); this.updateTabData({ url: "", ts: "" }); } onHideSidebar(event) { event.preventDefault(); this.showSidebar = false; } async onFavIcons(event) { for (const icon of event.detail.icons) { const resp = await fetch(icon.href); if (resp.status === 200) { const ct = resp.headers.get("Content-Type"); if (ct && !ct.startsWith("text/")) { this.favIconUrl = icon.href; return; } } } // not found this.favIconUrl = ""; } onPurgeCache(event) { event.preventDefault(); const reload = event.detail && event.detail.reload !== undefined ? event.detail.reload : true; this.deleteFully(reload); } async deleteFully(reload = false) { const deleteURL = this.itemInfo!.apiPrefix + (reload ? "?reload=1" : ""); const resp = await fetch(deleteURL, { method: "DELETE", }); if (resp.status !== 200) { console.warn("purge failed: " + resp.status); } if (!reload && !this.embed) { window.location.search = ""; return; } //window.location.hash = ""; window.location.reload(); } onSubmit(event) { event.preventDefault(); const input = this.renderRoot.querySelector("input") as HTMLInputElement; if (input.value) { this.navigateTo(input.value); } else { input.value = this.url; } return false; } onLostFocus(event) { if (!event.currentTarget.value) { event.currentTarget.value = this.url; } } onTimestampDropdownBtnBlur(event: MouseEvent) { const btn = event.currentTarget as HTMLButtonElement; const dropdown = btn.closest("sl-dropdown") as SlDropdown; const relatedTarget = event.relatedTarget as HTMLElement; if (relatedTarget && dropdown.contains(relatedTarget)) { return; } if (dropdown.open) { dropdown.hide(); } } onSelectTimestamp(event: SlSelectEvent) { const { item } = event.detail; this.updateTabData({ ts: item.value }); } navigateTo(value) { let data; if (value.startsWith("http://") || value.startsWith("https://")) { data = { url: value }; if (value === this.tabData.url) { const replay = this.renderRoot.querySelector( "wr-coll-replay", ) as Replay; if (replay) { replay.refresh(); } return; } } else { if (!value.startsWith(RWP_SCHEME)) { data = { query: value, view: "pages" }; } else { data = this._stringToParams(value); } } this.updateTabData(data); } _stringToParams(value) { const q = new URLSearchParams(value.slice(RWP_SCHEME.length)); // eslint-disable-next-line @typescript-eslint/no-explicit-any -- TODO fixme const data: any = {}; data.url = ""; data.ts = ""; for (const param of [ "query", "view", "currList", "currMime", "urlSearchType", ]) { if (q.has(param)) { data[param] = q.get(param); } } return data; } _paramsToString(value: Record) { const q = new URLSearchParams(); for (const param of [ "query", "view", "currList", "currMime", "urlSearchType", ]) { if (param in value) { q.set(param, String(value[param])); } } return q.toString(); } onRefresh(event: Event | null, replayOnly = false) { if (event) { event.preventDefault(); } this.menuActive = false; if (this.tabData.url) { const replay = this.renderRoot.querySelector("wr-coll-replay") as Replay; if (replay) { replay.refresh(); } } else if (!replayOnly) { window.location.reload(); } } onAbout() { this.dispatchEvent(new CustomEvent("about-show")); } onShowInfoDialog() { this.archiveInfoDialog.value?.show(); } onHideInfoDialog() { this.archiveInfoDialog.value?.hide(); } } customElements.define("wr-item", Item); export { Item };