// ==UserScript== // @name BearBit Tweak // @namespace https://bearbit.org/ // @version 26.7.04.1048 // @description BearBit Tweak // @author riffburn // @match https://bearbit.org/viewno18sbx.php* // @match https://bearbit.org/viewbrsb.php* // @match https://bearbit.org/details.php* // @grant GM_addStyle // @grant GM_setValue // @grant GM_getValue // @grant GM_xmlhttpRequest // @grant GM_download // @updateURL https://raw.githubusercontent.com/amajio/bearbit-tweak/master/bearbit.user.js // @downloadURL https://raw.githubusercontent.com/amajio/bearbit-tweak/master/bearbit.user.js // @connect bearbit.org // ==/UserScript== (function() { 'use strict'; // Shared GLightbox instance (lazily initialised on first use) let _glightbox = null; const noimage = 'https://bearbit.org/no_poster.jpg'; const imageSize = { 'width':{'minimal':50, 'small': 75, 'normal': 100, 'large': 140}, 'height':{'minimal':50, 'small': 75,'normal': 140, 'large': 180}, }; // Load GLightbox into the page context (bypasses userscript sandbox proxy issues) function loadGLightbox(callback) { if (document.getElementById('glightbox-js')) { // Already injected — wait for it if not yet ready if (typeof unsafeWindow.GLightbox === 'function') { callback(); } else { document.getElementById('glightbox-js').addEventListener('load', callback); } return; } // Inject CSS const link = document.createElement('link'); link.rel = 'stylesheet'; link.href = 'https://cdn.jsdelivr.net/npm/glightbox/dist/css/glightbox.min.css'; document.head.appendChild(link); // Inject JS into page context so GLightbox runs outside the sandbox const script = document.createElement('script'); script.id = 'glightbox-js'; script.src = 'https://cdn.jsdelivr.net/npm/glightbox/dist/js/glightbox.min.js'; script.onload = callback; document.head.appendChild(script); } function openGLightbox(imageUrl) { loadGLightbox(() => { if (_glightbox) { _glightbox.destroy(); _glightbox = null; } // Use unsafeWindow.GLightbox so it runs in page context, not sandbox _glightbox = unsafeWindow.GLightbox({ elements: [{ href: imageUrl, type: 'image' }], touchNavigation: true, loop: false, autoplayVideos: false, openEffect: 'fade', touchNavigation: true, zoomable: true, draggable: true, dragToleranceX: 150, dragToleranceY: 150, onOpen: function() { // Force reset the container transform to 0 const mediaContainer = document.querySelector('.gslide-media'); if (mediaContainer) { // Override the transform to remove horizontal offset mediaContainer.style.transform = 'translate3d(0px, 0px, 0px)'; const observer = new MutationObserver(function(mutations) { mutations.forEach(function(mutation) { if (mutation.attributeName === 'style') { const currentTransform = mediaContainer.style.transform; if (currentTransform && currentTransform.includes('translate3d(-')) { mediaContainer.style.transform = 'translate3d(0px, 0px, 0px)'; } } }); }); observer.observe(mediaContainer, { attributes: true }); } } }); _glightbox.open(); }); } let previewContainer = null; let currentLink = null; let hideTimeout = null; let isHovering = false; const bookmarkClass = '.bookmark-btn'; // Default settings const defaultSettings = { HIDE_DESC:false, HIDE_STICKY: false, PREVIEW: false, THUMBNAIL_SIZE: 140 }; // Load settings from storage or use defaults const settings = { HIDE_STICKY: GM_getValue('HIDE_STICKY', defaultSettings.HIDE_STICKY), HIDE_DESC: GM_getValue('HIDE_DESC', defaultSettings.HIDE_DESC), PREVIEW: GM_getValue('PREVIEW', defaultSettings.PREVIEW), THUMBNAIL_SIZE: GM_getValue('THUMBNAIL_SIZE', defaultSettings.THUMBNAIL_SIZE) }; // Safe CSS injection function function addStyle(css) { const style = document.createElement('style'); style.type = 'text/css'; style.textContent = css; document.head.appendChild(style); return style; } // Apply all styles addStyle(` #preview-card { max-height: 90vh !important; overflow: auto !important; } #preview-card img { height: 80vh !important; object-fit: contain !important; } .card-content { position: sticky !important; bottom: 0 !important; background: #222222 !important; } .poster-column { width: 5% !important; } .poster-column img { height: ${settings.THUMBNAIL_SIZE}px !important; max-height: ${settings.THUMBNAIL_SIZE}px !important; border: 0px !important; } /* Settings Panel Styles */ .bearbit-settings-panel { position: fixed; top: 50%; left: 50%; transform: translate(-50%, -50%); background: #222222 !important; border: 2px solid #444 !important; border-radius: 10px !important; padding: 20px !important; z-index: 10000 !important; box-shadow: 0 0 20px rgba(0,0,0,0.8) !important; min-width: 250px !important; font-family: Arial, sans-serif !important; color: white !important; } .bearbit-settings-header { display: flex !important; justify-content: space-between !important; align-items: center !important; margin-bottom: 15px !important; border-bottom: 1px solid #444 !important; padding-bottom: 10px !important; } .bearbit-settings-title { font-size: 18px !important; font-weight: bold !important; color: white !important; } .bearbit-settings-close { background: none !important; border: none !important; font-size: 20px !important; cursor: pointer !important; color: #ccc !important; } .bearbit-settings-close:hover { color: white !important; } .bearbit-settings-group { margin-bottom: 15px !important; } .bearbit-settings-label { display: flex !important; align-items: center !important; margin-bottom: 8px !important; cursor: pointer !important; color: white !important; position: relative; } /* Tooltip container for the info icon */ .info-icon { display: inline-flex; align-items: center; justify-content: center; width: 18px; height: 18px; margin-left: 6px; font-size: 12px; font-weight: bold; border-radius: 50%; background-color: rgba(255, 255, 255, 0.2); cursor: help; position: relative; } /* Tooltip text */ .info-icon .tooltip-text { visibility: hidden; opacity: 0; width: 260px; background-color: #1D2E27; color: #fff; text-align: left; border-radius: 8px; padding: 10px 12px; position: absolute; z-index: 1000; bottom: 125%; left: 50%; transform: translateX(-50%); white-space: normal; font-size: 12px; font-weight: normal; line-height: 1.5; transition: opacity 0.2s ease, visibility 0.2s ease; box-shadow: 0 4px 12px rgba(0, 0, 0, 0.3); border: 1px solid rgba(255, 255, 255, 0.1); pointer-events: none; } /* Tooltip arrow */ .info-icon .tooltip-text::after { content: ""; position: absolute; top: 100%; left: 50%; margin-left: -6px; border-width: 6px; border-style: solid; border-color: #1a1a2e transparent transparent transparent; } /* Show tooltip on hover */ .info-icon:hover .tooltip-text { visibility: visible; opacity: 1; } /* Tooltip list styling */ .tooltip-text ul { margin: 0; padding-left: 16px; } .tooltip-text li { margin: 4px 0; } .tooltip-text strong { color: #ffd966; } .bearbit-settings-checkbox { margin-right: 8px !important; } .bearbit-settings-select { width: 100% !important; padding: 8px !important; border: 1px solid #444 !important; border-radius: 4px !important; background: #333 !important; color: white !important; margin-top: 5px !important; } .bearbit-settings-buttons { display: flex !important; justify-content: space-between !important; margin-top: 20px !important; gap: 20px; } .bearbit-settings-btn { padding: 10px 16px !important; border: none !important; border-radius: 4px !important; cursor: pointer !important; font-weight: bold !important; color: white !important; } .bearbit-settings-save { background: #4CAF50 !important; } .bearbit-settings-save:hover { background: #45a049 !important; } .bearbit-settings-reset { background: #666 !important; } .bearbit-settings-reset:hover { background: #777 !important; } .bearbit-settings-toggle { position: fixed !important; top: 10px !important; right: 10px !important; background: #222 !important; color: white !important; border: 2px solid #444 !important; border-radius: 50% !important; width: 40px !important; height: 40px !important; cursor: pointer !important; z-index: 9999 !important; font-size: 18px !important; display: flex !important; align-items: center !important; justify-content: center !important; } .bearbit-settings-toggle:hover { background: #333 !important; border-color: #555 !important; } .bearbit-settings-overlay { position: fixed !important; top: 0 !important; left: 0 !important; width: 100% !important; height: 100% !important; background: rgba(0,0,0,0.7) !important; z-index: 9998 !important; } /* Style checkboxes for dark theme */ .bearbit-settings-checkbox { accent-color: #4CAF50 !important; } /* Style labels and text */ .bearbit-settings-group label { color: white !important; font-weight: normal !important; } /* Style bearbit actions button */ .bb-actions { margin-top: 3px; display: flex; gap: 6px; align-items: center; flex-wrap: wrap; } .bb-actions a { font-family: Tahoma, Arial, sans-serif; font-size: 11px; font-weight: bold; text-decoration: none; border-radius: 16px; padding: 5px 11px; line-height: 1.2; display: inline-flex; align-items: center; gap: 4px; border: 1px solid transparent; box-shadow: rgba(15, 23, 42, 0.12) 0px 1px 4px; } .bb-preview { background: #ffffff; border-color: rgb(203, 213, 225) !important; } .bb-bookmark { background: none !important; color: rgb(157, 171, 190); border: 0px !important; box-shadow: none !important; padding: 3px 5px !important; font-size: 20px !important; } .bb-bookmark.bookmarked { color: rgb(245, 162, 20); } /* Enable scrolling on GLightbox content for tall images */ .glightbox-container .glightbox-caption { max-height: 80vh; overflow-y: auto; } .glightbox-mobile .glightbox-container .gslide-image img { max-height: 80vh !important; object-fit: contain; } /* Allow scrolling on the slide content */ .gslide-media { overflow-y: auto !important; max-height: 95vh !important; } /* Custom scrollbar styling */ .gslide-media::-webkit-scrollbar { width: 8px; } .gslide-media::-webkit-scrollbar-track { background: #333; } .gslide-media::-webkit-scrollbar-thumb { background: #666; border-radius: 4px; } .filter-container { padding-top: 3px; padding-bottom: 3px; top: 0px; z-index: 101; display: flex; flex-wrap: wrap; justify-content: center; gap: 5px; } .filter-container a { font-family: Tahoma, Arial, sans-serif; font-size: 10px; font-weight: bold; text-decoration: none; border-radius: 16px; padding: 5px 11px; min-width: 80px; line-height: 1.2; display: inline-flex; align-items: center; justify-content: center; margin: 3px; border: 1px solid transparent; box-shadow: rgba(15, 23, 42, 0.12) 0px 1px 4px; color: #ffffff; transition: all 0.3s ease; white-space: nowrap; } `); function createSettingsPanel() { const existingPanel = document.getElementById('bearbit-settings-panel'); if (existingPanel) return; const overlay = document.createElement('div'); overlay.className = 'bearbit-settings-overlay'; overlay.onclick = hideSettingsPanel; const panel = document.createElement('div'); panel.id = 'bearbit-settings-panel'; panel.className = 'bearbit-settings-panel'; panel.innerHTML = `
BearBit Tweak Settings
`; // Event listeners panel.querySelector('.bearbit-settings-close').onclick = hideSettingsPanel; panel.querySelector('.bearbit-settings-save').onclick = saveSettings; panel.querySelector('.bearbit-settings-reset').onclick = resetSettings; // Handle dropdown change const sizeSelect = panel.querySelector('#thumbnail-size-select'); document.body.appendChild(overlay); document.body.appendChild(panel); } function hideSettingsPanel() { const panel = document.getElementById('bearbit-settings-panel'); const overlay = document.querySelector('.bearbit-settings-overlay'); const toggle = document.querySelector('.bearbit-settings-toggle'); if (panel) { panel.querySelector('.bearbit-settings-close')?.removeEventListener('click', hideSettingsPanel); panel.remove(); } if (overlay) overlay.remove(); if (toggle) toggle.style.display = 'block'; } function saveSettings() { const sizeSelect = document.getElementById('thumbnail-size-select'); const newSettings = { HIDE_DESC: document.getElementById('hide-desc').checked, PREVIEW: document.getElementById('preview').checked, THUMBNAIL_SIZE: sizeSelect.value, }; // Save to storage GM_setValue('HIDE_DESC', newSettings.HIDE_DESC); GM_setValue('PREVIEW', newSettings.PREVIEW); GM_setValue('THUMBNAIL_SIZE', newSettings.THUMBNAIL_SIZE); // Update current settings Object.assign(settings, newSettings); hideSettingsPanel(); window.location.reload(); } function resetSettings() { if (confirm('Reset all settings to defaults?')) { GM_setValue('HIDE_STICKY', defaultSettings.HIDE_STICKY); GM_setValue('HIDE_DESC', defaultSettings.HIDE_DESC); GM_setValue('PREVIEW', defaultSettings.PREVIEW); GM_setValue('THUMBNAIL_SIZE', defaultSettings.THUMBNAIL_SIZE); Object.assign(settings, defaultSettings); hideSettingsPanel(); window.location.reload(); } } function createSettingsButton() { const button = document.createElement('button'); button.className = 'bearbit-settings-toggle'; button.innerHTML = '⚙️'; button.title = 'BearBit Settings'; button.onclick = function() { this.style.display = 'none'; createSettingsPanel(); }; document.body.appendChild(button); const posters = document.querySelectorAll('.poster-column img'); let matchedSize = null; Object.entries(imageSize.height).forEach(([size, value]) => { if(settings.THUMBNAIL_SIZE == value) { matchedSize = size; } }); if(matchedSize) { posters.forEach(image => { image.style.setProperty('width', imageSize.width[matchedSize] + 'px', 'important'); }); } } function cleanupButtons() { document.querySelectorAll('[data-processed="true"]').forEach(btn => { if (btn.clickHandler) { btn.removeEventListener('click', btn.clickHandler); delete btn.clickHandler; } delete btn.dataset.processed; }); } function hideHotTorrentSection() { const h2 = document.querySelector('h2 img[alt="Hot"]')?.closest('h2'); const hr = [...document.querySelectorAll('h2')].find(el => el.textContent.includes('รายการไฟล์'))?.previousElementSibling; const elementsToHide = []; if (h2 && hr) { let current = h2.nextSibling; while (current && current !== hr) { if (current.nodeType === Node.ELEMENT_NODE) { elementsToHide.push(current); } current = current.nextSibling; } elementsToHide.push(hr); elementsToHide.push(h2); elementsToHide.forEach(element => { if(!settings.HIDE_STICKY){ element.style.display = ''; }else{ element.style.display = 'none'; } }); } } function hideColumns(){ const text_header = ['ไฟล์','ติชม','เสร็จ']; const headers = document.querySelectorAll('.colhead.poster-column'); headers.forEach(head => { const table = head.closest('table'); const headerRow = table.querySelector('tr'); const rows = table.querySelectorAll('tr'); const columnsToHide = []; let columnsUploader = null; const headerCells = headerRow.querySelectorAll('th, td'); headerCells.forEach((cell, index) => { const cellText = cell.textContent.trim(); if (text_header.includes(cellText)) { columnsToHide.push(index + 1); } if(cellText == 'ผู้ปล่อยไฟล์') columnsUploader = index + 1; }); rows.forEach(row => { // Hide uploader avatar if(columnsUploader){ const uploader = row.querySelector(`td:nth-child(${columnsUploader})`); if (uploader) { uploader.style.width = 'auto'; const children = uploader.children; for (let i = 0; i < children.length; i++) { if (children[i].tagName !== 'A') { children[i].style.display = 'none'; } } } } //Hide column columnsToHide.forEach(col => { let el = row.querySelector(`td:nth-child(${col})`); if (el) el.style.display = 'none'; }); }); }); } function hideDescription(){ if(!settings.HIDE_DESC) return; const posters = document.querySelectorAll('.poster-column'); if(!posters) return; posters.forEach(poster => { const row = poster.closest('tr'); if (!row) return; const br = row.querySelector('br'); if(br){ let nextDiv = br.nextElementSibling; while (nextDiv && nextDiv.tagName !== 'DIV') { nextDiv = nextDiv.nextElementSibling; } if (nextDiv) { let current = br; let nextNode; while (current && current !== nextDiv) { nextNode = current.nextSibling; current.remove(); current = nextNode; } } else { let elem = br; while (elem && elem.nextSibling && elem.nextSibling.tagName !== 'DIV') { elem = elem.nextSibling; elem.remove(); } if (elem && elem.nextSibling && elem.nextSibling.tagName === 'DIV') { } } } }); } function cleanWhitespace(row) { const textNodes = []; const walker = document.createTreeWalker( row, NodeFilter.SHOW_TEXT, { acceptNode: function(node) { const text = node.textContent; if(text.trim() === '' || text.trim() === 'whitespace') { return NodeFilter.FILTER_ACCEPT; } return NodeFilter.FILTER_REJECT; } } ); while(walker.nextNode()) { textNodes.push(walker.currentNode); } textNodes.forEach(node => { if(node.parentNode) { node.parentNode.removeChild(node); } }); const emptyDivs = row.querySelectorAll('div'); emptyDivs.forEach(div => { if(div.children.length === 0 && div.textContent.trim() === '') { div.remove(); } else if(div.children.length === 0 && div.textContent.trim() === 'whitespace') { div.remove(); } }); } function hideBBVip(){ const div_vip = document.querySelectorAll('.bb-file-actions'); div_vip.forEach(div => { div.style.setProperty('display', 'none','important'); }); } function buildFilterButton(text, suffix, color){ const element = document.createElement('a'); element.style.cssText = ` display: inline-flex; padding: 5px; cursor: pointer; border: 2px solid; border-color: ${color}; color: black; transition: all 0.2s ease; background: white; `; element.textContent = `${text}`; element.className = `bb-filter ${suffix}` element.dataset.color = `${color}`; if (suffix === 'all') { element.classList.add('active'); element.style.backgroundColor =`${color}`; element.style.color = 'black'; } element.addEventListener('click', (e) => { e.preventDefault(); const currentActive = document.querySelector('.bb-filter.active'); if (currentActive) { currentActive.classList.remove('active'); currentActive.style.backgroundColor = 'white'; currentActive.style.color = 'black'; } element.classList.add('active'); element.style.backgroundColor = element.dataset.color; if(e.target.classList.contains('all')){ element.style.color = 'black'; }else{ element.style.color = 'white'; } const btnDownloads = document.querySelectorAll('.bb-download'); btnDownloads.forEach(btn => { const row = btn.closest('tr'); if(!row) return; if (e.target.classList.contains('all')) { row.style.display = ''; }else{ if (!btn.classList.contains(`${suffix}`)) { row.style.display = 'none'; }else{ row.style.display = ''; } } }); const headers = document.querySelectorAll('.colhead.poster-column'); let counter=0; headers.forEach((head, tindex )=> { const empty = document.getElementById(`empty-table-${tindex}`); if(empty){ empty.remove(); } const table = head.closest('table'); const rows = table.querySelectorAll('tr'); rows.forEach((row, rindex) =>{ if(rindex == 0) return; //skip header if(row.style.display != 'none') return; counter++; }); if((rows.length-1)-counter == 0){ const tr = document.createElement('tr'); const td = document.createElement('td'); tr.id = `empty-table-${tindex}`; tr.style.height = '60px'; td.colSpan = 13; td.textContent = 'ไม่พบไฟล์'; td.style.textAlign = 'center'; td.style.verticalAlign = 'middle'; tr.appendChild(td); table.appendChild(tr); } counter=0 }); }); return element } function filters(){ const filterStyle = [ { text: 'ทั้งหมด', suffix: 'all', color: '#CBD5E1' },{ text: 'น้อยกว่า 4 GB', suffix: 'small', color: '#16A34A' },{ text: '4 - 10 GB', suffix: 'medium', color: '#2563EB' },{ text: '10 - 30 GB', suffix: 'large', color: '#EA580C' },{ text: '30 GB ขึ้นไป', suffix: 'xlarge', color: '#DC2626' } ]; const toggle_posters = document.getElementById('toggle-posters-btn'); if(!toggle_posters) return; const hideHotTorrent = document.createElement('button'); let content = settings.HIDE_STICKY ? 'แสดง Hot Torrents':'ซ่อน Hot Torrents'; hideHotTorrent.textContent = content; hideHotTorrent.style.cssText = ` font-size: 11px; padding: 5px 10px; cursor: pointer; margin-right: 10px; `; hideHotTorrent.addEventListener('click', (e) => { settings.HIDE_STICKY = !settings.HIDE_STICKY GM_setValue('HIDE_STICKY', settings.HIDE_STICKY); let content = settings.HIDE_STICKY ? 'แสดง Hot Torrents':'ซ่อน Hot Torrents'; if(settings.HIDE_STICKY){ hideHotTorrent.textContent = content; }else{ hideHotTorrent.textContent = content; } hideHotTorrentSection(); }); const parentDiv = toggle_posters.parentElement; parentDiv.style.cssText = ` display: flex; justify-content: space-between; align-items: center; margin: 10px 0; padding-right: 5px; `; const centerWrapper = document.createElement('div'); centerWrapper.className = 'filter-container'; centerWrapper.style.cssText = ` display: flex; gap: 10px; justify-content: center; flex: 1; `; filterStyle.forEach(style => { let elem = buildFilterButton(style.text, style.suffix, style.color); centerWrapper.appendChild(elem); }); parentDiv.insertBefore(centerWrapper, toggle_posters); parentDiv.insertBefore(hideHotTorrent, toggle_posters); const rightWrapper = document.createElement('div'); rightWrapper.style.cssText = 'text-align: right;'; parentDiv.insertBefore(rightWrapper, toggle_posters); rightWrapper.appendChild(toggle_posters); } function actionsButton() { cleanupButtons(); hideBBVip(); const posterRows = document.querySelectorAll('tr td[class="poster-column"]'); const baseUrl = "https://bearbit.org/"; posterRows.forEach(posterTd => { const row = posterTd.closest('tr'); if (!row) return; const link = row.querySelector('a[href^="details.php"]'); if (!link) return; const sizeCell = row.querySelector('.bb-lock-download'); let fileSize = ' - ' let numericSize = 0; let sizeUnit = ''; if (sizeCell) { const sizeText = sizeCell.innerText.trim(); const match = sizeText.match(/\(([\d.]+)\s*([kK][bB]|[mM][bB]|[gG][bB]|[tT][bB])\)/); if (match) { numericSize = parseFloat(match[1]); sizeUnit = match[2].toUpperCase(); fileSize = `${numericSize} ${sizeUnit}`; // Convert to GB for comparison if (sizeUnit === 'TB') { numericSize = numericSize * 1024; // 1 TB = 1024 GB } else if (sizeUnit === 'MB') { numericSize = numericSize / 1024; } else if (sizeUnit === 'KB') { numericSize = numericSize / (1024 * 1024); } } } //hideDiv(row); row.querySelector('img[src="pic/cams.gif "]')?.style.setProperty('display', 'none'); const originalBookmark = row.querySelector(`${bookmarkClass}`); const divGroup = document.createElement('div'); divGroup.className = 'bb-actions'; const nameCell = row.querySelector('td:nth-child(3)'); const filename_div = document.createElement('div'); filename_div.className = 'hover-area'; filename_div.style.display = 'inline-block'; link.parentNode.insertBefore(filename_div, link); filename_div.appendChild(link); const btnDownload = document.createElement('a'); btnDownload.textContent = `📥 ดาวน์โหลด (${fileSize})`; btnDownload.style.color = '#ffffff'; btnDownload.style.cursor = 'pointer'; btnDownload.title = 'คลิกเพื่อดาวน์โหลด'; const btnImage = document.createElement('a'); const camsImg = row.querySelector('.bb-preview-btn')?.href; btnImage.textContent = `📷 รูป`; btnImage.style.cursor = 'pointer'; btnImage.className = 'bb-preview'; btnImage.href = '#'; btnImage.title = 'ดูรูปตัวอย่าง'; if (camsImg) { btnImage.addEventListener('click', (e) => { e.preventDefault(); e.stopPropagation(); openGLightbox(camsImg); }); } else { btnImage.style.opacity = '0.4'; btnImage.style.cursor = 'default'; btnImage.title = 'ไม่มีรูปตัวอย่าง'; } const btnBookmark = document.createElement('a'); btnBookmark.textContent = `★`; btnBookmark.className = 'bb-bookmark'; btnBookmark.style.cursor = 'pointer'; btnBookmark.title = 'เพิ่ม/ลบ ออกจากบุ๊กมาร์ก'; if (originalBookmark.classList.contains('bookmarked')) { btnBookmark.classList.add('bookmarked'); } // Set background color based on file size (in GB) let bgColor, suffixClass; if (numericSize < 4) { bgColor = '#16A34A'; // Green suffixClass = 'small'; } else if (numericSize >= 4 && numericSize <= 10 ) { bgColor = '#2563EB'; // blue suffixClass = 'medium'; } else if (numericSize > 10 && numericSize <= 30) { bgColor = '#EA580C'; // Orange suffixClass = 'large'; } else if (numericSize > 30) { bgColor = '#DC2626'; // Red suffixClass = 'xlarge'; } else { bgColor = '#16A34A'; // Green (for unknown size) suffixClass = 'small'; } btnDownload.style.backgroundColor = bgColor; btnDownload.className = `bb-download ${suffixClass}`; divGroup.appendChild(btnImage); divGroup.appendChild(btnDownload); divGroup.appendChild(btnBookmark); nameCell.appendChild(divGroup); cleanWhitespace(row); if (!btnDownload || btnDownload.dataset.processed === 'true') return; const targetUrl = `${baseUrl}${link.getAttribute('href')}`; btnDownload.dataset.targetUrl = targetUrl; btnDownload.dataset.processed = 'true'; btnDownload.removeEventListener('click', btnDownload.clickHandler); btnBookmark.addEventListener('click', () => { originalBookmark.click(); if (originalBookmark.classList.contains('bookmarked')) { btnBookmark.classList.add('bookmarked'); }else{ btnBookmark.classList.remove('bookmarked'); } }); btnDownload.clickHandler = function(event) { event.preventDefault(); event.stopPropagation(); if (this.dataset.loading === 'true') return; const storedUrl = this.dataset.targetUrl; if (!storedUrl) { console.error('No target URL found'); return; } this.dataset.loading = 'true'; // Store current state before changing const currentState = { html: this.innerHTML, cursor: this.style.cursor }; this.innerHTML = '⏳ กำลังโหลด...'; this.style.cursor = 'wait'; GM_xmlhttpRequest({ method: "GET", url: storedUrl, timeout: 10000, onload: function(response) { if (response.status !== 200) { console.error(`Failed to load ${storedUrl}: ${response.status}`); this.innerHTML = '❌ ล้มเหลว'; this.style.cursor = 'pointer'; setTimeout(() => { this.innerHTML = currentState.html; this.style.cursor = currentState.cursor; this.dataset.loading = 'false'; }, 2000); return; } const parser = new DOMParser(); const doc = parser.parseFromString(response.responseText, "text/html"); const downloadBtn = doc.querySelector('a[href^="downloadnew.php"]') if (downloadBtn) { let downloadUrl = downloadBtn.href; const fullDownloadUrl = downloadUrl.startsWith('http') ? downloadUrl : `${baseUrl}${downloadUrl}`; // 🔥 FIX: Set loading to true BEFORE showing "รอลิงค์ดาวน์โหลด..." this.dataset.loading = 'true'; this.innerHTML = '⏳ รอลิงค์ดาวน์โหลด...'; this.style.cursor = 'wait'; GM_xmlhttpRequest({ method: "GET", url: fullDownloadUrl, timeout: 10000, onload: function(secondResponse) { if (secondResponse.status !== 200) { console.error(`Failed to load ${fullDownloadUrl}: ${secondResponse.status}`); this.innerHTML = '❌ ล้มเหลว'; this.style.cursor = 'pointer'; setTimeout(() => { this.innerHTML = currentState.html; this.style.cursor = currentState.cursor; this.dataset.loading = 'false'; }, 2000); return; } const secondParser = new DOMParser(); const secondDoc = secondParser.parseFromString(secondResponse.responseText, "text/html"); const bbDlBtn = secondDoc.getElementById('bbDlBtn'); if (bbDlBtn) { let finalDownloadUrl = bbDlBtn.href; const fullFinalUrl = finalDownloadUrl.startsWith('http') ? finalDownloadUrl : `${baseUrl}${finalDownloadUrl}`; // Keep loading state true this.dataset.loading = 'true'; this.innerHTML = '⏳ รอลิงค์ดาวน์โหลด...'; this.style.cursor = 'wait'; try { const iframe = document.createElement('iframe'); iframe.style.display = 'none'; iframe.src = fullFinalUrl; document.body.appendChild(iframe); const self = this; iframe.addEventListener('load', function() { try { const iframeDoc = iframe.contentDocument || iframe.contentWindow.document; const waitForButton = setInterval(function() { const button = iframeDoc.getElementById('bbDlBtn'); if (button) { clearInterval(waitForButton); const observer = new MutationObserver(function(mutations) { mutations.forEach(function(mutation) { if (mutation.type === 'attributes' && mutation.attributeName === 'class') { const isDisabled = button.classList.contains('bb-disabled'); if (!isDisabled) { self.innerHTML = '✅ กำลังดาวน์โหลด...'; self.style.cursor = 'pointer'; button.click(); observer.disconnect(); setTimeout(function() { document.body.removeChild(iframe); self.innerHTML = currentState.html; self.style.cursor = currentState.cursor; // 🔥 FIX: Re-enable button after download starts self.dataset.loading = 'false'; }, 3000); } } }); }); observer.observe(button, { attributes: true, attributeFilter: ['class'] }); if (!button.classList.contains('bb-disabled')) { self.innerHTML = '✅ กำลังดาวน์โหลด...'; self.style.cursor = 'pointer'; button.click(); observer.disconnect(); setTimeout(function() { document.body.removeChild(iframe); self.innerHTML = currentState.html; self.style.cursor = currentState.cursor; // 🔥 FIX: Re-enable button after download starts self.dataset.loading = 'false'; }, 3000); } } }, 500); } catch (error) { console.error('Cannot access iframe content:', error); self.innerHTML = '❌ ข้อผิดพลาด'; self.style.cursor = 'pointer'; setTimeout(function() { self.innerHTML = currentState.html; self.style.cursor = currentState.cursor; document.body.removeChild(iframe); // 🔥 FIX: Re-enable button on error self.dataset.loading = 'false'; }, 2000); } }); // Timeout if iframe takes too long setTimeout(function() { if (iframe.parentNode) { document.body.removeChild(iframe); if (self.innerHTML === '⏳ รอลิงค์ดาวน์โหลด...') { self.innerHTML = '⏰ หมดเวลารอ'; self.style.cursor = 'pointer'; setTimeout(function() { self.innerHTML = currentState.html; self.style.cursor = currentState.cursor; // 🔥 FIX: Re-enable button on timeout self.dataset.loading = 'false'; }, 2000); } } }, 30000); } catch (error) { console.log('Iframe loading issue:', error.message); this.innerHTML = '❌ ข้อผิดพลาด'; this.style.cursor = 'pointer'; setTimeout(() => { this.innerHTML = currentState.html; this.style.cursor = currentState.cursor; this.dataset.loading = 'false'; }, 2000); } } else { this.innerHTML = '❌ ไม่พบ bbDlBtn'; this.style.cursor = 'pointer'; setTimeout(() => { this.innerHTML = currentState.html; this.style.cursor = currentState.cursor; this.dataset.loading = 'false'; }, 2000); } }.bind(this), onerror: function(error) { console.error(`Request failed for ${fullDownloadUrl}:`, error); this.innerHTML = '❌ เครือข่ายผิดพลาด'; this.style.cursor = 'pointer'; setTimeout(() => { this.innerHTML = currentState.html; this.style.cursor = currentState.cursor; this.dataset.loading = 'false'; }, 2000); }.bind(this), ontimeout: function() { console.warn(`Request timeout for ${fullDownloadUrl}`); this.innerHTML = '⏰ เวลาหมด'; this.style.cursor = 'pointer'; setTimeout(() => { this.innerHTML = currentState.html; this.style.cursor = currentState.cursor; this.dataset.loading = 'false'; }, 2000); }.bind(this) }); } else { this.innerHTML = '❌ ไม่พบลิงก์ดาวน์โหลด'; this.style.cursor = 'pointer'; setTimeout(() => { this.innerHTML = currentState.html; this.style.cursor = currentState.cursor; this.dataset.loading = 'false'; }, 2000); } }.bind(this), onerror: function(error) { console.error(`Request failed for ${storedUrl}:`, error); this.innerHTML = '❌ เครือข่ายผิดพลาด'; this.style.cursor = 'pointer'; setTimeout(() => { this.innerHTML = currentState.html; this.style.cursor = currentState.cursor; this.dataset.loading = 'false'; }, 2000); }.bind(this), ontimeout: function() { console.warn(`Request timeout for ${storedUrl}`); this.innerHTML = '⏰ เวลาหมด'; this.style.cursor = 'pointer'; setTimeout(() => { this.innerHTML = currentState.html; this.style.cursor = currentState.cursor; this.dataset.loading = 'false'; }, 2000); }.bind(this) }); }; btnDownload.addEventListener('click', btnDownload.clickHandler); }); } function autoThank() { let attempts = 0; const maxAttempts = 5; function tryThank() { if (attempts++ >= maxAttempts) return; if (window.location.pathname.includes('details.php')) { const sayThanksTd = document.getElementById('saythanks'); if (sayThanksTd) { const img = sayThanksTd.querySelector('img[src*="pic/thanks/th"]'); if (img) { const anchor = sayThanksTd.querySelector('a'); if (anchor) { anchor.click(); } } else { setTimeout(autoThank, 500); } } } } tryThank(); } function setupHoverDetection() { if(!settings.PREVIEW){ return; } const allDetailsLinks = document.querySelectorAll('a[href^="details.php"]'); allDetailsLinks.forEach(link => { let currentRow = link.closest('tr'); const camsImg = currentRow.querySelector('img[src="pic/cams.gif "]')?.closest('a'); const fileArea = currentRow.querySelector('.hover-area'); if (!fileArea) return; // Remove old listeners to avoid duplicates fileArea.removeEventListener('mouseenter', handleMouseEnter); fileArea.removeEventListener('mouseleave', handleMouseLeave); // Store the image URL with the element if (camsImg) { fileArea.dataset.previewUrl = camsImg.getAttribute('href'); } // Add new listeners fileArea.addEventListener('mouseenter', handleMouseEnter); fileArea.addEventListener('mouseleave', handleMouseLeave); }); } function handleMouseEnter(event) { const fileArea = event.currentTarget; const imageUrl = fileArea.dataset.previewUrl; if (imageUrl && !isHovering) { // Clear any pending hide timeout if (hideTimeout) { clearTimeout(hideTimeout); hideTimeout = null; } isHovering = true; currentLink = fileArea; showPreview(imageUrl); fileArea.style.backgroundColor = '#f0f0f0'; } } function handleMouseLeave(event) { const fileArea = event.currentTarget; // Delay hiding to prevent flickering hideTimeout = setTimeout(() => { fileArea.style.backgroundColor = ''; hidePreview(); isHovering = false; currentLink = null; }, 30); } function createPreviewContainer() { const container = document.createElement('div'); container.style.position = 'fixed'; container.style.zIndex = '999999'; container.style.backgroundColor = 'rgba(0,0,0,0.8)'; container.style.borderRadius = '12px'; container.style.boxShadow = '0 8px 24px rgba(0,0,0,0.4)'; container.style.padding = '3px'; container.style.display = 'none'; container.style.pointerEvents = 'none'; container.style.border = "1px solid #fff"; container.style.maxWidth = '70vh'; container.style.maxHeight = '90vh'; container.style.height = '85vh'; const img = document.createElement('img'); img.style.width = '100%'; img.style.height = '100%'; img.style.objectFit = 'contain'; img.style.display = 'block'; img.style.borderRadius = '12px'; container.appendChild(img); document.body.appendChild(container); return container; } function centerPreview(container) { if (!container) return; container.style.left = '50%'; container.style.top = '50%'; container.style.transform = 'translate(-50%, -50%)'; } function showPreview(imageUrl) { if (!previewContainer) { previewContainer = createPreviewContainer(); } const img = previewContainer.querySelector('img'); img.src = ''; img.alt = 'Loading...'; img.style.opacity = '0.5'; previewContainer.style.display = 'flex'; previewContainer.style.alignItems = 'center'; previewContainer.style.justifyContent = 'center'; centerPreview(previewContainer); const testImg = new Image(); testImg.onload = function() { img.src = imageUrl; img.style.opacity = '1'; }; testImg.onerror = function() { img.alt = 'Failed to load image'; img.style.opacity = '1'; img.src = noimage; }; testImg.src = imageUrl; img.src = imageUrl; } function hidePreview() { if (previewContainer && previewContainer.style.display !== 'none') { previewContainer.style.display = 'none'; const img = previewContainer.querySelector('img'); if (img) { img.src = ''; img.style.opacity = '1'; } } } // Clean up on page unload window.addEventListener('beforeunload', () => { if (previewContainer) { previewContainer.remove(); previewContainer = null; } }); window.addEventListener('resize', function() { if (previewContainer && previewContainer.style.display !== 'none') { centerPreview(previewContainer); } }); // Handle clicks that might interfere document.addEventListener('click', function(event) { const toggleBtn = event.target.closest('#toggle-posters-btn'); if (toggleBtn) { setTimeout(() => { hidePreview(); isHovering = false; if (currentLink) { currentLink.style.backgroundColor = ''; currentLink = null; } }, 50); } }); function init() { hideHotTorrentSection() filters(); actionsButton(); hideColumns(); hideDescription(); autoThank(); createSettingsButton(); setupHoverDetection(); } if (document.readyState === 'loading') { document.addEventListener('DOMContentLoaded', init); } else { init(); } })();