ginipick commited on
Commit
457e74f
ยท
1 Parent(s): e7b2ed2

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +247 -114
app.py CHANGED
@@ -1569,7 +1569,7 @@ HTML = """
1569
  </div>
1570
  </section>
1571
 
1572
- <script>
1573
  let projects=[], fb=null;
1574
  const grid=document.getElementById('grid'), viewer=document.getElementById('viewer');
1575
  pdfjsLib.GlobalWorkerOptions.workerSrc='/static/pdf.worker.js';
@@ -1584,20 +1584,114 @@ HTML = """
1584
  // ํ˜„์žฌ ์—ด๋ฆฐ PDF์˜ ID
1585
  let currentPdfId = null;
1586
 
1587
- /* ๐Ÿ”Š ์˜ค๋””์˜ค unlock โ€“ ๋‚ด์žฅ Audio ์™€ ๊ฐ™์€ MP3 ๊ฒฝ๋กœ ์‚ฌ์šฉ */
1588
- ['click','touchstart'].forEach(evt=>{
1589
- document.addEventListener(evt,function u(){new Audio('static/turnPage2.mp3')
1590
- .play().then(a=>a.pause()).catch(()=>{});document.removeEventListener(evt,u,{capture:true});},
1591
- {once:true,capture:true});
1592
- });
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1593
 
1594
  /* โ”€โ”€ ์œ ํ‹ธ โ”€โ”€ */
1595
  function $id(id){return document.getElementById(id)}
1596
 
1597
- // ํŒŒ์ผ ์—…๋กœ๋“œ ์ด๋ฒคํŠธ ์ฒ˜๋ฆฌ
1598
- document.addEventListener("DOMContentLoaded", function() {
1599
  console.log("DOM ๋กœ๋“œ ์™„๋ฃŒ, ์ด๋ฒคํŠธ ์„ค์ • ์‹œ์ž‘");
1600
 
 
 
 
1601
  // PDF ์—…๋กœ๋“œ ๋ฒ„ํŠผ
1602
  const pdfBtn = document.getElementById('pdfUploadBtn');
1603
  const pdfInput = document.getElementById('pdfInput');
@@ -1632,6 +1726,28 @@ HTML = """
1632
 
1633
  // ๊ด€๋ฆฌ์ž ๋ฒ„ํŠผ ์ด๋ฒคํŠธ ์„ค์ •
1634
  setupAdminFunctions();
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1635
  });
1636
 
1637
  // ์„œ๋ฒ„์— PDF ์—…๋กœ๋“œ ํ•จ์ˆ˜
@@ -1668,46 +1784,44 @@ HTML = """
1668
  }
1669
  }
1670
 
1671
- function addCard(i, thumb, title, isCached = false, pdfId = null) {
1672
- const d = document.createElement('div');
1673
- d.className = 'card fade-in';
1674
- d.onclick = () => open(i);
1675
-
1676
- // PDF ID๊ฐ€ ์žˆ์œผ๋ฉด ๋ฐ์ดํ„ฐ ์†์„ฑ์œผ๋กœ ์ €์žฅ
1677
- if (pdfId) {
1678
- d.dataset.pdfId = pdfId;
1679
- }
1680
-
1681
- // ์ œ๋ชฉ ์ฒ˜๋ฆฌ
1682
- const displayTitle = title ?
1683
- (title.length > 15 ? title.substring(0, 15) + '...' : title) :
1684
- 'ํ”„๋กœ์ ํŠธ ' + (i+1);
1685
-
1686
- // ์บ์‹œ ์ƒํƒœ ๋ฑƒ์ง€ ์ถ”๊ฐ€
1687
- const cachedBadge = isCached ?
1688
- '<div class="cached-status">์บ์‹œ๋จ</div>' : '';
1689
-
1690
- // ๋ฐ”๋กœ๊ฐ€๊ธฐ ๋งํฌ ์ถ”๊ฐ€ (PDF ID๊ฐ€ ์žˆ๋Š” ๊ฒฝ์šฐ์—๋งŒ)
1691
- const linkHtml = pdfId ?
1692
- `<div style="position: absolute; bottom: 55px; left: 50%; transform: translateX(-50%); z-index:5;">
1693
- <a href="/view/${pdfId}" target="_blank" style="color:#4a6ee0; font-size:11px;">Shaer Link</a>
1694
- </div>` : '';
1695
-
1696
- d.innerHTML = `
1697
- <div class="card-inner">
1698
- ${cachedBadge}
1699
- <img src="${thumb}" alt="${displayTitle}" loading="lazy">
1700
- ${linkHtml}
1701
- <p title="${title || 'ํ”„๋กœ์ ํŠธ ' + (i+1)}">${displayTitle}</p>
1702
- </div>
1703
- `;
1704
- grid.appendChild(d);
1705
-
1706
- // ํ”„๋กœ์ ํŠธ๊ฐ€ ์žˆ์œผ๋ฉด 'ํ”„๋กœ์ ํŠธ ์—†์Œ' ๋ฉ”์‹œ์ง€ ์ˆจ๊ธฐ๊ธฐ
1707
- $id('noProjects').style.display = 'none';
1708
- }
1709
-
1710
-
1711
 
1712
  /* โ”€โ”€ ํ”„๋กœ์ ํŠธ ์ €์žฅ โ”€โ”€ */
1713
  function save(pages, title, isCached = false, pdfId = null) {
@@ -1868,6 +1982,9 @@ function addCard(i, thumb, title, isCached = false, pdfId = null) {
1868
  /* โ”€โ”€ PDF ID๋กœ PDF ์—ด๊ธฐ โ”€โ”€ */
1869
  async function openPdfById(pdfId, pdfPath, isCached = false) {
1870
  try {
 
 
 
1871
  // ๋จผ์ € ํ™ˆ ํ™”๋ฉด์—์„œ ์นด๋“œ๋ฅผ ์ฐพ์•„์„œ ํด๋ฆญํ•˜๋Š” ๋ฐฉ๋ฒ• ์‹œ๋„
1872
  let foundCard = false;
1873
  const cards = document.querySelectorAll('.card');
@@ -2215,6 +2332,13 @@ function addCard(i, thumb, title, isCached = false, pdfId = null) {
2215
  viewer.style.width = size.width + 'px';
2216
  viewer.style.height = size.height + 'px';
2217
 
 
 
 
 
 
 
 
2218
  // ํŽ˜์ด์ง€ ๋ฐ์ดํ„ฐ ์ •์ œ (๋นˆ ํŽ˜์ด์ง€ ์ฒ˜๋ฆฌ)
2219
  const validPages = pages.map(page => {
2220
  // src๊ฐ€ ์—†๋Š” ํŽ˜์ด์ง€๋Š” ๋กœ๋”ฉ ์ค‘ ์ด๋ฏธ์ง€๋กœ ๋Œ€์ฒด
@@ -2235,7 +2359,7 @@ function addCard(i, thumb, title, isCached = false, pdfId = null) {
2235
  backgroundColor: '#fff',
2236
  /* ๐Ÿ”Š ๋‚ด์žฅ ์‚ฌ์šด๋“œ */
2237
  sound: true,
2238
- assets: {flipMp3: 'static/turnPage2.mp3', hardFlipMp3: 'static/turnPage2.mp3'},
2239
  controlsProps: {
2240
  enableFullscreen: true,
2241
  enableToc: true,
@@ -2300,24 +2424,6 @@ function addCard(i, thumb, title, isCached = false, pdfId = null) {
2300
  }
2301
 
2302
  /* โ”€โ”€ ๋„ค๋น„๊ฒŒ์ด์…˜ โ”€โ”€ */
2303
- $id('homeButton').onclick=()=>{
2304
- if(fb) {
2305
- fb.destroy();
2306
- viewer.innerHTML = '';
2307
- fb = null;
2308
- }
2309
- toggle(true);
2310
-
2311
- // ๋กœ๋”ฉ ์ธ๋””์ผ€์ดํ„ฐ ์ •๋ฆฌ
2312
- if (pageLoadingInterval) {
2313
- clearInterval(pageLoadingInterval);
2314
- pageLoadingInterval = null;
2315
- }
2316
- $id('loadingPages').style.display = 'none';
2317
- currentLoadingPdfPath = null;
2318
- currentPdfId = null;
2319
- };
2320
-
2321
  function toggle(showHome){
2322
  $id('home').style.display=showHome?'block':'none';
2323
  $id('viewerPage').style.display=showHome?'none':'block';
@@ -2335,63 +2441,90 @@ function addCard(i, thumb, title, isCached = false, pdfId = null) {
2335
  /* -- ๊ด€๋ฆฌ์ž ๊ธฐ๋Šฅ -- */
2336
  function setupAdminFunctions() {
2337
  // ๊ด€๋ฆฌ์ž ๋ฒ„ํŠผ ํด๋ฆญ - ๋ชจ๋‹ฌ ํ‘œ์‹œ
2338
- $id('adminButton').addEventListener('click', function() {
2339
- $id('adminLoginModal').style.display = 'flex';
2340
- $id('adminPasswordInput').value = '';
2341
- $id('adminPasswordInput').focus();
2342
- });
 
 
 
 
 
 
 
 
 
 
 
 
 
2343
 
2344
  // ๋ชจ๋‹ฌ ๋‹ซ๊ธฐ ๋ฒ„ํŠผ
2345
- $id('adminLoginClose').addEventListener('click', function() {
2346
- $id('adminLoginModal').style.display = 'none';
2347
- });
 
 
 
 
2348
 
2349
  // ์—”ํ„ฐ ํ‚ค๋กœ ๋กœ๊ทธ์ธ
2350
- $id('adminPasswordInput').addEventListener('keyup', function(e) {
2351
- if (e.key === 'Enter') {
2352
- $id('adminLoginButton').click();
2353
- }
2354
- });
 
 
2355
 
2356
  // ๋กœ๊ทธ์ธ ๋ฒ„ํŠผ
2357
- $id('adminLoginButton').addEventListener('click', async function() {
2358
- const password = $id('adminPasswordInput').value;
2359
-
2360
- try {
2361
- showLoading("๋กœ๊ทธ์ธ ์ค‘...");
2362
 
2363
- const formData = new FormData();
2364
- formData.append('password', password);
2365
 
2366
- const response = await fetch('/api/admin-login', {
2367
- method: 'POST',
2368
- body: formData
2369
- });
2370
-
2371
- const data = await response.json();
2372
-
2373
- hideLoading();
2374
-
2375
- if (data.success) {
2376
- // ๋กœ๊ทธ์ธ ์„ฑ๊ณต - ๊ด€๋ฆฌ์ž ํŽ˜์ด์ง€ ํ‘œ์‹œ
2377
- $id('adminLoginModal').style.display = 'none';
2378
- showAdminPage();
2379
- } else {
2380
- // ๋กœ๊ทธ์ธ ์‹คํŒจ
2381
- showError("๊ด€๋ฆฌ์ž ์ธ์ฆ ๏ฟฝ๏ฟฝํŒจ: ๋น„๋ฐ€๋ฒˆํ˜ธ๊ฐ€ ์ผ์น˜ํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.");
 
 
 
 
 
 
 
 
 
 
 
 
 
2382
  }
2383
- } catch (error) {
2384
- console.error("๊ด€๋ฆฌ์ž ๋กœ๊ทธ์ธ ์˜ค๋ฅ˜:", error);
2385
- hideLoading();
2386
- showError("๋กœ๊ทธ์ธ ์ฒ˜๋ฆฌ ์ค‘ ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ–ˆ์Šต๋‹ˆ๋‹ค.");
2387
- }
2388
- });
2389
 
2390
  // ๊ด€๋ฆฌ์ž ํŽ˜์ด์ง€ ๋’ค๋กœ๊ฐ€๊ธฐ
2391
- $id('adminBackButton').addEventListener('click', function() {
2392
- $id('adminPage').style.display = 'none';
2393
- $id('home').style.display = 'block';
2394
- });
 
 
2395
  }
2396
 
2397
  // ๊ด€๋ฆฌ์ž ํŽ˜์ด์ง€ ํ‘œ์‹œ
 
1569
  </div>
1570
  </section>
1571
 
1572
+ <script>
1573
  let projects=[], fb=null;
1574
  const grid=document.getElementById('grid'), viewer=document.getElementById('viewer');
1575
  pdfjsLib.GlobalWorkerOptions.workerSrc='/static/pdf.worker.js';
 
1584
  // ํ˜„์žฌ ์—ด๋ฆฐ PDF์˜ ID
1585
  let currentPdfId = null;
1586
 
1587
+ // ์˜ค๋””์˜ค ์ปจํ…์ŠคํŠธ์™€ ์ดˆ๊ธฐํ™” ์ƒํƒœ ๊ด€๋ฆฌ
1588
+ let audioInitialized = false;
1589
+ let audioContext = null;
1590
+
1591
+ // ์˜ค๋””์˜ค ์ดˆ๊ธฐํ™” ํ•จ์ˆ˜
1592
+ function initializeAudio() {
1593
+ if (audioInitialized) return Promise.resolve();
1594
+
1595
+ return new Promise((resolve) => {
1596
+ // ์˜ค๋””์˜ค ์ปจํ…์ŠคํŠธ ์ƒ์„ฑ (์‚ฌ์šฉ์ž ์ƒํ˜ธ์ž‘์šฉ์ด ํ•„์š” ์—†๋Š” ์ดˆ๊ธฐํ™”)
1597
+ audioContext = new (window.AudioContext || window.webkitAudioContext)();
1598
+
1599
+ // MP3 ๋กœ๋“œ ๋ฐ ์ดˆ๊ธฐํ™”
1600
+ const audio = new Audio('/static/turnPage2.mp3');
1601
+ audio.volume = 0.01; // ์ตœ์†Œ ๋ณผ๋ฅจ์œผ๋กœ ์„ค์ •
1602
+
1603
+ // ๋กœ๋“œ๋œ ์˜ค๋””์˜ค ์žฌ์ƒ ์‹œ๋„ (์‚ฌ์šฉ์ž ์ƒํ˜ธ์ž‘์šฉ ์š”๊ตฌ๋  ์ˆ˜ ์žˆ์Œ)
1604
+ const playPromise = audio.play();
1605
+
1606
+ if (playPromise !== undefined) {
1607
+ playPromise
1608
+ .then(() => {
1609
+ // ์„ฑ๊ณต์ ์œผ๋กœ ์žฌ์ƒ๋จ - ์ฆ‰์‹œ ์ผ์‹œ์ •์ง€
1610
+ audio.pause();
1611
+ audioInitialized = true;
1612
+ console.log('์˜ค๋””์˜ค ์ดˆ๊ธฐํ™” ์„ฑ๊ณต');
1613
+ resolve();
1614
+ })
1615
+ .catch((error) => {
1616
+ console.log('์ž๋™ ์˜ค๋””์˜ค ์ดˆ๊ธฐํ™” ์‹คํŒจ, ์‚ฌ์šฉ์ž ์ƒํ˜ธ์ž‘์šฉ ํ•„์š”:', error);
1617
+
1618
+ // ์‚ฌ์šฉ์ž ์ƒํ˜ธ์ž‘์šฉ์ด ํ•„์š”ํ•œ ๊ฒฝ์šฐ, ์ด๋ฒคํŠธ ๋ฆฌ์Šค๋„ˆ ์ถ”๊ฐ€
1619
+ const initOnUserAction = function() {
1620
+ const tempAudio = new Audio('/static/turnPage2.mp3');
1621
+ tempAudio.volume = 0.01;
1622
+ tempAudio.play()
1623
+ .then(() => {
1624
+ tempAudio.pause();
1625
+ audioInitialized = true;
1626
+ console.log('์‚ฌ์šฉ์ž ์ƒํ˜ธ์ž‘์šฉ์œผ๋กœ ์˜ค๋””์˜ค ์ดˆ๊ธฐํ™” ์„ฑ๊ณต');
1627
+ resolve();
1628
+
1629
+ // ์ด๋ฒคํŠธ ๋ฆฌ์Šค๋„ˆ ์ œ๊ฑฐ
1630
+ ['click', 'touchstart', 'keydown'].forEach(event => {
1631
+ document.removeEventListener(event, initOnUserAction, { capture: true });
1632
+ });
1633
+ })
1634
+ .catch(e => console.error('์˜ค๋””์˜ค ์ดˆ๊ธฐํ™” ์‹คํŒจ:', e));
1635
+ };
1636
+
1637
+ // ์‚ฌ์šฉ์ž ์ƒํ˜ธ์ž‘์šฉ ์ด๋ฒคํŠธ์— ๋ฆฌ์Šค๋„ˆ ์ถ”๊ฐ€
1638
+ ['click', 'touchstart', 'keydown'].forEach(event => {
1639
+ document.addEventListener(event, initOnUserAction, { once: true, capture: true });
1640
+ });
1641
+
1642
+ // ํŽ˜์ด์ง€ ๋กœ๋“œ ์งํ›„ ์‚ฌ์šฉ์ž์—๊ฒŒ ์˜ค๋””์˜ค ํ™œ์„ฑํ™” ์š”์ฒญ
1643
+ if (window.location.pathname.startsWith('/view/')) {
1644
+ // ์˜ค๋””์˜ค ํ™œ์„ฑํ™” ์•ˆ๋‚ด ๋ฉ”์‹œ์ง€ ํ‘œ์‹œ (๋ฐ”๋กœ๊ฐ€๊ธฐ ๋งํฌ๋กœ ์ ‘์†ํ•œ ๊ฒฝ์šฐ)
1645
+ setTimeout(() => {
1646
+ const audioPrompt = document.createElement('div');
1647
+ audioPrompt.style.position = 'fixed';
1648
+ audioPrompt.style.bottom = '80px';
1649
+ audioPrompt.style.left = '50%';
1650
+ audioPrompt.style.transform = 'translateX(-50%)';
1651
+ audioPrompt.style.backgroundColor = 'rgba(0,0,0,0.7)';
1652
+ audioPrompt.style.color = 'white';
1653
+ audioPrompt.style.padding = '10px 20px';
1654
+ audioPrompt.style.borderRadius = '20px';
1655
+ audioPrompt.style.zIndex = '10000';
1656
+ audioPrompt.style.cursor = 'pointer';
1657
+ audioPrompt.innerHTML = 'ํŽ˜์ด์ง€ ์–ด๋””๋“  ํด๋ฆญํ•˜์—ฌ ์†Œ๋ฆฌ ํšจ๊ณผ ํ™œ์„ฑํ™” <i class="fas fa-volume-up"></i>';
1658
+ audioPrompt.id = 'audioPrompt';
1659
+
1660
+ // ํด๋ฆญ ์‹œ ์†Œ๋ฆฌ ํ™œ์„ฑํ™” ๋ฐ ๋ฉ”์‹œ์ง€ ์ œ๊ฑฐ
1661
+ audioPrompt.addEventListener('click', function() {
1662
+ initOnUserAction();
1663
+ audioPrompt.remove();
1664
+ });
1665
+
1666
+ document.body.appendChild(audioPrompt);
1667
+
1668
+ // 10์ดˆ ํ›„ ์ž๋™์œผ๋กœ ์ˆจ๊น€
1669
+ setTimeout(() => {
1670
+ if (document.getElementById('audioPrompt')) {
1671
+ document.getElementById('audioPrompt').remove();
1672
+ }
1673
+ }, 10000);
1674
+ }, 2000);
1675
+ }
1676
+ });
1677
+ } else {
1678
+ // ๋ธŒ๋ผ๏ฟฝ๏ฟฝ๏ฟฝ์ €๊ฐ€ Promise ๊ธฐ๋ฐ˜ ์žฌ์ƒ์„ ์ง€์›ํ•˜์ง€ ์•Š๋Š” ๊ฒฝ์šฐ
1679
+ audioInitialized = true;
1680
+ resolve();
1681
+ }
1682
+ });
1683
+ }
1684
 
1685
  /* โ”€โ”€ ์œ ํ‹ธ โ”€โ”€ */
1686
  function $id(id){return document.getElementById(id)}
1687
 
1688
+ // DOM์ด ๋กœ๋“œ๋˜๋ฉด ์‹คํ–‰
1689
+ document.addEventListener('DOMContentLoaded', function() {
1690
  console.log("DOM ๋กœ๋“œ ์™„๋ฃŒ, ์ด๋ฒคํŠธ ์„ค์ • ์‹œ์ž‘");
1691
 
1692
+ // ์˜ค๋””์˜ค ์ดˆ๊ธฐํ™” ์‹œ๋„
1693
+ initializeAudio().catch(e => console.warn('์˜ค๋””์˜ค ์ดˆ๊ธฐํ™” ์‹คํŒจ:', e));
1694
+
1695
  // PDF ์—…๋กœ๋“œ ๋ฒ„ํŠผ
1696
  const pdfBtn = document.getElementById('pdfUploadBtn');
1697
  const pdfInput = document.getElementById('pdfInput');
 
1726
 
1727
  // ๊ด€๋ฆฌ์ž ๋ฒ„ํŠผ ์ด๋ฒคํŠธ ์„ค์ •
1728
  setupAdminFunctions();
1729
+
1730
+ // ํ™ˆ ๋ฒ„ํŠผ ์ด๋ฒคํŠธ ์„ค์ •
1731
+ const homeButton = document.getElementById('homeButton');
1732
+ if (homeButton) {
1733
+ homeButton.addEventListener('click', function() {
1734
+ if(fb) {
1735
+ fb.destroy();
1736
+ viewer.innerHTML = '';
1737
+ fb = null;
1738
+ }
1739
+ toggle(true);
1740
+
1741
+ // ๋กœ๋”ฉ ์ธ๋””์ผ€์ดํ„ฐ ์ •๋ฆฌ
1742
+ if (pageLoadingInterval) {
1743
+ clearInterval(pageLoadingInterval);
1744
+ pageLoadingInterval = null;
1745
+ }
1746
+ $id('loadingPages').style.display = 'none';
1747
+ currentLoadingPdfPath = null;
1748
+ currentPdfId = null;
1749
+ });
1750
+ }
1751
  });
1752
 
1753
  // ์„œ๋ฒ„์— PDF ์—…๋กœ๋“œ ํ•จ์ˆ˜
 
1784
  }
1785
  }
1786
 
1787
+ function addCard(i, thumb, title, isCached = false, pdfId = null) {
1788
+ const d = document.createElement('div');
1789
+ d.className = 'card fade-in';
1790
+ d.onclick = () => open(i);
1791
+
1792
+ // PDF ID๊ฐ€ ์žˆ์œผ๋ฉด ๋ฐ์ดํ„ฐ ์†์„ฑ์œผ๋กœ ์ €์žฅ
1793
+ if (pdfId) {
1794
+ d.dataset.pdfId = pdfId;
1795
+ }
1796
+
1797
+ // ์ œ๋ชฉ ์ฒ˜๋ฆฌ
1798
+ const displayTitle = title ?
1799
+ (title.length > 15 ? title.substring(0, 15) + '...' : title) :
1800
+ 'ํ”„๋กœ์ ํŠธ ' + (i+1);
1801
+
1802
+ // ์บ์‹œ ์ƒํƒœ ๋ฑƒ์ง€ ์ถ”๊ฐ€
1803
+ const cachedBadge = isCached ?
1804
+ '<div class="cached-status">์บ์‹œ๋จ</div>' : '';
1805
+
1806
+ // ๋ฐ”๋กœ๊ฐ€๊ธฐ ๋งํฌ ์ถ”๊ฐ€ (PDF ID๊ฐ€ ์žˆ๋Š” ๊ฒฝ์šฐ์—๋งŒ)
1807
+ const linkHtml = pdfId ?
1808
+ `<div style="position: absolute; bottom: 55px; left: 50%; transform: translateX(-50%); z-index:5;">
1809
+ <a href="/view/${pdfId}" target="_blank" style="color:#4a6ee0; font-size:11px;">Share Link</a>
1810
+ </div>` : '';
1811
+
1812
+ d.innerHTML = `
1813
+ <div class="card-inner">
1814
+ ${cachedBadge}
1815
+ <img src="${thumb}" alt="${displayTitle}" loading="lazy">
1816
+ ${linkHtml}
1817
+ <p title="${title || 'ํ”„๋กœ์ ํŠธ ' + (i+1)}">${displayTitle}</p>
1818
+ </div>
1819
+ `;
1820
+ grid.appendChild(d);
1821
+
1822
+ // ํ”„๋กœ์ ํŠธ๊ฐ€ ์žˆ์œผ๋ฉด 'ํ”„๋กœ์ ํŠธ ์—†์Œ' ๋ฉ”์‹œ์ง€ ์ˆจ๊ธฐ๊ธฐ
1823
+ $id('noProjects').style.display = 'none';
1824
+ }
 
 
1825
 
1826
  /* โ”€โ”€ ํ”„๋กœ์ ํŠธ ์ €์žฅ โ”€โ”€ */
1827
  function save(pages, title, isCached = false, pdfId = null) {
 
1982
  /* โ”€โ”€ PDF ID๋กœ PDF ์—ด๊ธฐ โ”€โ”€ */
1983
  async function openPdfById(pdfId, pdfPath, isCached = false) {
1984
  try {
1985
+ // ์˜ค๋””์˜ค ์ดˆ๊ธฐํ™” ์‹œ๋„
1986
+ await initializeAudio().catch(e => console.warn('PDF ์—ด๊ธฐ ์ „ ์˜ค๋””์˜ค ์ดˆ๊ธฐํ™” ์‹คํŒจ:', e));
1987
+
1988
  // ๋จผ์ € ํ™ˆ ํ™”๋ฉด์—์„œ ์นด๋“œ๋ฅผ ์ฐพ์•„์„œ ํด๋ฆญํ•˜๋Š” ๋ฐฉ๋ฒ• ์‹œ๋„
1989
  let foundCard = false;
1990
  const cards = document.querySelectorAll('.card');
 
2332
  viewer.style.width = size.width + 'px';
2333
  viewer.style.height = size.height + 'px';
2334
 
2335
+ // ์‚ฌ์šด๋“œ ์ดˆ๊ธฐํ™” ์—ฌ๋ถ€ ํ™•์ธ
2336
+ if (!audioInitialized) {
2337
+ initializeAudio()
2338
+ .then(() => console.log('FlipBook ์ƒ์„ฑ ์ „ ์˜ค๋””์˜ค ์ดˆ๊ธฐํ™” ์™„๋ฃŒ'))
2339
+ .catch(e => console.warn('FlipBook ์ƒ์„ฑ ์‹œ ์˜ค๋””์˜ค ์ดˆ๊ธฐํ™” ์‹คํŒจ:', e));
2340
+ }
2341
+
2342
  // ํŽ˜์ด์ง€ ๋ฐ์ดํ„ฐ ์ •์ œ (๋นˆ ํŽ˜์ด์ง€ ์ฒ˜๋ฆฌ)
2343
  const validPages = pages.map(page => {
2344
  // src๊ฐ€ ์—†๋Š” ํŽ˜์ด์ง€๋Š” ๋กœ๋”ฉ ์ค‘ ์ด๋ฏธ์ง€๋กœ ๋Œ€์ฒด
 
2359
  backgroundColor: '#fff',
2360
  /* ๐Ÿ”Š ๋‚ด์žฅ ์‚ฌ์šด๋“œ */
2361
  sound: true,
2362
+ assets: {flipMp3: '/static/turnPage2.mp3', hardFlipMp3: '/static/turnPage2.mp3'}, // ์ ˆ๋Œ€ ๊ฒฝ๋กœ๋กœ ์ˆ˜์ •
2363
  controlsProps: {
2364
  enableFullscreen: true,
2365
  enableToc: true,
 
2424
  }
2425
 
2426
  /* โ”€โ”€ ๋„ค๋น„๊ฒŒ์ด์…˜ โ”€โ”€ */
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2427
  function toggle(showHome){
2428
  $id('home').style.display=showHome?'block':'none';
2429
  $id('viewerPage').style.display=showHome?'none':'block';
 
2441
  /* -- ๊ด€๋ฆฌ์ž ๊ธฐ๋Šฅ -- */
2442
  function setupAdminFunctions() {
2443
  // ๊ด€๋ฆฌ์ž ๋ฒ„ํŠผ ํด๋ฆญ - ๋ชจ๋‹ฌ ํ‘œ์‹œ
2444
+ const adminButton = document.getElementById('adminButton');
2445
+ const adminLoginModal = document.getElementById('adminLoginModal');
2446
+ const adminLoginClose = document.getElementById('adminLoginClose');
2447
+ const adminLoginButton = document.getElementById('adminLoginButton');
2448
+ const adminPasswordInput = document.getElementById('adminPasswordInput');
2449
+ const adminBackButton = document.getElementById('adminBackButton');
2450
+
2451
+ if (adminButton) {
2452
+ adminButton.addEventListener('click', function() {
2453
+ if (adminLoginModal) {
2454
+ adminLoginModal.style.display = 'flex';
2455
+ if (adminPasswordInput) {
2456
+ adminPasswordInput.value = '';
2457
+ adminPasswordInput.focus();
2458
+ }
2459
+ }
2460
+ });
2461
+ }
2462
 
2463
  // ๋ชจ๋‹ฌ ๋‹ซ๊ธฐ ๋ฒ„ํŠผ
2464
+ if (adminLoginClose) {
2465
+ adminLoginClose.addEventListener('click', function() {
2466
+ if (adminLoginModal) {
2467
+ adminLoginModal.style.display = 'none';
2468
+ }
2469
+ });
2470
+ }
2471
 
2472
  // ์—”ํ„ฐ ํ‚ค๋กœ ๋กœ๊ทธ์ธ
2473
+ if (adminPasswordInput) {
2474
+ adminPasswordInput.addEventListener('keyup', function(e) {
2475
+ if (e.key === 'Enter' && adminLoginButton) {
2476
+ adminLoginButton.click();
2477
+ }
2478
+ });
2479
+ }
2480
 
2481
  // ๋กœ๊ทธ์ธ ๋ฒ„ํŠผ
2482
+ if (adminLoginButton) {
2483
+ adminLoginButton.addEventListener('click', async function() {
2484
+ if (!adminPasswordInput) return;
 
 
2485
 
2486
+ const password = adminPasswordInput.value;
 
2487
 
2488
+ try {
2489
+ showLoading("๋กœ๊ทธ์ธ ์ค‘...");
2490
+
2491
+ const formData = new FormData();
2492
+ formData.append('password', password);
2493
+
2494
+ const response = await fetch('/api/admin-login', {
2495
+ method: 'POST',
2496
+ body: formData
2497
+ });
2498
+
2499
+ const data = await response.json();
2500
+
2501
+ hideLoading();
2502
+
2503
+ if (data.success) {
2504
+ // ๋กœ๊ทธ์ธ ์„ฑ๊ณต - ๊ด€๋ฆฌ์ž ํŽ˜์ด์ง€ ํ‘œ์‹œ
2505
+ if (adminLoginModal) {
2506
+ adminLoginModal.style.display = 'none';
2507
+ }
2508
+ showAdminPage();
2509
+ } else {
2510
+ // ๋กœ๊ทธ์ธ ์‹คํŒจ
2511
+ showError("๊ด€๋ฆฌ์ž ์ธ์ฆ ์‹คํŒจ: ๋น„๋ฐ€๋ฒˆํ˜ธ๊ฐ€ ์ผ์น˜ํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.");
2512
+ }
2513
+ } catch (error) {
2514
+ console.error("๊ด€๋ฆฌ์ž ๋กœ๊ทธ์ธ ์˜ค๋ฅ˜:", error);
2515
+ hideLoading();
2516
+ showError("๋กœ๊ทธ์ธ ์ฒ˜๋ฆฌ ์ค‘ ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ–ˆ์Šต๋‹ˆ๋‹ค.");
2517
  }
2518
+ });
2519
+ }
 
 
 
 
2520
 
2521
  // ๊ด€๋ฆฌ์ž ํŽ˜์ด์ง€ ๋’ค๋กœ๊ฐ€๊ธฐ
2522
+ if (adminBackButton) {
2523
+ adminBackButton.addEventListener('click', function() {
2524
+ document.getElementById('adminPage').style.display = 'none';
2525
+ document.getElementById('home').style.display = 'block';
2526
+ });
2527
+ }
2528
  }
2529
 
2530
  // ๊ด€๋ฆฌ์ž ํŽ˜์ด์ง€ ํ‘œ์‹œ