пятница, 8 апреля 2016 г.

CustomButtons часть 5-я. Добавим интерактивности.

  1. Панели
  2. Вывод данных в виде HTML-документа
  3. Модальные диалоги и наблюдатели
Многие задачи автоматизации требуют либо вывода какой-то информации пользователю, либо предоставления ему возможности ввести какие-то данные или выбрать параметры выполнения. Самые простые задачи такого типа могут быть решены с помощью функций alert, confirm и prompt. А вот как быть, если этих простых окон недостаточно? Конечно, еще можно воспользоваться функцией open, но она открывает HTML-файл, существующий в сети или файловой системе, а как я уже говорил ранее, использовать кнопки, к которым прилагаются какие-то дополнительные файлы - идея не очень хорошая. Вот о том, как можно справиться с этими проблемами мы и поговорим.

Панели

XUL-элемент panel можно использовать и как диалоговое окно и как окно сообщений. Панель может содержать любой XUL-код и таким образом в ней можно реализовать любой интерфейс. Для тех, кто хорошо знаком с компановкой XUL-интерфейсов в принципе больше ничего и не надо, поскольку тут имеются полноценные элементы управления практически на любой вкус. Единственное чего не получится реализовать с помощью панелей - это модальные окна, поскольку они должны быть полноценными окнами.

Об использовании панелей можно почитать здесь
Panels - Mozilla | MDN
и здесь
Floating Panels - Mozilla | MDN
В двух словах: панель - это разновидность popup-элемента, наряду с menupopup, то есть она появляется "где скажут" и исчезает при потере фокуса. Но это поведение можно изменить, установив значение атрибута noautohide="true". Так же, установив значения других атрибутов можно настроить другие аспекты поведения панели: задать ей титлбар(зделав ее подобием окна), разместить на титлбаре кнопку закрытия, сделать так, чтобы панель можно было передвигать мышкой и т. д. В простейшем случае панель можно привязать к какому-то элементу интерфейса (кнопке, меню) и она будет появляться также, как появляется menupopup в районе этого элемента (где именно тоже можно указать явно). Можно также открывать панель программно с помощью методов openPopup и openPopupAtScreen.

Теперь для наглядности создадим простейшую панель. Создавать ее будем с помощью оверлея следующего содержания.
Кликните здесь для просмотра всего текста
Код 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 showValues()

  {

    var tb1 = document.getElementById("tb1");

    var tb2 = document.getElementById("tb2");

    alert("Text1 = " + tb1.value + ";\nText2 = " + tb2.value + ";");

  }

  ]]>

  </script>

  <popupset id="mainPopupSet">

    <panel id="myTestPanel">

      <hbox>

        <label>Text1</label>

        <textbox id="tb1"/>

      </hbox>

      <hbox>

        <label>Text2</label>

        <textbox id="tb2"/>

      </hbox>

      <button label="Показать значения" id="showValues" oncommand="showValues();"/>

    </panel>

  </popupset>

</overlay>

Загрузим оверлей с помощью ранее созданной кнопки Загрузить оверлей и для того, чтобы открыть панель после загрузки оверлея мы создадим кнопку, во вкладке Инициализация которой разместим следующий код
Код javascript Выделить

this.setAttribute("popup", "myTestPanel");

Далее при нажатии этой кнопки возле нее будет появляться наша панель. Введем в текстовые поля какой-нибудь текст и нажмем кнопку на панели, в результате чего появится окно сообщений, в котором будет показано какой текст был введен в поля панели. Атрибут popup должен иметь значение атрибута id того popup, который ассоциирован с этим элементом (в данном случае кнопкой). Кроме того, этот атрибут можно использовать и для создания меню кнопки, то есть в оверлее можно разместить menupopup, а в кнопке просто сослаться на его id, тогда не понадобится все, о чем я писал в статье о создании кнопок-меню.

Если нужно, чтобы панель появлялась в произвольном месте экрана, тогда в коде кнопки надо на вкладке Код поместить примерно такой код
Код javascript Выделить

document.getElementById("myTestPanel").openPopupAtScreen(300, 200, false);

300 и 200 - это координаты, где появится панель. Естественно, использовать атрибут popup в этом случае не нужно.

Вывод данных в виде HTML-документа



Пример, в котором может понадобиться собрать какую-то информацию на странице и представить ее совсем в другом виде можно посмотреть здесь. Наиболее очевидным решением этой задачи является сохранение сгенерированного содержимого во временный файл с последующим открытием его с помощью функции open. Как-то так
Кликните здесь для просмотра всего текста
Код javascript Выделить

function saveTextToTempFile(text)

{

    Components.utils.import("resource://gre/modules/FileUtils.jsm");

 

    var file = FileUtils.getFile("TmpD", ["temp_html_file.html"]);

    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(text);

    converter.close();

    return file.path;

}

var html = "<meta charset='utf-8'/><marquee style='color:red;font-size:30px;'>Привет</marquee>";

open("file:///" + saveTextToTempFile(html));

Этот код прекрасно работает, тем не менее рассмотрим и другие варианты, которые не предполагают сохранения документа в файл.

В примере со скачиванием медиафайлов из вконтактика сохранение во временный файл вообще было невозможно, поскольку делалось это в GreaseMonkey, там такой возможности нет (ну, или я о ней не знаю). Там открывалась пустая страница about:blank и в нее добавлялось содержимое программно. Проблема здесь в том, что если написать что-то-вроде
Код javascript Выделить

var newwin = open("about:blank");

newwin.document.body.innerHTML = htmlCode;

то это не сработает из-за того, что вновь открывшееся окно загружается не сразу, а ему для этого нужно какое-то время. В то же время обращение элементу body нового документа происходит сразу после выполнения open, а в это время тело документа еще не успевает создаться и мы получаем ошибку. Поэтому решается этот вопрос тем, что весь код инициализации мы пишем в обработчике события load окна. Так
Код javascript Выделить

var newwin = open("about:blank");

newwin.onload = function (evt)

{

    newwin.document.body.innerHTML = htmlCode;

}

или так
Код javascript Выделить

var newwin = open("about:blank");

newwin.addEventListener("load", function (evt)

{

    newwin.document.body.innerHTML = htmlCode;

}, false);

В CustomButtons работают оба варианта, в букмарклетах - ЕМНИП, только второй, а в GreaseMonke раньше это работало, потом перестало. Так же есть еще вариант, который по идее должен работать везде
Код javascript Выделить

var newwin = open("about:blank");

newwin.document.write(htmlCode);

newwin.document.close();

Возможно это лучший вариант, в GreaseMonkey я использовал именно его.

Модальные диалоги и наблюдатели



Открытие нового окна в режиме диалога осуществляется либо с помощью функции openDialog, либо open, но в параметрах надо указать, что это диалог. Для того, чтобы сделать окно модальным, следует в параметрах указать modal. Подробно о параметрах open можно прочитать здесь
Window.open() - Web APIs | MDN

Для того, чтобы загрузить окно из файловой системы, можно так же как и с окном вывода данных просто сохранить его код во временный файл и загрузить оттуда. Делается это так же, только окно открывается как модальный диалог.
Код javascript Выделить

openDialog(tempFileName, dialogName, "chrome,centerscreen,modal,width=500,height=300");

Window.openDialog() - Web APIs | MDN

А вот для того, чтобы сделать то же самое, ничего не сохраняя в файловую систему, а просто загрузив about:blank и сформировав содержимое в уже открытом окне, придется немножко "поколдовать". Проблема модального окна в том, что весь код, написанный после вызова open или openDialog, выполняться не будет пока окно не будет закрыто, а стало быть в этой области нет никакого смысла писать код, генерирующий содержимое окна.

Пролить свет на то, как можно генерировать содержимое модального окна "на лету" нам помогут следующие топики документации
nsIObserverService - Mozilla | MDN
nsIObserver - Mozilla | MDN
Observer Notifications | MDN

Для того, чтобы выполнить код во вновь появившемся окне мы создадим наблюдателя. Наблюдатель - это объект, содержащий метод observe, как описано здесь
nsIObserver - Mozilla | MDN. Кроме того, как показано в примере, полезно добавить в него методы для регистрации и разрегистрации наблюдателя. Регистрация заключается в создании экземпляра службы наблюдателей и передаче экземпляра наблюдателя
методу addOserver этой службы. Разрегистрация - в передаче этого же объекта методу removeObserver. Кроме того, нам потребуется передать аргумент параметру aTopic, это строка, указывающая какое именно глобальное событие мы хотим отслеживать. Список системных событий можно найти здесь Observer Notifications | MDN. Нас интересует toplevel-window-ready из этого списка. Таким образом шаблон нашего наблюдателя будет выглядеть так
Кликните здесь для просмотра всего текста
Код javascript Выделить

var observer =

      {

          observe: function (subject, topic, data)

          {

              // Здесь код обработки

          },

          register: function ()

          {

              var observerService = Components.classes["@mozilla.org/observer-service;1"]

                                    .getService(Components.interfaces.nsIObserverService);

              observerService.addObserver(this, "toplevel-window-ready", false);

          },

          unregister: function ()

          {

              var observerService = Components.classes["@mozilla.org/observer-service;1"]

                                      .getService(Components.interfaces.nsIObserverService);

              observerService.removeObserver(this, "toplevel-window-ready");

 

          }

      }


Теперь создадим код нашего диалогового окна.
Кликните здесь для просмотра всего текста
Код HTML Выделить

<!DOCTYPE html>

 

<html lang="en" xmlns="http://www.w3.org/1999/xhtml">

<head>

    <meta charset="utf-8" />

    <title>Пример модального диалога</title>

</head>

<body>

    <table>

        <tr>

            <td>Текст 1</td>

            <td><input type="text" id="txt1" /></td>

        </tr>

        <tr>

            <td>Текст 2</td>

            <td><input type="text" id="txt2" /></td>

        </tr>

    </table>

    <button id="btnReady">Готово</button>

    <script>

        var args = window.arguments[0];

        document.getElementById("txt1").value = args.text1;

        document.getElementById("txt2").value = args.text2;

        document.getElementById("btnReady").addEventListener("click", function (evt)

        {

            args.text1 = document.getElementById("txt1").value;

            args.text2 = document.getElementById("txt2").value;

            close();

        }, false);

 

    </script>

</body>

</html>

Окно простое, в нем табличка с двумя текстовыми полями надписями и кнопкой "Готово". При загрузке окна считываются аргументы и их значения вставляются в текстовые поля. Аргументы будем передавать при вызове окна. Далее можно будет ввести в текстовые поля другой текст, нажать кнопку и после закрытия окна можно будет из аргументов прочитать введенные пользователем значения( мы их выведем с помощью алерта). Для того чтобы можно было любой текст вставить в кнопку как строковую переменную мы создадим специальную кнопку, при нажатии которой текст в буфере обмена будет заменен на строковую переменную соответствующую этому тексту. Код такой кнопки предельно прост.
Код javascript Выделить
gClipboard.write(JSON.stringify(gClipboard.read()));
Создать js-строку из текста в буфере обмена

Функция преобразования текста в js-строку есть в некоторых редакторах кода или расширениях к ним, так что можно просто воспользоваться готовым инструментом.

Теперь собственно код кнопки, использующей это окно
Кликните здесь для просмотра всего текста
Код javascript Выделить

/*CODE*/

var dlg = "<!DOCTYPE html>\n\n<html lang=\"en\" xmlns=\"http://www.w3.org/1999/xhtml\">\n<head>\n    <meta charset=\"utf-8\" />\n    <title>Пример модального диалога</title>\n</head>\n<body>\n    <table>\n        <tr>\n            <td>Текст 1</td>\n            <td><input type=\"text\" id=\"txt1\" /></td>\n        </tr>\n        <tr>\n            <td>Текст 2</td>\n            <td><input type=\"text\" id=\"txt2\" /></td>\n        </tr>\n    </table>\n    <button id=\"btnReady\">Готово</button>\n    <script>\n        var args = window.arguments[0];\n        document.getElementById(\"txt1\").value = args.text1;\n        document.getElementById(\"txt2\").value = args.text2;\n        document.getElementById(\"btnReady\").addEventListener(\"click\", function (evt)\n        {\n            args.text1 = document.getElementById(\"txt1\").value;\n            args.text2 = document.getElementById(\"txt2\").value;\n            close();\n        }, false);\n\n    </script>\n</body>\n</html>";

var observer =

{

    observe: function (subject, topic, data)

    {

        if (subject.name == "myTestDialog")

        {

            subject.document.write(dlg);

            subject.document.close();

        }

    },

    register: function ()

    {

        var observerService = Components.classes["@mozilla.org/observer-service;1"]

                              .getService(Components.interfaces.nsIObserverService);

        observerService.addObserver(this, "toplevel-window-ready", false);

    },

    unregister: function ()

    {

        var observerService = Components.classes["@mozilla.org/observer-service;1"]

                                .getService(Components.interfaces.nsIObserverService);

        observerService.removeObserver(this, "toplevel-window-ready");

 

    }

}

 

observer.register();

var args = { text1: "Текст первого поля", text2: "Текст второго поля" };

openDialog("about:blank", "myTestDialog", "chrome,centerscreen,modal,width=300,height=200", args);

observer.unregister();

alert("Текст 1: " + args.text1 + "\nТекст 2: " + args.text2);

Показать модальный диалог

Учитывая предварительные объяснения, думаю, разобраться в этом коде будет несложно.
Вопросы работы с окнами из хром-кода неплохо освещены в следующей статье документации
Working with windows in chrome code | MDN

Комментариев нет :

Отправить комментарий