- Фильтрация элементов меню в зависимости от адреса страницы
- Выполнение скрипта при загрузке страницы
- Работа с закладками
Фильтрация элементов меню в зависимости от адреса страницы
В GreaseMonkey есть такая удобная штука: команды скриптов в соответствующем меню отображаются только на тех страницах для которых одни предназначались. Это позволяет не только экономить рабочее пространство браузера, но и делает список команд более компактным, поскольку он ограничивается только командами, актуальными для текущей страницы. Реализовать такое в своем меню - совсем несложно. Для этого надо всего-навсего обработать событие popupshowing соответствующего menupopup и в обработчике проверять, нужно ли делать menuitem видимым в данный момент.
Для фильтрации адресов по шаблону (как это реализовано в GreaseMonkey) мы воспользуемся способом описанным в следующем топике документации.
Match patterns - Mozilla | MDN
Там нужно будет импортировать два модуля, о других глобальных модулях, которые можно также использовать в своих проектах можно узнать здесь
JavaScript code modules - Mozilla | MDN.
Для того, чтобы сделать процесс создания таких меню более легким я ввел для menuitem, которые должны быть видны не везде, два дополнительных атрибута data-uri-include и data-uri-exclude. В первый мы будем размещать список шаблонов страниц, на которых данный элемент будет виден, во второй - список шаблонов страниц, которые могут соответствовать какому-то шаблону из первого списка, но их все равно надо исключить.
Для примера создадим оверлей следующего содержания
Код xml | Выделить |
<?xml version="1.0"
encoding="utf-8"
?>
<overlay xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
<script type="application/javascript">
<![CDATA[
Components.utils.import("resource://gre/modules/MatchPattern.jsm");
Components.utils.import("resource://gre/modules/BrowserUtils.jsm");
function setVisibility(element)
{
var ma = element.getAttribute("data-uri-include");
ma = ma ? ma.split(",") : null;
var mp = new
MatchPattern(ma);
var mipa = element.getAttribute("data-uri-exclude");
mipa = mipa ? mipa.split(",") : null;
var mip = new
MatchPattern(mipa);
var uri = BrowserUtils.makeURI(content.document.location.href);
if (mip.matches(uri))
{
element.setAttribute("hidden", "true");
}
else if
(mp.matches(uri))
{
element.setAttribute("hidden", "false");
}
else
{
element.setAttribute("hidden", "true");
}
}
function scriptsCommands_popupshowing(src)
{
var items = src.querySelectorAll("menuitem");
for (var i = 0;
i < items.length; i++)
{
setVisibility(items[i]);
}
}
]]>
</script>
<popupset id="mainPopupSet">
<menupopup id="filterExampleMenu">
<menu label="Команды
скриптов">
<menupopup onpopupshowing="scriptsCommands_popupshowing(this);">
<menuitem label="Киберфорум
и гугл"
data-uri-include="http://www.cyberforum.ru/*,*://*.google.com/*"/>
<menuitem label="Киберфорум
и гугл(кроме
блогов киберфорума)"
data-uri-include="http://www.cyberforum.ru/*,*://*.google.com/*"
data-uri-exclude="http://www.cyberforum.ru/blogs*"/>
<menuitem label="Гугл"
data-uri-include="*://*.google.com/*"/>
<menuitem label="Гугл
и вконтакте"
data-uri-include="*://vk.com/*,*://*.google.com/*"/>
<menuitem label="Вконтакте
и киберфорум" data-uri-include="http://www.cyberforum.ru/*,*://vk.com/*"/>
<menuitem label="Вконтакте
и киберфорум-блоги" data-uri-include="http://www.cyberforum.ru/blogs*,*://vk.com/*"/>
</menupopup>
</menu>
</menupopup>
</popupset>
</overlay>
Теперь для того, чтобы привязать это к кнопке, создаем новую кнопку и во вкладке "Инициализация" ее редактора размещаем код
Код javascript | Выделить |
this.type = "menu-button";
this.setAttribute("popup", "filterExampleMenu");
Выполнение скрипта при загрузке страницы
Другой полезной возможностью, которой обладает GreaseMonkey является возможность выполнять какой-то код при загрузке страницы, адрес которой соответствует определенному шаблону. О проверке на соответствие шаблону я уже написал, а вот что касается перехвата загрузки страницы, то подробно об этом написано здесь.
Intercepting Page Loads - Mozilla | MDN
Теперь попробуем связать эти темы в одном коде. Воспользуемся простейшим способом отслеживания загрузки страниц - подпиской на событие load объекта gBrowser. Для начала следует упомянуть, что код надо будет размещать во вкладке "Инициализация" кнопки, поэтому нам придется позаботиться о том, чтобы подписка на событие произошла только один раз, иначе при каждой инициализации у нас будет добавляться обработчик события и при загрузке страниц выполняться будут они все. Подписаться с помощью такого кода
Код javascript | Выделить |
gBrowser.onload = function (evt) {/* Здесь код обработки события*/ };
Код javascript | Выделить |
if (!gBrowser.loadHandlers)
{
gBrowser.loadHandlers = [];
gBrowser.addEventListener("load", function
(evt)
{
gBrowser.loadHandlers.forEach(function (h) { h(evt); });
}, true);
}
gBrowser.loadHandlers.pop();
gBrowser.loadHandlers.push(function (evt)
{
});
Для выполнения определенного кода на определенных страницах нам потребуется объект, который будет содержать функцию с кодом, который надо выполнить, и два свойства, представляющие из себя массивы шаблонов адресов (инклюд и эксклюд соответственно). Кроме того добавим в объект функцию, которая будет проверять документ(переданный как аргумент) на предмет соответствия шаблонам.
Код javascript | Выделить |
Components.utils.import("resource://gre/modules/MatchPattern.jsm");
Components.utils.import("resource://gre/modules/BrowserUtils.jsm");
function PageScript(func, include, exclude)
{
this.func = func; this.include
= include; this.exclude = exclude;
this.testPage = function
(doc)
{
var uri = BrowserUtils.makeURI(doc.location.href);
var inclMatch = new
MatchPattern(this.include);
var exclMatch = new
MatchPattern(this.exclude);
if (inclMatch.matches(uri) && !exclMatch.matches(uri)) return
true;
return false;
}
}
Код javascript | Выделить |
Components.utils.import("resource://gre/modules/MatchPattern.jsm");
Components.utils.import("resource://gre/modules/BrowserUtils.jsm");
function PageScript(func, include, exclude)
{
this.func = func; this.include
= include; this.exclude = exclude;
this.testPage = function
(doc)
{
var uri = BrowserUtils.makeURI(doc.location.href);
var inclMatch = new
MatchPattern(this.include);
var exclMatch = new
MatchPattern(this.exclude);
if (inclMatch.matches(uri) && !exclMatch.matches(uri)) return
true;
return false;
}
}
var scripts = [
new PageScript(() => alert("Это
гугл"), ["*://www.google.ru/*"], []),
new PageScript(() => alert("Это
вконтактик"), ["*://vk.com/*"], []),
new PageScript(() => alert("Это
киберфорум кроме блогов"),
["*://www.cyberforum.ru/*"], ["*://www.cyberforum.ru/blogs*"])
];
if (!gBrowser.loadHandlers)
{
gBrowser.loadHandlers = [];
gBrowser.addEventListener("load", function
(evt)
{
gBrowser.loadHandlers.forEach(function (h) { h(evt); });
}, true);
}
gBrowser.loadHandlers.pop();
gBrowser.loadHandlers.push(function (evt)
{
for(s of
scripts)
{
if (s.testPage(evt.target))
{
s.func();
}
}
});
for...of - JavaScript | MDN
Стрелочные функции - JavaScript | MDN
Здесь доступ к объекту document загружаемой страницы можно получить через evt.target, к объекту window - evt.target.defaultView.
Некоторые из скриптов могут неожиданно запуститься не на тех страницах, для которых они предназначены, это происходит из-за того, что на страницах могут находиться виджеты или какие-то другие фреймы этих сайтов. Фрейм загружается, скрипт выполняется, алерт выскакивает вроде бы на другой странице. Если скрипт изменяет целевую страницу, то это не страшно, поскольку в нем будет использоваться правильная ссылка на документ, но вот алерт может выскочить и не там где надо. Так что для тех случаев, когда это критично надо проверять, является ли загружаемая страница документом верхнего уровня или фреймом внутри другого документа. Для этого можно воспользоваться свойством Window.top - Web APIs | MDN.
Уж коль скоро начал я со сравнения с GreaseMonkey, думаю излишне объяснять, насколько более широкие возможности открываются, учитывая, что это chrome-код. Изменим немного код
Код javascript | Выделить |
/*Initialization Code*/
Components.utils.import("resource://gre/modules/MatchPattern.jsm");
Components.utils.import("resource://gre/modules/BrowserUtils.jsm");
function PageScript(func, include, exclude)
{
this.func = func; this.include
= include; this.exclude = exclude;
this.testPage = function
(doc)
{
var uri = BrowserUtils.makeURI(doc.location.href);
var inclMatch = new
MatchPattern(this.include);
var exclMatch = new
MatchPattern(this.exclude);
if (inclMatch.matches(uri) && !exclMatch.matches(uri)) return
true;
return false;
}
}
var scripts = [
new PageScript((evt) => alert("Это
гугл"), ["*://www.google.ru/*"], []),
new PageScript((evt) => alert("Это
вконтактик"), ["*://vk.com/*"], []),
new PageScript(function
(evt)
{
var selectCode = Array.prototype.filter.call(evt.target.querySelectorAll("a"),
a => a.textContent == "Выделить код");
selectCode.forEach(a => a.addEventListener("click", e => gClipboard.write(e.target.parentElement.parentElement.parentElement.parentElement.querySelector("td.de1 pre.de1").textContent),
false));
}, ["*://www.cyberforum.ru/*"])
];
if (!gBrowser.loadHandlers)
{
gBrowser.loadHandlers = [];
gBrowser.addEventListener("load", function
(evt)
{
gBrowser.loadHandlers.forEach(function (h) { h(evt); });
}, true);
}
gBrowser.loadHandlers.pop();
gBrowser.loadHandlers.push(function (evt)
{
for(s of
scripts)
{
if (s.testPage(evt.target) && evt.target === evt.target.defaultView.top.document)
{
s.func(evt);
}
}
});
Во-первых, здесь уже выполняется проверка документа, является ли он документом верхнего уровня.
Код javascript | Выделить |
if (s.testPage(evt.target) && evt.target === evt.target.defaultView.top.document)
Работа с закладками
Для работы с закладками существует много всевозможных расширений, я сам ими не пользовался, поскольку встроенного функционала мне обычно вполне хватает. Тем не менее программный доступ к функционалу закладок иногда может быть оказаться полезным. В частности можно сделать более удобной работу с букмарклетами, которые тоже являются закладками. Я уже писал, что одним из основных недостатков букмарклетов является неудобство их отладки. Некоторые, даже очень простые программные инструменты могут существенно упростить эту задачу. Для начала простой пример: код JavaScript для букмарклета, написанный в редакторе можно сохранить в буфер обмена и нажатием кнопки добавить на панель закладок букмарклет с этим кодом.
Код javascript | Выделить |
var bmsvc = Components.classes["@mozilla.org/browser/nav-bookmarks-service;1"].getService(Components.interfaces.nsINavBookmarksService);
var ios = Components.classes["@mozilla.org/network/io-service;1"].getService(Components.interfaces.nsIIOService);
/**
Закомментированная версия следующей строки создает букмарклет в точности из кода в буфере обмена,
следующая за ней - оборачивает его в самовызывающуюся анонимную функцию, чтобы предотвратить попытку
перехода на другую страницу, чем обычно страдают букмарклеты. Если используется первая версия, то нужно
чтобы код уже был обернут в (function(){})()
*/
//var uri = ios.newURI("javascript:" + encodeURI(gClipboard.read()), null, null);
var uri = ios.newURI("javascript:(function(){" + encodeURI(gClipboard.read()) +
"})();", null, null);
var newBkmkId = bmsvc.insertBookmark(bmsvc.toolbarFolder, uri, bmsvc.DEFAULT_INDEX, prompt("Код
javascript из буфера обмена
будет добавлен в
закладки. Введите имя закладки."));
Подробно все описано здесь
Манипулирование Закладками используя Службу | MDN
С другой стороны может появиться необходимость получить какие-то данные из закладки и выполнить какие-то действия над ними. Ну например скопировать закладку как BB-код для вставки ссылки на форуме или, опять-таки, отредактировать букмарклет, скопировав его код в буфер обмена. Для этого удобно использовать контекстное меню закладок. Контекстное меню закладок имеет id="placesContext". Получить ссылку на элемент, представляющий закладку через контекстное меню - нетрудно, раньше подобный пример приводился, когда рассматривалась работа с контекстным меню области содержимого. Но проблема в том, что triggerNode контекстного меню закладки - это обычный элемент menuitem и его стандартные свойства не содержат сведений о закладке. Зато у него есть дополнительное свойство _placesNode - объект реализующий интерфейс nsINavHistoryResultNode - Mozilla | MDN, а вот из него мы уже можем получить всю интересующую нас информацию.
В качестве примере приведу оверлей контекстного меню закладок, для копирования закладки как BB-кода и кода букмарклета
Код xml | Выделить |
<?xml version="1.0"
encoding="utf-8" ?>
<overlay xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
<script type="application/javascript">
<![CDATA[
function copyAsBB(src)
{
var node = src.parentElement.triggerNode;
var pnode = node._placesNode;
var s = "";
gClipboard.write("[URL=\"" + pnode.uri + "\"]"
+ pnode.title + "[/URL]");
}
function copyBookmarkletCode(src)
{
var node = src.parentElement.triggerNode;
var pnode = node._placesNode;
var s = "";
if (pnode.uri.startsWith("javascript:"))
{
gClipboard.write(decodeURIComponent(pnode.uri.substring(11)));
}
}
]]>
</script>
<popupset id="mainPopupSet">
<menupopup id="placesContext">
<menuitem label="Копировать
как BB-код"
onclick="copyAsBB(this);"
id="copyAsBB"/>
<menuitem label="Копировать
код букмарклета"
onclick="copyBookmarkletCode(this);"
id="copyBookmarkletCode"/>
</menupopup>
</popupset>
</overlay>
Комментариев нет :
Отправить комментарий