NOTICE: By continued use of this site you understand and agree to the binding Terms of Service and Privacy Policy.
// ==UserScript== // @name Highlighting of any author comments on YouTube.com // @name:ru Подсветка комментариев любого автора на YouTube.com // @description Search and highlight comments of any author on YouTube.com. Doesn't break links. Works with Ajax. // @description:ru Поиск и подсветка комментариев любого автора на YouTube.com. Не ломает ссылки. Работает с Ajax. // @namespace http://tampermonkey.net/ // @author Professor Woland (P_Woland) // @developer Professor Woland (P_Woland) // @license MIT // @version 1.2 (beta) // @match http*://www.youtube.com/* // @grant none // ==/UserScript== (function() { 'use strict'; //Edit const color = 'GreenYellow' // Highlight color const percent = '200%' //Nickname size as a percentage const fontWeight = 900 //The most bold font possible (100-900) //Edit_End const searchAttribute = "youtube-comment-search-plugin::searched" const searchAttributeValue= "highlighted" const textInInputForm = 'Введи ник для выделения комментария...' const buttonText = 'HighLight' const txtFieldDisabledMsg = 'Ну, что нашёл, то выделил...' const msgInputNickNameDisabled = `Ну, что ты тыкаешь на него, почём зря?!` const msgInputNickName = ` Сначала ник автора введи, а потом уже тыкай своими шалавливыми ручонками, умник!` //Объявление глобальных переменных. Знаю, плохой тон. //Пережиток найденного мной кода. Не хотел заморачиваться и лопатить весь код. //В любом случае весь этот скрипт обёрнут в общую функцию, запускаемую обезьянкой, //и все переменные не выйдут за её пределы. let canvas, ctx, xc, yc, options, i, button // --- Несколько мини функций --- function highlightComment(element) { element.style.fontWeight = fontWeight element.style.backgroundColor = color } function highlightNickname(element) { element.style.backgroundColor = color element.style.fontSize = percent } function inputOnBlur(element) { if (element.value == '') { element.value = textInInputForm element.style.fontWeight = 'normal' } } function inputOnFocus(element) { if (element.value == textInInputForm) { element.value = '' element.style.fontWeight = 'bold' } } function chekInputFieldIsNotEmpty(inputValue) { if (inputValue == textInInputForm || inputValue == '') { alert(msgInputNickName) return false } if (inputValue == txtFieldDisabledMsg) { alert(msgInputNickNameDisabled) return false } // запускаем поиск и выделение комментов и почти одновременно рисуем прогресс-бар setTimeout (function () {findCommentsOfRandomNickName(inputValue)}, 100) draw() } // --- Несколько мини функций ---End--- // --- Find & highlights comments --- function findCommentsOfRandomNickName(findStr) { let comments = document.querySelectorAll('#main') for (const cmnt of comments) { if (cmnt.getAttribute(searchAttribute) == searchAttributeValue) {continue} let author = cmnt.querySelector("#author-text > span") if (author && author.innerHTML.includes(findStr)) { let content = cmnt.querySelector("#content-text") highlightComment(content) highlightNickname(author) cmnt.setAttribute(searchAttribute, searchAttributeValue) } } setTimeout(findCommentsOfRandomNickName, 500, findStr) } // --- Find & highlights comments ---End--- // --- Create Widget --- function createWidget() { let btnAndCanvas = document.createElement('div') btnAndCanvas.className = 'youtube-myWrap' let cnvs = document.createElement('canvas') cnvs.id = 'youtube-myCanvas' cnvs.width = '100' cnvs.height = '100' let btn = document.createElement('button') btn.type = 'button' btn.className = 'youtube-myButton' btn.innerText = buttonText btn.id = 'youtube-progress-bar-button' btn.addEventListener('click', function() {chekInputFieldIsNotEmpty(textArea.value); return false}) btnAndCanvas.append(cnvs) btnAndCanvas.append(btn) let textArea = document.createElement('input') textArea.id = 'youtube-find-nickname-text' textArea.value = textInInputForm textArea.className = 'youtube-my-input-field' textArea.addEventListener('focus', function() {inputOnFocus(this); return false}) textArea.addEventListener('blur', function() {inputOnBlur(this); return false}) textArea.addEventListener('keydown', function(e) {if (e.keyCode == 13) { chekInputFieldIsNotEmpty(textArea.value) }; return false }) let container = document.createElement('div') container.className = 'youtube-my-widget-conteiner' container.prepend(textArea) container.prepend(btnAndCanvas) return container } // --- Create Widget ---End--- // --- Create CSS Stylsheet --- function createStyleSheet() { let sheet = document.createElement('style') sheet.type = 'text/css' sheet.id = 'youtube-css-sheet-button' let sheetContent = ` .youtube-myCanvas { display : block; margin : 0 auto 10px; } .youtube-myWrap { padding-bottom : 10px; margin-right: 10px; display : inline-block; //background-color : rgba(240,240,240,0.5); border-style : groove; border-radius : 20px; } .youtube-myButton { width : 70%; height : 25px; display : block; font-weight : 500; font-size : 12px; //line-height : 30px; font-family : Roboto, sans-serif; color : #eee; text-align : center; margin : 0 auto; border : solid 1px #333; -webkit-border-radius : 3px; border-radius : 10px; outline : none; -webkit-user-select : none; user-select : none; background-color : #888; cursor : pointer; transition : all 0.3s; } .youtube-myButton : hover { border-color : #285e8e; background-color : #3276b1; } .youtube-my-input-field { color : #ccc background-color : #eee; display : inline-block; width : 400px; margin : 20px; margin-left : 0px; border-style : groove; border-radius : 10px; padding : 3px; border-color : #555; } .youtube-my-widget-conteiner { background-color : rgba(153,153,153,0.5); display : inline; padding : 15px; border-style: groove; border-radius : 20px; } .disable, .disable : hover { border : solid 1px #357ebd; background-color : #428bca; opacity : 0.4; cursor : default; } ` sheet.innerHTML = sheetContent document.body.prepend(sheet) } // --- Create CSS Stylsheet ---End--- // --- Progress Bar Functions --- function getRadians(degree) { // переводим градусы в радианы return Math.PI / 180 * degree } function init() { // длительность отрисовки одного сектора options.duration = 200 // массив со значениями цвета начала и конца градиента секторов options.colors = ['#f00', '#ff2f00', '#ff7e00', '#ffde00', '#dffc00', '#7ae000', '#2cbb00', '#15b200'] // шаг отрисовки цветов (размер сектора) в радианах options.step = getRadians(45) // получаем угол начала прогресс бара в радианах options.start = getRadians(112.5) // ширина прогресс бара в px options.width = 30 // радиус прогресс бара в px options.r = xc - options.width // очищаем canvas ctx.clearRect(0, 0, canvas.width, canvas.height) // рисуем подложку без анимации drawSector('#eee', options.width) } function draw() { // получаем из массива пару цветов, которая будет использоваться // для создания градиента i-го сектора прогресс бара let startColor = options.colors[i], endColor = options.colors[i + 1] // получаем координаты X, Y точек начала и конца i-го сектора прогресс бара let x0 = xc + Math.cos(options.start) * options.r, y0 = yc + Math.sin(options.start) * options.r, x1 = xc + Math.cos(options.start + options.step) * options.r, y1 = yc + Math.sin(options.start + options.step) * options.r // используя метод createLinearGradient, создаём объект линейного градиента, // в качестве аргументов метод принимает значения координат начала и конца // сектора, к которому он будет применён let gradient = ctx.createLinearGradient(x0, y0, x1, y1) // используя метод addColorStop определяем цвет // в начале объекта градиента gradient.addColorStop(0, startColor) // в конце объекта градиента gradient.addColorStop(1.0, endColor) // старт анимации отрисовки одного сектора let start = new Date().getTime() let fn = function() { // время прошедшее от начала отрисовки сектора let now = new Date().getTime() - start // если текущее время превысило время анимации, присваиваем ему значение // времени анимации, в противном случае, сектор может получиться // большего размера, чем планировалось now = (now < options.duration) ? now : options.duration // на сколько должен быть отрисован текущий сектор let inc = options.step * now / options.duration // предварительно закрашиваем текущий сектор белым цветом на угол равный inc // толщину берём на 2px больше, чтобы закрасить возможные артефакты drawSector('#fff', options.width + 2, inc) // закрашиваем текущий сектор градиентом на угол равный inc drawSector(gradient, options.width, inc) // закрашиваем стыки секторов drawLine(i) // выводим проценты заполнения прогресс бара showPercents(i, inc) // если текущее время меньше времени анимации, продолжаем // рисование текущего сектора if (now < options.duration) { requestAnimationFrame(fn) } else { // увеличиваем индекс на единицу, чтобы выбрать из массива цветов следующую пару i++ // все сектора отрисованы, заканчиваем работу функции if (i >= options.colors.length - 1) { // делаем кнопку запуска прогресс бара неактивно //Так же делаем неактивным поле ввода// button.classList.add('disable') button.setAttribute('disabled', 'disabled') let txtField = document.getElementById('youtube-find-nickname-text') txtField.value = txtFieldDisabledMsg txtField.setAttribute('readonly', 'readonly') // удаляем зарегистрированный обработчик события //Не удаляем, так как я переписал логику добавления обработчика к кнопке// //button.removeEventListener('click', draw) // выходим из функции рисования прогресс бара return } // угол, с которого начинает отрисовываться следующий сектор options.start += options.step // запускаем рисование следующего сектора, рекурсивно // вызывая функцию draw return draw() } } // старт анимации отрисовки одного сектора requestAnimationFrame(fn) } function drawSector(colorFill, widthWheel, inc) { // beginPath используется чтобы начать серию действий, описывающих отрисовку фигуры. // каждый новый вызов этого метода сбрасывает все действия предыдущего и начинает // рисовать заново ctx.beginPath() // устанавливаем цвет или стиль, используемый при выполнении обводки ctx.strokeStyle = colorFill // устанавливается ширина линии, которой будет рисоваться дуга ctx.lineWidth = widthWheel // вычисляем конечный угол, если inc не задан, значит рисуется подложка // и задаётся конечный угол прогресс бара let end = (inc === undefined) ? getRadians(427.5) : options.start + inc // создаётся дуга, где xc и yc центр окружности, далее радиус, начальный и конечный угол ctx.arc(xc, yc, options.r, options.start, end) // рисуется дуга (часть сектора), с параметрами заданными с помощью // strokeStyle, lineWidth и arc ctx.stroke() return } function showPercents(i, inc) { // угол в радианах, на который отрисован прогресс бар на текущий момент let angle = options.step * i + inc, // получаем проценты, где 0.0549779 результат деления options.step * 7 на 100 percents = Math.ceil(angle / 0.0549779) // цвет текста ctx.fillStyle = '#666' // параметры шрифта и текста ctx.font = '400 12px Roboto' // центрирование текста по горизонтали ctx.textAlign = 'center' // центрирование текста по вертикали ctx.textBaseline = 'center' if (percents == 100) { ctx.font = '900 12px Roboto' ctx.fillStyle = 'green' } // очищаем область canvas в которую будет выведен текст // область представлена в виде прямоугольника заданного // начальной точкой (120px,125px), шириной и высотой (60px,30px) // отсчёт координат идёт от верхнего левого угла canvas ctx.clearRect(34, 37, 33, 17) // выводим текст в центр canvas ctx.fillText(percents + '%', xc, yc) } function drawLine(i) { // определяем координаты начала и конца линии границы текущего сектора let x0 = xc + Math.cos(options.start) * (options.r + 15), y0 = yc + Math.sin(options.start) * (options.r + 15), x1 = xc + Math.cos(options.start) * (options.r - 15), y1 = yc + Math.sin(options.start) * (options.r - 15) ctx.beginPath() // Вариант 1 - назначаем цвет границы стыка всех секторов //ctx.strokeStyle = '#fff' // Вариант 2 - выбираем цвет стыка текущего и следующего секторов из массива ctx.strokeStyle = options.colors[i] // устанавливаем координаты начала и конца рисуемой линии и её толщину ctx.moveTo(x0, y0) ctx.lineTo(x1, y1) ctx.lineWidth = 1 // рисуем границу секторов ctx.stroke() return } // --- Progress Bar Functions ---End--- function findElement() { let parentDiv = document.querySelector('#primary-inner') //Ищем контейнер верхнего уровня с наверняка уникальным id let childDiv = parentDiv.querySelector('#contents') //и только внутри него ищем "ребёнка" уже янвно с неуникальным id. if (!childDiv) { setTimeout(findElement, 250) return false } else {//Если страница не догрузилась, перезапускаем через четверь секунды. createStyleSheet() //Именно это она и делает :))) let firstParent = childDiv.parentNode //Добираемся по DOM-у до нужного родительского узла. let element = createWidget() firstParent.prepend(element) //Вставляем виджет перед всеми его потомками. canvas = document.getElementById('youtube-myCanvas') // контекст, через который будем управлять содержимым canvas ctx = canvas.getContext('2d') // центр по горизонтали и вертикали xc = canvas.width / 2 yc = canvas.height / 2 // объект содержащий настройки options = {} // объект кнопки, запускающей прогресс бар button = document.querySelector('#youtube-progress-bar-button') i = 0 //запускаем настройку отрисовки прогресс бара init() } } setTimeout(findElement, 3000) //Ну, не сразу скрипт запускаем, не надо сразу. })();