Skip to content

Instantly share code, notes, and snippets.

@EtienneDG
Last active January 6, 2024 14:29
Show Gist options
  • Save EtienneDG/fe32d3213d87f79e0da0f829e73ee4ab to your computer and use it in GitHub Desktop.
Save EtienneDG/fe32d3213d87f79e0da0f829e73ee4ab to your computer and use it in GitHub Desktop.
Trello syntax highlighting
// ==UserScript==
// @name Trello syntax highlighting
// @namespace http://tampermonkey.net/
// @version 2024-01-05
// @description Add some code syntax highlighting for trello cards description
// @author EtienneDG
// @match https://trello.com/*
// @icon https://www.google.com/s2/favicons?sz=64&domain=trello.com
// @grant GM_addStyle
// @grant GM_getResourceText
// @resource CSS https://cdnjs.cloudflare.com/ajax/libs/prism/1.29.0/themes/prism-solarizedlight.min.css
// @require https://cdnjs.cloudflare.com/ajax/libs/prism/1.29.0/prism.min.js
// @require https://cdnjs.cloudflare.com/ajax/libs/prism/1.29.0/components/prism-bash.min.js
// @require https://cdnjs.cloudflare.com/ajax/libs/prism/1.29.0/components/prism-javascript.min.js
// @require https://cdnjs.cloudflare.com/ajax/libs/prism/1.29.0/components/prism-python.min.js
// @require https://cdnjs.cloudflare.com/ajax/libs/prism/1.29.0/components/prism-rust.min.js
// @updateURL https://gist.githubusercontent.com/EtienneDG/fe32d3213d87f79e0da0f829e73ee4ab/raw/trello.js
// @downloadURL https://gist.githubusercontent.com/EtienneDG/fe32d3213d87f79e0da0f829e73ee4ab/raw/trello.js
// ==/UserScript==
(function() {
'use strict';
const CARD_MODAL_SELECTOR = ".window-overlay"
const DESCIPTION_SELECTOR = "div.current.markeddown.hide-on-edit.js-desc.js-show-with-desc"
// "// PRISM=foo" or "//PRISM=foo" or "# PRISM=foo" or "#PRISM=foo"
const LANG_REGEX = [/#\s{0,1}PRISM=(.*)\n/g, /\/\/\s{0,1}PRISM=(.*)\n/g]
function debounce(func, delay) {
let debounceTimer
return function() {
const context = this
const args = arguments
clearTimeout(debounceTimer)
debounceTimer = setTimeout(() => func.apply(context, args), delay)
};
};
function initPrism() {
// inject Prism's CSS
const cssAsText = GM_getResourceText("CSS")
GM_addStyle(cssAsText)
}
function getLang(text) {
for (let regex of LANG_REGEX) {
const matches = regex.exec(text)
if (Array.isArray(matches) && matches.length > 1) return matches[1]
}
return null
}
function setUpLangClass(element, lang) {
element.classList.add(`language-${lang}`)
}
// Used in MutationObserver => debounced by default to avoid multiple consecutive highlights
const highlightAll = debounce(() => {
const codeTags = document.querySelectorAll("code")
for (const node of codeTags) {
const lang = getLang(node.innerText)
setUpLangClass(node, lang)
}
// eslint-disable-next-line no-undef
Prism.highlightAll()
}, 100)
function initCardOpenObserver() {
const observer = new MutationObserver((mutations) => {
const description = document.querySelector(DESCIPTION_SELECTOR)
for (const mutation of mutations) {
for (const node of mutation.addedNodes) {
if (node.contains(description)) {
highlightAll()
}
}
}
})
observer.observe(document.querySelector(CARD_MODAL_SELECTOR), {
childList: true,
subtree: true
})
}
function initDescEditObserver() {
const observer = new MutationObserver((mutationList) => {
const description = document.querySelector(DESCIPTION_SELECTOR)
for (const mutation of mutationList) {
if (mutation.target === description) {
highlightAll()
}
}
})
const target = document.querySelector(CARD_MODAL_SELECTOR)
observer.observe(target, {attributes: true, childList: true, subtree: true });
}
document.onreadystatechange = () => {
if (document.readyState === "complete") {
initPrism()
highlightAll()
initCardOpenObserver()
initDescEditObserver()
}
}
})();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment