Skip to content

Instantly share code, notes, and snippets.

@LoserAntbear
Last active November 16, 2022 21:46
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save LoserAntbear/beb2d9e2ab87db7f218f9936c4000805 to your computer and use it in GitHub Desktop.
Save LoserAntbear/beb2d9e2ab87db7f218f9936c4000805 to your computer and use it in GitHub Desktop.
DTF Show hidden comment userscript
// ==UserScript==
// @name DTF Show hidden comment
// @description It tries to fetch and show to you hidden comments
// @namespace http://tampermonkey.net/
// @version 0.1
// @author LoserAntbear
// @match https://dtf.ru/*
// @supportURL https://gist.github.com/LoserAntbear
// @updateURL https://gist.github.com/LoserAntbear/beb2d9e2ab87db7f218f9936c4000805
// @downloadURL https://gist.github.com/LoserAntbear/beb2d9e2ab87db7f218f9936c4000805
// @icon https://www.google.com/s2/favicons?sz=64&domain=dtf.ru
// @grant none
// ==/UserScript==
const LATEST_BUTTON_IMPLEMENTATION_STRING='<div class="v-repost"><div title="Посмотреть, что он там пишет" class="v-repost__button"><svg height="22" width="22" class="icon icon--v_repost"><use xlink:href="#v_repost"></use></svg> <!----></div> <!----> <!----></div>',ERROR_ID_KEY="error-id",ERROR_HIDE_TIMEOUT_MS=5e3,ERR_GENERIC=Error("Что-то произошло, но я не в курсе."),ERR_NOT_COMMENT_FOUND=Error("Мы искали комментарий, но, увы, не вышло"),CSS_RULES=["@keyframes spin{100%{ -webkit-transform: rotate(360deg);transform: rotate(360deg);}}",".spin{animation-name: spin;animation-duration: 1s;animation-iteration-count: infinite;animation-timing-function: linear;}"],HEADERS={"x-this-is-csrf":"THIS IS SPARTA!"},CUSTOM_EVENTS={articleOpen:new Event("articleOpen"),articleClose:new Event("articleClose")},MUTATION_TYPES={other:"other",added:"added",removed:"removed"};function stringToElement(e){let t=document.createElement("template");return t.innerHTML=e.trim(),t.content.firstChild}function findCommentElement(e,t){let n=t.querySelectorAll(`div[data-id="${e}"]`);return n.length>1?null:n[0]}function replaceHiddenComment(e,t){let n=findCommentElement(e,document);if(n){let i=findCommentText(n);i?.replaceWith(t)}}function findCommentInResponse(e,t){return e.find(({id:e})=>e===Number(t))}function findCommentText(e){return e.querySelector('div[class="comment__text"]')}function createErrorElement(e){let t="error-"+new Date().getTime(),n=document.createElement("p"),i=document.createTextNode(e);return n.setAttribute(ERROR_ID_KEY,t),n.setAttribute("style","color: var(--color-text-negative); padding-left: 8px;"),n.appendChild(i),n}function removeError(e){let t=document.querySelector(`[${ERROR_ID_KEY}="${e}"]`);t?.remove()}function fetchComment(e,t){let n=buildFetchCommentsUrl(e),i=createFetchCommentPayload(t);return fetch(n,{method:"POST",credentials:"omit",body:i,headers:HEADERS}).then(e=>e.json()).then(({data:e})=>{let n=findCommentInResponse(e?.items||[],t),i=n?.html;return i?stringToElement(i):Promise.reject(ERR_NOT_COMMENT_FOUND)})}function buildFetchCommentsUrl(e){return[document.location.origin,"comments","loading",e].join("/")}function createFetchCommentPayload(e){let t=new FormData;return t.append("ids",e),t.append("with_subtree",!0),t.append("mode","raw"),t}function handleFetchError(e,t=ERR_GENERIC){let n=findCommentElement(e,document),i=findComentContent(n),o=findCommentText(n),r=createErrorElement(t.message);i.insertBefore(r,o),setTimeout(removeError.bind(null,r.getAttribute(ERROR_ID_KEY)),5e3)}function createBaseButton(){let e=createFallbackRepostButton();return e.removeAttribute("id"),e}function createFallbackRepostButton(){return stringToElement('<div class="v-repost"><div title="Посмотреть, что он там пишет" class="v-repost__button"><svg height="22" width="22" class="icon icon--v_repost"><use xlink:href="#v_repost"></use></svg> <!----></div> <!----> <!----></div>')}function addFetchButton(e,t,n){let i=Number(n?.dataset?.id),o=findComentContent(n),r=findCommentText(n),a=createFetchButton(t,i,e);o.insertBefore(a,r)}function findComentContent(e){return e.querySelector(".comment__content")}function findCommentText(e){return e.querySelector(".comment__text")}function createFetchButton(e,t,n){let i=n.cloneNode(!0);return i.setAttribute("data-fetch-id",t),i.addEventListener("click",handleFetchPress.bind(i,i,e,t)),i}function handleFetchPress(e,t,n){!e?.dataset?.isLoading&&(onFetchStart(e),fetchComment(t,n).then(replaceHiddenComment.bind(null,n)).catch(handleFetchError.bind(null,n)).finally(()=>{onFetchEnd(e)}))}function onFetchStart(e){e.classList.add("spin"),e.setAttribute("data-is-loading",!0)}function onFetchEnd(e){e.classList.remove("spin"),e.setAttribute("data-is-loading",!1)}function handleCommentsMutation(e,t){let n=t.find(({addedNodes:t})=>Array.from(t).filter(t=>e.includes(t?.dataset?.id)));n.forEach(e=>{e.remove()})}function getHiddenCommentsIds(e){return e?.map(e=>e?.dataset?.id).filter(Boolean)}function initCommentsUdpdate(e){let t=findCommentsSection(e);if(t){let n=updateHiddenComments(t),i=getHiddenCommentsIds(n),o=initObserver(t,handleCommentsMutation.bind(null,i)),r=()=>{handleArticleClose(o),document.removeEventListener(CUSTOM_EVENTS.articleClose.type,r)};document.addEventListener(CUSTOM_EVENTS.articleClose.type,r)}}function updateHiddenComments(e){let t=Array.from(findHiddenComments(e)),n=findArticleId();return addButtonsToComments(t,n),t}function findHiddenComments(e){return e.getElementsByClassName("comment--removed")}function findArticleId(){let{articleInfo:e}=document.querySelector("[data-article-info]")?.dataset||{};return e?Number(JSON.parse(e).id):null}function addButtonsToComments(e,t){let n=createBaseButton();e.forEach(addFetchButton.bind(null,n,t))}function findMainNode(){return document.querySelector('[class="main layout"]')}function findCommentsSection(e){return e.querySelector('[class="comments__body"]')}function observeMainForCommentsSection(){let e=findMainNode();initObserver(e,handleMainMutation)}function handleMainMutation(e){let t=getUpdatedPageWrapper(e),n=getMutationType(t);n===MUTATION_TYPES.added?(initCommentsUdpdate(t.target),document.dispatchEvent(CUSTOM_EVENTS.articleOpen)):n===MUTATION_TYPES.removed&&document.dispatchEvent(CUSTOM_EVENTS.articleClose)}function getUpdatedPageWrapper(e){return e.find(({target:e})=>"page_wrapper"===e.id)}function getMutationType({addedNodes:e,removedNodes:t}={}){let n=MUTATION_TYPES.other;return n=validateMutationType(e,n,MUTATION_TYPES.added),n=validateMutationType(t,n,MUTATION_TYPES.removed)}function validateMutationType(e,t,n){return t===MUTATION_TYPES.other?verifyMutationType(e,n):t}function verifyMutationType(e,t){if(e){let n=findPageEntry(e);if(n)return t}return MUTATION_TYPES.other}function findPageEntry(e){return Array.from(e).find(e=>e?.classList?.contains("page--entry"))}function initObserver(e,t,n={attributes:!1,childList:!0,subtree:!0}){if(!e)return;let i=new MutationObserver(t);return i.observe(e,n),i}function handleArticleClose(e){e&&e.disconnect()}function injectCSS(){if(!document.styleSheets.length)return;let e=document.styleSheets.item(document.styleSheets.length-1);CSS_RULES.forEach(t=>e?.insertRule(t,e.length))}function handleReadyStateChange(e,t){["complete","interactive"].includes(t.target.readyState)&&e()}function handleAppInit(){let e=findMainNode();e&&initCommentsUdpdate(e)}function execute(){injectCSS(),observeMainForCommentsSection(),document.addEventListener("readystatechange",handleReadyStateChange.bind(null,handleAppInit))}execute();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment