// ==UserScript== // @name HTML Sniffer // @namespace http://gerald.top // @author Gerald // @icon http://cn.gravatar.com/avatar/a0ad718d86d21262ccd6ff271ece08a3?s=80 // @version 0.1.0 // @description A tool to sniff HTML tags and attributes. // @include * // @grant GM_addStyle // @grant GM_getValue // @grant GM_setValue // @downloadURL none // ==/UserScript== function prevent(e) { e.preventDefault(); } function capture(e) { prevent(e); if (sniffer.current) { sniffer.current.classList.remove(sniffer.CURRENT); } sniffer.current = e.target; sniffer.current.classList.add(sniffer.CURRENT); updateDOM(); } function onmouseover(e) { if (sniffer.hovered) { sniffer.hovered.classList.remove(sniffer.HOVERED); } sniffer.hovered = e.target; sniffer.hovered.classList.add(sniffer.HOVERED); } function safeHTML(html) { return html.replace(/[<&]/g, (m) => { return { '<' : '<', '&' : '&', }[m]; }); } function updateDOM() { var current = sniffer.current; var tagName = current.tagName.toLowerCase(); var tags = document.querySelectorAll(tagName); var arrayProto = Array.prototype; var index = arrayProto.indexOf.call(tags, current); dom.domName.textContent = tagName; dom.domRank.textContent = `${index}/${tags.length}`; dom.domAttrs.innerHTML = arrayProto.map.call(current.attributes, (attr) => { return `
  • ${safeHTML(attr.name)} = ${safeHTML(attr.value)}
  • `; }).join(''); } function init() { initFrame(); document.addEventListener('mousedown', capture, true); document.addEventListener('click', prevent, true); document.addEventListener('mouseover', onmouseover, false); locate(GM_getValue('hs-location')); } function locate(pos) { pos = pos || {}; if (pos.left == null) pos.left = 'auto'; else if (!isNaN(pos.left)) pos.left += 'px'; if (pos.right == null) { pos.right = pos.left == 'auto' ? 0 : 'auto'; } else if (!isNaN(pos.right)) pos.right += 'px'; if (pos.bottom == null) pos.bottom = 'auto'; else if (!isNaN(pos.bottom)) pos.bottom += 'px'; if (pos.top == null) { pos.top = pos.bottom == 'auto' ? 0 : 'auto'; } else if (!isNaN(pos.top)) pos.top += 'px'; var frame = dom.frame; frame.style.top = pos.top; frame.style.left = pos.left; frame.style.right = pos.right; frame.style.bottom = pos.bottom; } function initFrame() { var frame = dom.frame = document.createElement('iframe'); frame.id='hsniffer'; document.body.appendChild(frame); var doc = frame.contentDocument; var style = doc.createElement('style'); style.innerHTML = ` body { min-height: 100%; padding: 1em; background: wheat; cursor: move; } * { margin: 0; padding: 0; box-sizing: border-box; } .dom { cursor: text; } ul { padding: 20px; } .dom-attr-key { color: dodgerblue; } .dom-attr-val { color: green; } `; doc.head.appendChild(style); doc.body.innerHTML = `

    HTML Sniffer

    Current DOM:
    Rank:
    `; dom.dom = doc.body.querySelector('.dom'); dom.domName = doc.body.querySelector('.dom-name'); dom.domRank = doc.body.querySelector('.dom-rank'); dom.domAttrs = doc.body.querySelector('.dom-attrs'); doc.addEventListener('mousedown', onMoveStart, false); var moving; function onMoveStart(e) { if (dom.dom.contains(e.target)) return; e.preventDefault(); if (moving) return; moving = { x: e.clientX, y: e.clientY, }; doc.addEventListener('mousemove', onMoving, false); doc.addEventListener('mouseup', onMoveEnd, false); } function onMoving(e) { var rect = frame.getBoundingClientRect(); locate({ left: rect.left + e.clientX - moving.x, top: rect.top + e.clientY - moving.y, }); } function onMoveEnd(e) { var rect = frame.getBoundingClientRect(); var pos = {}; var right = document.body.clientWidth - rect.right; if (rect.left > right) pos.right = right; else pos.left = rect.left; var bottom = document.body.clientHeight - rect.bottom; if (rect.top > bottom) pos.bottom = bottom; else pos.top = rect.top; locate(pos); GM_setValue('hs-location', pos); moving = null; doc.removeEventListener('mousemove', onMoving, false); doc.removeEventListener('mouseup', onMoveEnd, false); } } GM_addStyle(` #hsniffer { position: fixed; width: 300px; height: 300px; border: 2px ridge gray; z-index: 10000; } *:not(#hsniffer).hsniffer-highlight { background: rgba(0,128,255,.3) !important; outline: 2px solid orange !important; } .hsniffer-current { background: rgba(0,128,255,.5) !important; outline: 2px solid red !important; } `); var dom = {}; var sniffer = { HOVERED: 'hsniffer-highlight', CURRENT: 'hsniffer-current', }; init();