aboutsummaryrefslogtreecommitdiff
path: root/ext/js/display/display-history.js
diff options
context:
space:
mode:
Diffstat (limited to 'ext/js/display/display-history.js')
-rw-r--r--ext/js/display/display-history.js73
1 files changed, 69 insertions, 4 deletions
diff --git a/ext/js/display/display-history.js b/ext/js/display/display-history.js
index a983346c..f9d2e35d 100644
--- a/ext/js/display/display-history.js
+++ b/ext/js/display/display-history.js
@@ -18,26 +18,39 @@
import {EventDispatcher, generateId, isObject} from '../core.js';
+/**
+ * @augments EventDispatcher<import('display-history').EventType>
+ */
export class DisplayHistory extends EventDispatcher {
+ /**
+ * @param {{clearable?: boolean, useBrowserHistory?: boolean}} details
+ */
constructor({clearable=true, useBrowserHistory=false}) {
super();
+ /** @type {boolean} */
this._clearable = clearable;
+ /** @type {boolean} */
this._useBrowserHistory = useBrowserHistory;
+ /** @type {Map<string, import('display-history').Entry>} */
this._historyMap = new Map();
const historyState = history.state;
const {id, state} = isObject(historyState) ? historyState : {id: null, state: null};
+ /** @type {import('display-history').Entry} */
this._current = this._createHistoryEntry(id, location.href, state, null, null);
}
+ /** @type {?import('display-history').EntryState} */
get state() {
return this._current.state;
}
+ /** @type {?import('display-history').EntryContent} */
get content() {
return this._current.content;
}
+ /** @type {boolean} */
get useBrowserHistory() {
return this._useBrowserHistory;
}
@@ -46,31 +59,54 @@ export class DisplayHistory extends EventDispatcher {
this._useBrowserHistory = value;
}
+ /** @type {boolean} */
+ get clearable() { return this._clearable; }
+ set clearable(value) { this._clearable = value; }
+
+ /** */
prepare() {
window.addEventListener('popstate', this._onPopState.bind(this), false);
}
+ /**
+ * @returns {boolean}
+ */
hasNext() {
return this._current.next !== null;
}
+ /**
+ * @returns {boolean}
+ */
hasPrevious() {
return this._current.previous !== null;
}
+ /** */
clear() {
if (!this._clearable) { return; }
this._clear();
}
+ /**
+ * @returns {boolean}
+ */
back() {
return this._go(false);
}
+ /**
+ * @returns {boolean}
+ */
forward() {
return this._go(true);
}
+ /**
+ * @param {?import('display-history').EntryState} state
+ * @param {?import('display-history').EntryContent} content
+ * @param {string} [url]
+ */
pushState(state, content, url) {
if (typeof url === 'undefined') { url = location.href; }
@@ -80,6 +116,11 @@ export class DisplayHistory extends EventDispatcher {
this._updateHistoryFromCurrent(!this._useBrowserHistory);
}
+ /**
+ * @param {?import('display-history').EntryState} state
+ * @param {?import('display-history').EntryContent} content
+ * @param {string} [url]
+ */
replaceState(state, content, url) {
if (typeof url === 'undefined') { url = location.href; }
@@ -89,11 +130,16 @@ export class DisplayHistory extends EventDispatcher {
this._updateHistoryFromCurrent(true);
}
+ /** */
_onPopState() {
this._updateStateFromHistory();
this._triggerStateChanged(false);
}
+ /**
+ * @param {boolean} forward
+ * @returns {boolean}
+ */
_go(forward) {
if (this._useBrowserHistory) {
if (forward) {
@@ -111,10 +157,16 @@ export class DisplayHistory extends EventDispatcher {
return true;
}
+ /**
+ * @param {boolean} synthetic
+ */
_triggerStateChanged(synthetic) {
- this.trigger('stateChanged', {synthetic});
+ this.trigger('stateChanged', /** @type {import('display-history').StateChangedEvent} */ ({synthetic}));
}
+ /**
+ * @param {boolean} replace
+ */
_updateHistoryFromCurrent(replace) {
const {id, state, url} = this._current;
if (replace) {
@@ -125,6 +177,7 @@ export class DisplayHistory extends EventDispatcher {
this._triggerStateChanged(true);
}
+ /** */
_updateStateFromHistory() {
let state = history.state;
let id = null;
@@ -151,24 +204,36 @@ export class DisplayHistory extends EventDispatcher {
this._clear();
}
+ /**
+ * @param {unknown} id
+ * @param {string} url
+ * @param {?import('display-history').EntryState} state
+ * @param {?import('display-history').EntryContent} content
+ * @param {?import('display-history').Entry} previous
+ * @returns {import('display-history').Entry}
+ */
_createHistoryEntry(id, url, state, content, previous) {
- if (typeof id !== 'string') { id = this._generateId(); }
+ /** @type {import('display-history').Entry} */
const entry = {
- id,
+ id: typeof id === 'string' ? id : this._generateId(),
url,
next: null,
previous,
state,
content
};
- this._historyMap.set(id, entry);
+ this._historyMap.set(entry.id, entry);
return entry;
}
+ /**
+ * @returns {string}
+ */
_generateId() {
return generateId(16);
}
+ /** */
_clear() {
this._historyMap.clear();
this._historyMap.set(this._current.id, this._current);