From 22adde79940d32c5f094f26f3e18b7160e7ccafc Mon Sep 17 00:00:00 2001 From: Lars-Dominik Braun Date: Sat, 1 Dec 2018 13:14:06 +0100 Subject: behavior: Move click script data to external file First step of issue #3 --- crocoite/data/click.js | 217 ++++++++++++++++--------------------------------- 1 file changed, 72 insertions(+), 145 deletions(-) (limited to 'crocoite/data/click.js') diff --git a/crocoite/data/click.js b/crocoite/data/click.js index b3e425b..ae189da 100644 --- a/crocoite/data/click.js +++ b/crocoite/data/click.js @@ -4,129 +4,7 @@ * like navigating to a different location. Thus whitelist known elements. */ -(function(){ -const selectorFlag = Object.freeze ({ - none: 0, - multi: 1, /* click item multiple times */ -}); -const defaultClickThrottle = 50; /* in ms */ -const discoverInterval = 1000; /* 1 second */ -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}, - /* initially show comments below a single post/video, i.e. /user/post/123 */ - {s: 'form.commentable_item a[data-comment-prelude-ref=action_link_bling][rel=ignore]', flags: selectorFlag.none}, - /* close the “register now” nag screen. for better screen shots */ - {s: 'a#expanding_cta_close_button[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}, - ], - }, { - hostname: /^(www|np)\.reddit\.com$/i, - selector: [ - /* show more comments, reddit’s javascript ignores events if too - * frequent */ - {s: 'span.morecomments a', flags: selectorFlag.none, throttle: 500}, - ], - }, { - hostname: /^www\.instagram\.com$/i, - selector: [ - /* posts may have multiple images that load dynamically, click the arrow */ - {s: 'a[role=button].coreSpriteRightChevron', flags: selectorFlag.multi, throttle: 500}, - /* load more comments */ - {s: 'article div ul li a[role=button]', flags: selectorFlag.multi}, - ], - }, { - hostname: /^www\.youtube\.com$/i, - selector: [ - /* expand comment thread */ - {s: 'ytd-comment-thread-renderer div.more-button', flags: selectorFlag.none}, - ], - }, { - hostname: /^www\.patreon\.com$/i, - selector: [ - /* load more content */ - {s: 'div[display=flex] div[display=block] button[color=gray][type=button]', flags: selectorFlag.multi}, - /* load more comments */ - {s: 'div.stackable[display=block] > div > div > a[color=dark][target=_self]', flags: selectorFlag.none}, - /* load more replies */ - {s: 'div > a[scale="0"][color=blue][size="1"]', flags: selectorFlag.none}, - ], - }, { - hostname: /^(www\.)?gab\.ai$/i, - selector: [ - /* post comments */ - {s: 'post-detail post-comment .post-comment__replies__count a', flags: selectorFlag.none}, - /* more comments */ - {s: 'post-detail .post-comment-list__loading a', flags: selectorFlag.none}, - /* more posts */ - {s: 'post-list a.post-list__load-more', flags: selectorFlag.multi}, - ], - }, { - hostname: /^(www\.)?github\.com$/i, - selector: [ - /* show hidden issue items, see https://github.com/dominictarr/event-stream/issues/116 */ - {s: 'div#discussion_bucket form.ajax-pagination-form button.ajax-pagination-btn', flags: selectorFlag.none}, - ], - } - ]); - -/* 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 = []; -let clickTimeout = null; -function click () { - if (queue.length > 0) { - const item = queue.shift (); - const o = item.o; - const selector = item.selector; - o.dispatchEvent (makeClickEvent ()); - - if (queue.length > 0) { - const nextTimeout = 'throttle' in selector ? - selector.throttle : defaultClickThrottle; - clickTimeout = window.setTimeout (click, nextTimeout); - } else { - clickTimeout = null; - } - } -} - +(function() { /* Element is visible if itself and all of its parents are */ function isVisible (o) { @@ -148,33 +26,82 @@ 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: o, selector: s}); - if (!(s.flags & selectorFlag.multi)) { - have.add (o); - } +const defaultClickThrottle = 50; /* in ms */ +const discoverInterval = 1000; /* 1 second */ + +class Click { + constructor(options) { + /* pick selectors matching current location */ + let hostname = document.location.hostname; + this.selector = []; + for (let s of options['sites']) { + let r = new RegExp (s.match, 'i'); + if (r.test (hostname)) { + this.selector = this.selector.concat (s.selector); } } + /* throttle clicking */ + this.queue = []; + this.clickTimeout = null; + + /* some sites don’t remove/replace the element immediately, so keep track of + * which ones we already clicked */ + this.have = new Set (); + + /* XXX: can we use a mutation observer instead? */ + this.interval = window.setInterval (this.discover.bind (this), discoverInterval); } - if (queue.length > 0 && clickTimeout === null) { - /* start clicking immediately */ - clickTimeout = window.setTimeout (click, 0); + + makeClickEvent () { + return new MouseEvent('click', { + view: window, + bubbles: true, + cancelable: true + }); + } + + click () { + if (this.queue.length > 0) { + const item = this.queue.shift (); + const o = item.o; + const selector = item.selector; + o.dispatchEvent (this.makeClickEvent ()); + + if (this.queue.length > 0) { + const nextTimeout = 'throttle' in selector ? + selector.throttle : defaultClickThrottle; + this.clickTimeout = window.setTimeout (this.click.bind (this), nextTimeout); + } else { + this.clickTimeout = null; + } + } + } + + discover () { + for (let s of this.selector) { + let obj = document.querySelectorAll (s.selector); + for (let o of obj) { + if (!this.have.has (o) && isClickable (o)) { + this.queue.push ({o: o, selector: s}); + if (!s.multi) { + this.have.add (o); + } + } + } + } + if (this.queue.length > 0 && this.clickTimeout === null) { + /* start clicking immediately */ + this.clickTimeout = window.setTimeout (this.click.bind (this), 0); + } + return true; } - return true; -} -/* XXX: can we use a mutation observer instead? */ -let interval = window.setInterval (discover, discoverInterval); -function stop() { - window.clearInterval (interval); + stop () { + window.clearInterval (this.interval); + window.clearTimeout (this.clickTimeout); + } } -return {'stop': stop}; + +return Click; }()) -- cgit v1.2.3