- // ==UserScript==
- // @name Linky Square
- // @author eight <eight04@gmail.com>
- // @version 0.2.1
- // @namespace eight04.blogspot.com
- // @description Grab links by dragging a square.
- // @include *
- // @grant GM_openInTab
- // @grant GM_setClipboard
- // @grant GM_addStyle
- // @grant GM_setValue
- // @grant GM_getValue
- // @grant GM_registerMenuCommand
- // @compatible firefox
- // @compatible chrome
- // @compatible opera
- // @require https://greasyfork.org/scripts/7212-gm-config-eight-s-version/code/GM_config%20(eight's%20version).js?version=156587
- // ==/UserScript==
-
- function createLinky(o){
- var delay = function(){
- function wrap(target) {
- target();
- target.delay = false;
- }
-
- return function(target) {
- if (!target.delay) {
- target.delay = true;
- setTimeout(wrap, 0, target);
- }
- };
- }();
-
- var tracker = function(o){
- var ox = 0, oy = 0, x = 0, y = 0,
- traceStart = false,
- linkCount = 0,
- enable = false;
-
- var ui = function(){
- GM_addStyle(".linky-info-box,.linky-select-box{position:absolute;z-index:65534;display:none}.linky .linky-anchor-box{background:#ff0}.linky .linky-anchor-box img{filter:url(data:image/svg+xml;charset=utf8;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciPg0KCTxmaWx0ZXIgaWQ9ImZpbHRlciI+DQoJCTxmZUZsb29kIHJlc3VsdD0iZmxvb2RGaWxsIiB4PSIwIiB5PSIwIiB3aWR0aD0iMTAwJSIgaGVpZ2h0PSIxMDAlIg0KCQkJCWZsb29kLWNvbG9yPSJ5ZWxsb3ciIGZsb29kLW9wYWNpdHk9IjEiLz4NCgkJPGZlQmxlbmQgaW49IlNvdXJjZUdyYXBoaWMiIGluMj0iZmxvb2RGaWxsIiBtb2RlPSJtdWx0aXBseSIvPg0KCTwvZmlsdGVyPg0KPC9zdmc+DQo=#filter)}.linky-select-box{border:2px dashed red;box-sizing:border-box}.linky-info-box{color:#000;border:1px solid grey;background:#fff;padding:.3em .6em}.linky body{-moz-user-select:none;-webkit-user-select:none;pointer-events:none}.linky .linky-info-box,.linky .linky-select-box{display:block}");
-
- var selectBox = document.createElement("div");
- selectBox.className = "linky-select-box";
-
- var infoBox = document.createElement("div");
- infoBox.className = "linky-info-box";
-
- var body = document.body;
- body.appendChild(selectBox);
- body.appendChild(infoBox);
-
- function updateSelectBox(x, y, w, h){
- var s = selectBox.style;
- s.left = x + "px";
- s.top = y + "px";
- s.width = w + "px";
- s.height = h + "px";
- }
-
- function updateInfoBox(x, y, text) {
- var s = infoBox.style;
- s.left = x + 16 + "px";
- s.top = y + 16 + "px";
- infoBox.textContent = text;
- }
-
- function on(){
- document.documentElement.classList.add("linky");
- }
-
- function off(){
- document.documentElement.classList.remove("linky");
- }
-
- return {
- on: on,
- off: off,
- updateSelectBox: updateSelectBox,
- updateInfoBox: updateInfoBox
- };
- }();
-
- function getOffset(node){
- var rect = node.getBoundingClientRect();
-
- return {
- x: window.pageXOffset + rect.left,
- y: window.pageYOffset + rect.top,
- width: rect.width,
- height: rect.height
- };
- }
-
- function updateSelectBox(){
- ui.updateSelectBox(
- Math.min(Math.min(x, ox)),
- Math.min(y, oy),
- Math.abs(x - ox),
- Math.abs(y - oy)
- );
- }
-
- function inSelect(node){
- var pos = getOffset(node);
- var centerx = pos.x + pos.width / 2;
- var centery = pos.y + pos.height / 2;
-
- if (centerx < Math.min(ox, x)) {
- return false;
- }
- if (centerx > Math.max(ox, x)) {
- return false;
- }
- if (centery < Math.min(oy, y)) {
- return false;
- }
- if (centery > Math.max(oy, y)) {
- return false;
- }
- return true;
- }
-
- function isJSURL(node){
- return node.href.lastIndexOf("javascript:", 0) != -1;
- }
-
- function updateLinkList(){
- var l = document.querySelectorAll("a[href]"), i, k = [], len = l.length;
- for (i = 0; i < len; i++) {
- k.push(inSelect(l[i]) && !isJSURL(l[i]));
- }
- linkCount = 0;
- for (i = 0; i < len; i++) {
- l[i].classList.toggle("linky-anchor-box", k[i]);
- linkCount += +k[i];
- }
- }
-
- function updateInfoBox() {
- ui.updateInfoBox(x, y, "selected " + linkCount + " link(s)");
- }
-
- function update(){
- updateSelectBox();
- delay(updateLinkList);
- updateInfoBox();
- }
-
- function takeLinks(){
- var l = document.querySelectorAll("a[href]"), i, links = [];
- for (i = 0; i < l.length; i++) {
- if (inSelect(l[i]) && !isJSURL(l[i])) {
- links.push(l[i].href);
- }
- }
- return links;
- }
-
- function handler(e){
- if (e.type == "mousedown") {
- if (traceStart) {
- return;
- }
- if (!o.config.key.regular(e)) {
- return;
- }
- traceStart = true;
-
- on();
- // call it directly will cause firefox to stop srcolling. why?
- setTimeout(ui.on);
-
- x = ox = e.pageX;
- y = oy = e.pageY;
-
- update();
-
- } else if (e.type == "mousemove") {
- if (!traceStart) {
- return;
- }
-
- x = e.pageX;
- y = e.pageY;
-
- update();
-
- } else if (e.type == "mouseup" || e.type == "keydown") {
- if (
- !traceStart ||
- e.type == "keydown" && !o.config.key.copy(e) && !o.config.key.cancel(e)
- ) {
- return;
- }
- traceStart = false;
-
- off();
- ui.off();
- o.callback(e, takeLinks());
- }
- }
-
- function on(){
- if (enable) {
- return;
- }
- window.addEventListener("mousemove", handler);
- window.addEventListener("mouseup", handler);
- window.addEventListener("keydown", handler);
- enable = true;
- }
-
- function off(){
- window.removeEventListener("mousemove", handler);
- window.removeEventListener("mouseup", handler);
- window.removeEventListener("keydown", handler);
- enable = false;
- }
-
- function track() {
- window.addEventListener("mousedown", handler);
- handler(o.initEvent);
- }
-
- return {
- on: on,
- off: off,
- track: track
- };
- }(o);
-
- tracker.track();
- }
-
- function openLinks(links) {
- var i;
- for (i = 0; i < links.length; i++) {
- GM_openInTab(links[i], true);
- }
- }
-
- // do object property check
- function objectProperties(a, b) {
- var key;
- for (key in b) {
- if (a[key] !== b[key]) return false;
- }
- return true;
- }
-
- function createConfig() {
- var config = {key: {
- regular: function(e) {
- return objectProperties(e, config.keyRegular);
- },
- copy: function(e) {
- return objectProperties(e, config.keyCopy);
- },
- cancel: function(e) {
- return objectProperties(e, config.keyCancel);
- }
- }};
- GM_config.setup({
- behavior: {
- label: "Default action after selecting",
- type: "select",
- default: "open",
- options: {
- open: "Open links in new tab",
- copy: "Copy URLs"
- }
- },
- keyRegular: {
- label: "Event to start selection",
- type: "textarea",
- default: JSON.stringify({altKey: true, ctrlKey: false, shiftKey: false, button: 0})
- },
- keyCopy: {
- label: "Event to copy url",
- type: "textarea",
- default: JSON.stringify({code: "KeyC"})
- },
- keyCancel: {
- label: "Event to cancel selection",
- type: "textarea",
- default: JSON.stringify({code: "Escape"})
- }
- }, function() {
- var o = GM_config.get();
- Object.assign(config, o);
- for (var key in o) {
- if (!key.startsWith("key")) {
- continue;
- }
- try {
- config[key] = JSON.parse(o[key]);
- } catch (err) {
- alert("Failed to create key config! Is your config broken?");
- }
- }
- });
- return config;
- }
-
- function copyLinks(links) {
- if (!links.length) return;
- GM_setClipboard(links.join("\n"));
- }
-
- var config = createConfig();
-
- function handler(e, links) {
- if (e.type == "mouseup" && config.behavior == "open") {
- openLinks(links);
- } else if (e.type == "mouseup" || config.key.copy(e)) {
- copyLinks(links);
- }
- }
-
- window.addEventListener("mousedown", function init(e){
- if (config.key.regular(e)) {
- window.removeEventListener("mousedown", init);
- createLinky({
- callback: handler,
- initEvent: e,
- config: config
- });
- }
- });