actionsbuilder.viewer.js 26 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539
  1. var ActionsBuilder;
  2. (function (ActionsBuilder) {
  3. var Viewer = (function () {
  4. function Viewer(type) {
  5. var _this = this;
  6. this.objectName = "Unnamed Object";
  7. this.zoom = 1.0;
  8. this._firstUpdate = true;
  9. this.viewerContainer = document.getElementById("GraphContainerID");
  10. this.viewerElement = document.getElementById("GraphElementID");
  11. this.paper = Raphael("GraphElementID", screen.width, screen.height);
  12. this.root = this.addAction(null, type, { name: this.objectName, text: this.objectName, properties: [], description: "" });
  13. this.selectedNode = null;
  14. window.addEventListener("resize", function (event) {
  15. _this.onResize(event);
  16. });
  17. window.addEventListener("mousemove", function (event) {
  18. _this.onMove(event);
  19. });
  20. this.paper.canvas.addEventListener("click", function (event) {
  21. _this.onClick(event);
  22. });
  23. this._toolbar = new ActionsBuilder.Toolbar(this);
  24. this._contextMenu = new ActionsBuilder.ContextMenu(this);
  25. this.parameters = new ActionsBuilder.Parameters(this);
  26. this.utils = new ActionsBuilder.Utils(this);
  27. this.parameters.parametersHelpElement.textContent = Viewer._DEFAULT_INFO_MESSAGE;
  28. this.onResize(null);
  29. }
  30. Object.defineProperty(Viewer, "NODE_WIDTH", {
  31. get: function () {
  32. return Viewer._NODE_WIDTH;
  33. },
  34. enumerable: true,
  35. configurable: true
  36. });
  37. Object.defineProperty(Viewer, "NODE_HEIGHT", {
  38. get: function () {
  39. return Viewer._NODE_HEIGHT;
  40. },
  41. enumerable: true,
  42. configurable: true
  43. });
  44. Object.defineProperty(Viewer, "NODE_MINIMIZED_WIDTH", {
  45. get: function () {
  46. return Viewer._NODE_MINIMIZE_WIDTH;
  47. },
  48. enumerable: true,
  49. configurable: true
  50. });
  51. Object.defineProperty(Viewer, "VERTICAL_OFFSET", {
  52. get: function () {
  53. return Viewer._VERTICAL_OFFSET;
  54. },
  55. enumerable: true,
  56. configurable: true
  57. });
  58. Viewer.prototype.onResize = function (event) {
  59. var tools = document.getElementById("ToolsButtonsID");
  60. this.viewerContainer.style.height = window.innerHeight - tools.getBoundingClientRect().height - 25 - 50 + "px";
  61. this.viewerElement.style.height = window.innerHeight - tools.getBoundingClientRect().height - 25 - 50 + "px";
  62. this.parameters.onResize();
  63. this._toolbar.onResize();
  64. if (this.paper.height < window.innerHeight) {
  65. this.paper.setSize(this.paper.width, window.innerHeight);
  66. }
  67. if (this._firstUpdate) {
  68. this.viewerElement.scrollLeft = ((this.viewerElement.scrollWidth / 2) - (this.viewerElement.getBoundingClientRect().width / 2));
  69. this._firstUpdate = false;
  70. }
  71. };
  72. Viewer.prototype.onMove = function (event) {
  73. this.mousex = event.clientX - this.paper.canvas.getBoundingClientRect().left;
  74. this.mousey = event.clientY - this.paper.canvas.getBoundingClientRect().top;
  75. };
  76. Viewer.prototype.onClick = function (event) {
  77. if (this._contextMenu.showing) {
  78. return;
  79. }
  80. if (this.selectedNode !== null) {
  81. var node = this.selectedNode.node;
  82. node.rect.attr("fill", this.getNodeColor(this.selectedNode.type, node.detached));
  83. }
  84. var result = this.traverseGraph(null, this.mousex, this.mousey, true);
  85. if (result.hit) {
  86. this.selectedNode = result.action;
  87. var node = this.selectedNode.node;
  88. node.rect.attr("fill", this.getSelectedNodeColor(this.selectedNode.type, node.detached));
  89. }
  90. else {
  91. this.selectedNode = null;
  92. this.parameters.clearParameters();
  93. this.parameters.parametersHelpElement.textContent = Viewer._DEFAULT_INFO_MESSAGE;
  94. }
  95. };
  96. Viewer.prototype.setColorTheme = function (color) {
  97. this.paper.canvas.style.background = color;
  98. };
  99. Viewer.prototype.getNodeColor = function (type, detached) {
  100. if (detached) {
  101. return Raphael.rgb(96, 122, 14);
  102. }
  103. switch (type) {
  104. case ActionsBuilder.Type.TRIGGER:
  105. return Raphael.rgb(133, 154, 185);
  106. break;
  107. case ActionsBuilder.Type.ACTION:
  108. return Raphael.rgb(182, 185, 132);
  109. break;
  110. case ActionsBuilder.Type.FLOW_CONTROL:
  111. return Raphael.rgb(185, 132, 140);
  112. break;
  113. case ActionsBuilder.Type.OBJECT:
  114. case ActionsBuilder.Type.SCENE:
  115. return Raphael.rgb(255, 255, 255);
  116. break;
  117. default: break;
  118. }
  119. return null;
  120. };
  121. Viewer.prototype.getSelectedNodeColor = function (type, detached) {
  122. if (detached) {
  123. return Raphael.rgb(96, 122, 14);
  124. }
  125. switch (type) {
  126. case ActionsBuilder.Type.TRIGGER:
  127. return Raphael.rgb(41, 129, 255);
  128. break;
  129. case ActionsBuilder.Type.ACTION:
  130. return Raphael.rgb(255, 220, 42);
  131. break;
  132. case ActionsBuilder.Type.FLOW_CONTROL:
  133. return Raphael.rgb(255, 41, 53);
  134. break;
  135. case ActionsBuilder.Type.OBJECT:
  136. case ActionsBuilder.Type.SCENE:
  137. return Raphael.rgb(255, 255, 255);
  138. break;
  139. default: break;
  140. }
  141. return null;
  142. };
  143. Viewer.prototype.removeAction = function (action, removeChildren) {
  144. if (action.parent !== null && action.parent.hub === action) {
  145. this.removeAction(action.parent, false);
  146. return;
  147. }
  148. this.removeNode(action.node);
  149. if (action.combineArray !== null) {
  150. this.removeNode(action.hub.node);
  151. for (var i = 0; i < action.combineArray.length; i++) {
  152. this.removeNode(action.combineArray[i].node);
  153. }
  154. action.combineArray.length = 0;
  155. }
  156. if (removeChildren) {
  157. for (var i = 0; i < action.children.length; i++) {
  158. this.removeAction(action.children[i], removeChildren);
  159. }
  160. action.clearChildren();
  161. }
  162. else {
  163. for (var i = 0; i < action.children.length; i++) {
  164. action.parent.addChild(action.children[i]);
  165. action.children[i].parent = action.parent;
  166. }
  167. }
  168. };
  169. Viewer.prototype.removeNode = function (node) {
  170. node.rect.remove();
  171. node.text.remove();
  172. if (node.line !== null) {
  173. node.line.remove();
  174. }
  175. };
  176. Viewer.prototype.update = function () {
  177. var _this = this;
  178. this._setActionPosition(this.root, (this.paper.width / 2) - (Viewer.NODE_WIDTH / 2) * this.zoom, 10);
  179. var onSetNodeSize = function (node) {
  180. node.rect.attr("width", node.minimized ? Viewer.NODE_MINIMIZED_WIDTH : Viewer.NODE_WIDTH * _this.zoom);
  181. node.rect.attr("height", Viewer.NODE_HEIGHT * _this.zoom);
  182. node.text.attr("font-size", 11 * _this.zoom);
  183. };
  184. var onSetPositionPass = function (action, yPosition) {
  185. var node = action.node;
  186. var parent = action.parent !== null ? action.parent : null;
  187. if (action.combineArray !== null) {
  188. for (var i = 0; i < action.combineArray.length; i++) {
  189. var combinedNode = action.combineArray[i].node;
  190. onSetNodeSize(combinedNode);
  191. }
  192. }
  193. onSetNodeSize(node);
  194. if (parent) {
  195. var parentx = parent.node.rect.attr("x");
  196. if (parent.combineArray !== null && parent.combineArray.length > 1) {
  197. parentx += parent.node.rect.attr("width") / 2;
  198. }
  199. _this._setActionPosition(action, parentx, yPosition);
  200. _this._setActionLine(action);
  201. }
  202. var totalSize = 0;
  203. for (var i = 0; i < action.children.length; i++) {
  204. var childNode = action.children[i].node;
  205. totalSize += childNode.rect.attr("width");
  206. }
  207. var nodeWidth = node.rect.attr("width");
  208. var startingPositionX = node.rect.attr("x");
  209. for (var i = 0; i < action.children.length; i++) {
  210. var childAction = action.children[i];
  211. var childNode = childAction.node;
  212. var newPositionX = startingPositionX;
  213. if (childAction.combineArray !== null && childAction.combineArray.length > 1) {
  214. newPositionX -= (childNode.rect.attr("width") / 2) - nodeWidth / 2;
  215. }
  216. var newPositionY = yPosition + Viewer.VERTICAL_OFFSET * _this.zoom;
  217. onSetPositionPass(childAction, newPositionY);
  218. _this._setActionPosition(childAction, newPositionX, newPositionY);
  219. _this._setActionLine(childAction);
  220. }
  221. };
  222. onSetPositionPass(this.root, 10 * this.zoom);
  223. var onGetSizePass = function (action, maxSize) {
  224. var mySize = 0;
  225. if (action.combineArray !== null) {
  226. for (var i = 0; i < action.combineArray.length; i++) {
  227. mySize += action.combineArray[i].node.rect.attr("width");
  228. }
  229. }
  230. else {
  231. mySize = action.node.rect.attr("width");
  232. }
  233. if (mySize > maxSize) {
  234. maxSize = mySize;
  235. }
  236. for (var i = 0; i < action.children.length; i++) {
  237. maxSize = onGetSizePass(action.children[i], maxSize);
  238. }
  239. return maxSize;
  240. };
  241. var onResizeCanvas = function (action) {
  242. var node = action.node;
  243. var nodex = node.rect.attr("x");
  244. var nodey = node.rect.attr("y");
  245. if (nodex < 0 || nodex > _this.paper.width) {
  246. _this.paper.setSize(_this.paper.width + 1000, _this.paper.height);
  247. _this._setActionPosition(_this.root, (_this.paper.width / 2) - (Viewer.NODE_WIDTH / 2) * _this.zoom, 10);
  248. }
  249. if (nodey > _this.paper.height) {
  250. _this.paper.setSize(_this.paper.width, _this.paper.height + 1000);
  251. _this._setActionPosition(_this.root, (_this.paper.width / 2) - (Viewer.NODE_WIDTH / 2) * _this.zoom, 10);
  252. }
  253. };
  254. var widths = new Array();
  255. for (var i = 0; i < this.root.children.length; i++) {
  256. var trigger = this.root.children[i];
  257. var triggerResult = { triggerWidth: onGetSizePass(trigger, 0), childrenWidths: new Array() };
  258. if (trigger.children.length > 0) {
  259. triggerResult.triggerWidth = 0;
  260. }
  261. for (var j = 0; j < trigger.children.length; j++) {
  262. var actionWidth = onGetSizePass(trigger.children[j], 0);
  263. triggerResult.triggerWidth += actionWidth + 15;
  264. triggerResult.childrenWidths.push({
  265. triggerWidth: actionWidth,
  266. childrenWidths: null
  267. });
  268. }
  269. widths.push(triggerResult);
  270. }
  271. var onSetNodePosition = function (action, widthArray, isChild) {
  272. var actionsCount = action.children.length;
  273. var actionsMiddle = actionsCount % 2;
  274. var actionsHasMiddle = actionsMiddle !== 0;
  275. var actionsLeftOffset = 0;
  276. var actionsRightOffset = 0;
  277. var actionWidth = action.node.rect.attr("width");
  278. if (actionsHasMiddle && actionsCount > 1) {
  279. var middle = Math.floor(actionsCount / 2);
  280. actionsLeftOffset += widthArray[middle].triggerWidth / 2;
  281. actionsRightOffset += widthArray[middle].triggerWidth / 2;
  282. }
  283. var leftStart = actionsHasMiddle ? Math.floor(actionsCount / 2) - 1 : (actionsCount / 2) - 1;
  284. for (var i = leftStart; i >= 0; i--) {
  285. var child = action.children[i];
  286. var node = child.node;
  287. var width = (widthArray[i].triggerWidth) + 15;
  288. _this._setActionPosition(action.children[i], node.rect.attr("x") - actionsLeftOffset - (width / 2), node.rect.attr("y"));
  289. _this._setActionLine(child);
  290. onResizeCanvas(child);
  291. actionsLeftOffset += width;
  292. }
  293. var rightStart = actionsHasMiddle ? Math.round(actionsCount / 2) : actionsCount / 2;
  294. for (var i = rightStart; i < actionsCount; i++) {
  295. var child = action.children[i];
  296. var node = child.node;
  297. var width = (widthArray[i].triggerWidth) + 15;
  298. _this._setActionPosition(action.children[i], node.rect.attr("x") + actionsRightOffset + (width / 2), node.rect.attr("y"));
  299. _this._setActionLine(child);
  300. onResizeCanvas(child);
  301. actionsRightOffset += width;
  302. }
  303. };
  304. onSetNodePosition(this.root, widths, false);
  305. for (var i = 0; i < this.root.children.length; i++) {
  306. onSetNodePosition(this.root.children[i], widths[i].childrenWidths, true);
  307. }
  308. };
  309. Viewer.prototype.addAction = function (parent, type, element) {
  310. var node = this._createNode(element.text, type, parent === null);
  311. var action = new ActionsBuilder.Action(node);
  312. if (element.name === "CombineAction") {
  313. action.combineArray = new Array();
  314. var hubElement = ActionsBuilder.Elements.FLOW_CONTROLS[ActionsBuilder.Elements.FLOW_CONTROLS.length - 1];
  315. var hub = this.addAction(action, ActionsBuilder.Type.FLOW_CONTROL, hubElement);
  316. action.hub = hub;
  317. action.addChild(hub);
  318. this._createActionAnimation(hub);
  319. }
  320. action.name = element.name;
  321. action.properties = element.properties;
  322. action.type = type;
  323. for (var i = 0; i < action.properties.length; i++) {
  324. action.propertiesResults.push({ targetType: action.properties[i].targetType, value: action.properties[i].value });
  325. }
  326. if (action.properties !== null && action.properties.length > 0) {
  327. if (action.properties[0].text === "target") {
  328. action.propertiesResults[0].value = this.objectName;
  329. }
  330. }
  331. if (parent !== null) {
  332. if (parent.combineArray === null) {
  333. parent.addChild(action);
  334. }
  335. else if (parent.combineArray !== null && action.name !== "Hub") {
  336. parent.combineArray.push(action);
  337. action.parent = parent;
  338. action.combineAction = parent;
  339. parent.node.text.attr("text", "");
  340. }
  341. }
  342. this._createActionAnimation(action);
  343. return action;
  344. };
  345. Viewer.prototype.traverseGraph = function (start, x, y, traverseCombine) {
  346. if (start === null)
  347. start = this.root;
  348. var result = { action: start, hit: true };
  349. if (start.node.isPointInside(x, y)) {
  350. return result;
  351. }
  352. for (var i = 0; i < start.children.length; i++) {
  353. var action = start.children[i];
  354. if (action.node.isPointInside(x, y)) {
  355. result.hit = true;
  356. result.action = start.children[i];
  357. if (traverseCombine && action.combineArray !== null) {
  358. for (var j = 0; j < action.combineArray.length; j++) {
  359. if (action.combineArray[j].node.isPointInside(x, y)) {
  360. result.action = action.combineArray[j];
  361. break;
  362. }
  363. }
  364. }
  365. return result;
  366. }
  367. result = this.traverseGraph(action, x, y, traverseCombine);
  368. if (result.hit) {
  369. return result;
  370. }
  371. }
  372. result.hit = false;
  373. result.action = null;
  374. return result;
  375. };
  376. Viewer.prototype._setActionPosition = function (action, x, y) {
  377. var node = action.node;
  378. var offsetx = node.rect.attr("x") - x;
  379. var parent = action.parent;
  380. if (parent !== null && parent.combineArray !== null && parent.combineArray.length > 1) {
  381. var parentNode = parent.node;
  382. x = parentNode.rect.attr("x") + (parent.node.rect.attr("width") / 2) - (node.rect.attr("width") / 2);
  383. }
  384. node.rect.attr("x", x);
  385. node.rect.attr("y", y);
  386. var textBBox = node.text.getBBox();
  387. var textWidth = 0;
  388. if (textBBox !== null && textBBox !== undefined) {
  389. textWidth = textBBox.width;
  390. }
  391. node.text.attr("x", x + node.rect.attr("width") / 2 - textWidth / 2);
  392. node.text.attr("y", y + node.rect.attr("height") / 2);
  393. if (action.combineArray !== null && action.combineArray.length > 0) {
  394. var length = 0;
  395. for (var i = 0; i < action.combineArray.length; i++) {
  396. var combinedAction = action.combineArray[i];
  397. var combinedNode = combinedAction.node;
  398. combinedNode.rect.attr("x", node.rect.attr("x") + length);
  399. combinedNode.rect.attr("y", node.rect.attr("y"));
  400. textBBox = combinedNode.text.getBBox();
  401. if (textBBox !== null) {
  402. textWidth = textBBox.width;
  403. }
  404. combinedNode.text.attr("x", combinedNode.rect.attr("x") + combinedNode.rect.attr("width") / 2 - textWidth / 2);
  405. combinedNode.text.attr("y", y + combinedNode.rect.attr("height") / 2);
  406. length += combinedNode.rect.attr("width");
  407. }
  408. node.rect.attr("width", length);
  409. }
  410. for (var i = 0; i < action.children.length; i++) {
  411. var child = action.children[i];
  412. this._setActionPosition(child, child.node.rect.attr("x") - offsetx, y + Viewer.VERTICAL_OFFSET * this.zoom);
  413. this._setActionLine(child);
  414. }
  415. };
  416. Viewer.prototype._setActionLine = function (action) {
  417. if (action.node.line === null) {
  418. return;
  419. }
  420. var node = action.node;
  421. var nodex = node.rect.attr("x");
  422. var nodey = node.rect.attr("y");
  423. var nodeWidth = node.rect.attr("width");
  424. var nodeHeight = node.rect.attr("height");
  425. var parent = action.parent.node;
  426. var parentx = parent.rect.attr("x");
  427. var parenty = parent.rect.attr("y");
  428. var parentWidth = parent.rect.attr("width");
  429. var parentHeight = parent.rect.attr("height");
  430. if (node.detached) {
  431. node.line.attr("path", ["M", nodex, nodey, "L", nodex, nodey]);
  432. return;
  433. }
  434. var line1x = nodex + (nodeWidth / 2);
  435. var line1y = nodey;
  436. var line2y = line1y - (line1y - parenty - parentHeight) / 2;
  437. var line3x = parentx + (parentWidth / 2);
  438. var line4y = parenty + parentHeight;
  439. node.line.attr("path", ["M", line1x, line1y, "L", line1x, line2y, "L", line3x, line2y, "L", line3x, line4y]);
  440. };
  441. Viewer.prototype._createNode = function (text, type, noLine) {
  442. var node = new ActionsBuilder.Node();
  443. var color = this.getNodeColor(type, false);
  444. node.rect = this.paper.rect(20, 20, Viewer.NODE_WIDTH, Viewer.NODE_HEIGHT, 0);
  445. node.rect.attr("fill", color);
  446. node.text = this.paper.text(20, 20, text);
  447. node.text.attr("font-size", 11);
  448. node.text.attr("text-anchor", "start");
  449. node.text.attr("font-family", "Sinkin Sans Light");
  450. if (!noLine) {
  451. node.line = this.paper.path("");
  452. node.line.attr("stroke", color);
  453. }
  454. return node;
  455. };
  456. Viewer.prototype._createActionAnimation = function (action) {
  457. var _this = this;
  458. var node = action.node;
  459. var finished = true;
  460. var nodex = 0;
  461. var nodey = 0;
  462. var onMove = function (dx, dy, x, y) { };
  463. var onStart = function (x, y, event) {
  464. if (node.minimized) {
  465. return;
  466. }
  467. if (finished) {
  468. nodex = node.rect.attr("x");
  469. nodey = node.rect.attr("y");
  470. }
  471. finished = false;
  472. node.rect.animate({
  473. x: node.rect.attr("x") - 10,
  474. y: node.rect.attr("y"),
  475. width: (Viewer.NODE_WIDTH + 20) * _this.zoom,
  476. height: (Viewer.NODE_HEIGHT + 10) * _this.zoom,
  477. opacity: 0.25
  478. }, 500, ">");
  479. };
  480. var onEnd = function (event) {
  481. if (!node.minimized) {
  482. node.rect.animate({
  483. x: nodex,
  484. y: nodey,
  485. width: Viewer.NODE_WIDTH * _this.zoom,
  486. height: Viewer.NODE_HEIGHT * _this.zoom,
  487. opacity: 1.0
  488. }, 500, ">", function () { finished = true; });
  489. }
  490. var dragResult = _this.traverseGraph(null, _this.mousex, _this.mousey, true);
  491. if (dragResult.hit && dragResult.action === action || !dragResult.hit) {
  492. _this.parameters.createParameters(action);
  493. }
  494. else {
  495. if (dragResult.action.children.length > 0 && action.type !== ActionsBuilder.Type.TRIGGER) {
  496. return;
  497. }
  498. if (action.type === ActionsBuilder.Type.TRIGGER && dragResult.action !== _this.root) {
  499. return;
  500. }
  501. if (action.type === ActionsBuilder.Type.ACTION && dragResult.action === _this.root) {
  502. return;
  503. }
  504. if (action.type === ActionsBuilder.Type.FLOW_CONTROL && (dragResult.action === _this.root || dragResult.action.type === ActionsBuilder.Type.FLOW_CONTROL)) {
  505. return;
  506. }
  507. if (action === dragResult.action.parent) {
  508. return;
  509. }
  510. if (action.parent !== null && action.parent.combineArray !== null) {
  511. return;
  512. }
  513. node.rect.stop(node.rect.animation);
  514. node.text.stop(node.text.animation);
  515. node.rect.undrag();
  516. node.text.undrag();
  517. node.rect.attr("opacity", 1.0);
  518. node.rect.attr("width", Viewer.NODE_WIDTH);
  519. node.rect.attr("height", Viewer.NODE_HEIGHT);
  520. if (action.parent !== null) {
  521. action.parent.removeChild(action);
  522. dragResult.action.addChild(action);
  523. _this.update();
  524. _this._createActionAnimation(action);
  525. }
  526. }
  527. };
  528. node.rect.drag(onMove, onStart, onEnd);
  529. node.text.drag(onMove, onStart, onEnd);
  530. };
  531. Viewer._NODE_WIDTH = 150;
  532. Viewer._NODE_HEIGHT = 25;
  533. Viewer._NODE_MINIMIZE_WIDTH = 50;
  534. Viewer._VERTICAL_OFFSET = 70;
  535. Viewer._DEFAULT_INFO_MESSAGE = "Select or add a node to customize actions";
  536. return Viewer;
  537. })();
  538. ActionsBuilder.Viewer = Viewer;
  539. })(ActionsBuilder || (ActionsBuilder = {}));