SVGTreeViewer — плагин для SVGTree

Библиотека SVGTree, о которой шла речь в предыдущем посте, позволяет рисовать красивые деревья, однако для удобства применения ей недостает интерактивности. Не помешала бы функциональность, позволяющая пользователям обращаться с созданными деревьями в соответствии со следующими сценарии использования:

  • создание и удаление вершин дерева при помощи более удобных средств, чем кнопки Insert и Delete (в мобильных устройствах этих кнопок попросту нет);
  • настройка внешнего вида дерева (например, формы вершин и ребер);
  • просмотр «исходного кода» дерева в Newick-формате и, возможно, его непосредственное редактирование (по аналогии с тем, как HTML-редакторы вроде TinyMCE предоставляют возможность редактировать исходный код HTML);
  • откат / повторение произведенных изменений (undo / redo).

Поскольку в неинтерактивных случаях использования базовой функциональности библиотеки SVGTree вполне достаточно, новую функциональность логично выделить в отдельный компонент — SVGTreeViewer, зависящий от базовой библиотеки. Компонент представляет собой JavaScript-файл и таблицу стилей, которые в сжатом виде занимают около 15 килобайт.

Базовые примеры использования компонента приведены на этой веб-странице, еще пара есть под катом.


Например, пример, приведенный в описании алгоритмов на деревьях будет выглядеть так:

Виджет SVGTreeViewer для дерева ((,A,)B,((C,D),E),(F,G))H;

Виджеты создаются при помощи метода SVGTreeViewer.

В качестве параметров при создании виджета можно указывать все параметры, используемые для отображения деревьев (например, interaction), а также дополнительные опции, которые относятся непосредственно к виджету (например, undo — опция, определяющая, стоит ли включать функциональность undo / redo).

Вот еще один пример с возможностью редактирования:

Виджет SVGTreeViewer с возможностью редактирования

Частные классы

В отличие от самой библиотеки SVGTree, виджеты создаются при помощи функций, а не конструктора. Этот шаблон проектирования соответствует случаю, когда нет нужды объявлять интерфейс для программного взаимодействия с объектом; в терминах объектно-ориентированного программирования, у объекта отсутствуют публичные методы и поля. Частные поля и функции реализуются в соответствии с шаблоном реализуются в виде внутренних по отношению к глобальной функции переменных:

Это же определение в виде традиционного класса:

Шаблон проектирования в чем-то напоминает чистые функции из функциональных языков программирования: в отличие от традиционных классов, объекты, созданные при помощи частных функций, не обладают состоянием, видимым постороннему наблюдателю. При необходимости ничто не мешает функции PrivateClass из листинга выше возвращать какое-то значение, позволяющее ограниченно влиять на состояние класса.

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

Undo / Redo

Функциональность отмены / повтора действий в JavaScript логично реализовать в виде шаблона проектирования «типаж», добавляя требуемую функциональность к каждому объекту, вызывая для него определенный метод.

By convention предполагается, что объект реализует базовые методы для хранения состояния:

  • метод getState возвращает текущее состояние объекта (например, сериализованное в виде строки);
  • метод setState устанавливает состояние объекта на основе данных, возвращенных методом getState;
  • событие onchange вызывается каждый раз при изменении состояния объекта.

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

Возникает вопрос: возможно ли изменить код функции UndoFixture, чтобы она могла патчить не отдельные объекты, а классы? Можно заметить, что вызов  UndoFixture(Foo.prototype)  приводит к «почти правильным» результатам. Единственная неувязка заключается в инициализации, так как создание свойств для прототипа — неправильное поведение (свойства должны создаваться в отдельности для каждого объекта). Поскольку конструкторы классов в JavaScript нельзя переопределить (т.е. вызов new Foo()  в любом случае вызывает функцию Foo), то решение состоит в небольшом изменении семантики функции:

Новая функция будет создавать класс с добавленными методами:

При этом оба определения можно объединить в одну функцию, действующую в зависимости от типа аргумента:

Добавить комментарий

Ваш e-mail не будет опубликован. Обязательные поля помечены *