// ==UserScript== // @name Text To link // @description Turn plain text URLs into clickable links, 把文字链接转换为可点击链接 // @author lkytal // @namespace Lkytal // @homepage http://lkytal.github.io/ // @include * // @exclude *pan.baidu.com/* // @exclude *renren.com/* // @exclude *exhentai.org/* // @exclude *music.google.com/* // @exclude *mail.google.com/* // @exclude *docs.google.com/* // @exclude *www.google.* // @exclude *acid3.acidtests.org/* // @exclude *.163.com/* // @version 2.7.2 // @icon http://lkytal.qiniudn.com/ic.ico // @grant unsafeWindow // @homepageURL https://git.oschina.net/coldfire/GM // @downloadURL none // ==/UserScript== "use strict"; var clearLink, excludedTags, filter, linkMixInit, linkPack, linkify, observePage, observer, setLink, url_regexp, xpath; url_regexp = /((https?:\/\/|www\.)[\x21-\x7e]+[\w\/]|(\w[\w._-]+\.(com|cn|org|net|info|tv|cc))(\/[\x21-\x7e]*[\w\/])?|ed2k:\/\/[\x21-\x7e]+\|\/|thunder:\/\/[\x21-\x7e]+=)/gi; clearLink = function(event) { var link, ref, url; link = (ref = event.originalTarget) != null ? ref : event.target; if (!((link != null) && link.localName === "a" && link.className.indexOf("texttolink") !== -1)) { return; } url = link.getAttribute("href"); if (url.indexOf("http") !== 0 && url.indexOf("ed2k://") !== 0 && url.indexOf("thunder://") !== 0) { return link.setAttribute("href", "http://" + url); } }; document.addEventListener("mouseover", clearLink); setLink = function(candidate) { var span, text; if ((candidate == null) || candidate.parentNode.className.indexOf("texttolink") !== -1 || candidate.nodeName === "#cdata-section") { return; } text = candidate.textContent.replace(url_regexp, '$1'); if (candidate.textContent.length === text.length) { return; } span = document.createElement("span"); span.innerHTML = text; return candidate.parentNode.replaceChild(span, candidate); }; excludedTags = "a,svg,canvas,applet,input,button,area,pre,embed,frame,frameset,head,iframe,img,option,map,meta,noscript,object,script,style,textarea,code".split(","); xpath = "//text()[not(ancestor::" + (excludedTags.join(') and not(ancestor::')) + ")]"; filter = new RegExp("^(" + (excludedTags.join('|')) + ")$", "i"); linkPack = function(result, start) { var i, j, k, ref, ref1, ref2, ref3; if (start + 10000 < result.snapshotLength) { for (i = j = ref = start, ref1 = start + 10000; ref <= ref1 ? j <= ref1 : j >= ref1; i = ref <= ref1 ? ++j : --j) { setLink(result.snapshotItem(i)); } setTimeout(function() { return linkPack(result, start + 10000); }, 15); } else { for (i = k = ref2 = start, ref3 = result.snapshotLength; ref2 <= ref3 ? k <= ref3 : k >= ref3; i = ref2 <= ref3 ? ++k : --k) { setLink(result.snapshotItem(i)); } } }; linkify = function(doc) { var result; result = document.evaluate(xpath, doc, null, XPathResult.UNORDERED_NODE_SNAPSHOT_TYPE, null); return linkPack(result, 0); }; observePage = function(root) { var tW; tW = document.createTreeWalker(root, NodeFilter.SHOW_TEXT, { acceptNode: function(a) { if (!filter.test(a.parentNode.localName)) { return NodeFilter.FILTER_ACCEPT; } } }, false); while (tW.nextNode()) { setLink(tW.currentNode); } }; observer = new window.MutationObserver(function(mutations) { var Node, j, k, len, len1, mutation, ref; for (j = 0, len = mutations.length; j < len; j++) { mutation = mutations[j]; if (mutation.type === "childList") { ref = mutation.addedNodes; for (k = 0, len1 = ref.length; k < len1; k++) { Node = ref[k]; observePage(Node); } } } }); linkMixInit = function() { if (window !== window.top || window.document.title === "") { return; } linkify(document.body); return observer.observe(document.body, { childList: true, subtree: true }); }; setTimeout(linkMixInit, 100);