window.domain = 'hedgedoc.fransgenre.fr' window.urlpath = '' window.debug = false window.version = '1.10.5-7185a44448b41080ccda16339f973c76874c62d2' window.enableUploads = 'all' window.allowedUploadMimeTypes = ["image/jpeg","image/png","image/gif","image/svg+xml"] window.linkifyHeaderStyle = 'keep-case' window.DROPBOX_APP_KEY = '' window.cookiePolicy = 'lax' window.userToken = '' // fg-copy-button.js // Script qui ajoute des boutons de copie aux blocs de code HedgeDoc. // Fonction pour détecter quand un élément
est connecté au DOM.
// Nécessaire parce que HedgeDoc effectue le rendu côté client.
function onPreConnected(connectedCallback) {
const observer = new MutationObserver((mutationList) => {
for (const mutation of mutationList) {
if (mutation.type != "childList") {
// Ça ne devrait pas arriver mais au cas où.
continue;
}
for (const addedNode of mutation.addedNodes) {
// Est-ce que le nouveau nœud est bien un élément HTML ?
if (!(addedNode instanceof HTMLElement)) {
continue;
}
// Est-ce que le nouvel élément est un bloc de code ?
if (addedNode instanceof HTMLPreElement) {
connectedCallback(addedNode);
continue;
}
// Des blocs de code peuvent être contenus dans le nouvel élément.
addedNode.querySelectorAll("pre").forEach(connectedCallback);
}
}
});
// Connexion des blocs de code préexistants.
document.querySelectorAll("pre").forEach(connectedCallback);
// Observation des nouveaux blocs de code.
observer.observe(document, {
childList: true,
subtree: true,
});
}
// Ajout d’un bouton de copie à chaque bloc de code.
onPreConnected((pre) => {
// On ne s’occupe que des blocs de code du rendu Markdown.
if (!pre.closest("#doc.markdown-body")) {
return;
}
// Création du bouton.
const button = document.createElement("button");
button.classList.add("btn", "btn-primary");
button.innerText = "Copier";
button.dataset.fgCopyButton = "true";
button.fgCopyButtonTarget = pre;
// Positionnement du bouton.
const wrapper = document.createElement("div");
Object.assign(wrapper.style, {
display: "flex",
justifyContent: "end",
paddingBottom: "0.5rem",
});
wrapper.style.display = "flex";
wrapper.appendChild(button);
// Ajout du bouton au-dessus du bloc de code.
pre.insertAdjacentElement("beforebegin", wrapper);
});
// Un seul écouteur pour tous les boutons de copie.
// Ainsi pas besoin de détecter la déconnexion des boutons pour nettoyer.
document.addEventListener("click", async (event) => {
const button = event.target;
// Est-ce bien un bouton de copie ?
if (!("fgCopyButton" in button.dataset)) {
return;
}
// Récupération du bloc de code du bouton.
const pre = event.target.fgCopyButtonTarget;
if (!pre) {
return;
}
// Suppression des numéros de ligne si besoin.
const clonedPre = pre.cloneNode(true);
const gutter = clonedPre.querySelector(".gutter.linenumber");
if (gutter) {
gutter.remove();
}
// Copie du contenu textuel.
await navigator.clipboard.writeText(clonedPre.textContent);
// Indication que la copie a été effectuée.
button.classList.remove("btn-primary");
button.classList.add("btn-success");
button.innerText = "Copié !";
// Nettoyage du timeout existant si besoin.
if (button.fgCopyButtonTimeout !== undefined) {
clearTimeout(button.fgCopyButtonTimeout);
}
// Remise à zéro du bouton au bout d’un moment.
button.fgCopyButtonTimeout = setTimeout(() => {
button.classList.remove("btn-success");
button.classList.add("btn-primary");
button.innerText = "Copier";
button.fgCopyButtonTimeout = undefined;
}, 750);
});