Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- // ==UserScript==
- // @name Markdown Editor Enhancer
- // @namespace http://tampermonkey.net/
- // @version 1.2
- // @description Live Markdown-to-HTML preview for cr8r.gg and expressional.social
- // @author Johan, ChatGPT
- // @match *://infosec.exchange/*
- // @match *://tilde.zone/*
- // @match *://cr8r.gg/*
- // @match *://expressional.social/*
- // @icon https://www.google.com/s2/favicons?sz=64&domain=cr8r.gg
- // @grant none
- // ==/UserScript==
- (function () {
- 'use strict';
- function enhanceTextareas() {
- document.querySelectorAll('textarea.autosuggest-textarea__textarea').forEach((textarea) => {
- textarea.classList.add('mdEdit');
- });
- }
- function applyRegex(el) {
- let rawText = el.value;
- // Экранируем HTML
- let escaped = rawText
- .replace(/&/g, "&")
- .replace(/</g, "<")
- .replace(/>/g, ">");
- const paragraphs = escaped.split(/\n{2,}/).map(block => {
- // Blockquote
- if (/^(>|>)\s+/m.test(block)) {
- const quoteLines = block
- .split('\n')
- .filter(line => /^(>|>)\s+/.test(line))
- .map(line => line.replace(/^(>|>)\s+/, ''))
- .join('<br>');
- let html = applyInlineMarkdown(quoteLines);
- return `<blockquote>${html}</blockquote>`;
- }
- // Headings
- const headingMatch = block.match(/^#{1,6} /);
- if (headingMatch) {
- const level = headingMatch[0].trim().length;
- const content = block.replace(/^#{1,6} /, '').trim();
- let html = applyInlineMarkdown(content);
- return `<h${level}>${html}</h${level}>`;
- }
- // Paragraph with <br>
- let html = block.replace(/\n/g, '<br>');
- html = applyInlineMarkdown(html);
- return `<p>${html}</p>`;
- });
- const parsed = paragraphs.join('');
- let displayEl = el.nextElementSibling;
- if (!displayEl || !displayEl.classList.contains('status__content__text')) {
- displayEl = document.createElement('div');
- displayEl.classList.add('status__content__text', 'status__content__text--visible');
- displayEl.style.borderTop = "1px solid #ccc";
- displayEl.style.padding = "10px";
- displayEl.style.marginTop = "5px";
- el.insertAdjacentElement('afterend', displayEl);
- }
- displayEl.innerHTML = parsed;
- }
- function applyInlineMarkdown(text) {
- return text
- .replace(/\*\*(.*?)\*\*/g, '<strong>$1</strong>')
- .replace(/\*(.*?)\*/g, '<em>$1</em>')
- .replace(/\b_(.*?)_\b/g, '<u>$1</u>')
- .replace(/~~(.*?)~~/g, '<del>$1</del>')
- .replace(/`(.*?)`/g, '<code>$1</code>')
- .replace(/\[(.*?)\]\((.*?)\)/g, '<a href="$2" target="_blank" class="u-url status-link">$1</a>')
- .replace(/(^|\s)(https?:\/\/[^\s<]+)/g, (match, p1, url) => {
- return `${p1}<a href="${url}" target="_blank" class="u-url status-link">${url}</a>`;
- })
- .replace(/(#[\wА-Яа-я\d_]+)/g, '<span class="mention hashtag status-link">$1</span>')
- .replace(/(@[\w\d_.@]+)/g, '<span class="u-url mention status-link">$1</span>');
- }
- function setupMarkdownEditing() {
- document.querySelectorAll('.mdEdit').forEach((el) => {
- el.addEventListener('input', () => applyRegex(el));
- applyRegex(el);
- document.addEventListener('keydown', (ev) => {
- if (ev.key === 'Enter' && document.activeElement !== el) {
- el.focus();
- ev.preventDefault();
- ev.stopPropagation();
- } else if (ev.key === 'Escape' && document.activeElement === el) {
- el.blur();
- // 👉 Раскомментируй одну из строк ниже, чтобы выбрать поведение:
- // 🔽 Вариант 1: оставить текст в предпросмотре (по умолчанию — удобно для вычитки)
- // // ничего не делаем
- // 🔽 Вариант 2: очищать предпросмотр при нажатии Escape
- // const displayEl = el.nextElementSibling;
- // if (displayEl && displayEl.classList.contains('status__content__text')) {
- // displayEl.innerHTML = '';
- // }
- }
- });
- });
- }
- window.addEventListener('load', () => {
- setTimeout(() => {
- enhanceTextareas();
- setupMarkdownEditing();
- }, 5000);
- });
- })();
Advertisement
Add Comment
Please, Sign In to add comment