- Оверлеи
- Внедрение оверлея в код кнопки
- Схема XUL
Дополнять контекстное меню одним-двумя элементами можно и программно, как это было показано в предыдущей статье, но создавать код более-менее сложной структуры вручную - как-то не хочется. Кроме того, пример с контекстным меню был просто иллюстрацией возможностей, другие элементы интерфейса, которые нам могут понадобиться, скорей всего окажутся значительно сложнее меню. Можно было бы для каждого такого элемента интерфейса добавить элемент в XML-документ, который был создан для генерации меню кнопки, но платформа Mozilla предлагает более универсальный механизм - оверлеи.
Оверлеи
Документация по оверлеям находится здесь.
XUL Overlays - Mozilla | MDN
В двух словах: оверлей - это фрагмент XUL-кода интерфейса, который располагается в отдельном файле, но подгружается при загрузке отдельного окна. При этом его код, добавляется в элементы окна. Сам документ с корневым элементом overlay заполняется другими XUL-элементами, причем эти элементы должны иметь id такой, чтобы в основном документе также присутствовал такой же элемент с таким же id. При загрузке оверлея, содержимое такого элемента будет заполнено содержимым такого же элемента из оверлея. Иными словами: если нам надо добавить элемент в контекстное меню, как в предыдущей статье, то мы создаем оверлее <menupopup id="contenAreaContextMenu"> и вставляем все что нужно туда.
Вообще оверлеи загружаются вместе с документом, но есть способ сделать это программно, когда документ уже загружен.
Document.loadOverlay() - Интерфейсы веб API | MDN
Возьмем пример из предыдущей статьи, где мы создавали контекстное меню, исправляющее ссылки "вконтакте" и реализуем его в виде оверлея.
Кликните здесь для просмотра всего текста
| XML | 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
| <?xml version="1.0" encoding="utf-8" ?>
<overlay xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
<script type="application/x-javascript"><![CDATA[
function correctVkLink_onclick ()
{
var menu = document.getElementById("contentAreaContextMenu");
var ctx = menu.triggerNode;
ctx = ctx.tagName == "A" ? ctx : ctx.parentElement
var startHref = "https://vk.com/away.php?to=";
if (ctx.tagName == "A" && ctx.href.startsWith(startHref))
{
var startLen = startHref.length;
var url = ctx.href.substring(startLen, ctx.href.indexOf("&"));
ctx.href = decodeURIComponent(url);
}
}
var correctVkLink_onPopupShowing = function ()
{
var corItem = document.getElementById("correct_vk_outer_link");
if (corItem)
{
var menu = document.getElementById("contentAreaContextMenu");
var ctx = menu.triggerNode;
ctx = ctx.tagName == "A" ? ctx : ctx.parentElement;
var startHref = "https://vk.com/away.php?to=";
if (ctx.tagName == "A" && ctx.href.startsWith(startHref))
{
corItem.setAttribute("hidden", "false");
}
else
{
corItem.setAttribute("hidden", "true");
}
}
}
]]></script>
<menupopup id="contentAreaContextMenu" onpopupshowing="correctVkLink_onPopupShowing();">
<menuitem label="Исправить ссылку vk" onclick="correctVkLink_onclick ()" id="correct_vk_outer_link"/>
</menupopup>
</overlay> |
|
Сохраняем файл с расширением .xul. Теперь создадим кнопку, для загрузки оверлея.
Кликните здесь для просмотра всего текста
| JavaScript | 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
| function queryFileName() {
const nsIFilePicker = Components.interfaces.nsIFilePicker;
var fp = Components.classes["@mozilla.org/filepicker;1"].createInstance(nsIFilePicker);
fp.init(window, "Dialog Title", nsIFilePicker.modeOpen);
fp.appendFilters(nsIFilePicker.filterAll | nsIFilePicker.filterXML);
var rv = fp.show();
if (rv == nsIFilePicker.returnOK || rv == nsIFilePicker.returnReplace) {
var file = fp.file;
// Get the path as string. Note that you usually won't
// need to work with the string paths.
var path = fp.file.path;
// work with returned nsILocalFile...
return path;
}
}
document.loadOverlay("file:///" + queryFileName(), null); |
|
Жмем кнопку, выбираем только что созданный файл и получаем такой же результат как и раньше.
На этом можно было бы закончить обсуждение вопроса оверлеев , но все-таки, что в этом методе, по моему мнению, не так...
Если речь идет об отдельном расширении, то использовать встроенный функционал - лучше всего. Но в данном случае речь идет о кнопке, поэтому, на мой взгляд, решений, которые подразумевают использование дополнительных файлов, следует избегать. Преимуществом кнопки является то, что в ней содержится весь код, необходимый для ее работы. Если для установки кнопки будет недостаточно перехода по ссылке, а потребуется еще установить в файловую систему какие-то дополнительные файлы, да еще найти способ указать кнопке, где именно эти файлы находятся, то использование кнопки в такой ситуации просто теряет смысл. Это случай для создания отдельного расширения, для которого предусмотрены инструменты установки. В нашем же случае кнопка должна содержать весь код. Вот этим мы сейчас и займемся.
Внедрение оверлея в код кнопки
Сначала я хотел решить эту задачу, используя псевдооверлеи. То есть воспользоваться XML-описанием кнопки, описанным в одной из предыдущих статей, добавить туда блок overley и генерировать код, который будет создавать все элементы, описанные в нем, тем же способом, которым создавалась кнопка-меню. Недостаток этого способа в том, что при его реализации пришлось бы отказаться от некоторых возможностей оверлеев, например от возможности использования скриптов и on-атрибутов. Поэтому способ, который я выбрал - предельно прост: код оверлея будет включен в код кнопки как текст, а для того, чтобы его загрузить, он сначала будет сохраняться во временный файл, из которого его можно будет грузить с помощью document.loadOverlay. Таким образом задача сводится к созданию кнопки(транслятора), которая будет на основе оверлея генерировать код другой кнопки, способной этот оверлей сохранить и загрузить.
Вот код такой кнопки
Кликните здесь для просмотра всего текста
| JavaScript | 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
| function queryFileName() {
const nsIFilePicker = Components.interfaces.nsIFilePicker;
var fp = Components.classes["@mozilla.org/filepicker;1"].createInstance(nsIFilePicker);
fp.init(window, "Dialog Title", nsIFilePicker.modeOpen);
fp.appendFilters(nsIFilePicker.filterAll | nsIFilePicker.filterXML);
var rv = fp.show();
if (rv == nsIFilePicker.returnOK || rv == nsIFilePicker.returnReplace) {
var file = fp.file;
// Get the path as string. Note that you usually won't
// need to work with the string paths.
var path = fp.file.path;
// work with returned nsILocalFile...
return path;
}
}
function ReadTextFromFile(fileName, charset, callback) {
var file = Components.classes["@mozilla.org/file/local;1"].createInstance(Components.interfaces.nsILocalFile);
file.initWithPath(fileName);
Components.utils.import("resource://gre/modules/NetUtil.jsm");
var channel = NetUtil.newChannel(file);
channel.contentType = "application/xml";
NetUtil.asyncFetch(channel, function (inputStream, status) {
callback(NetUtil.readInputStreamToString(inputStream, file.fileSize, { charset: charset }));
});
}
function saveAndLoadOverlay(code)
{
Components.utils.import("resource://gre/modules/FileUtils.jsm");
var file = FileUtils.getFile("TmpD", ["temp_overlay_file.xul"]);
file.createUnique(Components.interfaces.nsIFile.NORMAL_FILE_TYPE, FileUtils.PERMS_FILE);
var foStream = Components.classes["@mozilla.org/network/file-output-stream;1"].
createInstance(Components.interfaces.nsIFileOutputStream);
foStream.init(file, 0x02 | 0x08 | 0x20, 0666, 0);
var converter = Components.classes["@mozilla.org/intl/converter-output-stream;1"].
createInstance(Components.interfaces.nsIConverterOutputStream);
converter.init(foStream, "UTF-8", 0, 0);
converter.writeString(code);
converter.close();
document.loadOverlay("file:///" + file.path, null);
}
ReadTextFromFile(queryFileName(), "utf-8", function (data)
{
var buttonCode = "var overlayCode = " + JSON.stringify(data) + ";" + saveAndLoadOverlay + ";saveAndLoadOverlay(overlayCode);";
gClipboard.write(buttonCode);
}); |
|
Размещаем этот код во вкладке CODE новой кнопки(это и будет транслятор), жмем, в появившемся файл-диалоге выбираем файл оверлея и нужный код оказывается в буфере обмена. Теперь надо создать новую кнопку, разместить на ее вкладке инициализации полученный код из буфера обмена и дело в шляпе. Вот пример кода, который получится из приведенного выше кода оверлея
Кликните здесь для просмотра всего текста
| JavaScript | 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
| var overlayCode = "<?xml version="1.0" encoding="utf-8" ?>\n<overlay xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">\n <script type="application/x-javascript"><![CDATA[\n function correctVkLink_onclick ()\n {\n var menu = document.getElementById("contentAreaContextMenu");\n var ctx = menu.triggerNode;\n ctx = ctx.tagName == "A" ? ctx : ctx.parentElement\n var startHref = "https://vk.com/away.php?to=";\n \n if (ctx.tagName == "A" && ctx.href.startsWith(startHref))\n {\n var startLen = startHref.length;\n var url = ctx.href.substring(startLen, ctx.href.indexOf("&"));\n ctx.href = decodeURIComponent(url);\n }\n }\n \n var correctVkLink_onPopupShowing = function ()\n {\n var corItem = document.getElementById("correct_vk_outer_link");\n if (corItem)\n {\n var menu = document.getElementById("contentAreaContextMenu");\n var ctx = menu.triggerNode;\n ctx = ctx.tagName == "A" ? ctx : ctx.parentElement;\n var startHref = "https://vk.com/away.php?to=";\n \n if (ctx.tagName == "A" && ctx.href.startsWith(startHref))\n {\n corItem.setAttribute("hidden", "false");\n }\n else\n {\n corItem.setAttribute("hidden", "true");\n }\n \n }\n \n }\n ]]></script>\n <menupopup id="contentAreaContextMenu" onpopupshowing="correctVkLink_onPopupShowing();">\n <menuitem label="Исправить ссылку vk" onclick="correctVkLink_onclick ()" id="correct_vk_outer_link"/>\n </menupopup>\n</overlay>";function saveAndLoadOverlay(code)
{
Components.utils.import("resource://gre/modules/FileUtils.jsm");
var file = FileUtils.getFile("TmpD", ["temp_overlay_file.xul"]);
file.createUnique(Components.interfaces.nsIFile.NORMAL_FILE_TYPE, FileUtils.PERMS_FILE);
var foStream = Components.classes["@mozilla.org/network/file-output-stream;1"].
createInstance(Components.interfaces.nsIFileOutputStream);
foStream.init(file, 0x02 | 0x08 | 0x20, 0666, 0);
var converter = Components.classes["@mozilla.org/intl/converter-output-stream;1"].
createInstance(Components.interfaces.nsIConverterOutputStream);
converter.init(foStream, "UTF-8", 0, 0);
converter.writeString(code);
converter.close();
document.loadOverlay("file:///" + file.path, null);
};saveAndLoadOverlay(overlayCode); |
|
Схема XUL
Вопрос не принципиальный, но все-таки при наличии схемы создавать оверлеи было бы намного удобнее. Найти схему XUL-документа на официальном сайте мне не удалось, но когда-то группой энтузиастов создавался проект редактора XUL, насколько я помню проект этот благополучно загнулся, но существовал еще долго в открытом доступе в виде исходников. Там присутствовала схема, которая может и не в точности соответствует формату, но ее вполне можно было использовать после небольшой редактуры. Я ее малость модифицировал, для того, чтобы ее можно было использовать в Visual Studio. Было это довольно давно, так что, не исключено, что в сети можно найти что-то и получше. Но здесь я выложу ту самую схему, о которой говорю, ее вполне можно использовать для создания оверлеев.
Кроме того, есть вот такой проект
XUL Schema
Сам пока не пробовал использовать.
>>
|