diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..2e3fb06 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +.idea/ +.idea/* diff --git a/aimara-es6/index.html b/aimara-es6/index.html new file mode 100644 index 0000000..ef36d3f --- /dev/null +++ b/aimara-es6/index.html @@ -0,0 +1,64 @@ + + + + + Aimara + + + + + + +
+ + + + + + + + + + + + + + \ No newline at end of file diff --git a/aimara-es6/src/css/aimara.es6.css b/aimara-es6/src/css/aimara.es6.css new file mode 100644 index 0000000..3e982b3 --- /dev/null +++ b/aimara-es6/src/css/aimara.es6.css @@ -0,0 +1,135 @@ +ul.tree * { + transition: all .2s linear; +} +ul.tree, +ul.tree ul { + list-style: none; + position: relative; +} +ul.tree li { + position: relative; + margin-bottom: .7rem; + white-space: nowrap; + text-overflow: ellipsis; +} +ul.tree li > span { + border-radius: .1rem; + padding: .3rem .6rem; +} +ul.tree li.selected > span, +ul.tree li.hover > span:hover { + cursor: pointer; + background-color: green; +} +ul.tree li > ul { + display: none; + margin-top: .6rem; +} +ul.tree li.on > ul { + display: block; +} + +ul.tree li > i { + margin-right: .5rem; + z-index: 1; +} + +ul.tree li > span > i { + margin-right: .25rem; +} +ul.tree li:before { + content: ''; + display: inline-block; + width: 2.5rem; + height: 1.7rem; + border-right: unset; + border-top: unset; + border-left: 1px dashed lightgray; + border-bottom: 1px dashed lightgray; + position: absolute; + left: -2rem; + top: -1.2rem; + z-index: -1; +} +ul.tree > li:first-child:before { + border-left: 1px dashed lightgray; + border-bottom: 1px dashed lightgray; + height: 1rem; + top: -.5rem; +} +ul.tree li:not(:last-child) > ul:before { + content: ''; + display: inline-block; + width: 0; + height: calc(100% + .7rem); + border-right: unset; + border-top: unset; + border-left: 1px dashed lightgray; + border-bottom: 1px dashed lightgray; + position: absolute; + left: -2rem; + top: -1.15rem; + z-index: -1; +} +ul.tree li:last-child:before { + border-bottom-left-radius: 5px; + z-index: -1; +} + +ul.context-menu, +ul.context-menu ul { + list-style: none; +} +ul.context-menu { + padding: 0; + margin: 0; + position: fixed; + display: block; + max-width: 0; + max-height: 0; + overflow-x: hidden; + overflow-y: hidden; + z-index: 10; + + border: 1px solid rgba(0, 0, 0, .1); + border-radius: 2px; + box-shadow: 0 0 5px 0 rgba(0,0,0,0.75); + background-color: white; +} +ul.context-menu.active { + max-width: 50vw; + max-height: 50vh; + overflow-y: auto; +} +ul.context-menu li span { + padding: .5rem .7rem; + display: block; + cursor: pointer; +} +ul.context-menu li > ul > li > span { + padding: 5px; +} +ul.context-menu li span i:first-child { + margin-right: .5rem; +} +ul.context-menu i.chevron { + float: right; + transition: all .4s linear; +} +ul.context-menu li > ul { + max-height: 0; + overflow: hidden; + transition: all .4s ease-in; +} +ul.context-menu li:hover > ul { + max-height: 50vh; + transition: all .4s ease-out; +} +ul.context-menu li:hover > span > i.chevron { + transform: rotate(90deg); + transition: all .4s linear; +} + +ul.context-menu li:hover > span { + background-color: lightgrey; +} \ No newline at end of file diff --git a/aimara-es6/src/js/aimara.es6.controller.js b/aimara-es6/src/js/aimara.es6.controller.js new file mode 100644 index 0000000..34ffb77 --- /dev/null +++ b/aimara-es6/src/js/aimara.es6.controller.js @@ -0,0 +1,91 @@ +class AimaraController { + /** * @param {Aimara} parent */ + constructor(parent) { + /** * @type {Aimara} */ + this.parent = parent; + } + + + /** + * + * @param {Event} event + * @param {{parent: *, img: HTMLElement, label: {img: HTMLElement, anchor: HTMLElement, span: HTMLElement}, li: HTMLElement, events: {onClickExpandCollapse: {before: Array, after: Array}, onClickNodeHolderImage: {before: Array, after: Array}, onClickNodeHolder: {before: Array, after: Array}, onClickNodeHolderAnchor: {before: Array, after: Array}, onClickNode: {before: Array, after: Array}}, child: null}} node + */ + onClickNode(event, node) { + event.preventDefault(); + event.stopPropagation(); + node.events.onClickNode.before.map(callback => callback(event, node)); + + node.events.onClickNode.after.map(callback => callback(event, node)); + } + + /** + * + * @param {Event} event + * @param {{parent: *, img: HTMLElement, label: {img: HTMLElement, anchor: HTMLElement, span: HTMLElement}, li: HTMLElement, events: {onClickExpandCollapse: {before: Array, after: Array}, onClickNodeHolderImage: {before: Array, after: Array}, onClickNodeHolder: {before: Array, after: Array}, onClickNodeHolderAnchor: {before: Array, after: Array}, onClickNode: {before: Array, after: Array}}, child: null}} node + */ + onClickNodeHolder(event, node) { + event.preventDefault(); + event.stopPropagation(); + + node.events.onClickNodeHolder.before.map(callback => callback(event, node)); + + node.events.onClickNodeHolder.after.map(callback => callback(event, node)); + } + + /** + * + * @param {Event} event + * @param {{parent: *, img: HTMLElement, label: {img: HTMLElement, anchor: HTMLElement, span: HTMLElement}, li: HTMLElement, events: {onClickExpandCollapse: {before: Array, after: Array}, onClickNodeHolderImage: {before: Array, after: Array}, onClickNodeHolder: {before: Array, after: Array}, onClickNodeHolderAnchor: {before: Array, after: Array}, onClickNode: {before: Array, after: Array}}, child: null}} node + */ + onClickNodeHolderImage(event, node) { + event.preventDefault(); + event.stopPropagation(); + + node.events.onClickNodeHolderImage.before.map(callback => callback(event, node)); + + node.events.onClickNodeHolderImage.after.map(callback => callback(event, node)); + } + + /** + * + * @param {Event} event + * @param {{parent: *, img: HTMLElement, label: {img: HTMLElement, anchor: HTMLElement, span: HTMLElement}, li: HTMLElement, events: {onClickExpandCollapse: {before: Array, after: Array}, onClickNodeHolderImage: {before: Array, after: Array}, onClickNodeHolder: {before: Array, after: Array}, onClickNodeHolderAnchor: {before: Array, after: Array}, onClickNode: {before: Array, after: Array}}, child: null}} node + */ + onClickNodeHolderAnchor(event, node) { + event.preventDefault(); + event.stopPropagation(); + + node.events.onClickNodeHolderAnchor.before.map(callback => callback(event, node)); + + node.events.onClickNodeHolderAnchor.after.map(callback => callback(event, node)); + } + + /** + * + * @param {Event} event + * @param {{parent: *, img: HTMLElement, label: {img: HTMLElement, anchor: HTMLElement, span: HTMLElement}, li: HTMLElement, events: {onClickExpandCollapse: {before: Array, after: Array}, onClickNodeHolderImage: {before: Array, after: Array}, onClickNodeHolder: {before: Array, after: Array}, onClickNodeHolderAnchor: {before: Array, after: Array}, onClickNode: {before: Array, after: Array}}, child: null}} node + */ + onClickExpandCollapse(event, node) { + event.preventDefault(); + event.stopPropagation(); + node.events.onClickExpandCollapse.before.map(callback => callback(event, node)); + + if (node.li.classList.contains('on')) { + node.li.classList.remove('on'); + if (node.child) { node.img.src = 'static/vendors/aimara/images/expand.png'; } + } else { + node.li.classList.add('on'); + if (node.child) { node.img.src = 'static/vendors/aimara/images/collapse.png'; } + } + + node.events.onClickExpandCollapse.after.map(callback => callback(event, node)); + } +} + +class AimaraException extends DOMException { + constructor(message) { + super(); + console.log(message); + } +} \ No newline at end of file diff --git a/aimara-es6/src/js/aimara.es6.js b/aimara-es6/src/js/aimara.es6.js new file mode 100644 index 0000000..9503624 --- /dev/null +++ b/aimara-es6/src/js/aimara.es6.js @@ -0,0 +1,66 @@ +class Aimara { + /** + * + * @param {string|HTMLElement} container + * @param {Aimara.defaults.options|null|undefined} options + */ + constructor(container, options) { + options = options || JSON.parse(JSON.stringify(Aimara.defaults.options)); + /** * @type {Aimara.defaults.options|{}} */ + this.options = options; + + /** * @type {HTMLElement|null} */ + this.container = null; + + if (typeof(container) === "string") { this.container = document.getElementById(container); } + else if (container instanceof HTMLElement) { this.container = container; } + else { throw new AimaraException("Invalid Parent container.\n\nThe Parent container could be type of string or HTMLElement"); } + + if (!this.container) { throw new AimaraException("Invalid Parent container.\n\nThe Parent container could be type of string or HTMLElement"); } + + /** * @type {AimaraModel} */ + this.model = new AimaraModel(this); + /** * @type {AimaraView} */ + this.view = new AimaraView(this); + /** * @type {AimaraController} */ + this.controller = new AimaraController(this); + } + + /** + * + * @param {AimaraNode.defaults.options} options + * @return {AimaraNode} + */ + createNode(options) { + return this.model.createNode(null, options); + } + + /** + * + * @param {HTMLElement|null|undefined} parent + * @param {AimaraNode.defaults.options} options + * @return {AimaraNode} + */ + createChildNode(parent, options) { + return this.model.createNode(parent, options); + } + + clear() { + this.model.nodeList.map(node => node.remove()); + this.model.nodeList = []; + } + expandAll() { this.model.nodeList.map(node => node.controller.expand(null)); } + collapseAll() { this.model.nodeList.map(node => node.controller.collapse(null)); } +} + +/** + * + * @type {{options: {hover: boolean|null|undefined, select: boolean|null|undefined, multiSelection: boolean|null|undefined}}} + */ +Aimara.defaults = { + options: { + multiSelection: true, + select: true, + hover: true + } +}; \ No newline at end of file diff --git a/aimara-es6/src/js/aimara.es6.model.js b/aimara-es6/src/js/aimara.es6.model.js new file mode 100644 index 0000000..ff4980a --- /dev/null +++ b/aimara-es6/src/js/aimara.es6.model.js @@ -0,0 +1,25 @@ +class AimaraModel { + /** * @param {Aimara} parent */ + constructor(parent) { + /** * @type {Aimara} */ + this.parent = parent; + + /** * @type {[AimaraNode]} */ + this.nodeList = []; + } + + /** + * + * @param {HTMLElement|null|undefined} parent + * @param {AimaraNode.defaults.options} options + * @return {AimaraNode} + */ + createNode(parent, options) { + let node = new AimaraNode(this.parent, parent, options); + this.nodeList.push(node); + if (!parent) { this.parent.view.mainHolder.appendChild(this.nodeList[this.nodeList.length - 1].li); } + else { this.nodeList[this.nodeList.length - 1].parent.appendChild(this.nodeList[this.nodeList.length - 1].li); } + return this.nodeList[this.nodeList.length - 1]; + } + +} \ No newline at end of file diff --git a/aimara-es6/src/js/aimara.es6.view.js b/aimara-es6/src/js/aimara.es6.view.js new file mode 100644 index 0000000..556c046 --- /dev/null +++ b/aimara-es6/src/js/aimara.es6.view.js @@ -0,0 +1,13 @@ +class AimaraView { + /** * @param {Aimara} parent */ + constructor(parent) { + /** * @type {Aimara} */ + this.parent = parent; + + this.mainHolder = document.createElement('UL'); + + this.mainHolder.classList.add('tree'); + + this.parent.container.appendChild(this.mainHolder); + } +} \ No newline at end of file diff --git a/aimara-es6/src/js/aimara.node.controller.es6.js b/aimara-es6/src/js/aimara.node.controller.es6.js new file mode 100644 index 0000000..137ab2d --- /dev/null +++ b/aimara-es6/src/js/aimara.node.controller.es6.js @@ -0,0 +1,177 @@ +class AimaraNodeController { +/** * @param {AimaraNode} parent */ + constructor(parent) { + /** * @type {AimaraNode} */ + this.parent = parent; + + /** * @type {{onClickExpandCollapse: {before: Array, after: Array}, onClickNodeHolderImage: {before: Array, after: Array}, onClickNodeHolder: {before: Array, after: Array}, onClickNodeHolderAnchor: {before: Array, after: Array}, onClickNode: {before: Array, after: Array}}} */ + this.events = { + onClickNode: {after: [], before: []}, + onClickExpandCollapse: {after: [], before: []}, + onClickNodeHolder: {after: [], before: []}, + onClickNodeHolderImage: {after: [], before: []}, + onClickNodeHolderAnchor: {after: [], before: []}, + onSelectNode: {after: [], before: []}, + onUnselectNode: {after: [], before: []}, + onSelectionChange: {after: [], before: []} + }; + + this.__events(); + } + /** + * + * @private + */ + __events() { + this.parent.view.li.addEventListener('click', event => this.onClickNode(event), false); + this.parent.view.icon.addEventListener('click', event => this.onClickExpandCollapse(event), false); + this.parent.view.label.span.addEventListener('click', event => this.onClickNodeHolder(event), false); + this.parent.view.label.icon.addEventListener('click', event => this.onClickNodeHolderImage(event), false); + this.parent.view.label.anchor.addEventListener('click', event => this.onClickNodeHolderAnchor(event), false); + + if (this.parent.options.contextMenu) { + this.parent.view.li.addEventListener('contextmenu', event => this.showContextMenu(event), false); + window.addEventListener('click', event => { + if (this.parent.view.contextMenu.classList.contains('active')) { this.parent.view.contextMenu.classList.remove('active'); } + }, false); + } + } + showContextMenu(event) { + console.log(event); + event.preventDefault(); + event.stopPropagation(); + if (!this.parent.view.contextMenu.isConnected) { document.body.appendChild(this.parent.view.contextMenu); } + if (!this.parent.view.contextMenu.classList.contains('active')) { this.parent.view.contextMenu.classList.add('active'); } + this.parent.view.contextMenu.style.top = String((event.y - 12) || event.screenY || event.clientY) + 'px'; + this.parent.view.contextMenu.style.left = String(event.x || event.screenX || event.clientX) + 'px'; + } + /** + * + * @param {Event} event + */ + onClickNode(event) { + event.preventDefault(); + event.stopPropagation(); + this.events.onClickNode.before.map(callback => callback(event, this.parent)); + + this.events.onClickNode.after.map(callback => callback(event, this.parent)); + } + + /** + * + * @param {Event} event + */ + onClickNodeHolder(event) { + event.preventDefault(); + event.stopPropagation(); + this.events.onClickNodeHolder.before.map(callback => callback(event, this.parent)); + + this.__selection(); + + this.events.onClickNodeHolder.after.map(callback => callback(event, this.parent)); + } + + /** + * + * @param {Event} event + */ + onClickNodeHolderImage(event) { + event.preventDefault(); + event.stopPropagation(); + + this.events.onClickNodeHolderImage.before.map(callback => callback(event, this.parent)); + + this.__selection(); + + this.events.onClickNodeHolderImage.after.map(callback => callback(event, this.parent)); + } + + /** + * + * @param {Event} event + */ + onClickNodeHolderAnchor(event) { + event.preventDefault(); + event.stopPropagation(); + + this.events.onClickNodeHolderAnchor.before.map(callback => callback(event, this.parent)); + + this.__selection(); + + this.events.onClickNodeHolderAnchor.after.map(callback => callback(event, this.parent)); + } + /** + * + * @param {Event|null} event + */ + expand(event) { + this.parent.view.li.classList.add('on'); + if (this.parent.view.child) { + this.parent.view.icon.classList.add(AimaraNode.defaults.iconCollapse); + if (this.parent.view.icon.classList.contains(AimaraNode.defaults.iconExpand)) { this.parent.view.icon.classList.remove(AimaraNode.defaults.iconExpand); } + } + } + /** + * + * @param {Event|null} event + */ + collapse(event) { + this.parent.view.li.classList.remove('on'); + if (this.parent.view.child) { + this.parent.view.icon.classList.add(AimaraNode.defaults.iconExpand); + if (this.parent.view.icon.classList.contains(AimaraNode.defaults.iconCollapse)) { this.parent.view.icon.classList.remove(AimaraNode.defaults.iconCollapse); } + } + } + /** + * + * @param {Event} event + */ + onClickExpandCollapse(event) { + event.preventDefault(); + event.stopPropagation(); + this.events.onClickExpandCollapse.before.map(callback => callback(event, this.parent)); + + if (this.parent.view.li.classList.contains('on')) { this.collapse(event); } + else { this.expand(event); } + + this.events.onClickExpandCollapse.after.map(callback => callback(event, this.parent)); + } + + /** + * + * @private + */ + __selection() { + if (this.parent.options.select) { + if (this.parent.view.li.classList.contains('selected')) { + this.events.onUnselectNode.before.map(callback => callback(event, this.parent)); + this.parent.view.li.classList.remove('selected'); + this.events.onUnselectNode.after.map(callback => callback(event, this.parent)); + } else { + this.events.onSelectNode.before.map(callback => callback(event, this.parent)); + this.parent.view.li.classList.add('selected'); + this.events.onSelectNode.after.map(callback => callback(event, this.parent)); + } + } else if (this.parent.aimaraParent.options.select && this.parent.options.select !== false) { + if (this.parent.view.li.classList.contains('selected')) { + this.events.onUnselectNode.before.map(callback => callback(event, this.parent)); + this.parent.view.li.classList.remove('selected'); + this.events.onUnselectNode.after.map(callback => callback(event, this.parent)); + } else { + this.events.onSelectNode.before.map(callback => callback(event, this.parent)); + this.parent.view.li.classList.add('selected'); + this.events.onSelectNode.after.map(callback => callback(event, this.parent)); + } + } + if (!this.parent.aimaraParent.options.multiSelection) { + if (this.parent.options.select ? this.parent.options.select === false : false) { return; } + this.parent.aimaraParent.model.nodeList.map(node => { + if (node.view.li.classList.contains('selected') && node.id !== this.parent.id) { + this.events.onUnselectNode.before.map(callback => callback(event, this.parent)); + node.view.li.classList.remove('selected'); + this.events.onUnselectNode.after.map(callback => callback(event, this.parent)); + } + }); + } + } +} diff --git a/aimara-es6/src/js/aimara.node.es6.js b/aimara-es6/src/js/aimara.node.es6.js new file mode 100644 index 0000000..376b368 --- /dev/null +++ b/aimara-es6/src/js/aimara.node.es6.js @@ -0,0 +1,88 @@ +class AimaraNode { + /** + * + * @param {Aimara} aimara + * @param {HTMLElement|null|undefined} parent + * @param {AimaraNode.defaults.options} options + */ + constructor(aimara, parent, options) { + options = options || JSON.parse(JSON.stringify(AimaraNode.defaults.options)); + /** * @type {AimaraNode.defaults.options} */ + this.options = options; + + /** * @type {HTMLElement|null|undefined} */ + this.parent = parent || null; + /** * @type {Aimara} */ + this.aimaraParent = aimara; + /** * @type {AimaraNodeModel} */ + this.model = new AimaraNodeModel(this); + /** * @type {AimaraNodeView} */ + this.view = new AimaraNodeView(this); + /** * @type {AimaraNodeController} */ + this.controller = new AimaraNodeController(this); + } + + get li() { return this.view.li; } + set id(v) { this.view.li.id = v; } + get id() { return this.view.li.id; } + + addEventListener (event, callback, after=true) { + switch (event) { + case 'click_node': this.controller.events.onClickNode[after ? 'after' : 'before'].push(callback); break; + case 'click_expand_collapse': this.controller.events.onClickExpandCollapse[after ? 'after' : 'before'].push(callback); break; + case 'click_node_holder': this.controller.events.onClickNodeHolder[after ? 'after' : 'before'].push(callback); break; + case 'click_node_holder_image': this.controller.events.onClickNodeHolderImage[after ? 'after' : 'before'].push(callback); break; + case 'click_node_holder_anchor': this.controller.events.onClickNodeHolderAnchor[after ? 'after' : 'before'].push(callback); break; + default: throw new AimaraException('Invalid event type.'); + } + }; + appendChild(element) { + if (this.view.child) { this.view.child.appendChild(element); } + else { + this.view.child = document.createElement('UL'); + if (this.view.icon.classList.contains(AimaraNode.defaults.iconNoChild)) { this.view.icon.classList.remove(AimaraNode.defaults.iconNoChild); } + this.view.icon.classList.add(AimaraNode.defaults.iconExpand); + this.view.li.appendChild(this.view.child); + this.view.child.appendChild(element); + } + } + createNode(options) { + return this.aimaraParent.createChildNode(this, options); + } + remove() { + if (!this.view.li.isConnected) { return; } + + if (this.parent) { this.parent.view.child.removeChild(this.view.li); } + else { this.aimaraParent.view.mainHolder.removeChild(this.view.li); } + } + insert() { + if (this.view.li.isConnected) { return; } + + if (this.parent) { this.parent.view.child.appendChild(this.view.li); } + else { this.aimaraParent.view.mainHolder.appendChild(this.view.li); } + } +} + +/** + * + * @type {{iconNoChild: string, iconExpand: string, options: {hover: boolean, select: boolean, contextMenu: {itemName: {icon: string, action: AimaraNode.defaults.options.contextMenu.itemName.action, label: string, submenus: {}}}, icon: string, label: string}, iconCollapse: string}} + */ +AimaraNode.defaults = { + iconNoChild: 'fa-dot-circle', + iconExpand: 'fa-plus-square', + iconCollapse: 'fa-minus-square', + options: { + icon: '', + label: '', + hover: false, + select: false, + contextMenu: { + itemName: { + icon: '', + label: '', + action: ()=>{}, + submenus: {} + } + } + } +}; \ No newline at end of file diff --git a/aimara-es6/src/js/aimara.node.model.es6.js b/aimara-es6/src/js/aimara.node.model.es6.js new file mode 100644 index 0000000..9acd3a5 --- /dev/null +++ b/aimara-es6/src/js/aimara.node.model.es6.js @@ -0,0 +1,9 @@ +class AimaraNodeModel { + /** * @param {AimaraNode} parent */ + constructor(parent) { + /** * @type {AimaraNode} */ + this.parent = parent; + this.contextMenu = this.parent.options.contextMenu || null; + } + +} \ No newline at end of file diff --git a/aimara-es6/src/js/aimara.node.view.es6.js b/aimara-es6/src/js/aimara.node.view.es6.js new file mode 100644 index 0000000..10e669e --- /dev/null +++ b/aimara-es6/src/js/aimara.node.view.es6.js @@ -0,0 +1,95 @@ +class AimaraNodeView { +/** * @param {AimaraNode} parent */ + constructor(parent) { + /** * @type {AimaraNode} */ + this.parent = parent; + + /** * @type {HTMLElement|null|undefined} */ + this.li = document.createElement('LI'); /// Parent LI + this.li.id = 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) { let r = Math.random()*16|0, v = c === 'x' ? r : (r&0x3|0x8); return v.toString(16); }).toUpperCase(); + /** * @type {HTMLElement|null|undefined} */ + this.icon = document.createElement('I'); /// Expand/Collapse image + /** * @type {HTMLElement|null|undefined} */ + this.child = null; /// Child UL tree + /** * @type {{anchor: HTMLElement|null|undefined, icon: HTMLElement|null|undefined, span: HTMLElement|null|undefined}} */ + this.label = { + span: document.createElement('SPAN'), /// Span holder for node icon and label + icon: document.createElement('I'), /// Node icon + anchor: document.createElement('A') /// Node Label + }; + this.contextMenu = document.createElement('UL'); + this.contextMenu.classList.add('context-menu'); + this.buildContextMenu(this.parent.options.contextMenu, null); + this.__prettify(); + this.__assembly(); + this.__builder(); + } + + /** + * + * @param {AimaraNode.defaults.options.contextMenu} context + * @param parent + * @returns {null} + */ + buildContextMenu(context, parent) { + if (!context) { return null; } + let menus = Object.keys(context); + + menus.map(item => { + let li = document.createElement('LI'); + let span = document.createElement('SPAN'); + let icon = document.createElement('I'); + icon.classList.add('fa'); + if (context[item].icon) { icon.classList.add(context[item].icon); } + let anchor = document.createElement('A'); + anchor.innerHTML = context[item].label; + li.appendChild(span); + span.appendChild(icon); + span.appendChild(anchor); + li.addEventListener('click', event => { + event.stopPropagation(); + event.preventDefault(); + if (context[item].action) { + context[item].action(event, li, parent || this.contextMenu, this.parent); + + if (parent) { if (parent.classList.contains('active')) { parent.classList.remove('active'); } } + else { if (this.contextMenu.classList.contains('active')) { this.contextMenu.classList.remove('active'); } } + } + }, false); + if (parent) { parent.appendChild(li); } + else { this.contextMenu.appendChild(li); } + + if (context[item].submenus) { + let nParent = document.createElement('UL'); + let chev = document.createElement('I'); + chev.classList.add('fa'); + chev.classList.add('chevron'); + chev.classList.add('fa-chevron-right'); + span.appendChild(chev); + li.appendChild(nParent); + this.buildContextMenu(context[item].submenus, nParent); + } + }); + + } + __prettify() { + this.icon.classList.add('fa'); + this.icon.classList.add(AimaraNode.defaults.iconNoChild); + this.label.icon.classList.add('fa'); + + if (this.parent.options.hover || this.parent.options.select || (this.parent.aimaraParent.options.select && this.parent.options.select !== false)) { this.li.classList.add('hover'); } + else if (this.parent.aimaraParent.options.hover && this.parent.options.hover !== false) { this.li.classList.add('hover'); } + } + + __assembly() { + this.li.appendChild(this.icon); + this.li.appendChild(this.label.span); + + this.label.span.appendChild(this.label.icon); + this.label.span.appendChild(this.label.anchor); + } + __builder() { + if (this.parent.options.icon) { this.label.icon.classList.add(this.parent.options.icon); } + if (this.parent.options.label) { this.label.anchor.innerHTML = this.parent.options.label; } + } +} \ No newline at end of file