Created
July 10, 2026 04:15
-
-
Save zhantongz/1086d2f483959dca920ff8f083c49dae to your computer and use it in GitHub Desktop.
CanLII monkey scripts
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| // ==UserScript== | |
| // @name My CanLII scripts | |
| // @namespace https://github.com/violentmonkey/scripts | |
| // @version 2.2 | |
| // @description Some useful scripts for CanLII | |
| // @author You | |
| // @match https://www.canlii.org/en/* | |
| // @match https://www.canlii.org/fr/* | |
| // @grant GM_setClipboard | |
| // @run-at document-idle | |
| // ==/UserScript== | |
| (function () { | |
| 'use strict'; | |
| function getCleanUrl() { | |
| const url = new URL(window.location.href); | |
| url.search = ''; | |
| url.hash = ''; | |
| return url.toString(); | |
| } | |
| function getCitationText() { | |
| const title = document.title || ''; | |
| let citation = title.split('|')[0].trim(); | |
| if (citation.endsWith('(CanLII)')) { | |
| citation = citation.slice(0, -'(CanLII)'.length).trim(); | |
| } | |
| return citation; | |
| } | |
| function flash(button, message, revertTo) { | |
| button.textContent = message; | |
| setTimeout(() => { | |
| button.textContent = revertTo; | |
| }, 1200); | |
| } | |
| function copyPlainText(text, button, label) { | |
| navigator.clipboard.writeText(text).then( | |
| () => flash(button, '✓', label), | |
| () => { | |
| if (typeof GM_setClipboard === 'function') { | |
| GM_setClipboard(text, 'text'); | |
| flash(button, '✓', label); | |
| } else { | |
| flash(button, '✗', label); | |
| } | |
| } | |
| ); | |
| } | |
| function copyRichLink(url, text, button, label) { | |
| const html = `<a href="${url}">${text}</a>`; | |
| if (navigator.clipboard && window.ClipboardItem) { | |
| const item = new ClipboardItem({ | |
| 'text/html': new Blob([html], { type: 'text/html' }), | |
| 'text/plain': new Blob([text], { type: 'text/plain' }), | |
| }); | |
| navigator.clipboard.write([item]).then( | |
| () => flash(button, '✓', label), | |
| () => fallbackCopy() | |
| ); | |
| } else { | |
| fallbackCopy(); | |
| } | |
| function fallbackCopy() { | |
| if (typeof GM_setClipboard === 'function') { | |
| GM_setClipboard(html, 'html'); | |
| flash(button, '✓', label); | |
| } else { | |
| flash(button, '✗', label); | |
| } | |
| } | |
| } | |
| function makeButton(id, label, bottomOffset, rightOffset, onClick) { | |
| const button = document.createElement('button'); | |
| button.id = id; | |
| button.textContent = label; | |
| button.style.position = 'fixed'; | |
| button.style.bottom = bottomOffset; | |
| button.style.right = rightOffset; | |
| button.style.zIndex = '99'; | |
| button.style.padding = '8px 12px'; | |
| button.style.fontSize = '15px'; | |
| button.style.backgroundColor = '#f9f5e6'; | |
| button.style.color = '#fff'; | |
| button.style.border = 'none'; | |
| button.style.borderRadius = '8px'; | |
| button.style.cursor = 'pointer'; | |
| button.style.boxShadow = '0 2px 2px rgba(0,0,0,0.3)'; | |
| button.style.opacity = '0.7'; | |
| button.addEventListener('mouseenter', () => (button.style.opacity = '1')); | |
| button.addEventListener('mouseleave', () => (button.style.opacity = '0.7')); | |
| button.addEventListener('click', onClick); | |
| return button; | |
| } | |
| function createButtons() { | |
| const linkButton = makeButton('copyCleanLink', '🔗', '5rem', 'calc(var(--gar-chat-width, 0px) + 2.5rem)', () => | |
| copyPlainText(getCleanUrl(), linkButton, '🔗') | |
| ); | |
| const citeButton = makeButton('copyCitationLink', '📋', '5rem', 'calc(var(--gar-chat-width, 0px) + 5rem)', () => | |
| copyRichLink(getCleanUrl(), getCitationText(), citeButton, '📋') | |
| ); | |
| document.body.appendChild(linkButton); | |
| document.body.appendChild(citeButton); | |
| } | |
| function getPseudoMcGillText() { | |
| const parts = (document.title || '').split('|'); | |
| const firstPart = (parts[0] || '').trim(); | |
| const caseStyle = (parts[1] || '').trim().replace(/\./g, ''); | |
| return `<i>${caseStyle}</i>, ${firstPart}`; | |
| } | |
| function insertPseudoMcGillRow() { | |
| const container = document.getElementById('metas-container'); | |
| if (!container) return; | |
| // Avoid inserting twice if the script re-runs | |
| if (container.querySelector('.pseudo-mcgill-row')) return; | |
| const citationLabel = container.querySelector('.documentMeta-citation'); | |
| const citationRow = citationLabel ? citationLabel.closest('.row') : null; | |
| if (!citationRow) return; | |
| const pseudoMcGill = getPseudoMcGillText(); | |
| const rowHtml = ` | |
| <div class="row py-1 pseudo-mcgill-row"> | |
| <div class="col-3">Pseudo-McGill:</div> | |
| <div class="col">${pseudoMcGill}</div> | |
| </div> | |
| `; | |
| citationRow.insertAdjacentHTML('afterend', rowHtml); | |
| } | |
| function init() { | |
| createButtons(); | |
| insertPseudoMcGillRow(); | |
| } | |
| if (document.body) { | |
| init(); | |
| } else { | |
| window.addEventListener('DOMContentLoaded', init); | |
| } | |
| })(); |
Author
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Further development: https://codeberg.org/xngtng/canlii-monkey