// ==UserScript==
// @name         chan embedded quote fixer
// @namespace    http://tampermonkey.net/
// @version      1.5
// @description  Puts embedded quotes in end of post rather than beginning, can also click on checkboxes in posts to remove them, also works on 4channel boards, can tesselate whole thread/inlined posts, can change size of expanded media, can color posts to help keep track of embedded conversations, can hide extra options on each post via toggle (posts/thread), can manually drag posts around page via toggle (posts/thread), can remove (You)s
// @author       DUVish
// @match        http://boards.4chan.org/*/thread/*
// @match        https://boards.4chan.org/*/thread/*
// @match        https://boards.4channel.org/*/thread/*
// @match        http://boards.4channel.org/*/thread/*
// @grant        none
// ==/UserScript==
document.querySelector("head").innerHTML += `<style>div.post {overflow: visible}</style>`;
//window.onload(function() {
    setTimeout(function() {
        let quotesList = Array.from(document.getElementsByClassName("quotelink")).filter(node => window.getComputedStyle(node).getPropertyValue("font-size") === "10.6667px");
        quotesList.forEach(node => {
          node.addEventListener("click", qEV);
        });
        Array.from(document.getElementsByTagName("span")).filter(el => {
            try {
              if (el.children.length > 0 && el.children[0].classList.contains("quotelink")) return true;
            } catch(error) {
              //console.log("custom error in span EL", el);
              return false;
            }
        }).forEach(el => el.addEventListener("click", qSpanClick));
        addRemoveCapability();
        addCollapseAndExpand();
        addTessellationQuotes();
        addQuotedPostsContainer();
        addTessellationThread();
        addThreadWidthToggle();
        addMediaZoom();
        addColorPostsThread();
        addColorPosts();
        setPostColor();
        addCollapseExtraNodesToggle();
        addPostDraggableTogglePost();
        addPostsDraggableToggleThread();
        addRemoveYousInPost();
        addEditabilityToPost();
        addCollapseExtrasInPosts();
        addExpandExtrasInPosts();
        expandPostsPostOn();
    }, 2500);
//});
let greenTextStyler = `<style>
  blockquote > div > span.quote {
    color: #789922;
  }
</style>`;
document.querySelector("head").innerHTML += greenTextStyler;
//tessellation feature - move element all the way to the right, try and move up - if interfering with getBoundingClientRect in two axes, then move left, then try and move up again - repeat until both up and left are exhausted
  //repeat for all posts - tesselation would not just add display: flex and flex-wrap:wrap to thread container, but also margin/-margin to each individual post - can either add some caching capability by storing data on
  //dome nodes themselves, or just store all of the HTML before, revert on option, and re-compute each time, possibly also add to barchive, also add for within posts themselves
var postColor = '';
var colorDiff = 38;
if (window.location.href.includes("boards.4chan.org")) colorDiff = 28;
function addTessellationThread() {
  let newSpan = document.createElement("span");
  newSpan.innerText = "[Tessellate Thread]";
  newSpan.classList.add("tessellateThread");
  newSpan.style.paddingLeft = "6px";
  newSpan.style.paddingRight = "1px";
  newSpan.style.fontSize = "11px";
  newSpan.style.color = "rgb(46, 54, 144)";
  newSpan.style.opacity = "1";
  document.querySelector(".opContainer").querySelector(".postInfo.desktop").insertBefore(newSpan, document.querySelector(".opContainer").querySelector(".postMenuBtn"));
  newSpan.addEventListener("click", tessellateThread);
}
function tessellateThreadAlgorithmic(e) {
  let node = e.target;
  let threadNode = document.querySelector(".thread");
  let posts =  Array.from(document.querySelectorAll(".post.reply"));
  if (e.target.style.opacity === "1") {
    //threadNode.style.maxWidth = "100%";
    //threadNode.style.display = "flex";
    //threadNode.style.flexWrap = "wrap";
    node.style.opacity = "0.55";
    Array.from(document.querySelectorAll(".sideArrows")).forEach(el => {el.style.opacity = "0"; el.style.zIndex = "-1";});
    posts.forEach(el => {el.style.position = "relative"; el.style.bottom = "0px"; el.style.top = "0px"; el.style.left = "0px"; el.style.right = "0px";});
    let topBound = Math.floor(document.querySelector(".postContainer.opContainer").getBoundingClientRect().bottom + window.scrollY);
    let leftBound = 24;
    //console.log("before top-level iteration", topBound, leftBound);
    posts.forEach((post, postIdx) => {
      if (postIdx === 0) return;
      //post.style.left = document.documentElement.clientWidth - post.getBoundingClientRect().right;
      let beginningTop = post.getBoundingClientRect().top + window.scrollY;
      let currentBottomDiff =  0;
      let currentRightDiff =  0;
      let currentLeftDiff =  0;
      let currentTopDiff =  0;
      let currentLeftBound = document.documentElement.clientWidth - post.getBoundingClientRect().right;
      //console.log("before first while loop", beginningTop, currentLeftBound);
      //console.log("collection before while loop", posts, posts.slice(0, postIdx));
      while(!intersectionCheck(post.getBoundingClientRect().top + window.scrollY - currentBottomDiff, currentLeftBound, posts.slice(0, postIdx), topBound, leftBound, post)) {
        currentBottomDiff += 2;
        //post.style.bottom = `${Number(post.style.bottom.match(/(\d+)px/)[1]) + 2}px`;
      }
      post.style.bottom = `${Number(post.style.bottom.match(/(\d+)px/)[1]) + currentBottomDiff}px`;
      let currentTopBound = beginningTop + window.scrollY - currentBottomDiff;
      while(!intersectionCheck(currentTopBound, currentLeftBound - currentRightDiff, posts.slice(0, postIdx), topBound, leftBound, post)) {
        //post.style.right = `${Number(post.style.right.match(/(\d+)px/)[1]) + 2}px`;
        currentRightDiff += 2;
      }
      post.style.left = `${Number(post.style.left.match(/(\d+)px/)[1]) - currentRightDiff + currentLeftBound}px`;
    });
  } else {
    //undo
    node.style.opacity = "1";
    threadNode.style.maxWidth = "";
    Array.from(document.querySelectorAll(".sideArrows")).forEach(el => {el.style.opacity = "1"; el.style.zIndex = "0";});
    posts.forEach(el => {el.style.position = "unset"; el.style.bottom = "0px"; el.style.top = "0px"; el.style.left = "0px"; el.style.right = "0px";});
  }
}
function intersectionCheck(currentTop, currentLeft, collection, topBound, leftBound, post) {
  //console.log("entering intersectionCheck, collection", collection);
  for (let el of collection) {
    //console.log("comparing", post, el, currentTop, currentLeft, topBound, leftBound);
    if (currentTop < el.getBoundingClientRect().bottom + window.scrollY && currentLeft < el.getBoundingClientRect().right ||
        currentTop < topBound || currentLeft < leftBound) return true;
  }
  return false;
}
function tessellateThread(e) {
  let node = e.target;
  let threadNode = document.querySelector(".thread");
  if (threadNode.style.display !== "flex") {
    threadNode.style.display = "flex";
    threadNode.style.flexWrap = "wrap";
    threadNode.style.maxWidth = "100%";
    node.style.opacity = "0.55";
  } else {
    threadNode.style.display = "";
    threadNode.style.flexWrap = "";
    threadNode.style.maxWidth = "";
    node.style.opacity = "1.0";
  }
}
function addThreadWidthToggle() {
  let newSpan = document.createElement("span");
  newSpan.innerText = "[Toggle Thread Width]";
  newSpan.classList.add("threadWidthToggle");
  newSpan.style.paddingLeft = "6px";
  newSpan.style.paddingRight = "1px";
  newSpan.style.fontSize = "11px";
  newSpan.style.color = "rgb(46, 54, 144)";
  newSpan.title = "Toggle thread width to and from 100% viewport width (without tessellating)";
  document.querySelector(".opContainer").querySelector(".postInfo.desktop").insertBefore(newSpan, document.querySelector(".opContainer").querySelector(".postMenuBtn"));
  newSpan.addEventListener("click", toggleThreadWidth);
}
function toggleThreadWidth(e) {
  let node = e.target;
  if (Number(node.style.opacity) > 0.95 || !node.style.opacity) {
    document.querySelector(".thread").style.maxWidth = "100%";
    node.style.opacity = "0.55";
  } else {
    document.querySelector(".thread").style.maxWidth = "";
    node.style.opacity = "1.0";
  }
}
function addTessellationQuotes() {
  Array.from(document.getElementsByClassName("postInfo")).forEach(el => {
    if (el.getElementsByClassName("tessellateQuotes").length === 0) {
      let btn = el.querySelector(".postMenuBtn");
      let tesSpan = document.createElement("span");
      tesSpan.innerText = "[Tessellate]";
      tesSpan.classList.add("tessellateQuotes");
      tesSpan.classList.add("collapsible");
      tesSpan.style.paddingLeft = "6px";
      tesSpan.style.paddingRight = "1px";
      tesSpan.style.fontSize = "11px";
      tesSpan.style.color = "rgb(46, 54, 144)";
      el.insertBefore(tesSpan, btn);
      tesSpan.removeEventListener("click", tessellateQuotes);
      tesSpan.addEventListener("click", tessellateQuotes);
    }
  });
}
function tessellateQuotes(e) {
  let tesSpan = e.target;
  let quotesNode = e.target.parentNode.parentNode.querySelector(".quotedPostsContainer");
  if (quotesNode.style.display !== "flex") {
    quotesNode.style.display = "flex";
    quotesNode.style.flexWrap = "wrap";
    tesSpan.style.opacity = "0.55";
  } else {
    quotesNode.style.display = "";
    quotesNode.style.flexWrap = "";
    tesSpan.style.color = "rgb(46, 54, 144)";
    tesSpan.style.opacity = "1.0";
  }
}
function addQuotedPostsContainer() {
  Array.from(document.getElementsByClassName("postMessage")).forEach(el => {
    if (el.getElementsByClassName("quotedPostsContainer").length === 0) {
      let container = document.createElement("span");
      container.classList.add("quotedPostsContainer");
      el.appendChild(container);
    }
  });
}
function addCollapseAndExpand() {
  Array.from(document.querySelectorAll(".post.reply")).forEach(postNode => {
    if (Array.from(postNode.children[1].children).filter(node => node.classList.contains("expandSpan")).length === 0) {
      let expandSpan = document.createElement("span");
      expandSpan.classList.add("expandSpan");
      expandSpan.classList.add("collapsible");
      expandSpan.innerText = "[↓]";
      expandSpan.title = "expand all 'quoted by' posts";
      expandSpan.style.color = "#1019d2e6";
      expandSpan.style.fontSize = "11px";
      expandSpan.style.paddingLeft = "5px";
      expandSpan.style.paddingRight = "5px";
      expandSpan.style.whiteSpace = "nowrap";
      let collapseSpan = document.createElement("span");
      collapseSpan.classList.add("collapseSpan");
      collapseSpan.classList.add("collapsible");
      collapseSpan.style.color = "#1019d2e6";
      collapseSpan.innerText = "[↑]";
      collapseSpan.title = "collapse all 'quoted by' posts";
      collapseSpan.style.fontSize = "11px";
      collapseSpan.style.paddingRight = "0px";
      collapseSpan.style.whiteSpace = "nowrap";
      postNode.children[1].insertBefore(expandSpan, postNode.querySelector(".postMenuBtn"));
      postNode.children[1].insertBefore(collapseSpan, postNode.querySelector(".postMenuBtn"));
    }
    let expand = postNode.querySelector(".expandSpan");
    let collapse = postNode.querySelector(".collapseSpan");
    expand.removeEventListener("click", expandSpanEL);
    expand.addEventListener("click", expandSpanEL);
    collapse.removeEventListener("click", collapseSpanEL);
    collapse.addEventListener("click", collapseSpanEL);
  });
}
function expandSpanEL(e) {
  Array.from(Array.from(e.target.parentNode.children).filter(node => node.classList.contains("backlink"))[0].children).forEach(node => {if (!node.children[0].classList.contains("linkfade")) node.children[0].click();});
}
function collapseSpanEL(e) {
  Array.from(Array.from(e.target.parentNode.children).filter(node => node.classList.contains("backlink"))[0].children).forEach(node => {if (node.children[0].classList.contains("linkfade")) node.children[0].click();});
}
function qSpanClick(e) {
  e.preventDefault();
}
function addMediaZoom() {
  Array.from(document.querySelectorAll(".fileText")).forEach(el => {
    if (Array.from(el.children).filter(el => el.classList.contains("mediaSizeChange")).length === 0) {
      let span = document.createElement("span");
      span.classList.add("mediaSizeChange");
      span.classList.add("collapsible");
      let spanBigger = document.createElement("span");
      let spanSmaller = document.createElement("span");
      let spanBiggerMore = document.createElement("span");
      let spanSmallerMore = document.createElement("span");
      let spanReset = document.createElement("span");
      spanBigger.classList.add("mediaSizeIncrease");
      spanSmaller.classList.add("mediaSizeDecrease");
      spanBiggerMore.classList.add("mediaSizeIncreaseMore");
      spanSmallerMore.classList.add("mediaSizeDecreaseMore");
      spanReset.classList.add("mediaSizeReset");
      spanBigger.innerText = "(++)";
      spanSmaller.innerText = "(--)";
      spanBiggerMore.innerHTML = `(<span style="font-size: 16px; position: relative; top: 1.5px;">++</span>)`;
      spanSmallerMore.innerHTML = `(<span style="font-size: 18px; position: relative; top: 2px;">--</span>)`;
      spanReset.innerText = "(Reset)";
      spanBigger.style.color = "#1019d2e6";
      spanBiggerMore.style.color = "#1019d2e6";
      spanSmaller.style.color = "#1019d2e6";
      spanSmallerMore.style.color = "#1019d2e6";
      spanReset.style.color = "#1019d2e6";
      spanReset.style.fontSize = "10px";
      span.innerHTML = `[ Size - ${spanSmaller.outerHTML} ${spanSmallerMore.outerHTML} | ${spanBigger.outerHTML} ${spanBiggerMore.outerHTML} | ${spanReset.outerHTML} ]`;
      el.appendChild(span);
      span.style.fontSize = "12px";
      span.style.position = "relative";
      span.style.bottom = "1px";
      span.style.paddingRight = "4px";
      span.style.paddingLeft = "4px";
      spanReset.style.position = "relative";
      spanReset.style.bottom = "1px";
    }
    let spanBigger = el.getElementsByClassName("mediaSizeIncrease")[0];
    spanBigger.removeEventListener("click", mediaSizeIncrease);
    let spanSmaller = el.getElementsByClassName("mediaSizeDecrease")[0];
    spanSmaller.removeEventListener("click", mediaSizeDecrease);
    let spanBiggerMore = el.getElementsByClassName("mediaSizeIncreaseMore")[0];
    spanBiggerMore.removeEventListener("click", mediaSizeIncreaseMore);
    let spanSmallerMore = el.getElementsByClassName("mediaSizeDecreaseMore")[0];
    spanSmallerMore.removeEventListener("click", mediaSizeDecreaseMore);
    let spanReset = el.getElementsByClassName("mediaSizeReset")[0];
    spanReset.removeEventListener("click", mediaSizeReset);
    spanBigger.addEventListener("click", mediaSizeIncrease);
    spanSmaller.addEventListener("click", mediaSizeDecrease);
    spanBiggerMore.addEventListener("click", mediaSizeIncreaseMore);
    spanSmallerMore.addEventListener("click", mediaSizeDecreaseMore);
    spanReset.addEventListener("click", mediaSizeReset);
  });
}
function mediaSizeIncrease(e) {
  let min = 0;
  let max = Number(e.target.parentNode.parentNode.parentNode.innerText.match(/\d+x\d+/)[0].split("x")[0]);
  let fileDiv = e.target.parentNode.parentNode.parentNode;
  if (Array.from(fileDiv.children).filter(el => el.tagName === "VIDEO").length !== 0) {
    let vidEl = Array.from(fileDiv.children).filter(el => el.tagName === "VIDEO")[0];
    if (vidEl.style.maxWidth) {
      vidEl.style.width = vidEl.style.maxWidth;
      vidEl.style.maxWidth = "";
      vidEl.style.maxHeight = "";
    }
    vidEl.style.height = "";
    let currentWidth = Number(vidEl.style.width.match(/\d+/));
    currentWidth += 20;
    vidEl.style.width = `${currentWidth}px`;
  } else {
    if (fileDiv.classList.contains("image-expanded")) {
      let img = Array.from(fileDiv.querySelectorAll("img")).filter(el => el.classList.contains("expanded-thumb"))[0];
      if (img) {
        if (img.style.maxWidth) {
          img.style.width = img.style.maxWidth;
          img.style.maxWidth = "";
          img.style.maxHeight = "";
        }
        img.style.height = "";
        let currentWidth = Number(img.style.width.match(/\d+/));
        currentWidth += 20;
        img.style.width = `${currentWidth}px`;
      }
    }
  }
}
function mediaSizeDecrease(e) {
  let min = 0;
  let max = Number(e.target.parentNode.parentNode.innerText.match(/\d+x\d+/)[0].split("x")[0]);
  let fileDiv = e.target.parentNode.parentNode.parentNode;
  if (Array.from(fileDiv.children).filter(el => el.tagName === "VIDEO").length !== 0) {
    let vidEl = Array.from(fileDiv.children).filter(el => el.tagName === "VIDEO")[0];
    if (vidEl.style.maxWidth) {
      vidEl.style.width = vidEl.style.maxWidth;
      vidEl.style.maxWidth = "";
      vidEl.style.maxHeight = "";
    }
    vidEl.style.height = "";
    let currentWidth = Number(vidEl.style.width.match(/\d+/));
    currentWidth -= 20;
    if (currentWidth < 0) {
      vidEl.style.width = `0px`;
      currentWidth = 0;
    } else {
      vidEl.style.width = `${currentWidth}px`;
    }
  } else {
    if (fileDiv.classList.contains("image-expanded")) {
      let img = Array.from(fileDiv.querySelectorAll("img")).filter(el => el.classList.contains("expanded-thumb"))[0];
      if (img) {
        if (img.style.maxWidth) {
          img.style.width = img.style.maxWidth;
          img.style.maxWidth = "";
          img.style.maxHeight = "";
        }
        img.style.height = "";
        let currentWidth = Number(img.style.width.match(/\d+/));
        currentWidth -= 20;
        if (currentWidth < 0) {
          img.style.width = `${max}px}`;
        } else {
          img.style.width = `${currentWidth}px`;
        }
      }
    }
  }
}
function mediaSizeIncreaseMore(e) {
  let min = 0;
  let max = Number(e.target.parentNode.parentNode.parentNode.innerText.match(/\d+x\d+/)[0].split("x")[0]);
  let fileDiv = e.target.parentNode.parentNode.parentNode.parentNode;
  if (Array.from(fileDiv.children).filter(el => el.tagName === "VIDEO").length !== 0) {
    let vidEl = Array.from(fileDiv.children).filter(el => el.tagName === "VIDEO")[0];
    if (vidEl.style.maxWidth) {
      vidEl.style.width = vidEl.style.maxWidth;
      vidEl.style.maxWidth = "";
      vidEl.style.maxHeight = "";
    }
    vidEl.style.height = "";
    let currentWidth = Number(vidEl.style.width.match(/\d+/));
    currentWidth += 100;
    vidEl.style.width = `${currentWidth}px`;
  } else {
    if (fileDiv.classList.contains("image-expanded")) {
      let img = Array.from(fileDiv.querySelectorAll("img")).filter(el => el.classList.contains("expanded-thumb"))[0];
      if (img) {
        if (img.style.maxWidth) {
          img.style.width = img.style.maxWidth;
          img.style.maxWidth = "";
          img.style.maxHeight = "";
        }
        img.style.height = "";
        let currentWidth = Number(img.style.width.match(/\d+/));
        currentWidth += 100;
        img.style.width = `${currentWidth}px`;
      }
    }
  }
}
function mediaSizeDecreaseMore(e) {
  let min = 0;
  let max = Number(e.target.parentNode.parentNode.parentNode.innerText.match(/\d+x\d+/)[0].split("x")[0]);
  let fileDiv = e.target.parentNode.parentNode.parentNode.parentNode;
  if (Array.from(fileDiv.children).filter(el => el.tagName === "VIDEO").length !== 0) {
    let vidEl = Array.from(fileDiv.children).filter(el => el.tagName === "VIDEO")[0];
    if (vidEl.style.maxWidth) {
      vidEl.style.width = vidEl.style.maxWidth;
      vidEl.style.maxWidth = "";
      vidEl.style.maxHeight = "";
    }
    vidEl.style.height = "";
    let currentWidth = Number(vidEl.style.width.match(/\d+/));
    currentWidth -= 100;
    if (currentWidth < 0) {
      vidEl.style.width = `0px`;
      currentWidth = 0;
    } else {
      vidEl.style.width = `${currentWidth}px`;
    }
  } else {
    if (fileDiv.classList.contains("image-expanded")) {
      let img = Array.from(fileDiv.querySelectorAll("img")).filter(el => el.classList.contains("expanded-thumb"))[0];
      if (img) {
        if (img.style.maxWidth) {
          img.style.width = img.style.maxWidth;
          img.style.maxWidth = "";
          img.style.maxHeight = "";
        }
        img.style.height = "";
        let currentWidth = Number(img.style.width.match(/\d+/));
        currentWidth -= 100;
        if (currentWidth < 0) {
          img.style.width = `${max}px}`;
        } else {
          img.style.width = `${currentWidth}px`;
        }
      }
    }
  }
}
function mediaSizeReset(e) {
  let width = Number(e.target.parentNode.parentNode.innerText.match(/\d+x\d+/)[0].split("x")[0]);
  let height = Number(e.target.parentNode.parentNode.innerText.match(/\d+x\d+/)[0].split("x")[1]);
  let fileDiv = e.target.parentNode.parentNode.parentNode;
  if (Array.from(fileDiv.children).filter(el => el.tagName === "VIDEO").length !== 0) {
    let vidEl = Array.from(fileDiv.children).filter(el => el.tagName === "VIDEO")[0];
    vidEl.style.width = `${width}px`;
    vidEl.style.height = `${height}px`;
  } else {
     if (fileDiv.classList.contains("image-expanded")) {
      let img = Array.from(fileDiv.querySelectorAll("img")).filter(el => el.classList.contains("expanded-thumb"))[0];
      if (img) {
        img.style.width = `${width}px`;
        img.style.height = `${height}px`;
      }
    }
  }
}
function addYTSizeChangeCapability() {
  //change yt size
}
function addRemoveCapability() {
  //turns all delete checkboxes to simply post removal buttons
  Array.from(document.getElementsByClassName("postInfo desktop")).forEach(el => el.children[0].addEventListener("click", postRemove));
}
function postRemove(e) {
  e.preventDefault();
  e.target.parentNode.parentNode.parentNode.remove();
}
function setPostColor() {
  if (!postColor) {
    if (document.querySelectorAll(".post.reply").length > 0) {
      let post = document.querySelector(".post.reply");
      let rgbStr = window.getComputedStyle(post).backgroundColor;
      let matches = rgbStr.match(/rgb\((\d+),\s?(\d+),\s?(\d+)\)/i);
      postColor = {r: Number(matches[1]), g: Number(matches[2]), b: Number(matches[3])};
    }
  }
}
function addColorPostsThread() {
  let btn = document.querySelector(".op").querySelector(".postMenuBtn");
  let span = document.createElement("span");
  span.classList.add("colorChange");
  span.title = "Color all posts in thread";
  let colorOn = document.createElement("span");
  let colorOff = document.createElement("span");
  colorOn.classList.add("colorOnThread");
  colorOff.classList.add("colorOffThread");
  colorOn.innerText = "Color Posts";
  colorOff.innerText = "Off";
  span.innerHTML = `[${colorOn.outerHTML} | ${colorOff.outerHTML}]`;
  btn.parentNode.insertBefore(span, btn);
  span.style.paddingLeft = "6px";
  span.style.paddingRight = "1px";
  span.style.fontSize = "11px";
  span.style.color = "rgb(46, 53, 144)";
  document.querySelector(".colorOnThread").addEventListener("click", colorPostsThreadOn);
  document.querySelector(".colorOffThread").addEventListener("click", colorPostsThreadOff);
}
function colorPostsThreadOn(e) {
  Array.from(document.querySelectorAll(".post.reply")).forEach(el => {
    el.style.backgroundColor = `rgb(${Math.random() > 0.8 ? postColor.r - (Math.random() * (colorDiff / 5)) : postColor.r + (Math.random() * (colorDiff * 1.45))},
    ${Math.random() > 0.8 ? postColor.g - (Math.random() * (colorDiff / 5)) : postColor.g + (Math.random() * (colorDiff * 1.45))},
    ${Math.random() > 0.8 ? postColor.b - (Math.random() * (colorDiff / 5)) : postColor.b + (Math.random() * (colorDiff * 1.45))})`;
  });
}
function colorPostsThreadOff(e) {
  Array.from(document.querySelectorAll(".post.reply")).forEach(el => {
    el.style.backgroundColor = `rgb(${postColor.r}, ${postColor.g}, ${postColor.b})`;
  });
}
function addCollapseExtraNodesToggle() {
  let btn = document.querySelector(".op").querySelector(".postMenuBtn");
  let span = document.createElement("span");
  span.classList.add("collapseExtraNodes");
  span.title = "Collapse all extra nodes in thread";
  span.innerText = "[Collapse extras]";
  btn.parentNode.insertBefore(span, btn);
  span.style.paddingLeft = "6px";
  span.style.paddingRight = "1px";
  span.style.fontSize = "11px";
  span.style.color = "rgb(46, 53, 144)";
  span.style.opacity = "1";
  span.addEventListener("click", collapseNodesToggle);
}
function collapseNodesToggle(e) {
  if (e.target.style.opacity === "1") {
    Array.from(document.querySelectorAll(".collapsible")).forEach(el => el.style.display = "none");
    e.target.style.opacity = "0.55";
  } else {
    Array.from(document.querySelectorAll(".collapsible")).forEach(el => el.style.display = "unset");
    e.target.style.opacity = "1";
  }
}
function addCollapseExtrasInPosts() {
  Array.from(document.querySelectorAll(".postInfo.desktop")).forEach((el, i) => {
    if (i === 0) return;
    if (Array.from(el.children).filter(child => child.classList.contains("collapseExtraNodes")).length === 0) {
      el.parentNode.dataset.originalColor = window.getComputedStyle(el).backgroundColor;
      let span = document.createElement("span");
      span.classList.add("collapseExtraNodes");
      span.classList.add("collapsible");
      span.title = "Collapse all extra nodes of inlined posts within this one, including this - reverse with arrow button";
      span.innerHTML = `[Collapse extras]`;
      el.insertBefore(span, el.querySelector(".postMenuBtn"));
      span.style.paddingLeft = "6px";
      span.style.paddingRight = "1px";
      span.style.fontSize = "11px";
      span.style.color = "rgb(46, 53, 144)";
    }
    let collapseSpan = el.querySelector(".collapseExtraNodes");
    collapseSpan.removeEventListener("click", collapsePostsPostOn);
    collapseSpan.addEventListener("click", collapsePostsPostOn);
  });
}
function collapsePostsPostOn(e) {
  Array.from(e.target.parentNode.parentNode.parentNode.querySelectorAll(".post.reply")).forEach(el => {
    Array.from(el.querySelectorAll(".collapsible")).forEach(collapsibleEl => collapsibleEl.style.display = "none");
  });
}
function addExpandExtrasInPosts() {
  Array.from(document.querySelectorAll(".postInfo.desktop")).forEach((el, i) => {
    if (i === 0) return;
    let buttonMenuSpan = el.querySelector(".postMenuBtn");
    buttonMenuSpan.removeEventListener("click", expandPostsPostOn);
    buttonMenuSpan.addEventListener("click", expandPostsPostOn);
  });
}
function expandPostsPostOn(e) {
  Array.from(e.target.parentNode.parentNode.parentNode.querySelectorAll(".post.reply")).forEach(el => {
    Array.from(el.querySelectorAll(".collapsible")).forEach(collapsibleEl => collapsibleEl.style.display = "unset");
  });
}
function addColorPosts() {
  Array.from(document.querySelectorAll(".postInfo.desktop")).forEach((el, i) => {
    if (i === 0) return;
    if (Array.from(el.children).filter(child => child.classList.contains("colorChange")).length === 0) {
      el.parentNode.dataset.originalColor = window.getComputedStyle(el).backgroundColor;
      let span = document.createElement("span");
      span.classList.add("colorChange");
      span.classList.add("collapsible");
      span.title = "Color all inlined posts within this one";
      let colorOn = document.createElement("span");
      let colorOff = document.createElement("span");
      colorOn.classList.add("colorOnPost");
      colorOff.classList.add("colorOffPost");
      colorOn.innerText = "Color Posts";
      colorOff.innerText = "Off";
      span.innerHTML = `[${colorOn.outerHTML} | ${colorOff.outerHTML}]`;
      el.insertBefore(span, el.querySelector(".postMenuBtn"));
      span.style.paddingLeft = "6px";
      span.style.paddingRight = "1px";
      span.style.fontSize = "11px";
      span.style.color = "rgb(46, 53, 144)";
    }
    let colorOn = el.querySelector(".colorOnPost");
    let colorOff = el.querySelector(".colorOffPost");
    colorOn.removeEventListener("click", colorPostsPostOn);
    colorOn.addEventListener("click", colorPostsPostOn);
    colorOff.removeEventListener("click", colorPostsPostOff);
    colorOff.addEventListener("click", colorPostsPostOff);
  });
}
function colorPostsPostOn(e) {
  //console.log("colorPostsPoston", e.target);
  Array.from(e.target.parentNode.parentNode.parentNode.querySelectorAll(".post.reply")).forEach(el => {
    let nums = getTwoToThreeHigherNums();
    let randomDiff = getAtLeastOneQuarterFromRandom();
    let mappedNums = nums.map((num, idx) => num > 0.5 ? getSpecificColorFromMappedNumIdx(idx) + (randomDiff * colorDiff) : getSpecificColorFromMappedNumIdx(idx) - (randomDiff * colorDiff));
    el.style.backgroundColor = `rgb(${mappedNums[0]}, ${mappedNums[1]}, ${mappedNums[2]})`;
  });
}
function colorPostsPostOff(e) {
  Array.from(e.target.parentNode.parentNode.parentNode.querySelectorAll(".post.reply")).forEach(el => {
    el.style.backgroundColor = `rgb(${postColor.r}, ${postColor.g}, ${postColor.b})`;
  });
}
function getTwoToThreeHigherNums() {
    while(true) {
        let arr = [Math.random(), Math.random(), Math.random()];
        let numbersAboveHalf = arr.filter(el => el > 0.5).length;
        if (numbersAboveHalf > 1 && numbersAboveHalf < 4) {
            if (numbersAboveHalf === 3) {
                if (arr.filter(el => el > 0.85).length > 2) return arr;
            } else return arr;
        }
    }
}
function getAtLeastOneQuarterFromRandom() {
  while(true) {
    let num = Math.random();
    if (num > 0.25) return num;
  }
}
function getSpecificColorFromMappedNumIdx(idx) {
   // if (idx === 0) return 215;
   // if (idx === 1) return 215;
   // else return 215;
  if (idx === 0) return postColor.r;
  if (idx === 1) return postColor.g;
  else return postColor.b;
}
function addPostsDraggableToggleThread() {
  let btn = document.querySelector(".op").querySelector(".postMenuBtn");
  let metaSpan = document.createElement("span");
  metaSpan.classList.add("postsDraggableContainer");
  let spanDrag = document.createElement("span");
  let spanDragReset = document.createElement("span");
  spanDragReset.classList.add("postsDraggableReset");
  spanDrag.classList.add("postsDraggableToggle");
  spanDrag.title = "Toggle for whether posts are draggable/movable";
  spanDrag.innerText = "Posts Draggable";
  spanDragReset.title = "Reset positions of all posts";
  spanDragReset.innerText = "Reset";
  metaSpan.innerHTML = `[${spanDrag.outerHTML} | ${spanDragReset.outerHTML}]`;
  btn.parentNode.insertBefore(metaSpan, btn);
  metaSpan.style.paddingLeft = "6px";
  metaSpan.style.paddingRight = "1px";
  metaSpan.style.fontSize = "11px";
  metaSpan.style.color = "rgb(46, 53, 144)";
  spanDrag = document.querySelector(".postsDraggableToggle");
  spanDrag.style.opacity = "1";
  spanDragReset = document.querySelector(".postsDraggableReset");
  spanDrag.addEventListener("click", postsDraggableToggle);
  spanDragReset.addEventListener("click", postsDraggableReset);
}
function postsDraggableReset() {
  let posts = Array.from(document.querySelectorAll(".post.reply"));
  posts.forEach(post => {
    //post.style.position = "unset";
    post.style.top = "";
    post.style.bottom = "";
    post.style.left = "";
    post.style.right = "";
  });
}
let currentStartDragHorizontal = null;
let currentStartDragVertical = null;
let currentStartDragHorizontalDiff = null;
let currentStartDragVerticalDiff = null;
let currentStartDragNode = null;
let currentDragZIndex = 1;
function postsDraggableToggle(e) {
  if (e.target.style.opacity === "1") {
    e.target.style.opacity = "0.55";
    document.addEventListener("dragover", documentDragOver, true);
    document.addEventListener("drop", documentDrop, true);
    let posts = Array.from(document.querySelectorAll(".post.reply"));
    posts.forEach(post => {
      if (post.draggable !== true) {
        post.draggable = true;
        post.addEventListener("dragstart", postDragStart, true);
      }
    });
  } else {
    e.target.style.opacity = "1";
    document.removeEventListener("dragover", documentDragOver);
    document.removeEventListener("drop", documentDrop);
    let posts = Array.from(document.querySelectorAll(".post.reply"));
    posts.forEach(post => {
      if (post.draggable !== false) {
        post.draggable = false;
        post.removeEventListener("dragstart", postDragStart);
      }
    });
  }
}
function addPostDraggableTogglePost() {
  Array.from(document.querySelectorAll(".postInfo.desktop")).forEach((el, i) => {
    if (i === 0) return;
    if (Array.from(el.children).filter(child => child.classList.contains("postsDraggableContainer")).length === 0) {
      el.parentNode.dataset.originalColor = window.getComputedStyle(el).backgroundColor;
      let metaSpan = document.createElement("span");
      metaSpan.classList.add("postsDraggableContainer");
      metaSpan.classList.add("collapsible");
      let spanDrag = document.createElement("span");
      let spanDragReset = document.createElement("span");
      spanDragReset.classList.add("postsDraggableResetPost");
      spanDrag.classList.add("postsDraggableTogglePost");
      spanDrag.title = "Toggle for whether inlined posts are draggable/movable";
      spanDrag.innerText = "Posts Draggable";
      spanDragReset.title = "Reset positions of all inlined posts";
      spanDragReset.innerText = "Reset";
      metaSpan.innerHTML = `[${spanDrag.outerHTML} | ${spanDragReset.outerHTML}]`;
      el.insertBefore(metaSpan, el.querySelector(".postMenuBtn"));
      metaSpan.style.paddingLeft = "6px";
      metaSpan.style.paddingRight = "1px";
      metaSpan.style.fontSize = "11px";
      metaSpan.style.color = "rgb(46, 53, 144)";
    }
    let spanDrag = el.querySelector(".postsDraggableTogglePost");
    spanDrag.style.opacity = "1";
    let spanDragReset = el.querySelector(".postsDraggableResetPost");
    spanDrag.removeEventListener("click", postsDraggableTogglePost);
    spanDragReset.removeEventListener("click", postsDraggableResetPost);
    spanDrag.addEventListener("click", postsDraggableTogglePost);
    spanDragReset.addEventListener("click", postsDraggableResetPost);
  });
}
function postsDraggableResetPost(e) {
  let posts = Array.from(e.target.parentNode.parentNode.parentNode.querySelectorAll(".post.reply"));
  posts.forEach(post => {
    //post.style.position = "unset";
    post.style.top = "";
    post.style.bottom = "";
    post.style.left = "";
    post.style.right = "";
  });
}
function postsDraggableTogglePost(e) {
  if (e.target.style.opacity === "1") {
    e.target.style.opacity = "0.55";
    document.addEventListener("dragover", documentDragOver, true);
    document.addEventListener("drop", documentDrop, true);
    let posts = Array.from(e.target.parentNode.parentNode.parentNode.querySelectorAll(".post.reply"));
    posts.forEach(post => {
      if (post.draggable !== true) {
        post.draggable = true;
        post.addEventListener("dragstart", postDragStart, true);
      }
    });
  } else {
    e.target.style.opacity = "1";
    document.removeEventListener("dragover", documentDragOver);
    document.removeEventListener("drop", documentDrop);
    let posts = Array.from(e.target.parentNode.parentNode.parentNode.querySelectorAll(".post.reply"));
    posts.forEach(post => {
      if (post.draggable !== false) {
        post.draggable = false;
        post.removeEventListener("dragstart", postDragStart);
      }
    });
  }
}
function postDragStart(e) {
  e.dataTransfer.setData("text", e.target.outerHTML);
  //currentStartDragHorizontalDiff = Math.floor(e.clientX - e.target.getBoundingClientRect().left);
  //currentStartDragVerticalDiff = Math.floor(e.clientY - e.target.getBoundingClientRect().top);
  currentStartDragHorizontal = e.clientX;
  currentStartDragVertical = e.clientY + window.scrollY;
  currentStartDragNode = e.target;
  //e.target.style.position = "absolute";
  if (e.target.style.top === "") e.target.style.top = "0px";
  if (e.target.style.left === "") e.target.style.left = "0px";
  e.target.style.zIndex = JSON.stringify(currentDragZIndex);
  currentDragZIndex++;
}
function documentDragOver(e) {
  e.preventDefault();
}
function documentDrop(e) {
  e.preventDefault();
  currentStartDragNode.style.left = `${Number(currentStartDragNode.style.left.match(/([-\.\d]+)px/)[1]) + e.clientX - currentStartDragHorizontal}px`;
  currentStartDragNode.style.top = `${Number(currentStartDragNode.style.top.match(/([-\.\d]+)px/)[1]) + window.scrollY + e.clientY - currentStartDragVertical}px`;
  //currentStartDragNode.style.left = `${e.clientX - currentStartDragHorizontalDiff}px`;
  //currentStartDragNode.style.top = `${window.scrollY + e.clientY - currentStartDragVerticalDiff}px`;
  //currentStartDragNode.style.position = "absolute";
  if (currentStartDragNode.style.position !== "relative") currentStartDragNode.style.position = "relative";
  currentStartDragHorizontalDiff = null;
  currentStartDragVerticalDiff = null;
  currentStartDragNode = null;
}
function addRemoveYousInPost() {
  Array.from(document.querySelectorAll(".postInfo.desktop")).forEach(el => {
    if (Array.from(el.children).filter(el => el.classList.contains("removeYouPost")).length === 0) {
      let btn = el.querySelector(".postMenuBtn");
      let newSpan = document.createElement("span");
      newSpan.innerText = "[Remove (You)s]";
      newSpan.style.paddingLeft = "6px";
      newSpan.style.paddingRight = "1px";
      newSpan.style.fontSize = "11px";
      newSpan.style.color = "rgb(46, 53, 144)";
      newSpan.classList.add("removeYouPost");
      newSpan.classList.add("collapsible");
      newSpan.title = "Remove all (You)s from current post (works recursively downward)";
      btn.parentNode.insertBefore(newSpan, btn);
    }
  });
  Array.from(document.querySelectorAll(".removeYouPost")).forEach(el => {
    el.removeEventListener("click", removeYouPost);
    el.addEventListener("click", removeYouPost);
  });
}
function removeYouPost(e) {
  let post = e.target.parentNode.parentNode;
  Array.from(post.querySelectorAll("a.quotelink")).forEach(el => {
    el.innerText = el.innerText.replace(" (You)", "");
  });
}
function addEditabilityToPost() {
  Array.from(document.querySelectorAll(".postInfo.desktop")).forEach(el => {
    if (Array.from(el.children).filter(el => el.classList.contains("addEditability")).length === 0) {
      let btn = el.querySelector(".postMenuBtn");
      let newSpan = document.createElement("span");
      newSpan.innerText = "[Editable]";
      newSpan.style.paddingLeft = "6px";
      newSpan.style.paddingRight = "1px";
      newSpan.style.fontSize = "11px";
      newSpan.style.color = "rgb(46, 53, 144)";
      newSpan.classList.add("addEditability");
      newSpan.classList.add("collapsible");
      newSpan.title = "Make post type-editable";
      btn.parentNode.insertBefore(newSpan, btn);
    }
  });
  Array.from(document.querySelectorAll(".addEditability")).forEach(el => {
    el.removeEventListener("click", toggleEditabilityForPost);
    el.addEventListener("click", toggleEditabilityForPost);
  });
}
function toggleEditabilityForPost(e) {
  let flag = "false";
  if ((e.target.style.opacity === "1") || !e.target.style.opacity) {
    e.target.style.opacity = "0.55";
    flag = "true";
  } else {
    e.target.style.opacity = "1";
    flag = "false";
  }
    let post = e.target.parentNode.parentNode;
    let allEmbeddedPosts = Array.from(post.querySelectorAll(".postMessage"));
    allEmbeddedPosts.forEach(post => post.contentEditable = flag);
}
function qEV(e, node=e.target) {
  //console.log("quote clicked.");
  e.preventDefault();
  if (!node.classList.contains("linkfade")) {
    //if not grayed out, first time clicking, adding node
    node.classList.add("linkfade");
    //finding post and constructing new post
    let postsArr = Array.from(document.getElementsByClassName("postNum desktop")).filter(el => el.children[1].innerText === node.innerText.slice(2));
    let post = postsArr[0].parentNode.parentNode.parentNode;
    let newPost = post.cloneNode(true);
    //newPost.outerHTML = post.outerHTML;
    newPost.classList.add("inlined");
    newPost.children[1].classList.remove("highlight");
    newPost.style.display = "";
    newPost.style.marginTop = "10px";
    newPost.children[1].style.border = "1px solid rgba(172, 127, 127, 0.6)";
    newPost.children[1].style.borderRadius = "2px";
    newPost.children[0].style.display = "none";
    //adding to parent post
    if (node.parentNode.parentNode.parentNode.parentNode.children[2].classList.contains("file")) {
        Array.from(node.parentNode.parentNode.parentNode.parentNode.children[3].children).filter(el => el.classList.contains("quotedPostsContainer"))[0].appendChild(newPost);
        if (JSON.parse(localStorage.getItem("4chan-settings")).inlineQuotes) setTimeout(function() {node.parentNode.parentNode.parentNode.parentNode.children[3].children[0].remove();});
    } else {
        Array.from(node.parentNode.parentNode.parentNode.parentNode.children[2].children).filter(el => el.classList.contains("quotedPostsContainer"))[0].appendChild(newPost);
        if (JSON.parse(localStorage.getItem("4chan-settings")).inlineQuotes) setTimeout(function() {node.parentNode.parentNode.parentNode.parentNode.children[2].children[0].remove();});
    }
    setTimeout(function(){newPost.style.display = "";}, 10);
    //resetting event listeners
    resetQLEV();
  } else {
    //grayed out, second time clicking, removing node
    node.classList.remove("linkfade");
    let idx = node.parentNode.parentNode.parentNode.parentNode.children[2].classList.contains("file") ? 3 : 2;
    Array.from(node.parentNode.parentNode.parentNode.parentNode.children[idx].children).forEach(innerNode => {
      if (innerNode.classList.contains("quotedPostsContainer")) {
        Array.from(innerNode.children).forEach(el => {
          if (el.classList.contains("inlined") && node.innerText.slice(2) === el.children[1].children[1].children[3].children[1].innerText) el.remove();
        });
      }
    });
  }
}
function resetQLEV() {
  let newQuotesList = Array.from(document.getElementsByClassName("quotelink")).filter(node => window.getComputedStyle(node).getPropertyValue("font-size") === "10.6667px");
  newQuotesList.forEach(node => node.removeEventListener("click", qEV));
  newQuotesList.forEach(node => node.addEventListener("click", qEV));
  let newSpanList = Array.from(document.getElementsByTagName("span")).filter(el => {
        try {
          if (el.children.length > 0 && el.children[0].classList.contains("quotelink")) return true;
        } catch(error) {
          return false;
        }
    });
  newSpanList.forEach(el => el.removeEventListener("click", qSpanClick));
  newSpanList.forEach(el => el.addEventListener("click", qSpanClick));
  Array.from(document.getElementsByClassName("postInfo desktop")).forEach(el => el.children[0].removeEventListener("click", postRemove));
  addRemoveCapability();
  addCollapseAndExpand();
  let tesQuoteSpans = Array.from(document.getElementsByClassName("tessellateQuotes"));
  tesQuoteSpans.forEach(el => el.removeEventListener("click", tessellateQuotes));
  tesQuoteSpans.forEach(el => el.addEventListener("click", tessellateQuotes));
  addTessellationQuotes();
  addQuotedPostsContainer();
  addMediaZoom();
  addColorPosts();
  setPostColor();
  addPostDraggableTogglePost();
  addRemoveYousInPost();
  addEditabilityToPost();
  addCollapseExtrasInPosts();
  addExpandExtrasInPosts();
  expandPostsPostOn();
}
let observer = new MutationObserver(resetQLEV);
observer.observe(document.getElementsByClassName("thread")[0], {childList: true});
let quotePreviewObserver = new MutationObserver(quotePreviewHandler);
quotePreviewObserver.observe(document.querySelector("body"), {childList: true});
function quotePreviewHandler(newNodes) {
  if (newNodes[0] && newNodes[0].addedNodes.length > 0) newNodes[0].addedNodes.forEach(el => {if (el.id === "quote-preview") el.style.zIndex = currentDragZIndex++;});
}