Last active
November 1, 2016 02:42
-
-
Save htsign/5eed5473a9e75c7c45f3a5571d7d0803 to your computer and use it in GitHub Desktop.
URLらしき文字列なのにリンク(aタグ)になっていないものを対象に、順次リンクを付与していきます。
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| // ==UserScript== | |
| // @name リンク化 | |
| // @namespace jp.hateblo.htsign | |
| // @version 1.2.3 | |
| // @grant none | |
| // @downloadURL https://gist.github.com/htsign/5eed5473a9e75c7c45f3a5571d7d0803/raw/convertTextToLink.user.js | |
| // ==/UserScript== | |
| (() => { | |
| class ElementFactory { | |
| constructor(tagName, attrs) { | |
| this.template = document.createElement(tagName); | |
| for (let {attr, value} of attrs) this.template[attr] = value; | |
| } | |
| create(attrs) { | |
| const elem = this.template.cloneNode(true); | |
| for (let {attr, value} of attrs) elem[attr] = value; | |
| return elem; | |
| } | |
| } | |
| const pattern = /(?:h?t?tp(s?):\/\/(?:[\w-]+|[^ -~。-゚]+)\.[a-zA-Z]{2,4}[^\s "']*)|(?:[^/\s"'((「【『]*?(?:[\w-]+|[^ -~。-゚]+)\.(?:com|org|net|edu|gov|jp|to|tv|fm|info|(?:co|or|ne|ac|go)\.(?:jp|uk|fr|de))[^\s "'))」】』]*)/g; | |
| const anchorFactory = new ElementFactory("a", [{attr: "target", value: "_blank"}]); | |
| const observer = new MutationObserver(records => records.forEach(r => setTimeout(convertTextToLink, 10, r.target))); | |
| observer.observe(document.body, {childList: true}); | |
| window.addEventListener("unload", _ => observer.disconnect()); | |
| convertTextToLink(document.body); | |
| function convertTextToLink(rootNode) { | |
| const filter = node => { | |
| // 祖先ノードの中に a, script があれば true | |
| const isAnchorOrScript = function recur(node) { | |
| const name = node.nodeName.toLowerCase(); | |
| if (node instanceof Element && ["a", "script"].includes(name)) return true; | |
| return node.parentNode ? recur(node.parentNode) : false; | |
| }(node); | |
| return isAnchorOrScript ? NodeFilter.FILTER_SKIP : NodeFilter.FILTER_ACCEPT; | |
| }; | |
| const iter = document.createNodeIterator(rootNode, NodeFilter.SHOW_TEXT, filter, false); | |
| const range = document.createRange(); | |
| let current; | |
| while ((current = iter.nextNode()) !== null) { | |
| const getPathName = url => { | |
| const pattern2 = /:\/\//g; | |
| return pattern2.exec(url) ? url.slice(pattern2.lastIndex) : url; | |
| }; | |
| let regRes, matchStack = []; | |
| // マッチがなくなるまでループしてスタック | |
| while ((regRes = pattern.exec(current.data)) !== null) { | |
| matchStack.push({ | |
| url : `http${regRes[1] || ""}://${getPathName(regRes[0])}`, | |
| start : regRes.index, | |
| end : pattern.lastIndex, | |
| }); | |
| } | |
| // 後ろから1つずつ取り出して変更を適用 | |
| while (matchStack.length) { | |
| const item = matchStack.pop(); | |
| const a = anchorFactory.create([{attr: "href", value: item.url}]); | |
| range.setStart (current, item.start); | |
| range.setEnd (current, item.end ); | |
| range.surroundContents(a); | |
| } | |
| } | |
| range.detach(); | |
| } | |
| }).call(this); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment