// ==UserScript==
// @name Bitbucket Collapse Markdown
// @version 0.1.1
// @description A userscript that collapses markdown headers
// @license MIT
// @author Rob Garrison
// @namespace https://github.com/Mottie
// @include https://bitbucket.org/*
// @run-at document-idle
// @grant GM_addStyle
// @grant GM_getValue
// @grant GM_setValue
// @grant GM_registerMenuCommand
// @icon https://bitbucket.org/mottie/bitbucket-userscripts/raw/HEAD/images/bitbucket.svg
// ==/UserScript==
(() => {
"use strict";
const defaultColors = [
// palette generated by http://tools.medialab.sciences-po.fr/iwanthue/
// (colorblind friendly, soft)
"#6778d0", "#ac9c3d", "#b94a73", "#56ae6c", "#9750a1", "#ba543d"
],
headers = "H1 H2 H3 H4 H5 H6".split(" "),
collapsed = "bbcm-collapsed",
arrowColors = document.createElement("style");
let startCollapsed = GM_getValue("bbcm-collapsed", false),
colors = GM_getValue("bbcm-colors", defaultColors);
GM_addStyle(`
.wiki-content h1, .wiki-content h2, .wiki-content h3,
.wiki-content h4, .wiki-content h5, .wiki-content h6 {
position:relative;
padding-right:.8em;
cursor:pointer;
}
.wiki-content h1:after, .wiki-content h2:after, .wiki-content h3:after,
.wiki-content h4:after, .wiki-content h5:after, .wiki-content h6:after {
display:inline-block;
position:absolute;
right:0;
top:calc(50% - .5em);
font-size:.8em;
content:"\u25bc";
}
.wiki-content .${collapsed}:after {
transform: rotate(90deg);
}
.bbcm-hidden {
display:none !important;
}
`);
function addColors() {
arrowColors.textContent = `
.wiki-content h1:after { color:${colors[0]} }
.wiki-content h2:after { color:${colors[1]} }
.wiki-content h3:after { color:${colors[2]} }
.wiki-content h4:after { color:${colors[3]} }
.wiki-content h5:after { color:${colors[4]} }
.wiki-content h6:after { color:${colors[5]} }
`;
}
function toggle(el, shifted) {
if (el) {
el.classList.toggle(collapsed);
let els;
const name = el.nodeName || "",
level = parseInt(name.replace(/[^\d]/, ""), 10),
isCollapsed = el.classList.contains(collapsed);
if (shifted) {
// collapse all same level anchors
els = $$(`.wiki-content ${name}`);
for (el of els) {
nextHeader(el, level, isCollapsed);
}
} else {
nextHeader(el, level, isCollapsed);
}
removeSelection();
}
}
function nextHeader(el, level, isCollapsed) {
el.classList.toggle(collapsed, isCollapsed);
const selector = headers.slice(0, level).join(","),
name = [collapsed, "bbcm-hidden"],
els = [];
el = el.nextElementSibling;
while (el && !el.matches(selector)) {
els[els.length] = el;
el = el.nextElementSibling;
}
if (els.length) {
if (isCollapsed) {
els.forEach(el => {
el.classList.add("bbcm-hidden");
});
} else {
els.forEach(el => {
el.classList.remove(...name);
});
}
}
}
function removeSelection() {
// remove text selection - https://stackoverflow.com/a/3171348/145346
const sel = window.getSelection ?
window.getSelection() :
document.selection;
if (sel) {
if (sel.removeAllRanges) {
sel.removeAllRanges();
} else if (sel.empty) {
sel.empty();
}
}
}
function addBinding() {
document.addEventListener("click", event => {
const target = event.target;
if (target && headers.indexOf(target.nodeName || "") > -1) {
// make sure the header is inside of markdown
if (closest(".wiki-content", target)) {
toggle(target, event.shiftKey);
}
}
});
}
function checkColors() {
if (!colors || colors.length !== 6) {
colors = [].concat(defaultColors);
}
}
function init() {
document.querySelector("head").appendChild(arrowColors);
checkColors();
addColors();
addBinding();
}
function $$(selectors, el) {
return Array.from((el || document).querySelectorAll(selectors));
}
function closest(selector, el) {
while (el && el.nodeType === 1) {
if (el.matches(selector)) {
return el;
}
el = el.parentNode;
}
return null;
}
// Add GM options
GM_registerMenuCommand("Set Bitbucket collapse markdown state", () => {
const val = prompt(
"Set initial state to (c)ollapsed or (e)xpanded (first letter only):",
startCollapsed ? "collapsed" : "expanded"
);
if (val !== null) {
startCollapsed = /^c/i.test(val);
GM_setValue("bbcm-collapsed", startCollapsed);
console.log(
`Bitbucket Collapse Markdown: Headers will ` +
`${startCollapsed ? "be" : "not be"} initially collapsed`
);
}
});
GM_registerMenuCommand("Set Bitbucket collapse markdown colors", () => {
let val = prompt("Set header arrow colors:", JSON.stringify(colors));
if (val !== null) {
// allow pasting in a JSON format
try {
val = JSON.parse(val);
if (val && val.length === 6) {
colors = val;
GM_setValue("bbcm-colors", colors);
console.log("Bitbucket Collapse Markdown: colors set to", colors);
addColors();
return;
}
console.error(
"Bitbucket Collapse Markdown: invalid color definition (6 colors)",
val
);
// reset colors to default (in case colors variable is corrupted)
checkColors();
} catch (err) {
console.error("Bitbucket Collapse Markdown: invalid JSON");
}
}
});
init();
})();