// ==UserScript==
// @name Snap Links Mod
// @description 从网页中批量复制、打开链接,选择复选框
// @name:en Snap Links Mod
// @description:en snap Links(open, copy), radios, chenkboxs, images from website
// @author Griever, ywzhaiqi, lastdream2013, Hanchy Hill
// @namespace http://minhill.com/
// @homepageURL https://greasyfork.org/en/scripts/25051/
// @include http*
// @version 2021.07.05
// @license The MIT License (MIT); http://opensource.org/licenses/MIT
// @grant GM_getValue
// @grant GM_setValue
// @grant GM_openInTab
// @grant GM_deleteValue
// @grant GM_addStyle
// @run-at context-menu
// @grant GM_registerMenuCommand
// @grant GM_setClipboard
// @grant GM_log
// @compatible firefox
// @compatible chrome
// @compatible edge
// @icon http://minhill.com/blog/wp-content/uploads/2012/03/favicon.ico
// @note 2016/11/22 改写的第一个版本,存在BUG:无法正确选取图像
// @downloadURL https://update.greasyfork.org/scripts/25051/Snap%20Links%20Mod.user.js
// @updateURL https://update.greasyfork.org/scripts/25051/Snap%20Links%20Mod.meta.js
// ==/UserScript==
var snapLinks = {
timer: null,
button: 0,
init: function() {
/*if (!snapLinks.inited) {
var menuitem = document.getElementById("SnapLinksCopyLinksSetFormat");
if (menuitem) {
var func = function() {
var format = prompt('请输入需要设置的格式(%t:标题,%u:链接,%n:序号,%r:反向序号)',
'%r. %t
');
snapLinks.copyLinks(null, false, format);
};
menuitem.addEventListener('command', func, false);
}
snapLinks.inited = true;
}*/
this.win = window;
if (snapLinks.win == window) snapLinks.win = window;
this.doc = this.win.document;
this.body = this.doc.body;
if (!this.body instanceof HTMLBodyElement){
alert("Can not snaplinks.");
return false;
}
this.root = snapLinks.doc.documentElement;
//this.utils = this.win.QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIDOMWindowUtils);
this.popup = document.getElementById("snapLinksMenupopup");
this.bodyCursor = this.body.style.cursor;
this.rootCursor = this.root.style.cursor;
this.body.style.setProperty("cursor", "crosshair", "important");
this.root.style.setProperty("cursor", "crosshair", "important");
this.highlights = [];
this.elements = [];
this.doc.addEventListener("mousedown", snapLinks.handleEvent, true);
this.doc.addEventListener("pagehide", snapLinks.handleEvent, true);
},
uninit: function() {
snapLinks.doc.removeEventListener("mousedown", snapLinks.handleEvent, true);
snapLinks.doc.removeEventListener("mousemove", snapLinks.handleEvent, true);
snapLinks.doc.removeEventListener("pagehide", snapLinks.handleEvent, true);
snapLinks.doc.removeEventListener("mouseup", snapLinks.handleEvent, true);//?
setTimeout(function(self){
snapLinks.doc.removeEventListener("click", snapLinks.handleEvent, true);
}, 10, snapLinks);
if (snapLinks.box && snapLinks.box.parentNode)
snapLinks.box.parentNode.removeChild(snapLinks.box);
snapLinks.box = null;
snapLinks.body.style.cursor = snapLinks.bodyCursor;
snapLinks.root.style.cursor = snapLinks.rootCursor;
},
destroy: function() {
snapLinks.uninit();
snapLinks.lowlightAll();
document.removeEventListener("click",snapLinks.destroy,false);
var sslpop = document.getElementById("snapLinksMenupopup")
sslpop.setAttribute("class","hidden_popup");
sslpop.setAttribute("style",null);
},
handleEvent: function(event) {
switch(event.type){
case "mousedown":
if (event.button != 0 || event.ctrlKey || event.shiftKey || event.altKey) return;
event.preventDefault();
event.stopPropagation();
snapLinks.draw(event);
break;
case "mousemove":
event.preventDefault();
event.stopPropagation();
var moveX = event.pageX;
var moveY = event.pageY;
if (snapLinks.downX > moveX) snapLinks.box.style.left = moveX + "px";
if (snapLinks.downY > moveY) snapLinks.box.style.top = moveY + "px";
snapLinks.box.style.width = Math.abs(moveX - snapLinks.downX) + "px";
snapLinks.box.style.height = Math.abs(moveY - snapLinks.downY) + "px";
if (snapLinks.timer) {
clearTimeout(snapLinks.timer);
snapLinks.timer = null;
}
var timeStamp = new Date().getTime();
if (timeStamp - snapLinks.lastHiglightedTime > 150) {
snapLinks.boxRect = snapLinks.box.getBoundingClientRect();
snapLinks.highlightAll();
} else {
var self = snapLinks;
snapLinks.timer = setTimeout(function() {
self.boxRect = self.box.getBoundingClientRect();
self.highlightAll();
}, 200);
}
break;
case "mouseup":
if (event.button != snapLinks.button || event.ctrlKey || event.shiftKey) return;
event.preventDefault();
event.stopPropagation();
if (snapLinks.timer) {
clearTimeout(snapLinks.timer);
snapLinks.timer = null;
}
snapLinks.boxRect = snapLinks.box.getBoundingClientRect();
snapLinks.highlightAll();
for (let e of snapLinks.highlights) {
if (e instanceof HTMLImageElement) {
let link = snapLinks.doc.evaluate(
'ancestor::*[@href]', e, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue;
if (snapLinks.highlights.indexOf(link) === -1) {
snapLinks.elements[snapLinks.elements.length] = link;
}
continue;
}
snapLinks.elements[snapLinks.elements.length] = e;
}
snapLinks.elements = snapLinks.elements;//?
snapLinks.uninit();
snapLinks.showPopup(event);
break;
case "click":
event.preventDefault();
event.stopPropagation();
break;
case "pagehide":
snapLinks.destroy();
break;
}
},
draw: function(aEvent) {
this.lastHiglightedTime = new Date().getTime();
this.downX = aEvent.pageX;
this.downY = aEvent.pageY;
this.box = this.doc.createElement("div");
this.box.id = "snap-links-box";
this.box.style.cssText = [
'background-color: rgba(0,128,255,.1) !important;',
'border: 1px solid rgb(255,255,0) !important;',
'box-sizing: border-box !important;',
'-moz-box-sizing: border-box !important;',
'position: absolute !important;',
'z-index: 2147483647 !important;',
'top:' + this.downY + 'px;',
'left:' + this.downX + 'px;',
'cursor: crosshair !important;',
'margin: 0px !important;',
'padding: 0px !important;',
'outline: none !important;',
].join(" ");
this.body.appendChild(this.box);
this.doc.removeEventListener("mousedown", this.handleEvent, true);
this.doc.addEventListener("mousemove", this.handleEvent, true);
this.doc.addEventListener("mouseup", this.handleEvent, true);
this.doc.addEventListener("click", this.handleEvent, true);
},
highlightAll: function() {
var a = '[href]:not([href^="javascript:"]):not([href^="mailto:"]):not([href^="#"])';
var selector = a + ', ' + a + ' img, input[type="checkbox"], input[type="radio"]';
selector += ', a.b-in-blk.input-cbx[href^="javascript:"]'; // 百度盘的特殊多选框
var contains = this.getContainsElements();
contains.reverse();
var matches = [];
for (let e of contains) {
if (e.nodeType !== 1 || !e.matches(selector))
continue;
if (e.hasAttribute('href')) {
let imgs = Array.prototype.slice.call(e.getElementsByTagName('img'));
if (imgs[0]) {
[].push.apply(contains, imgs);
continue;
}
}
if (!("defStyle" in e))
this.highlight(e);
matches[matches.length] = e;
}
this.highlights.forEach(function(e, i, a){
if (matches.indexOf(e) === -1)
this.lowlight(e);
}, this);
this.highlights = matches;
this.lastHiglightedTime = new Date().getTime();
},
lowlightAll: function() {
this.highlights.forEach(function(e){
this.lowlight(e);
}, this);
},
highlight: function(elem) {
if (!('defStyle' in elem))
elem.defStyle = elem.getAttribute('style');
//elem.style.setProperty('outline', '2px solid #ff0000', 'important');
elem.style.setProperty('outline', '2px solid #ff0000', null);
elem.style.setProperty('outline-offset', '-1px', null);
//elem.style.setProperty('outline-offset', '-1px', 'important');
},
lowlight: function(elem) {
if ("defStyle" in elem) {
elem.defStyle?
elem.style.cssText = elem.defStyle:
elem.removeAttribute("style");
delete elem.defStyle;
}
},
getContainsElements: function() {
if (!this.boxRect) return;
var a = '[href]:not([href^="javascript:"]):not([href^="mailto:"]):not([href^="#"])';
var selector = a + ', ' + a + ' img, input[type="checkbox"], input[type="radio"]';
selector += ', a.b-in-blk.input-cbx[href^="javascript:"]';
//var nodes = document.querySelectorAll("a[href],img,radio,checkbox");
var nodes = document.querySelectorAll(selector);
var arraynode=[] , len = nodes.length , i;
for (i = 0; i < len; i++) {
if(this.inSelect(nodes[i])) arraynode.push(nodes[i]);
}
return arraynode;
},
inSelect : function (node){
var boxPos = snapLinks.boxRect;
var xmin = boxPos.left, xmax = boxPos.right, ymin = boxPos.top, ymax = boxPos.bottom;
var pos = this.getOffset(node);
var point = new Array();
point = [pos.x, pos.x + pos.width, pos.y, pos.y + pos.height];
var swithcase = [];
if((point[0]>xmin&&point[0]xmin&&point[1]xmax)){
swithcase[0] = true;
}
if((point[2]>ymin&&point[2]ymin&&point[3]ymax)){
swithcase[1] = true;
}
if(swithcase[0]&&swithcase[1]){
return true;
}
else{
return false;
}
},
getOffset : function(node){
var rect = node.getBoundingClientRect();
return {
//x: window.pageXOffset + rect.left,
//y: window.pageYOffset + rect.top,
x: rect.left,
y: rect.top,
width: rect.width,
height: rect.height
};
},
showPopup: function(aEvent) {
var cls = [];
var linkcount = 0;
var specialLinkCount = 0; // 特殊的类似多选框的链接
var imagecount = 0;
var checkboxcount = 0;
var radiocount = 0;
for (let elem of this.elements) {
if (elem instanceof HTMLAnchorElement)
elem.href.indexOf('javascript:') == 0 ? specialLinkCount++ : linkcount++;
}
for (let elem of this.elements) {
if (elem instanceof HTMLAnchorElement && /\.(jpe?g|png|gif|bmp)$/i.test(elem.href))
imagecount++;
}
for (let elem of this.elements) {
if (elem instanceof HTMLInputElement && elem.type === 'checkbox') {
checkboxcount++;
}
}
for (let elem of this.elements) {
if (elem instanceof HTMLInputElement && elem.type === 'radio') {
radiocount++;
}
}
if ( linkcount > 0 ) cls.push("hasLink");
if ( imagecount > 0 ) cls.push("hasImageLink");
if ( checkboxcount > 0 ) cls.push("hasCheckbox");
if ( radiocount > 0 ) cls.push("hasRadio");
if ( specialLinkCount > 0 ) cls.push("hasSpecialLink");
var setCount = function(id, label){
let currentEntry = document.getElementById(id);
if(currentEntry)
currentEntry.innerHTML = label;
};
var data = {
"SnapLinksOpenLinks": "在新标签打开所有链接 (" + linkcount + ")",
"SnapLinksCopyLinks": "复制所有链接URL (" + linkcount + ")",
"SnapLinksCopyLinksReverse": "复制所有链接URL (" + linkcount + ") (反向)",
"SnapLinksCopyLinksAndTitles": "复制所有链接标题 + URL (" + linkcount + ")",
"SnapLinksCopyLinksAndTitlesMD": "复制所有链接标题 + URL (" + linkcount + ") (MD)",
"SnapLinksCopyLinksAndTitlesBBS": "复制所有链接标题 + URL (" + linkcount + ") (BBS)",
"SnapLinksCopyLinksRegExp": "复制所有链接标题 + URL (" + linkcount + ") (筛选)",
"SnapLinksCopyLinksSetFormat": "复制所有链接标题 + URL (" + linkcount + ") (设置复制格式)",
"SnapLinksOpenImageLinks": "在新标签页打开所有图片链接 (" + imagecount + ")",
"SnapLinksImageLinksOnePage": "在一个标签页显示所有图片链接 (" + imagecount + ")",
"SnapLinksCheckBoxSelect": "复选框 - 选中 (" + checkboxcount + ")",
"SnapLinksCheckBoxCancel": "复选框 - 取消 (" + checkboxcount + ")",
"SnapLinksCheckBoxTaggle": "复选框 - 反选 (" + checkboxcount + ")",
"SnapLinksRadioSelect": "单选框 - 选中 (" + radiocount + ")",
"SnapLinksRadioCancel": "单选框 - 取消 (" + radiocount + ")",
"SnapLinksClickLinks": "特殊单选框 - 选中 (" + specialLinkCount + ")",
};
for(let id in data){
setCount(id, data[id]);
}
var setStyleNode = function(showList){
var setList = ["hasLink","hasImageLink","hasCheckbox","hasRadio","hasSpecialLink"];
setList.forEach(
function(elist){
eClass = document.getElementsByClassName(elist);
if(eClass){
if(showList.indexOf(elist)==-1){
for(var i=0;i 0) {
setStyleNode(cls);
this.openPopupAtScreen(aEvent.pageX, aEvent.pageY,aEvent.clientX,aEvent.clientY);
//snapLinks.popup.className = cls.join(' ');
} else {
this.lowlightAll();
}
},
openPopupAtScreen:function(ax,ay,cx,cy){
var popMenu = document.getElementById("snapLinksMenupopup");
var midx = document.documentElement.clientWidth/2;
var midy = document.documentElement.clientHeight/2;
//GM_log("pointerY:"+ay);
//GM_log("screen:"+midy*2);
popMenu.className = "trigger_popup";
//popMenu.style.position = "absolute";
var menuRight = ax - popMenu.clientWidth;
var menuDown = ay - popMenu.clientHeight;
document.addEventListener("click",snapLinks.destroy,false);
xaxis = (cx'
,'img { max-width: 100%; max-height: 100%; }'
,''].join('');
for (let elem of this.elements) {
if (elem instanceof HTMLAnchorElement && /\.(jpe?g|png|gif|bmp)$/i.test(elem.href))
htmlsrc += '\n'
}
GM_openInTab("data:text/html;charset=utf-8," +
'' + snapLinks.doc.domain + ' 图象列表' +
encodeURIComponent(htmlsrc));
},
checkbox : function(bool){
for (let elem of this.elements) {
if (elem instanceof HTMLInputElement && elem.type === 'checkbox') {
elem.checked = arguments.length == 0?
!elem.checked :
bool;
}
}
},
radio : function(bool){
for (let elem of this.elements) {
if (elem instanceof HTMLInputElement && elem.type === 'radio') {
elem.checked = arguments.length == 0?
!elem.checked :
bool;
}
}
},
unique: function(a){
var o = {},
r = [],
t;
for (var i = 0, l = a.length; i < l; i++) {
t = a[i];
if(!o[t]){
o[t] = true;
r.push(t);
}
}
return r;
}
};
function begin() {
var ibody = document.getElementsByTagName("body")[0];
/* ibody.setAttribute("contextmenu","popup-menu");
var rclickMenu = document.createElement("menu");
rclickMenu.setAttribute("type","context");
rclickMenu.setAttribute('id', "popup-menu");
var imenu = document.createElement("menuitem");
imenu.setAttribute("id","snapclicks");
imenu.setAttribute('label', "snap-links 🐔");
imenu.setAttribute("image","data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAALUlEQVQ4jWNgkEv8TxHGJ/nr18//v379HDWALgbAFJKKqWfAwIfBqAF4JInAAIZN/GgepyNAAAAAAElFTkSuQmCC");
//imenu.innerHTML = "snap links,,Ծ‸Ծ,, ";
rclickMenu.appendChild(imenu);
ibody.appendChild(rclickMenu);
imenu.addEventListener("click", function(){snapLinks.init();}, false); */
var popup = document.createElement("div");
//popup.setAttribute("onclick","snapLinks.lowlightAll();");
popup.setAttribute("id","snapLinksMenupopup");
popup.setAttribute("class","hidden_popup");
popup.innerHTML = '' +
'
在新标签打开所有链接
' +
'
复制所有链接URL
' +
'
复制所有链接URL(反向)
' +
'
复制所有链接标题 + URL
' +
'
复制所有链接标题 + URL (MD)
' +
'
复制所有链接标题 + URL (BBS)
' +
'
复制所有链接标题 + URL (筛选)
' +
'
复制所有链接标题 + URL (设置复制格式)
' +
'
在新标签页打开所有图片链接
' +
'
在一个标签页显示所有图片链接
' +
'
' +
'' +
'
复选框 - 选中
' +
'
复选框 - 取消
' +
'
复选框 - 反选
' +
'
单选框 - 选中
' +
'
单选框 - 取消
' +
'
特殊单选框 - 选中
' +
'
';
ibody.appendChild(popup);
//popup.addEventListener("click",function(){snapLinks.lowlightAll()},false);
document.getElementById("SnapLinksOpenLinks").addEventListener("click",function(){snapLinks.openLinks();}, false);
document.getElementById("SnapLinksCopyLinks").addEventListener("click",function(){snapLinks.copyLinks();},false);
document.getElementById("SnapLinksCopyLinksReverse").addEventListener("click",function(){snapLinks.copyLinks(null, true);},false);
document.getElementById("SnapLinksCopyLinksAndTitles").addEventListener("click",function(){snapLinks.copyLinks(null, false, '%t\n%u');},false);
document.getElementById("SnapLinksCopyLinksAndTitlesMD").addEventListener("click",function(){snapLinks.copyLinks(null, false, '[%t](%u)');},false);
document.getElementById("SnapLinksCopyLinksAndTitlesBBS").addEventListener("click",function(){snapLinks.copyLinks(null, false, '[url=%u]%t[/url]');},false);
document.getElementById("SnapLinksCopyLinksRegExp").addEventListener("click",function(){var reg=prompt('请输入需要筛选的 RegExp', '');snapLinks.copyLinks(new RegExp(reg));},false);
//document.getElementById("SnapLinksCopyLinksSetFormat").addEventListener("click",function(){snapLinks.copyLinks()},false);
document.getElementById("SnapLinksOpenImageLinks").addEventListener("click",function(){snapLinks.openLinks(/\.(jpe?g|png|gif|bmp)$/i);},false);
document.getElementById("SnapLinksImageLinksOnePage").addEventListener("click",function(){snapLinks.imageOnePage();},false);
document.getElementById("SnapLinksCheckBoxSelect").addEventListener("click",function(){snapLinks.checkbox(true);},false);
document.getElementById("SnapLinksCheckBoxCancel").addEventListener("click",function(){snapLinks.checkbox(false);},false);
document.getElementById("SnapLinksCheckBoxTaggle").addEventListener("click",function(){snapLinks.checkbox();},false);
document.getElementById("SnapLinksRadioSelect").addEventListener("click",function(){snapLinks.radio(true);},false);
document.getElementById("SnapLinksRadioCancel").addEventListener("click",function(){snapLinks.radio(false);},false);
document.getElementById("SnapLinksClickLinks").addEventListener("click",function(){snapLinks.clickLinks();},false);
GM_addStyle(".hidden_popup { display:none!important; } .trigger_popup{display:block!important;z-index:99999}" +
" #snapLinksMenupopup{position:absolute;background-color: rgb(45,53,63);border-bottom: 0px solid rgb(20,20,20); padding:5px;" +
"border-bottom: 0px solid rgb(20,20,20);cursor:pointer;border-radius: 4px;border: 1px solid rgb(22,25,28);box-shadow:0 1px 0 rgba(162,184,204,0.25) inset,0 0 4px hsla(0,0%,0%,0.95);}" +
"#snapLinksMenupopup div{color: white;} #snapLinksMenupopup > div > div:hover{color: rgb(51,159,255);" +
"background-color: transparent; background-image:linear-gradient(to bottom,rgb(37,46,54),rgb(36,40,45));} ");
}
begin();
snapLinks.init();