// ==UserScript==
// @name Select like opera
// @author lkytal
// @namespace Lkytal
// @homepage https://lkytal.github.io/
// @homepageURL https://lkytal.github.io/GM
// @icon https://github.com/lkytal/GM/raw/master/icons/def.ico
// @version 1.2.2
// @description Select texts insider links, support firefox and chrome
// @license AGPL
// @include *
// @grant unsafeWindow
// @grant GM_getValue
// @grant GM_setValue
// @run-at document-end
// @charset UTF-8
// @supportURL https://github.com/lkytal/GM/issues
// ==/UserScript==
var selectLikeOpera = function () {
var findHTMLAnchor = function (node) {
if (node.nodeType === 3) node = node.parentNode;
do {
if (node.constructor === HTMLAnchorElement) return node;
} while ((node = node.parentNode) && node !== document.body);
return null;
};
var preventEvent = function (e) {
e.preventDefault();
e.stopPropagation();
return false;
};
var rangeOperator = (function () {
var notFirefox = navigator.userAgent.indexOf("Firefox") == -1;
return {
createRange: function (x, y) {
if (notFirefox) {
return document.caretRangeFromPoint(x, y);
}
else {
var range = document.createRange();
var position = document.caretPositionFromPoint(x, y);
range.setStart(position.offsetNode, position.offset);
return range;
}
},
rangeAttr: (notFirefox ? '-webkit-user-select' : '-moz-user-select')
}
})();
var setStyle = (function () {
var styleList = [
{
p: rangeOperator.rangeAttr,
v: 'text'
},
{
p: 'outline-width',
v: 0
}
];
var node;
var s;
return function (_node) {
if (_node) {
node = _node, s = [];
for (var i = styleList.length - 1; i >= 0; i -= 1) {
s.push([node.style.getPropertyValue(styleList[i].p), node.style.getPropertyPriority(styleList[i].p)]);
node.style.setProperty(styleList[i].p, styleList[i].v, 'important');
}
}
else if (node) {
for (var i = styleList.length; i-- > 0;) {
node.style.removeProperty(styleList[i].p);
if (s[i][0] !== null) node.style.setProperty(styleList[i].p, s[i][0], s[i][1]);
}
node = s = null;
}
}
})();
var toggleEvent = function (events, bAdd) {
if (bAdd === undefined) bAdd = true;
if (events.constructor !== Array) events = [events];
for (var i = 0, len = events.length; i < len; i += 1) {
if (bAdd) {
document.addEventListener(events[i], eventList[events[i]], true);
}
else {
document.removeEventListener(events[i], eventList[events[i]], true);
}
}
};
var removeEvent = function (a) {
toggleEvent(a, false);
};
var position, q, u, v, z, resetState = function () {
q = v = true;
u = z = false;
};
var nodeInfo, selection = document.getSelection();
let selectEvent = function (e) {
if (e.which < 2) {
resetState();
var x = e.clientX,
y = e.clientY;
if (selection.rangeCount > 0) {
var selectedRange = selection.getRangeAt(0);
if (!selectedRange.collapsed) {
var newRange = rangeOperator.createRange(x, y);
if (newRange && selectedRange.isPointInRange(newRange.startContainer, newRange.startOffset)) return;
}
}
setStyle();
var target = e.target;
var node = findHTMLAnchor(target);
if (!node) node = target.nodeType !== 3 ? target : target.parentNode;
if (node.constructor === HTMLCanvasElement || node.textContent === '') return;
var range = node.getBoundingClientRect();
nodeInfo = {
n: node,
x: Math.round(range.left),
y: Math.round(range.top),
c: 0
};
position = {
x: x,
y: y
};
toggleEvent(['mousemove', 'mouseup', 'dragend', 'dragstart']);
setStyle(node);
}
};
var D = 3, K = 0.8;
var eventList = {
'mousemove': function (e) {
if (nodeInfo) {
if (nodeInfo.c++ < 12) {
var rect = nodeInfo.n.getBoundingClientRect();
if (Math.round(rect.left) !== nodeInfo.x || Math.round(rect.top) !== nodeInfo.y) {
removeEvent(['mousemove', 'mouseup', 'dragend', 'dragstart', 'click']);
setStyle();
selection.removeAllRanges();
return;
}
}
else {
nodeInfo = null;
}
}
var x = e.clientX,
y = e.clientY;
if (v) {
selection.removeAllRanges();
var offset = x > position.x ? -2 : 2;
var newRange = rangeOperator.createRange(x + offset, y);
if (newRange) {
selection.addRange(newRange);
v = false;
}
}
if (q) {
var c = Math.abs(position.x - x),
d = Math.abs(position.y - y);
u = d === 0 || c / d > K;
if (c > D || d > D) {
q = false;
z = true;
toggleEvent('click');
}
}
if (u) {
var newRange = rangeOperator.createRange(x, y);
if (newRange) selection.extend(newRange.startContainer, newRange.startOffset);
}
},
'dragstart': function (e) {
removeEvent('dragstart');
if (u) return preventEvent(e);
},
'mouseup': function (e) {
removeEvent(['mousemove', 'mouseup', 'dragstart', 'dragend']);
if (!u && z) z = false;
setTimeout(function () {
removeEvent('click');
}, 25);
},
'dragend': function (e) {
removeEvent(['dragend', 'mousemove', 'mouseup']);
},
'click': function (e) {
removeEvent('click');
if (z) return preventEvent(e);
}
};
document.addEventListener('mousedown', selectEvent, true);
};
setTimeout(selectLikeOpera, 100);