1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
|
// @license magnet:?xt=urn:btih:1f739d935676111cfff4b4693e3816e664797050&dn=gpl-3.0.txt GPL-v3-or-Later
// Copyright (C) 2026 Florian Fischer
// Get the id to this course's glossary activity
const activityAutoLinkDiv = document.getElementById("glossary-autolink");
const activityLinkNode = activityAutoLinkDiv.querySelector("a.autolink");
const glossaryUrl = activityLinkNode.href;
const glossaryId = (/id=(\d+)/).exec(glossaryUrl)[1];
// Remove the dummy autolink
activityLinkNode.remove();
class GlossaryPost {
constructor(eid, concept) {
this.eid = eid;
this.concept = concept;
}
}
function autoLinkHTML(post) {
const a = document.createElement("a");
a.classList.add("glossary", "autolink", "concept", `glossaryid${post.eid}`);
a.dataset.entryid=post.eid;
a.href = `/mod/glossary/showentry.php?eid=${post.eid}&displayformat=dictionary`;
a.innerHTML = post.concept;
return a.outerHTML;
}
function replaceAllText(node, pattern, newHTML) {
var walker = document.createTreeWalker(node, NodeFilter.SHOW_TEXT);
const found = [];
while (walker.nextNode()) {
const node = walker.currentNode;
if (node.nodeValue.trim() && !(["SCRIPT", "STYLE"]).includes(node.parentNode.nodeName) ) {
if (node.nodeValue.includes(pattern))
found.push(node.parentNode);
}
}
for (const node of found) {
node.innerHTML = node.innerHTML.replace(pattern, newHTML);
}
}
const termsAlreadyAdded = new Map();
async function autoLink() {
const glRes = await fetch(glossaryUrl + "&hook=ALL");
const glDoc = new DOMParser().parseFromString(await glRes.text(), "text/html");
glDoc.querySelectorAll(".glossarypost.dictionary").forEach((post) => {
const concept = post.querySelector(".entry > .concept").innerText;
const editurl = post.querySelector('.commands > a.icon[title*="Bearbeiten"]').href;
const eid = (/&id=(\d+)/).exec(editurl)[1];
termsAlreadyAdded.set(concept, new GlossaryPost(eid, concept));
});
const page = document.querySelector('div[role="main"]');
for (const post of termsAlreadyAdded.values()) {
replaceAllText(page, post.concept, autoLinkHTML(post));
}
}
async function addGlossaryItems() {
const itemsToAdd = document.querySelectorAll(".glossary-item-toadd");
for (const item of itemsToAdd) {
if (termsAlreadyAdded.has(item.dataset.term)) {
item.classList.remove("glossary-item-toadd");
continue;
}
item.addEventListener("click", async function() {
const editFormData = new FormData();
editFormData.set("cmid", glossaryId);
editFormData.set("sesskey", M.cfg.sesskey);
const editRes = await fetch("/mod/glossary/edit.php", {
method: "POST",
body: editFormData,
});
const editDoc = new DOMParser().parseFromString(await editRes.text(), "text/html");
const form = editDoc.querySelector('div[role="main"] > form');
const formData = new FormData(form);
formData.set("concept", item.dataset.term ? item.dataset.term: item.textContent);
formData.set("definition_editor[text]", item.dataset.def);
formData.set("usedynalink", "1");
formData.set("submitbutton", "Änderung speichern");
const addRes = await fetch("/mod/glossary/edit.php", {
method: "POST",
body: formData,
});
const showAddedPostUrl = addRes.url;
item.classList.remove("glossary-item-toadd");
const eid = (/hook=(\d+)/).exec(showAddedPostUrl)[1];
const post = new GlossaryPost(eid, item.dataset.term)
item.innerHTML = autoLinkHTML(post);
// Link to all occurrences of the added term
const page = document.querySelector('div[role="main"]');
replaceAllText(page, post.concept, autoLinkHTML(post));
}, { once: true });
}
}
autoLink().then( addGlossaryItems );
// @license-end
|