@@ -29,7 +29,12 @@ function createPostTreeWalker(post) {
2929 // Validating
3030 if ( node . nodeName === 'IMG' ) {
3131 // Constructing URL
32- const url = new URL ( node . src ) ;
32+ let url ;
33+ try {
34+ url = new URL ( node . src ) ;
35+ } catch {
36+ return NodeFilter . FILTER_SKIP ;
37+ }
3338
3439 // Checking to see if it's a valid post
3540 if ( mediaLinks . includes ( url . hostname ) && url . searchParams . has ( 'blur' ) ) {
@@ -49,49 +54,51 @@ function createPostTreeWalker(post) {
4954 * @param {Node } mediaNode The media node, often an image, to find the thread to
5055 * @returns {string } The source.
5156 */
52- function findExternalNode ( mediaNode ) {
53- let depth = 0 ;
54- let par = mediaNode ;
55- while ( par && par . classList ) {
56- if ( par . classList . contains ( 'scrollerItem' ) || par . nodeName == 'A' || depth >= 15 ) break ;
57- par = par . parentNode ;
58- depth ++ ;
59- }
57+ async function findExternalNode ( mediaNode ) {
58+ return new Promise ( ( resolve , reject ) => {
59+ let depth = 0 ;
60+ let par = mediaNode ;
61+ while ( par && par . classList ) {
62+ if ( par . classList . contains ( 'scrollerItem' ) || par . nodeName == 'A' || depth >= 15 ) break ;
63+ par = par . parentNode ;
64+ depth ++ ;
65+ }
6066
61- // Handling the gathered node
62- if ( ! par ) return null ;
63- if ( par . nodeName == 'A' ) {
64- return par ;
65- } else {
66- // i forgot what this entire section is even for..
67- // const treeWalker = document.createTreeWalker(par, NodeFilter.SHOW_ELEMENT, (node) => {
68- // // Validating
69- // if (node.nodeName == 'A') {
70- // if (invalidSourceLinks.filter((link) => node.href.match(link) != null).length <= 0)
71- // return NodeFilter.FILTER_ACCEPT;
72- // }
73-
74- // return NodeFilter.FILTER_SKIP;
75- // });
76-
77-
78- // while (treeWalker.nextNode()) {
79- // // Prevent duplicate
80- // let flag = true;
81- // const postWalker = createPostTreeWalker(treeWalker.currentNode);
82- // while (postWalker.nextNode()) {
83- // console.log(`node ${postWalker.currentNode} .. ${postWalker.currentNode.src}`)
84- // if (postWalker.currentNode.src == mediaNode.src) {
85- // flag = false;
86- // console.log('found duplicate');
87- // break;
88- // }
89- // }
90-
91- // if (!flag) continue;
92- // return treeWalker.currentNode;
93- // }
94- }
67+ // Handling the gathered node
68+ if ( ! par ) return null ;
69+ if ( par . nodeName == 'A' ) {
70+ resolve ( par ) ;
71+ } else {
72+ // i forgot what this entire section is even for..
73+ // const treeWalker = document.createTreeWalker(par, NodeFilter.SHOW_ELEMENT, (node) => {
74+ // // Validating
75+ // if (node.nodeName == 'A') {
76+ // if (invalidSourceLinks.filter((link) => node.href.match(link) != null).length <= 0)
77+ // return NodeFilter.FILTER_ACCEPT;
78+ // }
79+
80+ // return NodeFilter.FILTER_SKIP;
81+ // });
82+
83+
84+ // while (treeWalker.nextNode()) {
85+ // // Prevent duplicate
86+ // let flag = true;
87+ // const postWalker = createPostTreeWalker(treeWalker.currentNode);
88+ // while (postWalker.nextNode()) {
89+ // console.log(`node ${postWalker.currentNode} .. ${postWalker.currentNode.src}`)
90+ // if (postWalker.currentNode.src == mediaNode.src) {
91+ // flag = false;
92+ // console.log('found duplicate');
93+ // break;
94+ // }
95+ // }
96+
97+ // if (!flag) continue;
98+ // return treeWalker.currentNode;
99+ // }
100+ }
101+ } ) ;
95102}
96103
97104/**
@@ -138,19 +145,20 @@ function removeMediaSpoiler(mediaNode, source) {
138145 }
139146
140147 // Fix the post
141- let linkNode = findExternalNode ( mediaNode ) ;
142- for ( let child of linkNode . children ) {
143- if ( child !== mediaNode && child . nodeName == "DIV" ) {
144- let children = Array . from ( child . children ) ;
145- children = children . filter ( ( x ) => {
146- return x . nodeName === "BUTTON" && x . innerHTML . includes ( 'spoiler' ) ;
147- } ) ;
148-
149- if ( children [ 0 ] ) {
150- children [ 0 ] . style . display = "none" ;
148+ findExternalNode ( mediaNode ) . then ( ( linkNode ) => {
149+ for ( let child of linkNode . children ) {
150+ if ( child !== mediaNode && child . nodeName == "DIV" ) {
151+ let children = Array . from ( child . children ) ;
152+ children = children . filter ( ( x ) => {
153+ return x . nodeName === "BUTTON" && x . innerHTML . includes ( 'spoiler' ) ;
154+ } ) ;
155+
156+ if ( children [ 0 ] ) {
157+ children [ 0 ] . style . display = "none" ;
158+ }
151159 }
152160 }
153- }
161+ } ) ;
154162}
155163
156164/**
@@ -159,7 +167,7 @@ function removeMediaSpoiler(mediaNode, source) {
159167 * @param {string } externalSrc The link of the external media source to scan.
160168 * @returns {string } The unspoilered source link to the media.
161169 */
162- function grabSourceFromImage ( blurredSrc , externalSrc ) {
170+ async function grabSourceFromImage ( blurredSrc , externalSrc ) {
163171 return new Promise ( ( resolve , reject ) => {
164172 // Checking to see if the link isn't already unblurred
165173 const splitURL = externalSrc . split ( '?' ) [ 1 ] ;
@@ -217,74 +225,88 @@ function grabSourceFromImage(blurredSrc, externalSrc) {
217225/**
218226 * Attempts to get all images from the reddit post.
219227 * @param {Node } post the post to search for images in.
220- * @param {(mediaNode: Node, source: string) } callback the function to call each time an image is found.
221228 */
222- function getImagesFromPost ( post , callback ) {
223- // Creating tree walker from post
224- const treeWalker = createPostTreeWalker ( post ) ;
225- let currentNode = treeWalker . currentNode ;
226-
227- while ( currentNode ) {
228- let source = findExternalNode ( treeWalker . currentNode ) ;
229- if ( source ) {
230- callback ( treeWalker . currentNode , source . href )
229+ async function getImagesFromPost ( post ) {
230+ return new Promise ( ( resolve , reject ) => {
231+ // Creating tree walker from post
232+ const treeWalker = createPostTreeWalker ( post ) ;
233+ let currentNode = treeWalker . currentNode ;
234+ let promises = [ ] ;
235+
236+ while ( currentNode ) {
237+ promises . push ( findExternalNode ( treeWalker . currentNode ) ) ;
238+ currentNode = treeWalker . nextNode ( ) ;
231239 }
232240
233- currentNode = treeWalker . nextNode ( ) ;
234- }
241+ // getting promises
242+ Promise . race ( promises ) . then ( ( source ) => {
243+ resolve ( { mediaNode : treeWalker . currentNode , source : source . href } ) ;
244+ } )
245+ } )
235246}
236247
237248/**
238249 * Main function that will remove spoilers from posts, and check against the whitelist or blacklist
239250 * @param {Node } post The post to remove spoilered media from.
240251 */
241- function removePostSpoiler ( post , pageType ) {
252+ async function removePostSpoiler ( post , pageType ) {
242253 // Stopping unnecessary posts from continuing
243254 //if (post.nodeName !== "IMG" && !post.classList.contains('Post')) return;
244255
245256 // Managing different types
246257 let postType ;
247258 if ( pageType == 'thread' ) {
248- getImagesFromPost ( post , removeMediaSpoiler ) ;
259+ getImagesFromPost ( post ) . then ( ( { mediaNode, source } ) => {
260+ removeMediaSpoiler ( mediaNode , source ) ;
261+ } ) ;
262+
249263 post . click ( ) ;
250264 } else {
251- getImagesFromPost ( post , ( mediaNode , source ) => {
265+ getImagesFromPost ( post ) . then ( ( { mediaNode, source } ) => {
266+ if ( ! mediaNode || ! source ) return ;
252267 grabSourceFromImage ( mediaNode . src , source ) . then ( ( content ) => {
253268 removeMediaSpoiler ( mediaNode , content ) ;
254269 } )
255- } ) ;
270+ } )
256271 }
257272}
258273
259274/* ---------------------- */
260275
261- // Grabbing the rootNode depending on the post type
262- let rootNode , postType ;
263- if ( window . location . href . split ( '/' ) [ 5 ] !== 'comments' ) {
264- // Dynamically grabbing for homepage type
265- let depth = 0 ;
266- rootNode = document . getElementsByClassName ( 'scrollerItem Post' ) [ 0 ] ;
267- while ( rootNode . parentNode !== null && depth < 10 ) {
268- if ( rootNode . childNodes . length > 5 ) break ;
269-
270- rootNode = rootNode . parentNode ;
271- depth ++ ;
276+ ( ( ) => {
277+ // Grabbing the rootNode depending on the post type
278+ let rootNode , postType ;
279+ if ( window . location . href . split ( '/' ) [ 5 ] !== 'comments' ) {
280+ // Dynamically grabbing for homepage type
281+ let depth = 0 ;
282+ rootNode = document . getElementsByClassName ( 'scrollerItem Post' ) [ 0 ] ;
283+ while ( rootNode && depth < 10 ) {
284+ rootNode = rootNode . parentNode ;
285+ if ( rootNode . childNodes . length > 5 ) break ;
286+
287+ depth ++ ;
288+ }
289+ } else {
290+ rootNode = document . getElementsByClassName ( 'Post' ) [ 0 ] ;
291+ postType = 'thread' ;
272292 }
273293
274- // Call initial function on all child nodes
275- rootNode . childNodes . forEach ( ( x ) => { removePostSpoiler ( x , postType ) } ) ;
276- } else {
277- rootNode = document . getElementsByClassName ( 'Post' ) [ 0 ] ;
278- postType = 'thread' ;
294+ // Handling rootnode missing
295+ if ( ! rootNode ) return ;
296+ console . log ( "Spectacles loaded successfully!" )
279297
280- // Remove spoiler on 'root' because this should be the only post
281- removePostSpoiler ( rootNode )
282- }
298+ // Handling spoiler removal on load
299+ if ( postType === 'thread' ) {
300+ removePostSpoiler ( rootNode )
301+ } else {
302+ rootNode . childNodes . forEach ( ( x ) => { removePostSpoiler ( x , postType ) } ) ;
303+ }
283304
284- // Setting up a listener to detect when new posts are added
285- VM . observe ( rootNode , ( mutList ) => {
286- mutList . filter ( ( mut ) => mut . type === 'childList' ) . map ( ( mut ) => mut . addedNodes [ 0 ] ) . forEach ( ( x ) => {
287- if ( ! x ) return false ;
288- removePostSpoiler ( x , postType )
289- } ) ;
290- } , { childList : true } ) ;
305+ // Setting up a listener to detect when new posts are added
306+ VM . observe ( rootNode , ( mutList ) => {
307+ mutList . filter ( ( mut ) => mut . type === 'childList' ) . map ( ( mut ) => mut . addedNodes [ 0 ] ) . forEach ( ( x ) => {
308+ if ( ! x ) return false ;
309+ removePostSpoiler ( x , postType )
310+ } ) ;
311+ } , { childList : true } ) ;
312+ } ) ( ) ;
0 commit comments