diff --git a/Classes/Common/AbstractDocument.php b/Classes/Common/AbstractDocument.php index 30150904a6..84b83d3bcf 100644 --- a/Classes/Common/AbstractDocument.php +++ b/Classes/Common/AbstractDocument.php @@ -534,7 +534,7 @@ abstract protected function setPreloadedDocument($preloadedDocument): bool; * * @return array */ - public abstract function getAllFiles(); + abstract public function getAllFiles(): array; /** * This is a singleton class, thus an instance must be created by this method @@ -557,9 +557,10 @@ public static function &getInstance(string $location, array $settings = [], bool $iiif = null; if (!$forceReload) { + $instance = self::getDocumentCache($location); if (isset(self::$registry[$location])) { return self::$registry[$location]; - } elseif ($instance = self::getDocumentCache($location)) { + } elseif ($instance !== false) { self::$registry[$location] = $instance; return $instance; } @@ -1393,7 +1394,7 @@ public function toArray($uriBuilder, array $config = []) ]; foreach ($this->physicalStructureInfo[$this->physicalStructure[$page]]['files'] as $fileGrp => $fileId) { - if ($allFiles === null) { + if (!$allFiles) { $file = [ 'url' => $this->getFileLocation($fileId), 'mimetype' => $this->getFileMimeType($fileId), @@ -1416,11 +1417,13 @@ public function toArray($uriBuilder, array $config = []) ->reset() ->setTargetPageUid($GLOBALS['TSFE']->id) ->setCreateAbsoluteUri($forceAbsoluteUrl) - ->setArguments([ - 'eID' => 'tx_dlf_pageview_proxy', - 'url' => $file['url'], - 'uHash' => GeneralUtility::hmac($file['url'], 'PageViewProxy') - ]) + ->setArguments( + [ + 'eID' => 'tx_dlf_pageview_proxy', + 'url' => $file['url'], + 'uHash' => GeneralUtility::hmac($file['url'], 'PageViewProxy') + ] + ) ->build(); } diff --git a/Classes/Common/IiifManifest.php b/Classes/Common/IiifManifest.php index 600785d976..6c177bf994 100644 --- a/Classes/Common/IiifManifest.php +++ b/Classes/Common/IiifManifest.php @@ -422,7 +422,7 @@ public function getFileMimeType(string $id): string /** * @see AbstractDocument::getAllFiles() */ - public function getAllFiles() + public function getAllFiles(): array { $files = []; $canvases = $this->iiif->getDefaultCanvases(); diff --git a/Classes/Common/MetsDocument.php b/Classes/Common/MetsDocument.php index 1742f795dd..45580e9cb9 100644 --- a/Classes/Common/MetsDocument.php +++ b/Classes/Common/MetsDocument.php @@ -261,7 +261,7 @@ public function getFileMimeType(string $id): string /** * @see AbstractDocument::getAllFiles() */ - public function getAllFiles() + public function getAllFiles(): array { $files = []; $fileNodes = $this->mets->xpath('./mets:fileSec/mets:fileGrp/mets:file'); diff --git a/Classes/Controller/DocumentController.php b/Classes/Controller/DocumentController.php index 8925b10728..8d4c9ab784 100644 --- a/Classes/Controller/DocumentController.php +++ b/Classes/Controller/DocumentController.php @@ -13,7 +13,6 @@ namespace Kitodo\Dlf\Controller; use TYPO3\CMS\Core\Utility\GeneralUtility; -use TYPO3\CMS\Core\Utility\MathUtility; /** * Provide document JSON for client side access @@ -39,7 +38,7 @@ public function mainAction() // Quit without doing anything if required variables are not set. return; } - + $this->setPage(); $metadataUrl = null; @@ -49,11 +48,13 @@ public function mainAction() ->reset() ->setTargetPageUid((int) $this->settings['targetPidMetadata']) ->setCreateAbsoluteUri(true) - ->setArguments([ - 'tx_dlf' => [ - 'id' => $this->requestData['id'], - ], - ]) + ->setArguments( + [ + 'tx_dlf' => [ + 'id' => $this->requestData['id'], + ], + ] + ) ->build(); } @@ -65,7 +66,7 @@ public function mainAction() ? array_merge($imageFileGroups, $fulltextFileGroups) : [], ]; - $tx_dlf_loaded = [ + $loaded = [ 'state' => [ 'documentId' => $this->requestData['id'], 'page' => $this->requestData['page'], @@ -83,7 +84,7 @@ public function mainAction() $docConfiguration = ' window.addEventListener("DOMContentLoaded", function() { - const tx_dlf_loaded = ' . json_encode($tx_dlf_loaded) . '; + const tx_dlf_loaded = ' . json_encode($loaded) . '; window.dispatchEvent(new CustomEvent("tx-dlf-documentLoaded", { detail: { docController: new dlfController(tx_dlf_loaded) @@ -116,17 +117,21 @@ protected function getUrlTemplate() // page: \d+ // double: 0|1 - $make = function ($page, $double, $pagegrid) { + $make = function ($page, $double, $pageGrid) { $result = $this->uriBuilder->reset() - ->setTargetPageUid($GLOBALS['TSFE']->id) + ->setTargetPageUid($this->configurationManager->getContentObject()->data['pid']) ->setCreateAbsoluteUri(!empty($this->settings['forceAbsoluteUrl']) ? true : false) - ->setArguments([ - 'tx_dlf' => array_merge($this->requestData, [ - 'page' => $page, - 'double' => $double, - 'pagegrid' => $pagegrid - ]), - ]) + ->setArguments( + [ + 'tx_dlf' => array_merge( + $this->requestData, [ + 'page' => $page, + 'double' => $double, + 'pagegrid' => $pageGrid + ] + ), + ] + ) ->build(); $cHashIdx = strpos($result, '&cHash='); @@ -154,7 +159,7 @@ protected function getUrlTemplate() if ($first[$i] === '2') { $placeholder = 'PAGE_NO'; - } else if ($first[$i] === '1') { + } elseif ($first[$i] === '1') { $placeholder = 'DOUBLE_PAGE'; } else { $placeholder = 'PAGE_GRID'; @@ -166,5 +171,4 @@ protected function getUrlTemplate() return $result; } - } diff --git a/Classes/Controller/MetadataController.php b/Classes/Controller/MetadataController.php index 2947d91db6..c74d5dc7a9 100644 --- a/Classes/Controller/MetadataController.php +++ b/Classes/Controller/MetadataController.php @@ -375,7 +375,7 @@ private function parseMetadata(int $i, string $name, $value, array &$metadata) : } } - /** + /** * Get metadata for given id array. * * @access private @@ -385,7 +385,8 @@ private function parseMetadata(int $i, string $name, $value, array &$metadata) : * * @return void */ - private function getIds($toc, &$output) { + private function getIds($toc, &$output) + { foreach ($toc as $entry) { $output[$entry['id']] = true; if (is_array($entry['children'])) { diff --git a/Resources/Public/JavaScript/PageView/Controller.js b/Resources/Public/JavaScript/PageView/Controller.js index 163b07390a..60391fefd4 100644 --- a/Resources/Public/JavaScript/PageView/Controller.js +++ b/Resources/Public/JavaScript/PageView/Controller.js @@ -17,21 +17,21 @@ class dlfController { /** @private */ this.doc = doc; - this.eventTarget.addEventListener('tx-dlf-stateChanged', this.onStateChanged.bind(this)); - window.addEventListener('popstate', this.onPopState.bind(this)); + this.eventTarget.addEventListener("tx-dlf-stateChanged", this.onStateChanged.bind(this)); + window.addEventListener("popstate", this.onPopState.bind(this)); // Set initial state, so that browser navigation also works initial page history.replaceState(/** @type {dlf.PageHistoryState} */({ - type: 'tx-dlf-page-state', + type: "tx-dlf-page-state", ...this.doc.state - }), ''); + }), ""); this.updateMultiPage(this.simultaneousPages); if (doc.metadataUrl !== null) { this.metadataPromise = fetch(doc.metadataUrl) - .then(response => response.text()) - .then(html => ({ + .then((response) => response.text()) + .then((html) => ({ htmlCode: html, })); } else { @@ -64,13 +64,16 @@ class dlfController { getVisiblePages(firstPageNo = this.doc.state.page) { const result = []; + for (let i = 0; i < this.simultaneousPages; i++) { const pageNo = firstPageNo + i; const pageObj = this.doc.document.pages[pageNo - 1]; + if (pageObj !== undefined) { result.push({ pageNo, pageObj }); } } + return result; } @@ -95,6 +98,7 @@ class dlfController { */ findFileByGroup(pageNo, fileGroups) { const pageObj = this.getPageByNo(pageNo); + if (pageObj === undefined) { return; } @@ -109,7 +113,7 @@ class dlfController { * @returns {dlf.ResourceLocator | undefined} */ findFileByKind(pageNo, fileKind) { - return this.findFileByGroup(pageNo, this.doc.fileGroups[fileKind]); + return this.findFileByGroup(pageNo, this.doc.fileGroups[fileKind]); // eslint-disable-line } fetchMetadata() { @@ -120,16 +124,14 @@ class dlfController { * @param {dlf.StateChangeDetail} detail */ changeState(detail) { - // TODO(client-side): Consider passing full new state in stateChanged event, - // then reduce usage of currentPageNo and simultaneousPages properties - + // TODO(client-side): Consider passing full new state in stateChanged event, then reduce usage of currentPageNo and simultaneousPages properties if (detail.page !== undefined) { this.doc.state.page = detail.page; } if (detail.simultaneousPages !== undefined) { this.doc.state.simultaneousPages = detail.simultaneousPages; } - document.body.dispatchEvent(new CustomEvent('tx-dlf-stateChanged', { detail })); + document.body.dispatchEvent(new CustomEvent("tx-dlf-stateChanged", { detail })); } /** @@ -139,9 +141,10 @@ class dlfController { */ changePage(pageNo) { const clampedPageNo = Math.max(1, Math.min(this.numPages, pageNo)); + if (clampedPageNo !== this.doc.state.page) { this.changeState({ - source: 'navigation', + source: "navigation", page: clampedPageNo, }); } @@ -150,18 +153,20 @@ class dlfController { /** * @param {number} pageNo * @param {boolean} pageGrid + * @returns {string} */ makePageUrl(pageNo, pageGrid = false) { const doublePage = this.simultaneousPages >= 2 ? 1 : 0; + return this.doc.urlTemplate - .replace(/DOUBLE_PAGE/, doublePage) - .replace(/PAGE_NO/, pageNo) - .replace(/PAGE_GRID/, pageGrid ? '1' : '0'); + .replace(/DOUBLE_PAGE/u, doublePage) + .replace(/PAGE_NO/u, pageNo) + .replace(/PAGE_GRID/u, pageGrid ? "1" : "0"); } /** - * @private * @param {dlf.StateChangeEvent} e + * @private */ onStateChanged(e) { this.pushHistory(e); @@ -172,54 +177,55 @@ class dlfController { } /** - * @private * @param {PopStateEvent} e + * @private */ onPopState(e) { - if (e.state == null || e.state.type !== 'tx-dlf-page-state') { + if (e.state == null || e.state.type !== "tx-dlf-page-state") { return; } const state = /** @type {dlf.PageHistoryState} */(e.state); + if (state.documentId !== this.doc.state.documentId) { return; } e.preventDefault(); this.changeState({ - 'source': 'history', - 'page': state.page === this.currentPageNo ? undefined : state.page, - 'simultaneousPages': state.simultaneousPages === this.simultaneousPages ? undefined : state.simultaneousPages + "source": "history", + "page": state.page === this.currentPageNo ? undefined : state.page, + "simultaneousPages": state.simultaneousPages === this.simultaneousPages ? undefined : state.simultaneousPages }); } /** - * @private * @param {dlf.StateChangeEvent} e + * @private */ pushHistory(e) { // Avoid loop of pushState/dispatchEvent - if (e.detail.source === 'history') { + if (e.detail.source === "history") { return; } history.pushState(/** @type {dlf.PageHistoryState} */({ - type: 'tx-dlf-page-state', + type: "tx-dlf-page-state", ...this.doc.state - }), '', this.makePageUrl(this.doc.state.page)); + }), "", this.makePageUrl(this.doc.state.page)); } /** - * @private * @param {number} simultaneousPages + * @private */ updateMultiPage(simultaneousPages) { if (simultaneousPages === 1) { - document.body.classList.add('page-single'); - document.body.classList.remove('page-double'); + document.body.classList.add("page-single"); + document.body.classList.remove("page-double"); } else if (simultaneousPages === 2) { - document.body.classList.remove('page-single'); - document.body.classList.add('page-double'); + document.body.classList.remove("page-single"); + document.body.classList.add("page-double"); } } } diff --git a/Resources/Public/JavaScript/PageView/Metadata.js b/Resources/Public/JavaScript/PageView/Metadata.js index b1ad1b7ed4..5879eea5dc 100644 --- a/Resources/Public/JavaScript/PageView/Metadata.js +++ b/Resources/Public/JavaScript/PageView/Metadata.js @@ -41,7 +41,7 @@ class dlfMetadata { /** @protected */ this.config = config; - this.docController.eventTarget.addEventListener('tx-dlf-stateChanged', () => { + this.docController.eventTarget.addEventListener("tx-dlf-stateChanged", () => { this.onStateChanged(); }); @@ -54,9 +54,11 @@ class dlfMetadata { async fetchMetadata() { try { const metadata = await this.docController.fetchMetadata(); - const element = document.createElement('div'); - element.innerHTML = metadata.htmlCode; - const metadataContainer = element.querySelector('.dlf-metadata-container'); + const element = document.createElement("div"); + + element.innerHTML = metadata.htmlCode; // eslint-disable-line + const metadataContainer = element.querySelector(".dlf-metadata-container"); + if (metadataContainer !== null) { this.config.container.replaceWith(metadataContainer); this.updateSectionVisibility(); @@ -78,10 +80,11 @@ class dlfMetadata { * @protected */ updateSectionVisibility() { - document.querySelectorAll('[data-metadata-list][data-dlf-section]').forEach((element) => { + document.querySelectorAll("[data-metadata-list][data-dlf-section]").forEach((element) => { let isShown = false; + for (const page of this.docController.getVisiblePages()) { - if (this.shouldShowSection(page.pageObj, element.getAttribute('data-dlf-section'))) { + if (this.shouldShowSection(page.pageObj, element.getAttribute("data-dlf-section"))) { isShown = true; break; } @@ -92,9 +95,10 @@ class dlfMetadata { } /** - * @protected * @param {dlf.PageObject} pageObj * @param {string} section + * @returns {boolean} + * @protected */ shouldShowSection(pageObj, section) { switch (this.config.rootline) { diff --git a/Resources/Public/JavaScript/PageView/Navigation.js b/Resources/Public/JavaScript/PageView/Navigation.js index 3d3bdefb37..80468ba3d7 100644 --- a/Resources/Public/JavaScript/PageView/Navigation.js +++ b/Resources/Public/JavaScript/PageView/Navigation.js @@ -30,34 +30,34 @@ class dlfNavigation { */ this.navigationButtons = { pageStepBack: { - button: document.querySelector('.page-step-back'), + button: document.querySelector(".page-step-back"), getPage: (prevPageNo) => prevPageNo - this.getLongStep(), }, pageBack: { - button: document.querySelector('.page-back'), + button: document.querySelector(".page-back"), // When we're on second page in double-page mode, make sure the "back" button is still shown getPage: (prevPageNo) => Math.max(1, prevPageNo - this.docController.simultaneousPages), }, pageFirst: { - button: document.querySelector('.page-first'), + button: document.querySelector(".page-first"), getPage: (prevPageNo) => 1, }, pageStepForward: { - button: document.querySelector('.page-step-forward'), + button: document.querySelector(".page-step-forward"), getPage: (prevPageNo) => prevPageNo + this.getLongStep(), }, pageForward: { - button: document.querySelector('.page-forward'), + button: document.querySelector(".page-forward"), getPage: (prevPageNo) => prevPageNo + this.docController.simultaneousPages, }, pageLast: { - button: document.querySelector('.page-last'), + button: document.querySelector(".page-last"), getPage: (prevPageNo) => this.docController.numPages - (this.docController.simultaneousPages - 1), }, }; /** @private */ - this.pageSelect = document.querySelector('.page-select'); + this.pageSelect = document.querySelector(".page-select"); this.registerEvents(); this.updateNavigationControls(); @@ -68,24 +68,27 @@ class dlfNavigation { */ registerEvents() { for (const [key, value] of Object.entries(this.navigationButtons)) { - if (this.config.features[key]) { - value.button.addEventListener('click', (e) => { + + if (this.config.features[key]) { // eslint-disable-line + value.button.addEventListener("click", (e) => { e.preventDefault(); const pageNo = value.getPage(this.docController.currentPageNo); + this.docController.changePage(pageNo); }); } } - this.pageSelect.addEventListener('change', (e) => { + this.pageSelect.addEventListener("change", (e) => { e.preventDefault(); const pageNo = Number(e.target.value); + this.docController.changePage(pageNo); }); - this.docController.eventTarget.addEventListener('tx-dlf-stateChanged', () => { + this.docController.eventTarget.addEventListener("tx-dlf-stateChanged", () => { this.onStateChanged(); }); } @@ -101,8 +104,8 @@ class dlfNavigation { * Number of pages to jump in long step (e.g., 10 pages in single page mode * vs. 20 pages in double page mode). * - * @protected * @returns {number} + * @protected */ getLongStep() { return this.config.basePageSteps * this.docController.simultaneousPages; @@ -119,6 +122,7 @@ class dlfNavigation { for (const value of Object.values(this.navigationButtons)) { const btnPageNo = value.getPage(currentPageNo); + this.toggleButtonDisabled(value.button, btnPageNo); this.updateUrl(value.button, btnPageNo); this.updateText(value.button); @@ -132,42 +136,42 @@ class dlfNavigation { /** * Enable/disable the button depending on current page. * - * @param {button} Element - * @param {pageNo} - * + * @param {Element} button + * @param {number} pageNo * @private */ toggleButtonDisabled(button, pageNo) { - const isBtnPageVisible = this.docController.getVisiblePages(pageNo).some(page => page.pageNo === this.docController.currentPageNo); + const isBtnPageVisible = this.docController.getVisiblePages(pageNo).some((page) => page.pageNo === this.docController.currentPageNo); + if (!isBtnPageVisible && 1 <= pageNo && pageNo <= this.docController.numPages) { - button.classList.remove('disabled'); + button.classList.remove("disabled"); } else { - button.classList.add('disabled'); + button.classList.add("disabled"); } } /** * Update URLs of navigation button. - * - * @param {button} Element * + * @param {Element} button + * @param {number} pageNo * @private */ updateUrl(button, pageNo) { - button.setAttribute('href', this.docController.makePageUrl(pageNo)); + button.setAttribute("href", this.docController.makePageUrl(pageNo)); } /** * Update text of navigation button. - * - * @param {button} Element * + * @param {Element} button * @private */ updateText(button) { - const textTemplate = button.getAttribute('data-text'); + const textTemplate = button.getAttribute("data-text"); + if (textTemplate) { - button.textContent = textTemplate.replace(/PAGE_STEPS/, this.getLongStep()); + button.textContent = textTemplate.replace(/PAGE_STEPS/u, this.getLongStep()); } } } diff --git a/Resources/Public/JavaScript/PageView/PageView.js b/Resources/Public/JavaScript/PageView/PageView.js index e8fb660cc3..16984f52bd 100644 --- a/Resources/Public/JavaScript/PageView/PageView.js +++ b/Resources/Public/JavaScript/PageView/PageView.js @@ -17,12 +17,11 @@ /** * @typedef {{ - * div: string; - * progressElementId?: string; - * images?: dlf.ImageDesc[] | []; - * fulltexts?: dlf.FulltextDesc[] | []; - * controls?: ('OverviewMap' | 'ZoomPanel')[]; - * initDoc: dlf.Document; + * div: string; + * progressElementId?: string; + * images?: dlf.ImageDesc[] | []; + * fulltexts?: dlf.FulltextDesc[] | []; + * controls?: ('OverviewMap' | 'ZoomPanel')[]; * }} DlfViewerConfig * * @typedef {any} DlfDocument @@ -90,7 +89,7 @@ var dlfViewer = function(settings){ * @type {JQueryStatic.Deferred[]} * @private */ - this.fulltextsLoaded_ = {}; + this.fulltextsLoaded = {}; /** * IIIF annotation lists URLs for the current canvas @@ -188,7 +187,7 @@ var dlfViewer = function(settings){ * Cache of promises / jQuery Deferred returned by `initLayer()`. * This has two benefits: * - Switching to a page that has already been visited is basically instantaneous. - * When relying on browser cache, there still is a flicker. + * When relying on browser cache, there still is a flicker. * - It may allow to prefetch pages that are likely to be visited next (TODO(client-side): do that). * * @type {Record} @@ -196,11 +195,6 @@ var dlfViewer = function(settings){ */ this.layersCache = {}; - /** - * @private - */ - this.initDoc = settings.initDoc; - /** * @type {dlfController | null} * @private @@ -297,7 +291,7 @@ dlfViewer.prototype.countPages = function () { * * @param {JQueryStatic.Deferred | undefined} currentFulltext */ -dlfViewer.prototype.updateFulltext = function (currentFulltext) { +dlfViewer.prototype.updateFulltext = function(currentFulltext) { if (!this.fulltextControl) { this.fulltextControl = new dlfViewerFullTextControl(this.map); } @@ -305,18 +299,18 @@ dlfViewer.prototype.updateFulltext = function (currentFulltext) { this.fulltextDownloadControl = new dlfViewerFullTextDownloadControl(this.map); } if (currentFulltext !== undefined && this.images.length === 1) { - $('#tx-dlf-tools-fulltext').show(); + $("#tx-dlf-tools-fulltext").show(); currentFulltext - .then( (fulltextData) => { + .then((fulltextData) => { this.fulltextControl.loadFulltextData(fulltextData); this.fulltextDownloadControl.setFulltextData(fulltextData); }) - .catch( () => { + .catch(() => { this.fulltextControl.deactivate(); }); } else { - $('#tx-dlf-tools-fulltext').hide(); + $("#tx-dlf-tools-fulltext").hide(); this.fulltextControl.deactivate(); } }; @@ -328,33 +322,28 @@ dlfViewer.prototype.updateFulltext = function (currentFulltext) { * @param {Array.} controlNames */ dlfViewer.prototype.addCustomControls = function() { - var annotationControl = undefined, - imageManipulationControl = undefined, - images = this.images; + var annotationControl = undefined; + var imageManipulationControl = undefined; // Adds fulltext behavior and download only if there is fulltext available and no double page // behavior is active - const currentFulltext = this.fulltextsLoaded_[`${this.getVisiblePages()[0].pageNo}-0`]; + const currentFulltext = this.fulltextsLoaded[`${this.getVisiblePages()[0].pageNo}-0`]; + this.updateFulltext(currentFulltext); - if (this.annotationContainers[0] !== undefined && this.annotationContainers[0].annotationContainers !== undefined - && this.annotationContainers[0].annotationContainers.length > 0 && this.images.length === 1) { + if (this.annotationContainers[0] !== undefined && this.annotationContainers[0].annotationContainers !== undefined && this.annotationContainers[0].annotationContainers.length > 0 && this.images.length === 1) { // Adds annotation behavior only if there are annotations available and view is single page annotationControl = new DlfAnnotationControl(this.map, this.images[0], this.annotationContainers[0]); if (this.fulltextControl !== undefined) { $(this.fulltextControl).on("activate-fulltext", $.proxy(annotationControl.deactivate, annotationControl)); $(annotationControl).on("activate-annotations", $.proxy(this.fulltextControl.deactivate, this.fulltextControl)); } - } - else { - $('#tx-dlf-tools-annotations').remove(); + } else { + $("#tx-dlf-tools-annotations").remove(); } - // // Add image manipulation tool if container is added. - // - if ($('#tx-dlf-tools-imagetools').length > 0) { - + if ($("#tx-dlf-tools-imagetools").length > 0) { // should be called if cors is enabled imageManipulationControl = new dlfViewerImageManipulationControl({ controlTarget: $('.tx-dlf-tools-imagetools')[0], @@ -527,7 +516,7 @@ dlfViewer.prototype.displayHighlightWord = function(highlightWords = null) { var self = this; var values = decodeURIComponent(this.highlightWords).split(';'); - const currentFulltext = this.fulltextsLoaded_[this.getVisiblePages()[0].pageNo]; + const currentFulltext = this.fulltextsLoaded[this.getVisiblePages()[0].pageNo]; $.when.apply($, currentFulltext) .done(function (fulltextData, fulltextDataImageTwo) { var stringFeatures = []; @@ -653,10 +642,9 @@ dlfViewer.prototype.init = function(controlNames) { dlfViewer.prototype.getVisiblePages = function () { if (this.docController === null) { - return this.initDoc.pages.map( (page, i) => ({ - pageNo: this.initDoc.query.minPage + i, - pageObj: page - })); + /* eslint no-console: ["error", { allow: ["warn", "error"] }] */ + console.error("No document controller found"); + return; } else { return this.docController.getVisiblePages(); } @@ -713,7 +701,7 @@ dlfViewer.prototype.loadPages = function (visiblePages) { let i = 0; for (const page of pages) { - this.updateFulltext(this.fulltextsLoaded_[`${page.pageNo}-${i}`]); + this.updateFulltext(this.fulltextsLoaded[`${page.pageNo}-${i}`]); i++; } }); @@ -755,14 +743,13 @@ dlfViewer.prototype.initLayer = function (imageSourceObjs) { }; /** - * Start loading fulltexts and store them to `fulltextsLoaded_` (as jQuery deferred objects). + * Start loading fulltexts and store them to `fulltextsLoaded` (as jQuery deferred objects). * * @param {dlf.PageObject[]} visiblePages * @private */ dlfViewer.prototype.initLoadFulltexts = function (visiblePages) { if (this.docController === null) { - // TODO(client-side): Make it work then docController === null return; } @@ -774,8 +761,8 @@ dlfViewer.prototype.initLoadFulltexts = function (visiblePages) { const fulltext = this.docController.findFileByKind(visiblePages[i].pageNo, 'fulltext'); if (fulltext !== undefined) { - if (!(key in this.fulltextsLoaded_) && dlfUtils.isFulltextDescriptor(fulltext)) { - this.fulltextsLoaded_[key] = dlfFullTextUtils.fetchFullTextDataFromServer(fulltext.url, image, xOffset); + if (!(key in this.fulltextsLoaded) && dlfUtils.isFulltextDescriptor(fulltext)) { + this.fulltextsLoaded[key] = dlfFullTextUtils.fetchFullTextDataFromServer(fulltext.url, image, xOffset); } } else { /* eslint no-console: ["error", { allow: ["warn", "error"] }] */ diff --git a/Resources/Public/JavaScript/PageView/TableOfContents.js b/Resources/Public/JavaScript/PageView/TableOfContents.js index 90611e4b68..38eb6a98f5 100644 --- a/Resources/Public/JavaScript/PageView/TableOfContents.js +++ b/Resources/Public/JavaScript/PageView/TableOfContents.js @@ -23,32 +23,35 @@ class dlfTableOfContents { /** @private */ this.docController = docController; /** @private */ - this.tocItems = document.querySelectorAll('[data-toc-item]'); + this.tocItems = document.querySelectorAll("[data-toc-item]"); /** @private */ - this.tocLinks = document.querySelectorAll('[data-toc-link]'); + this.tocLinks = document.querySelectorAll("[data-toc-link]"); this.tocLinks.forEach((link) => { - const documentId = link.getAttribute('data-document-id'); + const documentId = link.getAttribute("data-document-id"); + if (documentId && documentId !== this.docController.documentId) { return; } - const pageNo = Number(link.getAttribute('data-page')); - link.addEventListener('click', e => { + const pageNo = Number(link.getAttribute("data-page")); + + link.addEventListener("click", (e) => { e.preventDefault(); this.docController.changePage(pageNo); }); }); - docController.eventTarget.addEventListener('tx-dlf-stateChanged', this.onStateChanged.bind(this)); + docController.eventTarget.addEventListener("tx-dlf-stateChanged", this.onStateChanged.bind(this)); } /** - * @private * @param {dlf.StateChangeEvent} e + * @private */ onStateChanged(e) { const activeLogSections = []; + // TODO(client-side): Add toplevel sections for (const page of this.docController.getVisiblePages()) { activeLogSections.push(...page.pageObj.logSections); @@ -59,17 +62,20 @@ class dlfTableOfContents { // See TableOfContentsController::getMenuEntry() this.tocItems.forEach((tocItem) => { let tocItemState = dlfTocState.Normal; - let isExpanded = Boolean(tocItem.getAttribute('data-toc-expand-always')); + let isExpanded = Boolean(tocItem.getAttribute("data-toc-expand-always")); + + const isCurrent = activeLogSections.includes(tocItem.getAttribute("data-dlf-section")); - const isCurrent = activeLogSections.includes(tocItem.getAttribute('data-dlf-section')); if (isCurrent) { tocItemState = dlfTocState.Current; } - const children = Array.from(tocItem.querySelectorAll('[data-toc-item]')); + const children = Array.from(tocItem.querySelectorAll("[data-toc-item]")); + if (children.length > 0 && isCurrent) { // TODO(client-side): check depth? - const isActive = children.some(tocItemChild => activeLogSections.includes(tocItemChild.getAttribute('data-dlf-section'))); + const isActive = children.some((tocItemChild) => activeLogSections.includes(tocItemChild.getAttribute("data-dlf-section"))); + if (isActive) { tocItemState = dlfTocState.Active; } @@ -78,27 +84,27 @@ class dlfTableOfContents { } if (tocItemState === dlfTocState.Normal) { - tocItem.classList.add('tx-dlf-toc-no'); + tocItem.classList.add("tx-dlf-toc-no"); } else { - tocItem.classList.remove('tx-dlf-toc-no'); + tocItem.classList.remove("tx-dlf-toc-no"); } if (tocItemState === dlfTocState.Active) { - tocItem.classList.add('active', 'tx-dlf-toc-act'); + tocItem.classList.add("active", "tx-dlf-toc-act"); } else { - tocItem.classList.remove('active', 'tx-dlf-toc-act'); + tocItem.classList.remove("active", "tx-dlf-toc-act"); } if (tocItemState === dlfTocState.Current) { - tocItem.classList.add('current', 'tx-dlf-toc-cur'); + tocItem.classList.add("current", "tx-dlf-toc-cur"); } else { - tocItem.classList.remove('current', 'tx-dlf-toc-cur'); + tocItem.classList.remove("current", "tx-dlf-toc-cur"); } if (isExpanded) { - tocItem.classList.remove('dlf-toc-collapsed'); + tocItem.classList.remove("dlf-toc-collapsed"); } else { - tocItem.classList.add('dlf-toc-collapsed'); + tocItem.classList.add("dlf-toc-collapsed"); } // "submenu" class does not change diff --git a/Resources/Public/JavaScript/PageView/Toolbox.js b/Resources/Public/JavaScript/PageView/Toolbox.js index 0852849c40..8ad4fcfcfe 100644 --- a/Resources/Public/JavaScript/PageView/Toolbox.js +++ b/Resources/Public/JavaScript/PageView/Toolbox.js @@ -17,15 +17,15 @@ class dlfToolbox { /** @private */ this.docController = docController; /** @private */ - this.pageLinks = document.querySelectorAll('[data-page-link]'); + this.pageLinks = document.querySelectorAll("[data-page-link]"); - docController.eventTarget.addEventListener('tx-dlf-stateChanged', this.onStateChanged.bind(this)); + docController.eventTarget.addEventListener("tx-dlf-stateChanged", this.onStateChanged.bind(this)); this.updatePageLinks(this.docController.currentPageNo); } /** - * @private * @param {dlf.StateChangeEvent} e + * @private */ onStateChanged(e) { if (e.detail.page !== undefined) { @@ -34,21 +34,20 @@ class dlfToolbox { } /** - * @private * @param {number} firstPageNo + * @private */ updatePageLinks(firstPageNo) { - this.pageLinks.forEach(element => { - const offset = Number(element.getAttribute('data-page-link')); + this.pageLinks.forEach((element) => { + const offset = Number(element.getAttribute("data-page-link")); const pageNo = firstPageNo + offset; const fileGroups = this.getFileGroups(element); - const file = fileGroups !== null - ? this.docController.findFileByGroup(pageNo, fileGroups) - : this.docController.findFileByKind(pageNo, 'download'); + const file = fileGroups !== null ? this.docController.findFileByGroup(pageNo, fileGroups) : this.docController.findFileByKind(pageNo, "download"); if (file === undefined) { $(element).hide(); + return; } $(element).show(); @@ -56,22 +55,24 @@ class dlfToolbox { if (element instanceof HTMLAnchorElement) { element.href = file.url; } else { - element.querySelectorAll('a').forEach(linkEl => { + element.querySelectorAll("a").forEach((linkEl) => { linkEl.href = file.url; }); } - const mimetypeLabelEl = element.querySelector('.dlf-mimetype-label'); + const mimetypeLabelEl = element.querySelector(".dlf-mimetype-label"); + if (mimetypeLabelEl !== null) { // Transliterated from ToolboxController - let mimetypeLabel = ''; + let mimetypeLabel = ""; + switch (file.mimetype) { - case 'image/jpeg': - mimetypeLabel = ' (JPG)'; + case "image/jpeg": + mimetypeLabel = " (JPG)"; break; - case 'image/tiff': - mimetypeLabel = ' (TIFF)'; + case "image/tiff": + mimetypeLabel = " (TIFF)"; break; } @@ -81,15 +82,17 @@ class dlfToolbox { } /** - * @private * @param {Element} element - * @return {string[] | null} + * @returns {string[] | null} + * @private */ getFileGroups(element) { - const fileGroupsJson = element.getAttribute('data-file-groups'); + const fileGroupsJson = element.getAttribute("data-file-groups"); + try { const fileGroups = JSON.parse(fileGroupsJson); - if (Array.isArray(fileGroups) && fileGroups.every(entry => typeof entry === 'string')) { + + if (Array.isArray(fileGroups) && fileGroups.every((entry) => typeof entry === "string")) { return fileGroups; } } catch (e) { diff --git a/Resources/Public/JavaScript/PageView/types.d.ts b/Resources/Public/JavaScript/PageView/types.d.ts index 147c8b43c6..beb010f096 100644 --- a/Resources/Public/JavaScript/PageView/types.d.ts +++ b/Resources/Public/JavaScript/PageView/types.d.ts @@ -28,7 +28,7 @@ namespace dlf { simultaneousPages: number; }; - type FileKind = 'images' | 'fulltext' | 'download'; + type FileKind = "images" | "fulltext" | "download"; type Loaded = { state: PageDisplayState; @@ -38,23 +38,23 @@ namespace dlf { document: Document; }; - type StateChangeEvent = CustomEvent; - type StateChangeDetail = { /** * Who triggered the event. * * `history`: Event is triggered due to history popstate. This is used - * to avoid pushing a popped state again. + * to avoid pushing a popped state again. * * `navigation`: Event is triggered by user navigation. */ - source: 'history' | 'navigation'; + source: "history" | "navigation"; } & Partial; + type StateChangeEvent = CustomEvent; + /** * State of document stored in `window.history`. */ type PageHistoryState = { - type: 'tx-dlf-page-state'; + type: "tx-dlf-page-state"; documentId: string | number; } & PageDisplayState; }