- // ==UserScript==
- // @name Select like a Boss
- // @namespace https://github.com/lcandy2/Select-like-a-Boss
- // @version 2023.7.33
- // @license MPL-2.0
- // @description With this extension, you can easily select link text just like regular text, making it easier to copy. Just Select like a Boss! ;)
- // @author seril🍋
- // @match *
- // @run-at document-end
- // @homepageURL https://lcandy2.github.io/Select-like-a-Boss/
- // @icon https://raw.githubusercontent.com/lcandy2/Select-like-a-Boss/main/src/icons/icon16.png
- // @supportURL https://github.com/lcandy2/Select-like-a-Boss/issues
- // @grant none
- // ==/UserScript==
-
- (function() {
- 'use strict';
- // Create and insert style element
- const styleElm = document.createElement('style');
- document.head.appendChild(styleElm);
- styleElm.sheet.insertRule('.ext-Select-like-a-Boss {user-select: text !important; outline-width: 0 !important;}', 0);
-
- const getRangeFromPoint = (x, y) =>
- document.caretPositionFromPoint
- ? (() => {
- const range = document.createRange();
- const p = document.caretPositionFromPoint(x, y);
- range.setStart(p.offsetNode, p.offset);
- return range;
- })()
- : document.caretRangeFromPoint(x, y);
-
- const stopEvent = (e) => {
- e.preventDefault();
- e.stopPropagation();
- return false;
- };
-
- let selection = document.getSelection();
- let cursor = {}, userSelecting;
- let justSelected = false;
-
- const handlers = {
- mousemove: (e) => {
- let x = e.clientX, y = e.clientY;
- let vx = Math.abs(cursor.x - x), vy = Math.abs(cursor.y - y);
- userSelecting = vy === 0 || vx / vy > 0.8;
- if (userSelecting && (vx > 3 || vy > 3)) {
- let range = getRangeFromPoint(x, y);
- if (range && selection.rangeCount > 0) { // Check if a selection range exists
- selection.extend(range.startContainer, range.startOffset);
- }
- }
- },
- mouseup: (e) => {
- ['mousemove', 'mouseup', 'dragstart', 'dragend'].forEach(event => removeEvent(event, handlers[event]));
- if (userSelecting) {
- justSelected = true;
- addEvent('click', handlers.click);
- // console.log('add click event', userSelecting)
- }
- toggleUserSelect(e.target, false);
- },
- dragstart: (e) => {
- if (userSelecting) return stopEvent(e);
- },
- dragend: (e) => ['dragend', 'mousemove', 'mouseup'].forEach(event => removeEvent(event, handlers[event])),
- click: (e) => {
- if (justSelected) {
- justSelected = false;
- setTimeout(() => {
- removeEvent('click', handlers.click);
- // console.log('remove click event', userSelecting)
- }, 50);
- return stopEvent(e);
- }
- },
- };
-
- const addEvent = (event, handler) => document.addEventListener(event, handler, true);
- const removeEvent = (event, handler) => document.removeEventListener(event, handler, true);
-
- const toggleUserSelect = (node, enable = false) => {
- if (node) {
- if (enable) node.classList.add('ext-Select-like-a-Boss');
- else node.classList.remove('ext-Select-like-a-Boss');
- }
- };
-
- document.addEventListener('mousedown', (e) => {
- const excludeTags = e.target.closest('img, canvas, picture, svg, audio, video, object, progress, textarea')
- let excludeTagsCount = 1;
- if (excludeTags) excludeTagsCount = excludeTags.childElementCount;
- if (e.button !== 0 || excludeTagsCount === 0) { excludeTagsCount = 1; return; }
- userSelecting = false;
- cursor.x = e.clientX;
- cursor.y = e.clientY;
- if (selection.type === 'Range') {
- let range = getRangeFromPoint(cursor.x, cursor.y);
- if (range && selection.getRangeAt(0).isPointInRange(range.startContainer, range.startOffset)) return;
- }
- let range = getRangeFromPoint(e.clientX, e.clientY);
- if (range) {
- selection.removeAllRanges();
- selection.addRange(range);
- }
- toggleUserSelect(e.target, true);
- ['mousemove', 'mouseup', 'dragstart', 'dragend'].forEach(event => addEvent(event, handlers[event]));
- }, true);
-
- })();