Углубленное изучение АПИ плагинов Р7-Офис
Занятие 6
Обзор модульной библиотеки r7module.

Предупреждение
Для понимания большей части изложенной информации в Занятии 6 требуется изучение предыдущих материалов курса «Углубленное изучение АПИ плагинов Р7-Офис».
Введение
Для облегчения создания плагинов в Р7-Офис нами была разработана модульная система, базирующаяся на принципах ООП (объектно-ориентированного программирования) языка javascript. Это стало возможно благодаря тому, что при их создании используются типовые операции, такие как:

• создание модульных окон (модуль r7_window.js);

• диспетчеризация сообщений между потоками модального окна и основного потока плагина(модуль r7_messageHandler.js);

• взаимодействие с внешними источниками данных по протоколам сети интернет (модуль r7_transport.js);

• получение разных данных в выбранных пользователем частях документа(модуль r7_userstate.js);

• использование в плагинах сложных элементов графического интерфейса, таких как деревья(модуль r7_treeUI.js);

• создание своих панелей графического меню в основном окне редакторов(модуль r7_toolbar.js);


Однотипные пользовательские операции спрятаны внутри методов разработанных нами классов JavaScript для Р7-Офис. Вместо многочисленного копи-паста кода из проекта в проект теперь достаточно объявить класс и использовать его методы. Это существенно экономит время на разработку. Помимо этого, снижается вероятность ошибок при программировании.

На занятии будет проведён обзор по базовым возможностям некоторых модулей библиотеки r7module (модульные окна, деревья)
Подключение модульной библиотеки
Перед использованием модульной библиотеки r7module её надо добавить в папку с проектом плагина. После распаковки архива, должна появиться папка модульной библиотеки:
Внутри этой папки содержатся файлы (список актуальный на момент написания данного модуля и может быть изменён). Прежде всего надо добавить в список подключаемых скриптов загрузчик «r7module.js»:
Для этого добавим в HTML файл плагина строку:

<script src="scripts/r7helper/r7module.js" type="text/javascript"></script>

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

Код вызова загрузчика в коде JavaScript для подключения необходимого модуля:

module=moduleR7(name_module);

Обзор модулей
Модули создания модальных окон и диспетчера сообщений («r7_window.js» и «r7_messageHandler.js»)
Примечание: создавать модальные окна имеет смысл только в базовом потоке плагина.
Код подключения модуля r7_window:

let wndr7=moduleR7('windows');

Загружается объект - функция «wndr7», которая используется для упрощения работы по созданию модульных классов. Они создаются через вызов дополнительных функций, с указанием параметров классов. В результате создается готовый объект требуемого класса:

//В аргументе data можно переслать пользовательские данные, для использования в работе созданного окна
let wnd = wndr7.createWindow(window.location,"test","index_wnd1.html",800,600,data);
Аргументы функции createWindow:
• window.location - объект положения файлов плагина;
• "test" - заголовок создаваемого окна;
• "index_wnd1.html" - имя HTML файла с описанием структуры создаваемого окна и указанием кода, выполняющего алгоритмы работы в нём;
• 800,600 ширина и высота создаваемого окна в пикселах;
• data опционально можно передавать какие-то данные, которые могут потребоваться на момент создания окна или при дальнейшем его функционировании (для исключения использования системы сообщений).
Этот объект не создает окно, а подготавливает его создание. Создание окна производится методом wnd.create(). Имеется два варианта использования этого метода:

• wnd.create(messageHandler) - используется функция диспетчера сообщений, передаваемый как аргумент;

• wnd.create() - используется глобальный диспетчер сообщений (он создаётся автоматически, при создании первого же модального окна).

Как описывалось на других занятиях курса, в API плагинов для Р7-Офис используется система передачи сообщений между базовым скриптом плагина и созданными модальными окнами. Разработчики рекомендуют использовать единую функцию. Если вы хотите использовать свою функцию, то её можно передавать в качестве аргумента.
Вот пример такой простой функции:


// функция диспетчер сообщений между окнами плагина и базовым окном
 function messageHandler(modal, message) {               
       switch (message.type) {            
            case "onTest"://Тестовое сообщение            
             /console.log("Диспетчер функция \n type message: " + message.type+"\n wndID: "+modal.id);              
                break;
            case "onCancelMethod"://Метод закрытия модального окна            
                window.Asc.plugin.executeMethod('CloseWindow', [modal.id]);             
                break;  
     }
 }

При использовании нашей модульной библиотеки, создавать такую функцию имеет смысл тогда, когда нужна своя версия обработки какого- то из сообщений. В иных случаях лучше использовать объект – синглтон (в системе создаётся только один такой объект) диспетчера сообщений, генерируемый автоматически при создании первого же модального окна. Сам по себе этот объект не создаётся. Для этого нужно просто ничего не передавать в функцию «create()». Действия диспетчера скрыты в действиях объекта окна. Так регистрация функции обработчика-любого сообщения происходит с помощью такого кода:

wnd.addGlobalMessageHandler("onTest",onTest);

Первый аргумент ("onTest") – имя сообщения, второй – функция-обработчик, вызываемая при передаче сообщения для окна.

Формат функции обработчика:

function onTest(modal, message){ … }

После регистрации сообщений окно выводится с помощью метода wnd.show().

При создании глобального обработчика для каждого окна автоматически создаются обработчики следующих сообщений:

• onCancelMethod - закрытие текущего модального окна;
• onGetWindowID - получение id модального окна;
• onGetWindowUserData - получение пользовательских данных, передаваемых при создании объекта модального окна (параметр data в аргументе wndr7.createWindow())

Обработчики можно использовать в модальных окнах без описания этих сообщений в диспетчере сообщений. Нужно только зарегистрировать сообщения в модальном окне, например:


window.Asc.plugin.attachEvent("onGetWindowUserData",function(data){        
 if(data!=undefined){
   if(typeof(data)==='object'){
	…
  }
}
Для получения требуемых данных в нужном месте кода модального окна вызвается сообщение:

window.Asc.plugin.sendToPlugin("onWindowMessage", {type: "onGetWindowUserData"});

Как описывалось на предыдущих занятиях курса, подобным же способом можно осуществлять передачу данных из основного потока плагина в поток модального окна своими собственными сообщениями.
Модуль дерева («r7_treeUI.js»)
Иерархические структуры данных (например: файловую систему) удобно показывать визуально в виде так называемой древовидной структуры.
В нашей библиотеке применяется jQuery-ui плагин fancytree. О возможностях библиотеки fancytree можно ознакомиться на ее сайте:

https://wwwendt.de/tech/fancytree/demo/index.html

Модуль подгружается следующим образом:

tree_r7= moduleR7('tree_fancy');

Примечание: Поскольку это jQuery-ui плагин, то для работы этого модуля, нужно использовать загрузку следующих JS модулей:

<script src="../v1/plugins.js" type="text/javascript" ></script>
<script src="../v1/plugins-ui.js" type="text/javascript"></script>
<script src="scripts/jquery-3.7.0.min.js"></script>  
<script src="scripts/jquery.fancytree-all-deps.min.js"></script>
Код создания объекта, работающего с таким деревом:

let tree = tree_r7.createTreeFancy("#tree_space");

В качестве аргумента нужно передавать описатель (чаще всего id) контейнера (обычно это <div>) в составе DOM модели HTML документа, в котором будет располагаться наше дерево.
Структура дерева может быть загружена сразу, либо будет добавляться динамически.

Описание структуры, которая показана на выше:

let source=[ {title: "n1", expanded: true, type:"mssql",children: [
                  {title: "n1.1", type:"mysql"},
                  {title: "n1.2"},
                  {title: "n1.3",idForLazy:"aab", lazy: true,children: [
                    {title: "n3.1", type:"mysql"},
                    {title: "n3.2",idForLazy:"abb",lazy:true, children: [
                        {title: "n4.1"},
                        {title: "n4.2"},
                        {title: "n4.3"},
                      ]},
                    {title: "n3.3"},
                  ]},
                ]},];
Структура передается, если при создании дерева необходимо отобразить полную структуру дерева или её часть. В этом модуле можно назначать свои обработчики событий, которые предусмотрены для исходной библиотеки fancytree:

• activate - выбор элемента;
• click - клик;
• dblclick - двойной клик кнопки мыши;
• lazyLoad - отложенная загрузка узла;
• select – выделение;
• beforeSelect - действие перед выделением.

Можно добавить обработчики:

tree.activateHandler=function(){};
tree.onDoubleClick=onDoubleClick;

В библиотеке предусмотрены другие свойства, доступные в fancytree. Рассмотрение этих свойств в наш курс не входит, и может быть исследовано самостоятельно.

После добавления всех необходимых предварительных определений данных и обработчиков, создается дерево:

tree.createTreeFromArray(source,2);

Первый аргумент - массив с описанием структуры, второй - уровень вложенности, который следует использовать при создании данного дерева из массива. Он нужен для ускорения создания дерева. Может оказаться, что дерево используется для создания отображения структуры данных, со многими сотнями, а то и тысячами узлами. В таком случае, даже если эти узлы являются глубоко вложенными, сама по себе структура данных DOM элементов для отображения всех узлов и листьев дерева по умолчанию будет создаваться. Для больших структур это может занимать много времени. Поэтому, можно ограничиться созданием дерева только до определённого уровня вложенности. А остальные узлы и листья, находящиеся на более глубоком уровне, будут подгружаться из массива данных (или вообще из внешней среды, если используется ссылка) по мере того, как пользователь будет углубляться в иерархию дерева всё глубже и глубже. В этом случае применяется вложенность уровня два. Всю работу по созданию и обработке структур и загрузке их по мере необходимости берет на себя модульная библиотека!

Дополнительно реализованы методы, которых нет в исходной библиотеке, а именно, возможности динамически с помощью пользовательских действий добавлять и удалять отдельные узлы и отдельные ветки.

Пример кода:

$("#addNode").click(function(){
                var nodeData={title: "n2.2", type:"mssql"};
                node=tree.selectedNode;
                tree.addTreeNode(nodeData,node);                
            });
            
            $("#addBranch").click(function(){                
                node=tree.selectedNode;
                tree.addTreeBranch(source,node);                
            });

            $("#removeNode").click(function(){
                node=tree.selectedNode;
                if(node!=null||node!=undefined)
                    tree.removeTreeNode(node);                                                      
            });

            $("#removeBranch").click(function(){
                node=tree.selectedNode;
                if(node!=null||node!=undefined)
                    tree.removeTreeBranch(node);                                                        
            });
Заключение:
Модульная библиотека позволяет упростить и ускорить разработку плагинов под Р7-Офис. Библиотека постоянно развивается по мере решения задач нашей командой. Более подробно библиотека r7module рассматривается в отдельном курсе.