diff --git a/src/frontend.js b/src/frontend.js
index a3ea706..ce9321a 100644
--- a/src/frontend.js
+++ b/src/frontend.js
@@ -17,41 +17,23 @@ class AppMain extends LitElement
constructor() {
super();
- this.coll = "";
- this.collInfo = {};
- this.loadInfo = {};
- this.updateRoute();
+ this.sourceUrl = null;
+ this.pageParams = {};
}
-
static get properties() {
return {
- pageState: { type: String },
pageParams: { type: Object },
- coll: { type: String },
- collInfo: { type: Object }
+ sourceUrl: { type: String },
}
}
- async updateRoute() {
+ firstUpdated() {
+ this.initRoute();
+ }
+
+ initRoute() {
this.pageParams = new URLSearchParams(window.location.search);
- const source = this.pageParams.get("source");
- if (source) {
- const {url, coll} = await this.sourceToId(source);
-
- this.coll = coll;
-
- const resp = await fetch(`/wabac/api/${coll}`);
- if (resp.status != 200) {
- this.sourceUrl = source;
- this.pageState = "load";
- } else {
- const json = await resp.json();
- this.collInfo = json;
- this.pageState = "coll";
- }
- return;
- }
// Google Drive
let state = this.pageParams.get("state");
@@ -72,53 +54,22 @@ class AppMain extends LitElement
}
}
-
- this.pageState = "index";
- }
-
- async sourceToId(url) {
- try {
- url = new URL(url, window.location.origin).href;
- } catch(e) {}
-
- const digest = await digestMessage(url, 'SHA-256');
- const coll = "id-" + digest.slice(0, 12);
- return {url, coll};
- }
-
- onCollLoaded(event) {
- this.updateRoute();
+ this.sourceUrl = this.pageParams.get("source") || "";
}
render() {
return html`
-
+
- ${this.renderContent()}
+ ${this.sourceUrl ? html`
+
+ ` : this.sourceUrl === "" ? html`` : html``}
`;
}
-
- renderContent() {
- switch (this.pageState) {
- case "load":
- return html``;
-
- case "coll":
- return html``;
-
- case "index":
- return html``;
-
- default:
- return html``;
- }
- }
-
- //createRenderRoot() { return this; }
}
// ===========================================================================
@@ -247,8 +198,6 @@ class WrLoader extends LitElement
return html``;
}
}
-
- //createRenderRoot() { return this; }
}
// ===========================================================================
@@ -266,33 +215,7 @@ class WrIndex extends LitElement
}
static get styles() {
- return css`
- fa-icon {
- cursor: pointer;
- display: inline-flex;
- }
-
- fa-icon.fa-spin {
- -webkit-animation: fa-spin 2s infinite linear;
- animation: fa-spin 2s infinite linear; }
- }
-
- @-webkit-keyframes fa-spin {
- 0% {
- -webkit-transform: rotate(0deg);
- transform: rotate(0deg); }
- 100% {
- -webkit-transform: rotate(360deg);
- transform: rotate(360deg); } }
-
- @keyframes fa-spin {
- 0% {
- -webkit-transform: rotate(0deg);
- transform: rotate(0deg); }
- 100% {
- -webkit-transform: rotate(360deg);
- transform: rotate(360deg); } }
- `;
+ return css``;
}
firstUpdated() {
@@ -353,8 +276,6 @@ class WrIndex extends LitElement
`;
}
-
- //createRenderRoot() { return this; }
}
// ===========================================================================
@@ -362,145 +283,310 @@ class WrColl extends LitElement
{
constructor() {
super();
+ this.sourceUrl = null;
+
+ this.baseApiPrefix = "/wabac/api";
+ this.baseReplayPrefix = "/wabac";
+ this.apiPrefix = "";
+ this.replayPrefix = "";
+
this.coll = "";
- this.offset = 0;
- this.currList = 0;
- this.bookmarks = {};
- this.collInfo = {lists: []};
- this.currList = null;
- this.lastST = 0;
+
+ this.collInfo = null;
this.hasCurated = false;
- this.currTab = "";
- this.clickTime = 0;
+ this.tabData = {};
}
static get properties() {
return {
- hasCurated: { type: Boolean },
+ sourceUrl: { type: String },
+
collInfo: { type: Object },
coll: { type: String },
- bookmarks: { type: Object },
- currList: { type: Number },
- currTab: { type: String }
- }
- }
- async doLoadBookmarks() {
- this.hasCurated = (this.collInfo.lists && this.collInfo.lists.length);
- this.bookmarks = {};
+ hasCurated: { type: Boolean },
- if (!this.hasCurated) {
- return;
- }
-
- const resp = await fetch(`/wabac/api/${this.coll}/bookmarks?offset=${this.offset}`);
- const json = await resp.json();
-
- this.total = json.total;
-
- for (const bookmark of json.bookmarks) {
- if (!this.bookmarks[bookmark.list]) {
- this.bookmarks[bookmark.list] = [];
- }
- this.bookmarks[bookmark.list].push(bookmark);
+ tabData: { type: Object }
}
}
firstUpdated() {
- window.addEventListener("hashchange", () => this.onHashChange());
+ window.addEventListener("hashchange", (event) => this.onHashChange(event));
+
+ this.doUpdateInfo().then(() => this.onHashChange());
+ }
+
+ async sourceToId(url) {
+ try {
+ url = new URL(url, window.location.origin).href;
+ } catch(e) {}
+
+ const digest = await digestMessage(url, 'SHA-256');
+ const coll = "id-" + digest.slice(0, 12);
+ return {url, coll};
+ }
+
+ updated(changedProperties) {
+ if (changedProperties.get("sourceUrl")) {
+ this.doUpdateInfo();
+ }
+ if (changedProperties.get("tabData")) {
+ this._manualHash = true;
+ // don't add empty params to shorten query
+ Object.keys(this.tabData).
+ forEach(key => !this.tabData[key] && delete this.tabData[key]);
+ window.location.hash = "#" + new URLSearchParams(this.tabData).toString();
+ }
+ }
+
+ async doUpdateInfo() {
+ const {url, coll} = await this.sourceToId(this.sourceUrl);
+
+ this.coll = coll;
+
+ const apiPrefix = this.baseApiPrefix + "/" + coll;
+ const replayPrefix = this.baseReplayPrefix + "/" + coll;
+
+ const resp = await fetch(apiPrefix);
+
+ if (resp.status != 200) {
+ this.collInfo = {};
+ this.hasCurated = false;
+ return;
+ }
+
+ const json = await resp.json();
+
+ this.collInfo = {apiPrefix, replayPrefix, coll, ...json};
if (!this.collInfo.title) {
this.collInfo.title = "Archive from " + this.collInfo.displayName;
}
- this.doLoadBookmarks().then(() => this.onHashChange());
+ this.hasCurated = (this.collInfo.lists && this.collInfo.lists.length);
}
- onHashChange() {
- const hash = window.location.hash;
- if (!hash) {
- this.currTab = this.hasCurated ? "curated" : "resources";
+ onCollLoaded(event) {
+ this.doUpdateInfo();
+ }
+
+ onHashChange(event) {
+ if (this._manualHash) {
+ this._manualHash = false;
return;
}
-
- if (hash === "#resources") {
- this.currTab = "resources";
- } else if (hash === "#curated") {
- this.currTab = "curated";
+ const hash = window.location.hash;
+ if (hash) {
+ this.tabData = Object.fromEntries(new URLSearchParams(hash.slice(1)).entries());
}
-
- if (hash.startsWith("#list-")) {
- const id = hash.slice(6);
- this.currTab = "curated";
- this.scrollToList(id);
- }
- }
-
- updated(changedProperties) {
- if (changedProperties.get("lists")) {
- this.doLoadBookmarks();
+ if (!this.tabData.currTab) {
+ this.tabData = {...this.tabData, currTab: this.hasCurated ? "curated" : "resources"};
}
}
onTabClick(event) {
event.preventDefault();
const hash = event.target.getAttribute("href");
- //this.currTab = hash.slice(1);
- window.location.hash = hash;
+ this.tabData = {...this.tabData, currTab: hash.slice(1)};
return false;
}
+ onCollTabNav(event) {
+ this.tabData = {...this.tabData, ...event.detail};
+ }
+
render() {
return html`
-
+ ${this.renderColl()}
`;
}
+ renderColl() {
+ if (this.collInfo && !this.collInfo.coll) {
+ return html``;
+ } else if (this.collInfo) {
+ return html`
+ `;
+ } else {
+ return html``;
+ }
+ }
+
+ renderCollTabs() {
+ return html`
+
+
+
+
+
+ ${this.tabData.currTab === "replay" ? html`
+
+
+ ` : ``}
+ `;
+
+
+ switch (this.tabData.currTab) {
+ case "curated":
+ return html`
+ `;
+
+ case "resources":
+ return html`
+
+ `;
+
+ default:
+ return html``;
+ }
+ }
+}
+
+
+// ===========================================================================
+class WrCuratedPages extends LitElement
+{
+ constructor() {
+ super();
+
+ this.collInfo = null;
+
+ this.currList = 0;
+
+ this.curatedPages = {};
+
+ this.offset = 0;
+ this.lastST = 0;
+ this.clickTime = 0;
+ }
+
+ static get properties() {
+ return {
+ collInfo: { type: Object },
+
+ curatedPages: { type: Object },
+ currList: { type: Number },
+ }
+ }
+
+ static get styles() {
+ return css`
+ .column {
+ max-height: calc(100vh - 145px);
+ }
+
+ #content {
+ margin-top: 10px;
+ max-height: calc(100vh - 155px);
+ }
+ `;
+ }
+
+ firstUpdated() {
+ this.doLoadCurated();
+ }
+
+ updated(changedProperties) {
+ if (changedProperties.get("collInfo")) {
+ this.doLoadCurated();
+ }
+ }
+
+ async doLoadCurated() {
+ this.curatedPages = {};
+
+ const resp = await fetch(`${this.collInfo.apiPrefix}/curatedPages?offset=${this.offset}`);
+ const json = await resp.json();
+
+ this.total = json.total;
+
+ for (const curated of json.curatedPages) {
+ if (!this.curatedPages[curated.list]) {
+ this.curatedPages[curated.list] = [];
+ }
+ this.curatedPages[curated.list].push(curated);
+ }
+
+ if (this.currList) {
+ this.scrollToList(this.currList);
+ }
+ }
+
+ render() {
+ return html`
+
+
+
+
+
+
+
+ ${this.collInfo.title}
+ ${this.collInfo.desc ? unsafeHTML(marked(this.collInfo.desc)) : ''}
+
+ ${this.renderContent()}
+
+
+ `;
+ }
+
+ onReplay(event) {
+ event.preventDefault();
+ const detail = {replayUrl: event.currentTarget.getAttribute("data-url"),
+ replayTS: event.currentTarget.getAttribute("data-ts"),
+ currTab: "replay"
+ }
+ this.dispatchEvent(new CustomEvent("coll-tab-nav", {detail}));
+ return false;
+ }
+
renderContent() {
return html`
${this.collInfo.lists.map((list, i) => html`
@@ -510,15 +596,15 @@ class WrColl extends LitElement
${list.desc}
- ${this.bookmarks[list.id] ? this.bookmarks[list.id].map((b) => html`
+ ${this.curatedPages[list.id] ? this.curatedPages[list.id].map((p) => html`
-
`) : html``}
@@ -530,11 +616,14 @@ class WrColl extends LitElement
}
onClickScroll(event) {
+ event.preventDefault();
this.scrollToList(event.target.getAttribute("data-list"));
+ return false;
}
scrollToList(id) {
this.currList = Number(id);
+ this.dispatchEvent(new CustomEvent("coll-tab-nav", {detail: {currList: this.currList || undefined}}));
const opts = {behavior: "smooth", block: "start", inline: "nearest"};
this.clickTime = new Date().getTime();
this.renderRoot.getElementById("list-" + this.currList).scrollIntoView(opts);
@@ -565,6 +654,7 @@ class WrColl extends LitElement
if (next && next != curr) {
if (next.id.startsWith("list-")) {
this.currList = Number(next.id.slice(5));
+ this.dispatchEvent(new CustomEvent("coll-tab-nav", {detail: {currList: this.currList}}));
}
}
@@ -578,12 +668,6 @@ class WrColl extends LitElement
sel.scrollIntoView(opts);
}
}
-
- bookmarkUrl(b) {
- return `/wabac/${this.coll}/${getTS(b.date)}/${b.url}`;
- }
-
- //createRenderRoot() { return this; }
}
// ===========================================================================
@@ -591,15 +675,19 @@ class WrResources extends LitElement
{
constructor() {
super();
+ this.collInfo = null;
+
this.currMime = "text/html,text/xhtml";
- this.results = [];
+ this.urlSearch = "";
+ this.urlSearchType = "";
+
this.filteredResults = [];
+
+ this.results = [];
+
this.tryMore = false;
this.loading = false;
- this.urlSearch = "";
- this.urlSearchType = "exact";
-
this.filters = [
{name: "HTML", filter: "text/html,text/xhtml"},
{name: "Images", filter: "image/"},
@@ -611,17 +699,27 @@ class WrResources extends LitElement
static get properties() {
return {
- coll: { type: String },
+ collInfo: { type: Object },
currMime: { type: String },
- //filterText: { type: String },
+ urlSearch: { type: String },
+ urlSearchType: { type: String },
filteredResults: { type: Array },
- //loading: { type: Boolean }
}
}
static get styles() {
return css`
-
+ :host {
+ display: flex;
+ flex-direction: column;
+ }
+ .notification {
+ width: 100%;
+ }
+ .main-scroll {
+ max-height: calc(100vh - 295px);
+ width: 100vw;
+ }
table {
table-layout: fixed;
word-wrap: break-word;
@@ -640,8 +738,16 @@ class WrResources extends LitElement
.col-ts {
width: 16%;
max-width: 16%;
+ min-width: 16%;
word-break: break-word;
}
+ td.col-mime {
+ width: 10%;
+ word-break: break-word;
+ }
+ td.col-status {
+ text-align: center;
+ }
.loading-cog {
width: 100vw;
display: flex;
@@ -658,13 +764,20 @@ class WrResources extends LitElement
const url = (this.urlSearchType !== "contains" ? this.urlSearch : "");
const prefix = url && this.urlSearchType === "prefix" ? 1 : 0;
+ const detail = {urlSearch: this.urlSearch,
+ currMime: this.currMime,
+ urlSearchType: this.urlSearchType};
+
+ this.dispatchEvent(new CustomEvent("coll-tab-nav", {detail}));
+
const params = new URLSearchParams({
mime: this.currMime,
url,
prefix,
count
}).toString();
- let resp = await fetch(`/wabac/api/${this.coll}/urls?${params}`);
+
+ let resp = await fetch(`${this.collInfo.apiPrefix}/urls?${params}`);
resp = await resp.json();
this.results = resp.urls;
this.tryMore = (resp.urls.length === count);
@@ -696,7 +809,7 @@ class WrResources extends LitElement
count
}).toString();
- let resp = await fetch(`/wabac/api/${this.coll}/urls?${params}`);
+ let resp = await fetch(`${this.collInfo.apiPrefix}/urls?${params}`);
resp = await resp.json();
this.results = this.results.concat(resp.urls);
console.log(resp.urls.length);
@@ -705,7 +818,7 @@ class WrResources extends LitElement
this.loading = false;
}
- onChangeSearch(event) {
+ onChangeTypeSearch(event) {
this.currMime = event.target.value;
this.doLoadResources();
}
@@ -746,9 +859,9 @@ class WrResources extends LitElement
`;
}
+
+ onReplay(event) {
+ event.preventDefault();
+ const detail = {replayUrl: event.currentTarget.getAttribute("data-url"),
+ replayTS: event.currentTarget.getAttribute("data-ts"),
+ currTab: "replay"
+ }
+ this.dispatchEvent(new CustomEvent("coll-tab-nav", {detail}));
+ return false;
+ }
+}
+
+// ===========================================================================
+class WrReplayPage extends LitElement
+{
+ static get properties() {
+ return {
+ collInfo: {type: Object },
+
+ replayUrl: { type: String },
+ replayTS: { type: String }
+ }
+ }
+
+ static get styles() {
+ return css`
+ host: {
+ display: flex;
+ }
+
+ iframe {
+ width: 100vw;
+ height: calc(100vh - 150px);
+ }
+ `;
+ }
+
+ render() {
+ return html`
+
+ `;
+ }
}
// ===========================================================================
@@ -876,18 +1031,56 @@ class WrGdrive extends LitElement
}
}
+// ===========================================================================
+class WrCollProxy extends LitElement
+{
+ constructor() {
+ super();
+ this.currTab = "replay";
+ this.replayTS = "";
+ }
+
+ static get properties() {
+ return {
+ replayUrl: { type: String },
+ replayTS: { type: String },
+ source: { type: String },
+
+ paramString: { type: String }
+ }
+ }
+
+ firstUpdated() {
+ this.paramString = new URLSearchParams({
+ replayUrl: this.replayUrl,
+ replayTS: this.replayTS,
+ source: this.source,
+ currTab: this.currTab
+ });
+ }
+
+ render() {
+ return html`
+
+ `;
+ }
+}
+
// ===========================================================================
// ===========================================================================
async function main() {
- const swPromise = initSW("swonly.js?replayPrefix=wabac&stats=true");
+ const swPromise = initSW("./dist/swonly.js?replayPrefix=wabac&stats=true");
await swPromise;
customElements.define("app-main", AppMain);
customElements.define("wr-index", WrIndex);
customElements.define("wr-loader", WrLoader);
customElements.define("wr-coll", WrColl);
+ customElements.define("wr-coll-proxy", WrCollProxy);
customElements.define("wr-coll-resources", WrResources);
+ customElements.define("wr-coll-curated", WrCuratedPages);
+ customElements.define("wr-replay-page", WrReplayPage);
customElements.define("wr-gdrive", WrGdrive);
}