Codeforces_spoiler_tutorial

Wrap spoilers in Codeforces tutorial in spoiler blocks

2020-05-15 기준 버전입니다. 최신 버전을 확인하세요.

이 스크립트를 설치하려면 Tampermonkey, Greasemonkey 또는 Violentmonkey와 같은 확장 프로그램이 필요합니다.

You will need to install an extension such as Tampermonkey to install this script.

이 스크립트를 설치하려면 Tampermonkey 또는 Violentmonkey와 같은 확장 프로그램이 필요합니다.

이 스크립트를 설치하려면 Tampermonkey 또는 Userscripts와 같은 확장 프로그램이 필요합니다.

이 스크립트를 설치하려면 Tampermonkey와 같은 확장 프로그램이 필요합니다.

이 스크립트를 설치하려면 유저 스크립트 관리자 확장 프로그램이 필요합니다.

(이미 유저 스크립트 관리자가 설치되어 있습니다. 설치를 진행합니다!)

이 스타일을 설치하려면 Stylus와 같은 확장 프로그램이 필요합니다.

이 스타일을 설치하려면 Stylus와 같은 확장 프로그램이 필요합니다.

이 스타일을 설치하려면 Stylus와 같은 확장 프로그램이 필요합니다.

이 스타일을 설치하려면 유저 스타일 관리자 확장 프로그램이 필요합니다.

이 스타일을 설치하려면 유저 스타일 관리자 확장 프로그램이 필요합니다.

이 스타일을 설치하려면 유저 스타일 관리자 확장 프로그램이 필요합니다.

(이미 유저 스타일 관리자가 설치되어 있습니다. 설치를 진행합니다!)

// ==UserScript==
// @name         Codeforces_spoiler_tutorial
// @version      0.1.0
// @description  Wrap spoilers in Codeforces tutorial in spoiler blocks
// @match        *://codeforces.com/blog/entry/*
// @match        *://codeforces.com/blog/entry/*
// @match        *://codeforces.com/contest/*
// @grant        GM_getValue
// @grant        GM_setValue
// @run-at       document-start
// @namespace    https://greasyfork.org/users/410786
// ==/UserScript==

(function(){

if(location.pathname.startsWith("/contest/")){
	window.addEventListener("DOMContentLoaded", function(){
		// Stores tutorial links. Sometimes it's not linked in some old contests, see  https://codeforces.com/blog/entry/7749?locale=en
		for(const element of document.querySelectorAll(".roundbox ul li a[href*='/blog/entry/'"))
			if(element.innerText.match(/Tutorial|Разбор задач/))
				GM_setValue("isTutorial"+element.href.match(/\/blog\/entry\/(\d+)/)[1], true)
	})
	return
}



function wrappedInSpoiler(node){
	while(node!==null){
		if(node.classList && node.classList.contains("spoiler")) return true;
		node=node.parentNode;
	}
	return false;
}

var newStyle=false

function wrapInSpoiler(nodes, spoilerTitle){
	if(!Array.isArray(nodes)) nodes=[nodes]
	if(nodes.length==0) return

	const spoilerElement=document.createElement("div")
	spoilerElement.classList.add("spoiler")
	spoilerElement.innerHTML='<b class="spoiler-title"></b><div class="spoiler-content" style="display: none;"></div>'
	spoilerElement.firstElementChild.innerText=spoilerTitle

	console.log("Wrapping in spoiler: ", nodes)
	//nodes[0].insertAdjacentElement('beforebegin', spoilerElement);
	nodes[0].parentNode.insertBefore(spoilerElement, nodes[0]);
	nodes.forEach(function(node){
		spoilerElement.lastElementChild.appendChild(node) // "If the given child is a reference to an existing node in the document, appendChild() moves it from its current position to the new position"
	})
}

function wrapOldStyle(){
	//const headerSelector="h2,h3,a[href^='/contest'][href*='/problem/']"
	const headerSelector="h2,h3,hr"
	for(const element of document.getElementsByClassName("content")[0].querySelectorAll(headerSelector)){
		const elements=[]
		let tmp=element.nextSibling
		console.log(tmp)
		while(tmp!==null && ((tmp instanceof Text) || !tmp.matches(headerSelector))){
			elements.push(tmp)
			tmp=tmp.nextSibling
		console.log(tmp)
		}
		wrapInSpoiler(elements, "Tutorial")
	}
}

const observer=new MutationObserver(function(mutationList, observer){
	for(const mutation of mutationList){
		for(const node of mutation.addedNodes){
			if(node instanceof Element && node.matches("div.roundbox.meta")){
				console.log("Detect meta box", "isTutorial"+location.href.match(/\/blog\/entry\/(\d+)/)[1])
				if(GM_getValue("isTutorial"+location.href.match(/\/blog\/entry\/(\d+)/)[1])){
					if(!newStyle) wrapOldStyle()
				}
				observer.disconnect()
				return
			}

			if(
				node.tagName==="SPAN" && node.style.padding==="0px 0.35em" &&
				node.firstChild instanceof Text &&
				["Tutorial of ", "Разбор задач "].includes(node.firstChild.nodeValue)
				//node.previousElementSibling.tagName==="IMG" && node.previousElementSibling.src.includes("paperclip")
			){
				if(!newStyle) wrapOldStyle()
				observer.disconnect()
				return
			}

			if(node.classList!==undefined && node.classList.contains("problemTutorial")){
				newStyle=true
				if(!wrappedInSpoiler(node)){
					var spoilerTitle='Tutorial'
					const problemcode=node.getAttribute("problemcode")
					if(problemcode) spoilerTitle+=' - '+problemcode

					wrapInSpoiler(node, spoilerTitle)
				}
			}
		}
	}
})
observer.observe(document, { childList: true, subtree: true });



})()