|
|
第24行: |
第24行: |
| $(this).find(".wikilink-popup").remove(); | | $(this).find(".wikilink-popup").remove(); |
| }); | | }); |
| })();
| |
|
| |
| /**
| |
| * 自动转换链接
| |
| * @author [[User:UnownHearn]]
| |
| * @version 1.8 (2019/03/31)
| |
| */
| |
| (function () {
| |
|
| |
| var rules = [{
| |
| // 两字母 + 一串数字
| |
| regex: /[a-zA-Z]{2}\d+/,
| |
| link: function (text) {
| |
| var base;
| |
| switch (text.slice(0, 2)) {
| |
| // niconico 主要
| |
| case "sm": case "nm": base = "https://www.nicovideo.jp/watch/"; break;
| |
| case "im": base = "https://seiga.nicovideo.jp/seiga/"; break;
| |
| // bilibili
| |
| case "av": base = "https://acg.tv/"; break;
| |
| case "cv": /*专栏*/ base = "https://www.bilibili.com/read/"; break;
| |
| case "am": /*音频*/ base = "https://www.bilibili.com/audio/"; break;
| |
| case "ml": /*~~my~~medialist*/ base = "https://www.bilibili.com/medialist/play/"; break;
| |
| // acfun
| |
| case "ac": base = "http://www.acfun.cn/v/"; break;
| |
| // niconico 其他, 参考: https://dic.nicovideo.jp/a/nico.ms
| |
| case "sg": case "mg": case "bk": /*静画*/ base = "https://seiga.nicovideo.jp/watch/"; break;
| |
| case "lv": /*生放送*/ base = "https://live.nicovideo.jp/watch/"; break;
| |
| /* l/co 略去 */
| |
| case "co": /*社区*/ base = "https://com.nicovideo.jp/community/"; break;
| |
| case "ch": /*频道*/ base = "https://ch.nicovideo.jp/channel/"; break;
| |
| case "ar": /*频道文章*/ base = "https://ch.nicovideo.jp/article/"; break;
| |
| case "nd": /*直贩*/ base = "https://chokuhan.nicovideo.jp/products/detail/"; break;
| |
| /* 市场略去 */
| |
| case "ap": /*应用*/ base = "https://app.nicovideo.jp/app/"; break;
| |
| case "jk": /*实况*/ base = "https://jk.nicovideo.jp/watch/"; break;
| |
| case "nc": /*共有*/ base = "https://www.niconicommons.jp/material/"; break;
| |
| case "nw": /*新闻*/ base = "https://news.nicovideo.jp/watch/"; break;
| |
| }
| |
| if (base !== undefined) { return base + text; }
| |
| return null;
| |
| }
| |
| }, {
| |
| // niconico mylist/ 及 user/
| |
| regex: /(mylist|user)\/\d+/,
| |
| link: "https://www.nicovideo.jp/{}"
| |
| }];
| |
|
| |
| // Returns the url the given `linkableText` represents (E.g. "sm3777" => "https://nicovideo.jp/watch/sm3777")
| |
| function generateLink(rule, linkableText) {
| |
| if (typeof rule.link === "function") {
| |
| var link = rule.link(linkableText);
| |
| if (link !== null && typeof link !== "string") {
| |
| throw "The return value of link() must be either a string or null!";
| |
| }
| |
| return link;
| |
| } else {
| |
| return rule.link.replace("{}", linkableText);
| |
| }
| |
| }
| |
|
| |
| // Creates an <a>nchor element with given url `link` and `text`
| |
| //
| |
| // the returned element has one and only one text node as its child.
| |
| function createAnchorElement(link, text) {
| |
| return $("<a/>").addClass("autolink").attr("href", link).text(text).get(0);
| |
|
| |
| // var a = document.createElement("a");
| |
| // a.className = "autolink";
| |
| // a.href = link;
| |
| // a.textContent = text;
| |
| // return a;
| |
| }
| |
|
| |
| // Tries detaching a linkable text from given `text` with given `rules`
| |
| //
| |
| // If `text` matches a rule, it will return a tuple contains:
| |
| // text before the linkable text,
| |
| // <a>nchor element that represents the linkable text (or a text node if the text is actually not linkable),
| |
| // text after the linkable text;
| |
| // Otherwise, it will return a tuple contains: "", null, and the original text.
| |
| function detachLinkableText(text, rules) {
| |
| var textBefore = "";
| |
| var node = null;
| |
| var textAfter = text;
| |
| var lastMatch = null;
| |
| for (var ruleIndex in rules) {
| |
| var rule = rules[ruleIndex];
| |
| var match = rule.regex.exec(text);
| |
| if (match != null && (lastMatch === null || match.index < lastMatch.index)) {
| |
| lastMatch = match;
| |
| textBefore = text.slice(0, match.index);
| |
|
| |
| var linkableText = match[0];
| |
| var link = generateLink(rule, linkableText);
| |
| if (link == null) {
| |
| node = document.createTextNode(linkableText);
| |
| } else {
| |
| node = createAnchorElement(link, linkableText);
| |
| }
| |
|
| |
| textAfter = text.slice(match.index + linkableText.length);
| |
| }
| |
| }
| |
| return [textBefore, node, textAfter];
| |
| }
| |
|
| |
| function main() {
| |
| var foundTextNodes = [];
| |
|
| |
| { //< put all text nodes that under `.WikiaArticle` elements but not under `a` or `.no-autolink` elements into `foundTextNodes`
| |
| var articles = document.getElementsByClassName("WikiaArticle");
| |
| var filter = (function (node) {
| |
| if (node.nodeType == 3 /* TEXT_NODE */) {
| |
| return NodeFilter.FILTER_ACCEPT;
| |
| } else if (node.nodeType != 1 /* ELEMENT_NODE */
| |
| || node.tagName.toLowerCase() == "a"
| |
| || node.classList.contains("no-autolink")) {
| |
| return NodeFilter.FILTER_REJECT;
| |
| }
| |
| return NodeFilter.FILTER_SKIP;
| |
| })
| |
| Array.prototype.forEach.call(articles, function (article) {
| |
| var walker = document.createTreeWalker(article, NodeFilter.SHOW_ALL, { acceptNode: filter });
| |
| while (walker.nextNode()) {
| |
| foundTextNodes.push(walker.currentNode);
| |
| }
| |
| })
| |
| }
| |
|
| |
| for (var nodeIndex in foundTextNodes) {
| |
| var foundTextNode = foundTextNodes[nodeIndex];
| |
| var remainText = foundTextNode.textContent;
| |
| // this array will only contain text nodes and <a>nchor element nodes that have text nodes
| |
| var nodes = [];
| |
|
| |
| while (true) {
| |
| var oldRemainText = remainText;
| |
|
| |
| var rets = detachLinkableText(remainText, rules);
| |
| var textBefore = rets[0];
| |
| var elem = rets[1];
| |
| remainText = rets[2];
| |
| if (elem === null) {
| |
| // no rule matches
| |
| break;
| |
| }
| |
| if (textBefore !== "") {
| |
| nodes.push(document.createTextNode(textBefore));
| |
| }
| |
| nodes.push(elem);
| |
|
| |
| // `detachLinkableText()` splits the `remainText` into 3 parts: `textBefore`, `elem`, and the new `remainText`.
| |
| // as long as `elem` is not null, the new `remainText` will always be shorter than the old one.
| |
| //
| |
| // this check ensures that the outer `while (true)` loop won't be an infinite loop even if `detachLinkableText()` not working properly
| |
| if (remainText.length >= oldRemainText.length) {
| |
| throw "oof";
| |
| }
| |
| }
| |
|
| |
| if (nodes.length !== 0) { //< check if need to replace the original node
| |
| nodes.push(document.createTextNode(remainText));
| |
| $(foundTextNode).replaceWith(nodes);
| |
| }
| |
| }
| |
| }
| |
|
| |
| main();
| |
|
| |
| })(); | | })(); |