您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Всякое-разное для Habr и GeekTimes
当前为
- // ==UserScript==
- // @name Habr.Features
- // @version 3.0.16
- // @description Всякое-разное для Habr и GeekTimes
- // @author AngReload
- // @include https://geektimes.com/*
- // @include https://habr.com/*
- // @include http://habr.com/*
- // @include http://geektimes.com/*
- // @include https://tmfeed.ru
- // @include http://tmfeed.ru
- // @namespace habr_comments
- // @run-at document-start
- // @grant none
- // @icon https://habr.com/favicon.ico
- // ==/UserScript==
- /* global localStorage, MutationObserver */
- // остановка гифок
- // клик по гифке заменит картинку на заглушку
- // повторный клик вернет гифку на место
- const GIF_STOP = true;
- // остановить гифки при загрузке страницы
- const GIF_STOP_ONLOAD = false;
- // цвета заглушки
- const GIF_STOP_COLOR_FG = 'White';
- const GIF_STOP_COLOR_BG = 'LightGray'; // WhiteSmoke
- // менять src вместо создания-удаления нод
- const GIF_STOP_OVERTYPE = true;
- // показывать счетчики рейтинга в виде:
- // рейтинг = число_голосовавших * (процент_плюсов - процент_минусов)%
- const RATING_DETAILS = true;
- // клик мышкой по рейтингу меняет вид на простой \ детальный
- const RATING_DETAILS_ONCLICK = false;
- const RATING_DETAILS_PN = false;
- const KARMA_DETAILS = true;
- // показывать метки времени в текущем часовом поясе
- // абсолютно, либо относительно текущего времени, либо относительно родительского времени
- // меняется по клику, в всплывающей подсказке другие виды времени, автообновляется
- const TIME_DETAILS = true;
- // добавить возможность сортировки комментариев
- const COMMENTS_SORT = true;
- // сортировать комменты при загрузке страницы или оставить сортировку по времени
- const COMMENTS_SORT_ONLOAD = true;
- // список доступных сортировок
- const sortVariants = [
- ['time', 'по времени'],
- ['freshness', 'свежести'],
- ['trend', 'трендам'],
- // ['quality', 'качеству'],
- // ['rating', 'рейтингу'],
- // ['popularity', 'популярности'],
- // ['shuffle', 'перемешать'],
- ];
- // добавить возможность сворачивать комментарии
- const COMMENTS_HIDE = true;
- // свернуть комментарии если их глубина вложенности равна некому числу
- const HIDE_LEVEL = 4;
- // сделать «возврат каретки» для комментариев чтобы глубина вложенности не превышала некого числа
- const LINE_LEN = 8;
- // const LINE_LEN = 16;
- const REDUCE_NEW_LINES = true;
- const RAINBOW_NEW_LINE = true;
- // заменить ссылки ведущие к новым комментариям на дерево комментариев
- const COMMENTS_LINKS = true;
- // запоминание галки «Использовать MarkDown» для комментариев
- const COMMENTS_MD = true;
- // в огнелисе, в отличии о хрома, при загрузке изображений не сохраняется прокрутка
- // меня раздражает когда дерево комментариев скачками пытается уехать вниз
- // эта опция зафиксирует высоту публикации пока та находится вне обзора
- const FIX_JUMPING_SCROLL = true;
- const SCROLL_LEGEND = true;
- // включить разные стили
- const USERSTYLE = true;
- const USERSTYLE_GIF_HOVER = false;
- const USERSTYLE_FEED_DISTANCED = true;
- const USERSTYLE_COUNTER_NEW_FIX_SPACE = true;
- const USERSTYLE_REMOVE_SIDEBAR_RIGHT = true;
- const USERSTYLE_REMOVE_BORDER_RADIUS_AVATARS = true;
- const USERSTYLE_USERINFO_BIG_AVATARS = true;
- const USERSTYLE_COMMENTS_IMG_MAXSIZE = 0;
- const USERSTYLE_COMMENTS_FIX = true;
- const USERSTYLE_CODE_FIX = true;
- const USERSTYLE_SPOILER_BORDERS = true;
- const USERSTYLE_STATIC_STICKY = true;
- const USERSTYLE_HLJS_LANG = true;
- const USERSTYLE_HLJS_LANG_HOVER = false;
- const USERSTYLE_CODE_FONT = 'PT Mono';
- const USERSTYLE_CODE_TABSIZE = 2;
- // свои стили
- const userStyleEl = document.createElement('style');
- let userStyle = '';
- if (USERSTYLE_GIF_HOVER) {
- userStyle += `
- .post__text img[src$=".gif"]:hover,
- .comment__message img[src$=".gif"]:hover {
- opacity: 0.75;
- }
- `;
- }
- if (SCROLL_LEGEND) {
- userStyle += `
- .legend_el {
- position: fixed;
- width: 4px;
- right: 0;
- transition: top 1s ease-out, height 1s ease-out;
- z-index: 101;
- }
- #xpanel {
- right: 4px;
- }
- `;
- }
- if (USERSTYLE_FEED_DISTANCED) {
- userStyle += `
- .post__body_crop {
- text-align: right;
- }
- .post__body_crop .post__text {
- text-align: left;
- }
- .post__footer {
- text-align: right;
- }
- .posts_list .content-list__item_post {
- padding: 40px 0;
- }
- `;
- }
- if (USERSTYLE_COUNTER_NEW_FIX_SPACE) {
- userStyle += `
- .toggle-menu__item-counter_new {
- margin-left: 4px;
- }
- `;
- }
- if (USERSTYLE_REMOVE_SIDEBAR_RIGHT) {
- // remove for
- // https://habr.com/post/352896/
- // https://habr.com/sandbox/
- // https://habr.com/sandbox/115216/
- // https://habr.com/users/saggid/posts/
- // https://habr.com/users/saggid/comments/
- // https://habr.com/users/saggid/favorites/
- // https://habr.com/users/saggid/favorites/posts/
- // https://habr.com/users/saggid/favorites/comments/
- // https://habr.com/company/pvs-studio/blog/353640/
- // https://habr.com/company/pvs-studio/blog/
- // https://habr.com/company/pvs-studio/blog/top/
- // https://habr.com/company/pvs-studio/
- // https://habr.com/feed/
- // https://habr.com/top/
- // https://habr.com/top/yearly/
- // https://habr.com/all/
- // https://habr.com/all/top10/
- // display for
- // https://habr.com/company/pvs-studio/profile/
- // https://habr.com/company/pvs-studio/vacancies/
- // https://habr.com/company/pvs-studio/fans/all/rating/
- // https://habr.com/company/pvs-studio/workers/new/rating/
- // https://habr.com/feed/settings/
- // https://habr.com/users/
- // https://habr.com/hubs/
- // https://habr.com/hubs/admin/
- // https://habr.com/companies/
- // https://habr.com/companies/category/software/
- // https://habr.com/companies/new/
- // https://habr.com/flows/design/
- const path = window.location.pathname;
- const isPost = /^\/post\/\d+\/$/.test(path);
- const isSandbox = /^\/sandbox\//.test(path);
- const isUserPosts = /^\/users\/[^/]+\/posts\//.test(path);
- const isUserComments = /^\/users\/[^/]+\/comments\//.test(path);
- const isUserFavorites = /^\/users\/[^/]+\/favorites\//.test(path);
- const isCompanyBlog = /^\/company\/[^/]+\/blog\//.test(path);
- const isCompanyBlog2 = /^\/company\/[^/]+\/(page\d+\/)?$/.test(path);
- const isFeed = /^\/feed\//.test(path);
- const isHome = /^\/$/.test(path);
- const isTop = /^\/top\//.test(path);
- const isAll = /^\/all\//.test(path);
- if (
- isPost || isSandbox ||
- isUserPosts || isUserComments || isUserFavorites ||
- isCompanyBlog || isCompanyBlog2 ||
- isFeed || isHome || isTop || isAll
- ) {
- userStyle += `
- .sidebar_right {
- display: none;
- }
- .content_left {
- padding-right: 0;
- }
- .comment_plain {
- max-width: 860px;
- }
- `;
- }
- }
- if (USERSTYLE_REMOVE_BORDER_RADIUS_AVATARS) {
- userStyle += `
- .user-info__image-pic,
- .user-pic_popover,
- .media-obj__image-pic {
- border-radius: 0;
- }
- `;
- }
- if (USERSTYLE_USERINFO_BIG_AVATARS) {
- userStyle += `
- .page-header {
- height: auto;
- }
- .media-obj__image-pic_hub,
- .media-obj__image-pic_user,
- .media-obj__image-pic_company {
- width: auto;
- height: auto;
- }
- `;
- }
- if (USERSTYLE_COMMENTS_FIX) {
- userStyle += `
- .comments_order {
- color: #333;
- font-size: 14px;
- font-family: "-apple-system",BlinkMacSystemFont,Arial,sans-serif;
- text-rendering: optimizeLegibility;
- border-bottom: 1px solid #e3e3e3;
- padding: 8px;
- text-align: right;
- }
- .comments_order a {
- color: #548eaa;
- font-style: normal;
- text-decoration: none;
- }
- .comments_order a:hover {
- color: #487284;
- }
- .content-list_comments {
- overflow: visible;
- }
- .comment__folding-dotholder {
- display: none !important;
- }
- .content-list_nested-comments {
- border-left: 1px solid #e3e3e3;
- margin: 0;
- padding-top: 20px;
- padding-left: 20px !important;
- }
- .content-list_comments {
- /*border-left: 1px solid silver;*/
- margin: 0;
- padding-left: 0;
- padding-top: 20px;
- /*background: #FCE4EC;*/
- }
- #comments-list .js-form_placeholder {
- border-left: 1px solid #e3e3e3;
- padding-left: 20px;
- }
- .comments_new-line {
- border-left: 1px solid #777;
- border-bottom: 1px solid #777;
- border-top: 1px solid #777;
- margin-left: -${LINE_LEN * 21}px !important;
- background: white;
- padding-bottom: 4px;
- }
- /* .comment__head_topic-author.comment__head_new-comment */
- .comment__head_topic-author .user-info {
- text-decoration: underline;
- }
- `;
- }
- if (RAINBOW_NEW_LINE) {
- userStyle += `
- .comments_new-line-1 {
- border-color: #0caefb;
- }
- .comments_new-line-2 {
- border-color: #06feb7;
- }
- .comments_new-line-3 {
- border-color: #fbcb02;
- }
- .comments_new-line-0 {
- border-color: #fb0543;
- }
- `;
- }
- if (REDUCE_NEW_LINES) {
- userStyle += `
- .comments_new-line .comments_new-line {
- margin-left: -${(LINE_LEN - 1) * 21}px !important;
- }
- `;
- }
- if (USERSTYLE_COMMENTS_IMG_MAXSIZE) {
- userStyle += `
- .comment__message img {
- max-height: ${USERSTYLE_COMMENTS_IMG_MAXSIZE}px;
- }
- .comment__message .spoiler .img {
- max-height: auto;
- }
- `;
- }
- if (USERSTYLE_CODE_FIX) {
- let addFont = '';
- if (USERSTYLE_CODE_FONT) {
- addFont = USERSTYLE_CODE_FONT;
- if (addFont.indexOf(' ') >= 0) {
- addFont = `"${addFont}"`;
- }
- addFont += ',';
- }
- const tabSize = USERSTYLE_CODE_TABSIZE || 4;
- userStyle += `
- .editor .text-holder textarea,
- .tm-editor__textarea {
- font-family: ${addFont} Menlo, Monaco, Consolas, 'Lucida Console', 'Courier New', monospace;
- }
- code {
- font-family: ${addFont} Menlo, Monaco, Consolas, 'Lucida Console', 'Courier New', monospace !important;;
- -o-tab-size: ${tabSize};
- -moz-tab-size: ${tabSize};
- tab-size: ${tabSize};
- background: #f7f7f7;
- border-radius: 3px;
- color: #505c66;
- display: inline-block;
- font-weight: 500;
- line-height: 1.29;
- padding: 5px 9px;
- vertical-align: 1px;
- }
- `;
- }
- if (USERSTYLE_SPOILER_BORDERS) {
- userStyle += `
- .spoiler .spoiler_text {
- border: 1px dashed rgb(12, 174, 251);
- }
- `;
- }
- if (USERSTYLE_STATIC_STICKY) {
- userStyle += `
- .wrapper-sticky,
- .js-ad_sticky,
- .js-ad_sticky_comments {
- position: static !important;
- }
- `;
- }
- if (USERSTYLE_HLJS_LANG) {
- let hover = '';
- if (USERSTYLE_HLJS_LANG_HOVER) hover = ':hover';
- userStyle += `
- pre {
- position: relative;
- }
- .hljs${hover}::after {
- position: absolute;
- font-size: 12px;
- content: 'code';
- right: 0;
- top: 0;
- padding: 1px 5px 0 4px;
- /*border-bottom: 1px solid #e5e8ec;
- border-left: 1px solid #e5e8ec;
- border-bottom-left-radius: 3px;
- color: #505c66;*/
- opacity: .5;
- }
- `;
- userStyle += [
- ['1c', '1C:Enterprise (v7, v8)'],
- ['abnf', 'Augmented Backus-Naur Form'],
- ['accesslog', 'Access log'],
- ['actionscript', 'ActionScript'],
- ['ada', 'Ada'],
- ['apache', 'Apache'],
- ['applescript', 'AppleScript'],
- ['arduino', 'Arduino'],
- ['armasm', 'ARM Assembly'],
- ['asciidoc', 'AsciiDoc'],
- ['aspectj', 'AspectJ'],
- ['autohotkey', 'AutoHotkey'],
- ['autoit', 'AutoIt'],
- ['avrasm', 'AVR Assembler'],
- ['awk', 'Awk'],
- ['axapta', 'Axapta'],
- ['bash', 'Bash'],
- ['basic', 'Basic'],
- ['bnf', 'Backus–Naur Form'],
- ['brainfuck', 'Brainfuck'],
- ['cal', 'C/AL'],
- ['capnproto', 'Cap’n Proto'],
- ['ceylon', 'Ceylon'],
- ['clean', 'Clean'],
- ['clojure-repl', 'Clojure REPL'],
- ['clojure', 'Clojure'],
- ['cmake', 'CMake'],
- ['coffeescript', 'CoffeeScript'],
- ['coq', 'Coq'],
- ['cos', 'Caché Object Script'],
- ['cpp', 'C++'],
- ['crmsh', 'crmsh'],
- ['crystal', 'Crystal'],
- ['cs', 'C#'],
- ['csp', 'CSP'],
- ['css', 'CSS'],
- ['d', 'D'],
- ['dart', 'Dart'],
- ['delphi', 'Delphi'],
- ['diff', 'Diff'],
- ['django', 'Django'],
- ['dns', 'DNS Zone file'],
- ['dockerfile', 'Dockerfile'],
- ['dos', 'DOS .bat'],
- ['dsconfig', 'dsconfig'],
- ['dts', 'Device Tree'],
- ['dust', 'Dust'],
- ['ebnf', 'Extended Backus-Naur Form'],
- ['elixir', 'Elixir'],
- ['elm', 'Elm'],
- ['erb', 'ERB (Embedded Ruby)'],
- ['erlang-repl', 'Erlang REPL'],
- ['erlang', 'Erlang'],
- ['excel', 'Excel'],
- ['fix', 'FIX'],
- ['flix', 'Flix'],
- ['fortran', 'Fortran'],
- ['fsharp', 'F#'],
- ['gams', 'GAMS'],
- ['gauss', 'GAUSS'],
- ['gcode', 'G-code (ISO 6983)'],
- ['gherkin', 'Gherkin'],
- ['glsl', 'GLSL'],
- ['go', 'Go'],
- ['golo', 'Golo'],
- ['gradle', 'Gradle'],
- ['groovy', 'Groovy'],
- ['haml', 'Haml'],
- ['handlebars', 'Handlebars'],
- ['haskell', 'Haskell'],
- ['haxe', 'Haxe'],
- ['hsp', 'HSP'],
- ['htmlbars', 'HTMLBars'],
- ['http', 'HTTP'],
- ['hy', 'Hy'],
- ['inform7', 'Inform 7'],
- ['ini', 'Ini'],
- ['irpf90', 'IRPF90'],
- ['java', 'Java'],
- ['javascript', 'JavaScript'],
- ['jboss-cli', 'jboss-cli'],
- ['json', 'JSON'],
- ['julia-repl', 'Julia REPL'],
- ['julia', 'Julia'],
- ['kotlin', 'Kotlin'],
- ['lasso', 'Lasso'],
- ['ldif', 'LDIF'],
- ['leaf', 'Leaf'],
- ['less', 'Less'],
- ['lisp', 'Lisp'],
- ['livecodeserver', 'LiveCode'],
- ['livescript', 'LiveScript'],
- ['llvm', 'LLVM IR'],
- ['lsl', 'Linden Scripting Language'],
- ['lua', 'Lua'],
- ['makefile', 'Makefile'],
- ['markdown', 'Markdown'],
- ['mathematica', 'Mathematica'],
- ['matlab', 'Matlab'],
- ['maxima', 'Maxima'],
- ['mel', 'MEL'],
- ['mercury', 'Mercury'],
- ['mipsasm', 'MIPS Assembly'],
- ['mizar', 'Mizar'],
- ['mojolicious', 'Mojolicious'],
- ['monkey', 'Monkey'],
- ['moonscript', 'MoonScript'],
- ['n1ql', 'N1QL'],
- ['nginx', 'Nginx'],
- ['nimrod', 'Nimrod'],
- ['nix', 'Nix'],
- ['nsis', 'NSIS'],
- ['objectivec', 'Objective-C'],
- ['ocaml', 'OCaml'],
- ['openscad', 'OpenSCAD'],
- ['oxygene', 'Oxygene'],
- ['parser3', 'Parser3'],
- ['perl', 'Perl'],
- ['pf', 'pf'],
- ['php', 'PHP'],
- ['pony', 'Pony'],
- ['powershell', 'PowerShell'],
- ['processing', 'Processing'],
- ['profile', 'Python profile'],
- ['prolog', 'Prolog'],
- ['protobuf', 'Protocol Buffers'],
- ['puppet', 'Puppet'],
- ['purebasic', 'PureBASIC'],
- ['python', 'Python'],
- ['q', 'Q'],
- ['qml', 'QML'],
- ['r', 'R'],
- ['rib', 'RenderMan RIB'],
- ['roboconf', 'Roboconf'],
- ['routeros', 'Microtik RouterOS script'],
- ['rsl', 'RenderMan RSL'],
- ['ruby', 'Ruby'],
- ['ruleslanguage', 'Oracle Rules Language'],
- ['rust', 'Rust'],
- ['scala', 'Scala'],
- ['scheme', 'Scheme'],
- ['scilab', 'Scilab'],
- ['scss', 'SCSS'],
- ['shell', 'Shell Session'],
- ['smali', 'Smali'],
- ['smalltalk', 'Smalltalk'],
- ['sml', 'SML'],
- ['sqf', 'SQF'],
- ['sql', 'SQL'],
- ['stan', 'Stan'],
- ['stata', 'Stata'],
- ['step21', 'STEP Part 21'],
- ['stylus', 'Stylus'],
- ['subunit', 'SubUnit'],
- ['swift', 'Swift'],
- ['taggerscript', 'Tagger Script'],
- ['tap', 'Test Anything Protocol'],
- ['tcl', 'Tcl'],
- ['tex', 'TeX'],
- ['thrift', 'Thrift'],
- ['tp', 'TP'],
- ['twig', 'Twig'],
- ['typescript', 'TypeScript'],
- ['vala', 'Vala'],
- ['vbnet', 'VB.NET'],
- ['vbscript-html', 'VBScript in HTML'],
- ['vbscript', 'VBScript'],
- ['verilog', 'Verilog'],
- ['vhdl', 'VHDL'],
- ['vim', 'Vim Script'],
- ['x86asm', 'Intel x86 Assembly'],
- ['xl', 'XL'],
- ['xml', 'HTML, XML'],
- ['xquery', 'XQuery'],
- ['yaml', 'YAML'],
- ['zephir', 'Zephir'],
- ].map(([langTag, langName]) => `.hljs.${langTag}${hover}::after{content:'${langName} [${langTag}]'}`).join('');
- }
- userStyleEl.innerHTML = userStyle;
- function readyHead(fn) {
- if (document.body) { // если есть body, значит head готов
- fn();
- } else if (document.documentElement) {
- const observer = new MutationObserver(() => {
- if (document.body) {
- observer.disconnect();
- fn();
- }
- });
- observer.observe(document.documentElement, { childList: true });
- } else {
- // рекурсивное ожидание появления DOM
- setTimeout(() => readyHead(fn), 10);
- }
- }
- readyHead(() => {
- if (USERSTYLE) document.head.appendChild(userStyleEl);
- });
- function ready(fn) {
- const { readyState } = document;
- if (readyState === 'loading') {
- document.addEventListener('DOMContentLoaded', () => {
- fn();
- });
- } else {
- fn();
- }
- }
- ready(() => {
- // интерфейс для хранения настроек
- const userConfig = {
- // имя записи в localsorage
- key: 'habrafix',
- // модель настроек: ключ - возможные значения
- model: {
- time_publications: ['fromNow', 'absolute'],
- time_comments: ['fromParent', 'fromNow', 'absolute'],
- comments_order: ['trend', 'time'],
- scores_details: [true, false],
- comment_markdown: [false, true],
- },
- config: {},
- // при старте для конфига берем сохраненные параметры либо по умолчанию
- init() {
- let jsonString = localStorage.getItem(userConfig.key);
- const loadedConfig = jsonString ? JSON.parse(jsonString) : {};
- const loadedKeys = Object.keys(loadedConfig);
- const config = {};
- Object.keys(userConfig.model).forEach((key) => {
- const exist = loadedKeys.indexOf(key) >= 0;
- config[key] = exist ? loadedConfig[key] : userConfig.model[key][0];
- });
- jsonString = JSON.stringify(config);
- localStorage.setItem(userConfig.key, jsonString);
- userConfig.config = config;
- },
- getItem(key) {
- return userConfig.config[key];
- },
- setItem(key, value) {
- let jsonString = localStorage.getItem(userConfig.key);
- const config = JSON.parse(jsonString);
- config[key] = value;
- jsonString = JSON.stringify(config);
- localStorage.setItem(userConfig.key, jsonString);
- userConfig.config = config;
- },
- // каруселит параметр по значения модели
- shiftItem(key) {
- const currentValue = userConfig.getItem(key);
- const availableValues = userConfig.model[key];
- const currentIdx = availableValues.indexOf(currentValue);
- const nextIdx = (currentIdx + 1) % availableValues.length;
- const nextValue = availableValues[nextIdx];
- userConfig.setItem(key, nextValue);
- return nextValue;
- },
- };
- userConfig.init();
- if (COMMENTS_MD) {
- const mdSelectorEl = document.getElementById('comment_markdown');
- if (mdSelectorEl) {
- if (userConfig.getItem('comment_markdown')) mdSelectorEl.checked = true;
- mdSelectorEl.addEventListener('input', () => {
- userConfig.setItem('comment_markdown', mdSelectorEl.checked);
- });
- }
- }
- // надо ли ещё
- [...document.querySelectorAll('iframe[src^="https://codepen.io/"]')]
- .map(el => el.setAttribute('scrolling', 'no'));
- // остановка гифок по клику и воспроизведение при повторном клике
- function toggleGIF(el) {
- // если атрибут со старым линком пуст или отсутствует
- if (!el.dataset.oldSrc) {
- // заменим ссылку на data-url-svg с треугольником в круге
- const w = Math.max(el.clientWidth || 256, 16);
- const h = Math.max(el.clientHeight || 128, 16);
- const cx = w / 2;
- const cy = h / 2;
- const r = Math.min(w, h) / 4;
- const ax = (r * 61) / 128;
- const by = (r * 56) / 128;
- const bx = (r * 35) / 128;
- const svg = `data:image/svg+xml;utf8,
- <svg width='${w}' height='${h}' baseProfile='full' xmlns='http://www.w3.org/2000/svg'>
- <rect x='0' y='0' width='${w}' height='${h}' fill='${GIF_STOP_COLOR_BG}'/>
- <circle cx='${cx}' cy='${cy}' r='${r}' fill='${GIF_STOP_COLOR_FG}'/>
- <polygon points='${cx + ax} ${cy} ${cx - bx} ${cy - by} ${cx - bx} ${cy + by}' fill='${GIF_STOP_COLOR_BG}' />
- </svg>
- `;
- el.dataset.oldSrc = el.getAttribute('src'); // eslint-disable-line no-param-reassign
- el.setAttribute('src', svg);
- } else if (GIF_STOP_OVERTYPE) {
- // иначе поставим svg с троеточием
- const w = el.clientWidth;
- const h = el.clientHeight;
- const cx = w / 2;
- const cy = h / 2;
- const r = Math.min(w, h) / 4;
- const r2 = r / 4;
- const svg = `data:image/svg+xml;utf8,
- <svg width='${w}' height='${h}' baseProfile='full' xmlns='http://www.w3.org/2000/svg'>
- <rect x='0' y='0' width='${w}' height='${h}' fill='${GIF_STOP_COLOR_BG}'/>
- <circle cx='${cx - r}' cy='${cy}' r='${r2}' fill='${GIF_STOP_COLOR_FG}'/>
- <circle cx='${cx}' cy='${cy}' r='${r2}' fill='${GIF_STOP_COLOR_FG}'/>
- <circle cx='${cx + r}' cy='${cy}' r='${r2}' fill='${GIF_STOP_COLOR_FG}'/>
- </svg>
- `;
- el.setAttribute('src', svg);
- // когда отрендерится троеточие, можно менять на исходную гифку
- setTimeout(() => {
- if (el.dataset.oldSrc) {
- el.setAttribute('src', el.dataset.oldSrc);
- el.dataset.oldSrc = ''; // eslint-disable-line no-param-reassign
- }
- }, 100);
- } else {
- const img = document.createElement('img');
- img.setAttribute('src', el.dataset.oldSrc);
- if (el.hasAttribute('align')) {
- img.setAttribute('align', el.getAttribute('align'));
- }
- el.parentNode.insertBefore(img, el);
- img.onclick = () => toggleGIF(img); // eslint-disable-line no-param-reassign
- el.parentNode.removeChild(el);
- }
- }
- if (GIF_STOP) {
- [...document.querySelectorAll('.post__text img[src$=".gif"], .comment__message img[src$=".gif"]')]
- .forEach((el) => {
- if (GIF_STOP_ONLOAD) toggleGIF(el);
- el.onclick = () => toggleGIF(el); // eslint-disable-line no-param-reassign
- });
- }
- // фиксирование высоты публикации чтобы убрать прыжки прокрутки
- if (FIX_JUMPING_SCROLL) {
- const postBodyEl = document.querySelector('.post__body_full');
- const checkPostBodyInViewport = () => postBodyEl.getBoundingClientRect().bottom > 0;
- const autoHeightPost = () => {
- if (checkPostBodyInViewport()) {
- window.removeEventListener('scroll', autoHeightPost);
- postBodyEl.style.height = 'auto';
- }
- };
- if (postBodyEl && !checkPostBodyInViewport()) {
- const h = postBodyEl.clientHeight;
- postBodyEl.style.height = `${h}px`;
- window.addEventListener('scroll', autoHeightPost);
- }
- }
- // счетчики кармы
- if (KARMA_DETAILS) {
- Array.from(document.querySelectorAll('.user-info__stats-item.stacked-counter')).forEach((itemCounter) => {
- itemCounter.style.marginRight = '16px'; // eslint-disable-line no-param-reassign
- });
- Array.from(document.querySelectorAll('.page-header__stats_karma')).forEach((karmaEl) => {
- karmaEl.style.width = 'auto'; // eslint-disable-line no-param-reassign
- karmaEl.style.minWidth = '84px'; // eslint-disable-line no-param-reassign
- });
- Array.from(document.querySelectorAll('.stacked-counter[href="/info/help/karma/"]')).forEach((couterEl) => {
- let total = parseInt(couterEl.title, 10);
- const scoreEl = couterEl.querySelector('.stacked-counter__value');
- if (!scoreEl || !total) return;
- couterEl.style.width = 'auto'; // eslint-disable-line no-param-reassign
- couterEl.style.minWidth = '84px'; // eslint-disable-line no-param-reassign
- const score = parseFloat(scoreEl.innerHTML.replace('–', '-').replace(',', '.'), 10);
- if (score > total) total = score;
- const likes = (total + score) / 2;
- const percent = Math.round((100 * likes) / total);
- const details = ` = ${total} × (${percent} − ${100 - percent})%`;
- const detailsEl = document.createElement('span');
- detailsEl.innerHTML = details;
- detailsEl.style.color = '#545454';
- detailsEl.style.fontFamily = '"-apple-system",BlinkMacSystemFont,Arial,sans-serif';
- detailsEl.style.fontSize = '13px';
- detailsEl.style.fontWeight = 'normal';
- detailsEl.style.verticalAlign = 'middle';
- scoreEl.appendChild(detailsEl);
- couterEl.title += `, ${(likes).toFixed(2)} плюсов и ${(total - likes).toFixed(2)} минусов`; // eslint-disable-line no-param-reassign
- });
- }
- // счетчики рейтинга с подробностями
- const scoresMap = new Map();
- class Score {
- constructor(el) {
- this.el = el;
- const data = this.constructor.parse(el);
- this.rating = data.rating;
- this.total = data.total;
- this.likes = data.likes;
- this.dislikes = data.dislikes;
- this.isDetailed = false;
- this.observer = new MutationObserver(() => this.update());
- }
- setDetails(isDetailed) {
- if (this.isDetailed === isDetailed) return;
- this.isDetailed = isDetailed;
- this.update();
- }
- update() {
- const data = this.constructor.parse(this.el);
- this.rating = data.rating;
- this.total = data.total;
- this.likes = data.likes;
- this.dislikes = data.dislikes;
- this.observer.disconnect();
- if (this.isDetailed) {
- this.details();
- } else {
- this.simply();
- }
- this.observer.observe(this.el, { childList: true });
- }
- static parse(el) {
- let [total, likes, dislikes] = el
- .attributes.title.textContent
- .match(/[0-9]+/g).map(Number);
- let [, sign, rating] = el.innerHTML.match(/([–]?)(\d+)/); // eslint-disable-line prefer-const
- rating = Number(rating);
- if (sign) rating = -rating;
- // не знаю что там происходит при голосовании, так что на всякий случай
- const diff = rating - (likes - dislikes);
- if (diff < 0) {
- total += Math.abs(diff);
- dislikes += Math.abs(diff);
- } else if (diff > 0) {
- total += diff;
- likes += diff;
- }
- return {
- rating,
- total,
- likes,
- dislikes,
- };
- }
- simply() {
- let innerHTML = '';
- if (this.rating > 0) {
- innerHTML = `+${this.rating}`;
- } else if (this.rating < 0) {
- innerHTML = `–${Math.abs(this.rating)}`;
- } else {
- innerHTML = '0';
- }
- this.el.innerHTML = innerHTML;
- }
- details() {
- let innerHTML = '';
- if (this.rating > 0) {
- innerHTML = `+${this.rating}`;
- } else if (this.rating < 0) {
- innerHTML = `–${Math.abs(this.rating)}`;
- } else {
- innerHTML = '0';
- }
- if (this.total !== 0) {
- let details = '';
- if (RATING_DETAILS_PN) {
- details = ` = ${this.likes} − ${this.dislikes}`;
- } else {
- const percent = Math.round((100 * this.likes) / this.total);
- details = ` = ${this.total} × (${percent} − ${100 - percent})%`;
- }
- innerHTML += ` <span style='color: #545454; font-weight: normal'>${details}</span>`;
- }
- this.el.innerHTML = innerHTML;
- }
- }
- // парсим их
- [...document.querySelectorAll('.voting-wjt__counter')].forEach((el) => {
- scoresMap.set(el, new Score(el));
- });
- // добавляем подробностей
- if (RATING_DETAILS) {
- if (RATING_DETAILS_ONCLICK) {
- const isDetailed = userConfig.getItem('scores_details');
- if (isDetailed) scoresMap.forEach(score => score.setDetails(isDetailed));
- scoresMap.forEach((score) => {
- score.el.onclick = () => { // eslint-disable-line no-param-reassign
- const nowDetailed = userConfig.shiftItem('scores_details');
- scoresMap.forEach(s => s.setDetails(nowDetailed));
- };
- });
- } else {
- scoresMap.forEach(score => score.setDetails(true));
- }
- }
- // метки времени и работа с ними
- const pageLoadTime = new Date();
- const monthNames = [
- 'января', 'февраля', 'марта',
- 'апреля', 'мая', 'июня',
- 'июля', 'августа', 'сентября',
- 'октября', 'ноября', 'декабря',
- ];
- class HabraTime {
- constructor(el, parent) {
- this.el = el;
- this.parent = parent;
- this.attrDatetime = this.constructor.getAttributeDatetime(el);
- this.date = new Date(this.attrDatetime);
- }
- // вот было бы хорошо, если б на хабре были datetime атрибуты
- static getAttributeDatetime(el) {
- const imagination = el.getAttribute('datetime');
- if (imagination) return imagination;
- const re = /((сегодня|вчера)|(\d+)[ .]([а-я]+|\d+)[ .]?(\d+)?) в (\d\d:\d\d)/;
- let [,,
- recently, // eslint-disable-line prefer-const
- day, month, year,
- time, // eslint-disable-line prefer-const
- ] = el.innerHTML.match(re);
- // и местное время
- let moscow;
- if (recently || year === undefined) {
- const offsetMoscow = 3 * 60 * 60 * 1000;
- const yesterdayShift = (recently === 'вчера') ? 24 * 60 * 60 * 1000 : 0;
- const offset = pageLoadTime.getTimezoneOffset() * 60 * 1000;
- const value = (pageLoadTime - yesterdayShift) + offsetMoscow + offset;
- moscow = new Date(value);
- }
- if (recently) {
- day = moscow.getDate();
- month = moscow.getMonth() + 1;
- } else if (month.length !== 2) {
- month = monthNames.indexOf(month) + 1;
- } else {
- month = +month;
- }
- if (day < 10) day = `0${+day}`;
- if (month < 10) month = `0${month}`;
- if (year < 100) year = `20${year}`;
- if (year === undefined) year = moscow.getFullYear();
- return `${year}-${month}-${day}T${time}+03:00`;
- }
- absolute() {
- let result = '';
- const time = this.date;
- const day = time.getDate();
- const month = time.getMonth();
- const monthName = monthNames[month];
- const year = time.getFullYear();
- const hours = time.getHours();
- const minutes = time.getMinutes();
- const now = new Date();
- const nowDay = now.getDate();
- const nowMonth = now.getMonth();
- const nowYear = now.getFullYear();
- const yesterday = new Date((now - 24) * 60 * 60 * 1000);
- const yesterdayDay = yesterday.getDate();
- const yesterdayMonth = yesterday.getMonth();
- const yesterdayYear = yesterday.getFullYear();
- const hhmm = `${hours}:${minutes >= 10 ? minutes : `0${minutes}`}`;
- const isToday =
- day === nowDay &&
- month === nowMonth &&
- year === nowYear;
- const isYesterday =
- day === yesterdayDay &&
- month === yesterdayMonth &&
- year === yesterdayYear;
- if (isToday) {
- result = `сегодня в ${hhmm}`;
- } else if (isYesterday) {
- result = `вчера в ${hhmm}`;
- } else if (nowYear === year) {
- result = `${day} ${monthName} в ${hhmm}`;
- } else {
- result = `${day} ${monthName} ${year} в ${hhmm}`;
- }
- return result;
- }
- static relative(milliseconds) {
- let result = '';
- const pluralForm = (n, forms) => {
- if (n % 10 === 1 && n % 100 !== 11) return forms[0];
- if (n % 10 >= 2 && n % 10 <= 4 && (n % 100 < 10 || n % 100 >= 20)) return forms[1];
- return forms[2];
- };
- const formats = [
- ['год', 'года', 'лет'],
- ['месяц', 'месяца', 'месяцев'],
- ['день', 'дня', 'дней'],
- ['час', 'часа', 'часов'],
- ['минуту', 'минуты', 'минут'],
- ];
- const minutes = milliseconds / 60000;
- const hours = minutes / 60;
- const days = hours / 24;
- const months = days / 30;
- const years = months / 12;
- const idx = [years, months, days, hours, minutes].findIndex(x => x >= 1);
- if (idx === -1) {
- result = 'несколько секунд';
- } else {
- const value = Math.floor([years, months, days, hours, minutes][idx]);
- const forms = formats[idx];
- const form = pluralForm(value, forms);
- result = `${value} ${form}`;
- }
- return result;
- }
- fromNow() {
- const diff = Math.abs(Date.now() - this.date);
- return `${this.constructor.relative(diff)} назад`;
- }
- fromParent() {
- const diff = Math.abs(this.date - this.parent.date);
- return `через ${this.constructor.relative(diff)}`;
- }
- }
- // собираем метки времени
- const datesMap = new Map();
- const megapostTimeEl = document.querySelector('.megapost-head__meta > .list_inline > .list__item');
- (megapostTimeEl ? [megapostTimeEl] : [])
- .concat([...document.querySelectorAll(`
- .post__time,
- .preview-data__time-published,
- time.comment__date-time_published,
- .tm-post__date,
- .user-message__date-time
- `)]).forEach((el) => {
- datesMap.set(el, new HabraTime(el));
- });
- function updateTime() {
- datesMap.forEach((habraTime) => {
- let type;
- let otherTypes;
- if (habraTime.parent) {
- type = userConfig.config.time_comments;
- otherTypes = userConfig.model.time_comments
- .filter(str => str !== type);
- } else {
- type = userConfig.config.time_publications;
- otherTypes = userConfig.model.time_publications
- .filter(str => str !== type);
- }
- const title = otherTypes.map(otherType => habraTime[otherType]()).join(', ');
- habraTime.el.innerHTML = habraTime[type](); // eslint-disable-line no-param-reassign
- habraTime.el.setAttribute('title', title);
- });
- }
- if (TIME_DETAILS) {
- datesMap.forEach((habraTime) => {
- habraTime.el.setAttribute(
- 'style',
- 'cursor: pointer; -moz-user-select: none; -webkit-user-select: none; -ms-user-select: none; user-select: none;',
- );
- habraTime.el.onclick = () => { // eslint-disable-line no-param-reassign
- if (habraTime.parent) {
- userConfig.shiftItem('time_comments');
- } else {
- userConfig.shiftItem('time_publications');
- }
- updateTime();
- };
- });
- // подождём, когда дерево комментариев будет построено
- // у некоторых меток времени будут установлены родители
- // тогда и обновим их тексты
- setTimeout(updateTime, 100);
- setInterval(updateTime, 30 * 1000);
- }
- // время публикации, понадобится для корня древа комментариев
- let datePublication = datesMap.get(megapostTimeEl || document.querySelector('.post__time'));
- // если нету публикации поищем самую раннюю метку времени
- if (!datePublication) {
- datePublication = { date: pageLoadTime };
- datesMap.forEach((date) => {
- if (date.date < datePublication.date) datePublication = date;
- });
- }
- // создаем дерево комментариев
- class ItemComment {
- constructor(el, parent) {
- this.parent = parent;
- this.el = el;
- this.lvl = parent.lvl + 1;
- this.id = Number(el.getAttribute('rel'));
- this.commentEl = el.querySelector('.comment');
- if (this.commentEl) {
- this.timeEl = this.commentEl.querySelector('time');
- this.ratingEl = this.commentEl.querySelector('.js-score');
- }
- this.date = datesMap.get(this.timeEl);
- if (this.date) {
- this.date.parent = parent.date;
- } else {
- this.date = parent.date;
- }
- this.votes = scoresMap.get(this.ratingEl) || {
- total: 0, likes: 0, dislikes: 0, rating: 0,
- };
- this.elList = el.querySelector('.content-list_nested-comments');
- }
- existId(id) {
- return !!this.elList.querySelector(id);
- }
- existNew() {
- return !!this.elList.querySelector('.js-comment_new');
- }
- getLength() {
- let { length } = this.list;
- this.list.forEach((node) => {
- length += node.getLength();
- });
- return length;
- }
- }
- class CommentsTree {
- constructor() {
- this.root = {
- isRoot: true,
- date: datePublication,
- lvl: 0,
- elList: document.getElementById('comments-list'),
- list: [],
- };
- }
- static exist() {
- return !!document.getElementById('comments-list');
- }
- update() {
- if (!this.root.elList) return;
- const recAdd = (node) => {
- node.list = Array.from(node.elList.children) // eslint-disable-line no-param-reassign
- .map(el => new ItemComment(el, node));
- node.list.forEach(recAdd);
- };
- recAdd(this.root);
- }
- walkTree(fn) {
- const walk = (tree) => {
- fn(tree);
- tree.list.forEach(walk);
- };
- walk(this.root);
- }
- sort(fn) {
- if (!this.root.elList) return;
- this.walkTree((tree) => {
- tree.list.sort(fn).forEach(subtree => tree.elList.appendChild(subtree.el));
- });
- }
- shuffle() {
- if (!this.root.elList) return;
- const randInt = maximum => Math.floor(Math.random() * (maximum + 1));
- this.walkTree((tree) => {
- const { list } = tree;
- for (let i = 0; i < list.length; i += 1) {
- const j = randInt(i);
- [list[i], list[j]] = [list[j], list[i]];
- }
- list.forEach(subtree => tree.elList.appendChild(subtree.el));
- });
- }
- }
- const commentsTree = new CommentsTree();
- commentsTree.update();
- // здесь начинается сортировка комментариев
- const commentsOrderEl = document.createElement('div');
- commentsOrderEl.classList.add('comments_order');
- commentsOrderEl.innerHTML = sortVariants.map(([type, text]) => {
- const underline = (type === 'time') ? 'style="text-decoration: underline;"' : '';
- return `<a href='#' data-order="${type}" ${underline}>${text}</a>`;
- }).join(', ');
- if (COMMENTS_SORT && document.getElementById('comments-list')) {
- const commentsList = document.getElementById('comments-list');
- commentsList.parentElement.insertBefore(commentsOrderEl, commentsList);
- }
- const commentsComparators = {
- time(a, b) {
- return a.id - b.id;
- },
- freshness(a, b) {
- return b.id - a.id;
- },
- rating(a, b) {
- const ascore = a.votes.rating;
- const bscore = b.votes.rating;
- if (bscore !== ascore) return bscore - ascore;
- return b.id - a.id;
- },
- popularity(a, b) {
- const aVotes = a.votes.total;
- const bVotes = b.votes.total;
- if (aVotes !== bVotes) return bVotes - aVotes;
- const aLength = a.getLength();
- const bLength = b.getLength();
- if (aLength !== bLength) return bLength - aLength;
- return b.id - a.id;
- },
- quality(a, b) {
- const aQuality = a.votes.rating / a.votes.total || 0;
- const bQuality = b.votes.rating / b.votes.total || 0;
- if (aQuality !== bQuality) return bQuality - aQuality;
- if (a.votes.rating !== b.votes.rating) return b.votes.rating - a.votes.rating;
- return b.id - a.id;
- },
- trend(a, b) {
- // в первые сутки после публикации статьи число посещений больше чем в остальное время
- const oneDay = 24 * 60 * 60 * 1000;
- const firstDayEnd = +datePublication.date + oneDay;
- // у комментария есть только три дня на голосование с момента его создания
- const threeDays = 3 * oneDay;
- const now = Date.now();
- // прикинем число голосов в первый день
- const aDate = +a.date.date;
- let aViews = 0;
- // в первый день
- if (aDate <= firstDayEnd) {
- aViews += Math.min(firstDayEnd, now) - aDate;
- }
- // и в остальное время
- if (now >= firstDayEnd) {
- const threeDaysEnd = aDate + threeDays;
- // для этого соотношения я собрал статистику
- aViews += (Math.min(threeDaysEnd, now) - Math.max(firstDayEnd, aDate)) / 16;
- }
- const aScore = a.votes.rating / aViews;
- // аналогично
- const bDate = +b.date.date;
- let bViews = 0;
- if (bDate <= firstDayEnd) {
- bViews += Math.min(firstDayEnd, now) - bDate;
- }
- if (now >= firstDayEnd) {
- const threeDaysEnd = bDate + threeDays;
- // найти зависимость активности голосования от времени суток не удалось
- bViews += (Math.min(threeDaysEnd, now) - Math.max(firstDayEnd, bDate)) / 16;
- }
- const bScore = b.votes.rating / bViews;
- if (bScore === aScore) return b.id - a.id;
- return bScore - aScore;
- },
- };
- const sortComments = () => {
- const order = userConfig.getItem('comments_order');
- Array.from(commentsOrderEl.children).forEach((el) => {
- if (el.dataset.order === order) {
- el.style.textDecoration = 'underline'; // eslint-disable-line no-param-reassign
- } else {
- el.style.textDecoration = ''; // eslint-disable-line no-param-reassign
- }
- });
- if (order === 'shuffle') {
- commentsTree.shuffle();
- } else {
- const compare = commentsComparators[order];
- commentsTree.sort(compare);
- }
- };
- // сортируем комменты при загрузке страницы
- // или не сортируем, если они уже по порядку
- if (COMMENTS_SORT && COMMENTS_SORT_ONLOAD && userConfig.getItem('comments_order') !== 'time') {
- sortComments();
- }
- Array.from(commentsOrderEl.children).forEach((el) => {
- el.onclick = () => { // eslint-disable-line no-param-reassign
- userConfig.setItem('comments_order', el.dataset.order);
- sortComments();
- };
- });
- // меняем ссылки ведущие к новым комментариям на ссылки к началу комментариев
- if (COMMENTS_LINKS) {
- const commentsLinks = document.getElementsByClassName('post-stats__comments-link');
- for (let i = 0; i < commentsLinks.length; i += 1) {
- const iLink = commentsLinks[i];
- const hrefValue = iLink.getAttribute('href');
- const hrefToComments = hrefValue.replace('#first_unread', '#comments');
- iLink.setAttribute('href', hrefToComments);
- }
- }
- // сворачивание комментов
- if (COMMENTS_HIDE) {
- const commentHash = window.location.hash;
- const toggle = (subtree) => {
- const listLength = subtree.list.length;
- if (listLength === 0) return;
- /* eslint-disable */
- if (subtree.switcherEl.dataset.isVisibleList === 'true') {
- subtree.switcherEl.dataset.isVisibleList = 'false';
- subtree.switcherEl.innerHTML = `\u229E раскрыть ветвь ${subtree.getLength()}`;
- subtree.elList.style.display = 'none';
- } else {
- subtree.switcherEl.dataset.isVisibleList = 'true';
- subtree.switcherEl.innerHTML = '\u229F';
- subtree.elList.style.display = 'block';
- }
- /* eslint-enable */
- };
- const isFirefox = navigator.userAgent.toLowerCase().indexOf('firefox') >= 0;
- commentsTree.walkTree((subtree) => {
- // не пытаемся сворачивать корень
- if (subtree.isRoot) return;
- // у похищенных нет футера
- const footerEl = subtree.commentEl.querySelector('.comment__footer');
- if (footerEl === null) return;
- // создаём переключатель
- const switcher = document.createElement('a');
- switcher.setAttribute('href', '#');
- switcher.classList.add('comment__footer-link');
- switcher.classList.add('comment__switcher');
- switcher.dataset.isVisibleList = 'true';
- switcher.innerHTML = '\u229F';
- if (subtree.list.length === 0) switcher.innerHTML = '\u22A1';
- switcher.style.marginLeft = isFirefox ? '-4px' : '-6px';
- footerEl.insertBefore(switcher, footerEl.children[0]);
- subtree.switcherEl = switcher; // eslint-disable-line no-param-reassign
- switcher.onclick = () => toggle(subtree);
- const isHideLvl = subtree.lvl === HIDE_LEVEL;
- const isLineLvl = subtree.lvl % LINE_LEN === 0;
- if (isLineLvl) {
- subtree.elList.classList.add('comments_new-line');
- const lineNumber = subtree.lvl / LINE_LEN;
- subtree.elList.classList.add(`comments_new-line-${lineNumber % 4}`);
- }
- // при запуске не сворачиваем ветки с новыми комментами, и содержащие целевой id
- if (
- (isHideLvl || isLineLvl) && !subtree.existNew() &&
- !(commentHash && subtree.existId(commentHash))
- ) {
- toggle(subtree);
- }
- });
- }
- if (SCROLL_LEGEND) {
- const postBodyEl = document.querySelector('.post__body_full') || document.querySelector('.article__body');
- const commentsEl = document.getElementById('comments-list');
- const getPercents = (el) => {
- if (!el) return { topPercent: 0, heightPercent: 0 };
- const pageHeight = document.documentElement.scrollHeight;
- const top = el.getBoundingClientRect().top + window.pageYOffset;
- const topPercent = ((100 * top) / pageHeight).toFixed(2);
- const height = el.clientHeight;
- const heightPercent = ((100 * height) / pageHeight).toFixed(2);
- return { topPercent, heightPercent };
- };
- const updateLegend = (pageEl, legendEl) => {
- const { topPercent, heightPercent } = getPercents(pageEl);
- legendEl.style.top = `${topPercent}%`; // eslint-disable-line no-param-reassign
- legendEl.style.height = `${heightPercent}%`; // eslint-disable-line no-param-reassign
- };
- const legendPost = document.createElement('div');
- legendPost.classList.add('legend_el');
- legendPost.style.background = 'rgba(84, 142, 170, 0.66)';
- updateLegend(postBodyEl, legendPost);
- document.body.appendChild(legendPost);
- const legendComments = document.createElement('div');
- legendComments.classList.add('legend_el');
- legendComments.style.background = 'rgba(49, 176, 7, 0.66)';
- updateLegend(commentsEl, legendComments);
- document.body.appendChild(legendComments);
- setInterval(() => {
- updateLegend(postBodyEl, legendPost);
- updateLegend(commentsEl, legendComments);
- }, 1000);
- }
- });