// ==UserScript==
// @name Pikabu UI++
// @namespace pikabuUIPlusPlus
// @version 1.60
// @description Улучшение интерфейса
// @author Array
// @license CC-BY-SA-4.0
// @match *://pikabu.ru/*
// @grant unsafeWindow
// @grant window.close
// @grant GM_getValue
// @grant GM_setValue
// @run-at document-body
// ==/UserScript==
// simbols source http://myhomeinet.ru/smilegenerator/page-274.html
// colors source https://www.w3schools.com/colors/colors_names.asp
// special thanks:
// @nazarpunk
// @moretraher2020
(function() {
`use strict`;
const DATA_LIFETIME = 604800000;
const DATABASE = `db`;
const DATABASE_VERSION = `1`;
const EXP_VALUES = [3, 5, 8];
const EXP_ICO = [`❊`, `•`, `︿`, `︽`, `⁂`, `★`];
const NOTE_VALUES = [`#добро`, `#интересно`, `#осторожно`, `#неприемлемо`],
NOTE_ICO = [`❤`, `♞`, `⚠`, `✖`],
NOTE_COLORS = [`LimeGreen`, `DodgerBlue`, `OrangeRed`, `Violet`];
const SUBS_VALUES = [40, 900, 4000, 9000],
SUBS_ICO = [``, `◔`, `◑`, `◕`, `◉`];
const BAN_ICO = [``, `ϟ`, `†`];
const GENDER_ICO = [``, `♂`, `♀`];
// ************************ ENTRY POINT *************************
let MAKE_OLD_HEADER = GM_getValue(`MAKE_OLD_HEADER`, 1);
let MAKE_ANSWER_TOP = GM_getValue(`MAKE_ANSWER_TOP`, 0);
let MUTE_WATERMARK = GM_getValue(`MUTE_WATERMARK`, 1);
let MINIFY_AUTHOR_PANEL = GM_getValue(`MINIFY_AUTHOR_PANEL`, 0);
let SHOW_AUTHOR_LIKE = GM_getValue(`SHOW_AUTHOR_LIKE`, 0);
let SHOW_SHORT_POST_DESIGN = GM_getValue(`SHOW_SHORT_POST_DESIGN`, 0);
let SHOW_EMOTIONS = GM_getValue(`SHOW_EMOTIONS`, 1);
let SHOW_COMMUNITY_DESCRIPTION = GM_getValue(`SHOW_COMMUNITY_DESCRIPTION`, 1);
let SHOW_HIDDEN_LIKES = GM_getValue(`SHOW_HIDDEN_LIKES`, 1);
let SHOW_HIDDEN_COMMENT_LIKES = GM_getValue(`SHOW_HIDDEN_COMMENT_LIKES`, 1);
let SHOW_EXP = GM_getValue(`SHOW_EXP`, 1);
let SHOW_NOTE = GM_getValue(`SHOW_NOTE`, 1);
let SHOW_RATE = GM_getValue(`SHOW_RATE`, 1);
let SHOW_SUBS = GM_getValue(`SHOW_SUBS`, 1);
let SHOW_BAN = GM_getValue(`SHOW_BAN`, 1);
let SHOW_GENDER = GM_getValue(`SHOW_GENDER`, 0);
const ENABLED_QUICK_NOTES = () => SHOW_EXP || SHOW_NOTE || SHOW_RATE || SHOW_SUBS || SHOW_BAN || SHOW_GENDER;
let SHOW_QUICK_NOTE_0 = GM_getValue(`SHOW_QUICK_NOTE_0`, 1);
let SHOW_QUICK_NOTE_1 = GM_getValue(`SHOW_QUICK_NOTE_1`, 1);
let SHOW_QUICK_NOTE_2 = GM_getValue(`SHOW_QUICK_NOTE_2`, 1);
let SHOW_QUICK_NOTE_3 = GM_getValue(`SHOW_QUICK_NOTE_3`, 1);
const SHOW_QUICK_NOTE_VALUES = [SHOW_QUICK_NOTE_0, SHOW_QUICK_NOTE_1, SHOW_QUICK_NOTE_2, SHOW_QUICK_NOTE_3];
let AUTOBAN = GM_getValue(`AUTOBAN`, 0);
let database = new Map();
let loadingComments = new Map();
let loadingOrder = [];
let updateOrder = [];
let intersectionItems = [];
let loadingComment = null;
let userpageRendered = false;
// ************************ INIT *************************
addCustomCSS();
loadDatabase();
document.addEventListener("DOMContentLoaded", () => {
checkUserPage();
checkSettingsPage();
renderAnswerTop();
}, {once: true});
// ************************ EVENTS *************************
// Intersections
const intersector = new IntersectionObserver(entries =>
{
for (const entry of entries) {
if (entry.isIntersecting)
{
intersectionItems.push(entry.target);
if(answer != undefined && entry.target.classList.contains(`story-comments__top-trigger`))
{
sectionTopTrigger.classList.toggle(`answer-trigger__place-holder`, false);
commentsRoot.prepend(answer);
sectionDownTrigger.classList.toggle(`answer-trigger__place-holder`, true);
}
if(answer != undefined && entry.target.classList.contains(`story-comments__down-trigger`))
{
sectionDownTrigger.classList.toggle(`answer-trigger__place-holder`, false);
comments.appendChild(answer);
sectionTopTrigger.classList.toggle(`answer-trigger__place-holder`, true);
}
}
else
{
const index = intersectionItems.indexOf(entry.target);
if (index > -1) {
intersectionItems.splice(index, 1);
}
}
}
}, { threshold: 0 });
// Ajax listener
!function(send) {
XMLHttpRequest.prototype.send = function(body) {
send.call(this, body);
if(body?.includes(`note`))
{
checkUserPage();
}
};
}(XMLHttpRequest.prototype.send);
// Mutation listener
new MutationObserver(mutations => {
for (const mutation of mutations) {
for (const node of mutation.addedNodes) {
if (!(node instanceof HTMLElement)) continue;
// watermark
if (MUTE_WATERMARK && node.dataset?.watermarked == 1)
{
node.remove();
}
// header
if (node.classList.contains(`header__main`))
{
renderHeader(node);
setTimeout(() => { renderHeader(node); }, 50);
}
// posts
if (node.classList.contains(`story`))
{
renderPost(node);
checkPostReady(node);
}
// comments
if (node.classList.contains(`comment__header`))
{
renderComment(node.parentElement.parentElement);
checkCommentReady(node);
}
}
}
}).observe(document.body, {childList: true, subtree: true});
// Visbility listener
document.addEventListener("visibilitychange", function() {
if(!document.hidden)
{
loadDatabase();
nextLoading();
}
});
// ************************ DATABASE *************************
function loadDatabase()
{
let jobject = JSON.parse(GM_getValue(DATABASE, `{}`));
database = new Map(Object.entries(jobject));
let ver = GM_getValue(`version`, ``);
if(ver != DATABASE_VERSION)
{
GM_setValue(`version`, DATABASE_VERSION);
database = new Map();
}
}
function saveDatabase()
{
let json = JSON.stringify(Object.fromEntries(database.entries()));
GM_setValue(DATABASE, json);
}
function resetDatabase()
{
GM_setValue(DATABASE, `{}`);
}
// ************************ BASEMENT *************************
function checkUserPage()
{
let url = window.location.href;
let key = url.split(`@`)[1];
if(url.includes(`@`) && !userpageRendered)
{
userpageRendered = true;
renderUserpageTools();
}
if(database.has(key))
{
database.get(key).timestamp = 0;
saveDatabase();
updateUser(key);
}
}
function checkSettingsPage()
{
let url = window.location.href;
if(url.includes(`settings`)) renderSettings();
}
function checkPostReady(node)
{
if(!ENABLED_QUICK_NOTES()) return;
let post = node?.querySelector(`.story__user-link`);
if(post == null)
{
setTimeout(() => { checkPostReady(node) }, 50);
return;
}
let userName = post.getAttribute(`data-name`);
// check author-panel
let panel = node?.querySelector(`.story__author-panel`);
if(MINIFY_AUTHOR_PANEL && panel != null)
{
renderPostAuthor(node, post, userName);
panel.remove();
}
checkUsername(userName, post);
}
function renderPostAuthor(node, userLink, userName)
{
let container = node.querySelector(`.story__community`);
// move data
let creationTime = container.querySelector(`.story__creation-date`);
container.prepend(creationTime);
// resize avatar
let avatar = userLink.querySelector(`.avatar`);
avatar.classList.toggle(`avatar_medium`, false);
avatar.classList.toggle(`avatar_small`, true);
// add text
userLink.append(userName);
// move elements
let subContainer = document.createElement(`div`);
subContainer.classList.add(`story__user`);
subContainer.classList.add(`user`);
node.querySelector(`.story__header`).prepend(subContainer);
subContainer.prepend(container);
container.prepend(userLink);
container.classList.toggle(`story__community`, false);
container.classList.toggle(`story__community_after-author-panel`, false);
container.classList.toggle(`story__user-info`, true);
}
function checkCommentReady(node)
{
if(!ENABLED_QUICK_NOTES()) return;
let commentBase = node.querySelector(`.comment__user`);
let comment = commentBase?.querySelector(`.user__nick`);
if(comment == null)
{
setTimeout(() => { checkCommentReady(node) }, 50);
return;
}
let userName = commentBase.getAttribute(`data-name`);
checkUsername(userName, comment);
}
function checkUsername(userName, holder)
{
if(userName == `DELETED` || userName == `Аноним`) return;
// check is raw
if(holder?.textContent.trim() == userName)
{
holder.dataset.name = userName;
intersector.observe(holder);
addComment(holder, userName);
}
}
function updateUser(requestedUser)
{
if(!ENABLED_QUICK_NOTES()) return;
// comemet usernames
document.querySelectorAll(`.comment__user`).forEach((commentBase) => {
let comment = commentBase.querySelector(`.user__nick`);
let userName = commentBase.getAttribute(`data-name`);
if(requestedUser == userName)
{
addComment(comment, userName);
}
});
//post usernames
document.querySelectorAll(`.story__user-link`).forEach((post) => {
let userName = post.getAttribute(`data-name`);
if(requestedUser == userName)
{
addComment(post, userName);
}
});
if(loadingComment == null)
{
nextLoading();
}
}
function addComment(comment, userName)
{
if(database.has(userName))
{
let date = new Date();
let data = database.get(userName);
renderUsername(userName, comment, data);
// update data
if(date.getTime() > (data.timestamp + DATA_LIFETIME))
{
if(loadingComments.has(userName))
{
loadingComments.get(userName).push(comment);
}
else
{
loadingComments.set(userName, [comment]);
updateOrder.push(userName);
}
}
}
else
{
//request data
if(loadingComments.has(userName))
{
loadingComments.get(userName).push(comment);
}
else
{
loadingComments.set(userName, [comment]);
loadingOrder.push(userName);
nextLoading();
}
}
}
// ************************ LOADING *************************
function nextLoading()
{
//find intersection
let intersectionUsername = null;
for(const intersected of intersectionItems)
{
if(intersected?.dataset?.name == null) continue;
if(loadingOrder.includes(intersected.dataset.name))
{
intersectionUsername = intersected.dataset.name;
const index = loadingOrder.indexOf(intersectionUsername);
if (index > -1) {
loadingOrder.splice(index, 1);
}
break;
}
if(updateOrder.includes(intersected.dataset.name))
{
intersectionUsername = intersected.dataset.name;
const index = updateOrder.indexOf(intersectionUsername);
if (index > -1) {
updateOrder.splice(index, 1);
}
break;
}
}
if(loadingComment == null && intersectionUsername != null)
{
loadingComment = intersectionUsername;
loadData(loadingComment);
}
else if(loadingComment == null && loadingOrder.length > 0)
{
loadingComment = loadingOrder.shift();
loadData(loadingComment);
}
else if(loadingComment == null && updateOrder.length > 0)
{
// update expired data
loadingComment = updateOrder.shift();
loadData(loadingComment);
}
}
function loadData(userName)
{
const body = new FormData();
body.set(`action`, `get_short_profile`);
body.set(`user_name`, userName);
fetch(`/ajax/user_info.php`, {
method: `post`,
body : body
})
.then(r => r.json())
.then(result => {
let data = parseData(result.data.html);
if(data == null)
{
nextLoading();
return;
}
if(document.hidden)
{
loadingOrder.push(loadingComment);
loadingComment = null;
return;
}
database.set(userName, data);
saveDatabase();
let comments = loadingComments.get(userName);
loadingComments.delete(userName);
if(comments != null)
{
for (let comment of comments)
{
renderUsername(userName, comment, data);
intersector.unobserve(comment);
}
}
loadingComment = null;
setTimeout(() => {nextLoading();}, 100 + 400 * Math.random());
});
}
// ************************ CLICKS *************************
let clickTimeout = false;
function clickUserNote(e)
{
if(clickTimeout) return;
clickTimeout = true;
let note = e.target.getAttribute(`data-note`);
let uid = e.target.getAttribute(`data-uid`);
let url = window.location.href;
let userName = url.split(`@`)[1];
let data = database.get(userName);
let message = ``;
let mtype = `-`;
if(data.note != (parseInt(note)+1))
{
message = NOTE_VALUES[note];
mtype = `+`;
}
let textarea = document.querySelector(`.page-profile`).querySelector(`.profile-note__textarea`).value = message;
let textdiv = document.querySelector(`.page-profile`).querySelector(`.profile-note__text`).textContent = message;
const body = new FormData();
body.set(`action`, `note`+mtype);
body.set(`id`, uid);
body.set(`message`, message);
fetch(`/ajax/users_actions.php`, {
method: `post`,
body : body
})
.then(result => {
data.timestamp = 0;
updateUser(userName);
clickTimeout = false;
applyAutoban(uid, (parseInt(note) == 3));
});
}
function clickQuicknote(e)
{
if(clickTimeout) return;
let note = e.target.getAttribute(`data-note`);
let uid = e.target.getAttribute(`data-uid`);
let comment = e.target?.parentElement?.parentElement?.querySelector(`.comment__user`);
let userName = comment?.getAttribute(`data-name`);
if(!database.has(userName)) return;
clickTimeout = true;
let refTool = e.target.parentElement?.querySelector(`a`);
let ref = refTool?.getAttribute(`href`);
let data = database.get(userName);
let message = ``;
let mtype = `-`;
if(data.note != (parseInt(note)+1))
{
message = NOTE_VALUES[note]+` `+ref;
mtype = `+`;
}
const body = new FormData();
body.set(`action`, `note`+mtype);
body.set(`id`, uid);
body.set(`message`, message);
fetch(`/ajax/users_actions.php`, {
method: `post`,
body : body
})
.then(result => {
data.timestamp = 0;
updateUser(userName);
clickTimeout = false;
applyAutoban(uid, (parseInt(note) == 3));
});
}
function applyAutoban(uid, isBan)
{
if(!AUTOBAN) return;
const body = new FormData();
body.set(`action`, isBan ? `ignore_in_comments` : `unignore_in_comments`);
body.set(`id`, uid);
fetch(`/ajax/ignore_actions.php`, {
method: `post`,
body : body
});
}
// ************************ PARSING *************************
function smartSlice(text, start, end)
{
let strIndex = text.indexOf(start);
let endIndex = text.indexOf(end, strIndex + start.length + 1);
return text.slice(strIndex+start.length, endIndex);
}
function parseData(html)
{
if(html == undefined || html == null) return null;
let data = new Object();
let regions = html.split(`information`);
// ban
let banStat = 0;
if(regions[0].includes(`ban-status`))
{
banStat = 1;
if(regions[0].includes(`навсегда`)) banStat = 2;
}
// note
let noteStat = 0;
if(regions[1].includes(`profile__note`))
{
let subRegions = regions[1].split(`profile__note-inner`);
regions[1] = subRegions[0];
for (let i = 0; i < NOTE_VALUES.length; i++)
{
if(subRegions[1].includes(NOTE_VALUES[i]))
{
noteStat = i + 1;
}
}
}
// gender
let genderStat = 0;
if(regions[1].includes(`шник`)) genderStat = 1;
if(regions[1].includes(`шница`)) genderStat = 2;
//exp
let expStat = 0;
let expSource = smartSlice(regions[1], `<span>`, `</div>`);
let y = expSource.includes(`лет`) || expSource.includes(`год`);
let m = expSource.includes(`месяц`);
if(y || m)
{
if(y)
{
let years = parseInt(expSource);
expStat = 2 + EXP_VALUES.length;
for (let i = EXP_VALUES.length - 1; i >= 0; i--)
{
if(years < EXP_VALUES[i])
{
expStat = 2 + i;
}
}
}
else
{
expStat = 1;
}
}
let digitalRegions = regions[1].split(`profile__digital`);
//rate
let rateStat = 0;
let rateRaw = smartSlice(digitalRegions[1], `<b>`, `</b>`);
if(rateRaw.includes(`К`))
{
rateStat = rateRaw.replace(`-`, `▽`);
}
else
{
rateStat = (Math.round(rateRaw/1000) + `K`).replace(`-`, `▽`);
}
if(rateStat.includes(`NaN`))
{
rateStat = `--`;
}
// subs
let subsStat = 0;
let rawSubs = smartSlice(digitalRegions[3], `<b>`, `</b>`);
if(rawSubs.includes(`К`))
{
subsStat = 4;
}
else if(rawSubs > SUBS_VALUES[0])
{
subsStat = SUBS_VALUES.length;
for (let i = SUBS_VALUES.length - 1; i > 0 ; i--)
{
if(rawSubs < SUBS_VALUES[i])
{
subsStat = i;
}
}
}
// timestamp
let date = new Date();
data.timestamp = date.getTime();
data.exp = expStat;
data.note = noteStat;
data.rate = rateStat;
data.subs = subsStat;
data.ban = banStat;
data.gender = genderStat;
return data;
}
// ************************ CSS *************************
function addCustomCSS()
{
let styles = ``;
for (let i = 0; i < NOTE_COLORS.length; i++)
{
styles += `.`+NOTE_COLORS[i]+`_note {background-color: `+NOTE_COLORS[i]+`;}`;
}
styles += `.userpage__forced-on {opacity: 1 !important; visibility: visible !important;}`;
styles += `.userpage-note-tools {margin-left: 15px !important; margin-top: 4px !important;}`;
styles += `.userpage-note-tool {font-size: 18px !important; padding: 12px !important; padding-left: 12px !important; padding-right: 12px !important;}`;
styles += `.header-old {background-color: var(--color--header__bg) !important; --color--header__bg: var(--color-primary-700) !important; color: #f9f9fb !important; --color-primary-100: var(--color-primary-500); --color-primary-800:#f9f9fb; --color-primary-200: var(--color-primary-400)}`;
styles += `.header-black-old {--color-primary-800:var(--color-primary-900)}`;
styles += `.header-item-old {--color-primary-700:var(--color-primary-900) !important;}`;
styles += `.header-item-button-old {--color-primary-700:var(--color-primary-900) !important;}`;
styles += `.settings__forced-off {opacity: 0 !important; visibility: hidden !important; max-height: 1px !important;}`;
styles += `.answer-trigger {opacity: 0 !important; visibility: hidden !important; padding: 0px; border: 0px; max-height: 0px; transition: none !important;}`;
styles += `.answer-trigger__place-holder {opacity: 0 !important; visibility: hidden !important; padding: 0px; border: 0px; height: 113px !important; max-height: 113px !important;}`;
styles += `.nsfw-blur {filter: blur(0px); object-position: 200% 200%; object-fit: none;}`;
let styleSheet = document.createElement(`style`);
styleSheet.type = `text/css`;
styleSheet.innerText = styles;
document.head.appendChild(styleSheet);
}
// ************************ RENDERING *************************
function renderUsername(userName, comment, data)
{
if(comment == null) return;
let exp = ``;
if(SHOW_EXP)
{
exp = EXP_ICO[data.exp] + ` `;
}
let note = ``;
if(SHOW_NOTE && data.note > 0)
{
note = NOTE_ICO[data.note - 1] + ` `;
let noteEl = comment?.parentElement?.parentElement?.querySelector(`.avatar__note`);
if(noteEl == null || noteEl == undefined)
{
let avatar = comment?.parentElement?.parentElement?.querySelector(`.avatar`);
noteEl = document.createElement(`span`);
noteEl.classList.add(`avatar__note`);
avatar.appendChild(noteEl);
}
// remove prev
for (let i = 0; i < NOTE_COLORS.length; i++)
{
noteEl.classList.toggle(NOTE_COLORS[i]+`_note`, false);
}
noteEl.classList.add(NOTE_COLORS[data.note - 1]+`_note`);
}
if(SHOW_NOTE && data.note == 0)
{
let noteEl = comment?.parentElement?.parentElement?.querySelector(`.avatar__note`);
if(noteEl != null && noteEl != undefined && noteEl.classList.length > 1)
{
noteEl.remove();
}
}
let rate = ``;
if(SHOW_RATE)
{
rate = data.rate + ` `;
}
let subs = ``;
if(SHOW_SUBS)
{
subs = SUBS_ICO[data.subs] + ` `;
}
let ban = ``;
if(SHOW_BAN && data.ban > 0)
{
ban = ` `+BAN_ICO[data.ban];
}
let gender = ``;
if(SHOW_GENDER && data.gender > 0)
{
gender = ` `+GENDER_ICO[data.gender];
}
if(comment.childNodes.length > 1)
{
for (let i = 0; i < comment.childNodes.length; i++)
{
if(comment.childNodes[i].textContent.trim() == userName)
{
comment.childNodes[i].textContent = exp + note + rate + subs + userName + ban + gender;
}
}
}
else
{
comment.textContent = exp + note + rate + subs + userName + ban + gender;
}
}
function renderComment(comment)
{
let tools = comment.querySelector(`.comment__tools`);
let authorLike = comment.querySelector(`.comment__author-like`);
let rating = comment.querySelector(`.comment__rating-count`);
let ratingUp = comment.querySelector(`.comment__rating-up`);
let ratingDown = comment.querySelector(`.comment__rating-down`);
renderAuthorLikes(authorLike);
renderCommunityDescription();
if(tools == null || rating == null || ratingUp == null || ratingDown == null)
{
setTimeout(() => { renderComment(comment); }, 50);
return;
}
let meta = comment.getAttribute(`data-meta`);
if(!comment.hasAttribute(`data-quicknote`) && (meta != null) && !meta.includes(`ua`))
{
comment.setAttribute(`data-quicknote`, 1);
let uid = smartSlice(meta, `aid=`, `;`);
renderCommentTools(tools, uid);
}
if(meta != null && meta.includes(`avh`))
{
let rawRating;
let metaSplit = meta.split(`avh=`);
if(metaSplit[1].includes(`;`))
{
rawRating = smartSlice(meta, `avh=`, `;`);
}
else
{
rawRating = metaSplit[1];
}
renderCommentLikes(rating, ratingUp, ratingDown, rawRating, comment.dataset.id, comment.dataset.authorId);
}
}
function renderCommentTools(tools, uid)
{
if(!ENABLED_QUICK_NOTES()) return;
for (let i = 0; i < NOTE_VALUES.length; i++)
{
if(!SHOW_QUICK_NOTE_VALUES[i]) continue;
let tool = document.createElement(`div`);
tool.classList.add(`comment__tool`);
tool.classList.add(`hint`);
tool.setAttribute(`aria-label`, `Отметить как `+NOTE_VALUES[i]);
tool.setAttribute(`data-note`, i);
tool.setAttribute(`data-uid`, uid);
tool.textContent = NOTE_ICO[i];
tool.addEventListener(`click`, clickQuicknote);
tools.appendChild(tool);
}
}
function renderCommunityDescription()
{
if(SHOW_COMMUNITY_DESCRIPTION) return;
let page = document.querySelector(`.page-story`);
page?.querySelectorAll(`.section-group`).forEach((section) => {
let item = section.querySelector(`.community-comments`);
if(item != null)
{
section.remove();
}
});
}
function renderAuthorLikes(like)
{
if(SHOW_AUTHOR_LIKE) return;
like?.remove();
}
function renderCommentLikes(rating, ratingUp, ratingDown, raw, id, aid)
{
if(!SHOW_HIDDEN_COMMENT_LIKES) return;
let dataMetaRating = raw.split(`:`);
let storyId = id;
let autorId = aid;
let key = 253537024;
let storySeed = (storyId % 99) + 1;
let autorSeed = (autorId % 98);
function restore(x)
{
return -x/storySeed - key - autorSeed / storySeed;
}
let plus = restore(dataMetaRating[0]);
let minus = restore(dataMetaRating[1]);
let resultRating = Math.round(plus - minus);
if(resultRating > 100000 || resultRating < -100000) resultRating = 0;
if(resultRating > 0) resultRating = `+`+resultRating;
rating?.childNodes[0]?.remove();
rating.textContent = resultRating;
rating.setAttribute(`aria-label`, Math.round(plus)+` плюсов, `+Math.round(minus)+` минусов`);
rating.dataset.originRating = resultRating;
ratingUp.addEventListener(`click`, onChangeCommentRating);
ratingDown.addEventListener(`click`, onChangeCommentRating);
}
function onChangeCommentRating(e)
{
let comment = e.target.parentNode;
let rating = comment.querySelector(`.comment__rating-count`);
let ratingUp = comment.querySelector(`.comment__rating-up`);
let ratingDown = comment.querySelector(`.comment__rating-down`);
setTimeout(() =>
{
let plus = ratingUp.classList.contains(`comment__rating-up_active`);
let minus = ratingDown.classList.contains(`comment__rating-down_active`);
let result = parseInt(rating.dataset.originRating) + (plus - minus);
if(result > 0) result = `+`+result;
rating.textContent = result;
}, 50);
}
function renderUserpageTools()
{
if(!ENABLED_QUICK_NOTES()) return;
let isUser = document.querySelector(`.page-profile`)?.querySelector(`.background`)?.hasAttribute(`data-editable`);
if(isUser == true) return;
let uid = document.querySelector(`.page-profile`).querySelector(`.section-group`).getAttribute(`data-user-id`);
let root = document.querySelector(`.main__inner`);
let panel = document.querySelector(`.feed-panel`);
let tools = document.createElement(`div`);
tools.classList.add(`comment__tools`);
tools.classList.add(`userpage__forced-on`);
tools.classList.add(`userpage-note-tools`);
root.insertBefore(tools, panel);
for (let i = 0; i < NOTE_VALUES.length; i++)
{
if(!SHOW_QUICK_NOTE_VALUES[i]) continue;
let tool = document.createElement(`div`);
tool.classList.add(`comment__tool`);
tool.classList.add(`hint`);
tool.classList.add(`userpage-note-tool`);
tool.setAttribute(`aria-label`, `Отметить как `+NOTE_VALUES[i]);
tool.setAttribute(`data-note`, i);
tool.setAttribute(`data-uid`, uid);
tool.textContent = NOTE_ICO[i];
tool.addEventListener(`click`, clickUserNote);
tools.appendChild(tool);
}
}
function renderPost(article)
{
let rating = article.querySelector(`.story__rating-count`);
let emotions = article.querySelector(`.story__emotions`);
if(rating == null || emotions == null)
{
setTimeout(() => { renderPost(article); }, 50);
return;
}
//
if (SHOW_HIDDEN_LIKES && rating?.firstChild?.tagName == `SPAN`) {
rating.removeChild(rating?.firstChild);
let dataMetaRating = article.getAttribute(`data-meta-rating`).split(`:`);
let storyId = article.getAttribute(`data-story-id`);
let autorId = article.getAttribute(`data-author-id`);
let key = 253537024;
let storySeed = (storyId % 99) + 1;
let autorSeed = (autorId % 98);
function restore(x)
{
return -x/storySeed - key - autorSeed / storySeed;
}
let resultRating = Math.round(restore(dataMetaRating[0]) - restore(dataMetaRating[1]));
if(resultRating > 100000 || resultRating < -100000) resultRating = 0;
rating.textContent = resultRating;
}
if(!SHOW_EMOTIONS)
{
emotions?.remove();
}
if(!SHOW_SHORT_POST_DESIGN)
{
article.classList.toggle(`story_short`, false);
let bg = article.querySelector(`.icon--ui__bg-story-short`);
bg?.remove();
}
}
function renderHeader(header)
{
if(header == undefined) header = document.querySelector(`.header__main`);
header.classList.toggle(`header-old`, MAKE_OLD_HEADER);
header.querySelector(`.icon--ui__logo-text`).childNodes[0].classList.toggle(`header-old`, MAKE_OLD_HEADER);
header.querySelector(`.header-menu__subs-counter`).classList.toggle(`header-black-old`, MAKE_OLD_HEADER);
header.querySelectorAll(`.header-menu__item`).forEach(item => {
item.classList.toggle(`header-item-button-old`, MAKE_OLD_HEADER);
});
header.querySelectorAll(`.header-right-menu__item`).forEach(item => {
item.classList.toggle(`header-item-old`, MAKE_OLD_HEADER);
});
}
let commentsRoot;
let comments;
let answer;
let sectionTopTrigger;
let sectionDownTrigger;
function renderAnswerTop()
{
if(!MAKE_ANSWER_TOP) return;
setTimeout(() =>
{
commentsRoot = document.querySelector(`.story-comments`);
comments = commentsRoot?.querySelector(`.story-comments__all`);
if(comments == null || comments.scrollHeight < window.screen.height) return;
comments.querySelectorAll(`section`).forEach(item => {
if(item.dataset?.role == `answer`)
{
answer = item;
}
});
sectionTopTrigger = document.createElement(`section`);
sectionTopTrigger.classList.add(`story-comments__top-trigger`);
sectionTopTrigger.classList.add(`answer-trigger`);
comments.prepend(sectionTopTrigger);
intersector.observe(sectionTopTrigger);
sectionDownTrigger = document.createElement(`section`);
sectionDownTrigger.classList.add(`story-comments__down-trigger`);
sectionDownTrigger.classList.add(`answer-trigger`);
comments.append(sectionDownTrigger);
intersector.observe(sectionDownTrigger);
}, 50);
}
// ************************ SETTINGS *************************
function renderSettings()
{
let enableUISection = false;
let header = document.querySelector(`.feed-panel`).querySelector(`.menu`);
let items = [];
header.querySelectorAll(`.menu__item`).forEach(item => {
items.push(item);
item.addEventListener(`click`, (e) => {
if(enableUISection)
{
settingItem.classList.toggle(`menu__item_current`, false);
settings?.remove();
if(settingsOld != null)
{
settingsContainer.appendChild(settingsOld);
setTimeout(() => e.target.classList.toggle(`menu__item_current`, true), 100);
}
enableUISection= false;
}
});
});
let settingsContainer = document.querySelector(`.settings`);
let settings;
let settingsOld;
let settingItem = document.createElement(`a`);
settingItem.classList.add(`menu__item`);
settingItem.classList.add(`menu__item_route`);
settingItem.textContent = `UI++`;
header.appendChild(settingItem);
settingItem.addEventListener(`click`, () => {
enableUISection = true;
items.forEach(item => {item.classList.toggle(`menu__item_current`, false);});
settingItem.classList.toggle(`menu__item_current`, true);
settingsOld = settingsContainer.querySelector(`.settings-main`)
|| settingsContainer.querySelector(`.settings-security`)
|| settingsContainer.querySelector(`.settings-save`)
|| settingsContainer.querySelector(`.settings-notifications`);
settingsOld?.remove();
settings = document.createElement(`div`);
settings.classList.add(`settings-main`);
settingsContainer.appendChild(settings);
let sectionGroup = document.createElement(`div`);
sectionGroup.classList.add(`section-group`);
settings.appendChild(sectionGroup);
let sectionHeader = document.createElement(`section`);
sectionHeader.classList.add(`section_gray`);
sectionGroup.appendChild(sectionHeader);
let h = document.createElement(`h4`);
h.textContent = `Настройки сторонего скрипта Pikabu UI++`;
sectionHeader.appendChild(h);
let sectionBody = document.createElement(`section`);
//sectionBody.classList.add(`section`);
sectionBody.classList.add(`section_padding_none`);
sectionGroup.appendChild(sectionBody);
let options = document.createElement(`div`);
options.classList.add(`settings-main__options`);
sectionBody.appendChild(options);
const addLine = (tittle, desc) => {
let line = document.createElement(`h4`);
line.classList.add(`settings-main__sub-header`);
line.textContent = tittle;
options.appendChild(line);
if(desc != undefined) line.insertAdjacentHTML(`beforeend`,`<i class="fa fa-question-circle hint" aria-label="`+desc+`"></i>`);
return line;
};
let quickNoteElements = [];
const addCheckbox = (tittle, current, name, desc) => {
let op = document.createElement(`div`);
op.classList.add(`settings-main__option`);
options.appendChild(op);
let l = document.createElement(`label`);
op.appendChild(l);
let checkbox = document.createElement(`span`);
checkbox.classList.add(`checkbox`);
checkbox.classList.add(`checkbox_switch`);
checkbox.classList.toggle(`checkbox_checked`, current);
checkbox.setAttribute(`tabindex`, `0`);
checkbox.setAttribute(`unselectable`, `on`);
l.appendChild(checkbox);
checkbox.addEventListener(`click`, () => {
let value = !GM_getValue(name, 1);
GM_setValue(name, value);
eval(name+` = value;`);
checkbox.classList.toggle(`checkbox_checked`, value);
let hideQuickNote = !ENABLED_QUICK_NOTES();
for(const el of quickNoteElements)
{
el.classList.toggle(`settings__forced-off`, hideQuickNote);
}
if(name == `MAKE_OLD_HEADER`)
{
MAKE_OLD_HEADER = value;
renderHeader();
}
});
l.insertAdjacentHTML(`beforeend`, tittle);
if(desc != undefined) l.insertAdjacentHTML(`beforeend`,`<i class="fa fa-question-circle hint" aria-label="`+desc+`"></i>`);
return op;
};
// design
addLine(`Внешний вид`);
addCheckbox(`Вернуть старый дизайн шапки `, MAKE_OLD_HEADER, `MAKE_OLD_HEADER`, `Часть цветов может не совпадать, а некоторые анимации не работать. Получилось что получилось.`);
addCheckbox(`Дублировать блок написания комментария в начало поста `, MAKE_ANSWER_TOP, `MAKE_ANSWER_TOP`);
addLine(`Отображение стандартной информации`);
addCheckbox(`Показывать лайки автора в его посте `, SHOW_AUTHOR_LIKE, `SHOW_AUTHOR_LIKE`);
addCheckbox(`Показывать дизайн коротких постов `, SHOW_SHORT_POST_DESIGN, `SHOW_SHORT_POST_DESIGN`);
addCheckbox(`Показывать эмоции у постов `, SHOW_EMOTIONS, `SHOW_EMOTIONS`);
addCheckbox(`Показывать описание сообщества при открытии поста `, SHOW_COMMUNITY_DESCRIPTION, `SHOW_COMMUNITY_DESCRIPTION`);
addCheckbox(`Уменьшить заголовок постов избранных авторов `, MINIFY_AUTHOR_PANEL, `MINIFY_AUTHOR_PANEL`);
addLine(`Отображение скрытой информации`);
addCheckbox(`Показывать оценку свежих постов `, SHOW_HIDDEN_LIKES, `SHOW_HIDDEN_LIKES`);
addCheckbox(`Показывать оценку свежих комментариев `, SHOW_HIDDEN_COMMENT_LIKES, `SHOW_HIDDEN_COMMENT_LIKES`, `Оценка обновляется с небольшой задержкой.`);
addLine(`Прочий функционал`);
addCheckbox(`Убрать вотермарку pikabu.ru при копировании изображений `, MUTE_WATERMARK, `MUTE_WATERMARK`);
addLine(`Дополнительная информация рядом с никнеймом `, `Данная функция отправляет множество запросов к серверу пикабу (на каждого пользователя по запросу), так что потенциально может замедлить работу сайта. Для отключения необходимо снять все пункты (блок умных заметок исчезнет).`);
let noteDesc = ``;
for(let i = 0; i < 4; i++) noteDesc += `<br> `+NOTE_ICO[i]+` - `+NOTE_VALUES[i];
addCheckbox(`Показывать дату регистрации `, SHOW_EXP, `SHOW_EXP`, EXP_ICO[0]+` - Менее месяца<br>`+EXP_ICO[1]+` - 1 год<br>`+EXP_ICO[2]+` - Более 1 года<br>`+EXP_ICO[3]+` - Более `+EXP_VALUES[0]+` лет<br>`+EXP_ICO[4]+` - Более `+EXP_VALUES[1]+` лет<br>`+EXP_ICO[5]+` - Более `+EXP_VALUES[2]+` лет`);
addCheckbox(`Показывать значок умной заметки `, SHOW_NOTE, `SHOW_NOTE`, `Оставьте в заметке пользователя один из хэштегов чтобы показывать специальный маркер, а также изменить цвет заметки: `+noteDesc);
addCheckbox(`Показывать рейтинг `, SHOW_RATE, `SHOW_RATE`);
addCheckbox(`Показывать подписчиков `, SHOW_SUBS, `SHOW_SUBS`, SUBS_ICO[1]+` - Более `+SUBS_VALUES[0]+` подписчиков<br>`+SUBS_ICO[2]+` - Более `+SUBS_VALUES[1]+` подписчиков<br>`+SUBS_ICO[3]+` - Более `+SUBS_VALUES[2]+` подписчиков<br>`+SUBS_ICO[4]+` - Более `+SUBS_VALUES[3]+` подписчиков`);
addCheckbox(`Показывать бан `, SHOW_BAN, `SHOW_BAN`, BAN_ICO[1]+` - Временныый бан<br>`+BAN_ICO[2]+` - Вечный бан` );
addCheckbox(`Показывать пол `, SHOW_GENDER, `SHOW_GENDER`, GENDER_ICO[1]+` - Пикабушник<br>`+GENDER_ICO[2]+` - Пикабушница` );
quickNoteElements.push(addLine(`Умные заметки `, `Рядом с комментариями и на странице пользователя будут кнопки для быстрого добавления заметки с хэштегом. Кнопки рядом с комментариями также сохраняют ссылку на комментарий. Нажатие на кнопку срабатывает сразу, однако иконка обновится с небольшой задержкой из-за очереди загрузки. <br>ОСТОРОЖНО: При добавлении хэтега через кнопку, предыдущая заметка полностью удаляется.`));
quickNoteElements.push(addCheckbox(`Показывать кнопку #добро `, SHOW_QUICK_NOTE_0, `SHOW_QUICK_NOTE_0`));
quickNoteElements.push(addCheckbox(`Показывать кнопку #интересно `, SHOW_QUICK_NOTE_1, `SHOW_QUICK_NOTE_1`));
quickNoteElements.push(addCheckbox(`Показывать кнопку #осторожно `, SHOW_QUICK_NOTE_2, `SHOW_QUICK_NOTE_2`));
quickNoteElements.push(addCheckbox(`Показывать кнопку #неприемлемый `, SHOW_QUICK_NOTE_3, `SHOW_QUICK_NOTE_3`));
quickNoteElements.push(addCheckbox(`Автоматический бан #неприемлемого `, AUTOBAN, `AUTOBAN`, `При добавлении хэштега #неприемлемый автоматически скрываются комментарии пользователя. Если убрать хэштег, то комментарии снова будут показываться. Работает только при добавлении хэштегов через кнопки быстрого добавления.` ));
let sectionBottom = document.createElement(`section`);
sectionBottom.classList.add(`section_gray`);
sectionBottom.classList.add(`section_center`);
sectionGroup.appendChild(sectionBottom);
let buttonClear = document.createElement(`button`);
buttonClear.classList.add(`button_danger`);
buttonClear.textContent = `УДАЛИТЬ КЭШИРОВАННЫЕ ДАННЫЕ`;
sectionBottom.appendChild(buttonClear);
buttonClear.addEventListener(`click`, () => {
resetDatabase();
});
let hideQuickNote = !ENABLED_QUICK_NOTES();
for(const el of quickNoteElements)
{
el.classList.toggle(`settings__forced-off`, hideQuickNote);
}
});
}
})();