Markdown and BB-codes for discuss.moe

Enable ability mark text in VK.

您需要先安装一个扩展,例如 篡改猴Greasemonkey暴力猴,之后才能安装此脚本。

您需要先安装一个扩展,例如 篡改猴暴力猴,之后才能安装此脚本。

您需要先安装一个扩展,例如 篡改猴暴力猴,之后才能安装此脚本。

您需要先安装一个扩展,例如 篡改猴Userscripts ,之后才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。

您需要先安装用户脚本管理器扩展后才能安装此脚本。

(我已经安装了用户脚本管理器,让我安装!)

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

(我已经安装了用户样式管理器,让我安装!)

// ==UserScript==
// @name			Markdown and BB-codes for discuss.moe
// @name:ru			Разметка и BB-коды для discuss.moe
// @namespace		https://discuss.moe/users/xcang
// @version			2016.09.12.03.12
// @description		Enable ability mark text in VK.
// @description:ru	Включает возможность размечать и оформлять текст в контакте.
// @author			XCanG
// @match			*.discuss.moe/*
// @grant			none
// @icon			https://upload.wikimedia.org/wikipedia/commons/thumb/4/48/Markdown-mark.svg/208px-Markdown-mark.svg.png
// ==/UserScript==

function repl(str) {
	// As Onebox have bugs with invalid code, here is a fix:
	if (/\[hr\]/i.test(str) || /(<br\s*\/?>|[\r\n]+)(((?:\*(?:(?!<br\s*\/?>)(?!\s*\[[ x]\])[^\r\n\|])+)(?:<br\s*\/?>)?){2,})<br\s*\/?>/i.test(str) || /\[list\].*?\[\*\]((?:(?!\[list\])(?!\[\/list])[^])+)\[\/list\]/i.test(str) || /~~([^\r\n~]+)~~/i.test(str) || /\[o\](.+?)\[\/o\]/i.test(str) || /\[sub\](.+?)\[\/sub\]/i.test(str) || /\[sup\](.+?)\[\/sup\]/i.test(str) || /{(?:#([\w\-]+)|<[\w\s="'\\//;:?&%]+>#([\w\-]+)<\/\w+>)}/i.test(str) || /\[c(?:enter)?\]([^]+?)\[\/c(?:enter)?\]/i.test(str) || /\[r(?:ight)?\](.+?)\[\/r(?:ight)?\]/i.test(str) || /\[j(?:ustify)?\](.+?)\[\/j(?:ustify)?\]/i.test(str) || /\%\%(.+?)\%\%/i.test(str) || /%((?:(?!(?:\/.*)?[0-9A-F]{2})[^\r\n])+?)%(?![0-9A-F]{2})/.test(str) || /(<a[\w\s]+href=")#([\w\-]+)"([\w\s="':;,\.\\\/#]*>(?:(?!<\/a>)[^])+<\/a>)/i.test(str) || /(<(p|br)\s*\/?>)\s*((?:- \[(?:x| )\](?:(?!<\/?p>[^-])(?!<br\s*\/?>[^-])[^])+)+)(<\/?\2\s*\/?>|)/i.test(str) || /(<(p|br)\s*\/?>)([\w\s.,;:\-_+=!@~`%^&()\\|]+)<br\s*\/?>[\-—\s|]+<br\s*\/?>((?:(?:<br\s*\/?>)?[\w\s.,;:\-_+=!@~`%^&()\\]+(?:\|[\w\s.,;:\-_+=!@~`%^&()\\]+)+)+)(<\/?\2\s*\/?>|)/i.test(str) || /\[y(?:ou)?t(?:ube)?\](?:([\w.:\/?=&\/]+)|<a[\w\s.,:;\/?=&%"'>]+https?:\/\/(?:www.)?youtu\.?be(?:.com)?\/(?:watch[\w?&=]*[?&]v=)?(\w+)(?:&\w*)?)\[\/y(?:ou)?t(?:ube)?(?:<\/a>)?\](?:<\/a>)?/i.test(str) || /\[font="?([\w\s]+)"?\]([^]+?)\[\/font\]/i.test(str) || /\[(?:glow|shadow)="?([\w\s]+)[, ]?([\w\s]+)[, ]?([\w\s]+)[, ]?([\w\s]+)"?\]([^]+?)\[\/(?:glow|shadow)\]/i.test(str) || /\[(raw|html)\]((?:(?!\[raw\])(?!\[html\])(?!\[\/raw\])(?!\[\/html\])[^])+)\[\/\1\]/i.test(str) || /(?:\\r)?\\n/.test(str)) {
		//console.time("Regex work");
		str = str.replace(/\[hr\]/ig, '<hr />'); // [hr] line break
		str = str.replace(/(<br\s*\/?>|[\r\n]+)(((?:\*(?:(?!<br\s*\/?>)(?!\s*\[[ x]\])[^\r\n\|])+)(?:<br\s*\/?>)?){2,})<br\s*\/?>/ig, '$1<ul class="list"><li>$2</li></ul>');
		while (/(<li>|<br\s*\/?>\*\s?)(?:\*\s?)?((?:(?!<\/li>)(?!<li>)(?!<\/ul>)(?!\s*\[[ x]\])[^\r\n|])+?)<br\s*\/?>(?!<\/li>)\*\s?((?!\s*\[[ x]\]).)/im.test(str)) str = str.replace(/(<li>|<br\s*\/?>\*\s?)(?:\*\s?)?((?:(?!<\/li>)(?!<li>)(?!<\/ul>)(?!\s*\[[ x]\])[^\r\n|])+?)<br\s*\/?>(?!<\/li>)\*\s?((?!\s*\[[ x]\]).)/ig, '$1$2</li><li>$3');
		str = str.replace(/\[list\].*?\[\*\]((?:(?!\[list\])(?!\[\/list])[^])+)\[\/list\]/ig, '<ul class="list"><li>$1</li></ul>').replace(/(?:<br\s*\/?>\s*)?\[\*\]\s*/ig, '</li><li>').replace(/<br\s*\/?><\/li>/ig, '</li>').replace(/(<br\s*\/?>)((?:(?:\d+\.(?:(?!<br\s*\/?>)[^\r\n])*)(?:<br\s*\/?>)?)+)(<br\s*\/?>)/ig, '$1<ol class="list"><li>$2</li></ol>$3').replace(/li>\s?\d+\.\s*/ig, 'li>').replace(/\[list\].*?\[\d+\]((?:(?!\[list\]|\[\/list])[^])*)\[\/list\]/ig, '<ol class="list"><li>$1</li></ol>').replace(/(?:<br\s*\/?>\s*)?\[\d+\]\s*/ig, '</li><li>'); // * items or - items or 1. item1 2. item2
		while (/(<li>(?:(?!<li>)(?!<\/ul>)(?!<\/ol>)[^\r\n\[\]])+?)<br/im.test(str)) str = str.replace(/(<li>(?:(?!<li>)(?!<\/ul>)(?!<\/ol>)[^\r\n\[\]])+?)<br/ig, '$1</li><li').replace(/li>\s?\d+\.\s*/ig, 'li>').replace(/<li><\/li>/ig, '');
		//str = str.replace(/\*\*([^\r\n\[\]]+?)\*\*/ig, '<strong class="bold">$1</strong>').replace(/\[b\](.+?)\[\/b\]/ig, '<strong class="bold">$1</strong>'); // **bold** or [b]bold[/b]
		str = str.replace(/~~([^\r\n~]+)~~/ig, '<span class="strikeout">$1</span>'); // ~~strike~~
		//str = str.replace(/__([^\r\n_]+?)__/ig, '<span class="underline">$1</span>').replace(/\[u\](.+?)\[\/u\]/ig, '<span class="underline">$1</span>'); // __underline__
		str = str.replace(/\[o\](.+?)\[\/o\]/ig, '<span class="overline">$1</span>'); // [o]overline[/o]
		str = str.replace(/\[sub\](.+?)\[\/sub\]/ig, '<span class="sub">$1</span>'); // [sub]subtitle[/sub]
		str = str.replace(/\[sup\](.+?)\[\/sup\]/ig, '<span class="sup">$1</span>'); // [sup]superscript[/sup]
		str = str.replace(/{(?:#([\w\-]+)|<[\w\s="'\\//;:?&%]+>#([\w\-]+)<\/\w+>)}/ig, '<span class="anchor" id="$1$2"></span>'); // {#anchor}
		str = str.replace(/\[c(?:enter)?\]([^]+?)\[\/c(?:enter)?\]/ig, '<span class="centering">$1</span>'); // [c]center[/c]
		str = str.replace(/\[r(?:ight)?\](.+?)\[\/r(?:ight)?\]/ig, '<span class="right">$1</span>'); // [r]right[/r]
		str = str.replace(/\[j(?:ustify)?\](.+?)\[\/j(?:ustify)?\]/ig, '<span class="justify">$1</span>'); // [j]justify[/j]
		str = str.replace(/\%\%(.+?)\%\%/ig, '<span class="spoiler" style="color: transparent;" onclick="this.className = ~className.indexOf(\'spoiler\') ? className.replace(\'spoiler\', \'unspoil\') : className.replace(\'unspoil\', \'spoiler\');">$1</span>').replace(/<span class="spoiled" style="background-color: transparent; color: transparent; text-shadow: 0px 0px 10px gray; -moz-user-select: none;">/ig, '<span class="spoiler" style="color: transparent;" onclick="this.className = ~className.indexOf(\'spoiler\') ? className.replace(\'spoiler\', \'unspoil\') : className.replace(\'unspoil\', \'spoiler\');">'); // %%spoiler%%
		str = str.replace(/%((?:(?!(?:\/.*)?[0-9A-F]{2})[^\r\n])+?)%(?![0-9A-F]{2})/g, '<span class="marked">$1</span>'); // %mark%
		//str = str.replace(/([^!])\[([\w\s\\/#?!^*()]+?)\]\((?:<a[\w\s]+(?:title|href)=(?:"|')(?:\/feed\?[\w&=;]+q=)?((?:(?!<\/a>)[^"'\[\]<>])+)(?:"|')(?:(?!<\/a>)[^\[\]])+<\/a>|((?:\/|\.|#|\w)[^()]*))\)/ig, '$1<a class="link" title="$2" href="$3$4" target="_blank">$2</a>').replace(/href="%23([\w\-]+)?"(?: target="_blank")?/ig, 'href="javascript:;" onclick="document.getElementById(\'$1\').scrollIntoView(); window.history.pushState(\'undefined\', \'Title\', \'#$1\');"'); // [Description](url) //.replace(/href="%23([\w\-]*)"(?: target="_blank")?/ig, 'href="#$1" target="_self" onclick="location.hash = \'#$1\'"');
		str = str.replace(/(<a[\w\s]+href=")#([\w\-]+)"([\w\s="':;,\.\\\/#]*>(?:(?!<\/a>)[^])+<\/a>)/ig, '<span class="anchorlink" onclick="document.getElementById(\'$2\').scrollIntoView(); window.history.pushState(\'undefined\', \'Title\', \'#$2\'); return false;">#$2</span>'); //'$1#$2" target="_self" onclick="return toanchor(\'$2\');"$3');
		str = str.replace(/(<(p|br)\s*\/?>)\s*((?:- \[(?:x| )\](?:(?!<\/?p>[^-])(?!<br\s*\/?>[^-])[^])+)+)(<\/?\2\s*\/?>|)/ig, '$1<div class="tasks"><br>$3</label></div>$4').replace(/<br\s*\/?>\s*(?:- \[(x| )\])/ig, '</label><label><input type="checkbox" name="tasks" disabled$1/>').replace(/(<input[\w ="']+)x\/>/ig, '$1 checked />').replace(/<div class="tasks"><\/label>/ig, '<div class="tasks">').replace(/<br\s*\/?>(<div class="tasks">(?:(?!<\/div>)[^])+<\/div>)(?:<br\s*\/?>)?/ig, '$1'); // - [x] Task compleated or - [ ] Task incompleated list
		str = str.replace(/(<(p|br)\s*\/?>)([\w\s.,;:\-_+=!@~`%^&()\\|]+)<br\s*\/?>[\-—\s|]+<br\s*\/?>((?:(?:<br\s*\/?>)?[\w\s.,;:\-_+=!@~`%^&()\\]+(?:\|[\w\s.,;:\-_+=!@~`%^&()\\]+)+)+)(<\/?\2\s*\/?>|)/ig, '$1<table class="mdtable"><thead><tr><th>$3</thead><tbody><tr><td>$4</tbody></table>$5').replace(/(<thead><tr>[^|]+?)(?:\s*\|\s*)((?:(?!<\/table>)[^])+?<\/thead>)/ig, '$1<th>$2').replace(/(<tbody><tr><td>[^|]+?)(?:\s*\|\s*)((?:(?!<\/table>)[^])+?<\/tbody>)/ig, '$1<td>$2').replace(/(<tbody>(?:(?!<\/table>)[^])+?)(?:\s*<br\s*\/?>\s*)((?:(?!<\/table>)[^])+?<\/tbody>)/ig, '$1<tr><td>$2'); //table: Write headers, separate by |, when after header write some ---, | as separator and --- again for fill, when other new lines separated by | will be choosed as cells. Ex.: Github table
		while (/(<thead><tr>[^|]+?)(?:\s*\|\s*)((?:(?!<\/table>)[^])+?<\/thead>)/im.test(str)) str = str.replace(/(<thead><tr>[^|]+?)(?:\s*\|\s*)((?:(?!<\/table>)[^])+?<\/thead>)/ig, '$1<th>$2');
		while (/(<tbody><tr><td>[^|]+?)(?:\s*\|\s*)((?:(?!<\/table>)[^])+?<\/tbody>)/im.test(str)) str = str.replace(/(<tbody><tr><td>[^|]+?)(?:\s*\|\s*)((?:(?!<\/table>)[^])+?<\/tbody>)/ig, '$1<td>$2');
		while (/(<tbody>(?:(?!<\/table>)[^])+?)(?:\s*<br\s*\/?>\s*)((?:(?!<\/table>)[^])+?<\/tbody>)/im.test(str)) str = str.replace(/(<tbody>(?:(?!<\/table>)[^])+?)(?:\s*<br\s*\/?>\s*)((?:(?!<\/table>)[^])+?<\/tbody>)/ig, '$1<tr><td>$2');
		// <iframe width="640" height="360" src="https://www.youtube.com/embed/00000000000?rel=0" frameborder="0" allowfullscreen></iframe>
		str = str.replace(/\[y(?:ou)?t(?:ube)?\](?:([\w.:\/?=&\/]+)|<a[\w\s.,:;\/?=&%"'>]+https?:\/\/(?:www.)?youtu\.?be(?:.com)?\/(?:watch[\w?&=]*[?&]v=)?(\w+)(?:&\w*)?)\[\/y(?:ou)?t(?:ube)?(?:<\/a>)?\](?:<\/a>)?/ig, '<iframe class="ytv" width="690" height="388" src="https://www.youtube.com/embed/$1$2?rel=0" frameborder="0" allowfullscreen></iframe>'); //.replace(/\\\\\\(?:https?)?:?\/{0,2}[\w\.\/?=]*([\w]{11})[\w\&=]*\/\/\//ig, '$1'); // [yt]video link[/yt]
		str = str.replace(/\[font="?([\w\s]+)"?\]([^]+?)\[\/font\]/ig, '<span class="customfont" style=\'font-family: "$1";\'>$2</span>'); // [font=Arial]text[/font], also may be [font="Lucide Consoles"]text[/font]
		str = str.replace(/\[(?:glow|shadow)="?([\w\s]+)[, ]?([\w\s]+)[, ]?([\w\s]+)[, ]?([\w\s]+)"?\]([^]+?)\[\/(?:glow|shadow)\]/ig, '<span class="shadow" style=\'text-shadow: $1px $2px $3px $4;\'>$5</span>'); // [glow=0,0,8,red]text[/glow] or [shadow=1,1,2,white]text[/shadow]
		while (/\[(raw|html)\](?:(?!\[raw\])(?!\[html\])(?!\[\/raw\])(?!\[\/html\])[^])*&lt;(?:(?!\[raw\])(?!\[html\])(?!\[\/raw\])(?!\[\/html\])[^])*\[\/\1\]/ig.test(str)) str = str.replace(/(\[(raw|html)\](?:(?!\[raw\])(?!\[html\])(?!\[\/raw\])(?!\[\/html\])[^])*)&lt;((?:(?!\[raw\])(?!\[html\])(?!\[\/raw\])(?!\[\/html\])[^])*\[\/\2\])/ig, '$1<$3').replace(/(\[(raw|html)\](?:(?!\[raw\])(?!\[html\])(?!\[\/raw\])(?!\[\/html\])[^])*)&gt;((?:(?!\[raw\])(?!\[html\])(?!\[\/raw\])(?!\[\/html\])[^])*\[\/\2\])/ig, '$1>$3');
		str = str.replace(/\[(raw|html)\]((?:(?!\[raw\])(?!\[html\])(?!\[\/raw\])(?!\[\/html\])[^])+)\[\/\1\]/ig, '$2');
		str = str.replace(/(?:\\r)?\\n/g, '<br />'); // \n or \r\n replace to newline char
		str = str.replace(/(<span class="centering"(?:(?!<span class="centering")[^])+<table class="mdtable")((?:(?!<\/table>)[^])+<\/table>(?:(?!<span class="centering")[^])*?<\/span>)/ig, '$1 style="display: table; margin: 0 auto;"$2');
		str = str.replace(/<span( class="spoiler"(?:(?!<\/span>)(?!<\/p>)[^])+?<(?:div|table|p)[^]*?)<\/span>/ig, '<div style="display: inline-block;"$1</div>');
		//console.timeEnd("Regex work");
		return str;
	} else {
		return false;
	}
}

function apply_md() {
	let skip = false;
	for (let i = 0; i < md_el.length; i++) {
		skip = false;
		for (let j = 0; j < used.length; j++) {
			if (md_el[i] == used[j]) {
				skip = true;
			}
		}
		if (!skip) {
			let fix = repl(md_el[i].innerHTML); // Onebox fix
			if (fix) md_el[i].innerHTML = fix;
			used = used.concat(md_el[i]);
		}
	}
}

function upd_md_el() {
	//console.time("Update time");
	md_el = Array.prototype.slice.call(document.getElementsByClassName('cooked'), 0).concat(Array.prototype.slice.call(document.getElementsByClassName('expanded-quote'), 0));
	apply_md();
	//console.timeEnd("Update time");
}

var md_el = [], used = [];
var html_doc = document.getElementsByTagName('head').item(0);
var css = document.createElement('style');
css.setAttribute('type', 'text/css');
css.innerHTML = '.strikeout { text-decoration: line-through; } .underline { text-decoration: underline; } .overline { text-decoration: overline; } .sub { vertical-align: sub; font-size: 12px; } .sup { vertical-align: super; font-size: 12px; } .anchor { position: relative; bottom: 63px; display: flex; } .centering { display: block; text-align: center; } .centering table { margin: 0 auto; text-align: left; } .right { display: block; text-align: right; } .justify { display: block; text-align: justify; } .cooked > pre > code, .quote > blockquote { max-height: 310px; text-overflow: ellipsis; white-space: pre-wrap; word-wrap: break-word; overflow-x: hidden; overflow-y: auto; } .spoiler { background-color: #fff; color: transparent; filter: blur(12px); -webkit-filter: blur(12px); margin-left: 8px; margin-right: 8px; } .spoiler:focus, .spoiler:active, .unspoil { background-color: transparent; color: #ffffff !important; filter: blur(0px); -webkit-filter: blur(0px); text-shadow: 1px 1px 1px gray; margin-left: 8px; margin-right: 8px; } .marked { background: #564146; padding: 1px; } .marked:hover { font-weight: bold; } .tasks { display: block; } .tasks > label { display: block; } .tasks > label > input { vertical-align: middle; cursor: alias; } .mdtable { box-shadow: 0 1px 0 0 #d7d8db, 0 0 0 1px #e3e4e8; margin: 2px; border-collapse: collapse; } .mdtable > thead > tr > th, .mdtable > tbody > tr > td { border: 1px solid #d7d8db; padding: 2px; word-break: break-all; word-wrap: break-word; } .mdtable > thead > tr > th { background-color: #12110F; text-align: center; } .mdtable > tbody > tr > td { text-align: justify; } .anchorlink { text-decoration: none; color: #1E9AE0; cursor: pointer; } .anchorlink:hover { text-decoration: underline; }';
html_doc.appendChild(css);
setInterval(upd_md_el, 1000);