Archive verification info + Embed improvements + Embed Receipts UI (#125)
info panel verification/integrity additions, (fixes on #113) - info panel: display number of hashes verified valid (and invalid), observer domain (if any), and certificate fingerprint, creation software, and package digest - info: ensure replayTS is updated, not just replayUrl, to ensure correct stats 404 page: improved formatting on 404 page (via wabac.js) embeds: - new embed 'replay-with-info mode, displays replay-only with dropdown showing archival 'receipt, which includes condensed info and verification (hashes valid/invalid, observer domain, cert fingerprint) - support 'newWindowBase' attribute for specifying base replay URL for opening new window. default: use current url if deepLink enabled, otherwise use https://replayweb.page/ use transparent for embeds - embed dropdown/receipt styling from @Shrinks99 - automatically setting noCache and noWebWorker for older browsers (eg. safari < 16) - add 'hideOffscreen' attribute to enable unloading embed when offscreen dependencies: update to wabac.js 2.13.0-beta.0 bump version to 1.7.0-beta.0 Embed receipt styling co-authored-by: @Shrinks99
This commit is contained in:
+14
-16
@@ -351,10 +351,13 @@ class ReplayWebApp extends LitElement
|
||||
this.showAbout = true;
|
||||
}
|
||||
|
||||
if (this.pageParams.get("config")) {
|
||||
if (this.pageParams.has("embed")) {
|
||||
if (!this.loadInfo) {
|
||||
this.loadInfo = {};
|
||||
}
|
||||
}
|
||||
|
||||
if (this.pageParams.get("config")) {
|
||||
try {
|
||||
this.loadInfo.extraConfig = JSON.parse(this.pageParams.get("config"));
|
||||
} catch (e) {
|
||||
@@ -362,38 +365,33 @@ class ReplayWebApp extends LitElement
|
||||
}
|
||||
}
|
||||
|
||||
if (this.pageParams.get("baseUrlSourcePrefix")) {
|
||||
this.loadInfo.extraConfig = this.loadInfo.extraConfig || {};
|
||||
this.loadInfo.extraConfig.baseUrlSourcePrefix = this.pageParams.get("baseUrlSourcePrefix");
|
||||
}
|
||||
|
||||
if (this.pageParams.get("basePageUrl")) {
|
||||
if (!this.loadInfo) {
|
||||
this.loadInfo = {extraConfig: {}};
|
||||
}
|
||||
if (!this.loadInfo.extraConfig) {
|
||||
this.loadInfo.extraConfig = {};
|
||||
}
|
||||
this.loadInfo.extraConfig = this.loadInfo.extraConfig || {};
|
||||
this.loadInfo.extraConfig.baseUrl = this.pageParams.get("basePageUrl");
|
||||
}
|
||||
|
||||
if (this.pageParams.get("customColl")) {
|
||||
if (!this.loadInfo) {
|
||||
this.loadInfo = {};
|
||||
}
|
||||
this.loadInfo.customColl = this.pageParams.get("customColl");
|
||||
}
|
||||
|
||||
if (this.pageParams.get("noWebWorker") === "1") {
|
||||
if (!this.loadInfo) {
|
||||
this.loadInfo = {};
|
||||
}
|
||||
this.loadInfo.noWebWorker = true;
|
||||
}
|
||||
|
||||
|
||||
if (this.pageParams.get("noCache") === "1") {
|
||||
if (!this.loadInfo) {
|
||||
this.loadInfo = {};
|
||||
}
|
||||
this.loadInfo.noCache = true;
|
||||
}
|
||||
|
||||
if (this.pageParams.get("hideOffscreen") === "1") {
|
||||
this.loadInfo.hideOffscreen = true;
|
||||
}
|
||||
|
||||
if (IS_APP && this.sourceUrl.startsWith("file://")) {
|
||||
// eslint-disable-next-line no-undef
|
||||
const url = new URL(__APP_FILE_SERVE_PREFIX__);
|
||||
|
||||
+67
-33
@@ -404,44 +404,88 @@ class CollInfo extends LitElement
|
||||
`;
|
||||
}
|
||||
|
||||
render() {
|
||||
renderSource(coll) {
|
||||
return html`
|
||||
<div class="column is-4">
|
||||
<span class="source-text"><p class="minihead">Source</p>
|
||||
${coll.sourceUrl && (coll.sourceUrl.startsWith("http://") || coll.sourceUrl.startsWith("https://")) ? html`
|
||||
<a href="${coll.sourceUrl}">${coll.sourceUrl} </a>` : html`
|
||||
${coll.sourceUrl} `}
|
||||
</span>
|
||||
|
||||
<a @click="${(e) => this.onCopy(e, coll.sourceUrl)}" class="copy"><fa-icon .svg="${fasCopy}"/></a>
|
||||
${coll.sourceUrl && coll.sourceUrl.startsWith("googledrive://") ? html`
|
||||
<p><i>(${coll.filename})</i></p>` : ""}
|
||||
</div>
|
||||
<div class="column is-2"><p class="minihead">Date Loaded</p>${coll.ctime ? new Date(coll.ctime).toLocaleString() : ""}</div>
|
||||
<div class="column is-2"><p class="minihead">Total Size</p>${prettyBytes(Number(coll.size || 0))}</div>
|
||||
`;
|
||||
}
|
||||
|
||||
renderSummaryView() {
|
||||
const coll = this.coll;
|
||||
const detailed = this.detailed;
|
||||
|
||||
return html`
|
||||
<div class="columns">
|
||||
<div class="column col-title is-4">
|
||||
<span class="subtitle has-text-weight-bold">
|
||||
<a href="?source=${encodeURIComponent(coll.sourceUrl)}">${coll.title || coll.filename}</a>
|
||||
</span>
|
||||
</div>
|
||||
${this.renderSource(coll)}
|
||||
</div>`;
|
||||
}
|
||||
|
||||
renderDetailed() {
|
||||
const coll = this.coll;
|
||||
|
||||
let {numValid, numInvalid, domain, certFingerprint, datapackageHash, software} = this.coll.verify || {};
|
||||
numValid = numValid || 0;
|
||||
numInvalid = numInvalid || 0;
|
||||
|
||||
const certFingerprintUrl = certFingerprint ? `https://search.censys.io/certificates/${certFingerprint}` : "";
|
||||
|
||||
return html`
|
||||
<div class="columns">
|
||||
<div class="column col-title is-4">
|
||||
<span class="subtitle has-text-weight-bold">
|
||||
${detailed ? html`
|
||||
${coll.title || coll.filename}
|
||||
` : html`
|
||||
<a href="?source=${encodeURIComponent(coll.sourceUrl)}">${coll.title || coll.filename}</a>`}
|
||||
</span>
|
||||
</div>
|
||||
${detailed && coll.desc ? html`
|
||||
${coll.desc ? html`
|
||||
<div class="column">
|
||||
<p class="minihead">Description</p>
|
||||
${coll.desc}
|
||||
</div>` : html`
|
||||
`}
|
||||
<div class="column is-4">
|
||||
<span class="source-text"><p class="minihead">Source</p>
|
||||
${coll.sourceUrl && (coll.sourceUrl.startsWith("http://") || coll.sourceUrl.startsWith("https://")) ? html`
|
||||
<a href="${coll.sourceUrl}">${coll.sourceUrl} </a>` : html`
|
||||
${coll.sourceUrl} `}
|
||||
</span>
|
||||
<div class="column"><p class="minihead">Filename</p>${coll.filename}</div>
|
||||
${this.renderSource(coll)}
|
||||
|
||||
<a @click="${(e) => this.onCopy(e, coll.sourceUrl)}" class="copy"><fa-icon .svg="${fasCopy}"/></a>
|
||||
${coll.sourceUrl && coll.sourceUrl.startsWith("googledrive://") ? html`
|
||||
<p><i>(${coll.filename})</i></p>` : ""}
|
||||
${domain ? html`
|
||||
<div class="column">
|
||||
<p class="minihead">Observed By</p>
|
||||
<p>${domain}</p>
|
||||
${certFingerprintUrl ? html`<span><a target="_blank" href="${certFingerprintUrl}">View Certificate</a></span>` : ""}
|
||||
</div>
|
||||
${detailed ? html`
|
||||
<div class="column"><p class="minihead">Filename</p>${coll.filename}</div>` : html``}
|
||||
` : software ? html`
|
||||
<div class="column">
|
||||
<p class="minihead">Created With</p>
|
||||
${software || "Unknown"}
|
||||
</div>
|
||||
` : ""}
|
||||
|
||||
<div class="column is-2"><p class="minihead">Date Loaded</p>${coll.ctime ? new Date(coll.ctime).toLocaleString() : ""}</div>
|
||||
<div class="column is-2"><p class="minihead">Total Size</p>${prettyBytes(Number(coll.size || 0))}</div>
|
||||
<div class="column">
|
||||
<p class="minihead">Validation</p>
|
||||
${numValid > 0 || numInvalid > 0 ? html`
|
||||
<p>${numValid} hashes verified${numInvalid ? html`, ${numInvalid} invalid` : ""}</p>` : html`
|
||||
Not Available`}
|
||||
</div>
|
||||
|
||||
<div class="column">
|
||||
<p class="minihead">Package Hash</p>
|
||||
${datapackageHash || "Not Available"}
|
||||
</div>
|
||||
|
||||
${detailed ? html`
|
||||
<div class="column">
|
||||
<p class="minihead">Loading Mode</p>
|
||||
${coll.onDemand ? "Download On-Demand" : "Fully Local"}
|
||||
@@ -450,22 +494,12 @@ class CollInfo extends LitElement
|
||||
<p class="minihead">Collection id</p>
|
||||
${coll.coll}
|
||||
</div>
|
||||
` : html``}
|
||||
|
||||
</div>`;
|
||||
}
|
||||
|
||||
// purge + delete buttons
|
||||
//
|
||||
// <div class="column">
|
||||
// <button @click="${(e) => this.onPurge(true)}" class="button">Purge Cache + Full Reload</button>
|
||||
// </div>
|
||||
|
||||
// ${this.canDelete ? html`
|
||||
// <div class="column">
|
||||
// <button @click="${(e) => this.onPurge(false)}" class="button is-outlined is-danger">Delete Archive</button>
|
||||
// </div>` : html``}
|
||||
//
|
||||
render() {
|
||||
return this.detailed ? this.renderDetailed() : this.renderSummaryView();
|
||||
}
|
||||
|
||||
onCopy(event, sourceUrl) {
|
||||
event.preventDefault();
|
||||
|
||||
+29
-3
@@ -24,6 +24,7 @@ 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 { RWPEmbedReceipt } from "./embed-receipt.js";
|
||||
import Split from "split.js";
|
||||
|
||||
|
||||
@@ -61,6 +62,7 @@ class Coll extends LitElement
|
||||
};
|
||||
|
||||
this.menuActive = false;
|
||||
this.embedDropdownActive = false;
|
||||
|
||||
this.hasStory = false;
|
||||
|
||||
@@ -107,6 +109,8 @@ class Coll extends LitElement
|
||||
menuActive: { type: Boolean },
|
||||
|
||||
embed: { type: String },
|
||||
embedDropdownActive: { type: Boolean },
|
||||
|
||||
editable: { type: Boolean },
|
||||
browsable: { type: Boolean },
|
||||
clearable: { type: Boolean },
|
||||
@@ -131,7 +135,7 @@ class Coll extends LitElement
|
||||
this.isFullscreen = !!document.fullscreenElement;
|
||||
});
|
||||
|
||||
if (this.embed) {
|
||||
if (this.embed && this.loadInfo && this.loadInfo.hideOffscreen) {
|
||||
this.observer = new IntersectionObserver((entries/*, observer*/) => {
|
||||
this.isVisible = entries[0].isIntersecting;
|
||||
});
|
||||
@@ -289,7 +293,7 @@ class Coll extends LitElement
|
||||
this.collInfo.title = this.collInfo.filename;
|
||||
}
|
||||
|
||||
if (this.embed === "replayonly") {
|
||||
if (this.embed === "replayonly" || this.embed === "replay-with-info") {
|
||||
this.showSidebar = false;
|
||||
}
|
||||
|
||||
@@ -475,6 +479,13 @@ class Coll extends LitElement
|
||||
flex: auto;
|
||||
}
|
||||
|
||||
rwp-embed-receipt {
|
||||
flex-direction: column;
|
||||
display: flex;
|
||||
}
|
||||
|
||||
${RWPEmbedReceipt.embedStyles}
|
||||
|
||||
${Coll.replayBarStyles}`;
|
||||
}
|
||||
|
||||
@@ -657,6 +668,7 @@ class Coll extends LitElement
|
||||
} else if (this.collInfo) {
|
||||
return html`
|
||||
${this.renderLocationBar()}
|
||||
${this.renderVerifyInfo()}
|
||||
<div id="tabContents">
|
||||
<div id="contents" class="is-light ${isSidebar ? "sidebar" : (isReplay ? "is-hidden" : "full-pages")}"
|
||||
role="${isSidebar ? "complementary" : ""}" aria-label="${isSidebar ? "Browse Contents" : ""}">
|
||||
@@ -744,7 +756,7 @@ class Coll extends LitElement
|
||||
}
|
||||
|
||||
renderLocationBar() {
|
||||
if (this.embed === "replayonly") {
|
||||
if (this.embed === "replayonly" || this.embed == "replay-with-info") {
|
||||
return "";
|
||||
}
|
||||
|
||||
@@ -880,6 +892,20 @@ class Coll extends LitElement
|
||||
</nav><p id="skip-replay-target" tabindex="-1" class="is-sr-only">Skipped</p>`;
|
||||
}
|
||||
|
||||
renderVerifyInfo() {
|
||||
if (this.embed !== "replay-with-info") {
|
||||
return "";
|
||||
}
|
||||
|
||||
return html`<rwp-embed-receipt
|
||||
.collInfo=${this.collInfo}
|
||||
url=${this.url}
|
||||
ts=${this.ts}
|
||||
.appLogo=${this.appLogo}
|
||||
>
|
||||
</rwp-embed-receipt>`;
|
||||
}
|
||||
|
||||
dragStart() {
|
||||
const replay = this.renderRoot.querySelector("wr-coll-replay");
|
||||
if (replay) {
|
||||
|
||||
@@ -0,0 +1,268 @@
|
||||
|
||||
import btGlobe from "../assets/globe.svg";
|
||||
import btAngleDoubleDown from "../assets/chevron-double-down.svg";
|
||||
import btAngleDoubleUp from "../assets/chevron-double-up.svg";
|
||||
import fabGithub from "@fortawesome/fontawesome-free/svgs/brands/github.svg";
|
||||
import fasDownload from "@fortawesome/fontawesome-free/svgs/solid/download.svg";
|
||||
|
||||
import {clickOnSpacebarPress} from "./misc";
|
||||
|
||||
import { LitElement, html, css } from "lit";
|
||||
import { tsToDate } from "./pageutils";
|
||||
import prettyBytes from "pretty-bytes";
|
||||
|
||||
|
||||
// ===========================================================================
|
||||
export class RWPEmbedReceipt extends LitElement
|
||||
{
|
||||
constructor() {
|
||||
super();
|
||||
this.collInfo = null;
|
||||
this.appLogo = null;
|
||||
this.ts = null;
|
||||
this.url = null;
|
||||
this.active = false;
|
||||
}
|
||||
|
||||
get renderRoot() {
|
||||
return this;
|
||||
}
|
||||
|
||||
static get properties() {
|
||||
return {
|
||||
collInfo: {type: Object},
|
||||
appLogo: {type: Object},
|
||||
url: {type: String},
|
||||
ts: {type: String},
|
||||
|
||||
active: {type: Boolean }
|
||||
};
|
||||
}
|
||||
|
||||
static get embedStyles() {
|
||||
return css`
|
||||
rwp-embed-receipt {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.icon {
|
||||
vertical-align: text-top;
|
||||
}
|
||||
|
||||
#embed-dropdown {
|
||||
max-height: calc(100vh - 50px);
|
||||
padding-top: 0;
|
||||
margin-top: -0.5rem;
|
||||
display: block;
|
||||
z-index: 1;
|
||||
pointer-events: none;
|
||||
transition: all .3s linear;
|
||||
transform-origin: left top;
|
||||
transform: scaleY(0);
|
||||
transition: all 300ms cubic-bezier(0.15, 0, 0.1, 1);
|
||||
filter: drop-shadow(0px 8px 4px rgba(0, 0, 0, 0.15));
|
||||
}
|
||||
|
||||
.dropdown.is-active #embed-dropdown {
|
||||
transform: scaleY(1);
|
||||
}
|
||||
|
||||
.embed-info-container {
|
||||
width: 100%;
|
||||
display: flex !important;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
button.embed-info {
|
||||
padding: 0;
|
||||
background-color: white;
|
||||
justify-content: space-between;
|
||||
max-width: 40rem;
|
||||
width: calc(100% - 1rem);
|
||||
height: 42px;
|
||||
border-color: #D1D5DA;
|
||||
border-width: 1px;
|
||||
border-style: solid;
|
||||
border-radius: 999px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
text-overflow: ellipsis;
|
||||
filter: drop-shadow(0px 1px 2px rgba(0, 0, 0, 0.15));
|
||||
transition-duration: 50ms;
|
||||
transition-timing-function: ease-out;
|
||||
cursor: pointer;
|
||||
z-index: 2
|
||||
}
|
||||
|
||||
button.embed-info:active {
|
||||
color: initial;
|
||||
}
|
||||
|
||||
button.embed-info:hover {
|
||||
filter: drop-shadow(0px 2px 4px rgba(0, 0, 0, 0.2));
|
||||
transform: scale(1.01);
|
||||
}
|
||||
|
||||
button.embed-info:hover:active {
|
||||
transform: translateY(0.25rem);
|
||||
}
|
||||
|
||||
.embed-info-buttontext {
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
flex-grow: 1;
|
||||
text-align: start;
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
.embed-info-drop {
|
||||
font-size: 14px;
|
||||
padding: 1rem;
|
||||
padding-top: 2rem;
|
||||
max-width: 38rem;
|
||||
max-height: 42rem;
|
||||
width: calc(100% - 2rem);
|
||||
border-top-right-radius: 0px;
|
||||
border-top-left-radius: 0px;
|
||||
pointer-events: auto;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.embed-info-drop > p {
|
||||
font-size: 14px;
|
||||
color: black;
|
||||
}
|
||||
|
||||
.embed-info-drop > h2 {
|
||||
margin-bottom: 0.25rem;
|
||||
font-size: 16px;
|
||||
font-weight: bold;
|
||||
text-transform: none;
|
||||
letter-spacing: 0;
|
||||
color: #24292E;
|
||||
}
|
||||
|
||||
.embed-info-drop-statscontainer > h3 {
|
||||
font-size: 12px;
|
||||
color: #394146;
|
||||
}
|
||||
|
||||
.embed-info-drop-statscontainer > p {
|
||||
font-size: 14px;
|
||||
color: black;
|
||||
}
|
||||
|
||||
.embed-info-drop a {
|
||||
word-break: break-all;
|
||||
}
|
||||
|
||||
.embed-info-drop .show-hash {
|
||||
word-break: break-all;
|
||||
font-family: monospace;
|
||||
}
|
||||
|
||||
.embed-globe {
|
||||
margin: 0.25rem;
|
||||
padding: 7px;
|
||||
background-color: #0366D6;
|
||||
border-radius: 9999px;
|
||||
color: white;
|
||||
border-width: 1px;
|
||||
border-color: #D1D5DA;
|
||||
border-style: solid;
|
||||
line-height: 0.5em;
|
||||
}`;
|
||||
}
|
||||
|
||||
render() {
|
||||
let {numValid, numInvalid, domain, certFingerprint, datapackageHash, software} = this.collInfo.verify || {};
|
||||
numValid = numValid || 0;
|
||||
numInvalid = numInvalid || 0;
|
||||
|
||||
const sourceUrl = this.collInfo.sourceUrl;
|
||||
|
||||
const certFingerprintUrl = certFingerprint ? `https://search.censys.io/certificates/${certFingerprint}` : "";
|
||||
|
||||
const dateStr = tsToDate(this.ts).toLocaleString();
|
||||
|
||||
return html`
|
||||
<div class="dropdown mb-4 ${this.active ? "is-active" : ""}">
|
||||
<div class="dropdown-trigger embed-info-container">
|
||||
<button class="embed-info is-small is-rounded mt-4" aria-haspopup="true" aria-controls="embed-dropdown" @click="${this.onEmbedDrop}">
|
||||
<fa-icon class="menu-logo mr-2 embed-globe" size="1rem" aria-hidden="true" .svg=${btGlobe}></fa-icon>
|
||||
<span class="embed-info-buttontext">
|
||||
This embed is part of a web archive. Click here to learn more.
|
||||
</span>
|
||||
<span class="icon is-small mr-4 ml-2">
|
||||
<fa-icon title="Toggle" .svg="${this.active ? btAngleDoubleUp : btAngleDoubleDown}" aria-hidden="true"></fa-icon>
|
||||
</span>
|
||||
</button>
|
||||
</div>
|
||||
<div class="dropdown-menu embed-info-container" id="embed-dropdown" role="menu">
|
||||
<div class="dropdown-content embed-info-drop">
|
||||
<p class="mb-4">
|
||||
Even if the original page goes offline or is changed, the content below will remain unchanged as it is loaded from a web archive.
|
||||
</p>
|
||||
<hr class="dropdown-divider">
|
||||
<h2 mt-4">Technical Information</h2>
|
||||
<div class="embed-info-drop-statscontainer mb-4">
|
||||
<h3>Original URL:</h3>
|
||||
<p><a target="_blank" href="${this.url}">${this.url}</a></p>
|
||||
<h3 class="mt-2">Archived On:</h3>
|
||||
<p>${dateStr}</p>
|
||||
${domain ? html`
|
||||
<h3 class="mt-2">Observed By:</h3>
|
||||
<p>${domain}</p>
|
||||
${certFingerprintUrl ?
|
||||
html`<p><a target="_blank" href="${certFingerprintUrl}">View Certificate</a></p>` : ""}
|
||||
` : software ? html`
|
||||
<h3 class="mt-2">Created With:</h3>
|
||||
<p>${software}</p>` : ""}
|
||||
<h3 class="mt-2">Validation:</h3>
|
||||
${numValid > 0 || numInvalid > 0 ? html`
|
||||
<p>${numValid} hashes verified${numInvalid ? html`, ${numInvalid} invalid` : ""}</p>` : html`
|
||||
<p>Not Available</p>
|
||||
`}
|
||||
<h3 class="mt-2">Package Hash:</h3>
|
||||
<p class="show-hash">${datapackageHash}</p>
|
||||
<h3 class="mt-2">Size</h3>
|
||||
<p>${prettyBytes(Number(this.collInfo.size || 0))}</p>
|
||||
</div>
|
||||
${sourceUrl ? html`
|
||||
<hr class="dropdown-divider">
|
||||
<a href="${sourceUrl}" class="button" @keyup="${clickOnSpacebarPress}">
|
||||
<span class="icon is-small">
|
||||
<fa-icon size="1.0em" class="has-text-grey" aria-hidden="true" .svg="${fasDownload}"></fa-icon>
|
||||
</span>
|
||||
<span>Download Archive</span>
|
||||
</a>
|
||||
<p class="mt-2">This archive can then be viewed directly in your browser by loading it via <a target="_blank" href="https://replayweb.page">replayweb.page</a></p>
|
||||
` : ""}
|
||||
<p class="is-size-7 is-flex is-justify-content-space-between" style="margin-top: 40px">
|
||||
<span>
|
||||
<a class="has-text-black" target="_blank" href="https://github.com/webrecorder/replayweb.page">
|
||||
<fa-icon class="menu-logo mr-1" size="1.0rem" aria-hidden="true" .svg=${this.appLogo}></fa-icon>
|
||||
Powered by ReplayWeb.page
|
||||
</a>
|
||||
</span>
|
||||
<span>
|
||||
<a class="has-text-black" target="_blank" href="https://github.com/webrecorder/replayweb.page">Source Code
|
||||
<fa-icon class="menu-logo ml-1" size="1.0rem" aria-hidden="true" .svg=${fabGithub}></fa-icon>
|
||||
</a>
|
||||
</span>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
onEmbedDrop(event) {
|
||||
event.stopPropagation();
|
||||
this.active = !this.active;
|
||||
}
|
||||
}
|
||||
|
||||
customElements.define("rwp-embed-receipt", RWPEmbedReceipt);
|
||||
@@ -10,6 +10,8 @@ var scriptSrc = document.currentScript && document.currentScript.src;
|
||||
|
||||
var defaultReplayFile = "";
|
||||
|
||||
const DEFAULT_REPLAY_BASE = "https://replayweb.page/";
|
||||
|
||||
|
||||
// ===========================================================================
|
||||
class Embed extends LitElement
|
||||
@@ -30,6 +32,7 @@ class Embed extends LitElement
|
||||
this.coll = "";
|
||||
this.paramString = null;
|
||||
this.deepLink = false;
|
||||
this.newWindowBase = "";
|
||||
this.swInited = false;
|
||||
this.embed = null;
|
||||
this.reloadCount = 0;
|
||||
@@ -72,6 +75,9 @@ class Embed extends LitElement
|
||||
noSandbox: { type: Boolean },
|
||||
noWebWorker: { type: Boolean },
|
||||
noCache: { type: Boolean },
|
||||
hideOffscreen: { type: Boolean },
|
||||
|
||||
newWindowBase: { type: String },
|
||||
|
||||
errorMessage: { type: String }
|
||||
};
|
||||
@@ -118,6 +124,23 @@ class Embed extends LitElement
|
||||
this.updateFromHash();
|
||||
window.addEventListener("hashchange", () => this.updateFromHash());
|
||||
}
|
||||
|
||||
this.loadBrowserDefaults();
|
||||
}
|
||||
|
||||
loadBrowserDefaults() {
|
||||
// set defaults for older browsers (eg. Safari < 16) based on recommendations from:
|
||||
// https://lil.law.harvard.edu/blog/2022/09/15/opportunities-and-challenges-of-client-side-playback/
|
||||
|
||||
// likely safari < 16, don't use web workers due to issues with split storage state
|
||||
if (window.GestureEvent !== undefined && window.SharedWorker === undefined ){
|
||||
this.noWebWorker = true;
|
||||
}
|
||||
|
||||
// if no storage manager or estimate, don't cache
|
||||
if (!navigator.storage || !navigator.storage.estimate) {
|
||||
this.noCache = true;
|
||||
}
|
||||
}
|
||||
|
||||
updateFromHash() {
|
||||
@@ -173,9 +196,14 @@ class Embed extends LitElement
|
||||
customColl: this.coll,
|
||||
config,
|
||||
basePageUrl: window.location.href.split("#")[0],
|
||||
baseUrlSourcePrefix: this.newWindowBase,
|
||||
embed: this.embed,
|
||||
};
|
||||
|
||||
if (!this.deepLink && !params.baseUrlSourcePrefix) {
|
||||
params.baseUrlSourcePrefix = DEFAULT_REPLAY_BASE;
|
||||
}
|
||||
|
||||
if (this.noWebWorker) {
|
||||
params.noWebWorker = "1";
|
||||
}
|
||||
@@ -184,6 +212,10 @@ class Embed extends LitElement
|
||||
params.noCache = "1";
|
||||
}
|
||||
|
||||
if (this.hideOffscreen) {
|
||||
params.hideOffscreen = "1";
|
||||
}
|
||||
|
||||
this.paramString = new URLSearchParams(params).toString();
|
||||
|
||||
this.hashString = new URLSearchParams({
|
||||
|
||||
+2
-1
@@ -101,7 +101,8 @@ class Replay extends LitElement
|
||||
this.dispatchEvent(new CustomEvent("update-title", {bubbles: true, composed: true, detail}));
|
||||
}
|
||||
|
||||
if (this.replayUrl && changedProperties.has("replayUrl")) {
|
||||
if ((this.replayUrl && changedProperties.has("replayUrl")) ||
|
||||
(this.replayTS && changedProperties.has("replayTS"))) {
|
||||
const data = {
|
||||
url: this.replayUrl,
|
||||
ts: this.replayTS,
|
||||
|
||||
Reference in New Issue
Block a user