(function($) { "use strict"; if (typeof window.initEbookModule === 'function') { return; } window.initEbookModule = function(moduleId, pdfUrl, config) { var $module = $('#' + moduleId); if (!$module.length || !pdfUrl) return; var pdfDoc = null; var pageCount = 0; var currentScale = 1.0; var initialLoadCount = 6; var isInitialized = false; var loadedPageCount = 0; var renderedCount = 0; var $flipbook = $module.find('.flipbook'); var $loadingOverlay = $module.find('.ebook-loading-overlay'); var $loadingText = $module.find('.loading-text'); var $viewport = $module.find('.flipbook-viewport'); var $controls = $module.find('.ebook-controls'); var $totalPages = $module.find('.total-pages'); var $loadedPages = $module.find('.loaded-pages'); var $pageInput = $module.find('.page-input'); var $totalPagesWrap = $module.find('.total-pages-wrap'); var $alertMsg = $module.find('.page-loading-alert'); function startEbookProcess() { if ($flipbook.turn('is')) { $flipbook.turn('destroy'); } $flipbook.empty(); $loadingOverlay.show(); $viewport.add($controls).addClass('loading'); var availableWidth = $viewport.width() || config.width || 922; var availableHeight = $viewport.height() || config.height || 600; var bookWidth, bookHeight; function renderAllPages(pdf) { pdfDoc = pdf; pageCount = pdf.numPages; $totalPages.text(pageCount); $pageInput.attr('max', pageCount); pdf.getPage(1).then(function(page) { var viewport = page.getViewport({ scale: 1 }); var pageRatio = viewport.width / viewport.height; var bookRatio = pageRatio * 2; bookWidth = availableWidth; bookHeight = bookWidth / bookRatio; if (bookHeight > availableHeight) { bookHeight = availableHeight; bookWidth = bookHeight * bookRatio; } var initialPromises = []; var loadLimit = Math.min(pageCount, initialLoadCount); for (var i = 1; i <= loadLimit; i++) { initialPromises.push(renderPage(i, bookWidth, bookHeight, true, loadLimit)); } Promise.all(initialPromises).then(function(pages) { pages.forEach(function(pageDiv) { $flipbook.append(pageDiv); }); initTurnJs(bookWidth, bookHeight); isInitialized = true; loadedPageCount = loadLimit; updatePageDisplay(); $loadingOverlay.fadeOut(500); $viewport.add($controls).removeClass('loading'); if (pageCount > initialLoadCount) { loadRemainingPages(initialLoadCount + 1, bookWidth, bookHeight); } else { updatePageDisplay(true); } }); }); } if (pdfDoc) { run(pdfDoc); } else { pdfjsLib.getDocument(pdfUrl).promise.then(renderAllPages); } } function loadRemainingPages(pageNum, bookWidth, bookHeight) { if (pageNum > pageCount) { updatePageDisplay(true); return; } renderPage(pageNum, bookWidth, bookHeight, false).then(function(pageDiv) { if ($flipbook.turn('is')) { $flipbook.turn('addPage', pageDiv, pageNum); } loadedPageCount = pageNum; updatePageDisplay(); loadRemainingPages(pageNum + 1, bookWidth, bookHeight); }); } function updatePageDisplay(isComplete = false) { if (isComplete) { $totalPagesWrap.hide(); $loadedPages.text(pageCount); } else { $loadedPages.text(loadedPageCount); $totalPagesWrap.show(); } } function renderPage(num, bookWidth, bookHeight, isInitial, loadLimit) { return pdfDoc.getPage(num).then(function(page) { var viewport = page.getViewport({ scale: 1.5 }); var canvas = document.createElement('canvas'); var ctx = canvas.getContext('2d'); var pageWidth = bookWidth / 2; var pageHeight = bookHeight; var scaleX = pageWidth / viewport.width; var scaleY = pageHeight / viewport.height; var fitScale = Math.max(scaleX, scaleY); var fitViewport = page.getViewport({ scale: fitScale }); canvas.height = pageHeight; canvas.width = pageWidth; var pageDiv = $('
').append(canvas); var renderContext = { canvasContext: ctx, viewport: fitViewport }; return page.render(renderContext).promise.then(function() { if (isInitial) { renderedCount++; var percent = Math.round((renderedCount / loadLimit) * 100); $loadingText.text('Loading... (' + percent + '%)'); } return pageDiv; }); }); } function initTurnJs(bookWidth, bookHeight) { if (typeof $flipbook.turn !== 'function') return; if ($flipbook.turn('is')) $flipbook.turn('destroy'); $flipbook.turn({ width: bookWidth, height: bookHeight, autoCenter: true, gradients: true, acceleration: true, elevation: 50, display: 'double', pages: pageCount, when: { turned: function(event, page, view) { $pageInput.val(page); } } }); } startEbookProcess(); // 이벤트 핸들러 (한 번만 등록) $module.find('#prev-btn, .nav-prev').off().on('click', function() { $flipbook.turn('previous'); }); $module.find('#next-btn, .nav-next').off().on('click', function() { $flipbook.turn('next'); }); $pageInput.off().on('change keyup', function(e) { if (e.type === 'keyup' && e.keyCode !== 13) return; var page = parseInt($(this).val()); if (page > 0 && page <= pageCount) $flipbook.turn('page', page); }); function updateZoom() { $viewport.css('transform', 'scale(' + currentScale + ')'); $module.find('.zoom-level').text(Math.round(currentScale * 100) + '%'); } $module.find('#zoom-in-btn').off().on('click', function() { if (currentScale < 2.0) { currentScale += 0.2; updateZoom(); } }); $module.find('#zoom-out-btn').off().on('click', function() { if (currentScale > 0.6) { currentScale -= 0.2; updateZoom(); } }); $module.find('#zoom-reset-btn').off().on('click', function() { currentScale = 1.0; updateZoom(); }); $module.find('#dark-mode-btn').off().on('click', function() { var currentTheme = $module.attr('data-theme'); var newTheme = currentTheme === 'light' ? 'dark' : 'light'; $module.attr('data-theme', newTheme); $(this).find('i').toggleClass('fa-sun fa-moon'); }); $module.find('#print-btn').off().on('click', function() { var $iframe = $module.find('iframe'); $iframe.attr('src', pdfUrl); $iframe.on('load', function() { try { this.contentWindow.print(); } catch (e) { window.open(pdfUrl, '_blank').print(); } }); }); }; })(jQuery);