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

353 lines
14 KiB
PHP

<?php
if (!defined('_GNUBOARD_')) exit;
// 1. 설정 파일 로드
$config_path = __DIR__ . '/config.php';
if (file_exists($config_path)) include_once($config_path);
// 2. 데이터 가져오기
$target_bo_table = '';
$target_wr_id = '';
if (isset($ebook_bo_table) && $ebook_bo_table) {
$target_bo_table = $ebook_bo_table;
} elseif (isset($ebook_config['bo_table'])) {
$target_bo_table = $ebook_config['bo_table'];
}
if (isset($ebook_wr_id) && $ebook_wr_id) {
$target_wr_id = $ebook_wr_id;
} elseif (isset($ebook_config['wr_id'])) {
$target_wr_id = $ebook_config['wr_id'];
}
// 옵션값
$use_download = isset($use_download) ? $use_download : (isset($ebook_config['use_download']) ? $ebook_config['use_download'] : true);
$use_print = isset($use_print) ? $use_print : (isset($ebook_config['use_print']) ? $ebook_config['use_print'] : true);
$pdfjs_url = G5_THEME_URL . '/js/pdfjs';
$pdf_file_url = '';
$pdf_download_url = '';
if ($target_bo_table && $target_wr_id) {
$sql = " SELECT * FROM {$g5['board_file_table']} WHERE bo_table = '{$target_bo_table}' AND wr_id = '{$target_wr_id}' ORDER BY bf_no ASC ";
$result = sql_query($sql);
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/' . $target_bo_table . '/' . urlencode($row['bf_file']);
$pdf_download_url = $pdf_file_url;
break;
}
}
}
$ebook_id = 'ebook_' . uniqid();
$popup_url = G5_THEME_URL . '/rb.custom/ebook_section/popup.php?bo_table=' . $target_bo_table . '&wr_id=' . $target_wr_id;
?>
<!-- Rebuilder 표준 구조 -->
<div class="rb_layout_box" data-id="ebook_section" data-title="E-Book">
<?php if (isset($is_admin) && $is_admin) { ?>
<div class="rb_module_set">
<a href="javascript:;" onclick="set_module_send(this);" class="btn_module_setup"><i class="fa fa-cog"></i></a>
</div>
<?php } ?>
<div class="ebook-section" id="<?php echo $ebook_id; ?>" data-theme="light">
<?php if ($pdf_file_url): ?>
<div class="ebook-loading-overlay" style="display:flex;">
<div class="spinner"></div>
<p class="loading-text">E-Book을 준비 중입니다... (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="container">
<div class="flipbook"></div>
</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" class="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="control-group">
<button id="zoom-out-btn" class="btn" title="축소"><i class="fa fa-search-minus"></i></button>
<span class="zoom-level" title="현재 배율">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>
<button id="dark-mode-btn" class="btn" title="다크 모드"><i class="fa fa-moon"></i></button>
<?php if ($use_print): ?>
<button id="print-btn" class="btn" title="PDF 인쇄"><i class="fa fa-print"></i></button>
<?php endif; ?>
<?php if ($use_download): ?>
<a href="<?php echo $pdf_download_url; ?>" class="btn download-btn" download target="_blank" title="PDF 다운로드">
<i class="fa fa-download"></i>
</a>
<?php endif; ?>
<a href="<?php echo $popup_url; ?>" class="btn popup-btn" target="_blank" onclick="window.open(this.href, 'ebook_popup', 'width=1200,height=800'); return false;" title="새 창으로 보기">
<i class="fa fa-expand"></i>
</a>
</div>
</div>
<iframe id="print-frame-<?php echo $ebook_id; ?>" style="display:none;"></iframe>
<?php else: ?>
<div class="ebook-empty">
PDF 파일이 없습니다.<br>
<?php if ($target_bo_table && $target_wr_id): ?>
게시판: <?php echo $target_bo_table; ?>, 게시물: <?php echo $target_wr_id; ?>
<?php else: ?>
설정 파일(config.php)을 확인해주세요.
<?php endif; ?>
</div>
<?php endif; ?>
</div>
</div>
<link rel="stylesheet" href="<?php echo G5_THEME_URL; ?>/rb.custom/ebook_section/module.css?ver=<?php echo G5_CSS_VER; ?>">
<script src="<?php echo $pdfjs_url; ?>/pdf.min.js"></script>
<script src="<?php echo G5_THEME_URL; ?>/rb.custom/ebook_section/turnjs4/lib/turn.min.js"></script>
<script>
(function($) {
if (!'<?php echo $pdf_file_url; ?>') return;
var resizeTimer; // 💡 [추가] 리사이즈 타이머 변수
var pdfDoc = null;
var pageCount = 0;
var currentScale = 1.0;
var initialLoadCount = 6;
var loadedPageCount = 0;
var renderedCount = 0;
var isInitialized = false;
var $container = $('#<?php echo $ebook_id; ?>');
pdfjsLib.GlobalWorkerOptions.workerSrc = '<?php echo $pdfjs_url; ?>/pdf.worker.min.js';
function startEbookProcess() {
var $flipbook = $container.find('.flipbook');
if ($flipbook.turn('is')) {
$flipbook.turn('destroy');
}
$flipbook.empty();
$container.find('.ebook-loading-overlay').show();
$container.find('.flipbook-viewport, .ebook-controls').addClass('loading');
var containerWidth = $container.width();
if (!containerWidth || containerWidth < 100) containerWidth = 900;
var maxWidth = <?php echo isset($ebook_config['width']) ? $ebook_config['width'] : 1200; ?>;
var maxHeight = <?php echo isset($ebook_config['height']) ? $ebook_config['height'] : 800; ?>;
var targetWidth = Math.min(containerWidth - 40, maxWidth);
var bookWidth, bookHeight;
// 💡 [수정] 로딩 관련 변수 초기화
renderedCount = 0;
isInitialized = false;
loadedPageCount = 0;
$container.find('.loading-text').text('E-Book을 준비 중입니다... (0%)');
function run(pdf) {
pdfDoc = pdf;
pageCount = pdf.numPages;
$container.find('.total-pages').text(pageCount);
$container.find('.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;
bookWidth = targetWidth;
bookHeight = bookWidth / bookRatio;
if (bookHeight > maxHeight) {
bookHeight = maxHeight;
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();
$container.find('.ebook-loading-overlay').fadeOut(500);
$container.find('.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 = $container.find('.flipbook');
if ($flipbook.turn('is')) {
$flipbook.turn('addPage', pageDiv, pageNum);
}
loadedPageCount = pageNum;
updatePageDisplay();
loadRemainingPages(pageNum + 1, bookWidth, bookHeight);
});
}
function updatePageDisplay(isComplete = false) {
if (isComplete) {
$container.find('.total-pages-wrap').hide();
$container.find('.loaded-pages').text(pageCount);
} else {
$container.find('.loaded-pages').text(loadedPageCount);
$container.find('.total-pages-wrap').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 = $('<div />').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);
$container.find('.loading-text').text('E-Book을 준비 중입니다... (' + percent + '%)');
}
return pageDiv;
});
});
}
function initTurnJs(bookWidth, bookHeight) {
var $flipbook = $container.find('.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: {
turned: function(event, page, view) {
$container.find('.page-input').val(page);
}
}
});
}
// 초기 실행
startEbookProcess();
// 💡 [추가] 화면 리사이즈 시 이북 다시 로드
$(window).on('resize', function() {
clearTimeout(resizeTimer);
resizeTimer = setTimeout(startEbookProcess, 250);
});
// 이벤트 핸들러
$container.find('#prev-btn, .nav-prev').click(function() { $container.find('.flipbook').turn('previous'); });
$container.find('#next-btn, .nav-next').click(function() { $container.find('.flipbook').turn('next'); });
$container.find('.page-input').on('change keyup', function(e) {
if (e.type === 'keyup' && e.keyCode !== 13) return;
var page = parseInt($(this).val());
if (page > 0 && page <= pageCount) $container.find('.flipbook').turn('page', page);
});
function updateZoom() {
$container.find('.flipbook-viewport').css('transform', 'scale(' + currentScale + ')');
$container.find('.zoom-level').text(Math.round(currentScale * 100) + '%');
}
$container.find('#zoom-in-btn').click(function() { if (currentScale < 2.0) { currentScale += 0.2; updateZoom(); } });
$container.find('#zoom-out-btn').click(function() { if (currentScale > 0.6) { currentScale -= 0.2; updateZoom(); } });
$container.find('#zoom-reset-btn').click(function() { currentScale = 1.0; updateZoom(); });
$container.find('#dark-mode-btn').click(function() {
var currentTheme = $container.attr('data-theme');
var newTheme = currentTheme === 'light' ? 'dark' : 'light';
$container.attr('data-theme', newTheme);
$(this).find('i').toggleClass('fa-sun fa-moon');
});
$container.find('#print-btn').click(function() {
var $iframe = $container.find('iframe');
$iframe.attr('src', '<?php echo $pdf_file_url; ?>');
$iframe.on('load', function() {
try { this.contentWindow.print(); }
catch (e) { window.open('<?php echo $pdf_file_url; ?>', '_blank').print(); }
});
});
var wheelTimeout;
$container.on('wheel', function(e) {
e.preventDefault();
if (wheelTimeout) return;
wheelTimeout = setTimeout(function() { wheelTimeout = null; }, 300);
if (e.originalEvent.deltaY > 0) $container.find('.flipbook').turn('next');
else $container.find('.flipbook').turn('previous');
});
})(jQuery);
</script>