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
|
/* Generic clicking
*
* We can’t just click every clickable object, since there may be side-effects
* like navigating to a different location. Thus whitelist known elements.
*/
(function(){
const selectorFlag = Object.freeze ({
none: 0,
multi: 1, /* click item multiple times */
});
const sites = Object.freeze ([
{
hostname: /^www\.facebook\.com$/i,
selector: [
/* show more comments */
{s: 'a.UFIPagerLink[role=button]', flags: selectorFlag.none},
/* show nested comments*/
{s: 'a.UFICommentLink[role=button]', flags: selectorFlag.none},
],
}, {
hostname: /^twitter\.com$/i,
selector: [
/* expand threads */
{s: 'a.ThreadedConversation-moreRepliesLink', flags: selectorFlag.none},
/* show hidden profiles */
{s: 'button.ProfileWarningTimeline-button', flags: selectorFlag.none},
/* show hidden/sensitive media */
{s: 'button.Tombstone-action.js-display-this-media', flags: selectorFlag.none},
],
}, {
hostname: /^disqus\.com$/i,
selector: [
/* load more comments */
{s: 'a.load-more__button', flags: selectorFlag.multi},
],
}
]);
/* pick selectors matching current location */
let hostname = document.location.hostname;
let selector = [];
for (let s of sites) {
if (s.hostname.test (hostname)) {
selector = selector.concat (s.selector);
}
}
function makeClickEvent () {
return new MouseEvent('click', {
view: window,
bubbles: true,
cancelable: true
});
}
/* throttle clicking */
let queue = [];
function click () {
let o = queue.shift ();
if (o !== undefined) {
o.dispatchEvent (makeClickEvent ());
}
return queue.length > 0;
}
/* Element is visible if itself and all of its parents are
*/
function isVisible (o) {
if (o === null || !(o instanceof Element)) {
return true;
}
let style = window.getComputedStyle (o);
if ('parentNode' in o) {
return style.display !== 'none' && isVisible (o.parentNode);
} else {
return style.display !== 'none';
}
}
/* Elements are considered clickable if they are a) visible and b) not
* disabled
*/
function isClickable (o) {
return !o.hasAttribute ('disabled') && isVisible (o);
}
/* some sites don’t remove/replace the element immediately, so keep track of
* which ones we already clicked */
let have = new Set ();
function discover () {
for (let s of selector) {
let obj = document.querySelectorAll (s.s);
for (let o of obj) {
if (!have.has (o) && isClickable (o)) {
queue.push (o);
if (!(s.flags & selectorFlag.multi)) {
have.add (o);
}
}
}
}
if (queue.length > 0) {
window.setInterval (click, 50);
}
return true;
}
/* XXX: can we use a mutation observer instead? */
window.setInterval (discover, 1000);
}());
|