更多操作
(以“→此处的JavaScript将加载于所有用户每一个页面。: →* * 自动转换链接 * @author [[User:UnownHearn]] * @version 1.4 (2019/03/18): (funct...”为内容创建页面) |
(strings that detached from the raw input are escaped (these strings are set with jQuery's .text() method)) |
||
第2行: | 第2行: | ||
/** | /** | ||
* 自动转换链接 | * 自动转换链接 - automatically convert texts to links | ||
* @author [[User:UnownHearn]] | * @author [[User:UnownHearn]] | ||
* @version 1. | * @version 1.8 (2019/03/31) | ||
*/ | */ | ||
(function () { | (function () { | ||
var rules = [{ | var rules = [{ | ||
// | // 两字母 + 一串数字 | ||
regex: / | regex: /[a-zA-Z]{2}\d+/, | ||
link: function (text) { | link: function (text) { | ||
var base; | |||
switch (text.slice(0, 2)) { | switch (text.slice(0, 2)) { | ||
case "sm": case "nm": | // niconico 主要 | ||
case "sm": case "nm": base = "https://www.nicovideo.jp/watch/"; break; | |||
case "im": | case "im": base = "https://seiga.nicovideo.jp/seiga/"; break; | ||
// bilibili | |||
case "av": | case "av": base = "https://acg.tv/"; break; | ||
case "cv": /*专栏*/ base = "https://www.bilibili.com/read/"; break; | |||
case "ac": | 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+/ | regex: /(mylist|user)\/\d+/, | ||
link: "https://www.nicovideo.jp/{}" | 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` | |||
var node = | // | ||
var | // 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) { | for (var ruleIndex in rules) { | ||
var rule = rules[ruleIndex]; | var rule = rules[ruleIndex]; | ||
var match = rule.regex.exec(text); | |||
var link; | if (match != null && (lastMatch === null || match.index < lastMatch.index)) { | ||
if ( | 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 { | } else { | ||
link = | 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(); | |||
})(); | })(); |
2019年4月4日 (四) 21:43的版本
/* 此处的JavaScript将加载于所有用户每一个页面。 */
/**
* 自动转换链接 - automatically convert texts to links
* @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();
})();