Это было во время очередной встречи разработчиков. Это было похоже на шутку, как и большинство интересных вещей, которые я когда-либо делал.

«Жаль, что GitHub не позволяет звуку комкания бумаги, когда я объединяю эти запросы на слияние!»

На самом деле, я не думаю, что мой коллега в полной мере понимает, как легко должно быть зарубить кого-то за это быстро и грязно. Я не думаю, что он даже знал, что это возможно.

И это настолько бесполезно, что я не смогу спать без этого.

Мне очень нравятся все возможности играть с tampermonkey. С помощью нескольких строк javascript вы можете добавить действительно классные функции, которые могут сэкономить вам некоторое время. К тому же n00bs смотрят на тебя так, словно ты абсолютный бог. И, как полу-н00б, я должен признаться, что я уязвим для лести.

В качестве первого раздела я расскажу, как мне удается запускать звук. Второй раздел будет говорить о воспроизведении самого звука. В-третьих, это дополнительная проблема, чтобы сделать его более привлекательным.

Очевидно, это не совсем то, как я заработал. Небольшой прогресс влево, небольшой прогресс вправо. Но я считаю, что это делает статью более читабельной.

Обратите внимание, что эта история больше tampermonkey/javascript ориентирована на новичков. Но вам нужно знать о программировании.

Прямо здесь

Итак, речь идет о включении звука.

Мы хотим воспроизвести когда мы нажимаем на самое последнее «Подтвердить слияние», «Подтвердить сжатие и слияние» или «Подтвердить перебазирование и объединить” (в зависимости от вашего шаблона слияния).

Что я обычно делаю, так это проверяю то, что я нажал: просто щелкните правой кнопкой мыши кнопку и inspect. Вы получите что-то вроде.

Таким образом, у вас есть под одним и тем же div одноуровневые узлы с классами btn-group-merge, btn-group-squash и btn-group-rebase. Кнопка, которую мы нажимаем, входит в число этих трех.

Это хорошая новость: теперь нам просто нужен удобный и эффективный способ найти эти кнопки с помощью (надеюсь) простого запроса javascript, и мы сможем добавить действие, когда любая из этих кнопок нажата. нажал.

Существует несколько способов создания этого javascript-запроса. Во-первых, вы можете использовать встроенную функцию вашего веб-браузера (щелкните правой кнопкой мыши, затем «Копировать путь JS»).

document.querySelector( “#partial-pull-merging > div.merge-pr.js-merge-pr.js-details-container.Details.is-merging.open.Details — on > div > form > div > div.commit-form-actions > div > div.BtnGroup.btn-group-merge > button” )

Повторите это для двух других кнопок (div.BtnGroup.btn-group-merge станет div.BtnGroup.btn-group-squashи div.BtnGroup.btn-group-rebase), и это сработает!

Существует другое решение, основанное на XPath (подумайте о своей веб-странице как о дереве, а о XPath как о правильном направлении каждый раз, когда вам нужно выбрать ветвь). Опять же, «Копировать XPath»:

//*[@id=”partial-pull-merging”]/div[1]/div/form/div/div[2]/div/div[1]/button

Повторите для других кнопок (последняя div[1] станет div[2] и div[3]), поместите ее в какую-нибудь document.evaluate, и это еще один способ найти эти кнопки.

Но это не совсем устраивает. Если вы посмотрите на свои запросы, то увидите, что они начинаются с четко определенного узла (он имеет уникальный идентификатор partial-pull-merging). И затем у нас есть куча элементов макета, которые могут измениться в любое время. Другими словами, он сломается, как только макет изменится.

Так что мы должны быть немного более осторожными здесь. Хорошие новости, не очень.

Если вы посмотрите на кнопки, которые мы рассматриваем, то увидите, что это уникальные элементы, имеющие атрибут data-disable-with=”Merging…”. Это означает, что если мы сможем найти все узлы с этим атрибутом, то все готово. И это просто (найдите «поиск javascript по атрибуту»), поскольку

[data-disable-with=”Merging…”]

достаточно, чтобы собрать все элементы с нужным атрибутом.

Конечно, он сломается, если GitHub изменит этот атрибут, но, поскольку этот атрибут предназначен для того, что меня интересует, я думаю, что меньше подвержен побочным эффектам.

Я чувствую себя лучше. ☯️

Потяните да тригга БУМ

Теперь мы уверены, что можем идентифицировать кнопки, на которые собираемся нажимать. Пришло время включить звук при нажатии.

Пришло время начать использовать расширение tampermonkey. По сути, это расширение позволяет вам запускать код javascript внутри вашей веб-страницы. Давайте пойдем шаг за шагом и сначала поработаем над надежным обнаружением кнопок. Давайте напишем наш первый скрипт.

Здесь нет ничего невероятного: когда скрипт внедряется и выполняется, всплывает простое окно, когда вы находитесь на странице запроса на включение. Работает, хорошо. Теперь давайте откроем это окно при нажатии кнопки окончательного слияния.

Это немного интереснее. Функция main ищет наши кнопки.

Затем для каждой из этих кнопок определяется прослушиватель (часто также называемый наблюдателем), который будет вызывать playMusic при нажатии кнопки.

(Обратите внимание, что это playMusic, а не playMusic(): мы пока не хотим вызывать playMusic, мы просто хотим, чтобы кто-то мог вызвать playMusic на click. . Поэтому мы должны предоставить саму функцию playMusic, мы не должны ее вызывать!)

Но это все равно не очень хорошо. Вы пытаетесь, это работает; но иногда нет. По сути ищем наши buttons, но не проверяем, что они найдены.

Здесь происходит то, что веб-страницы не загружаются все сразу. И они могут динамически меняться в зависимости от действий пользователя!

Так что да, мы можем проверить, что переменная buttons что-то содержит. Но самое главное, что мы могли бы сделать, если бы он был пуст? Нам нужно изменить наш подход и проверять наличие этих кнопок при изменении страницы.

Так будет лучше: MutationObserver будет наблюдатьнашу страницу (нашу document). Каждый раз, когда происходит модификация, мы ищем наши кнопки. Если мы их найдем, то сможем определить наши триггеры. Не забываем отключать наблюдателя, потому что раз уж мы их нашли один раз, нет необходимости искать их еще раз (в нашем варианте использования).

Но изменения страницы нам предоставляют через массив mutations MutationRecords. Не было бы более элегантно и эффективно использовать эту информацию вместо поиска по всему document?

Здесь мы ждем вставки узлов. Вот почему мы наблюдаем весь документ (то естьdocument и все его subtree) и хотим знать childList. Каждая MutationRecord будет содержать в своем addedNodes элементе добавленные child узлы. Затем нам просто нужно немного пошевелиться, чтобы извлечь правильную информацию. (Выровняйте все узлы с помощью flatMap, чтобы у нас был единый список; сохраняя только элементы или атрибуты путем фильтрации на nodeType , в конечном итоге выбирая узлы кнопки слияния.)

В основном это будет работать, за исключением того, что «Что, если страница больше не изменится с момента установки наблюдателя?». Триггер playMusic никогда не будет установлен, следовательно, никогда не будет запущен. Это немного маловероятно, но может.

Чтобы справиться с этим, мы попросили tampermonkey запустить наш скрипт как можно быстрее (см. @run-at document-startвверху). Мы не хотим пропустить ни одно событие!

«Но подождите! Когда мы выбираем наш режим слияния, мы видим, как появляется кнопка confirm merge. Мы должны уловить мутацию именно в этот момент?!» Ну, это не так просто.

В фоновом режиме, когда вы нажимаете первую кнопку Объединить запрос на вытягивание (не окончательное подтверждение), последняя кнопка становится видимой. Но они не были созданы: они уже существовали, но были скрыты. А структура веб-страницы (Объектная модель документа или DOM) действительно не изменяется.

Если вы посмотрите на html-дерево, вы увидите, что вокруг наших кнопок вставлено несколько CSS ::before и ::after, что делает эти элементы видимыми или невидимыми. Но сами кнопки не изменяются: те же самые attributes, classes… Другими словами, MutationObserver не будет уведомлен о такого рода изменениях CSS.

Итак, давайте будем честными, в такой ситуации, как взлом какого-нибудь скрипта tampermonkey, когда страницы перезагружаются каждый раз неочевидным образом, хотя это немного менее элегантно, использование некоторых грубых document.querySelectorAll также может быть лучшим способом удовлетворить ваши потребности.

Также наверняка можно комбинировать оба подхода: сначала проверить, есть ли кнопки, и только потом наблюдать за их появлением в DOM. В зависимости от вашего интереса к вопросу и того, насколько «серьезен» ваш сценарий, вы можете пойти по этому пути. Тем не менее, я не пойду туда для этой записи в блоге.

Итак, давайте остановимся на этом, теперь у нас есть достаточно простой способ определить, что действие должно быть выполнено при нажатии кнопки слияния.

Теперь playMusic! 🎵

Чтобы воспроизвести аудио, это выглядит просто…

… и да, обычно это работает. Но на https://github.com/*/*/pull/* у нас такая ошибка

Refused to load media from 'https://upload.wikimedia.org/wikipedia/commons/d/d9/Wilhelm_Scream.ogg' because it violates the following Content Security Policy directive: "media-src 'none'".

И как говорит мой браузер Chrome:

Политика безопасности контента вашего сайта блокирует некоторые ресурсы, поскольку их происхождение не указано в заголовке политики безопасности контента

Политика безопасности контента (CSP) повышает безопасность вашего сайта, определяя список доверенных источников и предписывая браузеру выполнять или отображать ресурсы только из этого списка. Доступ к некоторым ресурсам на вашем сайте недоступен, поскольку их происхождение не указано в CSP.

…СНИП…

GitHub не позволяет читать медиаданные с любого сайта! (media-src это none). Опять придется что-то хакнуть.

Вместо того, чтобы давать URL-адрес нашему экземпляру Audio, мы напрямую загружаем данные сами. Тогда наша обязанность соединить эти сырые кусочки с каким-то AudioContext. Я не буду слишком долго останавливаться на этом, вы найдете много лучших ресурсов там.

Соедините эти два сценария вместе, и все, у вас есть музыка!

Еще один вызов? Тик-так-тик-так бум!

На GitHub, когда вы нажимаете объединить, через несколько секунд появляется сообщение «Запрос на извлечение успешно объединен и закрыт».

Было бы неплохо, чтобы шум тик-так начинался, когда мы нажимаем, и взрыв в сообщении «объединение успешно»?

Вот моя попытка с некоторыми встроенными комментариями. Он далек от совершенства: не забывайте, я совершенно не разбираюсь в javascript, я никогда не трогал ни одного фронта, кроме трех скриптов tampermonkey. Жду ваших предложений!

  • Мне нужно, чтобы какие-то глобальные переменные могли использовать один и тот же звуковой контекст и отключать звук падения бомбы, когда она взрывается. Для этой цели я использую класс State.
  • Чтобы эффект был эффективным, звук взрыва должен воспроизводиться в точное время, когда на временной шкале появляется сообщение «объединение выполнено». В противном случае это действительно не работает.
    Поэтому мне нужно было начать загрузку звука взрыва, как только мы узнаем, что он понадобится: когда мы нажимаем кнопку «объединить». . Я использовал Promise для этого. Также GM_xmlhttpRequest имеет некоторые небольшие отличия от стандарта (из документа: «Примечание: флаг синхронный в деталях не поддерживается») , поэтому мне пришлось что-то взломать. Опять же, в Интернете есть ресурсы по этому поводу, не стесняйтесь рассказывать мне о лучших способах сделать это.
  • Чтобы обнаружить сообщение «слияние выполнено», я работал с mutations вместо глобального запроса, поэтому я могу запустить звук взрыва как можно быстрее. Я сохранил глобальный запрос для «начать слияние», потому что не хочу его пропустить, и здесь мы можем сделать небольшую задержку.

Объединить. тик-так-тик-так-так. Объединить готово. Хлопнуть!

Выводы

Мне нравятся эти бесполезные вызовы, я всегда многому учусь.

Кроме того, я мало знаком с javascript, поэтому будьте снисходительны. Или событие лучше: помогите мне в комментариях!

Последний совет, если вы начнете играть с tampermonkey. Когда вы пробуете такие сценарии, которые должны взаимодействовать с динамическими элементами страницы, постарайтесь быть очень точными, чтобы понять, что на самом деле происходит на странице. Когда появляются элементы, они вставляются или один из его classes или attributes изменяется? А может какой-нибудь ::before или::after CSS? 😉

Tampermonkey — невероятный инструмент!

Спасибо

  • Большое спасибо этому посту за аудио часть.
  • Эта ветка навела меня на верный путь к theMutationObeserver .
  • Спасибо https://lasonotheque.org/ за предоставление бесплатных звуков (как в случае с бесплатным пивом и свободой слова).