|
9 | 9 | // Configuration |
10 | 10 | const config = { |
11 | 11 | // Application version (displayed in footer) |
12 | | - version: '1.0.1', |
| 12 | + version: '1.0.2', |
13 | 13 | // Automatically restore and display content from IndexedDB on page load |
14 | 14 | autoRestoreContent: true, |
15 | 15 | // Open external links in a new window/tab (prevents navigation issues in iframes) |
|
68 | 68 | // Footer element |
69 | 69 | footerInfo: null, |
70 | 70 | // URL label element |
71 | | - urlLabel: null |
| 71 | + urlLabel: null, |
| 72 | + // URL help icon element |
| 73 | + urlHelpIcon: null, |
| 74 | + // Exit modal elements |
| 75 | + exitModal: null, |
| 76 | + btnConfirmExit: null |
72 | 77 | }; |
73 | 78 |
|
74 | 79 | /** |
|
171 | 176 | elements.footerInfo = document.getElementById('footerInfo'); |
172 | 177 | // URL label element |
173 | 178 | elements.urlLabel = document.getElementById('urlLabel'); |
| 179 | + // URL help icon element |
| 180 | + elements.urlHelpIcon = document.getElementById('urlHelpIcon'); |
| 181 | + // Exit modal elements |
| 182 | + elements.exitModal = document.getElementById('exitModal'); |
| 183 | + elements.btnConfirmExit = document.getElementById('btnConfirmExit'); |
174 | 184 | } |
175 | 185 |
|
176 | 186 | /** |
|
208 | 218 | function updateUrlLabel() { |
209 | 219 | if (!elements.urlLabel) return; |
210 | 220 |
|
211 | | - const key = config.gasProxyUrl ? 'welcome.loadFromUrlWithDrive' : 'welcome.loadFromUrl'; |
212 | | - elements.urlLabel.textContent = i18n.t(key); |
| 221 | + elements.urlLabel.textContent = i18n.t('welcome.loadFromUrl'); |
| 222 | + |
| 223 | + // Update tooltip on help icon |
| 224 | + if (elements.urlHelpIcon) { |
| 225 | + const tooltipKey = config.gasProxyUrl ? 'welcome.loadFromUrlTooltipWithDrive' : 'welcome.loadFromUrlTooltip'; |
| 226 | + elements.urlHelpIcon.setAttribute('title', i18n.t(tooltipKey)); |
| 227 | + // Refresh Bootstrap tooltip if already initialized |
| 228 | + const tooltipInstance = bootstrap.Tooltip.getInstance(elements.urlHelpIcon); |
| 229 | + if (tooltipInstance) { |
| 230 | + tooltipInstance.setContent({ '.tooltip-inner': i18n.t(tooltipKey) }); |
| 231 | + } |
| 232 | + } |
213 | 233 | } |
214 | 234 |
|
215 | 235 | /** |
|
220 | 240 | state.currentPackageName = i18n.t('navbar.restoredContent'); |
221 | 241 | elements.packageName.textContent = state.currentPackageName; |
222 | 242 | elements.packageName.title = state.currentPackageName; |
| 243 | + // Show package name visually only for restored content |
| 244 | + elements.packageName.classList.remove('visually-hidden'); |
223 | 245 | } |
224 | 246 | } |
225 | 247 |
|
|
535 | 557 | } |
536 | 558 |
|
537 | 559 | /** |
538 | | - * Convert special URLs (Google Drive, ownCloud/Nextcloud) to direct download links |
| 560 | + * Convert special URLs (Google Drive, ownCloud/Nextcloud, Dropbox) to direct download links |
539 | 561 | * @param {string} url - The original URL |
540 | 562 | * @returns {string} The converted URL for direct download |
541 | 563 | */ |
|
564 | 586 | return `${urlObj.origin}${cleanPath}/download`; |
565 | 587 | } |
566 | 588 |
|
| 589 | + // Dropbox shared links |
| 590 | + // Format: https://www.dropbox.com/s/HASH/filename.zip?dl=0 |
| 591 | + // or: https://www.dropbox.com/scl/fi/HASH/filename.zip?rlkey=xxx&dl=0 |
| 592 | + // Convert to: same URL with dl=1 for direct download |
| 593 | + if (urlObj.hostname === 'www.dropbox.com' || urlObj.hostname === 'dropbox.com') { |
| 594 | + urlObj.searchParams.set('dl', '1'); |
| 595 | + return urlObj.toString(); |
| 596 | + } |
| 597 | + |
567 | 598 | // Return original URL if no conversion needed |
568 | 599 | return url; |
569 | 600 | } catch (e) { |
|
960 | 991 | if (state.currentPackageName) { |
961 | 992 | elements.packageName.textContent = state.currentPackageName; |
962 | 993 | elements.packageName.title = state.currentPackageName; |
| 994 | + // Show package name visually only for restored content |
| 995 | + if (state.isRestoredContent) { |
| 996 | + elements.packageName.classList.remove('visually-hidden'); |
| 997 | + } |
963 | 998 | } |
964 | 999 |
|
965 | 1000 | // Update share and download button visibility |
966 | 1001 | updateShareButtonVisibility(); |
967 | 1002 | updateDownloadButtonVisibility(); |
968 | 1003 |
|
969 | | - // Show open in new window button |
| 1004 | + // Show open in new window and exit buttons |
970 | 1005 | elements.btnNewWindow.classList.remove('d-none'); |
| 1006 | + elements.btnLoadNew.classList.remove('d-none'); |
971 | 1007 |
|
972 | 1008 | // Set up history states: first mark current state as welcome, then push viewer state |
973 | 1009 | const welcomeState = { isWelcome: true }; |
|
1008 | 1044 | elements.btnDownload.classList.add('d-none'); |
1009 | 1045 | elements.btnShare.classList.add('d-none'); |
1010 | 1046 | elements.btnNewWindow.classList.add('d-none'); |
| 1047 | + elements.btnLoadNew.classList.add('d-none'); |
| 1048 | + elements.packageName.classList.add('visually-hidden'); |
1011 | 1049 | elements.welcomeScreen.classList.remove('d-none'); |
1012 | 1050 |
|
1013 | 1051 | // Clear file input and URL input |
|
1157 | 1195 | event.preventDefault(); |
1158 | 1196 | }); |
1159 | 1197 |
|
1160 | | - // Load new file button |
| 1198 | + // Load new file button - show confirmation modal |
1161 | 1199 | elements.btnLoadNew.addEventListener('click', () => { |
| 1200 | + const modal = new bootstrap.Modal(elements.exitModal); |
| 1201 | + modal.show(); |
| 1202 | + }); |
| 1203 | + |
| 1204 | + // Confirm exit button in modal |
| 1205 | + elements.btnConfirmExit.addEventListener('click', () => { |
| 1206 | + const modal = bootstrap.Modal.getInstance(elements.exitModal); |
| 1207 | + modal.hide(); |
1162 | 1208 | resetApplication(); |
1163 | 1209 | }); |
1164 | 1210 |
|
| 1211 | + // Handle Enter key in exit modal (like native confirm) |
| 1212 | + elements.exitModal.addEventListener('keydown', (event) => { |
| 1213 | + if (event.key === 'Enter') { |
| 1214 | + event.preventDefault(); |
| 1215 | + elements.btnConfirmExit.click(); |
| 1216 | + } |
| 1217 | + }); |
| 1218 | + |
1165 | 1219 | // URL input - load button click |
1166 | 1220 | elements.btnLoadUrl.addEventListener('click', () => { |
1167 | 1221 | downloadFromUrl(elements.urlInput.value); |
|
1208 | 1262 | if (elements.btnLoadNew) { |
1209 | 1263 | new bootstrap.Tooltip(elements.btnLoadNew); |
1210 | 1264 | } |
| 1265 | + if (elements.urlHelpIcon) { |
| 1266 | + new bootstrap.Tooltip(elements.urlHelpIcon); |
| 1267 | + } |
1211 | 1268 |
|
1212 | 1269 | // Setup language selector |
1213 | 1270 | setupLanguageSelector(); |
|
1460 | 1517 | elements.viewerContainer.classList.remove('d-none'); |
1461 | 1518 | elements.topNavbar.classList.remove('d-none'); |
1462 | 1519 | elements.btnNewWindow.classList.remove('d-none'); |
| 1520 | + elements.btnLoadNew.classList.remove('d-none'); |
1463 | 1521 | updateShareButtonVisibility(); |
1464 | 1522 | updateDownloadButtonVisibility(); |
1465 | 1523 | } |
|
1489 | 1547 | elements.btnDownload.classList.add('d-none'); |
1490 | 1548 | elements.btnShare.classList.add('d-none'); |
1491 | 1549 | elements.btnNewWindow.classList.add('d-none'); |
| 1550 | + elements.btnLoadNew.classList.add('d-none'); |
1492 | 1551 | elements.welcomeScreen.classList.remove('d-none'); |
1493 | 1552 | // Don't change iframe.src here - it would invalidate forward history |
1494 | 1553 | } |
|
0 commit comments