ninalanyon / Auto View All SW Replies

// ==UserScript==
// @name        Auto View All SW Replies
// @namespace   nlscripts
// @version     1.4.4
// @description Automatically press the "View n more replies" and "Show Comment" buttons on Similar Worlds pages.
// @include     https://similarworlds.com/*
// @grant       none
// @updateURL https://openuserjs.org/meta/ninalanyon/Auto_View_All_SW_Replies.meta.js
// @downloadURL https://openuserjs.org/install/ninalanyon/Auto_View_All_SW_Replies.user.js
// @copyright 2021, ninalanyon (https://openuserjs.org/users/ninalanyon)
// @license MIT
// ==/UserScript==

/*

Changes:
   1.4.4 Disable "Bring highlighted comment on screen" because I have 
         not been able to find a good way to decide when not to do it. 
   1.4.3 Bring highlighted comment on screen

   1.4.2 Unblur images in direct messages

   1.4.1 Enable on all SW addresses so that the script will be loaded
         on all pages that could turn into posts pages.

   1.4.0 Use only mutation observer, do not call setTimeout from
         inside the button press functions.  Also expand the post,
         this is an anchor with id = "story-compressed" typically
         where an image would be on the post itself.

   1.3.0 Use a mutation observer instead of the load event because
         when navigating in SW the page is built by Javascript rather
         than by loading.

  */

'use strict';

// See https://plainjs.com/javascript/styles/get-the-position-of-an-element-relative-to-the-document-24/
function offset(el) {
   const
     rect = el.getBoundingClientRect(),
     scrollLeft = window.pageXOffset || document.documentElement.scrollLeft,
     scrollTop = window.pageYOffset || document.documentElement.scrollTop;
    return {left: rect.left + scrollLeft, top: rect.top + scrollTop};
}

var highlightedElementTop = 0;

var allowScroll = true;
var lastUrl = "";
var postEntryHeight = 0;

var cancelScrollTimerId = 0;

function cancelScroll(){
  allowScroll = false; 
}
  
// Return false if we should not scroll the highlighted comment into view.  To determine this we check
// to see if the user has expanded the text areas for data entry.
function allowedToScroll(){
    console.log("allowedToScroll: ", allowScroll);
    if (lastUrl !== document.location.href){
        // SW sets the page url without 'navigating' to it.  Make sure
        // we start again if we are on a new page.
        console.log("url changed");
        allowScroll = true;
        postEntryHeight = 0;
        lastUrl = document.location.href;
    }
    if (allowScroll){
      // Check to see if we have entered the text entry box.  If so cancel scrolling.
	const textEntries = document.getElementsByClassName("form-textarea");
	const textEntry = textEntries.item(0);
	const h = textEntry.clientHeight;
        console.log("h: ", h);
	if (postEntryHeight == 0){
	    console.log("peh was 0");
    	    postEntryHeight = h;
	}
  	if (postEntryHeight != h) {
	    console.log("peh changed");
    	    postEntryHeight = h;
	    allowScroll = false;
	}
    }
    return allowScroll;
    
}

function scrollToHighlightedComment(){
    if (!allowedToScroll()){
        return;
    }
    const divs = document.getElementsByClassName("cmt-hghlt");
    console.log("scrollToHighlightedComment: ", divs);
    if (divs.length !== 0){
	let div = divs.item(0);
        let rect = offset(div);
	// Round to integer to avoid scrolling because of changes in
	// the umpteenth decimal place.
	let newTop = Math.round(rect.top);
	console.log("highlightedElementTop: ", highlightedElementTop);
	console.log("rect: ", rect.top);
	if (newTop != highlightedElementTop){
	    // position has changed.  
	    highlightedElementTop = newTop;
	    divs.item(0).scrollIntoView({behavior: 'smooth', block: "end",
					 inline: "nearest"});
    clearTimeOut(cancelScrollTimeoutId);
    cancelScrollimerId = setTimeout(cancelScroll, 1000)
	}
    }
}


function clickButtons(className) {
    const divs = document.getElementsByClassName(className);
    for (let i = 0; i < divs.length; i++) {
        divs.item(i).getElementsByClassName("small-button").item(0).click();
    }    
}

function unBlurInMessages() {
  const anchors = document.getElementsByClassName("small-button blur-btn active");
	[].forEach.call(anchors, function(a){
    if (a.style.display != "none"){ 
      a.click();
    }
   })
}

function clickStoryCompressed(){
    const a = document.getElementById("story-compressed");
    if (a !== null){
	  	a.click();
    }
}

function runClickButtons() {
    console.log("runClickButtons hasFocus: ", document.hasFocus());
    // click the view more replies button
    clickButtons("nested-comments-getmore"); 
    clickButtons("cmtbx-showhdn");
  unBlurInMessages()
  clickStoryCompressed();
  // TODO: reinstate when fixed:  
  //scrollToHighlightedComment();
}

const skipUrl = "https://similarworlds.com/";


let timerId = 0;
const observer = new MutationObserver((mutationList, observer) => {
    console.log("observer", mutationList);
    clearTimeout(timerId);  
    if (window.location.href === skipUrl){
	return; // Don't bother looking for buttons on pages that don't have them.
    }
    
    // click the buttons 10 milliseconds after the last mutation event
    timerId = setTimeout(runClickButtons, 100)
});

// observe everything
observer.observe(
    document.body,
    {childList: true, // observe direct children
     subtree: true, // and lower descendants too
     attributes: true
    }	
);