let PLUGIN_INFO = <VimperatorPlugin> <name>appendAnchor</name> <description>append anchors to texts look like url.</description> <description lang="ja">リンク中の URL っぽいテキストにアンカーをつける。</description> <version>0.4.7</version> <author>SAKAI, Kazuaki</author> <minVersion>2.0pre</minVersion> <maxVersion>2.4</maxVersion> <detail><![CDATA[ == Commands == :anc: Append anchors. == GlobalVariables == g:auto_append_anchor: Execute ":anc" automatically when Vimperator shows the hints. g:auto_append_anchor_once: Just first once. ]]></detail> <detail lang="ja"><![CDATA[ == Commands == :anc: アンカーを付加する。 == GlobalVariables == g:auto_append_anchor: Vimperator がヒントを表示するときに自動的に ":anc" する。 g:auto_append_anchor_once: 最初の一回だけ。 ]]></detail> </VimperatorPlugin>; (function(){ // settings --- // "ACEILMPRSTXY" is result of below code. // Array.prototype.uniq = function() this.reduceRight( function (a, b) (a[0] === b || a.unshift(b), a), []); // [ 'TITLE', 'STYLE', 'SCRIPT', 'TEXTAREA', 'XMP', 'A', ].join('').split('').sort().uniq().join(''); const xpathQueryPlainText = '/descendant::*[not(contains(" TITLE STYLE SCRIPT TEXTAREA XMP A ", concat(" ", translate(local-name(), "aceilmprstxy", "ACEILMPRSTXY"), " ")))]/child::text()'; const regexpLikeURL = new RegExp("(h?ttps?|ftp):/+([a-zA-Z0-9][-_.!~*'()a-zA-Z0-9;/?:@&=+$,%#]+[-_~*(a-zA-Z0-9;/?@&=+$%#])"); // process global variable if (stringToBoolean(liberator.globalVariables.auto_append_anchor, false)) { let originalHintsShow = liberator.modules.hints.show; let once = stringToBoolean(liberator.globalVariables.auto_append_anchor_once, true); hints.show = function () { if (!(once && content.document.anchor_appended)) { content.document.anchor_appended = true; liberator.execute('anc'); } originalHintsShow.apply(this, arguments); }; } // register command liberator.modules.commands.addUserCommand(['anc'], 'append anchors to texts look like url', function(arg) { function append() { let result = 0; const doc = window.content.document; const range = doc.createRange(); let nodes = util.evaluateXPath(xpathQueryPlainText); for (let node in nodes) { while (node) { range.selectNode(node) // search string like URL let start = range.toString().search(regexpLikeURL); // go to next node when there is nothing look like URL in current node if (!(start > -1)) break; result++; // build URL let scheme = RegExp.$1, host = RegExp.$2, lastMatch = RegExp.lastMatch; if (/^ttps?$/.test(scheme)) scheme = 'h' + scheme; let href = scheme + '://' + host; // reset range range.setStart(node, start); range.setEnd(node, start + lastMatch.length); // build anchor element let anchor = doc.createElement('a'); anchor.setAttribute('href', href); range.surroundContents(anchor); // insert range.insertNode(anchor); // iterate node = node.nextSibling.nextSibling.nextSibling; } } range.detach(); return result; } for (let i = 0; i < 20 && append(); i++) ; }, {}, true ); // stuff function function stringToBoolean(str, defaultValue) { if (typeof str === 'number') str = str.toString(); return !str ? (defaultValue ? true : false) : str.toLowerCase() === 'false' ? false : /^\d+$/.test(str) ? (parseInt(str) ? true : false) : true; } })();