feat: refine flipbook start modes and UI behavior
This commit is contained in:
commit
9f9b1c9935
23 files changed
+1007
No files matched your search
@@ -0,0 +1,2 @@
|
||||
node_modules/
|
||||
.DS_Store
|
||||
@@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2026 Mummert
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
@@ -0,0 +1,43 @@
|
||||
# Flipbook Bundle (Contao 5.7)
|
||||
|
||||
Contao Inhaltselement zum Darstellen von PDFs als blaetterbares Flipbook.
|
||||
|
||||
## Features
|
||||
|
||||
- Reines JavaScript (ES Modules)
|
||||
- `pdfjs-dist` fuer PDF-Rendering in `<canvas>`
|
||||
- `flipbook-js` fuer den Blaettereffekt
|
||||
- Lazy Rendering: 2-4 Seiten sofort, Rest on-demand
|
||||
- Optional Vor-/Zurueck-Navigation im Modul
|
||||
- Tastatursteuerung (Pfeiltasten) und Touch-Swipe
|
||||
- Responsive Breite mit beibehaltenem Seitenverhaeltnis
|
||||
|
||||
## Installation (VCS oder Paket)
|
||||
|
||||
```bash
|
||||
composer require mummert/flipbook-bundle
|
||||
```
|
||||
|
||||
## Inhaltselement im Backend
|
||||
|
||||
Elementtyp: `Blätterbares PDF`
|
||||
|
||||
Felder:
|
||||
|
||||
- `PDF-Datei` (singleSRC / UUID)
|
||||
- `Initial geladene Seiten` (2, 3 oder 4)
|
||||
- `Startmodus` (zentriert oder als Doppelseite)
|
||||
- `Navigation anzeigen` (optional)
|
||||
|
||||
## Hinweise
|
||||
|
||||
- Das Inhaltselement rendert PDF-Seiten als `<canvas>` innerhalb von `.c-flipbook__page`.
|
||||
- Die benoetigten Open-Source-Dateien sind lokal im Bundle enthalten.
|
||||
- Siehe `THIRD_PARTY_LICENSES.md` fuer Abhaengigkeiten.
|
||||
|
||||
## Release
|
||||
|
||||
```bash
|
||||
git tag v1.0.0
|
||||
git push origin v1.0.0
|
||||
```
|
||||
@@ -0,0 +1,17 @@
|
||||
# Third-Party Licenses
|
||||
|
||||
This bundle contains the following third-party open source libraries in `public/assets/vendor/`:
|
||||
|
||||
## pdfjs-dist
|
||||
|
||||
- Project: https://github.com/mozilla/pdf.js
|
||||
- Package: `pdfjs-dist`
|
||||
- Version: `4.9.155`
|
||||
- License: Apache-2.0
|
||||
|
||||
## flipbook-js
|
||||
|
||||
- Project: https://github.com/taufiqelrahman/flipbook-js
|
||||
- Package: `flipbook-js`
|
||||
- Version: `1.1.1`
|
||||
- License: MIT
|
||||
@@ -0,0 +1,32 @@
|
||||
{
|
||||
"name": "mummert/flipbook-bundle",
|
||||
"description": "PDF flipbook content element for Contao 5.7 using pdfjs-dist and flipbook-js.",
|
||||
"type": "contao-bundle",
|
||||
"license": "MIT",
|
||||
"keywords": [
|
||||
"contao",
|
||||
"contao-bundle",
|
||||
"pdf",
|
||||
"flipbook"
|
||||
],
|
||||
"require": {
|
||||
"php": "^8.3",
|
||||
"contao/core-bundle": "^5.7",
|
||||
"contao/manager-plugin": "^2.0"
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Mummert\\FlipbookBundle\\": "src/"
|
||||
}
|
||||
},
|
||||
"extra": {
|
||||
"contao-manager-plugin": "Mummert\\FlipbookBundle\\Contao\\Manager\\Plugin"
|
||||
},
|
||||
"prefer-stable": true,
|
||||
"config": {
|
||||
"allow-plugins": {
|
||||
"contao-components/installer": true,
|
||||
"contao/manager-plugin": true
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
services:
|
||||
_defaults:
|
||||
autowire: true
|
||||
autoconfigure: true
|
||||
|
||||
Mummert\FlipbookBundle\:
|
||||
resource: ../src/
|
||||
exclude:
|
||||
- ../src/DependencyInjection/
|
||||
- ../src/Contao/Manager/
|
||||
- ../src/FlipbookBundle.php
|
||||
@@ -0,0 +1,41 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
$GLOBALS['TL_DCA']['tl_content']['palettes']['blatterbares_pdf'] = '{type_legend},type,headline;{flipbook_legend},flipbookPdfSrc,flipbookInitialPages,flipbookStartMode,flipbookShowNavigation;{template_legend:hide},customTpl;{protected_legend:hide},protected;{expert_legend:hide},cssID;{invisible_legend:hide},invisible,start,stop';
|
||||
|
||||
$GLOBALS['TL_DCA']['tl_content']['fields']['flipbookPdfSrc'] = [
|
||||
'label' => &$GLOBALS['TL_LANG']['tl_content']['flipbookPdfSrc'],
|
||||
'exclude' => true,
|
||||
'inputType' => 'fileTree',
|
||||
'eval' => ['fieldType' => 'radio', 'files' => true, 'filesOnly' => true, 'extensions' => 'pdf', 'mandatory' => true, 'tl_class' => 'w50'],
|
||||
'sql' => ['type' => 'binary', 'length' => 16, 'notnull' => false],
|
||||
];
|
||||
|
||||
$GLOBALS['TL_DCA']['tl_content']['fields']['flipbookInitialPages'] = [
|
||||
'label' => &$GLOBALS['TL_LANG']['tl_content']['flipbookInitialPages'],
|
||||
'exclude' => true,
|
||||
'inputType' => 'select',
|
||||
'options' => ['2', '3', '4'],
|
||||
'reference' => &$GLOBALS['TL_LANG']['tl_content']['flipbookInitialPagesOptions'],
|
||||
'eval' => ['mandatory' => true, 'includeBlankOption' => false, 'tl_class' => 'w50'],
|
||||
'sql' => ['type' => 'string', 'length' => 1, 'default' => '4'],
|
||||
];
|
||||
|
||||
$GLOBALS['TL_DCA']['tl_content']['fields']['flipbookStartMode'] = [
|
||||
'label' => &$GLOBALS['TL_LANG']['tl_content']['flipbookStartMode'],
|
||||
'exclude' => true,
|
||||
'inputType' => 'select',
|
||||
'options' => ['center', 'spread'],
|
||||
'reference' => &$GLOBALS['TL_LANG']['tl_content']['flipbookStartModeOptions'],
|
||||
'eval' => ['mandatory' => true, 'includeBlankOption' => false, 'tl_class' => 'w50'],
|
||||
'sql' => ['type' => 'string', 'length' => 16, 'default' => 'center'],
|
||||
];
|
||||
|
||||
$GLOBALS['TL_DCA']['tl_content']['fields']['flipbookShowNavigation'] = [
|
||||
'label' => &$GLOBALS['TL_LANG']['tl_content']['flipbookShowNavigation'],
|
||||
'exclude' => true,
|
||||
'inputType' => 'checkbox',
|
||||
'eval' => ['tl_class' => 'w50 m12'],
|
||||
'sql' => ['type' => 'string', 'length' => 1, 'fixed' => true, 'default' => '1'],
|
||||
];
|
||||
@@ -0,0 +1,5 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
$GLOBALS['TL_LANG']['CTE']['blatterbares_pdf'] = ['Blätterbares PDF', 'Zeigt ein PDF als blätterbares Flipbook mit Lazy-Rendering an.'];
|
||||
@@ -0,0 +1,18 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
$GLOBALS['TL_LANG']['tl_content']['flipbook_legend'] = 'Blätterbares PDF';
|
||||
$GLOBALS['TL_LANG']['tl_content']['flipbookPdfSrc'] = ['PDF-Datei', 'Bitte waehlen Sie die PDF-Datei aus, die als Flipbook angezeigt werden soll.'];
|
||||
$GLOBALS['TL_LANG']['tl_content']['flipbookInitialPages'] = ['Initial geladene Seiten', 'Anzahl der Seiten, die beim Laden direkt gerendert werden (Rest lazy).'];
|
||||
$GLOBALS['TL_LANG']['tl_content']['flipbookInitialPagesOptions'] = [
|
||||
'2' => '2 Seiten',
|
||||
'3' => '3 Seiten',
|
||||
'4' => '4 Seiten',
|
||||
];
|
||||
$GLOBALS['TL_LANG']['tl_content']['flipbookStartMode'] = ['Startmodus', 'Legt fest, ob das Flipbook zentriert oder mit Doppelseite startet.'];
|
||||
$GLOBALS['TL_LANG']['tl_content']['flipbookStartModeOptions'] = [
|
||||
'center' => 'Zentriert starten',
|
||||
'spread' => 'als Doppelseite starten',
|
||||
];
|
||||
$GLOBALS['TL_LANG']['tl_content']['flipbookShowNavigation'] = ['Navigation anzeigen', 'Zeigt Vor-/Zurueck-Buttons unter dem Flipbook an.'];
|
||||
@@ -0,0 +1,5 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
$GLOBALS['TL_LANG']['CTE']['blatterbares_pdf'] = ['Flippable PDF', 'Displays a PDF as a page-turning flipbook with lazy rendering.'];
|
||||
@@ -0,0 +1,18 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
$GLOBALS['TL_LANG']['tl_content']['flipbook_legend'] = 'Flippable PDF';
|
||||
$GLOBALS['TL_LANG']['tl_content']['flipbookPdfSrc'] = ['PDF file', 'Please select the PDF file to display as a flipbook.'];
|
||||
$GLOBALS['TL_LANG']['tl_content']['flipbookInitialPages'] = ['Initially rendered pages', 'Number of pages rendered immediately on load (remaining pages are lazy-rendered).'];
|
||||
$GLOBALS['TL_LANG']['tl_content']['flipbookInitialPagesOptions'] = [
|
||||
'2' => '2 pages',
|
||||
'3' => '3 pages',
|
||||
'4' => '4 pages',
|
||||
];
|
||||
$GLOBALS['TL_LANG']['tl_content']['flipbookStartMode'] = ['Start mode', 'Defines whether the flipbook starts centered or as a double-page spread.'];
|
||||
$GLOBALS['TL_LANG']['tl_content']['flipbookStartModeOptions'] = [
|
||||
'center' => 'Start centered',
|
||||
'spread' => 'Start with double-page spread',
|
||||
];
|
||||
$GLOBALS['TL_LANG']['tl_content']['flipbookShowNavigation'] = ['Show navigation', 'Displays previous/next buttons below the flipbook.'];
|
||||
Whitespace-only changes.
@@ -0,0 +1,32 @@
|
||||
{% set hasPdf = pdfUrl|default('') is not empty %}
|
||||
|
||||
<div
|
||||
class="ce-blatterbares-pdf mod-pdf-flipbook"
|
||||
data-pdf-flipbook-element="1"
|
||||
data-pdf-url="{{ pdfUrl|default('')|e('html_attr') }}"
|
||||
data-initial-pages="{{ initialRenderPages|default(4)|e('html_attr') }}"
|
||||
data-start-mode="{{ startMode|default('center')|e('html_attr') }}"
|
||||
data-show-navigation="{{ showNavigation ? '1' : '0' }}"
|
||||
tabindex="0"
|
||||
>
|
||||
<div class="mod-pdf-flipbook__status" data-flipbook-loader="1" aria-live="polite">PDF wird geladen ...</div>
|
||||
|
||||
<div class="mod-pdf-flipbook__stage" data-flipbook-stage="1">
|
||||
<div id="flipbook-{{ data.id|default(random()) }}" class="c-flipbook" data-flipbook-book="1"></div>
|
||||
</div>
|
||||
|
||||
{% if showNavigation %}
|
||||
<div class="mod-pdf-flipbook__controls" aria-label="Flipbook Navigation">
|
||||
<button type="button" data-flipbook-prev="1">Zurück</button>
|
||||
<button type="button" data-flipbook-next="1">Weiter</button>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
{% if not hasPdf %}
|
||||
<p class="mod-pdf-flipbook__error">Keine PDF-Datei konfiguriert.</p>
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
<link rel="stylesheet" href="/bundles/flipbook/assets/vendor/flipbook.min.css?v=20260414d">
|
||||
<link rel="stylesheet" href="/bundles/flipbook/assets/flipbook-module.css?v=20260414d">
|
||||
<script type="module" src="/bundles/flipbook/assets/flipbook-module.js?v=20260414d"></script>
|
||||
@@ -0,0 +1,84 @@
|
||||
.mod-pdf-flipbook {
|
||||
--flipbook-bg: #e0e0e0;
|
||||
--flipbook-text: #302d29;
|
||||
--flipbook-accent: #6f4f28;
|
||||
position: relative;
|
||||
padding: 3rem;
|
||||
background: var(--flipbook-bg);
|
||||
}
|
||||
|
||||
.mod-pdf-flipbook:focus-visible {
|
||||
outline: 2px solid var(--flipbook-accent);
|
||||
outline-offset: 3px;
|
||||
}
|
||||
|
||||
.mod-pdf-flipbook__status {
|
||||
margin-bottom: 0.8rem;
|
||||
font-size: 0.92rem;
|
||||
color: var(--flipbook-text);
|
||||
}
|
||||
|
||||
.mod-pdf-flipbook__status.is-hidden {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.mod-pdf-flipbook__status.is-error {
|
||||
color: #9f1f1f;
|
||||
}
|
||||
|
||||
.mod-pdf-flipbook__stage {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
margin: 0 auto;
|
||||
background: #e0e0e0;
|
||||
}
|
||||
|
||||
.mod-pdf-flipbook .c-flipbook {
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
.mod-pdf-flipbook .c-flipbook__page {
|
||||
background: #fefdf9;
|
||||
overflow: hidden;
|
||||
border: 1px solid #e2dbcc;
|
||||
}
|
||||
|
||||
.mod-pdf-flipbook .c-flipbook__page canvas {
|
||||
display: block;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.mod-pdf-flipbook__page-loader {
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 0.85rem;
|
||||
color: #6f675a;
|
||||
background: repeating-linear-gradient(
|
||||
-45deg,
|
||||
rgba(184, 176, 160, 0.18),
|
||||
rgba(184, 176, 160, 0.18) 14px,
|
||||
rgba(239, 235, 227, 0.28) 14px,
|
||||
rgba(239, 235, 227, 0.28) 28px
|
||||
);
|
||||
}
|
||||
|
||||
.mod-pdf-flipbook__controls {
|
||||
display: flex;
|
||||
gap: 0.6rem;
|
||||
margin-top: 0.9rem;
|
||||
}
|
||||
|
||||
.mod-pdf-flipbook__error {
|
||||
margin-top: 0.8rem;
|
||||
color: #9f1f1f;
|
||||
}
|
||||
|
||||
@media (max-width: 767px) {
|
||||
.mod-pdf-flipbook__controls {
|
||||
justify-content: space-between;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,490 @@
|
||||
const MODULE_SELECTOR = '[data-pdf-flipbook-element="1"]';
|
||||
const PDF_MODULE_URL = '/bundles/flipbook/assets/vendor/pdf.min.mjs';
|
||||
const PDF_WORKER_URL = '/bundles/flipbook/assets/vendor/pdf.worker.min.mjs';
|
||||
const FLIPBOOK_MODULE_URL = '/bundles/flipbook/assets/vendor/flipbook.esm.min.js';
|
||||
const INIT_MARKER = 'pdfFlipbookInitialized';
|
||||
const BOOTSTRAP_MARKER = '__mummertPdfFlipbookBootstrapBound';
|
||||
|
||||
let dependenciesPromise;
|
||||
let moduleCounter = 0;
|
||||
|
||||
const loadDependencies = async () => {
|
||||
if (dependenciesPromise) {
|
||||
return dependenciesPromise;
|
||||
}
|
||||
|
||||
dependenciesPromise = Promise.all([
|
||||
import(PDF_MODULE_URL),
|
||||
import(FLIPBOOK_MODULE_URL),
|
||||
]).then(([pdfjsLib, flipbookModule]) => {
|
||||
pdfjsLib.GlobalWorkerOptions.workerSrc = PDF_WORKER_URL;
|
||||
|
||||
return {
|
||||
pdfjsLib,
|
||||
FlipBook: flipbookModule.default,
|
||||
};
|
||||
});
|
||||
|
||||
return dependenciesPromise;
|
||||
};
|
||||
|
||||
class PdfFlipbookModule {
|
||||
constructor(root, dependencies) {
|
||||
const parsedInitialPages = Number.parseInt(root.dataset.initialPages || '4', 10);
|
||||
|
||||
this.root = root;
|
||||
this.dependencies = dependencies;
|
||||
this.pdfUrl = (root.dataset.pdfUrl || '').trim();
|
||||
this.initialPages = Number.isFinite(parsedInitialPages)
|
||||
? Math.min(4, Math.max(2, parsedInitialPages))
|
||||
: 4;
|
||||
this.showNavigation = root.dataset.showNavigation === '1';
|
||||
this.startMode = (root.dataset.startMode === 'spread' || root.dataset.startMode === 'cover')
|
||||
? 'spread'
|
||||
: 'center';
|
||||
this.loader = root.querySelector('[data-flipbook-loader="1"]');
|
||||
this.stage = root.querySelector('[data-flipbook-stage="1"]');
|
||||
this.bookElement = root.querySelector('[data-flipbook-book="1"]');
|
||||
this.nextButton = this.showNavigation ? root.querySelector('[data-flipbook-next="1"]') : null;
|
||||
this.prevButton = this.showNavigation ? root.querySelector('[data-flipbook-prev="1"]') : null;
|
||||
|
||||
this.pdf = null;
|
||||
this.flipbook = null;
|
||||
this.pageElements = new Map();
|
||||
this.renderedPages = new Set();
|
||||
this.pendingPages = new Set();
|
||||
this.renderQueue = [];
|
||||
this.renderInProgress = false;
|
||||
this.resizeTimer = null;
|
||||
this.touchStart = null;
|
||||
this.aspectRatio = 1.4142;
|
||||
this.pageWidth = 0;
|
||||
this.pageHeight = 0;
|
||||
this.pageGap = 2;
|
||||
this.totalPages = 0;
|
||||
|
||||
this.instanceId = `pdf-flipbook-${++moduleCounter}`;
|
||||
}
|
||||
|
||||
async init() {
|
||||
if (!this.bookElement || !this.stage) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!this.pdfUrl) {
|
||||
this.setStatus('Keine PDF-Datei gefunden.', true);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
this.bookElement.id = this.instanceId;
|
||||
|
||||
try {
|
||||
this.setStatus('PDF wird geladen ...');
|
||||
await this.loadPdf();
|
||||
await this.resolveInitialAspectRatio();
|
||||
this.buildPageSkeleton();
|
||||
this.updateLayout();
|
||||
await this.renderInitialPages();
|
||||
this.initializeFlipbook();
|
||||
this.bindKeyboard();
|
||||
this.bindTouchSwipe();
|
||||
this.bindResize();
|
||||
this.queuePages(this.getLazyCandidates());
|
||||
this.setStatus('');
|
||||
} catch {
|
||||
this.setStatus('PDF konnte nicht geladen werden.', true);
|
||||
}
|
||||
}
|
||||
|
||||
async loadPdf() {
|
||||
const loadingTask = this.dependencies.pdfjsLib.getDocument({
|
||||
url: this.pdfUrl,
|
||||
useWorkerFetch: true,
|
||||
isEvalSupported: false,
|
||||
});
|
||||
|
||||
this.pdf = await loadingTask.promise;
|
||||
this.totalPages = Number(this.pdf.numPages || 0);
|
||||
|
||||
if (this.totalPages <= 0) {
|
||||
throw new Error('The selected PDF has no pages.');
|
||||
}
|
||||
}
|
||||
|
||||
async resolveInitialAspectRatio() {
|
||||
const firstPage = await this.pdf.getPage(1);
|
||||
const viewport = firstPage.getViewport({ scale: 1 });
|
||||
|
||||
if (viewport.width > 0 && viewport.height > 0) {
|
||||
this.aspectRatio = viewport.height / viewport.width;
|
||||
}
|
||||
}
|
||||
|
||||
buildPageSkeleton() {
|
||||
const visualPageCount = this.totalPages % 2 === 0 ? this.totalPages : this.totalPages + 1;
|
||||
|
||||
this.bookElement.innerHTML = '';
|
||||
this.pageElements.clear();
|
||||
|
||||
for (let pageNumber = 1; pageNumber <= visualPageCount; pageNumber += 1) {
|
||||
const page = document.createElement('div');
|
||||
const loader = document.createElement('div');
|
||||
|
||||
page.className = 'c-flipbook__page';
|
||||
page.dataset.pageNumber = String(pageNumber);
|
||||
|
||||
loader.className = 'mod-pdf-flipbook__page-loader';
|
||||
loader.textContent = pageNumber <= this.totalPages ? `Seite ${pageNumber} wird gerendert ...` : 'Leere Seite';
|
||||
page.appendChild(loader);
|
||||
|
||||
if (pageNumber > this.totalPages) {
|
||||
page.dataset.empty = '1';
|
||||
}
|
||||
|
||||
this.bookElement.appendChild(page);
|
||||
|
||||
if (pageNumber <= this.totalPages) {
|
||||
this.pageElements.set(pageNumber, page);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
updateLayout() {
|
||||
const stageWidth = Math.max(this.stage.clientWidth || 0, 282);
|
||||
this.pageWidth = Math.max(Math.floor((stageWidth - this.pageGap) / 2), 140);
|
||||
this.pageHeight = Math.max(Math.floor(this.pageWidth * this.aspectRatio), 180);
|
||||
|
||||
this.stage.style.height = `${this.pageHeight}px`;
|
||||
this.bookElement.style.height = `${this.pageHeight}px`;
|
||||
|
||||
this.pageElements.forEach((pageElement) => {
|
||||
pageElement.style.width = `${this.pageWidth}px`;
|
||||
pageElement.style.height = `${this.pageHeight}px`;
|
||||
});
|
||||
}
|
||||
|
||||
async renderInitialPages() {
|
||||
const limit = Math.min(this.initialPages, this.totalPages);
|
||||
|
||||
for (let pageNumber = 1; pageNumber <= limit; pageNumber += 1) {
|
||||
await this.renderPage(pageNumber);
|
||||
}
|
||||
}
|
||||
|
||||
initializeFlipbook() {
|
||||
const startsWithDoubleSpread = this.startMode === 'spread';
|
||||
|
||||
this.flipbook = new this.dependencies.FlipBook(this.bookElement, {
|
||||
nextButton: this.nextButton,
|
||||
previousButton: this.prevButton,
|
||||
canClose: !startsWithDoubleSpread,
|
||||
arrowKeys: false,
|
||||
initialActivePage: 0,
|
||||
initialCall: false,
|
||||
width: '100%',
|
||||
height: `${this.pageHeight}px`,
|
||||
onPageTurn: () => {
|
||||
this.queuePages(this.getLazyCandidates());
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
bindKeyboard() {
|
||||
this.root.addEventListener('pointerdown', () => {
|
||||
this.root.focus();
|
||||
});
|
||||
|
||||
this.root.addEventListener('keydown', (event) => {
|
||||
if (!this.flipbook) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (event.key === 'ArrowRight') {
|
||||
this.flipbook.turnPage('forward');
|
||||
event.preventDefault();
|
||||
}
|
||||
|
||||
if (event.key === 'ArrowLeft') {
|
||||
this.flipbook.turnPage('back');
|
||||
event.preventDefault();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
bindTouchSwipe() {
|
||||
this.bookElement.addEventListener('touchstart', (event) => {
|
||||
const touch = event.changedTouches && event.changedTouches[0];
|
||||
|
||||
if (!touch) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.touchStart = {
|
||||
x: touch.clientX,
|
||||
y: touch.clientY,
|
||||
};
|
||||
}, { passive: true });
|
||||
|
||||
this.bookElement.addEventListener('touchend', (event) => {
|
||||
if (!this.flipbook || !this.touchStart) {
|
||||
this.touchStart = null;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
const touch = event.changedTouches && event.changedTouches[0];
|
||||
|
||||
if (!touch) {
|
||||
this.touchStart = null;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
const deltaX = touch.clientX - this.touchStart.x;
|
||||
const deltaY = touch.clientY - this.touchStart.y;
|
||||
const horizontalThreshold = 40;
|
||||
const verticalLimit = 60;
|
||||
|
||||
if (Math.abs(deltaX) >= horizontalThreshold && Math.abs(deltaY) < verticalLimit) {
|
||||
if (deltaX < 0) {
|
||||
this.flipbook.turnPage('forward');
|
||||
} else {
|
||||
this.flipbook.turnPage('back');
|
||||
}
|
||||
}
|
||||
|
||||
this.touchStart = null;
|
||||
}, { passive: true });
|
||||
}
|
||||
|
||||
bindResize() {
|
||||
const onResize = () => {
|
||||
window.clearTimeout(this.resizeTimer);
|
||||
this.resizeTimer = window.setTimeout(() => {
|
||||
if (!this.pdf) {
|
||||
return;
|
||||
}
|
||||
|
||||
const previousHeight = this.pageHeight;
|
||||
this.updateLayout();
|
||||
|
||||
if (Math.abs(previousHeight - this.pageHeight) < 2) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.bookElement.style.height = `${this.pageHeight}px`;
|
||||
|
||||
const rerender = Array.from(this.renderedPages);
|
||||
this.renderedPages.clear();
|
||||
|
||||
rerender.forEach((pageNumber) => {
|
||||
const page = this.pageElements.get(pageNumber);
|
||||
|
||||
if (page) {
|
||||
const oldCanvas = page.querySelector('canvas');
|
||||
|
||||
if (oldCanvas) {
|
||||
oldCanvas.remove();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
this.queuePages(rerender, true);
|
||||
}, 120);
|
||||
};
|
||||
|
||||
if ('ResizeObserver' in window) {
|
||||
const observer = new ResizeObserver(onResize);
|
||||
observer.observe(this.stage);
|
||||
} else {
|
||||
window.addEventListener('resize', onResize, { passive: true });
|
||||
}
|
||||
}
|
||||
|
||||
getLazyCandidates() {
|
||||
const activePages = Array.from(this.bookElement.querySelectorAll('.c-flipbook__page.is-active'));
|
||||
const candidates = new Set();
|
||||
|
||||
if (activePages.length === 0) {
|
||||
const fallbackFrom = Math.min(this.initialPages + 1, this.totalPages);
|
||||
const fallbackTo = Math.min(fallbackFrom + 2, this.totalPages);
|
||||
|
||||
for (let pageNumber = fallbackFrom; pageNumber <= fallbackTo; pageNumber += 1) {
|
||||
candidates.add(pageNumber);
|
||||
}
|
||||
|
||||
return Array.from(candidates);
|
||||
}
|
||||
|
||||
activePages.forEach((activePage) => {
|
||||
const currentPage = Number(activePage.dataset.pageNumber || 0);
|
||||
|
||||
for (let offset = -1; offset <= 3; offset += 1) {
|
||||
const pageNumber = currentPage + offset;
|
||||
|
||||
if (pageNumber >= 1 && pageNumber <= this.totalPages) {
|
||||
candidates.add(pageNumber);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return Array.from(candidates);
|
||||
}
|
||||
|
||||
queuePages(pageNumbers, prioritize = false) {
|
||||
for (const pageNumber of pageNumbers) {
|
||||
if (!Number.isInteger(pageNumber) || pageNumber < 1 || pageNumber > this.totalPages) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (this.renderedPages.has(pageNumber) || this.pendingPages.has(pageNumber)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
this.pendingPages.add(pageNumber);
|
||||
|
||||
if (prioritize) {
|
||||
this.renderQueue.unshift(pageNumber);
|
||||
} else {
|
||||
this.renderQueue.push(pageNumber);
|
||||
}
|
||||
}
|
||||
|
||||
this.processRenderQueue();
|
||||
}
|
||||
|
||||
async processRenderQueue() {
|
||||
if (this.renderInProgress) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.renderInProgress = true;
|
||||
|
||||
while (this.renderQueue.length > 0) {
|
||||
const pageNumber = this.renderQueue.shift();
|
||||
|
||||
if (!pageNumber) {
|
||||
continue;
|
||||
}
|
||||
|
||||
this.pendingPages.delete(pageNumber);
|
||||
|
||||
try {
|
||||
await this.renderPage(pageNumber);
|
||||
} catch {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
this.renderInProgress = false;
|
||||
}
|
||||
|
||||
async renderPage(pageNumber) {
|
||||
if (this.renderedPages.has(pageNumber)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const pageElement = this.pageElements.get(pageNumber);
|
||||
|
||||
if (!pageElement) {
|
||||
return;
|
||||
}
|
||||
|
||||
const page = await this.pdf.getPage(pageNumber);
|
||||
const viewportAtScale1 = page.getViewport({ scale: 1 });
|
||||
const scale = this.pageWidth / viewportAtScale1.width;
|
||||
const viewport = page.getViewport({ scale });
|
||||
const devicePixelRatio = window.devicePixelRatio || 1;
|
||||
const outputScale = Math.max(1, devicePixelRatio);
|
||||
const existingCanvas = pageElement.querySelector('canvas');
|
||||
|
||||
if (existingCanvas) {
|
||||
existingCanvas.remove();
|
||||
}
|
||||
|
||||
const canvas = document.createElement('canvas');
|
||||
const context = canvas.getContext('2d', { alpha: false });
|
||||
|
||||
if (!context) {
|
||||
throw new Error('Could not create canvas 2D context.');
|
||||
}
|
||||
|
||||
canvas.width = Math.floor(viewport.width * outputScale);
|
||||
canvas.height = Math.floor(viewport.height * outputScale);
|
||||
canvas.style.width = `${Math.floor(viewport.width)}px`;
|
||||
canvas.style.height = `${Math.floor(viewport.height)}px`;
|
||||
|
||||
const renderTask = page.render({
|
||||
canvasContext: context,
|
||||
viewport,
|
||||
transform: outputScale === 1 ? null : [outputScale, 0, 0, outputScale, 0, 0],
|
||||
intent: 'display',
|
||||
});
|
||||
|
||||
await renderTask.promise;
|
||||
|
||||
const loader = pageElement.querySelector('.mod-pdf-flipbook__page-loader');
|
||||
|
||||
if (loader) {
|
||||
loader.remove();
|
||||
}
|
||||
|
||||
pageElement.appendChild(canvas);
|
||||
this.renderedPages.add(pageNumber);
|
||||
}
|
||||
|
||||
setStatus(message, isError = false) {
|
||||
if (!this.loader) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!message) {
|
||||
this.loader.classList.add('is-hidden');
|
||||
this.loader.classList.remove('is-error');
|
||||
this.loader.textContent = '';
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
this.loader.classList.remove('is-hidden');
|
||||
this.loader.classList.toggle('is-error', isError);
|
||||
this.loader.textContent = message;
|
||||
}
|
||||
}
|
||||
|
||||
const run = async () => {
|
||||
const modules = Array.from(document.querySelectorAll(MODULE_SELECTOR))
|
||||
.filter((moduleElement) => moduleElement.dataset[INIT_MARKER] !== '1');
|
||||
|
||||
if (modules.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
const dependencies = await loadDependencies();
|
||||
|
||||
await Promise.all(modules.map(async (moduleElement) => {
|
||||
moduleElement.dataset[INIT_MARKER] = '1';
|
||||
|
||||
const module = new PdfFlipbookModule(moduleElement, dependencies);
|
||||
await module.init();
|
||||
}));
|
||||
};
|
||||
|
||||
if (window[BOOTSTRAP_MARKER]) {
|
||||
run().catch(() => {
|
||||
});
|
||||
|
||||
} else {
|
||||
window[BOOTSTRAP_MARKER] = true;
|
||||
|
||||
if (document.readyState === 'loading') {
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
run().catch(() => {
|
||||
});
|
||||
}, { once: true });
|
||||
} else {
|
||||
run().catch(() => {
|
||||
});
|
||||
}
|
||||
}
|
||||
+1
@@ -0,0 +1 @@
|
||||
function getDefaultExportFromCjs(e){return e&&e.__esModule&&Object.prototype.hasOwnProperty.call(e,"default")?e.default:e}var hasRequiredFlipBook,FlipBook$1={exports:{}},FlipBook_1=FlipBook$1.exports;function requireFlipBook(){return hasRequiredFlipBook||(hasRequiredFlipBook=1,function(e){class s{constructor(e,t={}){if(!(this instanceof s))return new s(e,t);this.options={nextButton:null,previousButton:null,canClose:!1,arrowKeys:!0,initialActivePage:0,onPageTurn:()=>{},initialCall:!1,width:"100%",height:"283px",...t},this.classNames={page:"c-flipbook__page",hiddenCover:"hidden-cover",atFrontCover:"at-front-cover",atBackCover:"at-rear-cover",firstPage:"first-page",lastPage:"last-page",isReady:"is-ready",isActive:"is-active",isCalling:"is-calling",isAnimating:"is-animating",wasActive:"was-active"},this.Modernizr=window.Modernizr||{csstransforms3d:!0},"boolean"!=typeof this.Modernizr.preserve3d&&(this.Modernizr.preserve3d=!0),this.el="string"==typeof e?document.getElementById(e):e,this.el.style.width=this.options.width,this.el.style.height=this.options.height,this.el.setAttribute("data-useragent",navigator.userAgent),this.pages=this.el.querySelectorAll(`.${this.classNames.page}, .${this.classNames.hiddenCover}`),this.options.canClose&&(0===this.options.initialActivePage&&this.el.classList.add(this.classNames.atFrontCover),this.pages.item(0).classList.add(this.classNames.firstPage),this.pages.item(this.pages.length-1).classList.add(this.classNames.lastPage)),this.init()}getActivePages(){return this.pages=this.el.querySelectorAll(`.${this.classNames.page}, .${this.classNames.hiddenCover}`),Array.from(this.pages).reduce((e,s,t)=>(s.classList.contains(this.classNames.isActive)&&e.push(t),e),[])}init(){const{el:e,options:t,classNames:i}=this;e.classList.add(i.isReady);const a=t.canClose?"even":"odd",o=t.canClose?"odd":"even";let r,n=Array.from(e.querySelectorAll(`.${i.page}:nth-child(${a})`)),l=Array.from(e.querySelectorAll(`.${i.page}:nth-child(${o})`)),c=t.initialActivePage;if(1&c&&(c-=1),t.canClose)0===t.initialActivePage&&this.pages.item(0).classList.add(i.isActive);else{const s=document.createElement("div");s.classList.add(i.hiddenCover),e.prepend(s.cloneNode()),e.append(s.cloneNode())}(0!==t.initialActivePage&&t.canClose||!t.canClose)&&Array.from(this.pages).forEach((e,s)=>{s!==c&&s!==c+1||e.classList.add(i.isActive)}),t.initialCall&&l[0]&&(r=s.setupPageCalls(l,i,r)),t.previousButton&&(n=[...n,t.previousButton]),t.nextButton&&(l=[...l,t.nextButton]),n.forEach(e=>e.addEventListener("click",()=>this.turnPage("back"))),l.forEach(e=>e.addEventListener("click",()=>{this.turnPage("forward"),t.initialCall&&clearInterval(r)})),t.arrowKeys&&s.handleArrowKeys(t,r,this.turnPage.bind(this),this.Modernizr)}isLastPage(){const e=this.getActivePages()[0];return this.pages.length-1===e}isFirstPage(){return 0===this.getActivePages()[0]}turnPage(e){const{el:t,options:i,classNames:a,Modernizr:o}=this,r=t.querySelectorAll(`.${a.page}.${a.isActive}`);let n=t.querySelectorAll(`.${a.page}.${a.isAnimating}`);const l=t.querySelectorAll(`.${a.page}, .${a.hiddenCover}`);if(!o.preserve3d&&n.length>2)return;const c=this.getActivePages()[0],d=c+1,h=c===(i.canClose?0:1)&&"back"===e,g=d===(i.canClose?this.pages.length-1:this.pages.length-2)&&"forward"===e;if(h||g)return;let p,v;if("number"==typeof e){const s=1&e,t=(i.canClose?s:!s)?e:e+1,a=t-1;if(a===c)return;a>d?(e="forward",p=a,v=p+1):(e="back",p=t,v=p-1)}else p="forward"===e?d+1:c-1,v="forward"===e?d+2:c-2;"forward"===e&&2===p&&(p=1,v=2);const u="back"===e?r.item(0):r.item(1);let f=l.item(p),m=[f];-1!==v&&(m=[...m,l.item(v)]),n=[f,u],r.forEach(e=>{e.classList.remove(a.isActive),e.classList.add(a.wasActive)}),m.forEach(e=>{e&&e.classList.add(a.isActive)}),o.csstransforms3d&&n.forEach(e=>{e&&e.classList.add(a.isAnimating)}),s.handleAnimationEnd(n,r,this),i.canClose&&s.handleAtCovers(m,a,e,t),i.onPageTurn(t,{pagesActive:r,children:l})}static setupPageCalls(e,s,t){const i=()=>{e[0].classList.add(s.isCalling),setTimeout(()=>{e[0].classList.remove(s.isCalling)},900)};return setTimeout(i,500),setInterval(i,3e3)}static handleArrowKeys(e,s,t,i){document.addEventListener("keydown",({keyCode:a})=>{const o=i.csstransforms3d?39:37;a===(i.csstransforms3d?37:39)&&t("back"),a===o&&(t("forward"),e.initialCall&&clearInterval(s))})}static handleAnimationEnd(e,s,t){e.forEach(i=>{if(!i)return;["webkitTransitionEnd","oTransitionEnd","msTransitionEnd","transitionend"].forEach(a=>{i.addEventListener(a,()=>{e.forEach(e=>{e&&e.classList.remove(t.classNames.isAnimating)}),s.forEach(e=>{e.classList.remove(t.classNames.wasActive)})})})})}static handleAtCovers(e,s,t,i){const a=e[e.length-1],o=e[0]&&e[0].classList.contains(s.lastPage),r=a&&a.classList.contains(s.firstPage);"back"===t&&r?(i.classList.remove(s.atBackCover),i.classList.add(s.atFrontCover)):"forward"===t&&o?(i.classList.remove(s.atFrontCover),i.classList.add(s.atBackCover)):(i.classList.remove(s.atBackCover),i.classList.remove(s.atFrontCover))}}"undefined"Line truncated
|
||||
+1
@@ -0,0 +1 @@
|
||||
.c-flipbook{perspective:2200px;transform-style:preserve-3d;opacity:1;height:200px;position:absolute;left:0;transition:left .7s;top:0}.c-flipbook.at-front-cover{left:-25%}.c-flipbook.at-rear-cover{left:25%}.c-flipbook:not(.is-ready) *{transition:none!important}.c-flipbook:after{content:'';display:table;clear:both}.c-flipbook[data-useragent*='MSIE 10.0'] .c-flipbook__page{opacity:0}.c-flipbook[data-useragent*='MSIE 10.0'] .c-flipbook__page.is-active{transition:opacity .9s ease,transform .9s ease;opacity:1}.c-flipbook[data-useragent*='MSIE 10.0'] .c-flipbook__page.was-active{transition-delay:2s;transition:opacity .9s ease,transform .9s ease;opacity:0}.is-calling{transform:rotateY(-20deg)!important}.c-flipbook__page{cursor:pointer;overflow:hidden;position:absolute;width:50%;background:#efeef4;backface-visibility:hidden;transform:rotateY(0);user-select:none;transition:transform .9s ease}.c-flipbook__page.is-active{z-index:2}.c-flipbook__page.was-active{z-index:1}.c-flipbook__page.is-animating:nth-child(odd){z-index:4}.c-flipbook__page.is-animating:nth-child(odd)~.c-flipbook__page.is-animating{z-index:3}.c-flipbook__page.is-animating+.c-flipbook__page:not(.is-animating):nth-child(odd){z-index:1}.c-flipbook__page:nth-child(2n){transform-origin:100%;left:0;border-radius:6px 0 0 6px}.c-flipbook__page:nth-child(2n).is-active{transform:rotateY(10deg)}.c-flipbook__page:nth-child(2n).is-active:hover{transform:rotateY(15deg)}.c-flipbook__page:nth-child(2n):not(.last-page){border-right:none}.c-flipbook__page:nth-child(2n).is-active:hover{transform:rotateY(5deg)}.c-flipbook__page:nth-child(odd){transform-origin:0;right:0;transform:rotateY(-180deg);border-radius:0 6px 6px 0}.c-flipbook__page:nth-child(odd).is-active{transform:rotateY(-10deg)}.c-flipbook__page:nth-child(odd).is-active:hover{transform:rotateY(-15deg)}.c-flipbook__page:nth-child(odd):not(.first-page){border-left:none}.c-flipbook__page:nth-child(odd).is-active~.c-flipbook__page:nth-child(2n){transform:rotateY(180deg)}.c-flipbook__page:nth-child(odd).is-active~.c-flipbook__page:nth-child(odd){transform:rotateY(0)}.c-flipbook__page:nth-child(odd).is-active:hover{transform:rotateY(-5deg)}.c-flipbook__page.is-active:not(:hover){transform:rotateY(0)}.c-flipbook__page:before{content:'';position:absolute;z-index:3;right:0;width:100%;height:100%;background-size:100% 100%}.no-csstransforms3d .c-flipbook__page{display:none}.no-csstransforms3d .c-flipbook__page.is-active{display:block;position:relative;float:left}.c-flipbook-image{position:relative;z-index:2;height:auto;width:100%;display:block;pointer-events:none}.c-flipbook__page .ss-loading{font-size:2rem;position:absolute;z-index:1;top:0;bottom:0;width:100%;display:flex}.c-flipbook__page .ss-loading:before{display:flex;align-items:center;justify-content:center;width:100%}@supports (transition:transform 0.9s ease) and (not (-ms-ime-align:auto)){.c-flipbook__page{transition:transform .9s ease}}
|
||||
Vendored
+28
@@ -0,0 +1,28 @@
|
||||
/**
|
||||
* @licstart The following is the entire license notice for the
|
||||
* JavaScript code in this page
|
||||
*
|
||||
* Copyright 2024 Mozilla Foundation
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
* @licend The above is the entire license notice for the
|
||||
* JavaScript code in this page
|
||||
*/
|
||||
/**
|
||||
* pdfjsVersion = 5.6.205
|
||||
* pdfjsBuild = ada343803
|
||||
*/const t=!("object"!=typeof process||process+""!="[object process]"||process.versions.nw||process.versions.electron&&process.type&&"browser"!==process.type),e=[.001,0,0,.001,0,0],i=1.35,n=1,s=2,r=4,a=16,o=32,l=64,h=128,d=256,c={DISABLE:0,ENABLE:1,ENABLE_FORMS:2,ENABLE_STORAGE:3},u="pdfjs_internal_editor_",p={DISABLE:-1,NONE:0,FREETEXT:3,HIGHLIGHT:9,STAMP:13,INK:15,POPUP:16,SIGNATURE:101,COMMENT:102},g={RESIZE:1,CREATE:2,FREETEXT_SIZE:11,FREETEXT_COLOR:12,FREETEXT_OPACITY:13,INK_COLOR:21,INK_THICKNESS:22,INK_OPACITY:23,HIGHLIGHT_COLOR:31,HIGHLIGHT_THICKNESS:32,HIGHLIGHT_FREE:33,HIGHLIGHT_SHOW_ALL:34,DRAW_STEP:41},m={PRINT:4,MODIFY_CONTENTS:8,COPY:16,MODIFY_ANNOTATIONS:32,FILL_INTERACTIVE_FORMS:256,COPY_FOR_ACCESSIBILITY:512,ASSEMBLE:1024,PRINT_HIGH_QUALITY:2048},f=1,b=2,y=0,w=1,A=2,v=3,x=3,E=4,C={GRAYSCALE_1BPP:1,RGB_24BPP:2,RGBA_32BPP:3},T={TEXT:1,LINK:2,FREETEXT:3,LINE:4,SQUARE:5,CIRCLE:6,POLYGON:7,POLYLINE:8,HIGHLIGHT:9,UNDERLINE:10,SQUIGGLY:11,STRIKEOUT:12,STAMP:13,CARET:14,INK:15,POPUP:16,FILEATTACHMENT:17,SOUND:18,MOVIE:19,WIDGET:20,SCREEN:21,PRINTERMARK:22,TRAPNET:23,WATERMARK:24,THREED:25,REDACT:26},S=1,_=2,D=3,P=4,M=5,k={ERRORS:0,WARNINGS:1,INFOS:5},I={dependency:1,setLineWidth:2,setLineCap:3,setLineJoin:4,setMiterLimit:5,setDash:6,setRenderingIntent:7,setFlatness:8,setGState:9,save:10,restore:11,transform:12,moveTo:13,lineTo:14,curveTo:15,curveTo2:16,curveTo3:17,closePath:18,rectangle:19,stroke:20,closeStroke:21,fill:22,eoFill:23,fillStroke:24,eoFillStroke:25,closeFillStroke:26,closeEOFillStroke:27,endPath:28,clip:29,eoClip:30,beginText:31,endText:32,setCharSpacing:33,setWordSpacing:34,setHScale:35,setLeading:36,setFont:37,setTextRenderingMode:38,setTextRise:39,moveText:40,setLeadingMoveText:41,setTextMatrix:42,nextLine:43,showText:44,showSpacedText:45,nextLineShowText:46,nextLineSetSpacingShowText:47,setCharWidth:48,setCharWidthAndBounds:49,setStrokeColorSpace:50,setFillColorSpace:51,setStrokeColor:52,setStrokeColorN:53,setFillColor:54,setFillColorN:55,setStrokeGray:56,setFillGray:57,setStrokeRGBColor:58,setFillRGBColor:59,setStrokeCMYKColor:60,setFillCMYKColor:61,shadingFill:62,beginInlineImage:63,beginImageData:64,endInlineImage:65,paintXObject:66,markPoint:67,markPointProps:68,beginMarkedContent:69,beginMarkedContentProps:70,endMarkedContent:71,beginCompat:72,endCompat:73,paintFormXObjectBegin:74,paintFormXObjectEnd:75,beginGroup:76,endGroup:77,beginAnnotation:80,endAnnotation:81,paintImageMaskXObject:83,paintImageMaskXObjectGroup:84,paintImageXObject:85,paintInlineImageXObject:86,paintInlineImageXObjectGroup:87,paintImageXObjectRepeat:88,paintImageMaskXObjectRepeat:89,paintSolidColorImageMask:90,constructPath:91,setStrokeTransparent:92,setFillTransparent:93,rawFillPath:94},F=0,B=1,R=2,L=3,O=4,N={NEED_PASSWORD:1,INCORRECT_PASSWORD:2};let U=k.WARNINGS;function setVerbosityLevel(t){Number.isInteger(t)&&(U=t)}function getVerbosityLevel(){return U}function info(t){U>=k.INFOS&&console.info(`Info: ${t}`)}function warn(t){U>=k.WARNINGS&&console.warn(`Warning: ${t}`)}function unreachable(t){throw new Error(t)}function assert(t,e){t||unreachable(e)}function createValidAbsoluteUrl(t,e=null,i=null){if(!t)return null;if(i&&"string"==typeof t){if(i.addDefaultProtocol&&t.startsWith("www.")){const e=t.match(/\./g);e?.length>=2&&(t=`http://${t}`)}if(i.tryConvertEncoding)try{t=function stringToUTF8String(t){return decodeURIComponent(escape(t))}(t)}catch{}}const n=e?URL.parse(t,e):URL.parse(t);return function _isValidProtocol(t){switch(t?.protocol){case"http:":case"https:":case"ftp:":case"mailto:":case"tel:":return!0;default:return!1}}(n)?n:null}function updateUrlHash(t,e,i=!1){const n=URL.parse(t);if(n){n.hash=e;return n.href}return i&&createValidAbsoluteUrl(t,"http://example.com")?t.split("#",1)[0]+""+(e?`#${e}`:""):""}function stripPath(t){return t.substring(t.lastIndexOf("/")+1)}function shadow(t,e,i,n=!1){Object.defineProperty(t,e,{value:i,enumerable:!n,configurable:!0,writable:!1});return i}const H=function BaseExceptionClosure(){function BaseException(t,e){this.message=t;this.name=e}BaseException.prototype=new Error;BaseException.constructor=BaseException;return BaseException}();class PasswordException extends H{constructor(t,e){super(t,"PasswordException");this.code=e}}class UnknownErrorException extends H{constructor(t,e){super(t,"UnknownErrorException");this.details=e}}class InvalidPDFException extends H{constructor(t){super(t,"InvalidPDFException")}}class ResponseException extends H{constructor(t,e,i){super(t,"ResponseException");this.status=e;this.missing=i}}class FormatError extends H{constructor(t){super(t,"FormatError")}}class AbortException extends H{constructor(t){super(t,"AbortException")}}function stringToBytes(t){"string"!=typeof t&&unreachable("Invalid argument for stringToBytes");const e=t.length,i=new Uint8Array(e);for(let n=0;n<e;++n)i[n]=255&t.charCodeAt(n);return i}class FeatureTest{static get isLittleEndian(){return shadow(this,"isLittleEndian",function isLittleEndian(){const t=new
Line truncated
|
||||
/*webpackIgnore: true*/
|
||||
/*@vite-ignore*/
|
||||
this.workerSrc)).WorkerMessageHandler})())}}class WorkerTransport{downloadInfoCapability=Promise.withResolvers();#mr=null;#fr=new Map;#br=null;#yr=new Map;#wr=new Map;#Ar=new Map;#vr=null;constructor(t,e,i,n,s,r){this.messageHandler=t;this.loadingTask=e;this.#br=i;this.commonObjs=new PDFObjects;this.fontLoader=new FontLoader({ownerDocument:n.ownerDocument,styleElement:n.styleElement});this.enableHWA=n.enableHWA;this.loadingParams=n.loadingParams;this._params=n;this.canvasFactory=s.canvasFactory;this.filterFactory=s.filterFactory;this.binaryDataFactory=s.binaryDataFactory;this.pagesMapper=r;this.destroyed=!1;this.destroyCapability=null;this.setupMessageHandler()}updatePage(t){const{_pageIndex:e}=t;this.#yr.set(e,t);this.#wr.set(e,Promise.resolve(t))}#xr(t,e=null){return this.#fr.getOrInsertComputed(t,()=>this.messageHandler.sendWithPromise(t,e))}#Qn({loaded:t,total:e}){this.loadingTask.onProgress?.({loaded:t,total:e,percent:e?MathClamp(Math.round(t/e*100),0,100):NaN})}get annotationStorage(){return shadow(this,"annotationStorage",new AnnotationStorage)}getRenderingIntent(t,e=c.ENABLE,i=null,u=!1,p=!1){let g=s,m=Q;switch(t){case"any":g=n;break;case"display":break;case"print":g=r;break;default:warn(`getRenderingIntent - invalid intent: ${t}`)}const f=g&r&&i instanceof PrintAnnotationStorage?i:this.annotationStorage;switch(e){case c.DISABLE:g+=l;break;case c.ENABLE:break;case c.ENABLE_FORMS:g+=a;break;case c.ENABLE_STORAGE:g+=o;m=f.serializable;break;default:warn(`getRenderingIntent - invalid annotationMode: ${e}`)}u&&(g+=h);p&&(g+=d);const{ids:b,hash:y}=f.modifiedIds;return{renderingIntent:g,cacheKey:[g,m.hash,y].join("_"),annotationStorageSerializable:m,modifiedIds:b}}destroy(){if(this.destroyCapability)return this.destroyCapability.promise;this.destroyed=!0;this.destroyCapability=Promise.withResolvers();this.#vr?.reject(new Error("Worker was destroyed during onPassword callback"));const t=[];for(const e of this.#yr.values())t.push(e._destroy());this.#yr.clear();this.#wr.clear();this.#Ar.clear();this.hasOwnProperty("annotationStorage")&&this.annotationStorage.resetModified();const e=this.messageHandler.sendWithPromise("Terminate",null);t.push(e);Promise.all(t).then(()=>{this.commonObjs.clear();this.fontLoader.clear();this.#fr.clear();this.filterFactory.destroy();TextLayer.cleanup();this.#br?.cancelAllRequests(new AbortException("Worker was terminated."));this.messageHandler?.destroy();this.messageHandler=null;this.destroyCapability.resolve()},this.destroyCapability.reject);return this.destroyCapability.promise}setupMessageHandler(){const{messageHandler:t,loadingTask:e}=this;t.on("GetReader",(t,e)=>{assert(this.#br,"GetReader - no `BasePDFStream` instance available.");this.#mr=this.#br.getFullReader();this.#mr.onProgress=t=>this.#Qn(t);e.onPull=()=>{this.#mr.read().then(function({value:t,done:i}){if(i)e.close();else{assert(t instanceof ArrayBuffer,"GetReader - expected an ArrayBuffer.");e.enqueue(new Uint8Array(t),1,[t])}}).catch(t=>{e.error(t)})};e.onCancel=t=>{this.#mr.cancel(t);e.ready.catch(t=>{if(!this.destroyed)throw t})}});t.on("ReaderHeadersReady",async t=>{await this.#mr.headersReady;const{isStreamingSupported:e,isRangeSupported:i,contentLength:n}=this.#mr;e&&i&&(this.#mr.onProgress=null);return{isStreamingSupported:e,isRangeSupported:i,contentLength:n}});t.on("GetRangeReader",(t,e)=>{assert(this.#br,"GetRangeReader - no `BasePDFStream` instance available.");const i=this.#br.getRangeReader(t.begin,t.end);if(i){e.onPull=()=>{i.read().then(function({value:t,done:i}){if(i)e.close();else{assert(t instanceof ArrayBuffer,"GetRangeReader - expected an ArrayBuffer.");e.enqueue(new Uint8Array(t),1,[t])}}).catch(t=>{e.error(t)})};e.onCancel=t=>{i.cancel(t);e.ready.catch(t=>{if(!this.destroyed)throw t})}}else e.close()});t.on("GetDoc",({pdfInfo:t})=>{this.pagesMapper.pagesNumber=t.numPages;this._numPages=t.numPages;this._htmlForXfa=t.htmlForXfa;delete t.htmlForXfa;e._capability.resolve(new PDFDocumentProxy(t,this))});t.on("DocException",t=>{e._capability.reject(wrapReason(t))});t.on("PasswordRequest",t=>{this.#vr=Promise.withResolvers();try{if(!e.onPassword)throw wrapReason(t);const updatePassword=t=>{t instanceof Error?this.#vr.reject(t):this.#vr.resolve({password:t})};e.onPassword(updatePassword,t.code)}catch(t){this.#vr.reject(t)}return this.#vr.promise});t.on("DataLoaded",t=>{this.#Qn({loaded:t.length,total:t.length});this.downloadInfoCapability.resolve(t)});t.on("StartRenderPage",t=>{if(this.destroyed)return;this.#yr.get(t.pageIndex)._startRenderPage(t.transparency,t.cacheKey)});t.on("commonobj",([e,i,n])=>{if(this.destroyed)return null;if(this.commonObjs.has(e))return null;switch(i){case"Font":if("error"in n){const t=n.error;warn(`Error during font loading: ${t}`);this.commonObjs.resolve(e,t);break}const s=new FontInfo(n),r=this._params.pdfBug&&globalThis.FontInspector?.enabled?(t,e)=>globalThis.FontInspector.fontAdded(t,e):null,a=new FontFaceObject(s,r,n.charProcOperatorList,n.extra);this.fontLoader.bindLine truncated
|
||||
+28
@@ -0,0 +1,28 @@
|
||||
/**
|
||||
* @licstart The following is the entire license notice for the
|
||||
* JavaScript code in this page
|
||||
*
|
||||
* Copyright 2024 Mozilla Foundation
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
* @licend The above is the entire license notice for the
|
||||
* JavaScript code in this page
|
||||
*/
|
||||
/**
|
||||
* pdfjsVersion = 5.6.205
|
||||
* pdfjsBuild = ada343803
|
||||
*/const e=!("object"!=typeof process||process+""!="[object process]"||process.versions.nw||process.versions.electron&&process.type&&"browser"!==process.type),t=[.001,0,0,.001,0,0],n=1.35,a=.35,s=.25925925925925924,r=1,i=2,o=4,l=8,f=16,c=64,h=128,u=256,m="pdfjs_internal_editor_",p=3,d=9,g=13,b=15,w=101,j={PRINT:4,MODIFY_CONTENTS:8,COPY:16,MODIFY_ANNOTATIONS:32,FILL_INTERACTIVE_FORMS:256,COPY_FOR_ACCESSIBILITY:512,ASSEMBLE:1024,PRINT_HIGH_QUALITY:2048},k=1,y=2,q=3,v=0,S=4,x=1,C=2,F=3,T={TEXT:1,LINK:2,FREETEXT:3,LINE:4,SQUARE:5,CIRCLE:6,POLYGON:7,POLYLINE:8,HIGHLIGHT:9,UNDERLINE:10,SQUIGGLY:11,STRIKEOUT:12,STAMP:13,CARET:14,INK:15,POPUP:16,FILEATTACHMENT:17,SOUND:18,MOVIE:19,WIDGET:20,SCREEN:21,PRINTERMARK:22,TRAPNET:23,WATERMARK:24,THREED:25,REDACT:26},O="Group",R="R",H=1,D=2,M=4,N=16,E=32,z=128,L=512,U=1,_=2,X=4096,W=8192,K=32768,V=65536,G=131072,$=1048576,Y=2097152,J=8388608,Q=16777216,Z=1,ee=2,te=3,ne=4,ae=5,se={E:"Mouse Enter",X:"Mouse Exit",D:"Mouse Down",U:"Mouse Up",Fo:"Focus",Bl:"Blur",PO:"PageOpen",PC:"PageClose",PV:"PageVisible",PI:"PageInvisible",K:"Keystroke",F:"Format",V:"Validate",C:"Calculate"},re={WC:"WillClose",WS:"WillSave",DS:"DidSave",WP:"WillPrint",DP:"DidPrint"},ie={O:"PageOpen",C:"PageClose"},oe=1,le=5,fe=1,ce=2,he=3,ue=4,me=5,pe=6,de=7,ge=8,be=9,we=10,je=11,ke=12,ye=13,qe=14,ve=15,Se=16,Ae=17,xe=18,Ce=19,Ie=20,Fe=21,Te=22,Oe=23,Re=24,He=25,Be=26,De=27,Me=28,Pe=29,Ne=30,Ee=31,ze=32,Le=33,Ue=34,_e=35,Xe=36,We=37,Ke=38,Ve=39,Ge=40,$e=41,Ye=42,Je=43,Qe=44,Ze=45,et=46,tt=47,nt=48,at=49,st=50,rt=51,it=52,ot=53,lt=54,ft=55,ct=56,ht=57,ut=58,mt=59,pt=60,dt=61,gt=62,bt=63,wt=64,jt=65,kt=66,yt=67,qt=68,vt=69,St=70,At=71,xt=72,Ct=73,It=74,Ft=75,Tt=76,Ot=77,Rt=80,Ht=81,Bt=83,Dt=84,Mt=85,Pt=86,Nt=87,Et=88,zt=89,Lt=90,Ut=91,_t=92,Xt=93,Wt=94,Kt=0,Vt=1,Gt=2,$t=3,Yt=4,Jt=1,Qt=2;let Zt=oe;function getVerbosityLevel(){return Zt}function info(e){Zt>=le&&console.info(`Info: ${e}`)}function warn(e){Zt>=oe&&console.warn(`Warning: ${e}`)}function unreachable(e){throw new Error(e)}function assert(e,t){e||unreachable(t)}function createValidAbsoluteUrl(e,t=null,n=null){if(!e)return null;if(n&&"string"==typeof e){if(n.addDefaultProtocol&&e.startsWith("www.")){const t=e.match(/\./g);t?.length>=2&&(e=`http://${e}`)}if(n.tryConvertEncoding)try{e=stringToUTF8String(e)}catch{}}const a=t?URL.parse(e,t):URL.parse(e);return function _isValidProtocol(e){switch(e?.protocol){case"http:":case"https:":case"ftp:":case"mailto:":case"tel:":return!0;default:return!1}}(a)?a:null}function shadow(e,t,n,a=!1){Object.defineProperty(e,t,{value:n,enumerable:!a,configurable:!0,writable:!1});return n}const en=function BaseExceptionClosure(){function BaseException(e,t){this.message=e;this.name=t}BaseException.prototype=new Error;BaseException.constructor=BaseException;return BaseException}();class PasswordException extends en{constructor(e,t){super(e,"PasswordException");this.code=t}}class UnknownErrorException extends en{constructor(e,t){super(e,"UnknownErrorException");this.details=t}}class InvalidPDFException extends en{constructor(e){super(e,"InvalidPDFException")}}class ResponseException extends en{constructor(e,t,n){super(e,"ResponseException");this.status=t;this.missing=n}}class FormatError extends en{constructor(e){super(e,"FormatError")}}class AbortException extends en{constructor(e){super(e,"AbortException")}}function bytesToString(e){"object"==typeof e&&void 0!==e?.length||unreachable("Invalid argument for bytesToString");const t=e.length,n=8192;if(t<n)return String.fromCharCode.apply(null,e);const a=[];for(let s=0;s<t;s+=n){const r=Math.min(s+n,t),i=e.subarray(s,r);a.push(String.fromCharCode.apply(null,i))}return a.join("")}function stringToBytes(e){"string"!=typeof e&&unreachable("Invalid argument for stringToBytes");const t=e.length,n=new Uint8Array(t);for(let a=0;a<t;++a)n[a]=255&e.charCodeAt(a);return n}function string32(e){return String.fromCharCode(e>>24&255,e>>16&255,e>>8&255,255&e)}function objectSize(e){return Object.keys(e).length}class FeatureTest{static get isLittleEndian(){return shadow(this,"isLittleEndian",function isLittleEndian(){const e=new Uint8Array(4);e[0]=1;return 1===new Uint32Array(e.buffer,0,1)[0]}())}static get isEvalSupported(){return shadow(this,"isEvalSupported",function isEvalSupported(){try{new Function("");return!0}catch{return!1}}())}static get isOffscreenCanvasSupported(){return shadow(this,"isOffscreenCanvasSupported","undefined"!=typeof OffscreenCanvas)}static get isImageDecoderSupported(){return shadow(this,"isImageDecoderSupported","undefined"!=typeof ImageDecoder)}static get isFloat16ArraySupported(){return shadow(this,"isFloat16ArraySupported","undefined"!=typeof Float16Array)}static get isSanitizerSupported(){return shadow(this,"isSanitizerSupported","undefined"!=typeof Sanitizer)}static get platform(){const{platform:e,userAgent:t}=navigator;return shadow(this,"platform",{isAndroid:t.includes("Android"),isLinux:e.includes("Linux"),isMac:e.includes("Mac"),isWindows:e.includes("Win"),isLine truncated
|
||||
/*webpackIgnore: true*/
|
||||
/*@vite-ignore*/
|
||||
t)).default()}catch(e){warn(`JpxImage#getJsModule: ${e}`)}e(n)}static async#N(e,t,n){const a="openjpeg.wasm";try{this.#B||(this.#P?this.#B=await fetchBinaryData(`${this.#I}${a}`):this.#B=await this.#D.sendWithPromise("FetchBinaryData",{kind:"wasmUrl",filename:a}));return n((await WebAssembly.instantiate(this.#B,t)).instance)}catch(t){warn(`JpxImage#instantiateWasm: ${t}`);this.#U(e);return null}finally{this.#D=null}}static async decode(e,{numComponents:t=4,isIndexedColormap:n=!1,smaskInData:a=!1,reducePower:s=0}={}){if(!this.#M){const{promise:e,resolve:t}=Promise.withResolvers(),n=[e];this.#C?n.push(Jn({warn,instantiateWasm:this.#N.bind(this,t)})):this.#U(t);this.#M=Promise.race(n)}const r=await this.#M;if(!r)throw new JpxError("OpenJPEG failed to initialize");let i;try{const o=e.length;i=r._malloc(o);r.writeArrayToMemory(e,i);if(r._jp2_decode(i,o,t>0?t:0,!!n,!!a,s)){const{errorMessages:e}=r;if(e){delete r.errorMessages;throw new JpxError(e)}throw new JpxError("Unknown error")}const{imageData:l}=r;r.imageData=null;return l}finally{i&&r._free(i)}}static cleanup(){this.#M=null}static parseImageProperties(e){let t=e.getByte();for(;t>=0;){const n=t;t=e.getByte();if(65361===(n<<8|t)){e.skip(4);const t=e.getInt32()>>>0,n=e.getInt32()>>>0,a=e.getInt32()>>>0,s=e.getInt32()>>>0;e.skip(16);return{width:t-a,height:n-s,bitsPerComponent:8,componentsCount:e.getUint16()}}}throw new JpxError("No size marker found in JPX stream")}}function addState(e,t,n,a,s){let r=e;for(let e=0,n=t.length-1;e<n;e++){const n=t[e];r=r[n]||=[]}r[t.at(-1)]={checkFn:n,iterateFn:a,processFn:s}}const Qn=[];addState(Qn,[we,ke,Pt,je],null,function iterateInlineImageGroup(e,t){const n=e.fnArray,a=(t-(e.iCurr-3))%4;switch(a){case 0:return n[t]===we;case 1:return n[t]===ke;case 2:return n[t]===Pt;case 3:return n[t]===je}throw new Error(`iterateInlineImageGroup - invalid pos: ${a}`)},function foundInlineImageGroup(e,t){const n=e.fnArray,a=e.argsArray,s=e.iCurr,r=s-3,i=s-2,o=s-1,l=Math.min(Math.floor((t-r)/4),200);if(l<10)return t-(t-r)%4;let f=0;const c=[];let h=0,u=1,m=1;for(let e=0;e<l;e++){const t=a[i+(e<<2)],n=a[o+(e<<2)][0];if(u+n.width>1e3){f=Math.max(f,u);m+=h+2;u=0;h=0}c.push({transform:t,x:u,y:m,w:n.width,h:n.height});u+=n.width+2;h=Math.max(h,n.height)}const p=Math.max(f,u)+1,d=m+h+1,g=new Uint8Array(p*d*4),b=p<<2;for(let e=0;e<l;e++){const t=a[o+(e<<2)][0].data,n=c[e].w<<2;let s=0,r=c[e].x+c[e].y*p<<2;g.set(t.subarray(0,n),r-b);for(let a=0,i=c[e].h;a<i;a++){g.set(t.subarray(s,s+n),r);s+=n;r+=b}g.set(t.subarray(s-n,s),r);for(;r>=0;){t[r-4]=t[r];t[r-3]=t[r+1];t[r-2]=t[r+2];t[r-1]=t[r+3];t[r+n]=t[r+n-4];t[r+n+1]=t[r+n-3];t[r+n+2]=t[r+n-2];t[r+n+3]=t[r+n-1];r-=b}}const w={width:p,height:d};if(e.isOffscreenCanvasSupported){const e=new OffscreenCanvas(p,d);e.getContext("2d").putImageData(new ImageData(new Uint8ClampedArray(g.buffer),p,d),0,0);w.bitmap=e.transferToImageBitmap();w.data=null}else{w.kind=F;w.data=g}n.splice(r,4*l,Nt);a.splice(r,4*l,[w,c]);return r+1});addState(Qn,[we,ke,Bt,je],null,function iterateImageMaskGroup(e,t){const n=e.fnArray,a=(t-(e.iCurr-3))%4;switch(a){case 0:return n[t]===we;case 1:return n[t]===ke;case 2:return n[t]===Bt;case 3:return n[t]===je}throw new Error(`iterateImageMaskGroup - invalid pos: ${a}`)},function foundImageMaskGroup(e,t){const n=e.fnArray,a=e.argsArray,s=e.iCurr,r=s-3,i=s-2,o=s-1;let l=Math.floor((t-r)/4);if(l<10)return t-(t-r)%4;let f,c,h=!1;const u=a[o][0],m=a[i][0],p=a[i][1],d=a[i][2],g=a[i][3];if(p===d){h=!0;f=i+4;let e=o+4;for(let t=1;t<l;t++,f+=4,e+=4){c=a[f];if(a[e][0]!==u||c[0]!==m||c[1]!==p||c[2]!==d||c[3]!==g){t<10?h=!1:l=t;break}}}if(h){l=Math.min(l,1e3);const e=new Float32Array(2*l);f=i;for(let t=0;t<l;t++,f+=4){c=a[f];e[t<<1]=c[4];e[1+(t<<1)]=c[5]}n.splice(r,4*l,zt);a.splice(r,4*l,[u,m,p,d,g,e])}else{l=Math.min(l,100);const e=[];for(let t=0;t<l;t++){c=a[i+(t<<2)];const n=a[o+(t<<2)][0];e.push({data:n.data,width:n.width,height:n.height,interpolate:n.interpolate,count:n.count,transform:c})}n.splice(r,4*l,Dt);a.splice(r,4*l,[e])}return r+1});addState(Qn,[we,ke,Mt,je],function(e){const t=e.argsArray,n=e.iCurr-2;return 0===t[n][1]&&0===t[n][2]},function iterateImageGroup(e,t){const n=e.fnArray,a=e.argsArray,s=(t-(e.iCurr-3))%4;switch(s){case 0:return n[t]===we;case 1:if(n[t]!==ke)return!1;const s=e.iCurr-2,r=a[s][0],i=a[s][3];return a[t][0]===r&&0===a[t][1]&&0===a[t][2]&&a[t][3]===i;case 2:if(n[t]!==Mt)return!1;const o=a[e.iCurr-1][0];return a[t][0]===o;case 3:return n[t]===je}throw new Error(`iterateImageGroup - invalid pos: ${s}`)},function(e,t){const n=e.fnArray,a=e.argsArray,s=e.iCurr,r=s-3,i=s-2,o=a[s-1][0],l=a[i][0],f=a[i][3],c=Math.min(Math.floor((t-r)/4),1e3);if(c<3)return t-(t-r)%4;const h=new Float32Array(2*c);let u=i;for(let e=0;e<c;e++,u+=4){const t=a[u];h[e<<1]=t[4];h[1+(e<<1)]=t[5]}const m=[o,l,f,h];n.splice(r,4*c,Et);a.splice(r,4*c,m);return r+1});addState(Qn,[Ee,We,Ye,Qe,ze],null,function iterateShowTextGroup(e,t){const n=e.fnArray,a=e.argsArray,s=(t-(e.iCurr-4))%5;switch(s){casLine truncated
|
||||
@@ -0,0 +1,22 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Mummert\FlipbookBundle\Contao\Manager;
|
||||
|
||||
use Contao\CoreBundle\ContaoCoreBundle;
|
||||
use Contao\ManagerPlugin\Bundle\BundlePluginInterface;
|
||||
use Contao\ManagerPlugin\Bundle\Config\BundleConfig;
|
||||
use Contao\ManagerPlugin\Bundle\Parser\ParserInterface;
|
||||
use Mummert\FlipbookBundle\FlipbookBundle;
|
||||
|
||||
class Plugin implements BundlePluginInterface
|
||||
{
|
||||
public function getBundles(ParserInterface $parser): iterable
|
||||
{
|
||||
return [
|
||||
BundleConfig::create(FlipbookBundle::class)
|
||||
->setLoadAfter([ContaoCoreBundle::class]),
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Mummert\FlipbookBundle\Controller\ContentElement;
|
||||
|
||||
use Contao\ContentModel;
|
||||
use Contao\CoreBundle\Controller\ContentElement\AbstractContentElementController;
|
||||
use Contao\CoreBundle\DependencyInjection\Attribute\AsContentElement;
|
||||
use Contao\CoreBundle\Twig\FragmentTemplate;
|
||||
use Contao\FilesModel;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
|
||||
#[AsContentElement(type: 'blatterbares_pdf', category: 'media', template: 'content_element/blatterbares_pdf')]
|
||||
class BlatterbaresPdfController extends AbstractContentElementController
|
||||
{
|
||||
protected function getResponse(FragmentTemplate $template, ContentModel $model, Request $request): Response
|
||||
{
|
||||
$template->set('pdfUrl', $this->resolvePdfUrl($model));
|
||||
$template->set('showNavigation', '1' === (string) ($model->flipbookShowNavigation ?? '1'));
|
||||
$template->set('initialRenderPages', $this->normalizeInitialRenderPages((string) ($model->flipbookInitialPages ?? '4')));
|
||||
$template->set('startMode', $this->normalizeStartMode((string) ($model->flipbookStartMode ?? 'center')));
|
||||
|
||||
return $template->getResponse();
|
||||
}
|
||||
|
||||
private function resolvePdfUrl(ContentModel $model): string
|
||||
{
|
||||
$uuid = (string) ($model->flipbookPdfSrc ?? '');
|
||||
|
||||
if ('' === $uuid) {
|
||||
return '';
|
||||
}
|
||||
|
||||
$fileModel = FilesModel::findByUuid($uuid);
|
||||
|
||||
if (null === $fileModel) {
|
||||
return '';
|
||||
}
|
||||
|
||||
$path = trim((string) $fileModel->path, '/');
|
||||
|
||||
if ('' === $path || !str_ends_with(strtolower($path), '.pdf')) {
|
||||
return '';
|
||||
}
|
||||
|
||||
return '/'.implode('/', array_map('rawurlencode', explode('/', $path)));
|
||||
}
|
||||
|
||||
private function normalizeInitialRenderPages(string $value): int
|
||||
{
|
||||
$count = (int) $value;
|
||||
|
||||
if ($count < 2) {
|
||||
return 2;
|
||||
}
|
||||
|
||||
if ($count > 4) {
|
||||
return 4;
|
||||
}
|
||||
|
||||
return $count;
|
||||
}
|
||||
|
||||
private function normalizeStartMode(string $value): string
|
||||
{
|
||||
if ('spread' === $value || 'cover' === $value) {
|
||||
return 'spread';
|
||||
}
|
||||
|
||||
return 'center';
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Mummert\FlipbookBundle\DependencyInjection;
|
||||
|
||||
use Symfony\Component\Config\FileLocator;
|
||||
use Symfony\Component\DependencyInjection\ContainerBuilder;
|
||||
use Symfony\Component\DependencyInjection\Extension\Extension;
|
||||
use Symfony\Component\DependencyInjection\Loader\YamlFileLoader;
|
||||
|
||||
class FlipbookExtension extends Extension
|
||||
{
|
||||
public function load(array $configs, ContainerBuilder $container): void
|
||||
{
|
||||
$loader = new YamlFileLoader($container, new FileLocator(__DIR__.'/../../config'));
|
||||
$loader->load('services.yaml');
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Mummert\FlipbookBundle;
|
||||
|
||||
use Symfony\Component\HttpKernel\Bundle\Bundle;
|
||||
|
||||
class FlipbookBundle extends Bundle
|
||||
{
|
||||
public function getPath(): string
|
||||
{
|
||||
return dirname(__DIR__);
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user