- Подписка на печатную версию:
- Подписка на электронную версию:
- Подшивки старых номеров журнала (печатные версии)
LXF118:Greasemonkey
Материал из Linuxformat.
- Firefox Не давайте web допечь вас – Greasemonkey поможет
Содержание |
Greasemonkey: Правим Сеть
- Интернет – замечательная штука, но не всегда выглядит должным образом. Джульетта Кемп и Greasemonkey берутся это исправить.
Идея, лежащая в основе Greasemonkey, очень проста. Это расширение Firefox, и оно устанавливается так же, как и все остальные (найдите его с помощью меню Tools > Addons [Инструменты > Дополнения] и нажмите Install [Установить]). Однако само по себе оно ничего не делает, лишь позволяет выполнять скрипты (ваши или написанные другими пользователями), чтобы изменить внешний вид и поведение web-страниц.
Скрипты Greasemonkey – это небольшие программы, которые и делают всю работу; сам Greasemonkey только загружает их и управляет ими. Составляются они на JavaScript, но по соображениям безопасности нельзя просто написать обычный скрипт и использовать его. Нужно знать о кое-каких подводных камнях, хотя сценарии данного урока не затрагивают ни один из них.
Сразу скажу тем, кто не знаком с JavaScript, что в этой статье вы не найдете подробного описания его синтаксиса; но пусть это вас не останавливает. Приведенные примеры довольно логичны, и все объяснены.
Чтобы установить сценарий, написанный другим пользователем, откройте его страницу в Firefox и щелкните по ссылке на скрипт. Появится окно инсталлятора (как и для обычного расширения), и вы сможете просмотреть исходный код скрипта или установить его, если уверены в содержимом.
Часть 1 Мой первый скрипт Greasemonkey
Чтобы максимально упростить написание скрипта, Greasemonkey предоставляет полезный диалог. Сразу после установки Greasemonkey в правом нижнем углу окна Firefox на панели инструментов появится обезьянья мордочка. Кликните на ней правой кнопкой мыши, и откроется меню, где будет пункт Новый скрипт [New User Script]. Выберите его, и появится диалог, похожий на приведенный справа.
Поле ‘name’ – это имя скрипта. Лучше выбрать имя, ясно говорящее о том, что делает сценарий – потом будет проще управлять ими. Строка ‘namespace’ (пространство имен) позволяет избежать конфликтов вашего скрипта с другими. Если вы попробуете добавить сценарий, имя которого совпадает с уже установленным, как раз пространство имен и определит, будет ли последний перезаписан (если пространства имен одинаковы) или будет существовать совместно со старым (если они разные). Здесь есть несколько вариантов: например, использовать в качестве пространства доменное имя своего web-сайта. Как альтернативу, можно взять http://localhost или, если вы собираетесь загрузить свой готовый скрипт на сайт http://userscripts.org, этот адрес. В текущей версии Greasemonkey это поле обязательно для заполнения.
Поле ‘description’ содержит описание действий скрипта. Всегда заполняйте его, даже для собственных скриптов – их может накопиться много, и если оставить информацию о том, что делает каждый сценарий, ими будет проще управлять.
Правила включения (‘include’) и исключения (‘exclude’) определяют, на каких сайтах будет выполняться скрипт, и могут содержать шаблоны. Так, www.example.com/* соответствует адресу www.example.com/ и всем страницам, начинающимся с этого адреса (тогда как www.example.com/ без звездочки соответствует только главной странице). Шаблоны могут использоваться и в доменных именах: http://*.example.com/f* соответствует всем страницам, пути к которым начинаются с f, на всех серверах домена example.com. По умолчанию поле «include» содержит адрес страницы, с которой было открыто окно создания скрипта, но его можно спокойно очистить. Если адрес страницы соответствует хотя бы одному из правил включения и не подпадает ни под одно правило исключения, то скрипт будет запущен. Если правила включения не заданы, то Greasemonkey использует @include *, что соответствует всем адресам, и скрипт будет выполняться на каждой загружаемой странице.
В правилах включения можно использовать синтаксис Magic TLD.tld, который будет соответствовать любому домену верхнего уровня (включая домены второго уровня типа .co.uk и т.д.). Например, example.tld соответствует example.com, example.co.uk, example.org и целому набору других доменов. Однако по соображениям безопасности этот синтаксис не стоит использовать, если скрипт работает с личными данными.
Наш первый скрипт сделает фон страницы белым: это спасет вас, если вы угодите на сайт, чей автор – любитель гламурно-розового или покрыл фон мозаикой из картинки, через пару секунд вызывающей ломоту в висках. Итак, выберите сайт, у которого нужно изменить цвет фона, добавьте его адрес в поле @include (здесь я использую www.example.com) и заполните другие поля подходящими значениями.
Сразу после этого будет запрошен предпочитаемый редактор (если он еще не задан), и Greasemonkey загрузит файл скрипта. Сейчас в нем содержатся только метаданные.
Файл выглядит примерно так:
// ==UserScript== // @name Background Change // @namespace http://www.example.com/~juliet/ // @description Change the background colour of a page // @include http://www.example.com/* // ==/UserScript==
Пора написать сам сценарий. Он лишь изменяет цвет фона всех страниц в домене include на белый. (Бывает, что цвета фона и в самом деле не смотрятся.) Для страниц без фреймов или прочих осложнений это делается всего одной строкой:
document.body.style.background = “#ffffff”;
document – это встроенный способ ссылки на текущую страницу. Это объект DOM (Document Object Model – объектная модель документа), который представляет весь HTML-документ. Представьте его себе как дерево HTML-элементов (объектов). Каждый новый элемент является «дочерним» по отношению к предыдущему (посмотрите на схему, изображающую возможную структуру тела HTML-документа).
В этой модели для ссылки на объект используется нотация главный объект.потомок.потомкпотомка. Таким образом, сначала идет элемент body, затем стиль элемента body и, наконец, атрибут background стиля элемента body. Данный атрибут устанавливается в белый цвет (#ffffff – обозначение белого цвета в шестнадцатеричной записи; это один из стандартов HTML. Можно было бы использовать и просто white)
Попробуйте сценарий в деле – откройте страницу с произвольным фоном (не белым), с помощью меню Управление скриптами [Manage Scripts] добавьте ее адрес в список ‘include’ для только что созданного скрипта и обновите страницу. Помните, что меняется не сама страница, а только ее отображение: если результат вышел скверным, никто не пострадает! Скрипт можно просто отключить или отредактировать его и обновить страницу. Так что не бойтесь экспериментировать.
Greasemonkey включается и отключается щелчком левой кнопкой мыши на его значке. Можно отключить его, посмотреть, как выглядит страница, потом включить, перезагрузить страницу и увидеть, что делает скрипт.
Часть 2 Зададим стили CSS
Как было показано на схеме DOM, «дочерним» элементом HTML-элемента является тот, который в нем содержится. Представьте себе документ в виде большого дерева, каждый открывающий тэг которого (например, <p>) начинает новую ветвь, а каждый закрывающий (например, </p>) заканчивает ее. Самозакрывающиеся тэги (например, <br />) образуют ветвь, у которой не может быть дочерних. Таким образом, поиск можно ограничить внутри заданного абзаца или блока div.
Однако этот скрипт не работает с фреймами – он изменит цвет фона лишь у главного документа. Если на вашей странице с фоном «вырви глаз» есть фреймы, лучше всего одолеть их все сразу, добавив стиль CSS, который переопределит существующий. Чтобы отредактировать уже созданный файл, щелкните на обезьяньей мордочке правой кнопкой и выберите Управление скриптами. Укажите свой скрипт и нажмите на кнопку Правка [Edit], чтобы он снова появился в текстовом редакторе. Во время тестирования не нужно закрывать редактор или диалог управления скриптами – достаточно сохранить файл и попробовать обновить страницу.
function addCss(cssString) { var head = document.getElementsByTagName(‘head’)[0]; return unless head; var newCss = document.createElement(‘style’); newCss.type = “text/css”; newCss.innerHTML = cssString; head.appendChild(newCss); } addCss ( ‘* { background-color: #ffffff ! important; }’ );
Первая функция (addCss) задает способ добавления глобальной таблицы стилей CSS. document опять-таки ссылается на текущую страницу, но в этом примере мы используем функцию getElementsByTagName («получить элементы по имени тэга» – именно это она и делает) для выбора первого элемента head. Для тех, кто совсем не знаком с CSS: стили задаются [в том числе] в секции head HTML-файла, и нам нужно добыть этот элемент, чтобы он стал родительским для нового элемента CSS. Здесь есть строка, которая отлавливает ошибочную ситуацию, когда секции head нет (производится возврат из функции без каких-либо действий), затем скрипт создает элемент style и устанавливает его атрибут type в text/css. В строке
newCss.innerHTML = cssString
функция принимает то, что ей передано, и вставляет это в элемент style. Затем полностью созданный элемент style добавляется к элементу head – вот и все.
В последних строках происходит вызов функции с аргументом, устанавливающим цвет фона – там, где строка cssString. Обратите внимание на то, что мы используем скобки (), потому что вызываем функцию, а не определяем ее. Флаг ! important гарантирует, что ваши CSS-стили переопределят указанные на странице.
Фактически этот код добавляет в заголовок HTML-страницы следующие строки:
<style type=”text/css”> * {background-color: #ffffff ! important;} </style>
Точно так же можно задать собственные стили CSS для любого элемента страницы – просто задайте подходящую CSS-строку, которая будет передана функции addCss. Например, вызов
addCss ( ‘* { background-color: #ffffff ! important; text-align: justify ! important; color: black ! important; }’ );
установит цвет фона в белый, весь текст сделает черным и выровняет его по ширине.
Часть 3 Изменим заданные элементы
Итак, мы научились менять стиль одного элемента, переделав объект DOM, или множество стилей, добавляя собственные CSS-записи, и можем применить это к заданным сайтам с помощью правил включения/исключения Greasemonkey. А если нам нужно найти элементы определенного типа, встречающиеся на странице несколько раз, и изменить их?
Например, у вас есть форум, пользователи которого иногда используют малоприятные аватары, и вы хотите просто заменить их все на безопасную картинку из вашего собственного web-пространства. Прежде всего, найдем все картинки на странице:
var allImgs,thisImg; allImgs = document.evaluate(‘//img[@src]’, document, null, XPathResult.UNORDERED_NODE_SNAPSHOT_TYPE, null);
Полезен здесь метод document.evaluate. Его освоение дает огромные возможности. Первый параметр (’//img[@src]’) представляет собой XPath-запрос (подробнее об этом – чуть ниже); второй – область поиска. В данном случае это весь документ, но в дальнейшем ее можно ограничить только дочерними элементами. Третий параметр – функция разрешения пространства имен, которая имеет смысл только для документов типа application/xhtml+xml.
Четвертый параметр задает способ возвращения результатов. Вариант, указанный здесь, соответствует произвольному порядку, что чаще всего и нужно. Если результаты требуется вернуть в порядке их появления на странице, используйте вместо него XPathResult.ORDERED_NODE_SNAPSHOT_TYPE. Последний параметр позволяет объединить результаты запроса – передайте туда результат предыдущего вызова document.evaluate, и получите их назад вместе. Поэкспериментируйте с этим!
Запрос XPath (первый параметр) – «мотор» этой функции. XPath – мощный язык запросов для XML-документов, встроенный в Firefox [и не только, – прим. ред.], и его можно использовать в Greasemonkey. Если вы ищете определенный набор элементов, можно просто пройтись по дереву DOM, выудить наборы узлов и найти в них искомое. Но это довольно медленно и не очень красиво с точки зрения кода. XPath позволяет найти на странице все, что нужно, гораздо быстрее и элегантнее. Я воспользуюсь им в нескольких скриптах, и, надеюсь, это поможет вам понять, как он работает – если хотите узнать о нем подробнее, поищите спецификацию или онлайн-учебники в Интернете. Язык в самом деле очень гибок: если вы можете определить набор результатов, который хотите получить из HTML-документа, то сможете и написать XPath-запрос, который позволит их получить.
Вернемся к нашему скрипту: картинки мы добыли, и хотим с ними что-то сделать. Вот следующий фрагмент кода: допишите его сразу после приведенного выше:
for (var i=0;i<allImgs.snapshotLength;i++) { var thisImg = allImgs.snapshotItem(i); var src = thisImg.src; var srcMatch = src.match(‘^http://www.example.com/forums/userpic/’); if (srcMatch != null) { thisImg.src = ‘http://www.example.com/~juliet/safepic.gif’; } }
snapshotLength и snapshotItem — методы, работающие с результатом вызова document.evaluate и возвращающие соответственно общее число результатов и заданный результат по его номеру. Вставьте оба в цикл for, как в приведенном примере, и обработайте каждый элемент (здесь – каждое изображение со страницы) результата запроса XPath.
Небольшое замечание: в обычном JavaScript пройтись по коллекции объектов можно так:
for (var thisImg in allImgs) { // do stuff }
Первоначально в Greasemonkey было несколько серьезных дыр в безопасности, вызванных способом внедрения пользовательских скриптов непосредственно в web-страницы: так ваши сценарии оказывались доступны для сайтов страниц злоумышленников. Ныне Greasemonkey работает по-другому, на самом деле выполняя скрипты в «песочнице» и используя обертки для доступа к объектам на удаленной web-странице, которую хотят изменить ваши скрипты.
Из-за особенностей реализации безопасности в скриптах Greasemonkey такой способ работать не будет. Приходится идти окружным путем.
thisImg.src дает нам значение атрибута src изображения. Так, для тэга <img src=«foo.jpg»>, thisImg.src вернет foo.jpg. (Точно так же можно получить значение атрибутов width или height или любых других атрибутов тэга img. Полный список элементов и атрибутов объекта DOM можно найти в Интернете.)
В конце мы пытаемся сравнить src с ожидаемым значением для пользовательских аватар на этом форуме (чтобы узнать его, взгляните на исходный код страницы форума), и если результат не равен нулю (соответствие есть), значение атрибута src заменяется путем к безопасной картинке. (Можно было бы обойтись без лишней строки, вызвав src.match, но приведенный вариант более понятен.) Готово!
Часть 4 Замена текста
Теперь попробуем заменить текст, который встречается на странице в нескольких местах. Предположим, вас достало слово «аутсорсинг» [outsourcing], тут и там маячащее на главной странице сайта вашей компании, куда вам, к сожалению, приходится заходить регулярно. Заменим его словом «левак» [otherguy] (или любым другим, которое вы находите юморным, а не нудным).
// ==UserScript== // @name Deoutsourcing // @namespace http://www.example.com/~juliet/ // @description Replace “outsourcing” on corporate homepage // @include http://www.example.net/corporatehome // ==/UserScript== textNodes = document.evaluate( “//text()”, document, null, XPathResult.UNORDERED_NODE_SNAPSHOT_TYPE, null); var searchRE = new RegExp(‘outsourcing’,’gi’); var replace = ‘otherguy’; for (var i=0;i<textNodes.snapshotLength;i++) { var node = textNodes.snapshotItem(i); node.data = node.data.replace(searchRE, replace); }
Первый фрагмент кода мы уже видели в предыдущем скрипте. Он ищет в документе все текстовые узлы. Далее мы задаем регулярное выражение. Конструктор new RegExp() принимает два аргумента. Первый – это искомая строка, второй – модификаторы. g означает глобальное соответствие: заменяются все вхождения строки, а не только первое. (Чаще всего вам это и нужно.) i означает сравнение без учета регистра.
Есть и модификатор m для многострочного режима, в котором якоря ^ и $ (означающие начало и конец строки, соответственно) совпадают с позициями перед и после перевода строки, а не только с началом и концом текста.
Наконец, очередной цикл for проходит по результату запроса XPath, находя нашу строку и заменяя ее другой. Легко!
Greasemonkey способен на гораздо большее – просто поэкспериментируйте и увидите, что получится. Подстройте Интернет под себя! LXF
Радости отладки
В идеальном мире все, что бы вы ни написали, сразу заработает так, как задумано. Удачи!
Тем, у кого сразу не получилось, помогут некоторые средства отладки:
- DOM Inspector и InspectThis – доступны как дополнения к Firefox 3. Инспектор DOM (после установки его можно запустить из меню Tools [Инструменты]) позволяет взглянуть на объектную модель документа страницы – то есть ее структуру. InspectThis поможет вам исследовать отдельный элемент по щелчку на нем правой кнопкой мыши и выбору пункта “Inspect Element” [Инспектировать элемент] контекстного меню. Оба помогут получить информацию об именах и идентификаторах элементов страницы, которые вы ищете. Инспектор DOM поставляет и другую информацию, например, стили CSS и данные о JavaScript
- Консоль ошибок (Меню Tools в Firefox). На ней показываются все ошибки в скриптах с того момента, как вы открыли Firefox. Чтобы избавиться от них, нажмите кнопку Clear, затем обновите страницу со своим скриптом. Если он упадет, вы получите сообщение об ошибке. (Номер строки в голову не берите – из-за способа внедрения пользовательских сценариев в страницу он не будет соответствовать реальному. Разбирайтесь только с сообщением об ошибке.)
- Ведение журнала ошибок с помощью функции Greasemonkey GM_log. Сообщения будут появляться в консоли ошибок.
Радости прибавилось бы значительно, если бы можно было увидеть пример скрипта, который не просто добавлял бы комфорта в просмотре страниц, а выполнял бы какие то функциональные задачи, например: Сканировал последовательно html документ, натыкаясь на урл, открывал бы его страницу и ждал нажатия какой либо кнопки, приказывающей закрыть страницу и двинуться в поиске следующего урла в html документе