Skip to content

Commit 8a82bcc

Browse files
committed
Finished compose examples and add basic structure for gh-pages example
1 parent d32b749 commit 8a82bcc

42 files changed

Lines changed: 1165 additions & 104 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

docker/nginx-test.conf

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ types {
55
text/css css;
66
image/png png;
77
image/jpeg jpg jpeg;
8+
application/pdf pdf;
89
}
910

1011
server {

min/core.min.js

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/SimplePDFviewer.js

Lines changed: 20 additions & 75 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,6 @@
2222
let isLibLoading = false;
2323
const initQueue = [];
2424

25-
// Session-only storage for anonymous/private browsing mode
2625
const memoryStorage = {};
2726

2827
/**
@@ -97,11 +96,9 @@
9796
return `<span class="icon-svg">${icon.svg}</span><span class="icon-fallback" role="img">${icon.fallback}</span>`;
9897
}
9998

100-
// Configuration constants
101-
const MIN_TOUCH_TARGET_SIZE = 44; // px for accessibility
102-
const RENDER_DEBOUNCE_DELAY = 150; // ms
99+
const MIN_TOUCH_TARGET_SIZE = 44;
100+
const RENDER_DEBOUNCE_DELAY = 150;
103101

104-
// Default theme colors
105102
const DEFAULT_THEME = {
106103
primary: '#3498db',
107104
primaryHover: '#2980b9',
@@ -114,9 +111,8 @@
114111
lightBorder: '#ddd'
115112
};
116113

117-
// Proportional relationships (calculated from default colors)
118114
const COLOR_RATIOS = {
119-
primaryToHover: -18, // Lightness change percentage
115+
primaryToHover: -18,
120116
primaryToActive: -36,
121117
sidebarToHover: 8,
122118
sidebarToBorder: -8
@@ -236,12 +232,11 @@
236232
const hsl = hexToHsl(hex);
237233
const lightness = hsl.l;
238234

239-
// Auto-adjust if too dark or too bright
240235
let adjustedL = lightness;
241236
if (lightness < 15) {
242-
adjustedL = 25; // Minimum usable lightness
237+
adjustedL = 25;
243238
} else if (lightness > 85) {
244-
adjustedL = 75; // Maximum usable lightness
239+
adjustedL = 75;
245240
}
246241

247242
const adjustedHex = hslToHex(hsl.h, hsl.s, adjustedL);
@@ -637,12 +632,10 @@
637632

638633
if (!prevBtn || !nextBtn || !instance.pdfDoc) return;
639634

640-
// Check if at beginning (first page of first chapter)
641635
const isAtBeginning = instance.currentPage === 1 &&
642636
instance.currentChapter === 0 &&
643637
instance.currentModule === 0;
644638

645-
// Check if at end (last page of last chapter)
646639
const isLastChapterOfModule = instance.currentModule === course.modules.length - 1 &&
647640
instance.currentChapter === course.modules[instance.currentModule].chapters.length - 1;
648641
const isAtEnd = instance.currentPage === instance.pdfDoc.numPages && isLastChapterOfModule;
@@ -693,35 +686,29 @@
693686

694687
const toggleLoading = (show) => loading.style.display = show ? 'flex' : 'none';
695688

696-
// Scroll lock to prevent automatic scroll restoration during navigation
697689
let isNavigating = false;
698690

699691
/**
700692
* Ensures scroll position is reset and stays at top-left (0,0)
701693
* Forces scroll to 0 using multiple aggressive methods
702694
*/
703695
const resetScroll = () => {
704-
// Step 1: Disable scroll-behavior to prevent smooth scroll interference
705696
const originalScrollBehavior = canvasContainer.style.scrollBehavior;
706697
canvasContainer.style.scrollBehavior = 'auto';
707698

708-
// Step 2: Multiple simultaneous scroll reset methods
709699
canvasContainer.scrollTop = 0;
710700
canvasContainer.scrollLeft = 0;
711701
canvasContainer.scroll(0, 0);
712702
canvasContainer.scrollTo(0, 0);
713703

714-
// Step 3: Aggressive continuous locking during rendering
715704
let lockCount = 0;
716705
const maxLocks = 15;
717706

718707
const forceLock = () => {
719708
if (lockCount < maxLocks & isNavigating) {
720-
// Read current position
721709
const currentTop = canvasContainer.scrollTop;
722710
const currentLeft = canvasContainer.scrollLeft;
723711

724-
// If not at 0, force it back
725712
if (currentTop !== 0 || currentLeft !== 0) {
726713
canvasContainer.scrollTop = 0;
727714
canvasContainer.scrollLeft = 0;
@@ -736,10 +723,8 @@
736723

737724
requestAnimationFrame(forceLock);
738725

739-
// Step 4: Final cleanup and release after 600ms
740726
setTimeout(() => {
741727
isNavigating = false;
742-
// Restore original scroll-behavior
743728
canvasContainer.style.scrollBehavior = originalScrollBehavior;
744729
}, 600);
745730
};
@@ -780,12 +765,10 @@
780765
* @param {number} newZoom - Zoom level (100-200)
781766
*/
782767
function applyZoom(newZoom) {
783-
// Validate zoom (100-200, integer only)
784768
newZoom = Math.max(100, Math.min(200, Math.floor(newZoom)));
785769

786770
if (newZoom === instance.zoom) return;
787771

788-
// Save current scroll position relative to content
789772
const scrollContainer = canvasContainer;
790773
const scrollRatio = {
791774
x: scrollContainer.scrollWidth > 0 ? scrollContainer.scrollLeft / scrollContainer.scrollWidth : 0,
@@ -794,7 +777,6 @@
794777

795778
instance.zoom = newZoom;
796779

797-
// Update UI
798780
const zoomInput = container.querySelector('.zoom-input');
799781
if (zoomInput) zoomInput.value = newZoom + '%';
800782

@@ -805,9 +787,7 @@
805787

806788
updateZoomUI();
807789

808-
// Re-render page with new zoom
809790
renderPage(instance.currentPage).then(() => {
810-
// Restore scroll position proportionally
811791
setTimeout(() => {
812792
if (scrollContainer.scrollWidth > 0) {
813793
scrollContainer.scrollLeft = scrollRatio.x * scrollContainer.scrollWidth;
@@ -848,11 +828,9 @@
848828

849829
return instance.pdfDoc.getPage(num).then(page => {
850830
try {
851-
// Get available width from parent container
852831
const container = canvas.parentElement;
853832
let availableWidth = container.clientWidth;
854833

855-
// Remove padding from available width for calculation
856834
const styles = window.getComputedStyle(container);
857835
const paddingLeft = parseFloat(styles.paddingLeft) || 0;
858836
const paddingRight = parseFloat(styles.paddingRight) || 0;
@@ -861,10 +839,8 @@
861839
const dpr = window.devicePixelRatio || 1;
862840
const viewport = page.getViewport({ scale: 1.0 });
863841

864-
// Calculate scale to fit available width with zoom applied
865842
let scale = (availableWidth / viewport.width) * (instance.zoom / 100);
866843

867-
// Ensure scale is at least 1.0 to avoid unnecessary horizontal scroll at 100% zoom
868844
if (instance.zoom <= 100) {
869845
scale = Math.max(1.0, scale);
870846
}
@@ -911,67 +887,48 @@
911887
if (!instance.enableTextSelection) return;
912888

913889
try {
914-
// Get the text layer container
915890
const textLayerDiv = container.querySelector('.pdf-viewer-text-layer');
916891
if (!textLayerDiv) return;
917892

918-
// Clear previous text layer
919893
textLayerDiv.innerHTML = '';
920894

921-
// Get text content from the page
922895
const textContent = await page.getTextContent();
923896

924897
if (!textContent || !textContent.items || textContent.items.length === 0) return;
925898

926-
// Set the text layer size and positioning
927899
const canvas = container.querySelector('.pdf-viewer-canvas');
928900
if (!canvas) return;
929901

930-
// Match canvas dimensions exactly
931902
textLayerDiv.style.width = canvas.offsetWidth + 'px';
932903
textLayerDiv.style.height = canvas.offsetHeight + 'px';
933904
textLayerDiv.style.top = canvas.offsetTop + 'px';
934905
textLayerDiv.style.left = canvas.offsetLeft + 'px';
935906

936-
// Get viewport information for coordinate conversion
937-
const dpr = window.devicePixelRatio || 1;
938907
const baseViewport = page.getViewport({ scale: 1.0 });
939908

940-
// Process each text item and create spans
941909
for (let item of textContent.items) {
942910
if (!item.str || item.str.trim() === '') continue;
943911

944-
// Create span element for text
945912
const span = document.createElement('span');
946913
span.textContent = item.str;
947914

948-
// Get transform matrix [a, b, c, d, e, f]
949-
// where: a, d = scaling; e, f = translation
950915
const transform = item.transform;
951916

952-
// Extract font size from scale coefficients
953917
const fontSize = Math.max(
954918
Math.abs(transform[0]),
955919
Math.abs(transform[3]),
956-
8 // Minimum font size
920+
8
957921
);
958922

959-
// Extract position - convert from PDF coordinates to screen coordinates
960-
// PDF coordinates: origin at bottom-left
961-
// Screen coordinates: origin at top-left
962-
const tx = transform[4]; // x translation
963-
const ty = transform[5]; // y translation
923+
const tx = transform[4];
924+
const ty = transform[5];
964925

965-
// Convert PDF coordinates to viewport coordinates
966-
// Apply scale to convert to actual viewport size
967926
const scaledX = tx * (scaledViewport.width / baseViewport.width);
968927
const scaledY = (baseViewport.height - ty) * (scaledViewport.height / baseViewport.height);
969928

970-
// Convert to percentages for responsive positioning
971929
const xPercent = (scaledX / scaledViewport.width) * 100;
972930
const yPercent = (scaledY / scaledViewport.height) * 100;
973931

974-
// Apply styles for text selection
975932
span.style.position = 'absolute';
976933
span.style.left = xPercent + '%';
977934
span.style.top = yPercent + '%';
@@ -996,14 +953,11 @@
996953
textLayerDiv.appendChild(span);
997954
}
998955

999-
// Make text layer visible for selection
1000956
textLayerDiv.style.display = 'block';
1001-
1002-
// Store reference for cleanup
1003957
instance.currentTextLayer = textLayerDiv;
958+
1004959
} catch (err) {
1005960
console.warn('Failed to render text layer:', err);
1006-
// Continue gracefully - text layer is optional
1007961
}
1008962
}
1009963

@@ -1157,7 +1111,6 @@
11571111
}
11581112

11591113

1160-
// Debounced resize render to prevent excessive rendering
11611114
const debouncedRender = debounce(() => {
11621115
if (instance.pdfDoc) renderPage(instance.currentPage);
11631116
}, RENDER_DEBOUNCE_DELAY);
@@ -1168,22 +1121,20 @@
11681121
const fullscreenBtn = container.querySelector('.fullscreen-btn');
11691122
const canvasContainer = container.querySelector('.pdf-viewer-canvas-container');
11701123

1171-
// Focus listener - clicking PDF area gives it focus for scroll control
11721124
canvasContainer.addEventListener('click', () => canvasContainer.focus());
1173-
1174-
// Scroll lock listener - prevents scroll restoration during navigation
11751125
canvasContainer.addEventListener('scroll', scrollLockHandler, true);
11761126

1177-
// Fullscreen state management
11781127
instance.isFullscreen = false;
11791128

1180-
// Sidebar collapse state management
11811129
const SIDEBAR_STORAGE_KEY = 'pdfViewer_sidebarCollapsed';
11821130
instance.sidebarCollapsed = Storage.getItem(SIDEBAR_STORAGE_KEY) === 'true';
11831131
if (instance.sidebarCollapsed) {
11841132
sidebar.classList.add('collapsed');
11851133
}
11861134

1135+
/**
1136+
* Toggles the PDF viewer between normal and full screen modes
1137+
*/
11871138
function toggleFullscreen() {
11881139
instance.isFullscreen = !instance.isFullscreen;
11891140
const pdfContainer = container.querySelector('.pdf-viewer-main');
@@ -1201,21 +1152,17 @@
12011152
prevBtn.onclick = goToPrev;
12021153
fullscreenBtn.onclick = toggleFullscreen;
12031154
toggleBtn.onclick = () => {
1204-
// On mobile: toggle sidebar visibility (open/close)
1205-
// On desktop: toggle sidebar collapse state
12061155
if (window.innerWidth < 768) {
12071156
const isOpen = sidebar.classList.toggle('open');
12081157
toggleBtn.setAttribute('aria-expanded', isOpen);
12091158
} else {
1210-
// Desktop: toggle collapse state
12111159
instance.sidebarCollapsed = !instance.sidebarCollapsed;
12121160
sidebar.classList.toggle('collapsed');
12131161
Storage.setItem(SIDEBAR_STORAGE_KEY, instance.sidebarCollapsed);
12141162
toggleBtn.setAttribute('aria-pressed', instance.sidebarCollapsed);
12151163
}
12161164
};
12171165

1218-
// Zoom controls
12191166
const zoomOutBtn = container.querySelector('.zoom-out-btn');
12201167
const zoomInBtn = container.querySelector('.zoom-in-btn');
12211168
const zoomInput = container.querySelector('.zoom-input');
@@ -1255,39 +1202,36 @@
12551202
}
12561203
};
12571204

1258-
// Store event listener for cleanup
12591205
instance.listeners.keyHandler = keyHandler;
12601206

12611207
document.addEventListener('keydown', keyHandler);
12621208

1263-
// Focus-based scroll handler to prevent overflow scrolling
1209+
/**
1210+
* Mouse wheel event handler for navigating PDF pages when the PDF container is in focus.
1211+
* Prevents default page scrolling when the user is at the top or bottom of the PDF container and tries to scroll outward.
1212+
* @param {WheelEvent} e - The event object.
1213+
* @returns {void}
1214+
*/
12641215
const wheelHandler = (e) => {
1265-
// Only handle scroll when PDF container has focus
12661216
if (canvasContainer === document.activeElement || canvasContainer.contains(e.target)) {
1267-
// PDF container has focus
12681217
const container = canvasContainer;
12691218
const isAtBottom = container.scrollHeight - container.scrollTop <= container.clientHeight + 1;
12701219
const isAtTop = container.scrollTop === 0;
12711220
const scrollingDown = e.deltaY > 0;
12721221
const scrollingUp = e.deltaY < 0;
12731222

1274-
// Prevent default if we're at the boundaries and trying to scroll outward
12751223
if ((isAtBottom && scrollingDown) || (isAtTop && scrollingUp)) {
12761224
e.preventDefault();
12771225
}
12781226
return;
12791227
}
1280-
// Outside PDF focus - allow page scroll normally
12811228
};
12821229

12831230
instance.listeners.wheelHandler = wheelHandler;
1284-
// Use non-passive listener to allow preventDefault
12851231
canvasContainer.addEventListener('wheel', wheelHandler, { passive: false });
12861232

12871233
const ro = new ResizeObserver(debouncedRender);
12881234
ro.observe(canvas.parentElement);
1289-
1290-
// Store ResizeObserver for cleanup
12911235
instance.resizeObserver = ro;
12921236

12931237
/**
@@ -1331,6 +1275,7 @@
13311275

13321276
return instance;
13331277
}
1334-
1278+
13351279
global.PDFViewer = { init };
1280+
13361281
})(window);

test-example/001_basic_usage/index.html

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
<!DOCTYPE html>
22
<html lang="en">
33
<head>
4+
<meta charset="UTF-8">
5+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
6+
<meta http-equiv="X-UA-Compatible" content="ie=edge">
47
<title>Basic Usage Example</title>
58
<link rel="icon" type="image/png" href="./favicon.png">
69
</head>

0 commit comments

Comments
 (0)