GroupAction.js 26 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784
  1. 'use strict';
  2. function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
  3. /**
  4. * Licensed Materials - Property of IBM
  5. * IBM Cognos Products: BI Cloud (C) Copyright IBM Corp. 2019, 2020
  6. * US Government Users Restricted Rights - Use, duplication or disclosure restricted by GSA ADP Schedule Contract with IBM Corp.
  7. */
  8. define(['../../../../lib/@waca/dashboard-common/dist/ui/interaction/Utils', '../../../../app/nls/StringResources', 'jquery', '../../../../lib/@waca/dashboard-common/dist/core/APIFactory', '../../../../lib/@waca/dashboard-common/dist/api/ContentActionsProviderAPI', '../../../../dashboard/util/PxPercentUtil', '../../../../lib/@waca/dashboard-common/dist/ui/interaction/Utils'], function (Utils, stringResources, $, APIFactory, ContentActionsProviderAPI, PxPercentUtil, utils) {
  9. var GroupAction = function () {
  10. function GroupAction(_ref) {
  11. var features = _ref.features;
  12. _classCallCheck(this, GroupAction);
  13. if (features) {
  14. this.dashboard = features.API;
  15. features.ContentActions.registerProvider('group', this.getAPI());
  16. this.deleteAction = features.deleteAction;
  17. this._icons = features.Icons;
  18. }
  19. this._handlers = [];
  20. }
  21. GroupAction.prototype.getAPI = function getAPI() {
  22. if (!this._api) {
  23. this._api = APIFactory.createAPI(this, [ContentActionsProviderAPI]);
  24. }
  25. return this._api;
  26. };
  27. GroupAction.prototype.getLifeCycleHandlers = function getLifeCycleHandlers() {
  28. return [{
  29. name: 'post:dashboard.initialize',
  30. action: this.postDashboardInitialize.bind(this)
  31. }, {
  32. name: 'dashboard.layout.interactions.ready',
  33. action: this.onDashboardLayoutInteractionsReady.bind(this)
  34. }];
  35. };
  36. GroupAction.prototype.postDashboardInitialize = function postDashboardInitialize() {
  37. if (this.dashboard) {
  38. this.controller = this.dashboard.getFeature('InteractionController.internal');
  39. this.selectionHandler = this.controller.selectionHandler;
  40. this.onDashboardLayoutInteractionsReady();
  41. }
  42. return Promise.resolve();
  43. };
  44. GroupAction.prototype.onDashboardLayoutInteractionsReady = function onDashboardLayoutInteractionsReady() {
  45. if (this.selectionHandler) {
  46. this.deRegisterEvents();
  47. this._handlers.push(this.selectionHandler.on('selection:before', this.beforeSelection, this));
  48. this.deleteAction.setOnBeforeDeleteCallback(this.onBeforeDelete.bind(this));
  49. var resize = this.controller.getInteraction('resize');
  50. if (resize) {
  51. this._handlers.push(resize.on('resize:done', this.recalculateGroupRect, this));
  52. }
  53. var moveGroupContent = this.controller.getInteraction('moveGroupContent');
  54. if (moveGroupContent) {
  55. this._handlers.push(moveGroupContent.on('moveGroupContent:done', this.recalculateGroupRect, this));
  56. }
  57. }
  58. };
  59. GroupAction.prototype.deRegisterEvents = function deRegisterEvents() {
  60. if (this._handlers) {
  61. this._handlers.forEach(function (handler) {
  62. handler.remove && handler.remove();
  63. });
  64. }
  65. this._handlers = [];
  66. };
  67. GroupAction.prototype.destroy = function destroy() {
  68. this.deRegisterEvents();
  69. this.dashboard = null;
  70. this.controller = null;
  71. this.selectionHandler = null;
  72. this.events = null;
  73. this._handlers = null;
  74. };
  75. GroupAction.prototype.getNodes = function getNodes(idList) {
  76. return Utils.getNodes(this.controller, idList);
  77. };
  78. GroupAction.prototype.isGroupEnabled = function isGroupEnabled(idList) {
  79. var hasSameParent = false;
  80. var nodes = this.getNodes(idList);
  81. if (nodes.length > 0) {
  82. var parent = null;
  83. // If all node have the same parents, then enable the group button.
  84. hasSameParent = !nodes.some(function (node) {
  85. if (parent && parent !== node.parentNode) {
  86. return true;
  87. }
  88. parent = node.parentNode;
  89. });
  90. }
  91. return this.dashboard.getMode() === this.dashboard.MODES.EDIT && hasSameParent && nodes.length > 1 && !this._getTopGroup(nodes[0]);
  92. };
  93. GroupAction.prototype.isUnGroupEnabled = function isUnGroupEnabled(idList) {
  94. var _this = this;
  95. var isGroups = false;
  96. var nodes = this.getNodes(idList);
  97. if (nodes.length > 0) {
  98. isGroups = !nodes.some(function (node) {
  99. return !$(node).hasClass('pagegroup') && !_this._getTopGroup(node);
  100. });
  101. }
  102. return this.dashboard.getMode() === this.dashboard.MODES.EDIT && isGroups;
  103. };
  104. GroupAction.prototype.getContentActionList = function getContentActionList(idList) {
  105. var contentActionList = [];
  106. if (this.isGroupEnabled(idList)) {
  107. contentActionList.push({
  108. name: 'group',
  109. label: stringResources.get('toolbarActionGroup'),
  110. icon: this._icons.getIcon('dashboard-group').id,
  111. type: 'Button',
  112. actions: {
  113. apply: this.groupContent.bind(this, idList)
  114. }
  115. });
  116. }
  117. if (this.isUnGroupEnabled(idList)) {
  118. contentActionList.push({
  119. name: 'ungroup',
  120. label: stringResources.get('toolbarActionUngroup'),
  121. icon: this._icons.getIcon('dashboard-ungroup').id,
  122. type: 'Button',
  123. actions: {
  124. apply: this.ungroupContent.bind(this, idList)
  125. }
  126. });
  127. }
  128. return contentActionList;
  129. };
  130. /**
  131. * Return the top group of a given node.
  132. *
  133. * @param node
  134. * @returns
  135. */
  136. GroupAction.prototype._getTopGroup = function _getTopGroup(node) {
  137. var nTopGroup = null;
  138. var $groups = $(node).parents('.pagegroup');
  139. if ($groups.length > 0) {
  140. nTopGroup = $groups[$groups.length - 1];
  141. }
  142. return nTopGroup;
  143. };
  144. /**
  145. * Called when the before selection event is triggered. Here we decide which element to select based on the following rules.
  146. * 1- No existing selection and we select an item inside a group, then we select the top group.
  147. * 2- A group is already selected and we select an item inside the same group, then we select the child item
  148. * 3- A group child is already selected and we select an item outside of the group, then the existing child item will be unselected and the top group will be selected.
  149. *
  150. *
  151. * @param payload
  152. */
  153. GroupAction.prototype.beforeSelection = function beforeSelection(payload) {
  154. var selectedNode = payload.newSelection;
  155. var existingSelections = payload.currentSelection;
  156. var nSelectedGroup = this._getTopGroup(selectedNode);
  157. var selectionCount = existingSelections.length;
  158. if (selectionCount > 0 && this.handleMultipleSelection(existingSelections, nSelectedGroup, payload)) {
  159. return;
  160. }
  161. if (nSelectedGroup) {
  162. if (selectionCount === 1 && existingSelections[0] === nSelectedGroup && payload.eventType !== 'mousedown') {
  163. // selecting a child of the group. We don't select on mouse down to give a chance if there is a drag event.
  164. this.selectionHandler.deselectAll();
  165. } else {
  166. payload.newSelection = nSelectedGroup;
  167. }
  168. }
  169. };
  170. /**
  171. * Handles the selection when we already have nodes that are current selected using the following rules:
  172. *
  173. * - If the current selection is not part of the same group. We select the groups.
  174. * - If the current selection is also part of the same group. We allow the selection.
  175. *
  176. * @param existingSelections
  177. * @param nSelectedGroup
  178. * @param payload
  179. * @returns {Boolean} - indicate whether we handled the selection and we want to stop any further handling
  180. */
  181. GroupAction.prototype.handleMultipleSelection = function handleMultipleSelection(existingSelections, nSelectedGroup, payload) {
  182. var nCurrentSelectionGroup = this._getTopGroup(existingSelections[0]);
  183. if (nCurrentSelectionGroup) {
  184. if (nCurrentSelectionGroup !== nSelectedGroup) {
  185. // the current selection is not part of the same group. We select the groups.
  186. this.selectionHandler.deselectAll();
  187. this.selectionHandler.selectNode(nCurrentSelectionGroup);
  188. if (nSelectedGroup) {
  189. payload.newSelection = nSelectedGroup;
  190. }
  191. }
  192. // the current selection is also part of the same group. We allow the selection.
  193. return true;
  194. }
  195. return false;
  196. };
  197. /**
  198. * Group a given set of node. This method will create a new div and move the element inside it while maintaining the position/orientation of all children
  199. * @param nodes
  200. * @param e
  201. * @return {*} the promise that indicate the operation is finished
  202. */
  203. GroupAction.prototype.groupContent = function groupContent(idList, e) {
  204. var _this2 = this;
  205. var nodes = this.getNodes(idList);
  206. // Build the group options and update the layout model
  207. if (nodes.length <= 0) {
  208. return;
  209. }
  210. var nodesPositions = [];
  211. for (var i = 0, iLen = nodes.length; i < iLen; i++) {
  212. var n = nodes[i];
  213. if (n._layout) {
  214. var position = Utils.position(n);
  215. var bounds = Utils.widgetSize(n);
  216. nodesPositions.push({ position: position, bounds: bounds });
  217. }
  218. }
  219. // find the model where we are adding the group
  220. var parentLayout = nodes[0]._layout.parentLayout;
  221. var parentModel = parentLayout.model;
  222. // Calculate the size and position of the group
  223. var rect = this.calculateGroupRect(nodes);
  224. var spec = {
  225. type: 'group',
  226. style: {
  227. top: rect.top + 'px',
  228. left: rect.left + 'px',
  229. width: rect.width + 'px',
  230. height: rect.height + 'px'
  231. }
  232. };
  233. var content = {
  234. containerId: parentModel.id,
  235. spec: spec
  236. };
  237. var canvasApi = this.dashboard.getCanvas();
  238. var transactionApi = this.dashboard.getFeature('Transaction');
  239. var transactionToken = transactionApi.startTransaction();
  240. return canvasApi.addContent(content, transactionToken).then(function (groupContentApi) {
  241. var containerId = groupContentApi.getId();
  242. // TODO - the selection API must be revisited so that we don't have to wait for the view
  243. // should use canvasApi.selectContent([containerId]); here
  244. return _this2.controller.layoutController.layoutReady(containerId).then(function (view) {
  245. var contentIds = nodes.map(function (node) {
  246. return node._layout.model.id;
  247. });
  248. var contentList = canvasApi.moveContent(containerId, contentIds, transactionToken);
  249. for (var _i = 0, _iLen = nodes.length; _i < _iLen; _i++) {
  250. var _n = nodes[_i];
  251. if (_n._layout) {
  252. var _position = nodesPositions[_i].position;
  253. var _bounds = nodesPositions[_i].bounds;
  254. var styles = {
  255. left: _position.left - rect.left + 'px',
  256. top: _position.top - rect.top + 'px',
  257. width: _bounds.width + 'px',
  258. height: _bounds.height + 'px'
  259. };
  260. PxPercentUtil.changePixelPropertiesToPercent(styles, rect);
  261. contentList[_i].setPropertyValue('left', styles.left, transactionToken);
  262. contentList[_i].setPropertyValue('top', styles.top, transactionToken);
  263. contentList[_i].setPropertyValue('width', styles.width, transactionToken);
  264. contentList[_i].setPropertyValue('height', styles.height, transactionToken);
  265. }
  266. }
  267. transactionApi.endTransaction(transactionToken);
  268. // Select the group after it is created
  269. _this2.selectionHandler.deselectAll();
  270. // pass the flag isTouch to properly render the resize edges for touch device.
  271. view && _this2.selectionHandler.selectNode(view.domNode, {
  272. 'isTouch': e ? e.type === 'touchstart' : false
  273. });
  274. });
  275. });
  276. };
  277. /**
  278. * Ungroup a set of groups. This method will remove all the children of a group and move it to the parent of the group and delete the group div
  279. * We maintain the position and orientation of elements that are being ungrouped.
  280. * @param e
  281. */
  282. GroupAction.prototype.ungroupContent = function ungroupContent(idList, e, undoRedoTransactionId) {
  283. var selectedNodes = this.getNodes(idList);
  284. var transactionApi = this.dashboard.getFeature('Transaction');
  285. var transactionToken = void 0;
  286. // We use the undo/redo transaction id to group actions like ungrouping an item then resizing the group to fit the new content
  287. if (!undoRedoTransactionId) {
  288. transactionToken = transactionApi.startTransaction();
  289. } else {
  290. transactionToken = transactionApi.startTransactionById(undoRedoTransactionId);
  291. }
  292. this.selectionHandler.deselectAll();
  293. var nodesToSelect = [];
  294. if ($(selectedNodes[0]).hasClass('pagegroup')) {
  295. // if we selected a group, we will ungroup the entire group.
  296. nodesToSelect = this.ungroupWholeGroups(selectedNodes, transactionToken);
  297. } else {
  298. this.ungroupIndividualNodes(selectedNodes, transactionToken);
  299. nodesToSelect = selectedNodes;
  300. }
  301. transactionApi.endTransaction(transactionToken);
  302. // select the nodes
  303. var i, n;
  304. for (i = nodesToSelect.length - 1; i >= 0; i--) {
  305. n = nodesToSelect[i];
  306. this.selectionHandler.selectNode(n, {
  307. 'isTouch': e ? e.type === 'touchstart' : false
  308. });
  309. }
  310. return nodesToSelect;
  311. };
  312. /**
  313. * Helper function used to get the operation needed to move a node outside of a group
  314. * This method calcualtes the new position taking into consideration the angle of the group
  315. * @param n - node to be moved
  316. * @param group - indicate where we want to move the node
  317. * @param angle - angle of the parent group
  318. */
  319. GroupAction.prototype._calculateNewStyleForEachNode = function _calculateNewStyleForEachNode(n, group, angle) {
  320. var parentLayout = group._layout.parentLayout;
  321. var nodeAngle = Utils.getAngleDegree(n);
  322. var clientRect = n.getBoundingClientRect();
  323. var coords = Utils.getRotatedCoordinates(parentLayout.domNode, clientRect.left, clientRect.top);
  324. var cs = window.getComputedStyle(n);
  325. var fullAngle = angle + nodeAngle;
  326. var bounds = Utils.widgetSize(n);
  327. return {
  328. height: bounds.height + 'px',
  329. width: bounds.width + 'px',
  330. top: coords.y - Utils.getStyleIntValue(cs.marginTop) + 'px',
  331. left: coords.x - Utils.getStyleIntValue(cs.marginLeft) + 'px',
  332. rotateAngle: fullAngle
  333. };
  334. };
  335. /**
  336. * Add the model operation required to ungroup the content of this group.
  337. *
  338. * @returns all the nodes inside the group
  339. */
  340. GroupAction.prototype._ungroupGroupNode = function _ungroupGroupNode(groupNode, transactionToken) {
  341. // DOM operation
  342. var nodesToSelect = [];
  343. var nodes = Utils.getContentWithoutDecoration(groupNode);
  344. var node, angle;
  345. var newStyles = {};
  346. for (var i = 0, iLen = nodes.length; i < iLen; i++) {
  347. node = nodes[i];
  348. angle = Utils.getAngleDegree(groupNode);
  349. newStyles[node._layout.id] = this._calculateNewStyleForEachNode(node, groupNode, angle);
  350. nodesToSelect.push(node);
  351. }
  352. // contentApi() operation
  353. // 1. move all inside nodes to group's parent
  354. // 2. recover all the properties
  355. // 3. delete group
  356. var canvasApi = this.dashboard.getCanvas();
  357. var groupApi = canvasApi.getContent(groupNode._layout.id);
  358. var contentList = canvasApi.moveContent(groupApi.getContainer().getId(), groupApi.getChildren().map(function (content) {
  359. return content.getId();
  360. }), transactionToken);
  361. canvasApi.removeContent(groupApi.getId(), transactionToken);
  362. contentList.forEach(function (content) {
  363. var contentId = content.getId();
  364. var propNameList = Object.keys(newStyles[contentId]);
  365. propNameList.forEach(function (propName) {
  366. content.setPropertyValue(propName, newStyles[contentId][propName], transactionToken);
  367. });
  368. });
  369. return nodesToSelect;
  370. };
  371. /**
  372. * Ungroup a list of groupNodes
  373. *
  374. * @param groupNodes
  375. * @returns {Array}
  376. */
  377. GroupAction.prototype.ungroupWholeGroups = function ungroupWholeGroups(groupNodes, transactionToken) {
  378. var nodesToSelect = [];
  379. // we are ungroup a whole group
  380. var i = void 0,
  381. iLen = void 0;
  382. for (i = 0, iLen = groupNodes.length; i < iLen; i++) {
  383. var group = groupNodes[i];
  384. var ungroupedNodes = this._ungroupGroupNode(group, transactionToken);
  385. nodesToSelect = nodesToSelect.concat(ungroupedNodes);
  386. }
  387. return nodesToSelect;
  388. };
  389. GroupAction.prototype.getNodeId = function getNodeId(modelId) {
  390. this.layoutController.modelIdToNodeId(modelId);
  391. };
  392. /**
  393. * Add the model operations required to remove the provided node from its group
  394. *
  395. * @param node
  396. * @param isMoveToParent - indicate where we want to move the node to the parent group or outside of the top group
  397. */
  398. GroupAction.prototype._ungroupSingleNode = function _ungroupSingleNode(groupNode, node, isMoveToParent, transactionToken) {
  399. var $nestedGroups = $(node).parents('.pagegroup');
  400. var nestedGroupLength = $nestedGroups.length;
  401. var canvasApi = this.dashboard.getCanvas();
  402. if (nestedGroupLength > 0) {
  403. //todo: question? if groupNode === $nestedGroups[0] ??
  404. var topGroup = isMoveToParent ? groupNode : $nestedGroups[nestedGroupLength - 1];
  405. var angle = Utils.radianToDegree(Utils.getAbsoluteAngleRadian(groupNode, topGroup));
  406. var newStyles = this._calculateNewStyleForEachNode(node, topGroup, angle);
  407. var nodeContentApi = canvasApi.getContent(node._layout.id);
  408. var topGroupApi = canvasApi.getContent(topGroup._layout.id);
  409. canvasApi.moveContent(topGroupApi.getContainer().getId(), [node._layout.id], transactionToken);
  410. var propNameList = Object.keys(newStyles);
  411. propNameList.forEach(function (propName) {
  412. nodeContentApi.setPropertyValue(propName, newStyles[propName], transactionToken);
  413. });
  414. }
  415. };
  416. /**
  417. * Remove the provided @nodes from @groupNode
  418. * @param nodes
  419. * @param groupNode
  420. *
  421. * @returns nodesToSelect
  422. */
  423. GroupAction.prototype._ungroupMultipleNodes = function _ungroupMultipleNodes(nodes, groupNode, transactionToken) {
  424. //these nodes share the same parent: groupNode
  425. var nodesToSelect = [];
  426. //step 1: move all nodes to top Level group.
  427. for (var i = 0; i < nodes.length; i++) {
  428. var node = nodes[i];
  429. this._ungroupSingleNode(groupNode, node, false, transactionToken);
  430. nodesToSelect.push(node);
  431. }
  432. //step2: checkout each node's
  433. var canvasApi = this.dashboard.getCanvas();
  434. var groupContentApi = canvasApi.getContent(groupNode._layout.id);
  435. var groupChildren = groupContentApi.findContent();
  436. if (groupChildren.length == 0) {
  437. //we should remove the group;
  438. canvasApi.removeContent(groupNode._layout.id, transactionToken);
  439. } else if (groupChildren.length == 1) {
  440. //we should remove the left one out to its parent, and then remove the group
  441. var nodeIdToBeRemoved = groupChildren[0].getId();
  442. var nodeToBeRemoved = $(groupNode).find('#' + nodeIdToBeRemoved)[0];
  443. this._ungroupSingleNode(groupNode, nodeToBeRemoved, true, transactionToken);
  444. canvasApi.removeContent(groupNode._layout.id, transactionToken);
  445. nodesToSelect.push(nodeToBeRemoved);
  446. } else {
  447. nodesToSelect.push(groupNode);
  448. }
  449. return nodesToSelect;
  450. };
  451. /**
  452. * Ungroup a list of nodes inside groups
  453. * @param nodes
  454. * @returns {Array}
  455. */
  456. GroupAction.prototype.ungroupIndividualNodes = function ungroupIndividualNodes(nodes, transactionToken) {
  457. var nodesToSelect = [];
  458. // group nodes based on the group they belong to so that we can remove them from each group
  459. var groupMap = {};
  460. for (var i = 0; i < nodes.length; i++) {
  461. var node = nodes[i];
  462. var groupId = node.parentNode._layout.id;
  463. if (!groupMap[groupId]) {
  464. groupMap[groupId] = [];
  465. }
  466. groupMap[groupId].push(node);
  467. }
  468. // For every affected group, we remove the specified nodes. We scan the groups from the most inner one to the most outer one.
  469. var groups = this._getAffectedGroups(nodes);
  470. var nodesToBeRemoved = void 0,
  471. ungroupedNodes = void 0;
  472. for (var _i2 = 0; _i2 < groups.length; _i2++) {
  473. var group = groups[_i2];
  474. nodesToBeRemoved = groupMap[group.id];
  475. ungroupedNodes = this._ungroupMultipleNodes(nodesToBeRemoved ? nodesToBeRemoved : [], group, transactionToken);
  476. nodesToSelect = nodesToSelect.concat(ungroupedNodes);
  477. }
  478. // Recalculate the group box dimension to reflect new changes
  479. for (var _i3 = 0; _i3 < groups.length; _i3++) {
  480. if (groups[_i3]._layout) {
  481. this.recalculateGroupRectForLayout(groups[_i3]._layout, transactionToken.transactionId);
  482. }
  483. }
  484. return nodesToSelect;
  485. };
  486. /**
  487. * Calculates the group dimensions based on the nodes being grouped
  488. * @param nBox
  489. * @param nodes
  490. */
  491. GroupAction.prototype.calculateGroupRect = function calculateGroupRect(nodes) {
  492. var rect = {};
  493. var topLeft = Utils.getMinMaxTopLeft(nodes, function (n) {
  494. var $n = $(n);
  495. var position = $n.position();
  496. position.top += $(n).parent().scrollTop();
  497. position.left += $(n).parent().scrollLeft();
  498. var rect = n.getBoundingClientRect();
  499. position.height = rect.height;
  500. position.width = rect.width;
  501. return position;
  502. });
  503. rect.width = topLeft.maxLeft - topLeft.minLeft;
  504. rect.height = topLeft.maxTop - topLeft.minTop;
  505. rect.top = Math.round(topLeft.minTop);
  506. rect.left = Math.round(topLeft.minLeft);
  507. return rect;
  508. };
  509. /**
  510. * Called when one of the children's dimension changes. This method resizes the group rectangle.
  511. * @param payload
  512. */
  513. GroupAction.prototype.recalculateGroupRect = function recalculateGroupRect(payload) {
  514. var undoRedoTransactionId = payload.undoRedoTransactionId;
  515. var groups = this._getAffectedGroups(payload.currentSelection);
  516. for (var i = 0, iLen = groups.length; i < iLen; i++) {
  517. this.recalculateGroupRectForLayout(groups[i]._layout, undoRedoTransactionId);
  518. }
  519. };
  520. /**
  521. * Called when items are deleted.
  522. * @param payload
  523. */
  524. GroupAction.prototype.onBeforeDelete = function onBeforeDelete(payload) {
  525. var _this3 = this;
  526. var idList = payload.currentSelection;
  527. var nodes = this.getNodes(idList);
  528. var nodesToUngroup = [];
  529. var ungroupedNodes = [];
  530. for (var i = 0; i < nodes.length; i++) {
  531. if (nodes[i]._layout.parentLayout.model.type === 'group' || nodes[i]._layout.model.type === 'group') {
  532. nodesToUngroup.push(nodes[i]);
  533. } else {
  534. ungroupedNodes.push(nodes[i]);
  535. }
  536. }
  537. nodesToUngroup.forEach(function (node) {
  538. ungroupedNodes.push.apply(ungroupedNodes, _this3.ungroupContent([node.id], null, payload.undoRedoTransactionId));
  539. });
  540. return ungroupedNodes;
  541. };
  542. /**
  543. * Get a sorted list from child to parent of all the groups surrounding the given nodes
  544. *
  545. * @param nodes
  546. * @returns {Array}
  547. */
  548. GroupAction.prototype._getAffectedGroups = function _getAffectedGroups(nodes) {
  549. var aParentArrays = [];
  550. var i, iLen;
  551. for (i = 0, iLen = nodes.length; i < iLen; i++) {
  552. aParentArrays.push($(nodes[i]).parents('.pagegroup'));
  553. }
  554. var aParents = [],
  555. index = 0;
  556. var hasMore;
  557. do {
  558. hasMore = this._affectedGroupSortStep(aParentArrays, index, aParents);
  559. index++;
  560. } while (hasMore);
  561. return aParents;
  562. };
  563. /**
  564. * Sorting function used by the _getAffectedGroups method.
  565. *
  566. * @param aParentArrays
  567. * @param index
  568. * @param aParents
  569. * @returns {Boolean}
  570. */
  571. GroupAction.prototype._affectedGroupSortStep = function _affectedGroupSortStep(aParentArrays, index, aParents) {
  572. var hasMore = false;
  573. var i, iLen, a, g, j;
  574. for (i = 0, iLen = aParentArrays.length; i < iLen; i++) {
  575. a = aParentArrays[i];
  576. if (index < a.length) {
  577. hasMore = true;
  578. g = a[index];
  579. j = aParents.indexOf(g);
  580. if (j !== -1) {
  581. aParents.splice(j, 1);
  582. }
  583. aParents.push(g);
  584. }
  585. }
  586. return hasMore;
  587. };
  588. /**
  589. * Called when one of the children's dimension changes.
  590. * This method resizes the group rectangle and re-positions the children of the group
  591. * @param payload
  592. */
  593. GroupAction.prototype.recalculateGroupRectForLayout = function recalculateGroupRectForLayout(layout, undoRedoTransactionId) {
  594. var transactionApi = this.dashboard.getFeature('Transaction');
  595. var transactionToken = transactionApi.startTransactionById(undoRedoTransactionId);
  596. var nBox = layout.domNode;
  597. var nodes = utils.getContentWithoutDecoration(nBox);
  598. var topLeft = utils.getMinMaxTopLeft(nodes, function (n) {
  599. var rect = utils.position(n);
  600. var $n = $(n);
  601. rect.height = $n.outerHeight();
  602. rect.width = $n.outerWidth();
  603. // convert percent style to pixel.
  604. $n.height(rect.height);
  605. $n.width(rect.width);
  606. $n.css('left', rect.left);
  607. $n.css('top', rect.top);
  608. return rect;
  609. });
  610. // Get a reference point to restore to position if it shifts due to rotation
  611. var ref = utils.getCenter(nodes[0]);
  612. var rotatedRef1 = utils.getRotatedCoordinates(nBox.parentNode, ref.left, ref.top);
  613. var groupWidth = topLeft.maxLeft - topLeft.minLeft + 'px';
  614. nBox.style.width = groupWidth;
  615. var groupHeight = topLeft.maxTop - topLeft.minTop + 'px';
  616. nBox.style.height = groupHeight;
  617. var pos = utils.position(nBox);
  618. nBox.style.top = pos.top + topLeft.minTop + 'px';
  619. nBox.style.left = pos.left + topLeft.minLeft + 'px';
  620. // Update the size/postion of the group content
  621. var canvasApi = this.dashboard.getCanvas();
  622. var groupRect = {
  623. height: nBox.clientHeight,
  624. width: nBox.clientWidth
  625. };
  626. var i, iLen, n;
  627. for (i = 0, iLen = nodes.length; i < iLen; i++) {
  628. n = nodes[i];
  629. pos = utils.position(n);
  630. n.style.top = pos.top - topLeft.minTop + 'px';
  631. n.style.left = pos.left - topLeft.minLeft + 'px';
  632. if (n._layout) {
  633. var style = {
  634. top: n.style.top,
  635. left: n.style.left,
  636. height: $(n).height() + 'px',
  637. width: $(n).width() + 'px'
  638. };
  639. var content = canvasApi.getContent(n._layout.model.id);
  640. if (content) {
  641. PxPercentUtil.changePixelPropertiesToPercent(style, groupRect);
  642. for (var name in style) {
  643. content.setPropertyValue(name, style[name], transactionToken);
  644. }
  645. }
  646. }
  647. }
  648. ref = utils.getCenter(nodes[0]);
  649. var rotatedRef2 = utils.getRotatedCoordinates(nBox.parentNode, ref.left, ref.top);
  650. pos = utils.position(nBox);
  651. var groupTopValue = pos.top - rotatedRef2.y + rotatedRef1.y + 'px';
  652. nBox.style.top = groupTopValue;
  653. var groupLeftValue = pos.left - rotatedRef2.x + rotatedRef1.x + 'px';
  654. nBox.style.left = groupLeftValue;
  655. // Update the size/position of the group
  656. var group = canvasApi.getContent(layout.model.id);
  657. group.setPropertyValue('top', groupTopValue, transactionToken);
  658. group.setPropertyValue('left', groupLeftValue, transactionToken);
  659. group.setPropertyValue('height', groupHeight, transactionToken);
  660. group.setPropertyValue('width', groupWidth, transactionToken);
  661. transactionApi.endTransaction(transactionToken);
  662. };
  663. return GroupAction;
  664. }();
  665. return GroupAction;
  666. });
  667. //# sourceMappingURL=GroupAction.js.map