Графическое дерево JavaScript с Layout

ОГЛАВЛЕНИЕ

Компонента Tree (Дерево) в JavaScript, которая реализует алгоритм разметки J.Q.Walker II .

Рисунок 1. Пример с конкретными цветами, обработанными при помощи VML в Internet Explorer.

Рисунок 2. Пример с конкретными цветами, обработанными при помощи холста (canvas) в Firefox.

Рисунок 3. Пример, демонстрирующий свернутые и выбранные узлы, обработанные посредством VML в Internet Explorer.

Введение

Данная статья представляет компоненту в JavaScript, которая обрабатывает дерево используя VML (Vector Markup Language) в обозревателе Internet Explorer 6+ или элемент <canvas> в Firefox 1.5+.

Существует множество доступных элементов дерева в JavaScript, и большинство из них имеют лучшую совместимость среди обозревателей, работают быстрее и более оптимизированы. Целью данного кода является предоставление реализации клиентской стороны алгоритма разметки Walker, не имея при этом оптимизаций кода. Вкратце, пример - лучше, чем просто абстрактный алгоритм.

Одним из классических компонент представлений дерева JavaScript является дерево Geir Landr: невероятно и просто. Также есть компоненты горизонтальных деревьев навигации JavaScript, основанных на DTree, которые используют HTML-таблицы для обработки дерева в горизонтальном направлении. Множество функций примера данной статьи копируют те, что представлены в таких компонентах.

 


 

 

Возможности

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

  • Узлы могут быть любого размера и цвета. Поиграв с кодом, вы можете адаптировать их по вашему желанию.
  • Узлы могут иметь заголовки и скрытые метаданные, связанные с ними.
  • Узлами выделения являются: множественная выборка, единая выборка и без выборки.
  • Связи между узлами могут быть представлены прямыми линиями (Манхэттен) или кривыми Безье.
  • Разметка дерева может быть направлена сверху вниз, слева направо и обратно.
  • Узлы могут иметь связанную с ними гиперссылку.
  • Поддеревья могут быть свернуты и развернуты по желанию.
  • Поиск по узлам может быть выполнен на основе заголовка и метаданных.

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

 


 

 

Основы

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

  1. Узлы на одном уровне выровнены по прямой, а оси всех уровней параллельны.
  2. Родитель должен быть расположен центрально относительно своих дочерних элементов.
  3. Дерево и то же дерево, указанное в обратном порядке, может порождать разметку, которая является зеркальной относительно друг друга. Поддерево должно быть обработано одинаково независимо от того, где оно появляется в основном дереве.

Цель базового алгоритма по отношению к своим предшественникам заключалась в предоставлении решения, которое полностью удовлетворяло третье правило, соответственно располагая поддеревья относительно своих больших поддеревьев.

Рисунок 4. Пример, демонстрирующий разницу между принципами распределения алгоритмов.

Основными принципами алгоритма являются:

  • Обращение с каждым поддеревом как с независимой единицей, а передвижение узла предполагает передвижение всех его дочерних элементов.
  • Это выполнимо, если использовать предварительные координаты и модификатор для каждого узла. Передвижение узла подразумевает увеличение/уменьшение его предварительных координат и его модификатора, но только модификатор будет иметь значение для финальной позиции дочерних элементов узла. Тем самым, передвижение поддерева означает изменение модификатора корневого поддерева. Финальная позиция узла вычисляется путем суммирования предварительных координат узла со всеми модификаторами дочерних элементов.

Алгоритм работает в два этапа. Первый этап (firstWalk) является обходом в обратном порядке. Различные поддеревья обрабатываются рекурсивно со дна вверх и слева направо, позиционируя независимые элементы, образующие каждое поддерево, до того, как ни один из них не будет касаться другого. По мере обхода, меньшие поддеревья комбинируются, формируя большие поддеревья до того, как они достигнут корня. В этом процессе, apportion расположит на равном расстоянии более мелкие поддеревья, которые могли плавать между двумя большими связанными поддеревьями (для удовлетворенья 3-го правила о симметрии). Второй проход (secondWalk) является обходом в обратном порядке, который подсчитывает позицию конечного узла путем добавления всех модификаторов предшественников к предварительным координатам каждого узла.

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

Алгоритм также подстраивает подсчеты в случае, если разметка будет выполнена в другом направлении (то есть снизу вверх). Различные использования могут потребовать различные виды. Обычно традиционное расположение сверху вниз означает власть - к примеру, иерархию в организациях; при этом разметка снизу означает эволюцию или рост, как в биологии; а слева направо разметка может означать временную эволюцию.

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

 


 

 

Использование кода

Краткое руководство по установке

А теперь давайте создадим пример (все примеры доступны для загрузки) для того, чтобы понять способ рисования простого дерева. После этого, имея справку по программному интерфейсу и изучив код усовершенствованных примеров, вы можете полностью понять, как именно использовать данную компоненту.

Во-первых, вы должны включить скрипт компоненты и связать таблицу стилей в секции <head> вашей HTML-страницы (не забудьте установить пути перед установкой):

<head>
    <!-- Место для содержимого... -->
    <script type="text/javascript" src="/ECOTree.js"></script>
    <link type="text/css" rel="style-sheet" href="/ECOTree.css" />
    <!-- Место для содержимого... -->
</head>

Кроме этого, если вы используете обозреватель Internet Explorer, то вам необходимо добавить следующие строки в секцию <head> вашей HTML-страницы для правильной обработки VML:

<head>
    <!-- Место для содержимого... -->
    <xml:namespace ns="urn:schemas-microsoft-com:vml" prefix="v"/>
    <style>v\:*{ behavior:url(#default#VML);}</style>         
    <!-- Место для содержимого... -->
</head>

В вашей HTML-странице вы должны расположить блочный контейнер для дерева, как <div> с идентификатором (ID), который вы должны предоставить конструктору дерева. Поэтому вам стоит включить блок <script> для создания самого дерева, добавить другие узлы - наверняка из базы данных реальных проектов - а затем нарисовать дерево. Давайте изучим пример:

<div id="myTreeContainer"></div>
var myTree = new ECOTree("myTree","myTreeContainer");
myTree.add(0,-1,"Apex Node");
myTree.add(1,0,"Left Child");
myTree.add(2,0,"Right Child");
myTree.UpdateTree();

А вот результат :

Рисунок 5. Простой пример 1.

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

Учтите, что компонента имеет стандартные значения практически для всего, тем самым те пять строк кода могут создать дерево, которое может быть собрано и расширено, и вы можете выбрать/отменить выбор узлов просто щелкнув по ним. Также, каждый узел по стандарту имеет гиперссылку Javascript:void(0);. Практически каждый раз вам будет необходимо изменить вид и поведение дерева для того, чтобы вам было удобнее им пользоваться. Это может быть выполнено при помощи экземпляра config дерева. Давайте модифицируем предыдущий пример путем добавления нескольких строк:

var myTree = new ECOTree("myTree","myTreeContainer");    
myTree.config.linkType = 'B';
myTree.config.iRootOrientation = ECOTree.RO_BOTTOM;                        
myTree.config.topYAdjustment = -160;
myTree.config.linkColor = "black";
myTree.config.nodeColor = "#FFAAAA";
myTree.config.nodeBorderColor = "black";
myTree.config.useTarget = false;
myTree.config.selectMode = ECOTree.SL_SINGLE;
myTree.add(0,-1,"Apex Node");
myTree.add(1,0,"Left Child");
myTree.add(2,0,"Right Child");
myTree.UpdateTree();

С такиими незначительными изменениями дерево будет выглядеть следующим образом:

Рисунок 6. Простой пример 2.

На этот раз узлы не обладают гиперссылками (useTarget = false) и вы можете выбрать за один раз только по одному узлу (selectMode = ECOTree.SL_SINGLE).

Теперь, когда основы были изучены, мы можем изучить все возможности.

 


 

 

Справка по программному интерфейсу (API)

Конфигурация ECOTree

А вот параметры конфигурации с их стандартными значениями:

this.config = {
    iMaxDepth : 100,
    iLevelSeparation : 40,
    iSiblingSeparation : 40,
    iSubtreeSeparation : 80,
    iRootOrientation : ECOTree.RO_TOP,
    iNodeJustification : ECOTree.NJ_TOP,
    topXAdjustment : 0,
    topYAdjustment : 0,
    render : "AUTO",
    linkType : "M",
    linkColor : "blue",
    nodeColor : "#CCCCFF",
    nodeFill : ECOTree.NF_GRADIENT,
    nodeBorderColor : "blue",
    nodeSelColor : "#FFFFCC",
    levelColors : ["#5555FF","#8888FF","#AAAAFF","#CCCCFF"],
    levelBorderColors : ["#5555FF","#8888FF","#AAAAFF","#CCCCFF"],
    colorStyle : ECOTree.CS_NODE,
    useTarget : true,
    searchMode : ECOTree.SM_DSC,
    selectMode : ECOTree.SL_MULTIPLE,
    defaultNodeWidth : 80,
    defaultNodeHeight : 40,
    defaultTarget : 'Javascript:void(0);',
    expandedImage : './img/less.gif',
    collapsedImage : './img/plus.gif',
    transImage : './img/trans.gif'
}
  • iMaxDepth: максимальное число уровней, разрешенное для дерева. Установите данное свойство на одно значение выше, чем необходимое число уровней.
  • iLevelSeparation: расстояние между уровнями в пикселях. Заметьте, что размер уровня будет задан посредством максимального размера узла на данном уровне.
  • iSiblingSeparation: минимальное расстояние в пикселях между узлами одного уровня.
  • iSubtreeSeparation: минимальное расстояние в пикселях между соседними узлами (которые не имеют общего родителя).
  • iRootOrientation: направление разметки. Возможными значениями могут быть:
    • ECOTree.RO_TOP: сверху вниз.
    • ECOTree.RO_BOTTOM: снизу вверх.
    • ECOTree.RO_RIGHT: справа налево.
    • ECOTree.RO_LEFT: слева направо.
  • iNodeJustification: выравнивание узлов, которые принадлежат одному уровню. Возможными значениями могут быть:
    • ECOTree.NJ_TOP: выравнивание по верхнему краю.
    • ECOTree.NJ_CENTER: выравнивание по центру.
    • ECOTree.NJ_BOTTOM: выравнивание по низу.
  • topXAdjustment: горизонтальный отступ в пикселях, который задан для корневого узла. Используйте его для центрирования/смещения дерева на экране.
  • topYAdjustment: вертикальный отступ в пикселях, который задан для корневого узла. Используйте его для центрирования/смещения дерева на экране.
  • render: тип обработки. Возможными значениями могут быть:
    • "AUTO": автоматическая - это стандартное значение. Код автоматически определит, если используется обозреватель Internet Explorer 6+ или Firefox, а тип обработки будет установлен соответственно в "VML""CANVAS". или
    • "VML": Vector Markup Language - векторный язык разметки только для Internet Explorer. Если вы будете использовать его, то помните, что необходимо добавить пространство имен XML namespace и <style> в область head , как указано в статье.
    • "CANVAS": обработка будет основана на HTML-элементе <canvas>. Возможна только для Firefox 1.5+. (Firefox 2.0 все же быстрее).
  • linkType: стиль ссылок узлов. Возможными значениями могут быть:
    • "M": Манхэтен. Классический стиль - ссылки отображены прямыми линиями.
    • "B": Безье. Ссылки представлены кривыми Безье. Иногда, такой стиль более пригоден.
  • linkColor: цвет, используемый для строк ссылок. Подходит любой цвет, который может быть выражен в HTML.
  • nodeColor: цвет, используемый для узлов. Подходит любой цвет, который может быть выражен в HTML.
  • nodeFill: стиль заполнения узлов. Возможными значениями могут быть:
    • ECOTree.NF_GRADIENT: узлы будут заполнены градиентом от цвета узла до светло серого ("whitesmoke" - F5,F5,F5\245,245,245).
    • ECOTree.NF_FLAT: узлы будут иметь монолитное заполнение.
  • nodeBorderColor: цвет используется для границ узлов. Подходит любой цвет, который может быть выражен в HTML.
  • nodeSelColor: цвет используется для выбранных узлов. Подходит любой цвет, который может быть выражен в HTML.
  • levelColors: массив в Javascript с цветами для последующих уровней. Применяется в случае, если опция colorStyle установлена на использование цветов уровней. Узлы на первом уровне дерева будут иметь первый цвет из данного массива, на втором уровне - второй, и т.д. Если уровней будет больше, чем элементов в массиве, то последующие уровни будут заново повторять цвета по кругу. Подходит любой цвет, который может быть выражен в HTM.
  • levelBorderColors: тоже самое, что и предыдущее, но для цвета границ.
  • colorStyle: указывает то, как высчитываются цвета узлов. Возможными значениями могут быть:
    • ECOTree.CS_NODE: каждый узел будет использовать свой цвет для обработки.
    • ECOTree.CS_LEVEL: узлы будут иметь различные цвета, в зависимости от уровня узла в дереве, игнорируя свои цвета.
  • useTarget: будут ли узлы представлять собой ссылки или нет. Возможными значениями могут быть true или false
  • searchMode: выражает то, как будет выполнен поиск (смотрите API относительно searchNodes). Возможными значениями могут быть:
    • ECOTree.SM_DSC: поиск будет выполнен в пределах заголовков узлов.
    • ECOTree.SM_META: поиск будет выполнен в пределах метаданных узлов.
    • ECOTree.SM_BOTH: поиск будет выполнен в пределах обоих значений.
  • selectMode: указывает поведение выборки в дереве. Возможными значениями могут быть:
    • ECOTree.SL_MULTIPLE: пользователь может интерактивно выбирать и отменять выбор множества узлов щелкая по ним.
    • ECOTree.SL_SINGLE: пользователь может интерактивно выбирать и отменять выбор однога узла щелкая по нему. Выбор нового узла отменит выбор предыдущего.
    • ECOTree.SL_NONE: пользователь не может выбирать узлы щелкая по ним. В любом случае методы API, которые вызывают выборку узлов, будут всегда работать, так же как и поиск в дереве.
  • defaultNodeWidth: ширина узла в пикселях, если другое явное значение не указано при добавлении узла к дереву.
  • defaultNodeHeight: высота узла в пикселях, если другое явное значение не указано при добавлении узла к дереву.
  • defaultTarget: гиперссылка узла (при нажатии на заголовок), если не была указана явная ссылка при добавлении узла к дереву. Обычно устанавливается тогда, когда все или большинство узлов имеют одну и ту же ссылку.
  • expandedImage: иконка знака "минус", которая позволяет собрать поддерево. Вы можете изменить ее по желанию или же указать на другой каталог с желаемым изображением.
  • collapsedImage: иконка знака "плюс", которая позволяет развернуть собранное поддерево. Вы можете изменить ее по желанию или же указать на другой каталог с желаемым изображением.
  • transImage: прозрачная иконка, используемая для разделения иконок для сбора/расширения и заголовка. Вы можете изменить ее по желанию или же указать на другой каталог с желаемым изображением.

 


 

Публичные методы ECOTree

  • UpdateTree(): вызывает обновление дерева.
  • add(id, pid, dsc, w, h, c, bc, target, meta): Добавляет новый узел к дереву. Первые три параметра являются обязательными. Вот все параметры:
    • id: идентификатор узла (ID). Любое число или строка.
    • pid: идентификатор родительского узла (ID). Если это корневой узел, то идентификатор родителя будет равен -1.
    • dsc: заголовок узла. Это будет видно в описании - он также будет являться ссылкой узла.
    • w: (Опционален) ширина узла в пикселях.
    • c: (Опционален) цвет узла.
    • bc: (Опционален) цвет границы узла.
    • h: (Опционален) высота узла в пикселях.
    • target: (Опционален) целевой объект гиперссылки. Если вы не предоставляете данное значение, то у узла будет стандартное значение. Но в случае, если вы предоставите пустую строку в качестве целевого объекта, то у вас будет узел без целевого объекта.
    • meta: (Опционален) метаданные узла. Метаданные не будут видны, но вы можете осуществлять поиск узлов на основе их содержимого, или же вы можете использовать их как контейнер для своих данных. Если вы используете объект Javascript в качестве метаданных, то вам необходимо предоставить метод toString() в прототипе данного объекта для того, чтобы поиск работал исправно.

    После того, как узел будет добавлен к дереву вы можете использовать другие программные интерфейсы для его модификации , к примеру, для отметки выделенного или свернутого узла в случае, если у него есть дочерние, до первого вызова к UpdateTree(). Добавлять узлы в каком-либо порядке совсем не обязательно.

  • searchNodes(str): осуществляет поиск по узлам дерева, содержащим строку str . Найденные узлы будут выбраны, и их предшественники будут расширены для того, чтобы отобразить найденный узел. Поиск может быть выполнен по заголовкам узлов, их метаданным или по обоим параметрам. Поиск чувствителен к регистру. Если selectModeSL_MULTIPLE или SL_NONE , то все найденные узлы будут выбраны. Если selectMode равен SL_SINGLE , то только первый найденный узел будет выбран (в порядке базы данных), но последующие поиски будут начинаться от следующего за ним узла, потому вы можете вызывать данный интерфейс для поиска следующего значения. После того, как все узлы будут просмотрены, поиск будет проведен заново. Будет вызвана внутренняя функция UpdateTree() , а поэтому вам не нужно обновлять дерево. равен
  • selectAll(): осуществляет выборку всех узлов в дереве. Если selectMode установлено в SL_NONE , то данный интерфейс не будет возвращать никакую выборку. UpdateTree() вызывается внутри, поэтому вам не нужно обновлять дерево.
  • unselectAll(): очищает выборку. Может быть использовано независимо от того, чему было равно selectMode. Должно быть использовано для очистки выборки между поисками. UpdateTree() вызывается внутри, поэтому вам не нужно обновлять дерево.
  • collapseAll(): сворачивает все родительские узлы. UpdateTree() вызывается внутри, поэтому вам не нужно обновлять дерево.
  • expandAll(): разворачивает все родительские узлы. UpdateTree() вызывается внутри, поэтому вам не нужно обновлять дерево.
  • collapseNode(nodeid, upd): сворачивает узел, идентификатор которого равен = nodeid. UpdateTree() вызывается внутри только в случае, если вы предоставите второй параметр в качестве true. Потому, если вы планируете свернуть несколько узлов, то вам стоит обновлять только после того, как будет выполнена вся работа.
  • selectNode(nodeid, upd): отмечает как выделенный тот узел, идентификатор которого равен = nodeid. UpdateTree() вызывается внутри только в случае, если вы предоставите второй параметр в качествеtrue.
  • setNodeTitle(nodeid, title, upd): устанавливает заголовок title того узла, идентификатор которого равен = nodeid. UpdateTree() вызывается внутри только в случае, если вы предоставите второй параметр в качестве true.
  • setNodeMetadata(nodeid, meta, upd): устанавливает метаданные meta того узла, идентификатор которого равен = nodeid. UpdateTree() вызывается внутри только в случае, если вы предоставите третий параметр в качестве true.
  • setNodeTarget(nodeid, target, upd): устанавливает целевой объект из гиперссылки (target) того узла, идентификатор которого равен = nodeid. UpdateTree() вызывается внутри только в случае, если вы предоставите третий параметр в качестве true.
  • setNodeColors(nodeid, color, border, upd): устанавливает фоновый цвет ( color ) и цвет границы (border) того узла, идентификатор которого равен = nodeid. UpdateTree() вызывается внутри только в случае, если вы предоставите четвертый параметр в качестве true.
  • getSelectedNodes(): возвращает массив объектов в JavaScript, каждый их которых имеет члены экземпляров "id", "dsc", "meta" со значениями идентификатора, заголовка и мета-данных каждого выбранного узла соответственно. Изучите, как использовать примеры. Может быть полезным, если вы выполняете некое редактирование с клиентской частью дерева и используете объект XMLHttp для отсылки результатов пользовательской выборки или поисков на сервер. (XMLHttp и AJAX не входят в тему данной статьи, но стоит отметить, что обозреватель IE7 наконец-то реализует XMLHttp в качестве собственного внутреннего объекта.)

 


 

Включенные примеры

В прикрепленном примере вы можете найти несколько простых примеров для экспериментов. Все рисунки данной статьи были сделаны при помощи кода данных примеров. Также присутствует усовершенствованный пример, который позволит вам тестировать практически все опции данного компонента. Данные в примерах были получены из Wikipedia.

Также стоит добавить компонентам возможности редактирования (клиент), такие как добавление, удаление, перестановка узлов и изменение их свойств (то есть, контекстное меню).

Автор: Emilio CL

Загрузить исходный код с примерами - 15.5 Kb