1const isPerformanceBot = 2 /Chrome-Lighthouse|PageSpeed|GTmetrix|Googlebot|bingbot|slurp|duckduckbot|baiduspider|yandexbot|bot|crawler|spider|headless|phantom|prerender|selenium|webdriver|lighthouse|pingdom|uptimerobot|semrush|ahrefs|majestic|moz|facebookexternalhit|twitterbot|whatsapp|telegrambot|slackbot|discordbot/i.test( 3 navigator.userAgent 4 ) || navigator.webdriver === true; 5 6const hostname = window.location.hostname; 7const isPageSpeed = 8 hostname.includes("pagespeed") || 9 hostname.includes("gtmetrix") || 10 hostname.includes(".netlify.app") || 11 hostname.includes(".vercel.app") || 12 hostname.includes("preview") || 13 /^(localhost|127\.0\.0\.1|0\.0\.0\.0|192\.168\.\d+\.\d+|10\.\d+\.\d+\.\d+)$/.test(hostname); 14 15// Register service worker 16if ("serviceWorker" in navigator) { 17 if (hostname !== "localhost" && hostname !== "127.0.0.1") { 18 window.addEventListener("load", () => { 19 navigator.serviceWorker 20 .register("/sw.js") 21 .then((reg) => { 22 console.log("Service Worker registered.", reg); 23 24 // Listen for messages from the service worker 25 navigator.serviceWorker.addEventListener('message', (event) => { 26 if (event.data && event.data.type === 'SW_UPDATED') { 27 console.log('Service Worker updated, reloading page...'); 28 window.location.reload(); 29 } 30 }); 31 }) 32 .catch((err) => 33 console.log("Service Worker registration failed:", err) 34 ); 35 }); 36 } else { 37 console.log("Service Worker not registered on localhost"); 38 } 39} 40 41if ( 42 // !isPerformanceBot && 43 // !isPageSpeed && 44 // hostname !== "localhost" && 45 // hostname !== "127.0.0.1" 46 true 47) { 48 // Google Analytics (gtag) 49 const gaScript = document.createElement("script"); 50 gaScript.src = "https://www.googletagmanager.com/gtag/js?id=G-QBM18BD3WP"; 51 gaScript.async = true; 52 document.head.appendChild(gaScript); 53 54
vendor: 142 bytes, lines 54-59
54window.dataLayer = window.dataLayer || []; 55 function gtag() { 56 dataLayer.push(arguments); 57 } 58 gtag("js", new Date()); 59 gtag("config", "
59G-QBM18BD3WP
vendor: 7 bytes, lines 59-61
59"); 60 61
61// Web Vitals 62 // const webVitalsScript = document.createElement("script"); 63 // webVitalsScript.src = 64 // "https://unpkg.com/web-vitals@3/dist/web-vitals.iife.js"; 65 // webVitalsScript.async = true; 66 // document.head.appendChild(webVitalsScript); 67 68 // webVitalsScript.onload = () => { 69 // function sendToGtag({ name, delta, id, value }) { 70 // gtag("event", name, { 71 // event_category: "Web Vitals", 72 // event_label: id, 73 // value: Math.round(name === "CLS" ? delta * 1000 : value), 74 // non_interaction: true, 75 // }); 76 // } 77 78 // webVitals.getCLS(sendToGtag); 79 // webVitals.getFID(sendToGtag); 80 // webVitals.getLCP(sendToGtag); 81 // webVitals.getFCP(sendToGtag); 82 // webVitals.getTTFB(sendToGtag); 83 // }; 84 85 // Statcounter 86 window.sc_project = 13146153; 87 window.sc_invisible = 1; 88 window.sc_security = "04bec386"; 89 90 const scScript = document.createElement("script"); 91 scScript.src = "https://www.statcounter.com/counter/counter.js"; 92 scScript.async = true; 93 document.head.appendChild(scScript); 94 95 // Helper function to track events 96 window.trackEvent = function(eventName, params = {}) { 97 if (typeof gtag === 'function') { 98 gtag('event', eventName, params); 99 } 100 }; 101 102 window.addEventListener("DOMContentLoaded", async () => { 103 // Track page view 104 trackEvent('page_view', { 105 page_path: window.location.pathname, 106 page_title: document.title 107 }); 108 109 // GA event tracking 110 const dropZone = document.getElementById("dropZone"); 111 if (dropZone) { 112 dropZone.addEventListener("click", function () { 113 trackEvent("drop_zone_click", { 114 event_category: "engagement", 115 event_label: "Drop Zone Clicked" 116 }); 117 }); 118 119 // Track drag events 120 dropZone.addEventListener("dragover", function () { 121 trackEvent("file_drag_over", { 122 event_category: "engagement", 123 event_label: "File Dragged Over Drop Zone" 124 }); 125 }, { once: true }); 126 } 127 128 // Track demo button 129 const tryDemoBtn = document.getElementById("tryDemoBtn"); 130 if (tryDemoBtn) { 131 tryDemoBtn.addEventListener("click", function () { 132 trackEvent("demo_epub_click", { 133 event_category: "engagement", 134 event_label: "Try Demo EPUB", 135 demo_file: "alice-in-wonderland" 136 }); 137 }); 138 } 139 140 // Track download button 141 const downloadBtn = document.getElementById("downloadEPUB"); 142 if (downloadBtn) { 143 downloadBtn.addEventListener("click", function () { 144 const fileName = window.uploadedFileName || 'unknown'; 145 trackEvent("download_epub_click", { 146 event_category: "conversion", 147 event_label: "Download EPUB", 148 file_name: fileName 149 }); 150 }); 151 } 152 153 // Track Export PDF/XTC button 154 const exportPdfXtcBtns = document.querySelectorAll('[onclick*="openXTCModal"]'); 155 exportPdfXtcBtns.forEach(btn => { 156 btn.addEventListener("click", function () { 157 const fileName = window.uploadedFileName || 'unknown'; 158 trackEvent("export_pdf_xtc_click", { 159 event_category: "conversion", 160 event_label: "Export PDF/XTC Button Clicked", 161 file_name: fileName 162 }); 163 }); 164 }); 165 166 // Track settings changes 167 const hintLevel = document.getElementById("hintLevel"); 168 if (hintLevel) { 169 hintLevel.addEventListener("change", function (e) { 170 trackEvent("hint_level_change", { 171 event_category: "settings", 172 event_label: "Hint Level Changed", 173 value: e.target.value, 174 text: e.target.options[e.target.selectedIndex].text 175 }); 176 }); 177 } 178 179 const translationLang = document.getElementById("translationLanguage"); 180 if (translationLang) { 181 translationLang.addEventListener("change", function (e) { 182 trackEvent("translation_language_change", { 183 event_category: "settings", 184 event_label: "Translation Language Changed", 185 language: e.target.value, 186 language_name: e.target.options[e.target.selectedIndex].text 187 }); 188 }); 189 } 190 191 const wordwiseStyle = document.getElementById("wordwiseStyle"); 192 if (wordwiseStyle) { 193 wordwiseStyle.addEventListener("change", function (e) { 194 trackEvent("wordwise_style_change", { 195 event_category: "settings", 196 event_label: "Display Style Changed", 197 style: e.target.value, 198 style_name: e.target.options[e.target.selectedIndex].text 199 }); 200 }); 201 } 202 203 const useFullDef = document.getElementById("useFullDefinition"); 204 if (useFullDef) { 205 useFullDef.addEventListener("change", (e) => { 206 trackEvent("full_definition_toggle", { 207 event_category: "settings", 208 event_label: "Full Definition Toggle", 209 checked: e.target.checked 210 }); 211 }); 212 } 213 214 const showPhoneme = document.getElementById("showPhoneme"); 215 if (showPhoneme) { 216 showPhoneme.addEventListener("change", (e) => { 217 trackEvent("phoneme_toggle", { 218 event_category: "settings", 219 event_label: "Phoneme Toggle", 220 checked: e.target.checked 221 }); 222 }); 223 } 224 225 // Track text input 226 const textInput = document.getElementById("textInput"); 227 if (textInput) { 228 let inputTimeout; 229 textInput.addEventListener("input", (e) => { 230 clearTimeout(inputTimeout); 231 inputTimeout = setTimeout(() => { 232 const charCount = e.target.value.length; 233 if (charCount > 0) { 234 trackEvent("text_input_used", { 235 event_category: "engagement", 236 event_label: "Text Pasted", 237 character_count: charCount, 238 character_range: charCount < 1000 ? 'short' : charCount < 5000 ? 'medium' : 'long' 239 }); 240 } 241 }, 2000); 242 }); 243 } 244 245 // Track preview button 246 const previewBtn = document.getElementById("previewBtn"); 247 if (previewBtn) { 248 previewBtn.addEventListener("click", function () { 249 trackEvent("preview_text_click", { 250 event_category: "engagement", 251 event_label: "Preview Text Button", 252 mode: "paste_text" 253 }); 254 }); 255 } 256 257 // Track mode switching 258 const modeUploadEpub = document.getElementById("modeUploadEpub"); 259 if (modeUploadEpub) { 260 modeUploadEpub.addEventListener("click", function () { 261 trackEvent("mode_switch", { 262 event_category: "navigation", 263 event_label: "Switch to Upload Mode", 264 mode: "upload" 265 }); 266 }); 267 } 268 269 const modePasteText = document.getElementById("modePasteText"); 270 if (modePasteText) { 271 modePasteText.addEventListener("click", function () { 272 trackEvent("mode_switch", { 273 event_category: "navigation", 274 event_label: "Switch to Paste Mode", 275 mode: "paste" 276 }); 277 }); 278 } 279 280 // Track extension clicks 281 const extensionImg = document.getElementById("extenstionImg"); 282 if (extensionImg) { 283 extensionImg.addEventListener("click", function () { 284 trackEvent("extension_click", { 285 event_category: "engagement", 286 event_label: "Extension Banner Clicked", 287 link_url: "chrome_web_store" 288 }); 289 }); 290 } 291 292 // Track extension links 293 const extensionLinks = document.querySelectorAll('a[href*="chromewebstore"]'); 294 extensionLinks.forEach(link => { 295 link.addEventListener("click", function () { 296 trackEvent("extension_link_click", { 297 event_category: "outbound", 298 event_label: "Chrome Web Store Link", 299 link_url: this.href 300 }); 301 }); 302 }); 303 304 // Track library book clicks 305 const demoBooksGrid = document.getElementById("demoBooksGrid"); 306 if (demoBooksGrid) { 307 demoBooksGrid.addEventListener("click", function(e) { 308 const bookCard = e.target.closest('.book-card'); 309 if (bookCard) { 310 const title = bookCard.querySelector('.book-title')?.textContent || 'Unknown'; 311 const author = bookCard.querySelector('.book-author')?.textContent || 'Unknown'; 312 trackEvent("library_book_click", { 313 event_category: "engagement", 314 event_label: "Library Book Selected", 315 book_title: title, 316 book_author: author 317 }); 318 } 319 }); 320 } 321 322 // Track PWA install 323 const installPwaBtn = document.getElementById('installPwaBtn'); 324 if (installPwaBtn) { 325 installPwaBtn.addEventListener("click", function () { 326 trackEvent("pwa_install_click", { 327 event_category: "engagement", 328 event_label: "PWA Install Button Clicked" 329 }); 330 }); 331 } 332 333 // Track page navigation 334 const prevPage = document.getElementById("prevPage"); 335 if (prevPage) { 336 prevPage.addEventListener("click", function () { 337 trackEvent("epub_navigation", { 338 event_category: "engagement", 339 event_label: "Previous Page", 340 direction: "previous" 341 }); 342 }); 343 } 344 345 const nextPage = document.getElementById("nextPage"); 346 if (nextPage) { 347 nextPage.addEventListener("click", function () { 348 trackEvent("epub_navigation", { 349 event_category: "engagement", 350 event_label: "Next Page",
351 direction: "next" 352 }); 353 }); 354 } 355 356 // Track scroll to top 357 const goToTopBtn = document.getElementById("goToTopBtn"); 358 if (goToTopBtn) { 359 goToTopBtn.addEventListener("click", function () { 360 trackEvent("scroll_to_top", { 361 event_category: "navigation", 362 event_label: "Go to Top Button" 363 }); 364 }); 365 } 366 367 // Track scroll depth 368 let maxScroll = 0; 369 let scrollTimeout; 370 window.addEventListener('scroll', function() { 371 clearTimeout(scrollTimeout); 372 scrollTimeout = setTimeout(() => { 373 const scrollPercent = Math.round((window.scrollY / (document.documentElement.scrollHeight - window.innerHeight)) * 100); 374 if (scrollPercent > maxScroll) { 375 maxScroll = scrollPercent; 376 if (scrollPercent >= 25 && scrollPercent < 50) { 377 trackEvent('scroll_depth', { event_category: 'engagement', event_label: '25%', value: 25 }); 378 } else if (scrollPercent >= 50 && scrollPercent < 75) { 379 trackEvent('scroll_depth', { event_category: 'engagement', event_label: '50%', value: 50 }); 380 } else if (scrollPercent >= 75 && scrollPercent < 90) { 381 trackEvent('scroll_depth', { event_category: 'engagement', event_label: '75%', value: 75 }); 382 } else if (scrollPercent >= 90) { 383 trackEvent('scroll_depth', { event_category: 'engagement', event_label: '90%', value: 90 }); 384 } 385 } 386 }, 300); 387 }); 388 389 // Track slider interaction 390 const sliderHandle = document.querySelector('.slider-handle'); 391 if (sliderHandle) { 392 let sliderInteracted = false; 393 sliderHandle.addEventListener('mousedown', function() { 394 if (!sliderInteracted) { 395 trackEvent('slider_interaction', { 396 event_category: 'engagement', 397 event_label: 'Before/After Slider Used', 398 interaction_type: 'mouse' 399 }); 400 sliderInteracted = true; 401 } 402 }); 403 sliderHandle.addEventListener('touchstart', function() { 404 if (!sliderInteracted) { 405 trackEvent('slider_interaction', { 406 event_category: 'engagement', 407 event_label: 'Before/After Slider Used', 408 interaction_type: 'touch' 409 }); 410 sliderInteracted = true; 411 } 412 }); 413 } 414 415 // Track time on page 416 let startTime = Date.now(); 417 window.addEventListener('beforeunload', function() { 418 const timeSpent = Math.round((Date.now() - startTime) / 1000); 419 trackEvent('time_on_page', { 420 event_category: 'engagement', 421 event_label: 'Time Spent', 422 value: timeSpent, 423 time_range: timeSpent < 30 ? 'short' : timeSpent < 120 ? 'medium' : 'long' 424 }); 425 }); 426 }); 427 428 // Track file upload success/error 429 window.addEventListener('epub_upload_success', function(e) { 430 trackEvent('epub_upload_success', { 431 event_category: 'conversion', 432 event_label: 'EPUB Upload Success', 433 file_name: e.detail?.fileName || 'unknown', 434 file_size: e.detail?.fileSize || 0 435 }); 436 }); 437 438 window.addEventListener('epub_upload_error', function(e) { 439 trackEvent('epub_upload_error', { 440 event_category: 'error', 441 event_label: 'EPUB Upload Error', 442 error_message: e.detail?.error || 'unknown' 443 }); 444 }); 445 446 // Track EPUB processing success/error 447 window.addEventListener('epub_process_success', function(e) { 448 trackEvent('epub_process_success', { 449 event_category: 'conversion', 450 event_label: 'EPUB Processing Success', 451 file_name: e.detail?.fileName || 'unknown', 452 language: e.detail?.language || 'unknown', 453 processing_time: e.detail?.processingTime || 0 454 }); 455 }); 456 457 window.addEventListener('epub_process_error', function(e) { 458 trackEvent('epub_process_error', { 459 event_category: 'error', 460 event_label: 'EPUB Processing Error', 461 error_message: e.detail?.error || 'unknown' 462 }); 463 }); 464 465 // Track download success 466 window.addEventListener('epub_download_success', function(e) { 467 trackEvent('epub_download_success', { 468 event_category: 'conversion', 469 event_label: 'EPUB Download Success', 470 file_name: e.detail?.fileName || 'unknown', 471 file_size: e.detail?.fileSize || 0 472 }); 473 }); 474 475 // Track XTC Modal events 476 window.addEventListener('xtc_modal_opened', function(e) { 477 trackEvent('xtc_modal_opened', { 478 event_category: 'conversion', 479 event_label: 'XTC Modal Opened', 480 file_name: e.detail?.fileName || 'unknown' 481 }); 482 }); 483 484 window.addEventListener('xtc_modal_closed', function(e) { 485 trackEvent('xtc_modal_closed', { 486 event_category: 'conversion', 487 event_label: 'XTC Modal Closed', 488 time_spent: e.detail?.timeSpent || 0 489 }); 490 }); 491 492 // Track PDF export 493 window.addEventListener('pdf_export_started', function(e) { 494 trackEvent('pdf_export_started', { 495 event_category: 'conversion', 496 event_label: 'PDF Export Started', 497 file_name: e.detail?.fileName || 'unknown' 498 }); 499 }); 500 501 window.addEventListener('pdf_export_success', function(e) { 502 trackEvent('pdf_export_success', { 503 event_category: 'conversion', 504 event_label: 'PDF Export Success', 505 file_name: e.detail?.fileName || 'unknown', 506 page_count: e.detail?.pageCount || 0, 507 export_time: e.detail?.exportTime || 0 508 }); 509 }); 510 511 window.addEventListener('pdf_export_error', function(e) { 512 trackEvent('pdf_export_error', { 513 event_category: 'error', 514 event_label: 'PDF Export Error', 515 error_message: e.detail?.error || 'unknown' 516 }); 517 }); 518 519 // Track XTC export
520 window.addEventListener('xtc_export_started', function(e) { 521 trackEvent('xtc_export_started', { 522 event_category: 'conversion', 523 event_label: 'XTC Export Started', 524 file_name: e.detail?.fileName || 'unknown' 525 }); 526 }); 527 528 window.addEventListener('xtc_export_success', function(e) { 529 trackEvent('xtc_export_success', { 530 event_category: 'conversion', 531 event_label: 'XTC Export Success', 532 file_name: e.detail?.fileName || 'unknown', 533 file_size: e.detail?.fileSize || 0, 534 export_time: e.detail?.exportTime || 0 535 }); 536 }); 537 538 window.addEventListener('xtc_export_error', function(e) { 539 trackEvent('xtc_export_error', { 540 event_category: 'error', 541 event_label: 'XTC Export Error', 542 error_message: e.detail?.error || 'unknown' 543 }); 544 }); 545 546 // Track EPUB to XTC conversion (ruby tag processing) 547 window.addEventListener('epub_xtc_conversion_started', function(e) { 548 trackEvent('epub_xtc_conversion_started', { 549 event_category: 'conversion', 550 event_label: 'EPUB to XTC Conversion Started', 551 file_name: e.detail?.fileName || 'unknown' 552 }); 553 }); 554 555 window.addEventListener('epub_xtc_conversion_success', function(e) { 556 trackEvent('epub_xtc_conversion_success', { 557 event_category: 'conversion', 558 event_label: 'EPUB to XTC Conversion Success', 559 file_name: e.detail?.fileName || 'unknown', 560 ruby_tags_converted: e.detail?.rubyTagsConverted || 0 561 }); 562 }); 563 564 // Track PWA install events 565 window.addEventListener('beforeinstallprompt', function(e) { 566 trackEvent('pwa_install_prompt_shown', { 567 event_category: 'pwa', 568 event_label: 'Install Prompt Shown' 569 }); 570 }); 571 572 window.addEventListener('appinstalled', function() { 573 trackEvent('pwa_installed', { 574 event_category: 'pwa', 575 event_label: 'App Installed Successfully' 576 }); 577 }); 578}
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.