Files
replayweb.page/src/replay.js
T
Ilya Kreymer 0ef8f955db 1.4.0 Release (#45)
* customizations for chooser, coll-index and appmain to support usage in archiveweb.page
- chooser: support panel/message view
- move apiPrefix to misc
- make coll-index extensible, overridable styles, filtering

* api changes: make configurable in coll-index, now using '/w/' instead of '/wabac/', '/c/' prefix for coll apis

* lint pass!

* bulma: update to 0.9.2, fix delete spinner with latest bulma

* ui fixes:
- show 'Loading Archives...' on initial load until first api response succeeds
- fix typo in oncontrollerchange not actually resolving the promise!

* embed sandbox: add other allow options, except top-nav
- first api load: keep loading message until first successful api load
- don't reset hash on purge to keep current page
- sw config: fix typo with latest wabac.js
- embed: add 'noSandbox' option to avoid adding sandbox to iframe, needed to show PDFs, add more allow rules (all except top-navigation)

* ui improvements:
- refresh: ensure location refresh from hitting enter in location bar always resets url
- pageentry doubleclick: ensure double-clicking on page in sidebar always triggers reload, even if current page
- pageentry typo fix: fix missing var causing selection error (fixes webrecorder/archivewebpage#25)
- lint: don't include 'event' as global
- use latest wabac.js 2.7.0-beta.1

* add CHANGES for 1.4.0, add 'noSandbox' to embedding options
- electron app: fix path to 'w/'
- bump electron to 11.4.3

* docs: autoload current version from package.json
- chooser: support .json files for multiwacz (experimental)
- update CHANGES.md
- add missing .eslintrc.js file

* bump version to 1.4.0
2021-04-25 16:45:40 -07:00

323 lines
8.7 KiB
JavaScript

import { LitElement, html, css } from "lit-element";
import { wrapCss, rwpLogo } from "./misc";
// ===========================================================================
class Replay extends LitElement
{
constructor() {
super();
this.replayUrl = "";
this.replayTS = "";
this.url = "";
this.ts = "";
this.title = "";
this.collInfo = null;
this.showAuth = false;
this.reauthWait = null;
this.authFileHandle = null;
}
static get properties() {
return {
collInfo: {type: Object },
sourceUrl: { type: String },
// external url set from parent
url: { type: String },
ts: { type: String },
// actual replay url
replayUrl: { type: String },
replayTS: { type: String },
title: { type: String },
iframeUrl: { type: String },
showAuth: { type: Boolean },
authFileHandle: { type: Object }
};
}
firstUpdated() {
window.addEventListener("message", (event) => this.onReplayMessage(event));
navigator.serviceWorker.addEventListener("message", (event) => this.handleAuthMessage(event));
}
async handleAuthMessage(event) {
if (event.data.type === "authneeded" && this.collInfo && event.data.coll === this.collInfo.coll) {
if (event.data.fileHandle) {
this.authFileHandle = event.data.fileHandle;
try {
if (await this.authFileHandle.requestPermission({mode: "read"}) === "granted") {
this.showAuth = false;
this.reauthWait = null;
this.refresh();
return;
}
} catch (e) {
console.warn(e);
}
} else {
this.authFileHandle = null;
}
if (this.reauthWait) {
await this.reauthWait;
} else {
this.showAuth = true;
}
}
}
doSetIframeUrl() {
this.iframeUrl = this.url ? `${this.collInfo.replayPrefix}/${this.ts || ""}mp_/${this.url}` : "";
}
updated(changedProperties) {
if (changedProperties.has("sourceUrl") || changedProperties.has("collInfo")) {
this.reauthWait = null;
}
if (this.url &&
((this.replayUrl != this.url) || (this.replayTS != this.ts)) &&
(changedProperties.has("url") || changedProperties.has("ts"))) {
this.replayUrl = this.url;
this.replayTS = this.ts;
this.showAuth = false;
this.reauthWait = null;
this.doSetIframeUrl();
}
if (this.iframeUrl && changedProperties.has("iframeUrl")) {
this.waitForLoad();
const detail = {title: "Archived Page", replayTitle: false};
this.dispatchEvent(new CustomEvent("update-title", {bubbles: true, composed: true, detail}));
}
if (this.replayUrl && changedProperties.has("replayUrl")) {
const data = {
url: this.replayUrl,
ts: this.replayTS,
};
this.dispatchEvent(new CustomEvent("coll-tab-nav", {detail: {replaceLoc: true, data}}));
}
}
setDisablePointer(disable) {
const iframe = this.renderRoot.querySelector("iframe");
if (iframe) {
iframe.style.pointerEvents = disable ? "none" : "all";
}
}
onReplayMessage(event) {
const iframe = this.renderRoot.querySelector("iframe");
if (iframe && event.source === iframe.contentWindow) {
if (event.data.wb_type === "load" || event.data.wb_type === "replace-url") {
this.replayTS = event.data.ts;
this.replayUrl = event.data.url;
this.title = event.data.title || this.title;
this.clearLoading(iframe.contentWindow);
if (event.data.icons) {
const icons = event.data.icons;
this.dispatchEvent(new CustomEvent("replay-favicons", {bubbles: true, composed: true, detail: {icons}}));
}
} else if (event.data.wb_type === "title") {
this.title = event.data.title;
}
}
if (this.title) {
const detail = {title: this.title, replayTitle: true};
this.dispatchEvent(new CustomEvent("update-title", {bubbles: true, composed: true, detail}));
}
}
onReAuthed(event) {
this.reauthWait = (async () => {
if (!this.authFileHandle) {
// google drive reauth
const headers = event.detail.headers;
await fetch(`${this.collInfo.apiPrefix}/updateAuth`, {
method: "POST",
body: JSON.stringify({headers})
});
} else {
if (await this.authFileHandle.requestPermission({mode: "read"}) !== "granted") {
this.reauthWait = null;
return;
}
this.authFileHandle = null;
}
if (this.showAuth) {
this.showAuth = false;
this.reauthWait = null;
}
this.refresh();
})();
}
waitForLoad() {
this.setLoading();
this._loadPoll = window.setInterval(() => {
const iframe = this.renderRoot.querySelector("iframe");
if (!iframe || !iframe.contentDocument || !iframe.contentWindow ||
(iframe.contentDocument.readyState === "complete" && !iframe.contentWindow._WBWombat)) {
this.clearLoading(iframe && iframe.contentWindow);
}
}, 5000);
}
clearLoading(iframeWin) {
this.dispatchEvent(new CustomEvent("replay-loading", {detail: {loading: false}}));
if (this._loadPoll) {
window.clearInterval(this._loadPoll);
this._loadPoll = null;
}
if (iframeWin) {
iframeWin.addEventListener("beforeunload", () => {
this.setLoading();
});
}
}
setLoading() {
this.dispatchEvent(new CustomEvent("replay-loading", {detail: {loading: true}}));
}
refresh() {
const iframe = this.renderRoot.querySelector("iframe");
if (!iframe) {
return;
}
const oldIframeUrl = this.iframeUrl;
// set iframe url to expected, refresh if same url
this.doSetIframeUrl();
if (oldIframeUrl === this.iframeUrl) {
this.waitForLoad();
iframe.contentWindow.location.href = this.iframeUrl;
}
}
static get styles() {
return wrapCss(css`
:host {
display: flex;
flex-direction: column;
height: 100%;
}
.iframe-container {
position: relative;
width: 100%;
height: 100%;
border: 0px;
}
.iframe-main {
position: absolute;
top: 0px;
left: 0px;
right: 0px;
bottom: 0px;
width: 100%;
height: 100%;
}
.intro-panel .panel-heading {
font-size: 1.0em;
display: inline-block;
}
.iframe-main.modal-bg {
z-index: 200;
background-color: rgba(10, 10, 10, 0.70)
}
#wrlogo {
vertical-align: middle;
}
.intro-panel .panel-block {
padding: 1.0em;
flex-direction: column;
line-height: 2.5em;
}
div.intro-panel.panel {
min-width: 40%;
display: flex;
flex-direction: column;
margin: 3em;
background-color: white;
}
`);
}
render() {
const title = `Replay of ${this.title ? `${this.title}:` :""} ${this.url}`;
return html`
<h1 id="replay-heading" class="is-sr-only">${title}</h1>
${!this.iframeUrl ? html`
<div class="panel intro-panel">
<p class="panel-heading">Replay Web Page</p>
<div class="panel-block">
<p>Enter a URL above to replay it from the web archive!</p>
<p>(Or, check out <a href="#view=pages">Pages</a> or <a href="#view=resources">URLs</a> to explore the contents of this archive.)</p>
</div>
</div>` : html`
<div class="iframe-container">
<iframe class="iframe-main" @message="${this.onReplayMessage}" allow="autoplay 'self'; fullscreen" allowfullscreen
src="${this.iframeUrl}" title="${title}"></iframe>
${this.showAuth ? html`
<div class="iframe-main modal-bg">
<div class="panel intro-panel">
<p class="panel-heading">
<fa-icon id="wrlogo" size="1.5rem" .svg=${rwpLogo} aria-hidden="true"></fa-icon>
Authorization Needed
</p>
<div class="panel-block">
${this.authFileHandle ? html`
<p>This archive is loaded from a local file: <b>${this.authFileHandle.name}</b></p>
<p>The browser needs to confirm your permission to continue loading from this file.</p>
<button class="button is-warning is-rounded" @click="${this.onReAuthed}">Show Confirmation</button>
` : html`
<wr-gdrive
.sourceUrl="${this.sourceUrl}"
.state="trymanual"
.reauth="${true}"
@load-ready="${this.onReAuthed}"/>`}
</div>
</div>
</div>
`: ""}
</div>
`}`;
}
}
customElements.define("wr-coll-replay", Replay);
export { Replay };