Files
dnssash/theme/rd.laser/rb.custom/ebook_section/popup.php
T
2026-06-11 18:47:38 +09:00

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');
?>