返回首頁 

Greasy Fork is available in English.

Habr.Features

Всякое-разное для Habr aka habr.com


安装此脚本?
  1. // ==UserScript==// @name Habr.Features// @version 3.7.71// @description Всякое-разное для Habr aka habr.com// @author AngReload// @include https://habr.com/*// @include http://habr.com/*// @namespace habr_comments// @run-at document-start// @grant GM.xmlHttpRequest// @connect m.habr.com// @icon https://habr.com/favicon.ico// ==/UserScript==/* global localStorage, MutationObserver, GM */// настройки по умолчаниюconst FLAGS = {};// размеры кнопокFLAGS.SMALL_BUTTONS = false;// Предотвратить закрытие страницы, если статья удаленаFLAGS.PREVENT_CLOSING_DELETED_PAGES = false;// остановка гифок// клик по гифке заменит картинку на заглушку// повторный клик вернет гифку на местоFLAGS.GIF_STOP = true;// остановить гифки при загрузке страницыFLAGS.GIF_STOP_ONLOAD = false;// цвета заглушкиFLAGS.GIF_STOP_COLOR_FG = 'White'; // WhiteFLAGS.GIF_STOP_COLOR_BG = 'LightGray'; // LightGray or WhiteSmoke// менять src вместо создания-удаления нодFLAGS.GIF_STOP_OVERTYPE = false;// показывать счетчики рейтинга в виде:// рейтинг = число_плюсов - число_минусовFLAGS.RATING_DETAILS = true;// клик мышкой по рейтингу меняет вид на простой \ детальныйFLAGS.RATING_DETAILS_ONCLICK = false;// рейтинг = число_голосовавших * (процент_плюсов - процент_минусов)%FLAGS.RATING_DETAILS_PN = false;// карма = число_голосовавших * (процент_товарищей - процент_неприятелей)%FLAGS.KARMA_DETAILS = true;// показывать метки времени в текущем часовом поясе// абсолютно, либо относительно текущего времени, либо относительно родительского времени// меняется по клику, в всплывающей подсказке другие виды времени, автообновляетсяFLAGS.TIME_DETAILS = true;// добавить возможность сортировки комментариевFLAGS.COMMENTS_SORT = true;// сортировать комменты при загрузке страницы или оставить сортировку по времениFLAGS.COMMENTS_SORT_ONLOAD = true;// список доступных сортировокFLAGS.COMMENTS_SORT_BY_FRESHNESS = true;FLAGS.COMMENTS_SORT_BY_TREND = true; // самая полезная сортировкаFLAGS.COMMENTS_SORT_BY_QUALITY = false;FLAGS.COMMENTS_SORT_BY_RATING = false;FLAGS.COMMENTS_SORT_BY_POPULARITY = false;FLAGS.COMMENTS_SORT_BY_REDDIT = false;FLAGS.COMMENTS_SORT_BY_RANDOM = false;// добавить возможность сворачивать комментарииFLAGS.COMMENTS_HIDE = false;// свернуть комментарии если их глубина вложенности равна некому числуFLAGS.HIDE_LEVEL = 4;// свернуть комменты с 4-го уровняFLAGS.HIDE_LEVEL_4 = false;// сделать «возврат каретки» для комментариев чтобы глубина вложенности не превышала некого числаFLAGS.LINE_LEN = 8;// отбивать каждый следущий уровень вложенности дополнительным отступомFLAGS.REDUCE_NEW_LINES = true;// глубина вложенности при «возврате каретки» комментариев обозначается разным цветомFLAGS.RAINBOW_NEW_LINE = true;// заменить ссылки ведущие к новым комментариям на дерево комментариевFLAGS.COMMENTS_LINKS = true;// запоминание галки «Использовать MarkDown» для комментариевFLAGS.COMMENTS_MD = true;// добавить ссылку на хабрасторадж в форму редактирования комментариевFLAGS.HTSO_BTN = true;// добавить ссылку на отслеживаемых в странице профиляFLAGS.SUBS_BTN = true;// включить поиск комментариев в профиляхFLAGS.FIND_COMMENTS = true;// линии вдоль прокрутки для отображения размеров поста и комментариевFLAGS.SCROLL_LEGEND = true;// добавляет кнопку активации ночного режимаFLAGS.NIGHT_MODE = true;// добавляет диалок настроекFLAGS.CONFIG_INTERFACE = true;// включить разные стилиFLAGS.USERSTYLE = true;// лента постов с увеличенными отступами, нижний бар выровнен вправоFLAGS.USERSTYLE_FEED_DISTANCED = true;// мелкий фикс для отступа в счетчике лентыFLAGS.USERSTYLE_COUNTER_NEW_FIX_SPACE = true;// удаляет правую колонку, там где не жалкоFLAGS.USERSTYLE_REMOVE_SIDEBAR_RIGHT = true;// аватарки без скругленийFLAGS.USERSTYLE_REMOVE_BORDER_RADIUS_AVATARS = true;// большие аватарки в профиле и карточкахFLAGS.USERSTYLE_USERINFO_BIG_AVATARS = true;// ограничить размер картинок в комментахFLAGS.USERSTYLE_COMMENTS_IMG_MAXSIZE = 0;// ограничить размер картинок в комментахFLAGS.USERSTYLE_COMMENTS_OPACITY = false;// нормальные стили для комментариевFLAGS.USERSTYLE_COMMENTS_FIX = true;// нормальные стили для кодаFLAGS.USERSTYLE_CODE_FIX = true;// окантовка границ спойлеровFLAGS.USERSTYLE_SPOILER_BORDERS = true;// делает глючные плавающие блоки статичнымиFLAGS.USERSTYLE_STATIC_STICKY = true;// показывать язык подсветки блоков кодаFLAGS.USERSTYLE_HLJS_LANG = true;// показывать язык кода только при наведенииFLAGS.USERSTYLE_HLJS_LANG_HOVER = false;// свой шрифт для блоков кодаFLAGS.USERSTYLE_CODE_FONT = ''; // PT Mono// размер табов в кодеFLAGS.USERSTYLE_CODE_TABSIZE = 2;// для ночного режимаFLAGS.USERSTYLE_CODE_NIGHT = true;// для настроекFLAGS.USERSTYLE_CONFIG_INTERFACE = true;FLAGS.SCROLL_BEHAVIOR_SMOOTH = true;const configOptions = [['KARMA_DETAILS', 'счётчики кармы с плюсами и минусами'],['RATING_DETAILS', 'рейтинги с плюсами и минусами'],['RATING_DETAILS_PN', 'рейтинги в процентах'],['TIME_DETAILS', 'отметки о времени с подробностями'],['PREVENT_CLOSING_DELETED_PAGES', 'предотвратить закрытие страницы, если статья удалена'],['GIF_STOP', 'остановка гифок'],['GIF_STOP_ONLOAD', 'остановить гифки при загрузке страницы'],['COMMENTS_SORT', 'сортировка комментов'],['COMMENTS_SORT_ONLOAD', 'сортировать комменты при загрузке'],['COMMENTS_SORT_BY_FRESHNESS', 'сортировка новые'],['COMMENTS_SORT_BY_TREND', 'сортировка горячие'],['COMMENTS_SORT_BY_QUALITY', 'сортировка хорошие'],['COMMENTS_SORT_BY_REDDIT', 'сортировка проверенные'],['COMMENTS_SORT_BY_RATING', 'сортировка рейтинговые'],['COMMENTS_SORT_BY_POPULARITY', 'сортировка популярные'],['COMMENTS_SORT_BY_RANDOM', 'сортировка случайные'],// ['COMMENTS_HIDE', 'сворачивание комментов'],// ['HIDE_LEVEL_4', 'свернуть комменты с 4-го уровня'],// ['REDUCE_NEW_LINES', 'возврат каретки с уменьшением ширины'],// ['RAINBOW_NEW_LINE', 'возврат каретки комментов в разных цветах'],// ['COMMENTS_LINKS', '"читать комментарии" ведёт на корень комментов'],// ['COMMENTS_MD', 'запоминать галку MarkDown'],['USERSTYLE_COMMENTS_OPACITY', 'заминусованные комменты без прозрачности'],// ['FIX_JUMPING_SCROLL', 'заморозить высоту статьи при загрузке'],['SCROLL_LEGEND', 'ленгенда страницы у скроллбара'],['SUBS_BTN', 'табы подписок в профилях'],['FIND_COMMENTS', 'поиск по комментариям в профилях'],['NIGHT_MODE', 'ночной режим'],['SMALL_BUTTONS', 'маленькие кнопки настроек и ночного режима'],// ['USERSTYLE', 'стилизация'],['USERSTYLE_COMMENTS_FIX', 'стили комментов'],['USERSTYLE_HLJS_LANG', 'показать язык для блоков кода'],// ['USERSTYLE_HLJS_LANG_HOVER', 'язык кода скрыт до наведении курсора'],['USERSTYLE_STATIC_STICKY', 'зафиксировать плавающие блоки'],// ['USERSTYLE_SPOILER_BORDERS', 'видимые границы спойлеров'],['USERSTYLE_FEED_DISTANCED', 'большие отступы между постами в ленте'],['USERSTYLE_REMOVE_SIDEBAR_RIGHT', 'удалить правую колонку сайта'],// ['USERSTYLE_REMOVE_BORDER_RADIUS_AVATARS', 'квадратные аватарки'],['USERSTYLE_USERINFO_BIG_AVATARS', 'по возможности большие аватарки в профилях'],];// сохраняем умолчания для панели настроекif (!localStorage.getItem('habrafixFlags')) {localStorage.setItem('habrafixFlags', JSON.stringify(FLAGS));} else {const jsonString = localStorage.getItem('habrafixFlags');const loadedConfig = jsonString ? JSON.parse(jsonString) : {};const loadedKeys = Object.keys(loadedConfig);Object.keys(FLAGS).forEach((key) => {if (loadedKeys.includes(key) &&configOptions.find(arr => arr[0] === key)) {FLAGS[key] = loadedConfig[key];}});}let BUTTON_SIZE = 16;let BUTTON_SIZE2 = 25;let BUTTON_SIZE4 = 48;let KARMA_WIDTH = 84; // ?if (FLAGS.SMALL_BUTTONS) {BUTTON_SIZE = 16;BUTTON_SIZE2 = 25;BUTTON_SIZE4 = 48;KARMA_WIDTH = 84; // ?} else {BUTTON_SIZE = 32;BUTTON_SIZE2 = 25;BUTTON_SIZE4 = 88;KARMA_WIDTH = 84;}// интерфейс для хранения настроекconst userConfig = {// имя записи в localsoragekey: 'habrafix',// модель настроек: ключ - возможные значенияmodel: {time_publications: ['fromNow', 'absolute'],time_comments: ['fromParent', 'fromNow', 'absolute'],comments_order: ['trend', 'time'],scores_details: [true, false],comment_markdown: [false, true],night_mode: [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) {const jsonString = localStorage.getItem(userConfig.key);const config = JSON.parse(jsonString);return config[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();// свои стилиconst userStyleEl = document.createElement('style');let userStyle = '';if (FLAGS.SCROLL_LEGEND) {userStyle += `.legend_el {position: fixed;width: 4px;right: 0;transition: top 1s ease-out, height 1s ease-out;z-index: 10000;}#xpanel {right: 4px;}`;}if (FLAGS.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 (FLAGS.USERSTYLE_COUNTER_NEW_FIX_SPACE) {userStyle += `.toggle-menu__item-counter_new {margin-left: 4px;}`;}if (FLAGS.USERSTYLE_COMMENTS_OPACITY) {userStyle += `.comment__message_downgrade {opacity: 1;}`;}if (FLAGS.USERSTYLE_REMOVE_SIDEBAR_RIGHT || FLAGS.PREVENT_CLOSING_DELETED_PAGES) {// 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 = /^\/(ru|en)\/post\/\d+\/$/.test(path);const isSandbox = /^\/(ru|en)\/sandbox\//.test(path);const isUserPosts = /^\/(ru|en)\/users\/[^/]+\/posts\//.test(path);const isUserComments = /^\/(ru|en)\/users\/[^/]+\/comments\//.test(path);const isUserFavorites = /^\/(ru|en)\/users\/[^/]+\/favorites\//.test(path);// const isUserSubscription = /^\/(ru|en)\/users\/[^/]+\/subscription\//.test(path);const isCompanyBlog = /^\/(ru|en)\/company\/[^/]+\/blog\//.test(path);const isCompanyBlog2 = /^\/(ru|en)\/company\/[^/]+\/(page\d+\/)?$/.test(path);const isCompanyBlogPost = /^\/(ru|en)\/company\/[^/]+\/blog\/[^/]+/.test(path);const isFeed = /^\/(ru|en)\/feed\//.test(path);const isHome = /^\/(ru|en)\/$/.test(path);const isTop = /^\/(ru|en)\/top\//.test(path);const isAll = /^\/(ru|en)\/all\//.test(path);const isNews = /^\/(ru|en)\/news\//.test(path);const isNewsT = /^\/(ru|en)\/news\/t\/\d+\/$/.test(path);if (FLAGS.USERSTYLE_REMOVE_SIDEBAR_RIGHT && (isPost || isSandbox ||isUserPosts || isUserComments || isUserFavorites ||isCompanyBlog || isCompanyBlog2 ||isFeed || isHome || isTop || isAll ||isNews || isNewsT)) {userStyle += `.sidebar_right,.sidebar {display: none;}.content_left {padding-right: 0;}.comment_plain {max-width: 860px;}`;}if (FLAGS.PREVENT_CLOSING_DELETED_PAGES && (isPost || isSandbox ||isCompanyBlogPost ||isNews || isNewsT)) {window.onbeforeunload = (e) => {const xhr = new XMLHttpRequest();xhr.open('HEAD', '', false);xhr.send();if (xhr.status < 200 || xhr.status >= 400) {e.preventDefault();const message = 'Статья уже удалена или недоступна';e.returnValue = message;return message;}return undefined;};}}if (FLAGS.USERSTYLE_REMOVE_BORDER_RADIUS_AVATARS) {userStyle += `.user-info__image-pic,.user-pic_popover,.media-obj__image-pic,.company-info__image-pic {border-radius: 0;}`;}if (FLAGS.USERSTYLE_USERINFO_BIG_AVATARS) {userStyle += `.page-header {height: auto;}.media-obj__image-pic_hub,.user-info__stats .media-obj__image-pic_user,/* .media-obj__image-pic_company, */.company-info__image-pic {width: auto;height: auto;}.page-header_tall .company-info__image-pic {width: 48px;height: 48px;}`;}if (FLAGS.COMMENTS_SORT) {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;}`;}if (FLAGS.USERSTYLE_COMMENTS_FIX) {userStyle += `.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:not(:empty) {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: -${FLAGS.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;}/* фикс, когда не добавляется класс &_plus или minus при user_vote_action */.voting-wjt__button[title="Вы проголосовали положительно"] {color: #7ba600;}.voting-wjt__button[title="Вы проголосовали отрицательно"] {color: #d53c30;}`;}if (FLAGS.USERSTYLE_COMMENTS_FIX && FLAGS.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;}.js-comment_parent:not(:hover) {color: #cd66cd !important;}`;}if (FLAGS.USERSTYLE_COMMENTS_FIX && FLAGS.REDUCE_NEW_LINES) {userStyle += `.comments_new-line .comments_new-line {margin-left: -${(FLAGS.LINE_LEN - 1) * 21}px !important;}`;}if (FLAGS.USERSTYLE_COMMENTS_IMG_MAXSIZE) {userStyle += `.comment__message img {max-height: ${FLAGS.USERSTYLE_COMMENTS_IMG_MAXSIZE}px;}.comment__message .spoiler .img {max-height: auto;}`;}if (FLAGS.USERSTYLE_CODE_FIX) {let addFont = '';if (FLAGS.USERSTYLE_CODE_FONT) {addFont = FLAGS.USERSTYLE_CODE_FONT;if (addFont.indexOf(' ') >= 0) {addFont = `"${addFont}"`;}addFont += ',';}const tabSize = FLAGS.USERSTYLE_CODE_TABSIZE || 4;userStyle += `.editor .text-holder textarea,.tm-editor__textarea,pre {font-family: ${addFont} 'Ubuntu Mono', Menlo, Monaco, Consolas, 'Lucida Console', 'Courier New', monospace;}code {font-family: ${addFont} 'Ubuntu Mono', 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 (FLAGS.USERSTYLE_SPOILER_BORDERS) {userStyle += `.spoiler .spoiler_text {border: 1px dashed rgb(12, 174, 251);}`;}if (FLAGS.USERSTYLE_STATIC_STICKY) {userStyle += `.wrapper-sticky,.js-ad_sticky,.js-ad_sticky_comments {position: static !important;}.sticky-spacer {display: none !important;}`;}if (FLAGS.GIF_STOP) {userStyle += `.habrafix_gif-stop:hover {outline: 4px solid #548eaa;outline-offset: -4px;}`;}if (FLAGS.USERSTYLE_HLJS_LANG) {let hover = '';if (FLAGS.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'],['brain####', 'Brain####'],['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'],['pgsql', 'PostgreSQL'],['plaintext', 'просто текст'], // на будущее['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('');}if (FLAGS.USERSTYLE_CODE_NIGHT) {userStyle += `.night_mode_switcher {box-sizing: border-box;position: fixed;width: 32px;height: 32px;right: 32px;bottom: 32px;width: ${BUTTON_SIZE}px;height: ${BUTTON_SIZE}px;right: ${BUTTON_SIZE}px;bottom: ${BUTTON_SIZE}px;z-index: 10000;background-color: transparent;border-radius: 50%;border: 4px solid #aaa;border-right-width: ${BUTTON_SIZE / 2}px;transition: border-color 0.1s ease-out;}.night_mode_switcher:hover {border-color: #333;}.night .night_mode_switcher {border-color: #515151;}.night .night_mode_switcher:hover {border-color: #9e9e9e;}.night ::-webkit-scrollbar,.night ::-webkit-scrollbar-corner,.night ::-webkit-scrollbar-track-piece {background-color: #000;}.night ::-webkit-scrollbar-thumb {background-color: #22272b;border: 1px solid #000;}.night ::-webkit-scrollbar-thumb:hover {background-color: #2C3237;}.night {scrollbar-color: dark;scrollbar-face-color: #22272b;scrollbar-track-color: #000;scrollbar-color: #22272b #000;}/* bg */.night .sidebar-block__suggest,.night .dropdown-container,.night .poll-r###lt__bar,.night .comments_new-line,.night .tm-editor__textarea,.night .layout,.night .toggle-menu__most-read,.night .toggle-menu_most-comments,.night .partner-info {background: #171c20;}/* text */.night .companies-rating__name:not(:hover),.night .profile-section__title,.night .profile-section__about-text,.night .profile-section__invited,.night .sidebar-block__suggest,.night .user-message__body,.night .promo-block__title_total,.night .beta-anounce__text,.night .defination-list__label,.night .defination-list__value,.night .search-field__select,.night .search-field__input[type="text"],.night .search-form__field,.night .post-info__title:not(:hover),.night .dropdown__user-stats,.night .dropdown-container_white .user-info__special,.night .n-dropdown-menu__item-link,.night body,.night .default-block__polling-title,.night .poll-r###lt__data-label,.night code,.night .user-info__fullname,.night .user-info__specialization,.night .page-header__info-title,.night .page-header__info-desc,.night .post__title-text,.night .post__title_link:not(:visited),.night .checkbox__label,.night .radio__label,.night .tm-editor__textarea,.night .footer-block__title,.night #TMpanel .container .bmenu > a.current,.night .post__text-html,.night .comment__message,.night .comment-form__preview,.night .post-share__title,.night .post-donate__title,.night .news-block__title,.night .news-topic__title:not(:visited),.night .partner-info__title,.night .partner-info__description {color: #9e9e9e;}/* non important text */.night .icon-svg_bookmark,.night .icon-svg_views-count,.night .icon-svg_post-comments,.night .icon-svg_edit,.night .icon-svg_recommend,.night .icon-svg_report,.night .voting-wjt__button:not(.voting-wjt__button_plus):not(.voting-wjt__button_minus),.night .icon_comment-edit,.night .icon_comment-anchor,.night .icon_comment-bookmark,.night .icon_comment-branch,.night .icon_comment-arrow-up,.night .icon_comment-arrow-down,.night .layout__elevator {color: #515151;}.night .voting-wjt__button:not(.voting-wjt__button_plus):not(.voting-wjt__button_minus):hover,.night .icon_comment-anchor:hover,.night .icon_comment-bookmark:hover,.night .icon_comment-branch:hover,.night .icon_comment-arrow-up:hover,.night .icon_comment-arrow-down:hover {color: #548eaa;}.night .n-dropdown-menu__item-link:hover {color: white;}/* top lvl bg */.night .h-popover,.night .profile-section__user-hub:not(.profile-section__user-hub_cross),.night a.sort-panel__item-toggler.active,.night .checkbox__label::before,.night .radio__label::before,.night .content-list__item_conversation:hover,.night .search-field__select,.night .search-field__input[type="text"],.night .search-form__field,.night .dropdown-container,.night .n-dropdown-menu,.night .post__translatation,.night code,.night .megapost-teasers,.night .tm-editor_comments,.night .promo-block__header,.night .post__text-html blockquote,.night .default-block,.night .post-share,.night .post-donate,.night .company-info__author,.night .layout__row_footer-links {background: #22272B;}/* not important bg */.night .profile-section__user-hub:not(.profile-section__user-hub_cross):hover,.night .btn_blue.disabled,.night .btn_blue[disabled],.night .tracker_page table.tracker_folowers tr.new,.night .dropdown__user-stats,.night .comment__head_topic-author,.night .promo-item:hover,.night .layout__row_navbar,.night .layout__row_footer,.night #TMpanel,.night .n-dropdown-menu__item-link_flow:hover,.night #tracker-page .tracker-table__row.new {background: #1f2327;}/* borders */.night #tracker-page .tracker-table__header,.night #tracker-page .tracker-table__cell,.night .h-popover,.night .h-popover__stats,.night .default-block__footer,.night .toggle-menu__item-link_bordered,.night .default-block_promote,.night .sort-panel,.night .n-dropdown-menu_flows,.night .for_users_only_msg,.night #comments-list .js-form_placeholder,.night .sidebar-block__suggest,.night .content-list_preview-message,.night .btn_outline_blue[disabled],.night .user-message__body_html pre code,.night .content-list_user-dialog,.night .wysiwyg-toolbar,.night .content-list__item_bordered,.night .promo-block__total,.night .search-field__select,.night .search-field__input[type="text"],.night .search-form__field,.night .tracker_page table.tracker_folowers tr td,.night .tracker_page table.tracker_folowers tr th,.night .stacked-menu__item_devided,.night .post__text-html table,.night .post__text-html table td,.night .post__text-html table th,.night .n-dropdown-menu__item_border,.night .dropdown-container,.night .default-block_bordered,.night .default_block_polling,.night .column-wrapper_tabs .sidebar_right,.night .post__type-label,.night .promo-block__header,.night .user-info__contacts,.night .comment__message pre code,.night .comment-form__preview pre code,.night .sandbox-panel,.night .comment__post-title,.night .tm-editor__textarea,.night .promo-block__footer,.night .author-panel,.night .promo-block,.night .post__text-html pre code,.night .footer-block__title,.night #TMpanel,.night .layout__row_navbar,.night .page-header_bordered,.night .post-stats,.night .company-info__about,.night .company-info_post-additional,.night .company-info__contacts,.night .post-share,.night .post-donate,.night .content-list__item_devided,.night .comments_order,.night .comments-section__head,.night .content-list_nested-comments,.night .default-block__header,.night .column-wrapper_bordered,.night .tabs-menu,.night .toggle-menu,.night .news-block__header,.night .news-block__footer {border-color: #393d41;}.night .rating-info__progress,.night .poll-r###lt__progress {background-color: #515151;}.night .poll-r###lt__progress_winner {background-color: #5e8eac;}.night .layout__elevator:hover {background-color: #22272B;}.night .comment__head_topic-author {background: #003030;}.night .comment__head_my-comment {background: #003000;}.night .comment__head_new-comment {background: black}.night .user-info__nickname_comment,.night .icon-svg_logo-habrahabr {color: inherit;}.night [disabled] {opacity: 0.5}.night .content-list_comments .comment__folding-dotholder::before,.night .comment.is_selected::after {filter: invert(0.9);}/* img filter */.night .comment__message img,.night .comment-form__preview img,.night .default-block__content #facebook_like_box,.night .default-block__content #vk_groups,.night .post img,.night .page-header__banner img,.night .company_top_banner img,.night img .teaser__image,.night .teaser__image-pic,.night .article__body img {filter: brightness(0.5);transition: filter .6s ease-out;}.night .comment__message img:hover,.night .comment-form__preview img:hover,.night .default-block__content #facebook_like_box:hover,.night .default-block__content #vk_groups:hover,.night img[alt="en"],.night img[alt="habr"],.night img:hover,.night a.post-author__link img,.night img.user-info__image-pic,.night .teaser__image-pic:hover,.night .teaser__image:hover {filter: none;}/* Atelier Cave Dark */.night .hljs-comment,.night .hljs-quote {color:#7e7887 !important}.night .hljs-variable,.night .hljs-template-variable,.night .hljs-attribute,.night .hljs-regexp,.night .hljs-link,.night .hljs-tag,.night .hljs-name,.night .hljs-selector-id,.night .hljs-selector-class {color:#be4678 !important}.night .hljs-number,.night .hljs-meta,.night .hljs-built_in,.night .hljs-builtin-name,.night .hljs-literal,.night .hljs-type,.night .hljs-params {color:#aa573c !important}.night .hljs-string,.night .hljs-symbol,.night .hljs-bullet {color:#2a9292 !important}.night .hljs-title,.night .hljs-section {color:#576ddb !important}.night .hljs-keyword,.night .hljs-selector-tag {color:#955ae7 !important}.night .hljs-deletion,.night .hljs-addition {color:#19171c !important;display:inline-block !important;width:100% !important}.night .hljs-deletion {background-color:#be4678 !important}.night .hljs-addition {background-color:#2a9292 !important}.night .hljs {display:block !important;overflow-x:auto !important;background:#19171c !important;color:#8b8792 !important;/*padding:0.5em !important*/}.night .hljs-emphasis {font-style:italic !important}.night .hljs-strong {font-weight:bold !important}`;}if (FLAGS.USERSTYLE_CONFIG_INTERFACE) {userStyle += `.config_button {box-sizing: border-box;position: fixed;width: ${BUTTON_SIZE}px;height: ${BUTTON_SIZE2}px;right: ${BUTTON_SIZE}px;bottom: ${FLAGS.NIGHT_MODE ? BUTTON_SIZE4 : BUTTON_SIZE}px;z-index: 10000;background: -webkit-linear-gradient(top, #aaa 50%, transparent 50%);background: -moz-linear-gradient(top, #aaa 50%, transparent 50%);background: -moz-linear-gradient(top, #aaa 50%, transparent 50%);background-size: 10px 10px;transition: background 0.1s ease-out;}.config_button:hover {background: -webkit-linear-gradient(top, #333 50%, transparent 50%);background: -moz-linear-gradient(top, #333 50%, transparent 50%);background: -moz-linear-gradient(top, #333 50%, transparent 50%);background-size: 10px 10px;}.night .config_button {background: -webkit-linear-gradient(top, #515151 50%, transparent 50%);background: -moz-linear-gradient(top, #515151 50%, transparent 50%);background: -moz-linear-gradient(top, #515151 50%, transparent 50%);background-size: 10px 10px;}.night .config_button:hover {background: -webkit-linear-gradient(top, #9e9e9e 50%, transparent 50%);background: -moz-linear-gradient(top, #9e9e9e 50%, transparent 50%);background: -moz-linear-gradient(top, #9e9e9e 50%, transparent 50%);background-size: 10px 10px;}.config_frame {box-sizing: border-box;position: fixed;right: 80px;bottom: 32px;z-index: 10000;border: 1px solid #aaa;padding: 8px;background: #f7f7f7;-webkit-user-select: none;-moz-user-select: none;-ms-user-select: none;user-select: none;overflow-y: auto;max-height: calc(100vh - 64px);min-width: 390px;}.config_frame label:hover {cursor: pointer;background: rgba(128, 128, 128, 0.3);}.config_frame input {cursor: pointer;position: absolute;opacity: 0;}.config_frame input + span:before {content: '';display: inline-block;width: 0.5em;height: 0.5em;margin: 0 0.4em 0.1em 0.3em;outline: 1px solid currentcolor;outline-offset: 1px;}.config_frame input:checked + span:before {background: currentcolor;}.night .config_frame {background: #22272B;border-color: #393d41;}`;}userStyleEl.innerHTML = userStyle;const navigatorEdge = /Edge/.test(navigator.userAgent);function readyHead(fn) {if (document.body) { // если есть body, значит head готовfn();} else if (document.documentElement && !navigatorEdge) {const observer = new MutationObserver(() => {if (document.body) {observer.disconnect();fn();}});observer.observe(document.documentElement, { childList: true });} else {// рекурсивное ожидание появления DOMsetTimeout(() => readyHead(fn), 16);}}readyHead(() => {if (document.getElementById('habrafixmarker')) return;if (FLAGS.USERSTYLE) document.head.appendChild(userStyleEl);if (FLAGS.NIGHT_MODE && userConfig.getItem('night_mode')) {document.documentElement.classList.add('night');}});function ready(fn) {const { readyState } = document;if (readyState === 'loading') {document.addEventListener('DOMContentLoaded', () => {fn();});} else {fn();}}ready(() => {if (document.getElementById('habrafixmarker')) return;if (FLAGS.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);});}}if (FLAGS.HTSO_BTN) {const commentForm = document.getElementById('comment-form');if (commentForm) {const toolbar = commentForm.querySelector('.tm-editor__toolbar');if (toolbar) {const item = document.createElement('li');item.classList.add('wysiwyg-toolbar__item');item.innerHTML = `<button type="button" class="btn btn_wysiwyg" tabindex="0" title="Загрузка картинок"onclick="window.open('//hsto.org', '_blank').focus();"><svg class="icon-svg icon-svg_spoiler_wysiwyg" aria-hidden="true" aria-labelledby="title"version="1.1" role="img" width="24" viewBox="0 0 100 82.9"><pathd="M77.9,82.9H5.6c-3.4,0-5.6-2.4-5.6-5.2V21.9c0-3.5,2.1-6.1,5.6-6.1H50v11.1H11v45h61V54.8l12,0v22.9 C84,80.5,81.2,82.9,77.9,82.9L77.9,82.9z"></path><polygon points="16.5,66.9 39.8,44.6 50.2,54.4 61.5,39.6 67,50.2 67,66.9 "></polygon><pathd="M28,44.4c-3.2,0-5.7-2.6-5.7-5.7c0-3.2,2.6-5.8,5.7-5.8c3.2,0,5.8,2.6,5.8,5.8C33.8,41.9,31.2,44.4,28,44.4 L28,44.4z"></path><polygon points="84,21.9 84,44 72,44 72,21.9 56.1,21.9 78.1,0 100,21.9 "></polygon></svg></button>`;toolbar.appendChild(item);}}}if (FLAGS.SUBS_BTN) {const userBtn = document.querySelector('.tabs-menu__item_link');const isUserPage = /^\/(ru|en)\/users\/[^/]+\//.test(window.location.pathname);if (userBtn && isUserPage) {const bar = userBtn.parentElement;const tab = document.createElement('a');const isSubs = /subscription\/$/.test(window.location.pathname);tab.classList.add('tabs-menu__item', 'tabs-menu__item_link');tab.href = `${userBtn.href}subscription/`;tab.innerHTML = `<h3 class="tabs-menu__item-text ${isSubs ? 'tabs-menu__item-text_active' : ''}">Он читает</h3>`;bar.appendChild(tab);const tab2 = document.createElement('a');const isFols = /followers\/$/.test(window.location.pathname);tab2.classList.add('tabs-menu__item', 'tabs-menu__item_link');tab2.href = `${userBtn.href}subscription/followers/`;tab2.innerHTML = `<h3 class="tabs-menu__item-text ${isFols ? 'tabs-menu__item-text_active' : ''}">Его читают</h3>`;bar.appendChild(tab2);}}// надо ли ещёArray.from(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='${FLAGS.GIF_STOP_COLOR_BG}'/><circle cx='${cx}' cy='${cy}' r='${r}' fill='${FLAGS.GIF_STOP_COLOR_FG}'/><polygon points='${cx + ax} ${cy} ${cx - bx} ${cy - by} ${cx - bx} ${cy + by}' fill='${FLAGS.GIF_STOP_COLOR_BG}' /></svg>`;el.dataset.oldSrc = el.getAttribute('src'); // eslint-disable-line no-param-reassignel.setAttribute('src', svg);} else if (FLAGS.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='${FLAGS.GIF_STOP_COLOR_BG}'/><circle cx='${cx - r}' cy='${cy}' r='${r2}' fill='${FLAGS.GIF_STOP_COLOR_FG}'/><circle cx='${cx}' cy='${cy}' r='${r2}' fill='${FLAGS.GIF_STOP_COLOR_FG}'/><circle cx='${cx + r}' cy='${cy}' r='${r2}' fill='${FLAGS.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-reassignel.parentNode.removeChild(el);}}if (FLAGS.GIF_STOP) {Array.from(document.querySelectorAll('.post__text img[src$=".gif"], .comment__message img[src$=".gif"]')).filter((el) => {const excludes = ['https://habrastorage.org/storage3/976/d3e/38a/976d3e38a34b003f86f91795524af9f8.gif','https://habrastorage.org/storage3/2e2/522/737/2e2522737ec404a9f76047e108dfaea0.gif','https://habrastorage.org/getpro/habr/post_images/d4b/289/ef0/d4b289ef0a00e969108c25d0c3d75f58.gif',];return !excludes.includes(el.getAttribute('src'));}).forEach((el) => {if (FLAGS.GIF_STOP_ONLOAD) toggleGIF(el);el.classList.add('habrafix_gif-stop');el.onclick = () => toggleGIF(el); // eslint-disable-line no-param-reassign});}// счетчики кармыif (FLAGS.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-reassignkarmaEl.style.minWidth = `${KARMA_WIDTH}px`; // eslint-disable-line no-param-reassign});Array.from(document.querySelectorAll(`.stacked-counter[href="https://habr.com/ru/info/help/karma/"],.stacked-counter[href="https://habr.com/en/info/help/karma/"]`)).forEach((counterEl) => {let total = parseInt(counterEl.title, 10);const scoreEl = counterEl.querySelector('.stacked-counter__value');if (!scoreEl || !total) return;counterEl.style.width = 'auto'; // eslint-disable-line no-param-reassigncounterEl.style.minWidth = `${KARMA_WIDTH}px`; // eslint-disable-line no-param-reassignconst 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 = `&nbsp;= ${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);counterEl.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;this.parentEl = el.parentNode;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 newChild = this.parentEl.querySelector('.voting-wjt__counter, .post-stats__r###lt-counter');if (!newChild) return;this.el = newChild;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.parentEl, { childList: true });}static parse(el) {let [, likes, dislikes] = el.attributes.title.textContent.match(/[0-9]+/g).map(Number);let total = likes + dislikes;let [, sign, rating] = el.innerHTML.match(/([–]?)(\d+)/); // eslint-disable-line prefer-constrating = 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 (FLAGS.RATING_DETAILS_PN) {const percent = Math.round((100 * this.likes) / this.total);details = `&nbsp;= ${this.total} × (${percent} − ${100 - percent})%`;} else {details = `&nbsp;= ${this.likes} − ${this.dislikes}`;}innerHTML += ` <span style='color: #545454; font-weight: normal'>${details}</span>`;}this.el.innerHTML = innerHTML;}}// парсим ихArray.from(document.querySelectorAll('.voting-wjt__counter, .post-stats__r###lt-counter')).forEach((el) => {scoresMap.set(el, new Score(el));});// добавляем подробностейif (FLAGS.RATING_DETAILS) {if (FLAGS.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-reassignconst nowDetailed = userConfig.shiftItem('scores_details');scoresMap.forEach(s => s.setDetails(nowDetailed));};});} else {scoresMap.forEach(score => score.setDetails(true));}}// метки времени и работа с нимиconst pageLoadTime = new Date();const monthNames = ['января', 'февраля', 'марта','апреля', 'мая', 'июня','июля', 'августа', 'сентября','октября', 'ноября', 'декабря',];const monthNamesEng = ['January', 'February', 'March','April', 'May', 'June','July', 'August', 'September','October', 'November', 'December',];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') || el.getAttribute('data-time_published');if (imagination) return imagination;let recently;let day;let month;let year;let time;let isEng = false;let meridiem;if (/^\d\d:\d\d$/.test(el.innerHTML)) {[, time] = el.innerHTML.match(/(\d\d:\d\d)/);recently = 'сегодня';} else if (/^\d\d:\d\d (AM|PM)$/.test(el.innerHTML)) {[, time, meridiem] = el.innerHTML.match(/(\d\d:\d\d) (AM|PM)/);recently = 'сегодня';} else if (/at/.test(el.innerHTML)) {isEng = true;const re = /((today|yesterday)|([A-z]+) (\d+), (\d+)) at (\d\d:\d\d) (AM|PM)/;[,,recently,month, day, year,time,meridiem,] = el.innerHTML.match(re);if (recently === 'today') {recently = 'сегодня';} else if (recently === 'yesterday') {recently = 'вчера';}} else {const re = /((сегодня|вчера)|(\d+)[ .]([а-я]+|\d+)[ .]?(\d+)?) в (\d\d:\d\d)/;[,,recently,day, month, year,time,] = el.innerHTML.match(re);}const [, h, m] = time.match(/(\d\d):(\d\d)/);if (meridiem === 'PM') {time = `${Number(h === '12' ? 0 : h) + 12}:${m}`;} else if (meridiem === 'AM') {time = `${h === '12' ? '00' : h}:${m}`;}// и местное время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 = (isEng ? monthNamesEng : 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 r###lt = '';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) {r###lt = `сегодня в ${hhmm}`;} else if (isYesterday) {r###lt = `вчера в ${hhmm}`;} else if (nowYear === year) {r###lt = `${day} ${monthName} в ${hhmm}`;} else {r###lt = `${day} ${monthName} ${year} в ${hhmm}`;}return r###lt;}static relative(milliseconds) {let r###lt = '';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) {r###lt = 'несколько секунд';} else {const value = Math.floor([years, months, days, hours, minutes][idx]);const forms = formats[idx];const form = pluralForm(value, forms);r###lt = `${value} ${form}`;}return r###lt;}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)}`;}static datetimeToMsk(datetime) {const [, yyyy, mm, dd, h, m] = datetime.match(/([0-9]+)-([0-9]+)-([0-9]+)T([0-9]+):([0-9]+)/);return `${Number(dd)} ${monthNames[mm - 1]} ${yyyy} в ${h}:${m}`;}}// собираем метки времениconst datesMap = new Map();const megapostTimeEl = document.querySelector('.megapost-head__meta > .list_inline > .list__item');(megapostTimeEl ? [megapostTimeEl] : []).concat(Array.from(document.querySelectorAll(`.post__time,.preview-data__time-published,time.comment__date-time_published,.tm-post__date,.user-message__date-time,.news-topic__attr_date-time`))).forEach((el) => {datesMap.set(el, new HabraTime(el));});function updateTime(el) {datesMap.forEach((habraTime) => {if (!habraTime.el ||!document.body.contains(habraTime.el) ||(el && el !== habraTime.el)) return;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-reassignhabraTime.el.setAttribute('title', title);});}if (FLAGS.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-reassignif (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;});}if (FLAGS.FIND_COMMENTS) {const commentsList = document.querySelector('.user_comments');const match = document.location.pathname.match(/users\/([^/]+)\/comments/i);if (match && commentsList) {const nickname = match[1];const originalTitle = document.title;const searchForm = document.createElement('div');searchForm.classList.add('search-form', 'search-form_expanded');searchForm.style.width = 'auto';searchForm.innerHTML = `<span class="search-field__icon icon-svg_search" style="left: 0;"><svg class="icon-svg" width="32" height="32"viewBox="0 0 32 32" aria-hidden="true" version="1.1" role="img"><path d="M21.416 13.21c0 4.6-3.65 8.34-8.148.34S5.11 17.81 5.11 13.21c0-4.632 3.65-8.373 8.167-8.373 4.488 0 8.14 3.772 8.14 8.372zm1.9457.083c1.407-2.055 2.155-4.57 2.155-7.084C25.515 6.277 20.04.665 13.277.665S1.04 6.278 1.04 13.21c0 6.93 5.47512.542 12.237 12.542 2.454 0 4.907-.797 6.942-2.208l7.6 7.79 3.14-3.22-7.6-7.82z"></path></svg></span><span class="search-field__icon icon-svg_loading" style="left: 0;"><svg class="icon-svg" width="40" height="40"viewBox="0 0 100 100" enable-background="new 0 0 0 0"><circle cx="50" cy="50" fill="none" stroke="#333333"stroke-width="4" r="20" stroke-dasharray="94.24777960769379 33.41592653589793"transform="rotate(88.5132 50 50)"><animateTransform attributeName="transform" type="rotate" calcMode="linear"values="0 50 50;360 50 50" keyTimes="0;1" dur="1s" begin="0s" repeatCount="indefinite"></animateTransform></circle></svg></span><labelid="comments_search_label"class="search-form__field-wrapper"style="background: linear-gradient(to right, rgba(84, 142, 170, 0.2) 0%, transparent 0%)"><input type="text" class="search-form__field" id="search-comments-field" placeholder="Поиск по тексту комментариев"style="position: absolute;background-color: transparent;"><button type="button" class="btn btn_search-close" id="search-comments-clear" title="Очистить"><svg class="icon-svg icon-svg_navbar-close-search" width="31" height="32" viewBox="0 0 31 32" aria-hidden="true" version="1.1" role="img"><path d="M26.67 0L15.217 11.448 3.77 0 0 3.77l11.447 11.45L0 26.666l3.773.77L15.218 18.99l11.45 11.448 3.772-3.77-11.448-11.45L30.44 3.772z"></path></svg></button></label>`;const notFoundLabel = document.createElement('p');notFoundLabel.style.textAlign = 'center';notFoundLabel.style.fontSize = '18px';notFoundLabel.style.color = 'gray';notFoundLabel.style.display = 'none';notFoundLabel.textContent = 'Ничего не найдено';const commentsSubList = document.createElement('ul');commentsSubList.classList.add('content-list', 'content-list_comments');commentsSubList.id = 'search-comments';commentsSubList.style.display = 'none';commentsList.insertBefore(commentsSubList, commentsList.firstChild);commentsList.insertBefore(notFoundLabel, commentsList.firstChild);commentsList.insertBefore(searchForm, commentsList.firstChild);const Progress = {set(value) {if (value === 1) {document.title = 'Поиск завершён';this.setProgressBar(0);} else {const percent = Math.round(value * 100);document.title = `${percent}%, идёт поиск`;this.setProgressBar(value);}},setProgressBar(value) {const percent = value * 100;document.getElementById('comments_search_label').style.background = `linear-gradient(to right, rgba(84, 142, 170, 0.2) ${percent}%, transparent ${percent}%)`;},reset() {this.setProgressBar(0);document.title = originalTitle;},};const makeComment = (comment) => {const commentEl = document.createElement('li');commentEl.classList.add('content-list__item', 'content-list__item_comment', 'content-list__item_comment-plain');let ratingClass = '';let rating = '0';if (comment.score > 0) {rating = `+${comment.score}`;ratingClass = 'voting-wjt__counter_positive';} else if (comment.score < 0) {rating = `–${Math.abs(comment.score)}`;ratingClass = 'voting-wjt__counter_negative';}let avatar;if (comment.avatar === 'https://habr.com/images/avatars/stub-user-middle.gif') {avatar = `<svg class="default-image default-image_mini default-image_green" width="24"height="24"><use xlink:href="https://habr.com/images/1558430991/common-svg-sprite.svg#slug"></use></svg>`;} else {avatar = `<img src="${comment.avatar}" class="user-info__image-pic user-info__image-pic_small" width="24" height="24">`;}commentEl.innerHTML = `<div class="comment__post-title"><a href="${comment.post.url}" class="comment__post-link">${comment.post.title}</a><div class="comment__post-footer"><a href="${comment.post.url}#comments"><svg class="icon-svg_comments icon-svg_comments-plain" width="14" height="13"><use xlink:href="https://habr.com/images/1556525186/common-svg-sprite.svg#comment"></use></svg><span class="comment__post-comments-counter">${comment.post.comments_count}</span></a></div></div><div class="comment comment_plain" rel="${comment.id}" id="habrafix_comment_${comment.id}"><div class="comment__head"><a href="https://habr.com/ru/users/${comment.author.login}/"class="user-info user-info_inline" rel="user-popover" data-user-login="${comment.author.login}">${avatar}<span class="user-info__nickname user-info__nickname_small user-info__nickname_comment">${comment.author.login}</span></a><svg class="icon_comment-edit" title="Комментарий был изменен"style="display: ${comment.time_changed !== '0' ? 'block' : 'none'}" width="12"height="12"><use xlink:href="https://habr.com/images/1558430991/common-svg-sprite.svg#pencil"></use></svg><time class="comment__date-time comment__date-time_published"datetime="${comment.time_published}">${comment.time_published}</time><ul class="inline-list inline-list_comment-nav"><li class="inline-list__item inline-list__item_comment-nav"><a href="${comment.post.url}#comment_${comment.id}" class="icon_comment-anchor"title="Ссылка на комментарий"><svg width="12" height="12"><use xlink:href="https://habr.com/images/1556525186/common-svg-sprite.svg#anchor"></use></svg></a></li><li class="inline-list__item inline-list__item_comment-nav"><a href="#" class="icon_comment-bookmark " onclick="comments_add_to_favorite(this)" data-type="3"data-id="${comment.id}" data-action="add" title="Добавить в закладки"><svg width="12" height="12"><use xlink:href="https://habr.com/images/1556525186/common-svg-sprite.svg#book"></use></svg></a></li></ul><div class="voting-wjt voting-wjt_comments js-comment-vote"><span class="voting-wjt__counter ${ratingClass}js-score" title="Общий рейтинг ${rating}">${rating}</span></div></div><div class="comment__message">${comment.message}</div></div>`;return commentEl;};const addComment = (comment) => {document.getElementById('search-comments').appendChild(makeComment(comment));const timeEl = document.querySelector(`#habrafix_comment_${comment.id} time`);if (FLAGS.TIME_DETAILS) {timeEl.setAttribute('style','cursor: pointer; -moz-user-select: none; -webkit-user-select: none; -ms-user-select: none; user-select: none;',);timeEl.onclick = () => { // eslint-disable-line no-param-reassignuserConfig.shiftItem('time_publications');updateTime();};datesMap.set(timeEl, new HabraTime(timeEl));updateTime(timeEl);} else {timeEl.textContent = HabraTime.datetimeToMsk(comment.time_published);}};// let fetchCors = url => fetch(`https://cors.io/?${url}`);let fetchCors = url => fetch(url);if (typeof GM !== 'undefined' && GM.xmlHttpRequest) {fetchCors = url => new Promise((resolve, reject) => GM.xmlHttpRequest({method: 'GET',url,onload(response) {resolve({json: () => Promise.resolve(JSON.parse(response.responseText)),});},onerror() { reject(); },ontimeout() { reject(); },}));}const loadComments = async (page) => {const url = `https://m.habr.com/kek/v1/users/${nickname}/comments?comments=true&user=${nickname}&page=${page}`;const res = await fetchCors(url);const json = await res.json();return json.data;};const sleep = ms => new Promise(resolve => setTimeout(resolve, ms));const pageCache = {};const memoizedLoadComments = async (page) => {if (pageCache[page]) {await sleep(0);return pageCache[page];}const r###lt = await loadComments(page);pageCache[page] = r###lt;return r###lt;};let latestSearchId = 0;const removeR###ltsList = () => {document.getElementById('search-comments-field').value = '';latestSearchId += 1;document.getElementById('search-comments').innerHTML = '';document.getElementById('search-comments').style.display = 'none';document.getElementById('comments').style.display = '';const footer = document.querySelector('.page__footer');if (footer) footer.style.display = '';Progress.reset();notFoundLabel.style.display = 'none';searchForm.classList.remove('loading');};const addR###ltsList = () => {document.getElementById('search-comments').innerHTML = '';document.getElementById('search-comments').style.display = '';document.getElementById('comments').style.display = 'none';const footer = document.querySelector('.page__footer');if (footer) footer.style.display = 'none';Progress.set(0);notFoundLabel.style.display = 'none';searchForm.classList.add('loading');};document.getElementById('search-comments-clear').onclick = removeR###ltsList;// eslint-disable-next-line consistent-returnconst search = async () => {latestSearchId += 1;const currentSearchId = latestSearchId;const text = document.getElementById('search-comments-field').value;if (!text) return removeR###ltsList();addR###ltsList();const filter = (comment) => {const t = text.toLowerCase();const message = comment.message.toLowerCase();const title = comment.post.title.toLowerCase();return message.includes(t) || title.includes(t);};let pages = 1;for (let curPage = 1; curPage <= pages; curPage += 1) {let data;while (!data) {try {// eslint-disable-next-line no-await-in-loopdata = await memoizedLoadComments(curPage);} catch (e) {document.title = 'Ошибка сети, повтор...';// eslint-disable-next-line no-await-in-loopawait sleep(5000);}}// eslint-disable-next-line consistent-returnif (currentSearchId !== latestSearchId) return; // пользователь изменил текстpages = pages === 1 ? data.pages : pages;Progress.set(curPage / pages);data.comments.filter(filter).forEach(addComment);}if (commentsSubList.childNodes.length === 0) notFoundLabel.style.display = '';searchForm.classList.remove('loading');};const awaitEndOfInput = (func, ms) => {let timerId;return () => {clearTimeout(timerId);timerId = setTimeout(func, ms);};};document.getElementById('search-comments-field').oninput = awaitEndOfInput(search, 1000);}}// создаем дерево комментариев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();FLAGS.sortVariants = [['time', 'старые'],];if (FLAGS.COMMENTS_SORT_BY_FRESHNESS) FLAGS.sortVariants.push(['freshness', 'новые']);if (FLAGS.COMMENTS_SORT_BY_TREND) FLAGS.sortVariants.push(['trend', 'горячие']);if (FLAGS.COMMENTS_SORT_BY_QUALITY) FLAGS.sortVariants.push(['quality', 'хорошие']);if (FLAGS.COMMENTS_SORT_BY_REDDIT) FLAGS.sortVariants.push(['reddit', 'проверенные']);if (FLAGS.COMMENTS_SORT_BY_RATING) FLAGS.sortVariants.push(['rating', 'рейтинговые']);if (FLAGS.COMMENTS_SORT_BY_POPULARITY) FLAGS.sortVariants.push(['popularity', 'популярные']);if (FLAGS.COMMENTS_SORT_BY_RANDOM) FLAGS.sortVariants.push(['shuffle', 'случайные']);// здесь начинается сортировка комментариевconst commentsOrderEl = document.createElement('div');commentsOrderEl.classList.add('comments_order');commentsOrderEl.innerHTML = FLAGS.sortVariants.map(([type, text]) => {const underline = (type === 'time') ? '; text-decoration: underline' : '';return `<a data-order="${type}" style="cursor: pointer${underline}">${text}</a>`;}).join(', ');if (FLAGS.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;},reddit(a, b) {const wilsonScore = (ups, downs) => {const n = ups + downs;if (n === 0) return 0;const z = 1.281551565545;const p = ups / n;const left = p + ((1 / (2 * n)) * z * z);const right = z * Math.sqrt(((p * (1 - p)) / n) + ((z * z) / (4 * n * n)));const under = 1 + ((1 / n) * (z * z));return (left - right) / under;};const aScore = wilsonScore(a.votes.likes, a.votes.dislikes);const bScore = wilsonScore(b.votes.likes, b.votes.dislikes);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 (FLAGS.COMMENTS_SORT && FLAGS.COMMENTS_SORT_ONLOAD && userConfig.getItem('comments_order') !== 'time') {sortComments();}Array.from(commentsOrderEl.children).forEach((el) => {el.onclick = () => { // eslint-disable-line no-param-reassignuserConfig.setItem('comments_order', el.dataset.order);sortComments();};});// меняем ссылки ведущие к новым комментариям на ссылки к началу комментариевif (FLAGS.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);}}// сворачивание комментов// eslint-disable-next-line no-constant-conditionif (false) { // FLAGS.COMMENTS_HIDEconst 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 */};commentsTree.walkTree((subtree) => {// не пытаемся сворачивать кореньif (subtree.isRoot) return;// у похищенных нет футераconst footerEl = subtree.commentEl.querySelector('.comment__footer');if (footerEl === null) return;// создаём переключательconst switcher = document.createElement('a');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.cursor = 'pointer';switcher.style.marginLeft = '-5px';footerEl.insertBefore(switcher, footerEl.children[0]);subtree.switcherEl = switcher; // eslint-disable-line no-param-reassignswitcher.onclick = () => toggle(subtree);const isHideLvl = subtree.lvl === FLAGS.HIDE_LEVEL && FLAGS.HIDE_LEVEL_4;const isLineLvl = subtree.lvl % FLAGS.LINE_LEN === 0;if (isLineLvl) {subtree.elList.classList.add('comments_new-line');const lineNumber = subtree.lvl / FLAGS.LINE_LEN;subtree.elList.classList.add(`comments_new-line-${lineNumber % 4}`);}// при запуске не сворачиваем ветки с новыми комментами, и содержащие целевой idif ((isHideLvl || isLineLvl) && !subtree.existNew() &&!(commentHash && subtree.existId(commentHash))) {toggle(subtree);}});}if (FLAGS.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-reassignlegendEl.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);}if (FLAGS.NIGHT_MODE) {const switcherEl = document.createElement('div');switcherEl.classList.add('night_mode_switcher');switcherEl.onclick = () => {const isNightMode = userConfig.shiftItem('night_mode');document.documentElement.classList.toggle('night', isNightMode);};document.body.appendChild(switcherEl);setInterval(() => {const boolClass = document.documentElement.classList.contains('night');const isNightMode = userConfig.getItem('night_mode');if (boolClass !== isNightMode) {document.documentElement.classList.toggle('night', isNightMode);}}, 1000);}if (FLAGS.CONFIG_INTERFACE) {const configFrame = document.createElement('div');configOptions.forEach(([key, text]) => {if (typeof FLAGS[key] !== 'boolean') return;const inputEl = document.createElement('input');inputEl.type = 'checkbox';inputEl.value = key;inputEl.checked = FLAGS[key];const labelEl = document.createElement('label');labelEl.setAttribute('unselectable', 'on');labelEl.setAttribute('onselectstart', 'return false');const spanEl = document.createElement('span');spanEl.innerHTML = text;configFrame.appendChild(labelEl);labelEl.appendChild(inputEl);labelEl.appendChild(spanEl);inputEl.onchange = () => {FLAGS[key] = inputEl.checked;localStorage.setItem('habrafixFlags', JSON.stringify(FLAGS));};configFrame.appendChild(document.createElement('br'));});const reloadText = document.createElement('div');reloadText.style.textAlign = 'right';reloadText.innerHTML = `* чтобы увидеть изменения<a href="#" onclick="location.reload(); return false">обновите страницу</a>`;configFrame.appendChild(reloadText);configFrame.classList.add('config_frame');configFrame.style.display = 'none';document.body.appendChild(configFrame);const configButton = document.createElement('div');configButton.classList.add('config_button');document.body.appendChild(configButton);configButton.onclick = () => {if (configFrame.style.display) {configFrame.style.display = '';} else {configFrame.style.display = 'none';}};}setTimeout(() => {const marker = document.createElement('meta');marker.id = 'habrafixmarker';document.head.appendChild(marker);}, 300);});