NOTICE: By continued use of this site you understand and agree to the binding Terms of Service and Privacy Policy.
// ==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;
};
})();