import { LitElement, html, css } from "lit";
import { wrapCss, rwpLogo, IS_APP, VERSION, clickOnSpacebarPress, apiPrefix, replayPrefix } from "./misc";
import { sourceToId, tsToDate, getPageDateTS } 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 Split from "split.js";
const RWP_SCHEME = "search://";
// ===========================================================================
class Coll extends LitElement
{
constructor() {
super();
this.sourceUrl = null;
this.inited = false;
this.isLoading = false;
this.coll = "";
this.collInfo = null;
this._replaceLoc = false;
this._locUpdateNeeded = false;
this._locationHash = "";
this.tabData = {};
this.url = "";
this.ts = "";
this.tabNames = ["pages", "story", "resources", "info"];
this.tabLabels = {
"pages": "Pages",
"story": "Story",
"resources": "URLs",
"info": "Archive Info",
};
this.menuActive = false;
this.hasStory = false;
this.editable = false;
this.showSidebar = localStorage.getItem("pages:showSidebar") === "1";
this.splitter = null;
this.isVisible = true;
this.favIconUrl = "";
this.appName = "ReplayWeb.page";
this.appLogo = rwpLogo;
}
static get properties() {
return {
inited: { type: Boolean },
sourceUrl: { type: String },
loadInfo: { type: Object, attribute: false },
showSidebar: { type: Boolean },
collInfo: { type: Object, attribute: false },
coll: { type: String },
hasStory: { type: Boolean },
isLoading: { type: Boolean },
tabData: { type: Object, attribute: false },
url: { type: String },
ts: { type: String },
isFullscreen: { type: Boolean },
menuActive: { type: Boolean },
embed: { type: String },
editable: { type: Boolean },
isVisible: { type: Boolean },
favIconUrl: {type: String },
appName: { type: String },
appLogo: { type: String },
};
}
firstUpdated() {
this.inited = true;
window.addEventListener("hashchange", (event) => this.onHashChange(event));
this.addEventListener("fullscreenchange", () => {
this.isFullscreen = !!document.fullscreenElement;
});
if (this.embed) {
this.observer = new IntersectionObserver((entries/*, observer*/) => {
this.isVisible = entries[0].isIntersecting;
});
this.observer.observe(this);
}
}
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._pollColl = setInterval(() => this.doUpdateInfo(true), 10000);
} else if (this._pollColl) {
clearInterval(this._pollColl);
}
}
if (changedProperties.has("tabData")) {
if (!this.collInfo || !this.collInfo.coll) {
return;
}
// don't add empty params to shorten query
Object.keys(this.tabData).
forEach(key => !this.tabData[key] && delete this.tabData[key]);
const newHash = "#" + new URLSearchParams(this.tabData).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");
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");
const replay = this.renderRoot.querySelector("wr-coll-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 coll = this.loadInfo && this.loadInfo.customColl;
if (!coll) {
const res = await sourceToId(this.sourceUrl);
coll = res.coll;
}
this.coll = coll;
const collApiPrefix = apiPrefix + "/c/" + coll;
const collReplayPrefix = replayPrefix + "/" + coll;
const resp = await fetch(collApiPrefix + "?all=1");
if (resp.status != 200) {
this.collInfo = {};
return;
}
const json = await resp.json();
this.collInfo = {
apiPrefix: collApiPrefix,
replayPrefix: collReplayPrefix,
coll,
...json
};
if (!this.collInfo.title) {
this.collInfo.title = this.collInfo.filename;
}
if (this.embed === "replayonly") {
this.showSidebar = false;
}
this.hasStory = this.collInfo.desc || this.collInfo.lists.length;
this.dispatchEvent(new CustomEvent("coll-loaded", {detail: {
collInfo: this.collInfo,
alreadyLoaded: true
}}));
this.onHashChange();
}
onCollLoaded(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.collInfo,
}}));
}
onCollUpdate(event) {
if (!this.editable) {
return;
}
this.collInfo = {...this.collInfo, ...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.collInfo.coll && !this.tabNames.includes(this.tabData.view)) {
const view = this.hasStory ? "story" : (this.editable || this.collInfo.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 (!isNaN(pageIndex) && pageIndex < this.collInfo.pages.length) {
const page = this.collInfo.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;
}
onCollTabNav(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(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;
}
#tabContents {
height: 100%;
width: 100%;
display: flex;
flex-direction: row;
min-height: 0px;
flex: auto;
}
${Coll.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 .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: 1em;
z-index: 10;
background: linear-gradient(90deg, rgba(255, 255, 255, 0), #FFF 15%, #FFF);
margin: -35px 0 0 0px;
padding-left: 3em;
line-height: 2;
}
.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%;
}
.info-bg {
background-color: whitesmoke;
width: 100%;
height: 100%;
display: flex;
}
.is-list {
margin: 1.0em;
background-color: whitesmoke;
}
#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 = {title: this.tabLabels[this.tabData.view], replayTitle: false};
this.dispatchEvent(new CustomEvent("update-title", {bubbles: true, composed: true, detail}));
}
if (this.collInfo && !this.collInfo.coll) {
return html`
Skipped
`; } dragStart() { const replay = this.renderRoot.querySelector("wr-coll-replay"); if (replay) { replay.setDisablePointer(true); } } dragEnd() { const replay = this.renderRoot.querySelector("wr-coll-replay"); if (replay) { replay.setDisablePointer(false); } } renderCollInfo() { return html`