// ==UserScript== // @name 哈粉宾馆房间直达 // @namespace http://tampermonkey.net/ // @version 1.0 // @description 输入房间数字ID直达房间 // @author Redbell // @match https://hafen.icu/h5 // @grant none // @run-at document-end // @license MIT // ==/UserScript== (function() { 'use strict'; const IFRAME_SRC_PATTERN = '/nitro-client/nitro-cool/index.html'; const TOOL_NAME = '宾馆房间直达'; const SUBMIT_TEXT = 'GO!'; const INPUT_PLACEHOLDER = '输入数字ID'; // ---------- 等待元素出现 ---------- function waitForElement(selector, timeout = 10000) { return new Promise((resolve) => { if (document.querySelector(selector)) { resolve(document.querySelector(selector)); return; } const observer = new MutationObserver(() => { const el = document.querySelector(selector); if (el) { observer.disconnect(); resolve(el); } }); observer.observe(document.body, { childList: true, subtree: true }); setTimeout(() => { observer.disconnect(); resolve(null); }, timeout); }); } // ---------- 主逻辑 ---------- async function init() { const buttonGroup = await waitForElement('.client-buttons'); if (!buttonGroup) { console.warn('[房间入口] 未找到 .client-buttons,放弃加载'); return; } // ---------- 折叠按钮(指南针图标,保留原样) ---------- const toggleBtn = document.createElement('button'); toggleBtn.className = 'btn btn-warning'; toggleBtn.id = 'room-toggle-btn'; toggleBtn.innerHTML = ''; toggleBtn.title = '宾馆房间直达'; buttonGroup.appendChild(toggleBtn); // ---------- 卡片(展开状态) ---------- const card = document.createElement('div'); card.id = 'room-card'; card.style.cssText = ` display: none; position: absolute; top: calc(100% + 8px); left: 0; right: auto; z-index: 9999; background: #ededed; border-radius: 0.75rem; border: 2px solid #454566; box-shadow: 0 0 0 1pt #fff, 0 4px 12px rgba(0,0,0,0.15); min-width: 180px; max-width: 240px; overflow: hidden; font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif; `; // 标题栏 const cardHeader = document.createElement('div'); cardHeader.style.cssText = ` display: flex; align-items: center; justify-content: space-between; background: #454566; color: #fff; height: 34px; min-height: 34px; line-height: 34px; padding: 0 14px; border-bottom: 2px solid #454566; `; const titleSpan = document.createElement('span'); titleSpan.textContent = '🧭 ' + TOOL_NAME; titleSpan.style.cssText = ` color: #fff; text-shadow: 0 4px 4px rgba(0,0,0,0.25); font-size: 1.1rem; font-weight: 500; `; // ---------- 卡片右上角关闭按钮 ---------- const closeBtn = document.createElement('div'); closeBtn.textContent = '✕'; closeBtn.style.cssText = ` width: 28px; height: 26px; background: #d32f2f; border: 1px solid #222; border-radius: 6px; color: #fff; font-size: 18px; font-weight: bold; display: inline-flex; align-items: center; justify-content: center; cursor: pointer; transition: background 0.2s, transform 0.1s; padding: 0; line-height: 1; text-align: center; box-sizing: border-box; `; closeBtn.addEventListener('mouseenter', () => { closeBtn.style.background = '#b71c1c'; }); closeBtn.addEventListener('mouseleave', () => { closeBtn.style.background = '#d32f2f'; }); closeBtn.addEventListener('mousedown', () => { closeBtn.style.transform = 'scale(0.92)'; }); closeBtn.addEventListener('mouseup', () => { closeBtn.style.transform = 'scale(1)'; }); closeBtn.addEventListener('click', (e) => { e.stopPropagation(); card.style.display = 'none'; }); cardHeader.appendChild(titleSpan); cardHeader.appendChild(closeBtn); card.appendChild(cardHeader); // 内容 const content = document.createElement('div'); content.style.cssText = ` padding: 14px 16px 16px 16px; display: flex; flex-direction: column; gap: 10px; background: #ededed; `; const row = document.createElement('div'); row.style.cssText = `display: flex; gap: 8px; align-items: center;`; const input = document.createElement('input'); input.type = 'number'; input.step = '1'; input.placeholder = INPUT_PLACEHOLDER; input.style.cssText = ` flex: 1; min-width: 60px; padding: 6px 10px; font-size: 14px; font-family: inherit; background: #fff; color: #1a1a1a; border: 1px solid #ccc; border-radius: 4px; outline: none; transition: border-color 0.2s, box-shadow 0.2s; `; input.addEventListener('focus', () => { input.style.borderColor = '#454566'; input.style.boxShadow = '0 0 0 2px rgba(69,69,102,0.2)'; }); input.addEventListener('blur', () => { input.style.borderColor = '#ccc'; input.style.boxShadow = 'none'; }); const goLink = document.createElement('a'); goLink.textContent = SUBMIT_TEXT; goLink.href = '#'; goLink.style.cssText = ` padding: 6px 16px; background: #454566; color: #fff; border: 1px solid #454566; border-radius: 4px; font-size: 14px; font-weight: 500; cursor: pointer; white-space: nowrap; text-decoration: none; display: inline-block; text-align: center; transition: background 0.2s, opacity 0.2s, box-shadow 0.2s; opacity: 0.7; pointer-events: none; box-shadow: 0 2px 4px rgba(0,0,0,0.1); `; goLink.addEventListener('mouseenter', () => { if (goLink.style.pointerEvents !== 'none') { goLink.style.background = '#3a3a5a'; goLink.style.boxShadow = '0 4px 8px rgba(0,0,0,0.15)'; } }); goLink.addEventListener('mouseleave', () => { if (goLink.style.pointerEvents !== 'none') { goLink.style.background = '#454566'; goLink.style.boxShadow = '0 2px 4px rgba(0,0,0,0.1)'; } }); goLink.addEventListener('mousedown', () => { if (goLink.style.pointerEvents !== 'none') { goLink.style.transform = 'scale(0.96)'; } }); goLink.addEventListener('mouseup', () => { if (goLink.style.pointerEvents !== 'none') { goLink.style.transform = 'scale(1)'; } }); function updateLink(value) { const trimmed = value.trim(); if (trimmed === '') { goLink.href = '#'; goLink.style.opacity = '0.7'; goLink.style.pointerEvents = 'none'; goLink.style.cursor = 'default'; goLink.title = '请输入数字'; } else if (/^-?\d+$/.test(trimmed)) { goLink.href = 'event:navigator/goto/' + trimmed; goLink.style.opacity = '1'; goLink.style.pointerEvents = 'auto'; goLink.style.cursor = 'pointer'; goLink.title = '点击直达房间 ' + trimmed; } else { goLink.href = '#'; goLink.style.opacity = '0.7'; goLink.style.pointerEvents = 'none'; goLink.style.cursor = 'default'; goLink.title = '请输入有效数字'; } } input.addEventListener('input', function() { updateLink(this.value); }); updateLink(''); // GO 点击:在 iframe 内触发跳转,跳转后不关闭卡片 goLink.addEventListener('click', function(e) { if (this.href === '#') { e.preventDefault(); return; } const iframe = document.querySelector('iframe[src*="' + IFRAME_SRC_PATTERN + '"]'); if (!iframe) { console.warn('[房间入口] 未找到目标iframe'); alert('未找到房间iframe,请刷新页面重试'); return; } try { const iframeDoc = iframe.contentDocument || iframe.contentWindow.document; if (!iframeDoc) { throw new Error('无法访问iframe文档'); } const link = iframeDoc.createElement('a'); link.href = this.href; link.style.display = 'none'; iframeDoc.body.appendChild(link); link.click(); setTimeout(() => { if (link.parentNode) link.parentNode.removeChild(link); }, 500); console.log('[房间入口] 已在iframe内触发跳转:', this.href); input.value = ''; // 可选,跳转后清空输入框 updateLink(''); // 跳转后不关闭卡片,可继续输入 } catch (e) { console.error('[房间入口] 跳转失败:', e); alert('跳转失败,可能是跨域问题,请尝试刷新'); } }); row.appendChild(input); row.appendChild(goLink); content.appendChild(row); const hint = document.createElement('div'); hint.textContent = '✈️输入房间数字ID,一键直达!'; hint.style.cssText = ` font-size: 12px; color: #666; text-align: center; margin-top: 2px; letter-spacing: 0.2px; `; content.appendChild(hint); card.appendChild(content); buttonGroup.style.position = 'relative'; buttonGroup.appendChild(card); // 切换显示 toggleBtn.addEventListener('click', function(e) { e.stopPropagation(); const isVisible = card.style.display !== 'none'; card.style.display = isVisible ? 'none' : 'block'; if (!isVisible) { setTimeout(() => input.focus(), 100); } }); // 点击外部关闭 document.addEventListener('click', function(e) { if (card.style.display === 'block') { if (!card.contains(e.target) && e.target !== toggleBtn && !toggleBtn.contains(e.target)) { card.style.display = 'none'; } } }); // 回车支持 input.addEventListener('keydown', function(e) { if (e.key === 'Enter') { e.preventDefault(); if (goLink.style.pointerEvents !== 'none' && goLink.href && goLink.href !== '#') { goLink.click(); } else { input.style.borderColor = '#e74c3c'; input.style.boxShadow = '0 0 0 2px rgba(231,76,60,0.2)'; setTimeout(() => { input.style.borderColor = '#ccc'; input.style.boxShadow = 'none'; }, 600); } } }); console.log('%c🧭 房间直达引擎已启动', 'font-size:14px;font-weight:600;color:#454566;'); } if (document.readyState === 'complete') { init(); } else { window.addEventListener('load', init); } })();