summaryrefslogtreecommitdiff
path: root/crocoite/data/click.js
blob: 701348770f654d5447e1930e117c4e0bac1f3bd7 (plain)
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);
}());