pedrosalvarezo / Rainforest Unified Script

// ==UserScript==
// @name         Rainforest Unified Script
// @namespace    http://tampermonkey.net/
// @version      0.50
// @description  Functional RF UI
// @author       Paul Sweeney Jr., Alexis Ramirez S. and PedroSalvarezo
// @match        https://tester.rainforestqa.com/tester/*
// @require      https://html2canvas.hertzen.com/dist/html2canvas.js
// @license MIT
// @updateURL https://openuserjs.org/meta/pedrosalvarezo/Rainforest_Unified_Script.meta.js
// @grant        none
// ==/UserScript==

(function() {

// ****************  CONFIGURE THE LOCATION AND SIZE OF THE INTERFASE:  ****************//
    var sideColumn = true; // set to false for instruction at TOP (yes/no on bottom)
    var colSize = { instruction: "25%", terminal: "75%" }; // for sideColumn in percent (20% + 80% = 100%)
    var location = { instruction: "right", terminal: "left" }; // set location (left/right)
    var font = { family: "verdana", size: "20px" }; // your preferred font and size
// ***************  CONFIGURE THE FONT COLOR OF THE CLOCK:  ***************//
// ****************         DEFAULT COLOR: BLACK           ****************//
// * change this value to true if you want the clock fonts to be white:    //
    var font_white = false;
//*************************************************************************//

    window.toggleUIState = false;

    var divForButtons = document.createElement('div');
    divForButtons.style.position = "fixed";
    divForButtons.style.zIndex = 1000000;
    divForButtons.style.right = 0;
    divForButtons.style.top = 0;

    var uiButton = document.createElement('button');
    uiButton.innerHTML = "UI";

    var stepsNumberSpan = document.createElement('span');
    stepsNumberSpan.id = "lastStep";
    stepsNumberSpan.style.backgroundColor = "#175617";
    stepsNumberSpan.style.color = "white";

    divForButtons.appendChild(stepsNumberSpan);
    divForButtons.appendChild(uiButton);

    function updateUI(t) {

        var job = document.querySelector('div[class*="assignment_job"]');
        var vm = document.querySelector('div[class*="terminal_mainWrapper"]');
        var instruction = document.querySelector('div[class*="instructions_instructionsWrapper"]');
        var steps = document.querySelector('div[class*="steps_steps"]');


        if (sideColumn) {
          job.style.float = t ? location.instruction : "";
          job.style.width = t ? colSize.instruction : "";

          vm.style.float = t ? location.terminal : "";
          vm.style.width = t ? colSize.terminal : "";

          steps.id = t? "notflex" : "";
        }

        instruction.id = t? "notflex" : "";

        var stepNumbers = document.querySelectorAll('div[class*="steps_stepHolder"] > div');
        var lastNumber = stepNumbers[stepNumbers.length - 1].innerHTML;
        document.querySelector('#lastStep').innerHTML = "Last Step: " + lastNumber;
    }

    function switchUI(e) {
        window.toggleUIState = !window.toggleUIState;
        updateUI(window.toggleUIState);
        toolBar = document.querySelector("div.terminal_toolbar_3Z6dn");
        var screenshotalive = document.querySelector('button[class*="Ssbtn"]');
        if (toolBar && !screenshotalive) {
            StartScript()
        };

    }

    // Startup default layout after 2 seconds
    setTimeout(function() {
        window.toggleUIState = !window.toggleUIState;
        updateUI(window.toggleUIState);
        document.body.appendChild(divForButtons);
    }, 2000);

    uiButton.onclick = switchUI;
    document.body.appendChild(divForButtons);


    var style = document.createElement('style'); // to break-word for url copy
    style.innerHTML = `
      div[class*='instructions_main']{
        word-wrap: break-word;
      }
      span[class*='clickToCopy_holder']{
        word-wrap: break-word; width:100%
        font-family: ${font.family};
        font-size: ${font.size};
      }
      div[class*="instructions_instructionsWrapper"]#notflex {
        display:block;
        font-family: ${font.family};
        font-size: ${font.size};
      }
      div[class*="steps_steps"]#notflex {
        display:block;
        padding-left: 0px;
      }
      div[class*="ReactModal__Content ReactModal__Content--after-open"] {
        resize: both;
        overflow: auto;
      }
      .dots {
        position: absolute;
        background: yellow;
        width: 10px;
        height: 10px;
        border-radius: 50%;
        font-size: 10px;
        font-weight: bold;
        text-align: center;
      }
      .overlay {
        margin: 0px;
        width: 0%;
        height: 100%;
        position: absolute;
        z-index: 2000000;
        right: 0;
        top: 0;
        overflow-x: hidden;
        background: rgba(0, 0, 0, 0.3);
      }
      .Cbtn {
        position: absolute;
        top: 0.5%;
        left: 0.5%;
        font-family: ${font.family};
        font-size: ${font.size};
      }
      .misc_textarea_1yrFf {
        resize: both;
        overflow: auto;
      }
      /* The grid: Four equal columns that floats next to each other */
    .column {
      display: inline-block;
      width: 8%;
      height: 8%;
      padding: 0px;
    }

/* Style the images inside the grid */
.column img {
  width: 100%;
  opacity: 0.8;
  cursor: pointer;
}

.column img:hover {
  opacity: 1;
}

/* Clear floats after the columns */
.row:after {
  content: "";
  display: table;
  clear: both;
}

/* The expanding image container (positioning is needed to position the close button and the text) */
.container {
  width: 100%;
  float: ${location.terminal};
  position: relative;
}

/* Expanding image text */
#imgtext {
  position: absolute;
  bottom: 15px;
  left: 20px;
  width: 100%;
  color: black;
  font-size: 20px;
}

/* Closable button inside the image */
.closebtn {
  position: absolute;
  top: 10px;
  ${location.instruction}: 10%;
  color: white;
  font-size: 16px;
  cursor: pointer;
}
.Ssbtn {
  position: absolute;
  top: 10px;
  ${location.instruction}: 13%;
  color: white;
  font-size: 14px;
  cursor: pointer;
}
       .Soverlay {
        margin: 0px;
        width: 0%;
        height: 100%;
        position: absolute;
        z-index: 2000000;
        right: 0;
        top: 0;
        overflow-x: hidden;
        background: rgba(0, 0, 0, 0.3);
      }

      `;

    document.body.insertBefore(style, document.body.firstChild);

     //Draggable Report

function dragElement(elmnt) {
  var pos1 = null, pos2 = null, pos3 = null, pos4 = null;

  if (document.querySelector('div[class*="reportModal_title_2EiTk"]')) {
    document.querySelector('div[class*="reportModal_title_2EiTk"]').onmousedown = dragMouseDown;
      elmnt.style.bottom = 'auto';
      elmnt.style.right = 'auto';
  } else {
    document.querySelector('small[class*="inlineScreenshot_hint_9kYtM"]').onmousedown = dragMouseDown;
      elmnt.style.bottom = 'initial';
      elmnt.style.right = 'auto';
  }

  function dragMouseDown(e) {
    e = e || window.event;
    // get the mouse cursor position at startup:
    pos3 = e.clientX;
    pos4 = e.screenY;
    document.onmouseup = closeDragElement;
    // call a function whenever the cursor moves:
    document.onmousemove = elementDrag;
  }

  function elementDrag(e) {
    e = e || window.event;
    e.preventDefault();
    // calculate the new cursor position:
    pos1 = pos3 - e.clientX;
    pos2 = pos4 - e.screenY;
    pos3 = e.clientX;
    pos4 = e.screenY;
    // set the element's new position:
      if (document.querySelector('div[class*="reportModal_title_2EiTk"]')) {
         elmnt.style.top = (elmnt.offsetTop + pos4 + 300)/2 + "px";
         elmnt.style.left = (elmnt.offsetLeft - pos1) + "px";
      } else if (document.querySelector('small[class*="inlineScreenshot_hint_9kYtM"]')) {
         elmnt.style.top = (elmnt.offsetTop + pos4 - 100)/2 + "px";
         elmnt.style.left = (elmnt.offsetLeft - pos1) + "px";
      }
  }

  function closeDragElement() {
    document.onmouseup = null;
    document.onmousemove = null;
  }
}

    // Clock and CLick history

    var minG = 0; var segG = 0; var minL = 0; var segL = 0; var clickCount = 0; var counterM = 0
    var tim;
    var tbox; var wObserver; var stpwrapper;
    var chronoG; var chronoL; var clickCounter; var clickOverlay; var closeBtn; var dots;
    var page; var instxt; var previnst = ''; var toolBar; var spacer


    function updateChrono(){

      addStuff()


      if(minG == 80) chronoG.style.color = "red";
      segG++; segL++;
      if(segG == 60){
        minG++;
        segG = 0;
      }
      if(segL == 60){
        minL++;
        segL = 0;
      }
        // Pop-ups and Overlays selection and modifications
      if(document.querySelector('div[class*="ReactModal__Content ReactModal__Content--after-open"]')){
        var elment = document.querySelector('div[class*="ReactModal__Content ReactModal__Content--after-open"]')
        var overlay = document.querySelector('div[class*="ReactModal__Overlay ReactModal__Overlay--after-open"]')
        overlay.style.backgroundColor = "rgb(100 100 100 / 0%)"
        elment.style.border = "thick solid #3b3b3b"

        if(document.querySelector('div[class*="reportModal_title_2EiTk"]')){
             var title = document.querySelector('div[class*="reportModal_title_2EiTk"]')
             title.style.backgroundColor = "#199ad9"
        }
        if(document.querySelector('small[class*="inlineScreenshot_hint_9kYtM"]')){
            var title2 = document.querySelector('small[class*="inlineScreenshot_hint_9kYtM"]')
            title2.style.backgroundColor = "#199ad9"
        }
        if (elment.style.left == "10%") {
            elment.style.left = "35%";
        }
        dragElement(elment)
      }
      chronoG.textContent = ' Test: ' + String(minG).padStart(2,'0') + ':' + String(segG).padStart(2,'0');
      chronoL.textContent = ' Step: ' + String(minL).padStart(2,'0') + ':' + String(segL).padStart(2,'0');
      clickCounter.textContent = '🖱️: ' + clickCount;
    }

    function wrapperobs(){
      instxt = stpwrapper.querySelector('#currentStep');
      if(instxt){
        if(instxt.textContent != previnst){
          previnst = instxt.textContent;
          minL = 0;
          segL = 0;
          while (clickCount>0) {
            dots = document.querySelector('div[class*="dots"]');
            dots.remove();
            clickCount = clickCount - 1;
          }
        }
      }
      else chronoG.textContent = "didn't find the currrent step.";
    }

    var openOverlay = function(){
        clickOverlay.style.width = "100%";
    };

    var closeOverlay = function(){
        clickOverlay.style.width = "0%";
    };

    function printMousePos(e) {
        var topLoc = e.pageY - 5;
        var leftLoc = e.pageX - 5;
        clickCount += 1;
        var point = document.createElement('div');
        point.textContent = clickCount;
        point.style.top = topLoc + 'px';
        point.style.left = leftLoc + 'px';
        point.classList.add("dots");
        clickOverlay.appendChild(point);
    }

    var audio = new Audio('https://www.myinstants.com/media/sounds/alarm_clock.mp3');
    var timerCDiv = document.createElement('div');
    var timerC = document.createElement('spam');
    timerCDiv.style.textAlign = "center";
    timerCDiv.style.position = "absolute";
    timerCDiv.style.zIndex= "1999999";
    if(font_white) timerCDiv.style.color = "white";
    timerCDiv.style.right = '23%';

    var minCD = 4
    var segCD = 0
    var din

    function myStopFunction() {
        clearInterval(din);
        segCD = 0
        minCD = 4
        timerC.textContent = '  ' + String(minCD).padStart(2,'0') + ':' + String(segCD).padStart(2,'0');
        playBtn.disabled = false;
    }

    function myStartFunction() {
        din = setInterval(updateCountDown, 1000);
        playBtn.disabled = true;
    }


    function updateCountDown(){
        if ((segCD<=0 && minCD<=0) || (minCD<0)) {
            myStopFunction()
            audio.play()
            return;
        }
        segCD--;
        if(segCD <= 0){
              minCD--;
              segCD = 59;
        }
      timerC.textContent = '  ' + String(minCD).padStart(2,'0') + ':' + String(segCD).padStart(2,'0');
    }

    var plusBtn = document.createElement('button');
    var minusBtn = document.createElement('button');
    var playBtn = document.createElement('button');
    var stopBtn = document.createElement('button');
    playBtn.textContent = "▶";
    stopBtn.textContent = "■";
    minusBtn.textContent = "-";
    plusBtn.textContent = "+";
    playBtn.onclick = myStartFunction;
    stopBtn.onclick = myStopFunction;
    minusBtn.onclick = lessMin;
    plusBtn.onclick = moreMin;

    function moreMin() {
        minCD++;
        timerC.textContent = '  ' + String(minCD).padStart(2,'0') + ':' + String(segCD).padStart(2,'0');
    }

    function lessMin() {
        minCD--;
        timerC.textContent = '  ' + String(minCD).padStart(2,'0') + ':' + String(segCD).padStart(2,'0');
    }



    clickOverlay = document.createElement('div');
    clickOverlay.classList.add("overlay");
    document.body.appendChild(clickOverlay);
    closeBtn = document.createElement('button');
    closeBtn.textContent = "X";
    closeBtn.classList.add("Cbtn");
    closeBtn.addEventListener('click', closeOverlay);
    clickOverlay.appendChild(closeBtn);


    clickCounter = document.createElement('a');
    clickCounter.addEventListener('click', openOverlay);
    tbox = document.createElement('div');
    tbox.id = "tbox";
    chronoG = document.createElement('spam');
    chronoL = document.createElement('spam');
    tbox.style.textAlign = "center";
    tbox.style.position = "absolute";
    tbox.style.zIndex= "1999999";
    if(font_white) tbox.style.color = "white";
    tbox.style.left = '15%';
    spacer = document.createElement('span');
    spacer.textContent = "  ";
    tbox.appendChild(clickCounter);
    tbox.appendChild(spacer);
    tbox.appendChild(chronoG);
    tbox.appendChild(spacer);
    tbox.appendChild(chronoL);


    timerCDiv.appendChild(playBtn);
    timerCDiv.appendChild(stopBtn);
    timerCDiv.appendChild(plusBtn);
    timerCDiv.appendChild(minusBtn);
    timerCDiv.appendChild(spacer);
    timerCDiv.appendChild(timerC);

    function screenShotModule() {
        var screenshotOverlay = document.createElement('div');
        screenshotOverlay.classList.add("Soverlay");


        var container = document.createElement('div');
        container.classList.add("container");


        var imgText = document.createElement('div');
        imgText.id = "imgtext";
        imgText.classList.add("imgtext")
        container.appendChild(imgText);

        var screenshotbtn = document.createElement('button');
        screenshotbtn.classList.add("Ssbtn");
        screenshotbtn.innerText = "📷";
        screenshotbtn.addEventListener('click', screenshot);
        document.body.appendChild(screenshotbtn);

        var expandedImg = document.createElement('img');
        expandedImg.id = "expandedImg";
        expandedImg.classList.add("image-class")
        container.appendChild(expandedImg);

        var row = document.createElement('div');
        row.classList.add("row");
        var rowWrapper = document.createElement('div');
        rowWrapper.appendChild(row);
        screenshotOverlay.appendChild(rowWrapper);

        var copybtn = document.createElement('button');
        copybtn.classList.add("Ssbtn");
        copybtn.innerText = "Copy IMG";
        screenshotOverlay.appendChild(copybtn);


        var sScounter = 0;

        var addRow = function(count) {
            var row = document.createElement('div');
            row.classList.add("row");
            rowWrapper.appendChild(row);
            for (var i = count; i < count + 10; i++) {
                var columnBtn = document.createElement('button');
                columnBtn.classList.add("column");
                columnBtn.id = i;
                columnBtn.onclick = myFunction;
                row.appendChild(columnBtn);
            };
        };

        for (var i = 1; i < 11; i++) {
            var columnBtn = document.createElement('button');
            columnBtn.classList.add("column");
            columnBtn.id = i;
            columnBtn.onclick = myFunction;
            row.appendChild(columnBtn);
        };

        function screenshot() {
            var vm = document.querySelector('div[class*="terminal_trackingContainer"]');
            html2canvas(vm).then(function(canvas) {
                var newimg = convertCanvasToImage(canvas);
                newimg.id = "IMG " + sScounter;
                newimg.classList.add("img");
                var column = document.getElementById(sScounter);
                if (!column) {addRow(sScounter); column = document.getElementById(sScounter);};
                column.appendChild(newimg);
            })
            sScounter += 1;
        };

        function myFunction(e) {
            var imgs = document.getElementById(e.srcElement.id);
            // Get the expanded image
            var expandImg = document.getElementById("expandedImg");
            // Get the image text
            var imgText = document.getElementById("imgtext");
            // Use the same src in the expanded image as the image being clicked on from the grid
            expandImg.src = e.srcElement.currentSrc;
            // Use the value of the id attribute of the clickable image as text inside the expanded image
            imgText.innerHTML = imgs.id;
            // Show the container element (hidden with CSS)
            expandImg.parentElement.style.display = "block";
        }


        function convertCanvasToImage(canvas) {
            let image = new Image();
            image.src = canvas.toDataURL();
            return image;
        }


        var openOverlay = function(){
            screenshotOverlay.style.width = "100%";
        };

        var closeOverlay = function(){
            screenshotOverlay.style.width = "0%";
        };

        var copyIMG = function(event) {
            // Select the email link anchor text

            const copyImgBtn = copybtn;
            const canWriteEl = document.getElementById('can-write');
            const img = expandedImg;
            const errorEl = document.getElementById('errorMsg')

            async function askWritePermission() {
                try {
                    const { state } = await navigator.permissions.query({ name: 'clipboard-write', allowWithoutGesture: false })
                    return state === 'granted'
                } catch (error) {
                    errorEl.textContent = `Compatibility error (ONLY CHROME > V66): ${error.message}`
                    console.log(error)
                    return false
                }
            }

            const setToClipboard = blob => {
                const data = [new ClipboardItem({ [blob.type]: blob })]
                return navigator.clipboard.write(data)
            }

            copyImgBtn.addEventListener('click', async () => {
                try {
                    const response = await fetch(img.src)
                    const blob = await response.blob()
                    await setToClipboard(blob)
                } catch (error) {
                    console.error('Something wrong happened')
                    console.error(error)
                }
            })

        };

        copyIMG()


        var closebtn = document.createElement('button');
        closebtn.classList.add("closebtn");
        closebtn.innerText = "Close";
        closebtn.addEventListener('click', closeOverlay);
        screenshotOverlay.appendChild(closebtn);



        var openbtn = document.createElement('button');
        openbtn.classList.add("closebtn");
        openbtn.innerText = "Open";
        openbtn.addEventListener('click', openOverlay);
        document.body.appendChild(openbtn);

        screenshotOverlay.appendChild(container);
        document.body.appendChild(screenshotOverlay);



    }


    function addStuff() {
        toolBar = document.querySelector("div.terminal_toolbar_3Z6dn")
      if (toolBar){
          toolBar.appendChild(tbox);
          toolBar.appendChild(timerCDiv);
                  }
      timerC.textContent = String(minCD).padStart(2,'0') + ':' + String(segCD).padStart(2,'0');
      var vm = document.querySelector('div[class*="terminal_trackingContainer"]');
        if (vm){
            vm.onclick = printMousePos;
            }
    };

    function StartScript(){
      addStuff()
      screenShotModule()
      page = document.querySelector('#page');
        if(page){
          var pobs = new MutationObserver(function(){
            stpwrapper = page.querySelector('#stepHolder');
            if(stpwrapper){
              wrapperobs();
              wObserver = new MutationObserver(wrapperobs);
              wObserver.observe(stpwrapper, {subtree: true, childList: true, characterData: true});
              tim = setInterval(updateChrono, 1000);
              this.disconnect();
            }
          });
          pobs.observe(page, {subtree: true, childList: true});
        }
    };

    StartScript();

})();