actionsbuilder.viewer.ts 29 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772
  1. module ActionsBuilder {
  2. export interface TraverseResult {
  3. action: Action;
  4. hit: boolean;
  5. }
  6. export interface UpdateResult {
  7. triggerWidth: number;
  8. childrenWidths: Array<UpdateResult>;
  9. }
  10. export class Viewer {
  11. // Statics
  12. private static _NODE_WIDTH: number = 150;
  13. private static _NODE_HEIGHT: number = 25;
  14. private static _NODE_MINIMIZE_WIDTH: number = 50;
  15. private static _VERTICAL_OFFSET: number = 70;
  16. private static _DEFAULT_INFO_MESSAGE = "Select or add a node to customize actions";
  17. public static get NODE_WIDTH(): number {
  18. return Viewer._NODE_WIDTH;
  19. }
  20. public static get NODE_HEIGHT(): number {
  21. return Viewer._NODE_HEIGHT;
  22. }
  23. public static get NODE_MINIMIZED_WIDTH(): number {
  24. return Viewer._NODE_MINIMIZE_WIDTH;
  25. }
  26. public static get VERTICAL_OFFSET(): number {
  27. return Viewer._VERTICAL_OFFSET;
  28. }
  29. // Members
  30. // Public
  31. public viewerContainer: HTMLElement;
  32. public viewerElement: HTMLElement;
  33. public objectName: string = "Unnamed Object";
  34. public zoom: number = 1.0;
  35. public mousex: number;
  36. public mousey: number;
  37. public paper: Paper;
  38. public root: Action;
  39. public selectedNode: Action;
  40. public parameters: Parameters;
  41. public utils: Utils;
  42. // Private
  43. private _toolbar: Toolbar;
  44. private _contextMenu: ContextMenu;
  45. private _firstUpdate: boolean = true;
  46. /*
  47. * Constructor
  48. * @param type: the root type object (OBJECT or SCENE)
  49. */
  50. constructor(type: number) {
  51. // Get HTML elements
  52. this.viewerContainer = document.getElementById("GraphContainerID");
  53. this.viewerElement = document.getElementById("GraphElementID");
  54. // Create element
  55. this.paper = Raphael("GraphElementID", screen.width, screen.height);
  56. // Configure this
  57. //var name = type === Type.OBJECT ? "Unnamed object" : "Scene";
  58. this.root = this.addAction(null, type, { name: this.objectName, text: this.objectName, properties: [], description: "" });
  59. this.selectedNode = null;
  60. // Configure events
  61. window.addEventListener("resize", (event: Event) => {
  62. this.onResize(event);
  63. });
  64. window.addEventListener("mousemove", (event: MouseEvent) => {
  65. this.onMove(event);
  66. });
  67. this.paper.canvas.addEventListener("click", (event: MouseEvent) => {
  68. this.onClick(event);
  69. });
  70. // Load modules
  71. this._toolbar = new Toolbar(this);
  72. this._contextMenu = new ContextMenu(this);
  73. this.parameters = new Parameters(this);
  74. this.utils = new Utils(this);
  75. // Finish
  76. this.parameters.parametersHelpElement.textContent = Viewer._DEFAULT_INFO_MESSAGE;
  77. }
  78. /*
  79. * Resize event
  80. * @param event: the resize event
  81. */
  82. public onResize(event?: Event): void {
  83. var tools = document.getElementById("ToolsButtonsID");
  84. this.viewerContainer.style.height = window.innerHeight - tools.getBoundingClientRect().height - 25 - 50 + "px";
  85. this.viewerElement.style.height = window.innerHeight - tools.getBoundingClientRect().height - 25 - 50 + "px";
  86. this.parameters.onResize();
  87. this._toolbar.onResize();
  88. if (this.paper.height < window.innerHeight) {
  89. this.paper.setSize(this.paper.width, window.innerHeight);
  90. }
  91. if (this._firstUpdate) {
  92. this.viewerElement.scrollLeft = ((this.viewerElement.scrollWidth / 2) - (this.viewerElement.getBoundingClientRect().width / 2));
  93. this._firstUpdate = false;
  94. }
  95. }
  96. /*
  97. * Handles the onMove event
  98. * @param event: the onMove mouse event
  99. */
  100. public onMove(event: MouseEvent): void {
  101. this.mousex = event.clientX - this.paper.canvas.getBoundingClientRect().left;
  102. this.mousey = event.clientY - this.paper.canvas.getBoundingClientRect().top;
  103. }
  104. /*
  105. * Handles the onClick event to get selected node
  106. * @param event: the onClick mouse event
  107. */
  108. public onClick(event: MouseEvent): void {
  109. if (this._contextMenu.showing) {
  110. return;
  111. }
  112. // Reset selected node
  113. if (this.selectedNode !== null) {
  114. var node = this.selectedNode.node;
  115. node.rect.attr("fill", this.getNodeColor(this.selectedNode.type, node.detached));
  116. }
  117. // Configure new selected node
  118. var result = this.traverseGraph(null, this.mousex, this.mousey, true);
  119. if (result.hit) {
  120. this.selectedNode = result.action;
  121. var node = this.selectedNode.node;
  122. node.rect.attr("fill", this.getSelectedNodeColor(this.selectedNode.type, node.detached));
  123. }
  124. else {
  125. this.selectedNode = null;
  126. this.parameters.clearParameters();
  127. this.parameters.parametersHelpElement.textContent = Viewer._DEFAULT_INFO_MESSAGE;
  128. }
  129. }
  130. /*
  131. * Set the color theme of the viewer
  132. * @param color: the color theme ( ex: "rgb(64, 64, 64)" )
  133. */
  134. public setColorTheme(color: string): void {
  135. this.paper.canvas.style.background = color;
  136. }
  137. /*
  138. * Returns the color according to the given parameters
  139. * @param action: the action used to select the color
  140. * @param detached: if the node is attached to its parent or not
  141. */
  142. public getNodeColor(type: number, detached: boolean): RaphaelColor {
  143. if (detached) {
  144. return Raphael.rgb(96, 122, 14);
  145. }
  146. switch (type) {
  147. case Type.TRIGGER: return Raphael.rgb(133, 154, 185); break;
  148. case Type.ACTION: return Raphael.rgb(182, 185, 132); break;
  149. case Type.FLOW_CONTROL: return Raphael.rgb(185, 132, 140); break;
  150. case Type.OBJECT:
  151. case Type.SCENE: return Raphael.rgb(255, 255, 255); break;
  152. default: break;
  153. }
  154. return null;
  155. }
  156. /*
  157. * Returns the selected node color according to the given parameters
  158. * @param action: the action used to select the color
  159. * @param detached: if the node is attached to its parent or not
  160. */
  161. public getSelectedNodeColor(type: number, detached: boolean): RaphaelColor {
  162. if (detached) {
  163. return Raphael.rgb(96, 122, 14);
  164. }
  165. switch (type) {
  166. case Type.TRIGGER: return Raphael.rgb(41, 129, 255); break;
  167. case Type.ACTION: return Raphael.rgb(255, 220, 42); break;
  168. case Type.FLOW_CONTROL: return Raphael.rgb(255, 41, 53); break;
  169. case Type.OBJECT:
  170. case Type.SCENE: return Raphael.rgb(255, 255, 255); break;
  171. default: break;
  172. }
  173. return null;
  174. }
  175. /*
  176. * Removes the given action from the graph
  177. * @param action: the action to remove
  178. * @param removeChildren: if remove the branch or not
  179. */
  180. public removeAction(action: Action, removeChildren: boolean): void {
  181. // If selected node is combine
  182. if (action.parent !== null && action.parent.hub === action) {
  183. this.removeAction(action.parent, false);
  184. return;
  185. }
  186. // Basic suppress
  187. this.removeNode(action.node);
  188. if (action.combineArray !== null) {
  189. this.removeNode(action.hub.node);
  190. // Remove combine array
  191. for (var i = 0; i < action.combineArray.length; i++) {
  192. this.removeNode(action.combineArray[i].node);
  193. }
  194. action.combineArray.length = 0;
  195. }
  196. if (removeChildren) {
  197. for (var i = 0; i < action.children.length; i++) {
  198. this.removeAction(action.children[i], removeChildren);
  199. }
  200. action.clearChildren();
  201. }
  202. else {
  203. for (var i = 0; i < action.children.length; i++) {
  204. action.parent.addChild(action.children[i]);
  205. action.children[i].parent = action.parent;
  206. }
  207. }
  208. }
  209. /*
  210. * Removes the given node (not the action)
  211. * @param node: the node to remove
  212. */
  213. public removeNode(node: Node): void {
  214. node.rect.remove();
  215. node.text.remove();
  216. if (node.line !== null) {
  217. node.line.remove();
  218. }
  219. }
  220. /*
  221. * Updates the graph viewer
  222. */
  223. public update(): void {
  224. // Set root position
  225. this._setActionPosition(this.root, (this.paper.width / 2) - (Viewer.NODE_WIDTH / 2) * this.zoom, 10);
  226. // Sets node size
  227. var onSetNodeSize = (node: Node) => {
  228. node.rect.attr("width", node.minimized ? Viewer.NODE_MINIMIZED_WIDTH : Viewer.NODE_WIDTH * this.zoom);
  229. node.rect.attr("height", Viewer.NODE_HEIGHT * this.zoom);
  230. node.text.attr("font-size", 11 * this.zoom);
  231. };
  232. // First pass: set actions positions according to parents
  233. var onSetPositionPass = (action: Action, yPosition: number): void => {
  234. var node = action.node;
  235. var parent = action.parent !== null ? action.parent : null;
  236. // Set node properties (size, text size, etc.)
  237. if (action.combineArray !== null) {
  238. for (var i = 0; i < action.combineArray.length; i++) {
  239. var combinedNode = action.combineArray[i].node;
  240. onSetNodeSize(combinedNode);
  241. }
  242. }
  243. onSetNodeSize(node);
  244. // Set position from parent
  245. if (parent) {
  246. var parentx = parent.node.rect.attr("x");
  247. if (parent.combineArray !== null && parent.combineArray.length > 1) {
  248. parentx += parent.node.rect.attr("width") / 2;
  249. }
  250. this._setActionPosition(action, parentx, yPosition);
  251. this._setActionLine(action);
  252. }
  253. // Calculate total width for current action
  254. var totalSize = 0;
  255. for (var i = 0; i < action.children.length; i++) {
  256. var childNode = action.children[i].node;
  257. totalSize += childNode.rect.attr("width");
  258. }
  259. // Get values to place nodes according to the parent position
  260. var nodeWidth = node.rect.attr("width");
  261. var startingPositionX = node.rect.attr("x");
  262. // Set children positions
  263. for (var i = 0; i < action.children.length; i++) {
  264. var childAction = action.children[i];
  265. var childNode = childAction.node;
  266. var newPositionX = startingPositionX;
  267. if (childAction.combineArray !== null && childAction.combineArray.length > 1) {
  268. newPositionX -= (childNode.rect.attr("width") / 2) - nodeWidth / 2;
  269. }
  270. var newPositionY = yPosition + Viewer.VERTICAL_OFFSET * this.zoom;
  271. onSetPositionPass(childAction, newPositionY);
  272. this._setActionPosition(childAction, newPositionX, newPositionY);
  273. this._setActionLine(childAction);
  274. }
  275. };
  276. onSetPositionPass(this.root, 10 * this.zoom);
  277. // Seconds pass, get sizes of groups
  278. var onGetSizePass = (action: Action, maxSize: number): number => {
  279. var mySize = 0;
  280. if (action.combineArray !== null) {
  281. for (var i = 0; i < action.combineArray.length; i++) {
  282. mySize += action.combineArray[i].node.rect.attr("width");
  283. }
  284. }
  285. else {
  286. mySize = action.node.rect.attr("width");
  287. }
  288. if (mySize > maxSize) {
  289. maxSize = mySize;
  290. }
  291. for (var i = 0; i < action.children.length; i++) {
  292. maxSize = onGetSizePass(action.children[i], maxSize);
  293. }
  294. return maxSize;
  295. };
  296. // Resize canvas
  297. var onResizeCanvas = (action: Action): void => {
  298. var node = action.node;
  299. var nodex = node.rect.attr("x");
  300. var nodey = node.rect.attr("y");
  301. if (nodex < 0 || nodex > this.paper.width) {
  302. this.paper.setSize(this.paper.width + 1000, this.paper.height);
  303. this._setActionPosition(this.root, (this.paper.width / 2) - (Viewer.NODE_WIDTH / 2) * this.zoom, 10);
  304. }
  305. if (nodey > this.paper.height) {
  306. this.paper.setSize(this.paper.width, this.paper.height + 1000);
  307. this._setActionPosition(this.root, (this.paper.width / 2) - (Viewer.NODE_WIDTH / 2) * this.zoom, 10);
  308. }
  309. };
  310. var widths = new Array<UpdateResult>();
  311. for (var i = 0; i < this.root.children.length; i++) {
  312. var trigger = this.root.children[i];
  313. var triggerResult: UpdateResult = { triggerWidth: onGetSizePass(trigger, 0), childrenWidths: new Array<UpdateResult>() };
  314. if (trigger.children.length > 0) {
  315. triggerResult.triggerWidth = 0;
  316. }
  317. for (var j = 0; j < trigger.children.length; j++) {
  318. var actionWidth = onGetSizePass(trigger.children[j], 0);
  319. triggerResult.triggerWidth += actionWidth + 15;
  320. triggerResult.childrenWidths.push({
  321. triggerWidth: actionWidth,
  322. childrenWidths: null
  323. });
  324. }
  325. widths.push(triggerResult);
  326. }
  327. // Third pass, set positions of nodes
  328. var onSetNodePosition = (action: Action, widthArray: Array<UpdateResult>, isChild: boolean) => {
  329. var actionsCount = action.children.length;
  330. var actionsMiddle = actionsCount % 2;
  331. var actionsHasMiddle = actionsMiddle !== 0;
  332. var actionsLeftOffset = 0;
  333. var actionsRightOffset = 0;
  334. var actionWidth = action.node.rect.attr("width");
  335. if (actionsHasMiddle && actionsCount > 1) {
  336. var middle = Math.floor(actionsCount / 2);
  337. actionsLeftOffset += widthArray[middle].triggerWidth / 2;
  338. actionsRightOffset += widthArray[middle].triggerWidth / 2;
  339. }
  340. // Move left
  341. var leftStart = actionsHasMiddle ? Math.floor(actionsCount / 2) - 1 : (actionsCount / 2) - 1;
  342. for (var i = leftStart; i >= 0; i--) {
  343. var child = action.children[i];
  344. var node = child.node;
  345. var width = (widthArray[i].triggerWidth) + 15;
  346. this._setActionPosition(action.children[i], node.rect.attr("x") - actionsLeftOffset - (width / 2), node.rect.attr("y"));
  347. this._setActionLine(child);
  348. onResizeCanvas(child);
  349. actionsLeftOffset += width;
  350. }
  351. // Move right
  352. var rightStart = actionsHasMiddle ? Math.round(actionsCount / 2) : actionsCount / 2;
  353. for (var i = rightStart; i < actionsCount; i++) {
  354. var child = action.children[i];
  355. var node = child.node;
  356. var width = (widthArray[i].triggerWidth) + 15;
  357. this._setActionPosition(action.children[i], node.rect.attr("x") + actionsRightOffset + (width / 2), node.rect.attr("y"));
  358. this._setActionLine(child);
  359. onResizeCanvas(child);
  360. actionsRightOffset += width;
  361. }
  362. };
  363. onSetNodePosition(this.root, widths, false);
  364. for (var i = 0; i < this.root.children.length; i++) {
  365. onSetNodePosition(this.root.children[i], widths[i].childrenWidths, true);
  366. }
  367. }
  368. /*
  369. * Adds an action to the graph viewer and returns it
  370. * @param parent: the parent action
  371. * @param type: the action type
  372. * @param element: the Actions Builder type (TRIGGERS, ACTIONS, FLOW_CONTROLS)
  373. */
  374. public addAction(parent: Action, type: number, element: Element): Action {
  375. var node = this._createNode(element.text, type, parent === null);
  376. var action = new Action(node);
  377. if (element.name === "CombineAction") {
  378. action.combineArray = new Array<Action>();
  379. var hubElement = Elements.FLOW_CONTROLS[Elements.FLOW_CONTROLS.length - 1];
  380. var hub = this.addAction(action, Type.FLOW_CONTROL, hubElement);
  381. action.hub = hub;
  382. action.addChild(hub);
  383. this._createActionAnimation(hub);
  384. }
  385. action.name = element.name;
  386. action.properties = element.properties;
  387. action.type = type;
  388. // Configure properties
  389. for (var i = 0; i < action.properties.length; i++) {
  390. action.propertiesResults.push({ targetType: action.properties[i].targetType, value: action.properties[i].value });
  391. }
  392. if (action.properties !== null && action.properties.length > 0) {
  393. if (action.properties[0].text === "target") {
  394. action.propertiesResults[0].value = this.objectName;
  395. }
  396. }
  397. if (parent !== null) {
  398. if (parent.combineArray === null) {
  399. parent.addChild(action);
  400. }
  401. else if (parent.combineArray !== null && action.name !== "Hub") {
  402. parent.combineArray.push(action);
  403. action.parent = parent;
  404. action.combineAction = parent;
  405. parent.node.text.attr("text", "");
  406. }
  407. }
  408. // Create animation
  409. this._createActionAnimation(action);
  410. return action;
  411. }
  412. /*
  413. * Traverses the graph viewer and returns if an action
  414. * is selected at coordinates (x, y)
  415. * @param start: the start node. Can be null
  416. * @param x: the x coordinate
  417. * @param y: the y coordinate
  418. * @param traverseCombine: if we traverse combine actions children
  419. */
  420. public traverseGraph(start: Action, x: number, y: number, traverseCombine: boolean): TraverseResult {
  421. if (start === null) start = this.root;
  422. var result: TraverseResult = { action: start, hit: true };
  423. if (start.node.isPointInside(x, y)) {
  424. return result;
  425. }
  426. for (var i = 0; i < start.children.length; i++) {
  427. var action = start.children[i];
  428. if (action.node.isPointInside(x, y)) {
  429. result.hit = true;
  430. result.action = start.children[i];
  431. if (traverseCombine && action.combineArray !== null) {
  432. for (var j = 0; j < action.combineArray.length; j++) {
  433. if (action.combineArray[j].node.isPointInside(x, y)) {
  434. result.action = action.combineArray[j];
  435. break;
  436. }
  437. }
  438. }
  439. return result;
  440. }
  441. result = this.traverseGraph(action, x, y, traverseCombine);
  442. if (result.hit) {
  443. return result;
  444. }
  445. }
  446. result.hit = false;
  447. result.action = null;
  448. return result;
  449. }
  450. /*
  451. * Sets the action's position (node)
  452. * @param action: the action to place
  453. * @param x: the x position of the action
  454. * @param y: the y position of the action
  455. */
  456. public _setActionPosition(action: Action, x: number, y: number): void {
  457. var node = action.node;
  458. var offsetx = node.rect.attr("x") - x;
  459. var parent = action.parent;
  460. if (parent !== null && parent.combineArray !== null && parent.combineArray.length > 1) {
  461. var parentNode = parent.node;
  462. x = parentNode.rect.attr("x") + (parent.node.rect.attr("width") / 2) - (node.rect.attr("width") / 2);
  463. }
  464. node.rect.attr("x", x);
  465. node.rect.attr("y", y);
  466. var textBBox = node.text.getBBox();
  467. var textWidth = 0;
  468. if (textBBox !== null && textBBox !== undefined) {
  469. textWidth = textBBox.width;
  470. }
  471. node.text.attr("x", x + node.rect.attr("width") / 2 - textWidth / 2);
  472. node.text.attr("y", y + node.rect.attr("height") / 2);
  473. if (action.combineArray !== null && action.combineArray.length > 0) {
  474. var length = 0;
  475. for (var i = 0; i < action.combineArray.length; i++) {
  476. var combinedAction = action.combineArray[i];
  477. var combinedNode = combinedAction.node;
  478. combinedNode.rect.attr("x", node.rect.attr("x") + length);
  479. combinedNode.rect.attr("y", node.rect.attr("y"));
  480. textBBox = combinedNode.text.getBBox();
  481. if (textBBox !== null) {
  482. textWidth = textBBox.width;
  483. }
  484. combinedNode.text.attr("x", combinedNode.rect.attr("x") + combinedNode.rect.attr("width") / 2 - textWidth / 2);
  485. combinedNode.text.attr("y", y + combinedNode.rect.attr("height") / 2);
  486. length += combinedNode.rect.attr("width");
  487. }
  488. node.rect.attr("width", length);
  489. }
  490. for (var i = 0; i < action.children.length; i++) {
  491. var child = action.children[i];
  492. this._setActionPosition(child, child.node.rect.attr("x") - offsetx, y + Viewer.VERTICAL_OFFSET * this.zoom);
  493. this._setActionLine(child);
  494. }
  495. }
  496. /*
  497. * Configures the line (link) between the action and its parent
  498. * @param action: the action to configure
  499. */
  500. private _setActionLine(action: Action): void {
  501. if (action.node.line === null) {
  502. return;
  503. }
  504. var node = action.node;
  505. var nodex = node.rect.attr("x");
  506. var nodey = node.rect.attr("y");
  507. var nodeWidth = node.rect.attr("width");
  508. var nodeHeight = node.rect.attr("height");
  509. var parent = action.parent.node;
  510. var parentx = parent.rect.attr("x");
  511. var parenty = parent.rect.attr("y");
  512. var parentWidth = parent.rect.attr("width");
  513. var parentHeight = parent.rect.attr("height");
  514. if (node.detached) {
  515. node.line.attr("path", ["M", nodex, nodey, "L", nodex, nodey]);
  516. return;
  517. }
  518. var line1x = nodex + (nodeWidth / 2);
  519. var line1y = nodey;
  520. var line2y = line1y - (line1y - parenty - parentHeight) / 2;
  521. var line3x = parentx + (parentWidth / 2);
  522. var line4y = parenty + parentHeight;
  523. node.line.attr("path", ["M", line1x, line1y, "L", line1x, line2y, "L", line3x, line2y, "L", line3x, line4y]);
  524. }
  525. /*
  526. * Creates and returns a node
  527. * @param text: the text to draw in the nde
  528. * @param color: the node's color
  529. * @param noLine: if draw a line to the parent or not
  530. */
  531. public _createNode(text: string, type: number, noLine: boolean): Node {
  532. var node = new Node();
  533. var color = this.getNodeColor(type, false);
  534. node.rect = this.paper.rect(20, 20, Viewer.NODE_WIDTH, Viewer.NODE_HEIGHT, 0);
  535. node.rect.attr("fill", color);
  536. node.text = this.paper.text(20, 20, text);
  537. node.text.attr("font-size", 11);
  538. node.text.attr("text-anchor", "start");
  539. node.text.attr("font-family", "Sinkin Sans Light");
  540. if (!noLine) {
  541. node.line = this.paper.path("");
  542. node.line.attr("stroke", color);
  543. }
  544. return node;
  545. }
  546. /*
  547. * Creates the drag animation
  548. * @param action: the action to animate
  549. */
  550. private _createActionAnimation(action: Action): void {
  551. var node = action.node;
  552. var finished = true;
  553. var nodex: number = 0;
  554. var nodey: number = 0;
  555. var onMove = (dx: number, dy: number, x: number, y: number) =>
  556. { };
  557. var onStart = (x: number, y: number, event: MouseEvent) => {
  558. if (node.minimized) {
  559. return;
  560. }
  561. if (finished) {
  562. nodex = node.rect.attr("x");
  563. nodey = node.rect.attr("y");
  564. }
  565. finished = false;
  566. node.rect.animate({
  567. x: node.rect.attr("x") - 10,
  568. y: node.rect.attr("y"),
  569. width: (Viewer.NODE_WIDTH + 20) * this.zoom,
  570. height: (Viewer.NODE_HEIGHT + 10) * this.zoom,
  571. opacity: 0.25
  572. }, 500, ">");
  573. };
  574. var onEnd = (event: MouseEvent) => {
  575. if (!node.minimized) {
  576. node.rect.animate({
  577. x: nodex,
  578. y: nodey,
  579. width: Viewer.NODE_WIDTH * this.zoom,
  580. height: Viewer.NODE_HEIGHT * this.zoom,
  581. opacity: 1.0
  582. }, 500, ">", () => { finished = true; });
  583. }
  584. var dragResult = this.traverseGraph(null, this.mousex, this.mousey, true);
  585. if (dragResult.hit && dragResult.action === action || !dragResult.hit) {
  586. // Create parameters. Action can be null
  587. this.parameters.createParameters(action);
  588. }
  589. else {
  590. // Manage drag'n'drop
  591. if (dragResult.action.children.length > 0 && action.type !== Type.TRIGGER) {
  592. return;
  593. }
  594. if (action.type === Type.TRIGGER && dragResult.action !== this.root) {
  595. return;
  596. }
  597. if (action.type === Type.ACTION && dragResult.action === this.root) {
  598. return;
  599. }
  600. if (action.type === Type.FLOW_CONTROL && (dragResult.action === this.root || dragResult.action.type === Type.FLOW_CONTROL)) {
  601. return;
  602. }
  603. if (action === dragResult.action.parent) {
  604. return;
  605. }
  606. if (action.parent !== null && action.parent.combineArray !== null) { // Musn't move hubs of combine actions
  607. return;
  608. }
  609. // Reset node
  610. node.rect.stop(node.rect.animation);
  611. node.text.stop(node.text.animation);
  612. node.rect.undrag();
  613. node.text.undrag();
  614. node.rect.attr("opacity", 1.0);
  615. node.rect.attr("width", Viewer.NODE_WIDTH);
  616. node.rect.attr("height", Viewer.NODE_HEIGHT);
  617. if (action.parent !== null) {
  618. // Configure drag'n'drop
  619. action.parent.removeChild(action);
  620. dragResult.action.addChild(action);
  621. this.update();
  622. this._createActionAnimation(action);
  623. }
  624. }
  625. };
  626. node.rect.drag(onMove, onStart, onEnd);
  627. node.text.drag(onMove, onStart, onEnd);
  628. }
  629. }
  630. }