'use strict'; function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } /** * Licensed Materials - Property of IBM * IBM Cognos Products: Dashboard (C) Copyright IBM Corp. 2015, 2020 * US Government Users Restricted Rights - Use, duplication or disclosure restricted by GSA ADP Schedule Contract with IBM Corp. */ define(['jquery', 'underscore', '../../app/util/DeepClone'], function ($, _) { var LayoutHelper = function () { function LayoutHelper() { _classCallCheck(this, LayoutHelper); } LayoutHelper.setPreferredAddOptions = function setPreferredAddOptions(addOptions, topLayoutModel, dashboardApi) { // if we have no parent Id, we will add to the last visible page if (!addOptions.parentId) { var lastPage = LayoutHelper.getLastVisiblePage(topLayoutModel.id, null, dashboardApi); if (lastPage) { addOptions.parentId = LayoutHelper.nodeIdToModelId(lastPage.id); } } var sourceModel = addOptions.model; LayoutHelper.setPreferredLocation(addOptions); if (addOptions.copyPaste) { LayoutHelper._setCopyPasteLayout(addOptions, sourceModel, topLayoutModel, dashboardApi); } }; LayoutHelper.setPreferredLocation = function setPreferredLocation(addOptions) { // Ask the layout to update the layout properties with preferred options (e.g. position) if (addOptions.parentId) { // find the layout object var nLayout = $('#' + LayoutHelper.modelIdToNodeId(addOptions.parentId), this.$el)[0]; if (nLayout && nLayout._layout && nLayout._layout.setPreferredLocation) { if (!addOptions.layoutProperties) { addOptions.layoutProperties = {}; } nLayout._layout.setPreferredLocation(addOptions); } } }; /** * Paste a widget to current page via using the copy-paste functionality * the pasted widget into the current page will be the same size as the source widget * Its position will be slightly offset from the source widget * the insertBefore id is null (because we want it pasted in front of the source widget). * @addOptions * @sourceModel the copied model */ LayoutHelper._setCopyPasteLayout = function _setCopyPasteLayout(addOptions, sourceModel, topLayoutModel, dashboardApi) { var currentPage = LayoutHelper.getLayoutContentContainer(topLayoutModel.id, null, dashboardApi); var currentPageId = LayoutHelper.nodeIdToModelId(currentPage.id); var topItem = topLayoutModel.findModel(currentPageId); if (!currentPage || !topItem || !(sourceModel.layout && sourceModel.layout.style || sourceModel.style)) { return; } addOptions.insertBefore = null; addOptions.parentId = topItem.id; var properties = addOptions.layoutProperties; if (!properties.style) { properties.style = {}; } // Convert top, left, width and height to pixel values so that they can be used in the out of bounds calculations var sourceStyle = sourceModel.layout && sourceModel.layout.style ? sourceModel.layout.style : sourceModel.style; var startingTop = sourceStyle.top; var startingLeft = sourceStyle.left; if (LayoutHelper._isOffsetAdjustmentRequired(currentPageId, topLayoutModel, dashboardApi)) { // Start from the source widget location, increment top and left offset in a loop until you find a location // with no widget. Then place that widget there. Set to a maximum of 30 so there is no possiblility of an inifnite lo var styles = topItem.items.map(function (x) { return x.style; }); var counter = 0, newStartingTop = 0, newStartingLeft = 0; while (counter < 30) { var array = styles.filter(function (style) { return style.top === startingTop && style.left === startingLeft; }); if (array && array.length === 0) { break; } newStartingTop = LayoutHelper._incrementStyleValue(startingTop); newStartingLeft = LayoutHelper._incrementStyleValue(startingLeft); if (LayoutHelper._isOutOfBounds(newStartingTop, sourceStyle.height, currentPage.clientHeight) === true || LayoutHelper._isOutOfBounds(newStartingLeft, sourceStyle.width, currentPage.clientWidth) === true) { break; } startingTop = newStartingTop; startingLeft = newStartingLeft; counter++; } } properties.style.top = startingTop; properties.style.left = startingLeft; properties.style.width = sourceStyle.width; properties.style.height = sourceStyle.height; }; // Check if the current page has any existed widgets or registered content types // @currentPageId current page Id // @return true if currentPage has any widget or registered content types // false otherwise LayoutHelper._isOffsetAdjustmentRequired = function _isOffsetAdjustmentRequired(currentPageId, topLayoutModel, dashboardApi) { var currentPage = topLayoutModel.findModel(currentPageId); var types = ['widget']; var contentTypeRegistry = dashboardApi.getFeature('ContentTypeRegistry'); types = types.concat(contentTypeRegistry.getRegisteredTypes()); var layouts = currentPage.findDescendantsWithType(types); if (layouts.length > 0) { return true; } else { return false; } }; LayoutHelper._incrementStyleValue = function _incrementStyleValue(value) { var val = value.match(/[0-9\\.]+/g), unit = value.match(/[^0-9\\.]+/g); if (val == null || unit == null || val.isEmpty || unit.isEmpty) { return value; } var newValue = parseFloat(val[0]); if (unit[0] === '%') { newValue = newValue + 5; } else { newValue = newValue + 25; } return newValue + unit[0]; }; /** * Given an origin and a dimension (height or width), calculates if the origin+dimension is out of bounds (above max) */ LayoutHelper._isOutOfBounds = function _isOutOfBounds(origin, dimension, max) { var newOrigin = LayoutHelper._getPixelValue(origin, max); var newDimension = LayoutHelper._getPixelValue(dimension, max); return newOrigin > max - newDimension; }; LayoutHelper._getPixelValue = function _getPixelValue(value, maxValue) { if (value[value.length - 1] === '%') { return parseFloat(value) / 100 * parseFloat(maxValue); } else { return parseFloat(value); } }; /** * Return the model id for a given node id * @param nodeId * @returns */ LayoutHelper.nodeIdToModelId = function nodeIdToModelId(nodeId) { return nodeId; }; /** * Return the node id for a given model id * @param modelId * @returns */ LayoutHelper.modelIdToNodeId = function modelIdToNodeId(modelId) { return modelId; }; /** * Find the last selected page to insert. If not found, find the last visible page. For example, if you have this hierarchy of pages: Block/Tab/Absolute. * It will return the Absolute of the selected tab. * @param id of topLayoutModel */ LayoutHelper.getLayoutContentContainer = function getLayoutContentContainer(topLayoutModelId, id, dashboardApi) { var findDropPage = function findDropPage(nTopPage) { var isMultiTab = false; var nSelectedPage = $('.pageTabContent .page:not(.pagegroup):not(.pagetemplateDropZone):not(.pagetemplateIndicator)', $(nTopPage)).filter(function (i, page) { isMultiTab = true; if (id) { return $('#' + id, page).length > 0; } // filter out the pages whose parent container is selected return $(this).closest('.pageTabContent').hasClass('selected'); }).last(); if (!isMultiTab) { var nLastPage = $('.page:not(.pagegroup):not(.pagetemplateDropZone):not(.pagetemplateIndicator)', $(nTopPage)).filter(function (i, page) { isMultiTab = true; if (id) { return $('#' + id, page).length > 0; } // filter out the hidden pages return $(this).is(':visible'); }).last(); return nLastPage && nLastPage[0] || nTopPage; } else { if (nSelectedPage && nSelectedPage[0]) { return nSelectedPage[0]; } else if (dashboardApi) { var stringResources = dashboardApi.getDashboardCoreSvc('.StringResources'); var translation = stringResources.get('pageSelectWarning'); dashboardApi.showToast(translation, { type: 'info' }); } } }; var nPage = findDropPage(document.getElementById(topLayoutModelId)); return nPage; }; /** * Find the genericPage for the dropTarget, and if found, return the dropTargetNode as the content container node. * If not found, find the last visible page. It will return the Absolute of this page as the content container node. * @param {node} dropTargetNode * @param {modelObject} topLayoutModel * @param {apiInstance} dashboardApi */ LayoutHelper.getLayoutContentContainerForDropTarget = function getLayoutContentContainerForDropTarget(dropTargetNode, topLayoutModel, dashboardApi) { if (dropTargetNode) { var layoutModelId = $(dropTargetNode).attr('id'); var layoutModel = topLayoutModel && topLayoutModel.findModel(layoutModelId); if (layoutModel && layoutModel.type === 'genericPage') { return dropTargetNode; } } return LayoutHelper.getLayoutContentContainer(topLayoutModel.id, null, dashboardApi); }; /** * Find the last selected page to insert. If not found, find the last visible dropzone. For example, if you have this hierarchy of pages: Block/Tab/Relative/templateDropZone. * It will return the the templateDropZone of the selected tab that the widget is apart of. * @param id of topLayoutModel */ LayoutHelper.getLastVisiblePage = function getLastVisiblePage(topLayoutModelId, id, dashboardApi) { var nPage = LayoutHelper.getLayoutContentContainer(topLayoutModelId, id, dashboardApi); // Now check if we have an empty drop zone. If yes, we will use it var emptyDropZones = $(nPage).find('.pagetemplateDropZone.empty'); return emptyDropZones.length > 0 ? emptyDropZones[0] : nPage; }; /** * @returns container Page id containing the layout id * @param topLayoutModel */ LayoutHelper.getContainerPageId = function getContainerPageId(topLayoutModel, id) { var topParent = topLayoutModel.findTopLevelParentItem(id); if (topParent) { return topParent.id; } return undefined; }; /** * Calculates the minimum bounding page size to contain its widgets on absolute layout pages * @param layoutModel The layout model to start calculating the minimum bounding page size * @param minimumPageSize The minimum page size (optional) * @returns (object { width: xxx, height: xxx }) The smallest page size (pixels) that can contain all widgets in the given model */ LayoutHelper.getBoundingPageSize = function getBoundingPageSize(layoutModel, minimumPageSize) { if (!layoutModel) { return undefined; } var boundingPageSize = { width: 0, height: 0 }; var pageModels = void 0; // Get the starting point pages if (layoutModel.type === 'genericPage') { pageModels = [layoutModel]; } else { pageModels = layoutModel.findDescendantsWithType('genericPage'); } if (minimumPageSize) { boundingPageSize = _.deepClone(minimumPageSize); } for (var _iterator = pageModels, _isArray = Array.isArray(_iterator), _i = 0, _iterator = _isArray ? _iterator : _iterator[Symbol.iterator]();;) { var _ref; if (_isArray) { if (_i >= _iterator.length) break; _ref = _iterator[_i++]; } else { _i = _iterator.next(); if (_i.done) break; _ref = _i.value; } var pageModel = _ref; if (pageModel.layoutPositioning === 'absolute') { var itemLayoutModels = pageModel.findDescendantsWithType(['widget', 'group']); for (var _iterator2 = itemLayoutModels, _isArray2 = Array.isArray(_iterator2), _i2 = 0, _iterator2 = _isArray2 ? _iterator2 : _iterator2[Symbol.iterator]();;) { var _ref2; if (_isArray2) { if (_i2 >= _iterator2.length) break; _ref2 = _iterator2[_i2++]; } else { _i2 = _iterator2.next(); if (_i2.done) break; _ref2 = _i2.value; } var itemLayoutModel = _ref2; var rawStyle = LayoutHelper._getRawStyle(itemLayoutModel.style, 'px'); if (rawStyle) { var right = rawStyle.left + rawStyle.width; if (right > boundingPageSize.width) { boundingPageSize.width = right; } var bottom = rawStyle.top + rawStyle.height; if (bottom > boundingPageSize.height) { boundingPageSize.height = bottom; } } } } } return boundingPageSize && boundingPageSize.width ? boundingPageSize : undefined; }; LayoutHelper._getRawStyle = function _getRawStyle(style, units) { // Validate style units if (style.left.indexOf(units) !== -1 && style.top.indexOf(units) !== -1 && style.width.indexOf(units) !== -1 && style.height.indexOf(units) !== -1) { return { left: parseInt(style.left, 10), top: parseInt(style.top, 10), width: parseInt(style.width, 10), height: parseInt(style.height, 10) }; } return undefined; }; LayoutHelper.styleIntToPx = function styleIntToPx(style) { style.top = style.top + 'px'; style.left = style.left + 'px'; style.width = style.width + 'px'; style.height = style.height + 'px'; }; return LayoutHelper; }(); return LayoutHelper; }); //# sourceMappingURL=LayoutHelper.js.map