IUC / TokkiPlugin_Kch

// ==UserScript==
// @name        TokkiPlugin_Kch
// @grant       GM_download
// @grant       GM_setValue
// @grant       GM_getValue
// @grant       GM_addStyle
// @match       *://kpop.re/*/*
// @exclude-match *://kpop.re/all/*
// @exclude-match *://kpop.re/admin/*
// @version     1.3.2
// @author      IUC
// @license GPL-3.0-only
// @updateURL https://openuserjs.org/meta/IUC/TokkiPlugin_Kch.meta.js
// @description Adds certain features to kch. Look at options button to activate the features.
// Changelog:
// 1.3.2 
// changed definition of args to make new openuserjs checker work
// 1.3.1
// Added postID_files feature to add postID_ in front of downloaded filenames. (Can be activated and deactivated by toggling the option without site re-loading)
// Adopted change to thumbnail feature.
// ==/UserScript==
/*jshint esversion: 6 */
(function(){
  
  //From https://gitlab.com/loopvid/scripts/-/blob/master/userscripts/kpop.re_post_id_fix.user.js
  // version 1.1
  // Feature: Highlights a post you select
  function fix_post_link(post) {
    var match,
      regex = /[#](\d+)/,
      link = post.querySelector(
        '.post-header a.post-id:not([data-id_fixed="true"])');
    if (link) {
      match = regex.exec(link.href);
      if (match) {
        link.href = '#post'+match[1];
      }
      link.dataset.id_fixed = 'true';
    }
  }
  //------------------------------------------------
  //anti-work mode
  //make all posts picture and webm only and hide no-media posts
  function toggle_anti_workmode(hidden) {
    if (hidden) {
      GM_addStyle('.post_whole_visibility { display: none }');
      GM_addStyle('.post_body_visibility { display: none }');
    }
    else {
      GM_addStyle('.post_whole_visibility { display: block }');
      GM_addStyle('.post_body_visibility { display: block }');
    }
  }
  // Adds custom css classes to posts for anti-workmode
  function add_post_invisible_class(post) {
    if (post.children[1].children.length < 2) {
      post.classList.add('post_whole_visibility');
    }
    else if (post.children[1].children.length > 1) { 
      if (post.children[1].children[0].children.length < 1) { post.classList.add('post_whole_visibility'); }
      else {post.children[1].children[1].classList.add('post_body_visibility'); }
    }
  }
  //------------------------------------------------
  // Feature: Adds download buttons in posts to download all or a selection of pictures and videos in the post
  function download(links) {
    var amountOfFiles = links.length -1;
    if (amountOfFiles > 1) {
        let postNode = document.getElementById(links[0]);
        let checkCounter = amountOfFiles;
        for (let g = 1; g<amountOfFiles+1;g++){
            let checkBoxValue = postNode.querySelector("#file" + g.toString()).checked;
            if (checkBoxValue) {
                var args = {};
                if (postID_files){
                  args = {
                    url: links[g],
                    name: links[0].slice(links[0].lastIndexOf('post')+4,links[0].length) + '_' + links[g].slice(links[g].lastIndexOf("/")+1, links[g].length)
                  };
                }
                else {
                  args = {
                    url: links[g],
                    name: links[g].slice(links[g].lastIndexOf("/")+1, links[g].length)
                  };
                }
                GM_download(args);
            }
            else {
                checkCounter--;
            }
        }
        if (checkCounter < 1) {
            for (let i = 1; i<links.length;i++) {
                var args_1 = {};
                if (postID_files){
                  args_1 = {
                    url: links[i],
                    name: links[0].slice(links[0].lastIndexOf('post')+4,links[0].length) + '_' + links[i].slice(links[i].lastIndexOf("/")+1, links[i].length)
                  };
                }
                else {
                  args_1= {
                    url: links[i],
                    name: links[i].slice(links[i].lastIndexOf("/")+1, links[i].length)
                  };
                }
                GM_download(args_1);
            }
        }
    }
    else {
            var args_2 = {};
            if (postID_files){
                  args_2 = {
                url: links[1],
                name: links[0].slice(links[0].lastIndexOf('post')+4,links[0].length) + '_' + links[1].slice(links[1].lastIndexOf("/")+1, links[1].length)
                  };
                }
            else {
                args_2 = {
                url: links[1],
                name: links[1].slice(links[1].lastIndexOf("/")+1, links[1].length)
                };
            }
            GM_download(args_2);
    }
  }
  
  function add_download_button(post) {
    let fileLinks = [];
    fileLinks[0] = post.getAttribute("id");
    var fileIndex = 1;
    var section = post.children[1];
    if (section.children.length > 1) {
        var footerPostControls = post.children[2].children[1];
        var fileDiv = section.children[0];
        var figures = fileDiv.children;
        for (let figure of figures) {
            var postFile = figure.children[1];
            fileLinks[fileIndex] = postFile.getAttribute("href");
            fileIndex++;
        }
        if (fileLinks.length>2){
            // Adding checkboxes with label
            for (let c = 1; c<fileLinks.length;c++) {
                let boxContainer = document.createElement("a");
                let fileCheckbox = document.createElement("input");
                fileCheckbox.type = "checkbox";
                fileCheckbox.id = "file"+ c.toString();
                let fileCheckBoxLabel = document.createElement("label");
                fileCheckBoxLabel.appendChild(document.createTextNode(c.toString()));
                fileCheckBoxLabel.style.marginRight = "3px";
                boxContainer.appendChild(fileCheckBoxLabel);
                boxContainer.appendChild(fileCheckbox);
                boxContainer.classList.add("post-control");
                boxContainer.style.marginRight = "5px";
                footerPostControls.appendChild(boxContainer);
            }
        }
        // Adding download button with icon
        var downloadNode = document.createElement("a");
        var downloadIcon = document.createElement("i");
        downloadIcon.classList.add("fa", "fa-download");
        downloadNode.appendChild(downloadIcon);
        downloadNode.setAttribute("id", "dlButton");
        downloadNode.classList.add("post-control", "control");
        downloadNode.style.marginLeft = "8px";
        footerPostControls.appendChild(downloadNode);
        downloadNode.addEventListener('click', function(){
            download(fileLinks);
        });
    }
  }
  //---------------------------
  // From https://gitlab.com/ysh16/dotfiles/-/blob/master/.config/qutebrowser/greasemonkey/kpop.re_thumbnail_files.user.js
  // version 0.4
  // Feature: Shows Youtube link thumbnails as pictures in posts
  function show_thumbnail_for_link_as_picture(p) {
    let files = p.querySelector('.post-files');
		if (!files) {
			files = document.createElement('div');
			files.className = 'post-files';
			p.querySelector('.post-body').insertAdjacentElement('afterbegin', files);
		}

		let embeds = p.querySelectorAll('.post-embed');
		embeds.forEach(function(e) {
			let link = e.getAttribute('href');
			let thumbnail = getThumbnail(link);
			let handled = false;
			p.querySelectorAll(".post-file-link").forEach(function(l) {
				if (l.attributes.href.value == link)
					handled = true;
			});
			if (!thumbnail || handled)
				return;

			let figure = document.createElement('figure');
			figure.className = 'post-file';
			let caption = document.createElement('figcaption');
			caption.className = 'post-file-info';
			if (link.length > 30)
				caption.innerText = link.substr(0, 30) + '...';
			else
				caption.innerText = link;
			figure.appendChild(caption);
			let a = document.createElement('a');
			a.className = 'post-file-link';
			a.href = link;
			a.target = '_blank';
			figure.appendChild(a);
			let ico = document.createElement('i');
			ico.className = 'fa fa-youtube-play post-file-badge post-file-video-badge';
			a.appendChild(ico);
			let img = document.createElement('img');
			img.className = 'post-file-thumb';
			img.src = thumbnail;
			img.style.maxWidth = '200px';
			img.style.maxHeight = '200px';
			img.loading = 'lazy';
			img.target = '_blank';
			a.appendChild(img);

			p.querySelector('.post-files').insertAdjacentElement('beforeend', figure);

			let fileCount = files.querySelectorAll('.post-file').length;
			if (fileCount >= 1)
				p.classList.add('post_file');
			if (fileCount > 1)
				p.classList.add('post_files');
		});
  }
  function getThumbnail(url) {
		let re = /^(https?:\/\/)?((www\.)?(youtube(-nocookie)?|youtube.googleapis)\.com.*(v\/|v=|vi=|vi\/|e\/|embed\/|user\/.*\/u\/\d+\/)|youtu\.be\/)([_0-9a-z-]+)/i;
		let match = url.match(re);
		let id = (match) ? match[7] : null;
		return (id) ? 'https://i.ytimg.com/vi/' + id + '/maxresdefault.jpg' : null;
	}
  //--------------------------------------
  //Feature: (You) highlighter in posts
  function you_highlighter(post) {
    let post_body = post.getElementsByClassName('post-message');
    for(var link of post_body[0].getElementsByTagName("a")){
      if (link.innerHTML.includes("(You)")){
        link.style.color = "rgb(252,152,16)";
        link.style.fontWeight = "bold";
      }
    }
  }
  //---------------------------------------
  
  function init_config() {
    // Making sure there is always a valid config in local storage
    let config = GM_getValue('config', false);
    if (config == false){
      config = {'download_buttons': false, 'link_thumbnails': false, 'post_selected_highlighter': false, 'you_highlighter': false, 'anti_workmode': false, 'postID_files': false};
      GM_setValue('config', config);
    }
    return config;
  }
    
  // Returns a set of feature function objects that should be called according to config
  // Done here to avoid checking config for every post on first site load
  function check_config_for_features(config) {
    let features = new Set();
    for (let key in config) {
      if (config[key]) {
        // download_buttons has to be executed before link_thumbnails to avoid making buttons for the fake pictures
        if (key == 'download_buttons') {features.add(add_download_button);}
        else if(key == 'link_thumbnails') {features.add(show_thumbnail_for_link_as_picture);}
        else if(key == 'post_selected_highlighter') {features.add(fix_post_link);}
        else if(key == 'you_highlighter') {features.add(you_highlighter);}        
      }
    }
    return features;
  }
  
  // START SCRIPT

  // Load config
  let config = init_config();
  let postID_files = config['postID_files'];
  
  // Tasks that are only done once after the site finished loading
  window.addEventListener('site_fully_loaded', function() {
    GM_addStyle('.post:target { border: 1px solid #555; }'); // replaces old setup_css()
    GM_addStyle('.post_body_visibility { }');
    GM_addStyle('.post_whole_visibility { }');
    toggle_anti_workmode(config['anti_workmode']);  
    
	  var features_to_execute = check_config_for_features(config);
    // Do nothing except prepare anti-workmode, if all features are disabled in config
    if(features_to_execute.size == 0) {
      [...document.getElementsByClassName("post")].forEach(post =>{
      add_post_invisible_class(post);
      });
      return;}
    // Loop over all posts for manipulations
    // document.getelementbyclassname runs in O(1) instead of selector which does O(n)
    [...document.getElementsByClassName("post")].forEach(post =>{
      features_to_execute.forEach(func => {func(post);});
      add_post_invisible_class(post);
    });
	}, false);

  // Listener for new_post events. 
  window.addEventListener("new_post_hook", function(event) {
    let new_post = event.detail.post;
    // Check config to only execute what is desired for the new post
    let features_to_execute = check_config_for_features(config);
    features_to_execute.forEach(func => {func(new_post);});
    add_post_invisible_class(new_post);
  },false);
  
  //build config UI. Jesus Christ this takes way too many lines for something as simple as this...
  let settings_node = document.getElementsByClassName("modal-container");
  let media_download_button_config = document.createElement("input");
  media_download_button_config.type = "checkbox";
  media_download_button_config.id = 'download_buttons_toggle';
  media_download_button_config.name = 'download_buttons_toggle';
  media_download_button_config.title = 'Toggles download buttons for pictures in posts';
  media_download_button_config.classList.add("option-checkbox");
  media_download_button_config.checked = config['download_buttons'];
  settings_node[0].children[2].children[1].children[0].appendChild(media_download_button_config);
  let media_download_button_config_label = document.createElement("label");
  media_download_button_config_label.htmlFor = 'download_buttons_toggle';
  media_download_button_config_label.classList.add('option-label');
  media_download_button_config_label.textContent = "Post media download buttons";
  media_download_button_config_label.title = 'Toggles download buttons for pictures in posts';
  settings_node[0].children[2].children[1].children[0].appendChild(media_download_button_config_label);
  let break_element_1 = document.createElement("br");
  settings_node[0].children[2].children[1].children[0].appendChild(break_element_1);
  //--
  let link_thumbnail_config = document.createElement("input");
  link_thumbnail_config.type = "checkbox";
  link_thumbnail_config.id = 'link_thumbnail_toggle';
  link_thumbnail_config.name = 'link_thumbnail_toggle';
  link_thumbnail_config.title = 'Toggles showing link thumbnails as pictures';
  link_thumbnail_config.classList.add("option-checkbox");
  link_thumbnail_config.checked = config['link_thumbnails'];
  settings_node[0].children[2].children[1].children[0].appendChild(link_thumbnail_config);
  let link_thumbnail_config_label = document.createElement("label");
  link_thumbnail_config_label.htmlFor = 'link_thumbnail_toggle';
  link_thumbnail_config_label.classList.add('option-label');
  link_thumbnail_config_label.textContent = "Link thumbnails";
  link_thumbnail_config_label.title = 'Toggles showing link thumbnails as pictures';
  settings_node[0].children[2].children[1].children[0].appendChild(link_thumbnail_config_label);
  let break_element_2 = document.createElement("br");
  settings_node[0].children[2].children[1].children[0].appendChild(break_element_2);
  //--
  let post_target_highlighter_config = document.createElement("input");
  post_target_highlighter_config.type = "checkbox";
  post_target_highlighter_config.id = 'post_target_highlighter_toggle';
  post_target_highlighter_config.name = 'post_target_highlighter_toggle';
  post_target_highlighter_config.title = 'Toggles border highlighting of a post you target';
  post_target_highlighter_config.classList.add("option-checkbox");
  post_target_highlighter_config.checked = config['post_selected_highlighter'];
  settings_node[0].children[2].children[1].children[0].appendChild(post_target_highlighter_config);
  let post_target_highlighter_config_label = document.createElement("label");
  post_target_highlighter_config_label.htmlFor = 'post_target_highlighter_toggle';
  post_target_highlighter_config_label.classList.add('option-label');
  post_target_highlighter_config_label.textContent = "Post target highlighting";
  post_target_highlighter_config_label.title = 'Toggles border highlighting of a post you target';
  settings_node[0].children[2].children[1].children[0].appendChild(post_target_highlighter_config_label);
  let break_element_3 = document.createElement("br");
  settings_node[0].children[2].children[1].children[0].appendChild(break_element_3);
  //--
  let you_highlighter_config = document.createElement("input");
  you_highlighter_config.type = "checkbox";
  you_highlighter_config.id = 'you_highlighter_toggle';
  you_highlighter_config.name = 'you_highlighter_toggle';
  you_highlighter_config.title = 'Toggles highlighting (You)s';
  you_highlighter_config.classList.add("option-checkbox");
  you_highlighter_config.checked = config['you_highlighter'];
  settings_node[0].children[2].children[1].children[0].appendChild(you_highlighter_config);
  let you_highlighter_config_label = document.createElement("label");
  you_highlighter_config_label.htmlFor = 'you_highlighter_toggle';
  you_highlighter_config_label.classList.add('option-label');
  you_highlighter_config_label.textContent = "(You) highlighter";
  you_highlighter_config_label.title = 'Highlights (Yous)';
  settings_node[0].children[2].children[1].children[0].appendChild(you_highlighter_config_label);
  let break_element_4 = document.createElement("br");
  settings_node[0].children[2].children[1].children[0].appendChild(break_element_4);
  //--
  let anti_workmode_config = document.createElement("input");
  anti_workmode_config.type = "checkbox";
  anti_workmode_config.id = 'anti_workmode_toggle';
  anti_workmode_config.name = 'anti_workmode_toggle';
  anti_workmode_config.title = 'Hides text in posts';
  anti_workmode_config.classList.add("option-checkbox");
  anti_workmode_config.checked = config['anti_workmode'];
  settings_node[0].children[2].children[1].children[0].appendChild(anti_workmode_config);
  let anti_workmode_config_label = document.createElement("label");
  anti_workmode_config_label.htmlFor = 'anti_workmode_toggle';
  anti_workmode_config_label.classList.add('option-label');
  anti_workmode_config_label.textContent = "Anti-workmode";
  anti_workmode_config_label.title = 'Hides text in posts';
  settings_node[0].children[2].children[1].children[0].appendChild(anti_workmode_config_label);
  let break_element_5 = document.createElement("br");
  settings_node[0].children[2].children[1].children[0].appendChild(break_element_5);
  //--
  let postID_file_config = document.createElement("input");
  postID_file_config.type = "checkbox";
  postID_file_config.id = 'postID_file_toggle';
  postID_file_config.name = 'postID_file_toggle';
  postID_file_config.title = 'Adds postID in front of filenames';
  postID_file_config.classList.add("option-checkbox");
  postID_file_config.checked = config['postID_files'];
  settings_node[0].children[2].children[1].children[0].appendChild(postID_file_config);
  let postID_file_config_label = document.createElement("label");
  postID_file_config_label.htmlFor = 'postID_file_toggle';
  postID_file_config_label.classList.add('option-label');
  postID_file_config_label.textContent = "Add PostID in filenames";
  postID_file_config_label.title = 'Adds postID in front of filenames';
  settings_node[0].children[2].children[1].children[0].appendChild(postID_file_config_label);
  
  // Add config changing functionality to all the config checkboxes
  media_download_button_config.onclick = function() {
    config['download_buttons'] = this.checked;
    GM_setValue('config', config);
  };
  link_thumbnail_config.onclick = function() {
    config['link_thumbnails'] = this.checked;
    GM_setValue('config', config);
  };
  post_target_highlighter_config.onclick = function() {
    config['post_selected_highlighter'] = this.checked;
    GM_setValue('config', config);
  };
  you_highlighter_config.onclick = function() {
    config['you_highlighter'] = this.checked;
    GM_setValue('config', config);
  };
  anti_workmode_config.onclick = function() {
    config['anti_workmode'] = this.checked;
    GM_setValue('config', config);
    toggle_anti_workmode(this.checked);    
  };
  postID_file_config.onclick = function() {
    config['postID_files'] = this.checked;
    GM_setValue('config', config);
    postID_files = this.checked; 
  };
})();