viewer.js 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411
  1. /// <reference path="raphael.js" />
  2. /// <reference path="action.js" />
  3. var AB;
  4. (function (AB) {
  5. var Graph = (function () {
  6. //
  7. // Public functions
  8. //
  9. function Graph() {
  10. var scope = this;
  11. // Members
  12. this.element = document.getElementById('Graph');
  13. this.graph = Raphael('Graph', (50 * screen.width) / 100, screen.height);
  14. this.root = this._createNode('Object name', Raphael.rgb(200, 200, 200), true);
  15. this.parametersElement = document.getElementById('Parameters');
  16. this.mousex = 0;
  17. this.mousey = 0;
  18. this.objectName = '';
  19. // Context menu (to remove a node)
  20. this.contextMenu = null;
  21. this.selectedNode = null;
  22. this.graph.canvas.addEventListener('contextmenu', function (event) {
  23. var result = scope.traverseGraph(null, scope.mousex, scope.mousey);
  24. if (result.hit && result.element != scope.root.action) {
  25. scope.selectedNode = result.element;
  26. scope.contextMenu = scope._createNode('Remove', Raphael.rgb(255, 255, 255), true);
  27. scope._setNodePosition(scope.contextMenu, scope.mousex, scope.mousey);
  28. }
  29. window.event.returnValue = false;
  30. });
  31. document.onclick = function (event) {
  32. if (scope.contextMenu) {
  33. if (scope.contextMenu.isPointInside(scope.mousex, scope.mousey)) {
  34. scope.selectedNode.parent.removeChild(scope.selectedNode);
  35. scope._removeAction(scope.selectedNode, true);
  36. scope.update();
  37. }
  38. scope.selectedNode = null;
  39. scope._removeNode(scope.contextMenu);
  40. }
  41. scope.contextMenu = null;
  42. };
  43. document.onmousemove = function (event) {
  44. scope.mousex = event.clientX - scope.graph.canvas.getBoundingClientRect().left;
  45. scope.mousey = event.clientY - scope.graph.canvas.getBoundingClientRect().top;
  46. if (!scope.contextMenu)
  47. return;
  48. if (scope.contextMenu.isPointInside(scope.mousex, scope.mousey))
  49. scope.contextMenu.attr(scope.contextMenu.rect, 'fill', Raphael.rgb(140, 200, 230));
  50. else
  51. scope.contextMenu.attr(scope.contextMenu.rect, 'fill', Raphael.rgb(255, 255, 255));
  52. };
  53. // Set properties
  54. this.element.scrollLeft = (this.element.getBoundingClientRect().width / 2) + (Graph.NODE_WIDTH / 2);
  55. }
  56. Graph.prototype.addNode = function (parent, listElement) {
  57. var color = Raphael.rgb(200, 200, 200);
  58. switch (listElement.type) {
  59. case AB.ActionsBuilder.Type.TRIGGER: color = Raphael.rgb(100, 149, 237); break;
  60. case AB.ActionsBuilder.Type.ACTION: color = Raphael.rgb(240, 230, 140); break;
  61. case AB.ActionsBuilder.Type.FLOW_CONTROL: color = Raphael.rgb(205, 92, 92); break;
  62. }
  63. var n = this._createNode(listElement.name, color, false);
  64. n.action.name = listElement.name;
  65. n.action.type = listElement.type;
  66. n.action.properties = listElement.properties;
  67. for (var i = 0; i < listElement.properties.length; i++)
  68. n.action.propertiesResults.push(listElement.properties[i].value);
  69. if (parent)
  70. parent.addChild(n.action);
  71. this._createNodeAnimation(n);
  72. }
  73. Graph.prototype.update = function (node, yOffset, childID, rootChildID) {
  74. if (!yOffset)
  75. yOffset = 10;
  76. else
  77. yOffset += Graph.VERTICAL_OFFSET;
  78. if (!node) node = this.root;
  79. if (node == this.root) {
  80. this._setNodePosition(node, (this.graph.width / 2) - (Graph.NODE_WIDTH / 2), yOffset);
  81. }
  82. else {
  83. var length = node.action.parent.children.length;
  84. var parentx = node.action.parent.node.attr(node.action.parent.node.rect, 'x');
  85. var totalLength = Graph.NODE_WIDTH * length;
  86. var offset = ( Graph.NODE_WIDTH * (length - childID - 1) );
  87. var posx = parentx;
  88. posx += offset - ((Graph.NODE_WIDTH / 2) * (length - 1));
  89. this._setNodePosition(node, posx, yOffset);
  90. this._setLine(node.action);
  91. }
  92. for (var i = 0; i < node.action.children.length; i++) {
  93. if (node == this.root)
  94. rootChildID = i;
  95. this.update(node.action.children[i].node, yOffset, i, rootChildID);
  96. var n = node.action.children[i].node;
  97. if (n.action.children.length > 1) {
  98. for (var j = rootChildID; j >= 0; j--) {
  99. if (node.action.children.length < 2 || n.action.children.length < 2)
  100. continue;
  101. var rx = this.root.attr(this.root.rect, 'x');
  102. var x = this.root.action.children[j].node.attr(this.root.action.children[j].node.rect, 'x');
  103. var y = this.root.action.children[j].node.attr(this.root.action.children[j].node.rect, 'y');
  104. x -= ((Graph.NODE_WIDTH / 2) * (node.action.children.length - 1)) * (x > rx ? -1 : 1);
  105. this._setNodePosition(this.root.action.children[j].node, x, y);
  106. this._setLine(this.root.action.children[j]);
  107. }
  108. }
  109. }
  110. }
  111. Graph.prototype.createJSON = function (root, graph) {
  112. if (!root) root = this.root.action;
  113. if (!graph) graph = {};
  114. var action = {};
  115. action.type = root.type;
  116. action.name = root.name;
  117. action.children = new Array();
  118. action.properties = new Array();
  119. for (var i = 0; i < root.properties.length; i++)
  120. action.properties[i] = { name: root.properties[i].text, value: root.propertiesResults[i] };
  121. for (var i = 0; i < root.children.length; i++) {
  122. action.children.push(this.createJSON(root.children[i], action));
  123. }
  124. return action;
  125. }
  126. Graph.prototype.loadFromJSON = function (graph) {
  127. var scope = this;
  128. for (var i = 0; i < this.root.action.children.length; i++)
  129. this._removeAction(this.root.action.children[i], true);
  130. this.root.action.clearChildren();
  131. graph = JSON.parse(graph);
  132. console.log(graph);
  133. var load = function (root, parent) {
  134. if (!parent) parent = scope.root.action;
  135. if (!root) root = graph;
  136. if (root.type != AB.ActionsBuilder.Type.OBJECT) { // Means it is the root (the edited object)
  137. var e = {};
  138. e.type = root.type;
  139. e.name = root.name;
  140. e.properties = new Array();
  141. for (var i = 0; i < root.properties.length; i++)
  142. e.properties.push({ text: root.properties[i].name, value: root.properties[i].value });
  143. var n = scope.addNode(parent, e);
  144. parent = parent.children[parent.children.length - 1];
  145. }
  146. for (var i = 0; i < root.children.length; i++) {
  147. load(root.children[i], parent);
  148. }
  149. }
  150. load();
  151. this.update();
  152. }
  153. Graph.prototype.traverseGraph = function (start, x, y) {
  154. if (!start) start = this.root.action;
  155. var result = {
  156. hit: true,
  157. element: start
  158. };
  159. if (start.node.isPointInside(x, y))
  160. return result;
  161. for (var i = 0; i < start.children.length; i++) {
  162. if (start.children[i].node.isPointInside(x, y)) {
  163. result.hit = true;
  164. result.element = start.children[i];
  165. return result;
  166. }
  167. result = this.traverseGraph(start.children[i], x, y);
  168. if (result.hit)
  169. return result;
  170. }
  171. result.hit = false;
  172. result.element = null;
  173. return result;
  174. }
  175. //
  176. // Private functions
  177. //
  178. Graph.prototype._setLine = function (element) {
  179. var linex = element.node.attr(element.node.rect, 'x') + Graph.NODE_WIDTH / 2;
  180. var liney = element.node.attr(element.node.rect, 'y');
  181. var linex2 = element.parent.node.attr(element.parent.node.rect, 'x') + Graph.NODE_WIDTH / 2;
  182. var liney2 = element.parent.node.attr(element.parent.node.rect, 'y') + Graph.NODE_HEIGHT;
  183. element.node.attr(element.node.line, 'path', 'M' + linex + ' ' + liney + 'L' + linex2 + ' ' + liney2);
  184. }
  185. Graph.prototype._setNodePosition = function (node, x, y) {
  186. var offsetx = node.attr(node.rect, 'x') - x;
  187. node.attr(node.rect, 'x', x);
  188. node.attr(node.rect, 'y', y);
  189. node.attr(node.text, 'x', x + 5);
  190. node.attr(node.text, 'y', y + Graph.NODE_HEIGHT / 2);
  191. for (var i = 0; i < node.action.children.length; i++) {
  192. this._setNodePosition(node.action.children[i].node, node.action.children[i].node.attr(node.action.children[i].node.rect, 'x') - offsetx, y + Graph.VERTICAL_OFFSET);
  193. this._setLine(node.action.children[i]);
  194. }
  195. }
  196. Graph.prototype._removeNode = function (element) {
  197. element.rect.remove();
  198. element.text.remove();
  199. if (element.line)
  200. element.line.remove();
  201. }
  202. Graph.prototype._removeAction = function (action, removeChildren) {
  203. if (!removeChildren)
  204. removeChildren = false;
  205. this._removeNode(action.node);
  206. if (removeChildren) {
  207. for (var i = 0; i < action.children.length; i++)
  208. this._removeAction(action.children[i], removeChildren);
  209. action.clearChildren();
  210. }
  211. else {
  212. for (var i = 0; i < action.children.length; i++)
  213. action.children[i].parent = action.parent;
  214. }
  215. }
  216. Graph.prototype._createNode = function (text, color, noLine) {
  217. var n = new AB.Node();
  218. n.rect = this.graph.rect(0, 0, Graph.NODE_WIDTH, Graph.NODE_HEIGHT, 5);
  219. n.text = this.graph.text(0, 0, text);
  220. if (!noLine)
  221. n.line = this.graph.path('M10 10L90 90');
  222. n.action = new AB.Action(n);
  223. n.rect.attr('fill', color);
  224. n.text.attr('font-size', 12);
  225. n.text.attr('text-anchor', 'start');
  226. return n;
  227. }
  228. Graph.prototype._createParameters = function (element) {
  229. var onChange = function (input, propertyID) {
  230. return function () {
  231. var value = input.value;
  232. element.action.propertiesResults[propertyID] = value;
  233. }
  234. }
  235. while (this.parametersElement.childNodes.length)
  236. this.parametersElement.removeChild(this.parametersElement.firstChild);
  237. var p = element.action.properties;
  238. var pr = element.action.propertiesResults;
  239. if (p.length == 0)
  240. return;
  241. for (var i = 0; i < p.length; i++) {
  242. var el = document.createElement('input');
  243. el.setAttribute('value', pr[i]);
  244. el.onchange = onChange(el, i);
  245. var text = document.createElement('a');
  246. text.text = p[i].text;
  247. this.parametersElement.appendChild(document.createElement('br'));
  248. this.parametersElement.appendChild(text);
  249. this.parametersElement.appendChild(document.createElement('br'));
  250. this.parametersElement.appendChild(el);
  251. }
  252. }
  253. Graph.prototype._createNodeAnimation = function (element) {
  254. var scope = this;
  255. var mousex, mousey;
  256. var finished = true;
  257. var elementx = 0;
  258. var elementy = 0;
  259. var onMove = function (dx, dy, x, y, event) {
  260. mousex = x;
  261. mousey = y;
  262. };
  263. var onStart = function (x, y, event) {
  264. mousex = x;
  265. mousey = y;
  266. if (finished) {
  267. elementx = element.attr(element.rect, 'x');
  268. elementy = element.attr(element.rect, 'y');
  269. }
  270. finished = false;
  271. element.rect.animate({
  272. x: element.attr(element.rect, 'x') - 10,
  273. y: element.attr(element.rect, 'y') - 5,
  274. width: Graph.NODE_WIDTH + 20,
  275. height: Graph.NODE_HEIGHT + 10,
  276. opacity: 0.25
  277. }, 500, '>');
  278. };
  279. var onEnd = function (event) {
  280. element.rect.animate({
  281. x: elementx,
  282. y: elementy,
  283. width: Graph.NODE_WIDTH,
  284. height: Graph.NODE_HEIGHT,
  285. opacity: 1.0
  286. }, 500, '>', function () { finished = true; });
  287. var x = mousex - scope.graph.canvas.getBoundingClientRect().left;
  288. var y = mousey - scope.graph.canvas.getBoundingClientRect().top;
  289. var dragResult = scope.traverseGraph(null, x, y);
  290. var json = JSON.stringify(scope.createJSON());
  291. console.log(json);
  292. if (dragResult.hit && dragResult.element == element.action || !dragResult.hit) {
  293. scope._createParameters(element);
  294. }
  295. else {
  296. if (element.action.type == AB.ActionsBuilder.Type.TRIGGER && dragResult.element != scope.root.action)
  297. return;
  298. if (element.action.type == AB.ActionsBuilder.Type.ACTION && dragResult.element == scope.root.action)
  299. return;
  300. if (element.action.type == AB.ActionsBuilder.Type.FLOW_CONTROL && dragResult.element == scope.root.action)
  301. return;
  302. // Reset node
  303. element.rect.stop(element.rect.animation);
  304. element.attr(element.rect, 'opacity', 1.0);
  305. element.attr(element.rect, 'width', Graph.NODE_WIDTH);
  306. element.attr(element.rect, 'height', Graph.NODE_HEIGHT);
  307. element.action.parent.removeChild(element.action);
  308. dragResult.element.addChild(element.action);
  309. scope.update();
  310. }
  311. };
  312. element.rect.drag(onMove, onStart, onEnd);
  313. element.text.drag(onMove, onStart, onEnd);
  314. }
  315. Graph.NODE_WIDTH = 150;
  316. Graph.NODE_HEIGHT = 25;
  317. Graph.VERTICAL_OFFSET = 70;
  318. return Graph;
  319. })();
  320. AB.Graph = Graph;
  321. })(AB || (AB = {}));