352 lines
14 KiB
PHP
352 lines
14 KiB
PHP
<?php
|
|
include_once('../../../../common.php');
|
|
|
|
$bo_table = isset($_GET['bo_table']) ? $_GET['bo_table'] : '';
|
|
$wr_id = isset($_GET['wr_id']) ? $_GET['wr_id'] : '';
|
|
|
|
if (!$bo_table || !$wr_id) {
|
|
alert_close('잘못된 접근입니다.');
|
|
}
|
|
|
|
$sql = " SELECT * FROM {$g5['board_file_table']} WHERE bo_table = '{$bo_table}' AND wr_id = '{$wr_id}' ORDER BY bf_no ASC ";
|
|
$result = sql_query($sql);
|
|
$pdf_file_url = '';
|
|
for ($i=0; $row=sql_fetch_array($result); $i++) {
|
|
if (isset($row['bf_file']) && preg_match('/\.pdf$/i', $row['bf_file'])) {
|
|
$pdf_file_url = G5_DATA_URL . '/file/' . $bo_table . '/' . urlencode($row['bf_file']);
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!$pdf_file_url) {
|
|
alert_close('PDF 파일이 없습니다.');
|
|
}
|
|
$pop_use_download = true;
|
|
$pop_use_print = true;
|
|
|
|
$g5['title'] = 'E-Book Viewer';
|
|
include_once(G5_PATH.'/head.sub.php');
|
|
?>
|
|
|
|
<!-- 💡 [수정] module.css 파일을 불러오도록 변경 -->
|
|
<link rel="stylesheet" href="./module.css?ver=<?php echo G5_CSS_VER; ?>">
|
|
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.4/css/all.min.css">
|
|
|
|
<style>
|
|
/* 팝업 전용 스타일 */
|
|
body, html { margin: 0; padding: 0; width: 100%; height: 100%; overflow: hidden; }
|
|
.ebook-section { height: 100%; padding: 0; max-height: none; }
|
|
.ebook-controls { position: fixed; bottom: 20px; left: 50%; transform: translateX(-50%); z-index: 1000; }
|
|
.nav-arrow { width: 60px; height: 60px; font-size: 24px; }
|
|
.nav-prev { left: 30px; }
|
|
.nav-next { right: 30px; }
|
|
</style>
|
|
|
|
<div class="ebook-section" id="ebook-popup" data-theme="dark">
|
|
<div class="ebook-loading-overlay">
|
|
<div class="spinner"></div>
|
|
<p class="loading-text">Loading... (0%)</p>
|
|
</div>
|
|
|
|
<div class="page-loading-alert" style="display:none;">잠시만 기다려 주세요...</div>
|
|
|
|
<div class="nav-arrow nav-prev" title="이전"><i class="fa fa-chevron-left"></i></div>
|
|
<div class="nav-arrow nav-next" title="다음"><i class="fa fa-chevron-right"></i></div>
|
|
|
|
<div class="flipbook-viewport loading">
|
|
<div class="flipbook"></div>
|
|
</div>
|
|
|
|
<div class="ebook-controls loading">
|
|
<div class="control-group">
|
|
<button id="prev-btn" class="btn" title="이전"><i class="fa fa-chevron-left"></i></button>
|
|
<div class="page-input-wrap">
|
|
<input type="number" id="page-input" value="1" min="1"> / <span class="loaded-pages">0</span><span class="total-pages-wrap">(<span class="total-pages">0</span>)</span>
|
|
</div>
|
|
<button id="next-btn" class="btn" title="다음"><i class="fa fa-chevron-right"></i></button>
|
|
</div>
|
|
<div class="divider" style="width: 1px; height: 20px; background: rgba(128,128,128,0.5); margin: 0 5px;"></div>
|
|
<div class="control-group">
|
|
<button id="zoom-out-btn" class="btn" title="축소"><i class="fa fa-search-minus"></i></button>
|
|
<span class="zoom-level" style="min-width: 45px; text-align: center;">100%</span>
|
|
<button id="zoom-reset-btn" class="btn" title="초기화"><i class="fa fa-compress"></i></button>
|
|
<button id="zoom-in-btn" class="btn" title="확대"><i class="fa fa-search-plus"></i></button>
|
|
</div>
|
|
<div class="divider" style="width: 1px; height: 20px; background: rgba(128,128,128,0.5); margin: 0 5px;"></div>
|
|
<div class="control-group">
|
|
<?php if ($pop_use_print): ?>
|
|
<button id="print-btn" class="btn" title="인쇄"><i class="fa fa-print"></i></button>
|
|
<?php endif; ?>
|
|
<?php if ($pop_use_download): ?>
|
|
<a href="<?php echo $pdf_file_url; ?>" class="btn" download target="_blank" title="다운로드"><i class="fa fa-download"></i></a>
|
|
<?php endif; ?>
|
|
</div>
|
|
</div>
|
|
|
|
<iframe id="print-frame" style="display:none;"></iframe>
|
|
</div>
|
|
|
|
<?php if(!defined('G5_HEAD_SUB_FILE')) { ?>
|
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/1.12.4/jquery.min.js"></script>
|
|
<?php } ?>
|
|
<script src="<?php echo G5_THEME_URL; ?>/rb.custom/ebook_section/turnjs4/lib/turn.min.js"></script>
|
|
<script src="<?php echo G5_THEME_URL; ?>/js/pdfjs/pdf.min.js"></script>
|
|
|
|
<script>
|
|
(function($) {
|
|
var resizeTimer;
|
|
var pdfDoc = null;
|
|
var pageCount = 0;
|
|
var currentScale = 1.0;
|
|
var initialLoadCount = 6;
|
|
var loadedPageCount = 0;
|
|
var renderedCount = 0;
|
|
var isInitialized = false;
|
|
|
|
pdfjsLib.GlobalWorkerOptions.workerSrc = '<?php echo G5_THEME_URL; ?>/js/pdfjs/pdf.worker.min.js';
|
|
|
|
function startEbookProcess() {
|
|
var $flipbook = $('.flipbook');
|
|
|
|
if ($flipbook.turn('is')) {
|
|
$flipbook.turn('destroy');
|
|
}
|
|
$flipbook.empty();
|
|
$('.ebook-loading-overlay').show();
|
|
$('.flipbook-viewport, .ebook-controls').addClass('loading');
|
|
|
|
var canvasWidth = $(window).width() - 40;
|
|
var canvasHeight = $(window).height() - 140;
|
|
|
|
if (canvasWidth > canvasHeight * 1.5) {
|
|
canvasWidth = canvasHeight * 1.5;
|
|
} else {
|
|
canvasHeight = canvasWidth / 1.5;
|
|
}
|
|
|
|
// 💡 [수정] 로딩 관련 변수 초기화
|
|
renderedCount = 0;
|
|
isInitialized = false;
|
|
loadedPageCount = 0;
|
|
$('.loading-text').text('Loading... (0%)'); // 로딩 텍스트도 초기화
|
|
|
|
function run(pdf) {
|
|
pdfDoc = pdf;
|
|
pageCount = pdf.numPages;
|
|
$('.total-pages').text(pageCount);
|
|
$('#page-input').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;
|
|
|
|
var bookWidth = canvasWidth;
|
|
var bookHeight = bookWidth / bookRatio;
|
|
|
|
if (bookHeight > canvasHeight) {
|
|
bookHeight = canvasHeight;
|
|
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();
|
|
|
|
$('.ebook-loading-overlay').fadeOut(500);
|
|
$('.flipbook-viewport, .ebook-controls').removeClass('loading');
|
|
|
|
if (pageCount > initialLoadCount) {
|
|
loadRemainingPages(initialLoadCount + 1, bookWidth, bookHeight);
|
|
} else {
|
|
updatePageDisplay(true);
|
|
}
|
|
});
|
|
});
|
|
}
|
|
|
|
if (pdfDoc) {
|
|
run(pdfDoc);
|
|
} else {
|
|
pdfjsLib.getDocument('<?php echo $pdf_file_url; ?>').promise.then(run);
|
|
}
|
|
}
|
|
|
|
function loadRemainingPages(pageNum, bookWidth, bookHeight) {
|
|
if (pageNum > pageCount) {
|
|
updatePageDisplay(true);
|
|
return;
|
|
}
|
|
renderPage(pageNum, bookWidth, bookHeight, false).then(function(pageDiv) {
|
|
var $flipbook = $('.flipbook');
|
|
if ($flipbook.turn('is')) {
|
|
$flipbook.turn('addPage', pageDiv, pageNum);
|
|
}
|
|
loadedPageCount = pageNum;
|
|
updatePageDisplay();
|
|
loadRemainingPages(pageNum + 1, bookWidth, bookHeight);
|
|
});
|
|
}
|
|
|
|
function updatePageDisplay(isComplete = false) {
|
|
if (isComplete) {
|
|
$('.total-pages-wrap').hide();
|
|
$('.loaded-pages').text(pageCount);
|
|
} else {
|
|
$('.loaded-pages').text(loadedPageCount);
|
|
$('.total-pages-wrap').show();
|
|
}
|
|
}
|
|
|
|
function renderPage(num, bookWidth, bookHeight, isInitial, loadLimit) {
|
|
return pdfDoc.getPage(num).then(function(page) {
|
|
var canvas = document.createElement('canvas');
|
|
var ctx = canvas.getContext('2d', { willReadFrequently: true });
|
|
|
|
var pageWidth = bookWidth / 2;
|
|
var pageHeight = bookHeight;
|
|
|
|
var viewport = page.getViewport({ scale: 1.5 });
|
|
var scale = Math.min(pageWidth / viewport.width, pageHeight / viewport.height);
|
|
var scaledViewport = page.getViewport({ scale: scale * 1.5 });
|
|
|
|
canvas.height = scaledViewport.height;
|
|
canvas.width = scaledViewport.width;
|
|
|
|
$(canvas).css({ width: '100%', height: '100%' });
|
|
|
|
var pageDiv = $('<div />').append(canvas);
|
|
|
|
var renderContext = { canvasContext: ctx, viewport: scaledViewport };
|
|
|
|
return page.render(renderContext).promise.then(function() {
|
|
if (isInitial) {
|
|
renderedCount++;
|
|
var percent = Math.round((renderedCount / loadLimit) * 100);
|
|
$('.loading-text').text('Loading... (' + percent + '%)');
|
|
}
|
|
return pageDiv;
|
|
});
|
|
});
|
|
}
|
|
|
|
function initTurnJs(bookWidth, bookHeight) {
|
|
var $flipbook = $('.flipbook');
|
|
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: {
|
|
turning: function(event, page, view) {
|
|
if (page > loadedPageCount) {
|
|
event.preventDefault();
|
|
$('.page-loading-alert').fadeIn(200).delay(1000).fadeOut(200);
|
|
}
|
|
},
|
|
turned: function(event, page, view) {
|
|
$('#page-input').val(page);
|
|
}
|
|
}
|
|
});
|
|
}
|
|
|
|
// 초기 실행
|
|
startEbookProcess();
|
|
|
|
$(window).on('resize', function() {
|
|
clearTimeout(resizeTimer);
|
|
resizeTimer = setTimeout(startEbookProcess, 250);
|
|
});
|
|
|
|
// 이벤트 핸들러
|
|
$('#prev-btn, .nav-prev').click(function() { $('.flipbook').turn('previous'); });
|
|
$('#next-btn, .nav-next').click(function() {
|
|
var nextPage = $('.flipbook').turn('page') + 2;
|
|
if (nextPage > loadedPageCount) {
|
|
$('.page-loading-alert').fadeIn(200).delay(1000).fadeOut(200);
|
|
return;
|
|
}
|
|
$('.flipbook').turn('next');
|
|
});
|
|
$('#page-input').on('change keyup', function(e) {
|
|
if (e.type === 'keyup' && e.keyCode !== 13) return;
|
|
var page = parseInt($(this).val());
|
|
if (page > loadedPageCount) {
|
|
$('.page-loading-alert').fadeIn(200).delay(1000).fadeOut(200);
|
|
$(this).val($('.flipbook').turn('page'));
|
|
return;
|
|
}
|
|
if (page > 0 && page <= pageCount) $('.flipbook').turn('page', page);
|
|
});
|
|
function updateZoom() {
|
|
$('.flipbook-viewport').css('transform', 'scale(' + currentScale + ')');
|
|
$('.zoom-level').text(Math.round(currentScale * 100) + '%');
|
|
}
|
|
$('#zoom-in-btn').click(function() { if (currentScale < 2.0) { currentScale += 0.2; updateZoom(); } });
|
|
$('#zoom-out-btn').click(function() { if (currentScale > 0.6) { currentScale -= 0.2; updateZoom(); } });
|
|
$('#zoom-reset-btn').click(function() { currentScale = 1.0; updateZoom(); });
|
|
$('#dark-mode-btn').click(function() {
|
|
var $section = $('#ebook-popup');
|
|
var currentTheme = $section.attr('data-theme');
|
|
var newTheme = currentTheme === 'dark' ? 'light' : 'dark';
|
|
$section.attr('data-theme', newTheme);
|
|
$(this).find('i').toggleClass('fa-sun fa-moon');
|
|
});
|
|
$('#print-btn').click(function() {
|
|
var pdfUrl = '<?php echo $pdf_file_url; ?>';
|
|
var $iframe = $('#print-frame');
|
|
$iframe.attr('src', pdfUrl);
|
|
$iframe.on('load', function() {
|
|
try { this.contentWindow.print(); }
|
|
catch (e) { window.open(pdfUrl, '_blank').print(); }
|
|
});
|
|
});
|
|
var wheelTimeout;
|
|
window.addEventListener('wheel', function(e) {
|
|
e.preventDefault();
|
|
if (wheelTimeout) return;
|
|
wheelTimeout = setTimeout(function() { wheelTimeout = null; }, 300);
|
|
if (e.deltaY > 0) {
|
|
var nextPage = $('.flipbook').turn('page') + 2;
|
|
if (nextPage > loadedPageCount) {
|
|
$('.page-loading-alert').fadeIn(200).delay(1000).fadeOut(200);
|
|
return;
|
|
}
|
|
$('.flipbook').turn('next');
|
|
} else {
|
|
$('.flipbook').turn('previous');
|
|
}
|
|
}, { passive: false });
|
|
$(window).on('keydown', function(e) {
|
|
if (e.keyCode == 37) $('.flipbook').turn('previous');
|
|
else if (e.keyCode == 39) $('.flipbook').turn('next');
|
|
});
|
|
|
|
})(jQuery);
|
|
</script>
|
|
|
|
<?php
|
|
include_once(G5_PATH.'/tail.sub.php');
|
|
?>
|