Оверлеи
Документация по оверлеям находится здесь.
XUL Overlays - Mozilla | MDN
В двух словах: оверлей - это фрагмент XUL-кода интерфейса, который располагается в отдельном файле, но подгружается при загрузке отдельного окна. При этом его код, добавляется в элементы окна. Сам документ с корневым элементом overlay заполняется другими XUL-элементами, причем эти элементы должны иметь id такой, чтобы в основном документе также присутствовал такой же элемент с таким же id. При загрузке оверлея, содержимое такого элемента будет заполнено содержимым такого же элемента из оверлея. Иными словами: если нам надо добавить элемент в контекстное меню, как в предыдущей статье, то мы создаем оверлее <menupopup id="contenAreaContextMenu"> и вставляем все что нужно туда.
Вообще оверлеи загружаются вместе с документом, но есть способ сделать это программно, когда документ уже загружен.
Document.loadOverlay() - Интерфейсы веб API | MDN
Возьмем пример из предыдущей статьи, где мы создавали контекстное меню, исправляющее ссылки "вконтакте" и реализуем его в виде оверлея.
Код xml | Выделить |
<?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>
Код javascript | Выделить |
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 | Выделить |
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);
});
Код javascript | Выделить |
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 Schema
Нельзя сказать, что это полноценная схема, но она вполне юзабельна.
Комментариев нет :
Отправить комментарий