Skip to content

Commit 86513ed

Browse files
committed
Utils: Update mutations root observer
1 parent acd9882 commit 86513ed

File tree

1 file changed

+79
-74
lines changed

1 file changed

+79
-74
lines changed

mutations.js

Lines changed: 79 additions & 74 deletions
Original file line numberDiff line numberDiff line change
@@ -1,107 +1,113 @@
1-
/* GitHub mutations observer library script v0.4.4
1+
/* GitHub mutations observer library script v0.6.0
22
* Detect changes to various elements and trigger an event
33
* This script is meant to be used as a library for GitHub-based userscripts
4-
* Copyright © 2021 Rob Garrison
4+
* Copyright © 2022 Rob Garrison
55
* License: MIT
66
*/
77
(() => {
88
"use strict";
99

1010
// prefix for event & document body class name, e.g. "ghmo:container"
11-
const prefix = "ghmo",
12-
disableAttr = `data-${prefix}-disable`,
13-
debounceInterval = 200,
14-
targets = {
15-
// pjax container (covers general, repo & gists)
16-
// .news = newsfeed layout
17-
// .repository-content = file code (code folding)
18-
"[data-pjax-container], .news, .repository-content": {
19-
count: 0,
20-
name: "container"
21-
},
22-
// comment preview active
23-
".js-preview-body": {
24-
count: 0,
25-
name: "preview"
26-
},
27-
// .js-discussion = wrapper for progressively loaded comments;
28-
// "# items not shown" example: https://github.com/isaacs/github/issues/18
29-
// .discussion-item = issue status changed (github-issue-show-status)
30-
// #progressive-timeline-item-container = load hidden items (old?)
31-
// #js-progressive-timeline-item-container = load hidden items
32-
".js-discussion, .discussion-item, .toolbar-item, #progressive-timeline-item-container, #js-progressive-timeline-item-container": {
33-
count: 0,
34-
name: "comments"
35-
},
36-
// progressively loaded content (diff files)
37-
".js-diff-progressive-container, .data.blob-wrapper, .js-diff-load-container, .diff-table tbody": {
38-
count: 0,
39-
name: "diff"
40-
},
41-
// issues/pr sidebar & timeline sections: e.g. form actions, commit
42-
// references, deployment state & PR checks container
43-
".js-updatable-content, .js-updatable-content-preserve-scroll-position": {
44-
count: 0,
45-
name: "updatable"
46-
},
47-
// user profile menu (loads on hover)
48-
"details-menu": {
49-
count: 0,
50-
name: "menu"
51-
}
11+
const prefix = "ghmo";
12+
const disableAttr = `data-${prefix}-disable`;
13+
const debounceInterval = 250;
14+
const targets = {
15+
// pjax container (covers general, repo & gists)
16+
// .news = newsfeed layout
17+
// .repository-content = file code (code folding)
18+
// body = global changes
19+
"[data-pjax-container], .news, .repository-content, body": {
20+
count: 0,
21+
name: "container"
22+
},
23+
// comment preview active
24+
".js-preview-body": {
25+
count: 0,
26+
name: "preview"
27+
},
28+
// .js-discussion = wrapper for progressively loaded comments;
29+
// "# items not shown" example: https://github.com/isaacs/github/issues/18
30+
// .discussion-item = issue status changed (github-issue-show-status)
31+
// #progressive-timeline-item-container = load hidden items (old?)
32+
// #js-progressive-timeline-item-container = load hidden items
33+
// markdown-toolbar = issue comments
34+
".js-discussion, .discussion-item, .toolbar-item, #progressive-timeline-item-container, #js-progressive-timeline-item-container, markdown-toolbar": {
35+
count: 0,
36+
name: "comments"
37+
},
38+
// progressively loaded content (diff files)
39+
".js-diff-progressive-container, .data.blob-wrapper, .js-diff-load-container, .diff-table tbody, .diff-progressive-loader": {
40+
count: 0,
41+
name: "diff"
5242
},
53-
list = Object.keys(targets);
43+
// issues/pr sidebar & timeline sections: e.g. form actions, commit
44+
// references, deployment state & PR checks container
45+
".js-updatable-content, .js-updatable-content-preserve-scroll-position": {
46+
count: 0,
47+
name: "updatable"
48+
},
49+
// user profile menu (loads on hover)
50+
"details-menu": {
51+
count: 0,
52+
name: "menu"
53+
}
54+
};
55+
const list = Object.keys(targets);
56+
let queue = new Set();
57+
let timer;
5458

5559
function fireEvents() {
5660
list.forEach(selector => {
5761
if (targets[selector].count > 0) {
5862
// event => "ghmo:container", "ghmo:comments"
5963
const event = new Event(prefix + ":" + targets[selector].name);
6064
document.dispatchEvent(event);
65+
targets[selector].count = 0;
6166
}
62-
targets[selector].count = 0;
6367
});
6468
}
6569

70+
function process() {
71+
clearTimeout(timer);
72+
// avoiding use of forEach loops for performance reasons
73+
for (const target of queue) {
74+
if (target) {
75+
for (const item of list) {
76+
if (target.matches(item)) {
77+
targets[item].count++;
78+
}
79+
}
80+
}
81+
}
82+
timer = setTimeout(() => fireEvents(), debounceInterval);
83+
queue.clear();
84+
}
85+
6686
function init() {
6787
// prevent error when library is loaded at document-start & no body exists
6888
const container = document.querySelector("body");
69-
let timer;
89+
/* document.body attribute used to disable updates; it *should not*
90+
* be used regularly as multiple scripts may enable or disable the
91+
* observers at inappropriate times. It is best that each script handles
92+
* the mutation events triggered by this library on its own
93+
*/
94+
if (container.getAttribute(disableAttr)) {
95+
return;
96+
}
7097
// prevent script from installing more than once
7198
if (container && !container.classList.contains(prefix + "-enabled")) {
7299
container.classList.add(prefix + "-enabled");
73-
// bound to document.body... this may be bad for performance
74100
// http://stackoverflow.com/a/39332340/145346
75101
new MutationObserver(mutations => {
76-
clearTimeout(timer);
77-
/* document.body attribute used to disable updates; it *should not*
78-
* be used regularly as multiple scripts may enable or disable the
79-
* observers at inappropriate times. It is best that each script handles
80-
* the mutation events triggered by this library on its own
81-
*/
82-
if (container.getAttribute(disableAttr)) {
83-
return;
102+
if (!queue.size) {
103+
requestAnimationFrame(process);
84104
}
85-
let mindx, target, lindx,
86-
llen = list.length,
87-
mlen = mutations.length;
88-
// avoiding use of forEach loops for performance reasons
89-
for (mindx = 0; mindx < mlen; mindx++) {
90-
target = mutations[mindx].target;
91-
if (target) {
92-
for (lindx = 0; lindx < llen; lindx++) {
93-
if (target.matches(list[lindx])) {
94-
targets[list[lindx]].count++;
95-
}
96-
}
97-
}
98-
timer = setTimeout(() => {
99-
fireEvents();
100-
}, debounceInterval);
105+
for (const mutation of mutations) {
106+
queue.add(mutation.target);
101107
}
102-
}).observe(container, {
108+
}).observe(document, {
103109
childList: true,
104-
subtree: true
110+
subtree: true,
105111
});
106112
}
107113
}
@@ -111,5 +117,4 @@
111117
} else {
112118
init();
113119
}
114-
115120
})();

0 commit comments

Comments
 (0)