从网页中批量复制、打开链接,选择复选框
// ==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:无法正确选取图像 // ==/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:反向序号)', '<a href="%u">%r. %t</a><br>'); 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, XPathR###lt.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]<xmax)|| (point[1]>xmin&&point[1]<xmax)|| (point[0]<xmin&&point[1]>xmax)){ swithcase[0] = true; } if((point[2]>ymin&&point[2]<ymax)|| (point[3]>ymin&&point[3]<ymax)|| (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<eClass.length;i++){ eClass[i].style = "display:none"; } //eClass.forEach(function(enode){enode.setAttribute("stlye","display:none")}) } else{ for(var i=0;i<eClass.length;i++){ eClass[i].style = "display:block"; } //eClass.forEach(function(enode){enode.setAttribute("stlye","display:block")}) } } } ) } if (cls.length > 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<midx) ? "left: "+ax.toString()+"px;":"left: "+menuRight.toString()+"px;"; yaxis = (cy<midy) ? " top: "+ay.toString()+"px;" : " top: "+menuDown.toString()+"px;"; popMenu.setAttribute("style", xaxis + yaxis); }, openLinks : function(regexp){ var obj = {}; for (let elem of this.elements) { if (!elem.href || /^(?:javascript:|mailto:|#)/i.test(elem.href)) continue; if (!regexp || regexp.test(elem.href)) obj[elem.href] = true; } for (let [key, val] of Object.entries(obj)) { GM_openInTab(key); //gBrowser.addTab(key, { ownerTab: gBrowser.mCurrentTab }); } }, clickLinks : function(){ for (let elem of this.elements) { if (!elem.href || /^(?:javascript:|mailto:|#)/i.test(elem.href)) { elem.click(); } } }, copyLinks : function(regexp, reverse, format){ //GM_log(selements); var links = this.elements.filter(function(elem){ return elem instanceof HTMLAnchorElement && (!regexp || regexp.test(elem.href)) }); var num = 1, numReverse = links.length; links = links.map(function(e) { if (format) { return format.replace(/%t/g, e.textContent) .replace(/%u/g, e.href) .replace(/%r/g, numReverse--) .replace(/%n/g, num++); } return e.href; }); // 筛选出重复的 links = snapLinks.unique(links); if(reverse) links = links.reverse(); if (links.length){ GM_setClipboard(links.join('\n')); //Components.classes["@mozilla.org/widget/clipboardhelper;1"] // .getService(Components.interfaces.nsIClipboardHelper) // .copyString(links.join('\n')); } }, imageOnePage : function(){ var htmlsrc = [ '<style>' ,'img { max-width: 100%; max-height: 100%; }' ,'</style>'].join(''); for (let elem of this.elements) { if (elem instanceof HTMLAnchorElement && /\.(jpe?g|png|gif|bmp)$/i.test(elem.href)) htmlsrc += '\n<img src="' + elem.href + '">' } GM_openInTab("data:text/html;charset=utf-8," + '<html><head><title>' + snapLinks.doc.domain + ' 图象列表</title><body>' + 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 = '<div class = "-hasLink-">' + '<div id="SnapLinksOpenLinks" class="hasLink" >在新标签打开所有链接</div>' + '<div id="SnapLinksCopyLinks" class="hasLink" >复制所有链接URL</div>' + '<div id="SnapLinksCopyLinksReverse" class="hasLink" >复制所有链接URL(反向)</div>' + '<div id="SnapLinksCopyLinksAndTitles" class="hasLink" >复制所有链接标题 + URL</div>' + '<div id="SnapLinksCopyLinksAndTitlesMD" class="hasLink" >复制所有链接标题 + URL (MD)</div>' + '<div id="SnapLinksCopyLinksAndTitlesBBS" class="hasLink">复制所有链接标题 + URL (BBS)</div>' + '<div id="SnapLinksCopyLinksRegExp" class="hasLink" >复制所有链接标题 + URL (筛选)</div>' + '<div id="SnapLinksCopyLinksSetFormat" class="hasLink" >复制所有链接标题 + URL (设置复制格式)</div>' + '<div id="SnapLinksOpenImageLinks" class="hasImageLink" >在新标签页打开所有图片链接</div>' + '<div id="SnapLinksImageLinksOnePage" class="hasImageLink" >在一个标签页显示所有图片链接</div>' + '</div>' + '<div class="hasLink-hasCheckbox-hasRadio" >' + '<div id="SnapLinksCheckBoxSelect" class="hasCheckbox" >复选框 - 选中</div>' + '<div id="SnapLinksCheckBoxCancel" class="hasCheckbox" >复选框 - 取消</div>' + '<div id="SnapLinksCheckBoxTaggle" class="hasCheckbox" >复选框 - 反选</div>' + '<div id="SnapLinksRadioSelect" class="hasRadio" >单选框 - 选中</div>' + '<div id="SnapLinksRadioCancel" class="hasRadio">单选框 - 取消</div>' + '<div id="SnapLinksClickLinks" class="hasSpecialLink" >特殊单选框 - 选中</div>' + '</div>'; 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();