Convert the history code to use proper private methods

This allows us to get rid of the `@private` JSDoc comments, which were
used to convey intent back when proper private methods could not be used
yet in JavaScript. This improves code readability/maintenance and enables
better usage validation by tooling such as ESlint.
This commit is contained in:
Tim van der Meij 2024-04-11 15:17:30 +02:00
parent e78ce74b5f
commit a45aec6393
No known key found for this signature in database
GPG key ID: 8C3FD2925A5F2762

View file

@ -102,7 +102,7 @@ class PDFHistory {
this._updateUrl = updateUrl === true; this._updateUrl = updateUrl === true;
this._initialized = true; this._initialized = true;
this._bindEvents(); this.#bindEvents();
const state = window.history.state; const state = window.history.state;
this._popStateInProgress = false; this._popStateInProgress = false;
@ -114,19 +114,19 @@ class PDFHistory {
this._destination = null; this._destination = null;
this._position = null; this._position = null;
if (!this._isValidState(state, /* checkReload = */ true) || resetHistory) { if (!this.#isValidState(state, /* checkReload = */ true) || resetHistory) {
const { hash, page, rotation } = this._parseCurrentHash( const { hash, page, rotation } = this.#parseCurrentHash(
/* checkNameddest = */ true /* checkNameddest = */ true
); );
if (!hash || reInitialized || resetHistory) { if (!hash || reInitialized || resetHistory) {
// Ensure that the browser history is reset on PDF document load. // Ensure that the browser history is reset on PDF document load.
this._pushOrReplaceState(null, /* forceReplace = */ true); this.#pushOrReplaceState(null, /* forceReplace = */ true);
return; return;
} }
// Ensure that the browser history is initialized correctly when // Ensure that the browser history is initialized correctly when
// the document hash is present on PDF document load. // the document hash is present on PDF document load.
this._pushOrReplaceState( this.#pushOrReplaceState(
{ hash, page, rotation }, { hash, page, rotation },
/* forceReplace = */ true /* forceReplace = */ true
); );
@ -136,7 +136,7 @@ class PDFHistory {
// The browser history contains a valid entry, ensure that the history is // The browser history contains a valid entry, ensure that the history is
// initialized correctly on PDF document load. // initialized correctly on PDF document load.
const destination = state.destination; const destination = state.destination;
this._updateInternalState( this.#updateInternalState(
destination, destination,
state.uid, state.uid,
/* removeTemporary = */ true /* removeTemporary = */ true
@ -166,10 +166,10 @@ class PDFHistory {
*/ */
reset() { reset() {
if (this._initialized) { if (this._initialized) {
this._pageHide(); // Simulate a 'pagehide' event when resetting. this.#pageHide(); // Simulate a 'pagehide' event when resetting.
this._initialized = false; this._initialized = false;
this._unbindEvents(); this.#unbindEvents();
} }
if (this._updateViewareaTimeout) { if (this._updateViewareaTimeout) {
clearTimeout(this._updateViewareaTimeout); clearTimeout(this._updateViewareaTimeout);
@ -199,7 +199,7 @@ class PDFHistory {
`"${explicitDest}" is not a valid explicitDest parameter.` `"${explicitDest}" is not a valid explicitDest parameter.`
); );
return; return;
} else if (!this._isValidPage(pageNumber)) { } else if (!this.#isValidPage(pageNumber)) {
// Allow an unset `pageNumber` if and only if the history is still empty; // Allow an unset `pageNumber` if and only if the history is still empty;
// please refer to the `this._destination.page = null;` comment above. // please refer to the `this._destination.page = null;` comment above.
if (pageNumber !== null || this._destination) { if (pageNumber !== null || this._destination) {
@ -238,7 +238,7 @@ class PDFHistory {
return; return;
} }
this._pushOrReplaceState( this.#pushOrReplaceState(
{ {
dest: explicitDest, dest: explicitDest,
hash, hash,
@ -269,7 +269,7 @@ class PDFHistory {
if (!this._initialized) { if (!this._initialized) {
return; return;
} }
if (!this._isValidPage(pageNumber)) { if (!this.#isValidPage(pageNumber)) {
console.error( console.error(
`PDFHistory.pushPage: "${pageNumber}" is not a valid page number.` `PDFHistory.pushPage: "${pageNumber}" is not a valid page number.`
); );
@ -285,8 +285,8 @@ class PDFHistory {
return; return;
} }
this._pushOrReplaceState({ this.#pushOrReplaceState({
// Simulate an internal destination, for `this._tryPushCurrentPosition`: // Simulate an internal destination, for `this.#tryPushCurrentPosition`:
dest: null, dest: null,
hash: `page=${pageNumber}`, hash: `page=${pageNumber}`,
page: pageNumber, page: pageNumber,
@ -312,7 +312,7 @@ class PDFHistory {
if (!this._initialized || this._popStateInProgress) { if (!this._initialized || this._popStateInProgress) {
return; return;
} }
this._tryPushCurrentPosition(); this.#tryPushCurrentPosition();
} }
/** /**
@ -324,7 +324,7 @@ class PDFHistory {
return; return;
} }
const state = window.history.state; const state = window.history.state;
if (this._isValidState(state) && state.uid > 0) { if (this.#isValidState(state) && state.uid > 0) {
window.history.back(); window.history.back();
} }
} }
@ -338,7 +338,7 @@ class PDFHistory {
return; return;
} }
const state = window.history.state; const state = window.history.state;
if (this._isValidState(state) && state.uid < this._maxUid) { if (this.#isValidState(state) && state.uid < this._maxUid) {
window.history.forward(); window.history.forward();
} }
} }
@ -362,10 +362,7 @@ class PDFHistory {
return this._initialized ? this._initialRotation : null; return this._initialized ? this._initialRotation : null;
} }
/** #pushOrReplaceState(destination, forceReplace = false) {
* @private
*/
_pushOrReplaceState(destination, forceReplace = false) {
const shouldReplace = forceReplace || !this._destination; const shouldReplace = forceReplace || !this._destination;
const newState = { const newState = {
fingerprint: this._fingerprint, fingerprint: this._fingerprint,
@ -381,7 +378,7 @@ class PDFHistory {
// history.state.chromecomState is managed by chromecom.js. // history.state.chromecomState is managed by chromecom.js.
newState.chromecomState = window.history.state.chromecomState; newState.chromecomState = window.history.state.chromecomState;
} }
this._updateInternalState(destination, newState.uid); this.#updateInternalState(destination, newState.uid);
let newUrl; let newUrl;
if (this._updateUrl && destination?.hash) { if (this._updateUrl && destination?.hash) {
@ -407,10 +404,7 @@ class PDFHistory {
} }
} }
/** #tryPushCurrentPosition(temporary = false) {
* @private
*/
_tryPushCurrentPosition(temporary = false) {
if (!this._position) { if (!this._position) {
return; return;
} }
@ -421,12 +415,12 @@ class PDFHistory {
} }
if (!this._destination) { if (!this._destination) {
this._pushOrReplaceState(position); this.#pushOrReplaceState(position);
return; return;
} }
if (this._destination.temporary) { if (this._destination.temporary) {
// Always replace a previous *temporary* position. // Always replace a previous *temporary* position.
this._pushOrReplaceState(position, /* forceReplace = */ true); this.#pushOrReplaceState(position, /* forceReplace = */ true);
return; return;
} }
if (this._destination.hash === position.hash) { if (this._destination.hash === position.hash) {
@ -460,22 +454,16 @@ class PDFHistory {
// To avoid "flooding" the browser history, replace the current entry. // To avoid "flooding" the browser history, replace the current entry.
forceReplace = true; forceReplace = true;
} }
this._pushOrReplaceState(position, forceReplace); this.#pushOrReplaceState(position, forceReplace);
} }
/** #isValidPage(val) {
* @private
*/
_isValidPage(val) {
return ( return (
Number.isInteger(val) && val > 0 && val <= this.linkService.pagesCount Number.isInteger(val) && val > 0 && val <= this.linkService.pagesCount
); );
} }
/** #isValidState(state, checkReload = false) {
* @private
*/
_isValidState(state, checkReload = false) {
if (!state) { if (!state) {
return false; return false;
} }
@ -508,10 +496,7 @@ class PDFHistory {
return true; return true;
} }
/** #updateInternalState(destination, uid, removeTemporary = false) {
* @private
*/
_updateInternalState(destination, uid, removeTemporary = false) {
if (this._updateViewareaTimeout) { if (this._updateViewareaTimeout) {
// When updating `this._destination`, make sure that we always wait for // When updating `this._destination`, make sure that we always wait for
// the next 'updateviewarea' event before (potentially) attempting to // the next 'updateviewarea' event before (potentially) attempting to
@ -531,26 +516,20 @@ class PDFHistory {
this._numPositionUpdates = 0; this._numPositionUpdates = 0;
} }
/** #parseCurrentHash(checkNameddest = false) {
* @private
*/
_parseCurrentHash(checkNameddest = false) {
const hash = unescape(getCurrentHash()).substring(1); const hash = unescape(getCurrentHash()).substring(1);
const params = parseQueryString(hash); const params = parseQueryString(hash);
const nameddest = params.get("nameddest") || ""; const nameddest = params.get("nameddest") || "";
let page = params.get("page") | 0; let page = params.get("page") | 0;
if (!this._isValidPage(page) || (checkNameddest && nameddest.length > 0)) { if (!this.#isValidPage(page) || (checkNameddest && nameddest.length > 0)) {
page = null; page = null;
} }
return { hash, page, rotation: this.linkService.rotation }; return { hash, page, rotation: this.linkService.rotation };
} }
/** #updateViewarea({ location }) {
* @private
*/
_updateViewarea({ location }) {
if (this._updateViewareaTimeout) { if (this._updateViewareaTimeout) {
clearTimeout(this._updateViewareaTimeout); clearTimeout(this._updateViewareaTimeout);
this._updateViewareaTimeout = null; this._updateViewareaTimeout = null;
@ -575,9 +554,9 @@ class PDFHistory {
) { ) {
// If the current destination was set through the user changing the hash // If the current destination was set through the user changing the hash
// of the document, we will usually not try to push the current position // of the document, we will usually not try to push the current position
// to the browser history; see `this._tryPushCurrentPosition()`. // to the browser history; see `this.#tryPushCurrentPosition()`.
// //
// To prevent `this._tryPushCurrentPosition()` from effectively being // To prevent `this.#tryPushCurrentPosition()` from effectively being
// reduced to a no-op in this case, we will assume that the position // reduced to a no-op in this case, we will assume that the position
// *did* in fact change if the 'updateviewarea' event was dispatched // *did* in fact change if the 'updateviewarea' event was dispatched
// more than `POSITION_UPDATED_THRESHOLD` times. // more than `POSITION_UPDATED_THRESHOLD` times.
@ -602,17 +581,14 @@ class PDFHistory {
// the viewer has been idle for `UPDATE_VIEWAREA_TIMEOUT` milliseconds. // the viewer has been idle for `UPDATE_VIEWAREA_TIMEOUT` milliseconds.
this._updateViewareaTimeout = setTimeout(() => { this._updateViewareaTimeout = setTimeout(() => {
if (!this._popStateInProgress) { if (!this._popStateInProgress) {
this._tryPushCurrentPosition(/* temporary = */ true); this.#tryPushCurrentPosition(/* temporary = */ true);
} }
this._updateViewareaTimeout = null; this._updateViewareaTimeout = null;
}, UPDATE_VIEWAREA_TIMEOUT); }, UPDATE_VIEWAREA_TIMEOUT);
} }
} }
/** #popState({ state }) {
* @private
*/
_popState({ state }) {
const newHash = getCurrentHash(), const newHash = getCurrentHash(),
hashChanged = this._currentHash !== newHash; hashChanged = this._currentHash !== newHash;
this._currentHash = newHash; this._currentHash = newHash;
@ -621,20 +597,20 @@ class PDFHistory {
(typeof PDFJSDev !== "undefined" && (typeof PDFJSDev !== "undefined" &&
PDFJSDev.test("CHROME") && PDFJSDev.test("CHROME") &&
state?.chromecomState && state?.chromecomState &&
!this._isValidState(state)) || !this.#isValidState(state)) ||
!state !state
) { ) {
// This case corresponds to the user changing the hash of the document. // This case corresponds to the user changing the hash of the document.
this._uid++; this._uid++;
const { hash, page, rotation } = this._parseCurrentHash(); const { hash, page, rotation } = this.#parseCurrentHash();
this._pushOrReplaceState( this.#pushOrReplaceState(
{ hash, page, rotation }, { hash, page, rotation },
/* forceReplace = */ true /* forceReplace = */ true
); );
return; return;
} }
if (!this._isValidState(state)) { if (!this.#isValidState(state)) {
// This should only occur in viewers with support for opening more than // This should only occur in viewers with support for opening more than
// one PDF document, e.g. the GENERIC viewer. // one PDF document, e.g. the GENERIC viewer.
return; return;
@ -666,7 +642,7 @@ class PDFHistory {
// Navigate to the new destination. // Navigate to the new destination.
const destination = state.destination; const destination = state.destination;
this._updateInternalState( this.#updateInternalState(
destination, destination,
state.uid, state.uid,
/* removeTemporary = */ true /* removeTemporary = */ true
@ -691,31 +667,25 @@ class PDFHistory {
}); });
} }
/** #pageHide() {
* @private
*/
_pageHide() {
// Attempt to push the `this._position` into the browser history when // Attempt to push the `this._position` into the browser history when
// navigating away from the document. This is *only* done if the history // navigating away from the document. This is *only* done if the history
// is empty/temporary, since otherwise an existing browser history entry // is empty/temporary, since otherwise an existing browser history entry
// will end up being overwritten (given that new entries cannot be pushed // will end up being overwritten (given that new entries cannot be pushed
// into the browser history when the 'unload' event has already fired). // into the browser history when the 'unload' event has already fired).
if (!this._destination || this._destination.temporary) { if (!this._destination || this._destination.temporary) {
this._tryPushCurrentPosition(); this.#tryPushCurrentPosition();
} }
} }
/** #bindEvents() {
* @private
*/
_bindEvents() {
if (this._boundEvents) { if (this._boundEvents) {
return; // The event listeners were already added. return; // The event listeners were already added.
} }
this._boundEvents = { this._boundEvents = {
updateViewarea: this._updateViewarea.bind(this), updateViewarea: this.#updateViewarea.bind(this),
popState: this._popState.bind(this), popState: this.#popState.bind(this),
pageHide: this._pageHide.bind(this), pageHide: this.#pageHide.bind(this),
}; };
this.eventBus._on("updateviewarea", this._boundEvents.updateViewarea); this.eventBus._on("updateviewarea", this._boundEvents.updateViewarea);
@ -723,10 +693,7 @@ class PDFHistory {
window.addEventListener("pagehide", this._boundEvents.pageHide); window.addEventListener("pagehide", this._boundEvents.pageHide);
} }
/** #unbindEvents() {
* @private
*/
_unbindEvents() {
if (!this._boundEvents) { if (!this._boundEvents) {
return; // The event listeners were already removed. return; // The event listeners were already removed.
} }