define([ 'js/tplNode', 'js/events', 'js/client', 'js/input' ], function ( tplNode, events, client, input ) { return { links: [], nodes: [], mode: 'none', init: function () { events.on('onAreaSelect', this.events.onAreaSelect.bind(this)); events.on('onMouseMove', this.events.onMouseMove.bind(this)); }, findNode: function (x, y) { let res = this.nodes.find(n => ((n.pos.x == x) && (n.pos.y == y))); if (!res) { res = this.nodes.find(function (n) { return ((n.size > 0) && (Math.abs(n.pos.x - x) <= 1) && (Math.abs(n.pos.y - y) <= 1)); }); } return res; }, callAction: function (action, options = {}) { let node = options.node || this.findNode(options.x, options.y); options.node = node; return !this.actions[action].call(this, options); }, getSelected: function (single) { let selected = this.nodes.filter(n => n.selected); if ((single) && (selected.length != 1)) return null; if (single) return selected[0]; return selected; }, serialize: function () { return JSON.stringify({ nodes: this.nodes, links: this.links.map(function (l) { return { from: { id: l.from.id }, to: { id: l.to.id } }; }) }); }, getData: function () { return { nodes: this.nodes.map(function (n) { let res = { id: n.id, pos: n.pos }; ['size', 'color', 'stats', 'spiritStart'].forEach(function (s) { if (n[s] !== undefined) res[s] = n[s]; }); return res; }), links: this.links.map(function (l) { return { from: l.from.id, to: l.to.id }; }) }; }, getNextId: function () { for (var i = 0; i < this.nodes.length; i++) { if (!this.nodes.some(n => (n.id == i))) return i; } return this.nodes.length; }, setMode: function (mode) { this.mode = mode; }, actions: { reset: function () { this.nodes = []; this.links = []; events.emit('onNew'); }, load: function (data) { this.nodes = data.nodes; this.nodes.forEach(function (n) { if ((n.group) && (!n.group.push)) n.group = [n.group]; }); this.links = data.links.map(function (l) { l.from = this.nodes.find(n => (n.id == l.from.id)); l.to = this.nodes.find(n => (n.id == l.to.id)); return l; }, this); events.emit('onTreeLoaded', { nodes: this.nodes, links: this.links }); }, selectNode: function (options) { if ( ( (!options.node) || (!this.nodes.some(n => ((n.selected) && (n == options.node)))) ) && ( (!input.isKeyDown('shift')) || (options.force) ) ) this.nodes.forEach(n => (n.selected = false)); if (options.node) options.node.selected = true; else if (options instanceof Array) options.forEach(n => (n.selected = true)); events.emit('onSelectNode', this.nodes.filter(n => n.selected)); if (options.node) events.emit('onFocusNode', options.node); return !options.node; }, addNode: function (options) { this.nodes.push(tplNode.build({ id: this.getNextId(), x: options.x, y: options.y })); this.callAction('selectNode'); }, connectNode: function (options) { let node = options.node; if (!node) { this.callAction('selectNode'); return true; } let singleSelected = this.getSelected(true); if ((singleSelected) && (input.isKeyDown('ctrl'))) { if (options.shiftDown) { this.links.spliceWhere(l => ( ( (l.from == node) || (l.to == node) ) && ( (l.from == singleSelected) || (l.to == singleSelected) ) && (node != singleSelected) )); } else { this.links.push({ from: singleSelected, to: node }); } this.callAction('selectNode', { force: true }); this.callAction('selectNode', { node: options.node }); } else { return this.callAction('selectNode', { node: node }); } }, moveNode: function (options) { let selected = this.getSelected(); if (!selected.length) return true; if (selected.length == 0) return; selected.sort(function (a, b) { let distanceA = Math.abs(a.pos.x - options.x) + Math.abs(a.pos.y - options.y); let distanceB = Math.abs(b.pos.x - options.x) + Math.abs(b.pos.y - options.y); return (distanceA > distanceB) ? 1 : -1; }); let deltaX = selected[0].pos.x - options.x; let deltaY = selected[0].pos.y - options.y; selected.forEach(function (s) { s.pos.x -= deltaX; s.pos.y -= deltaY; }); }, deleteNode: function (options) { let selected = this.getSelected(); selected.forEach(function (s) { this.nodes.spliceWhere(n => (n == s)); this.links.spliceWhere(n => ((n.from == s) || (n.to == s))); s.selected = false; events.emit('onDeleteNode', s); }, this); }, recolorNode: function () { let selected = this.getSelected(true); if (!selected) return true; selected.color = (selected.color + 1) % 7; }, resizeNode: function () { let selected = this.getSelected(true); if (!selected) return true; selected.size = (selected.size + 1) % 3; } }, events: { onAreaSelect: function (from, to) { if (!input.isKeyDown('ctrl')) this.nodes.forEach(n => (n.selected = false)); let lowX = Math.min(from.x, to.x); let lowY = Math.min(from.y, to.y); let highX = Math.max(from.x, to.x); let highY = Math.max(from.y, to.y); for (let i = lowX; i <= highX; i++) { for (let j = lowY; j <= highY; j++) { let node = this.findNode(i, j); if (!node) continue; node.selected = true; } } events.emit('onSelectNode', this.nodes.filter(n => n.selected)); }, onMouseMove: function (e) { let hoverNode = this.findNode(e.x, e.y); if (hoverNode) { let text = ''; let stats = hoverNode.stats || {}; for (let s in stats) text += s + ': ' + stats[s] + '
'; text = text.substr(0, text.length - 6); if (text.length > 0) events.emit('onShowTooltip', e, text); } else events.emit('onHideTooltip'); } } }; });