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
116
117
118
119
120
121
122
123
124
|
export function registerObserver() {
document.addEventListener("DOMContentLoaded", function () {
// When the DOM loaded, only the Web client framework is available, Odoo loads its
// stuff later and so we must wait, and react to new nodes as they are added
const root = document.querySelector(".o_web_client");
if (!root) {
return;
}
// This function is called (see below) whenever a DOM mutation happens
const callback = function (mutationsList, observer) {
for (let mutation of mutationsList) {
try {
if (mutation.type === 'childList' && mutation.addedNodes.length > 0) {
// We are only interested in children that are adding nodes
const elem = mutation.addedNodes.item(0);
if (elem.classList === undefined) {
// This skips nodes that are not HTML tags
continue;
}
else if (elem.attributes.toni) {
// As we clone tags further down, and add those clones, we'd enter an
// infinite recursion unless we skip elements we created, which are
// marked with an attribute
console.debug("Skipping TONI element:", elem);
continue;
}
if (elem.classList.contains("o_switch_company_item")) {
// Now we have an individual company menu item in `elem`. To get rid of the
// Odoo-provided click-event handler, we clone it, and replace the original,
// as cloning does not copy the event handlers
try {
let clone = elem.cloneNode(true);
clone.attributes.toni = true // remember this for later
const cid = clone.getAttribute("data-company-id");
// Copy title and label from the checkbox child
const col1 = clone.childNodes[0];
const company = col1.getAttribute("aria-label");
const iscur = col1.getAttribute("aria-checked");
col1.removeAttribute("title");
col1.removeAttribute("aria-label");
col1.removeAttribute("aria-checked");
col1.removeAttribute("role");
col1.setAttribute("style", "width:42px");
if (iscur == "true") {
col1.classList.add("bg-primary-subtle");
}
clone.title = company;
clone.setAttribute("aria-label", company);
clone.setAttribute("aria-pressed", iscur);
const checkbox = col1.childNodes[0];
let img = document.createElement("img");
img.setAttribute("src", `/inject/${cid}/apple-touch-icon.png`);
img.setAttribute("class", "h-100 px-1");
col1.replaceChild(img, checkbox);
const col2 = clone.childNodes[1];
col2.removeAttribute("title");
col2.removeAttribute("aria-label");
col2.removeAttribute("aria-pressed");
col2.removeAttribute("role");
console.warn(clone);
clone.addEventListener('click', () => {
// … and we add our own event handler that opens a new window on the
// subdomain URL for the other company
const url = document.URL.replace(/:\/\/(\d+\.)?odoo/, `://${cid}.odoo`);
if (url) {
console.debug("Opening new window on", url);
const newwin = window.open(url, "_blank", "noreferrer");
if (newwin) {
newwin.focus();
}
const menubtn = document.querySelector(".o_switch_company_menu > button:nth-child(1)");
if (menubtn) {
// Finally, click the menu, so that it disappears
menubtn.click();
}
} else {
console.error("URL is empty, clone is:", clone);
}
});
elem.parentNode.replaceChild(clone, elem);
console.debug("Replaced with TONI clone:", clone);
} catch (e) { console.error("While cloning element:", elem, e); }
// TODO: The following is an attempt to modify the document title and include the
// company name. but it get overwritten by Odoo right away, so we need another
// approach
/*
} else if (elem.classList.contains("o_switch_company_menu")) {
const btn = elem.childNodes[0];
const company = btn?.title;
if (company !== undefined) {
const titleelem = document.querySelector("title");
titleelem.innerText += ` | ${company}`;
}
*/
}
}
}
catch (e) { console.error("While handling mutation:", mutation, e); };
}
};
// Configure and start the MutationObserver
const observer = new MutationObserver(callback);
const config = { childList: true, subtree: true };
observer.observe(root, config);
console.info("Installed TONI observer");
});
}
|