NOTICE: By continued use of this site you understand and agree to the binding Terms of Service and Privacy Policy.
// ==UserScript== // @name FlexibleHighlight // @namespace DScript // @version 0.1.6 // @description Ctrl+Shift+Click to draw highlight rectangle. Ctrl+Right click to highlight element. Escape to close highlight. Right click on overlay to show settings. // @author DScript // @match https://*/* // @match http://*/* // @grant GM_setValue // @grant GM_getValue // @updateURL https://openuserjs.org/meta/DScript/FlexibleHighlight.meta.js // ==/UserScript== (function() { 'use strict'; var highlight = { visible: false, preventContext: false, overlay: null, settings: null, target: null, padding: GM_getValue('highlight.padding', 6), toggle: function(on) { highlight.visible = on; if(!highlight.overlay) return; highlight.overlay.left.style.display = highlight.overlay.right.style.display = highlight.overlay.top.style.display = highlight.overlay.bottom.style.display = (on ? 'block' : 'none'); if(!on) { highlight.rectStartX = -1; highlight.rectStartY = -1; highlight.isDrawingRect = false; highlight.target = null; document.body.classList.remove('noselect'); } else { document.body.classList.add('noselect'); } }, createOverlay: function() { var div = document.createElement('div'); div.style.backgroundColor = '#000'; div.style.opacity = GM_getValue('highlight.opacity', 0.4); div.style.position = 'absolute'; div.style.zIndex = GM_getValue('highlight.zIndex', 10000); div.style.display = 'none'; div.oncontextmenu = function(e) { console.log(e); if(e.button == 2) { highlight.showSettings(); e.preventDefault(); } }; document.body.appendChild(div); return div; }, build: function() { if(!!highlight.overlay) return; highlight.overlay = { left: highlight.createOverlay(), right: highlight.createOverlay(), top: highlight.createOverlay(), bottom: highlight.createOverlay() }; highlight.overlay.left.style.left = 0; highlight.overlay.left.style.top = 0; highlight.overlay.left.style.height = document.body.clientHeight + 'px'; highlight.overlay.right.style.top = 0; highlight.overlay.right.style.height = document.body.clientHeight + 'px'; highlight.overlay.top.style.top = 0; var style = document.createElement('style'); document.head.appendChild(style); style.sheet.insertRule('.noselect { -webkit-touch-callout: none; -webkit-user-select: none; user-select: none; }', 0); var settings = document.createElement('div'); settings.style.backgroundColor = '#fff'; settings.style.border = '1px solid #999'; settings.style.borderRadius = '4px'; settings.style.position = 'absolute'; settings.style.zIndex = GM_getValue('highlight.zIndex', 10000) + 2; settings.style.display = 'none'; settings.style.padding = '20px'; settings.style.width = '300px'; settings.style.left = ((document.body.clientWidth - 320) / 2) + 'px'; settings.style.top = '50px'; settings.innerHTML = '<div style="margin-bottom: 3px; font-size: 1.5em;"><b>FlexibleHighlight settings</b></div>'; settings.innerHTML += '<div>Background opacity</div><div><input type="number" style="width: 100%;" max="1" min="0" step="0.1" class="highlight-opacity" value="' + GM_getValue('highlight.opacity', 0.4) + '"/></div>'; settings.innerHTML += '<div>Padding around element (px)</div><div><input type="number" style="width: 100%;" max="1" min="30" step="1" class="highlight-padding" value="' + GM_getValue('highlight.padding', 6) + '"/></div>'; settings.innerHTML += '<div>Background z-index</div><div><input type="number" style="width: 100%;" step="1" class="highlight-z" value="' + GM_getValue('highlight.zIndex', 10000) + '"/></div>'; var buttons = document.createElement('div'); settings.appendChild(buttons); var saveSettings = document.createElement('button'); saveSettings.innerText = 'Save'; saveSettings.type = 'button'; saveSettings.onclick = highlight.saveSettings; buttons.appendChild(saveSettings); var cancelSettings = document.createElement('button'); cancelSettings.innerText = 'Cancel'; cancelSettings.type = 'button'; cancelSettings.onclick = highlight.cancelSettings; buttons.appendChild(cancelSettings); highlight.settings = settings; document.body.appendChild(settings); }, showSettings: function() { highlight.settings.style.display = 'block'; highlight.settings.style.top = (50 + document.documentElement.scrollTop) + 'px'; }, saveSettings: function() { var opacity = highlight.settings.querySelector('.highlight-opacity').value || 0; var padding = highlight.settings.querySelector('.highlight-padding').value || 0; var zIndex = highlight.settings.querySelector('.highlight-z').value || 0; GM_setValue('highlight.opacity', opacity); GM_setValue('highlight.padding', padding); GM_setValue('highlight.zIndex', zIndex); highlight.overlay.left.style.opacity = opacity; highlight.overlay.right.style.opacity = opacity; highlight.overlay.top.style.opacity = opacity; highlight.overlay.bottom.style.opacity = opacity; highlight.overlay.left.style.zIndex = zIndex; highlight.overlay.right.style.zIndex = zIndex; highlight.overlay.top.style.zIndex = zIndex; highlight.overlay.bottom.style.zIndex = zIndex; highlight.padding = padding; highlight.settings.style.display = 'none'; }, cancelSettings: function() { highlight.settings.querySelector('.highlight-opacity').value = GM_getValue('highlight.opactiy', 0.4); highlight.settings.querySelector('.highlight-padding').value = GM_getValue('highlight.padding', 6); highlight.settings.querySelector('.highlight-z').value = GM_getValue('highlight.zIndex', 10000); highlight.settings.style.display = 'none'; }, start: function(target) { var style = window.getComputedStyle(target); while(style && style.display == 'inline') { target = target.parentNode; style = window.getComputedStyle(target); } highlight.target = target; highlight.parents = -1; highlight.highlightElement(target); }, highlightElement: function(el) { var left = el.offsetLeft; var top = el.offsetTop; var p = el.offsetParent; while(!!p) { left += p.offsetLeft; top += p.offsetTop; p = p.offsetParent; } left = Math.max(0, left - highlight.padding); top = Math.max(0, top - highlight.padding); highlight.setRect(left, top, left + el.offsetWidth + (highlight.padding*2), top + el.offsetHeight + (highlight.padding*2)); }, parents: -1, cycleTarget: function (up) { var p = highlight.target; if(up) { highlight.parents++; for(var i = 0; i < highlight.parents; i++) p = p.parentNode; if (p == document.body) { highlight.toggle(false); } else if(p != null) { highlight.parents++; highlight.highlightElement(p); } } else { highlight.parents = Math.max(highlight.parents-1, -1); for(var i = 0; i < highlight.parents; i++) p = p.parentNode; highlight.highlightElement(p); } }, rectStartX: -1, rectStartY: -1, isDrawingRect: false, startRect: function(x1, y1) { highlight.isDrawingRect = true; highlight.rectStartX = x1; highlight.rectStartY = y1; highlight.target = null; highlight.setRect(x1, y1, x1, y1); }, updateRect: function(x2, y2) { highlight.setRect(highlight.rectStartX, highlight.rectStartY, x2, y2); }, stopRect: function(x2, y2) { highlight.isDrawingRect = false; highlight.setRect(highlight.rectStartX, highlight.rectStartY, x2, y2); highlight.rectStartX = -1; highlight.rectStartY = -1; }, setRect: function(x1, y1, x2, y2) { highlight.build(); if(x2 < x1) { var x3 = x1; x1 = x2; x2 = x3; } if(y2 < y1) { var y3 = y1; y1 = y2; y2 = y3; } highlight.overlay.left.style.width = x1 + 'px'; highlight.overlay.right.style.left = x2 + 'px'; highlight.overlay.right.style.width = (document.body.clientWidth - x2) + 'px'; highlight.overlay.top.style.left = x1 + 'px'; highlight.overlay.top.style.height = y1 + 'px'; highlight.overlay.top.style.width = (x2 - x1) + 'px'; highlight.overlay.bottom.style.top = y2 + 'px'; highlight.overlay.bottom.style.left = x1 + 'px'; highlight.overlay.bottom.style.width = (x2 - x1) + 'px'; highlight.overlay.bottom.style.height = (document.body.clientHeight - y2) + 'px'; highlight.toggle(true); } }; var getX = function(e) { return e.x + document.documentElement.scrollLeft; }; var getY = function(e) { return e.y + document.documentElement.scrollTop; }; document.body.onkeyup = function(e) { if(e.keyCode == 27) highlight.toggle(false); }; document.body.onclick = function(e) { if(highlight.isDrawingRect) { highlight.stopRect(getX(e), getY(e)); return; } if(!e.ctrlKey || !e.shiftKey) return; e.preventDefault(); highlight.startRect(getX(e), getY(e)); }; document.body.onmousemove = function(e) { if(!highlight.isDrawingRect) return; highlight.updateRect(getX(e), getY(e)); }; document.body.onmousedown = function(e) { if(e.button != 2 || !e.ctrlKey) return; highlight.preventContext = true; highlight.start(e.target); }; document.body.oncontextmenu = function(e) { if(highlight.preventContext) e.preventDefault(); highlight.preventContext = false; }; document.body.onwheel = function(e) { if(!highlight.target) return; highlight.cycleTarget(e.deltaY < 0); e.preventDefault(); }; })();