// ==UserScript== // @name Nijiura CyTube Popup Player (PointerResize) // @namespace http://tampermonkey.net/ // @version 0.7 // @description CyTubeポップアップをドラッグ移動+右下ハンドルでリサイズ // @match https://nijiurachan.net/pc/thread.php* // @run-at document-end // @grant none // ==/UserScript== (function() { 'use strict'; const processed = new WeakSet(); let popup, popupFrame, popupHeader, resizeHandle; function createPopup() { if (popup) return; popup = document.createElement('div'); popup.style.position = 'fixed'; popup.style.top = '10%'; popup.style.left = '50%'; popup.style.transform = 'translateX(-50%)'; popup.style.width = '900px'; popup.style.height = '520px'; popup.style.background = '#000'; popup.style.border = '1px solid #666'; popup.style.zIndex = '99999'; popup.style.display = 'none'; popup.style.boxShadow = '0 4px 16px rgba(0,0,0,0.6)'; popup.style.overflow = 'hidden'; // ==== ヘッダー(ドラッグ移動) ==== popupHeader = document.createElement('div'); popupHeader.textContent = 'CyTube Player'; popupHeader.style.height = '30px'; popupHeader.style.lineHeight = '30px'; popupHeader.style.padding = '0 8px'; popupHeader.style.background = '#333'; popupHeader.style.color = '#fff'; popupHeader.style.cursor = 'move'; popupHeader.style.userSelect = 'none'; popup.appendChild(popupHeader); // 閉じるボタン const closeBtn = document.createElement('button'); closeBtn.textContent = '✖'; closeBtn.style.float = 'right'; closeBtn.style.marginTop = '4px'; closeBtn.style.marginRight= '4px'; closeBtn.style.cursor = 'pointer'; closeBtn.addEventListener('click', () => { popup.style.display = 'none'; popupFrame.src = 'about:blank'; }); popupHeader.appendChild(closeBtn); // ==== iframe本体 ==== popupFrame = document.createElement('iframe'); popupFrame.style.width = '100%'; popupFrame.style.height = 'calc(100% - 30px)'; popupFrame.style.border = 'none'; popupFrame.allowFullscreen = true; popup.appendChild(popupFrame); // ==== 右下リサイズハンドル ==== resizeHandle = document.createElement('div'); resizeHandle.style.position = 'absolute'; resizeHandle.style.right = '0'; resizeHandle.style.bottom = '0'; resizeHandle.style.width = '18px'; resizeHandle.style.height = '18px'; resizeHandle.style.cursor = 'nwse-resize'; resizeHandle.style.background = 'rgba(255,255,255,0.2)'; resizeHandle.style.borderTop = '1px solid #888'; resizeHandle.style.borderLeft = '1px solid #888'; popup.appendChild(resizeHandle); document.body.appendChild(popup); // ==== ドラッグ移動 ==== let dragging = false; let offsetX = 0, offsetY = 0; popupHeader.addEventListener('mousedown', (e) => { dragging = true; const rect = popup.getBoundingClientRect(); offsetX = e.clientX - rect.left; offsetY = e.clientY - rect.top; document.body.style.userSelect = 'none'; }); document.addEventListener('mousemove', (e) => { if (!dragging) return; popup.style.top = (e.clientY - offsetY) + 'px'; popup.style.left = (e.clientX - offsetX) + 'px'; popup.style.transform = ''; // 1回動かしたら中央基準解除 }); document.addEventListener('mouseup', () => { dragging = false; document.body.style.userSelect = ''; }); // ==== pointer キャプチャ付きリサイズ ==== let resizing = false; let startW, startH, startX, startY, pointerId; resizeHandle.addEventListener('pointerdown', (e) => { resizing = true; pointerId = e.pointerId; const rect = popup.getBoundingClientRect(); startW = rect.width; startH = rect.height; startX = e.clientX; startY = e.clientY; resizeHandle.setPointerCapture(pointerId); e.preventDefault(); }); resizeHandle.addEventListener('pointermove', (e) => { if (!resizing) return; const diffX = e.clientX - startX; const diffY = e.clientY - startY; const newW = Math.max(400, startW + diffX); const newH = Math.max(300, startH + diffY); popup.style.width = newW + 'px'; popup.style.height = newH + 'px'; e.preventDefault(); }); resizeHandle.addEventListener('pointerup', (e) => { if (!resizing) return; resizing = false; try { resizeHandle.releasePointerCapture(pointerId); } catch(_) {} }); resizeHandle.addEventListener('pointercancel', () => { resizing = false; }); } function openPopup(url) { createPopup(); popupFrame.src = url; popup.style.display = 'block'; } function setupForLink(a) { if (processed.has(a)) return; if (!a.href) return; if (!a.href.includes('cytube.mm428.net/r/')) return; processed.add(a); const btn = document.createElement('button'); btn.textContent = '▶ CyTubeポップアップ'; btn.style.marginLeft = '8px'; btn.style.cursor = 'pointer'; btn.style.fontSize = '0.8em'; btn.addEventListener('click', (e) => { e.preventDefault(); e.stopPropagation(); openPopup(a.href); }); a.insertAdjacentElement('afterend', btn); } function scan() { const links = document.querySelectorAll("a[href*='cytube.mm428.net/r/']"); links.forEach(setupForLink); } scan(); const observer = new MutationObserver(scan); observer.observe(document.body, { childList: true, subtree: true }); })();