// ==UserScript==
// @name Mobile Element Selector
// @author ZNJXL
// @version 1.1.3
// @namespace http://tampermonkey.net/
// @description 모바일 요소 선택기
// @description:en Mobile Element Selector (especially for cromium browsers)
// @match *://*/*
// @license MIT
// @grant GM_setClipboard
// ==/UserScript==
(function() {
'use strict';
let selecting = false;
let selectedEl = null;
let initialTouchedElement = null;
let includeSiteName = true;
let touchStartX = 0, touchStartY = 0;
let touchMoved = false;
const moveThreshold = 10;
// 다국어 텍스트 정의
const i18n = {
ko: {
blockerInfo: "선택된 요소:",
blockerCopy: "복사",
blockerToggleSite: (siteNameOn) => `사이트명: ${siteNameOn ? "ON" : "OFF"}`,
blockerBlock: "미리보기",
blockerCancel: "취소",
blockerMode: "차단 모드",
blockerSelecting: "선택 중...",
blockerPreview: "미리보기",
blockerRevert: "되돌리기",
alertNoElement: "선택된 요소가 없습니다.",
alertCopySuccess: "✅ 선택자가 복사되었습니다!",
alertCopyFail: "❌ 클립보드 복사에 실패했습니다.",
alertDirectCopy: "선택자를 직접 복사하세요:",
},
en: {
blockerInfo: "Selected Element:",
blockerCopy: "Copy",
blockerToggleSite: (siteNameOn) => `Site Name: ${siteNameOn ? "ON" : "OFF"}`,
blockerBlock: "Preview",
blockerCancel: "Cancel",
blockerMode: "Block Mode",
blockerSelecting: "Selecting...",
blockerPreview: "Preview",
blockerRevert: "Revert",
alertNoElement: "No element selected.",
alertCopySuccess: "✅ Selector copied!",
alertCopyFail: "❌ Failed to copy to clipboard.",
alertDirectCopy: "Copy the selector manually:"
}
};
// 언어 감지 및 선택 (기본값은 한국어)
const lang = navigator.language.startsWith('ko') ? 'ko' : 'en';
const t = i18n[lang];
const style = document.createElement('style');
style.textContent = `
.mobile-block-ui { /* 스타일 내용은 동일 */ }
#blocker-slider { /* 스타일 내용은 동일 */ }
#blocker-slider::-webkit-slider-thumb { /* 스타일 내용은 동일 */ }
#blocker-slider::-moz-range-thumb { /* 스타일 내용은 동일 */ }
.selected-element { /* 스타일 내용은 동일 */ }
#mobile-block-panel { /* 스타일 내용은 동일 */ }
#mobile-block-toggleBtn { /* 스타일 내용은 동일 */ }
.mb-btn { /* 스타일 내용은 동일 */ }
#blocker-info-wrapper { /* 스타일 내용은 동일 */ }
#blocker-info { /* 스타일 내용은 동일 */ }
`;
document.head.appendChild(style);
const panel = document.createElement('div');
panel.id = 'mobile-block-panel';
panel.classList.add('mobile-block-ui', 'ui-ignore');
panel.innerHTML = `
<div id="blocker-info-wrapper">
<span style="font-size: 12px; color: #ccc;">${t.blockerInfo}</span>
<span id="blocker-info">없음</span>
</div>
<input type="range" id="blocker-slider" min="0" max="10" value="0" class="ui-ignore">
<div class="button-grid">
<button id="blocker-copy" class="mb-btn ui-ignore">${t.blockerCopy}</button>
<button id="blocker-toggle-site" class="mb-btn ui-ignore">${t.blockerToggleSite(includeSiteName)}</button>
<button id="blocker-block" class="mb-btn ui-ignore">${t.blockerBlock}</button>
<button id="blocker-cancel" class="mb-btn ui-ignore">${t.blockerCancel}</button>
</div>
`;
document.body.appendChild(panel);
const toggleBtn = document.createElement('button');
toggleBtn.id = 'mobile-block-toggleBtn';
toggleBtn.classList.add('mobile-block-ui', 'ui-ignore');
toggleBtn.textContent = t.blockerMode;
document.body.appendChild(toggleBtn);
function setBlockMode(enabled) {
selecting = enabled;
toggleBtn.textContent = enabled ? t.blockerSelecting : t.blockerMode;
toggleBtn.classList.toggle('selecting', enabled);
panel.style.display = enabled ? 'block' : 'none';
if (!enabled && selectedEl) {
selectedEl.classList.remove('selected-element');
selectedEl = null;
initialTouchedElement = null;
}
panel.querySelector('#blocker-slider').value = 0;
updateInfo();
}
function updateInfo() {
const infoSpan = panel.querySelector('#blocker-info');
infoSpan.textContent = selectedEl ? generateSelector(selectedEl) : '없음';
}
function generateSelector(el) { /* generateSelector 함수는 그대로 유지 */ }
const uiExcludeClass = '.ui-ignore';
document.addEventListener('touchstart', e => {
if (!selecting || e.target.closest(uiExcludeClass)) return;
const touch = e.touches[0];
touchStartX = touch.clientX; touchStartY = touch.clientY; touchMoved = false;
}, { passive: true });
document.addEventListener('touchmove', e => {
if (!selecting || e.target.closest(uiExcludeClass) || !e.touches[0]) return;
if (!touchMoved) {
const touch = e.touches[0];
const dx = touch.clientX - touchStartX, dy = touch.clientY - touchStartY;
if (Math.sqrt(dx * dx + dy * dy) > moveThreshold) touchMoved = true;
}
}, { passive: true });
document.addEventListener('touchend', e => {
if (!selecting || e.target.closest(uiExcludeClass)) return;
if (touchMoved) { touchMoved = false; return; }
e.preventDefault(); e.stopImmediatePropagation();
const touch = e.changedTouches[0];
const targetEl = document.elementFromPoint(touch.clientX, touch.clientY);
if (!targetEl || targetEl.closest(uiExcludeClass)) return;
if (selectedEl) selectedEl.classList.remove('selected-element');
selectedEl = targetEl;
initialTouchedElement = targetEl;
selectedEl.classList.add('selected-element');
panel.querySelector('#blocker-slider').value = 0;
updateInfo();
}, { capture: true, passive: false });
const slider = panel.querySelector('#blocker-slider');
slider.addEventListener('input', handleSlider);
function handleSlider(e) { /* handleSlider 함수는 그대로 유지 */ }
panel.querySelector('#blocker-copy').addEventListener('click', () => {
if (selectedEl) {
const fullSelector = generateSelector(selectedEl);
let finalSelector = "##" + fullSelector;
if (includeSiteName) finalSelector = location.hostname + finalSelector;
try {
GM_setClipboard(finalSelector);
alert(t.alertCopySuccess + '\n' + finalSelector);
} catch (err) {
console.error("클립보드 복사 실패:", err);
alert(t.alertCopyFail);
prompt(t.alertDirectCopy, finalSelector);
}
} else { alert(t.alertNoElement); }
});
panel.querySelector('#blocker-toggle-site').addEventListener('click', () => {
includeSiteName = !includeSiteName;
panel.querySelector('#blocker-toggle-site').textContent = t.blockerToggleSite(includeSiteName);
});
const blockBtn = panel.querySelector('#blocker-block');
let isHidden = false;
blockBtn.textContent = t.blockerPreview;
blockBtn.addEventListener('click', () => {
if (!selectedEl) {
alert(t.alertNoElement);
return;
}
if (!isHidden) {
selectedEl.dataset._original_display = selectedEl.style.display || '';
selectedEl.style.display = 'none';
blockBtn.textContent = t.blockerRevert;
isHidden = true;
} else {
selectedEl.style.display = selectedEl.dataset._original_display || '';
blockBtn.textContent = t.blockerPreview;
isHidden = false;
}
});
panel.querySelector('#blocker-cancel').addEventListener('click', () => setBlockMode(false));
toggleBtn.addEventListener('click', () => setBlockMode(!selecting));
function makeDraggable(el) { /* makeDraggable 함수는 그대로 유지 */ }
makeDraggable(panel);
makeDraggable(toggleBtn);
})();