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