Форум программистов, компьютерный форум, киберфорум
Наши страницы
diadiavova
Войти
Регистрация
Восстановить пароль
Оценить эту запись

CustomButtons часть 4-я. Добавим оверлеи в кнопку

Запись от diadiavova размещена 06.03.2016 в 00:35
Обновил(-а) diadiavova 12.04.2016 в 15:39

  1. Оверлеи
  2. Внедрение оверлея в код кнопки
  3. Схема 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
Сам пока не пробовал использовать.

>>
Вложения
Тип файла: rar xul.rar (9.1 Кб, 96 просмотров)
Размещено в CustomButtons
Просмотров 629 Комментарии 0
Всего комментариев 0
Комментарии
 
КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin® Version 3.8.9
Copyright ©2000 - 2019, vBulletin Solutions, Inc.
Рейтинг@Mail.ru