'use strict'; /** * Licensed Materials - Property of IBM * IBM Cognos Products: BI Cloud (C) Copyright IBM Corp. 2013, 2020 * US Government Users Restricted Rights - Use, duplication or disclosure restricted by GSA ADP Schedule Contract with IBM Corp. */ define(['../../../lib/@waca/core-client/js/core-client/ui/core/Class', '../../../view/features/content/contentViewDom/api/impl/ContentViewDomImpl', '../../../lib/@waca/baglass/js/baglass/utils/Utils', 'jquery', 'underscore', '../../../lib/@waca/core-client/js/core-client/utils/ClassFactory', '../../../app/nls/StringResources', '../../widgets/PropertiesUtil', 'react-dom', '../../../lib/@waca/dashboard-common/dist/ui/CenterLoadingSpinner', '../../../api/impl/Layout', '../LayoutHelper', '../../../lib/@waca/core-client/js/core-client/ui/dialogs/ConfirmationDialog', '../../widgets/PropertyListUtil', '../../util/ContentRegistryUtil', '../../../app/util/UnderscoreExt'], function (Class, ContentViewDomImpl, Utils, $, _, ClassFactory, StringResources, PropertiesUtil, ReactDOM, CenterLoadingSpinner, LayoutApi, LayoutHelper, ConfirmationDialog, PropertiesListUtil, ContentRegistryUtil) { var MINIMUM_PAGESIZE = { width: 320, height: 200 }; var PAGESIZES = { '16:9': { 'width': 1280, 'height': 720 }, '4:3': { 'width': 1280, 'height': 960 }, 'letter': { 'width': 612, 'height': 792 }, 'legal': { 'width': 612, 'height': 1008 }, 'a4': { 'width': 595, 'height': 842 }, 'tabloid': { 'width': 792, 'height': 1224 }, 'infographic': { 'width': 512, 'height': 1024 } }; var BaseLayout = Class.extend({ init: function init(options) { BaseLayout.inherited('init', this, arguments); this.layoutController = options.layoutController; this.isAuthoringMode = this.layoutController.isAuthoring; this.model = options.model; this.id = this.model.id; this.parentLayout = options.parentLayout; this.glassContext = options.services.biGlass.glassContext; this.appSettings = options.appSettings; this.dashboardApi = options.dashboardApi; this.additionalWidgetData = options.additionalWidgetData; this.contentFeatureLoader = options.contentFeatureLoader; this.content = options.content; if (this.dashboardApi) { this.colorsService = this.dashboardApi.getFeature('Colors'); this.translationService = this.dashboardApi.getDashboardCoreSvc('TranslationService'); this.contentTypeRegistry = this.dashboardApi.getFeature('ContentTypeRegistry'); } var nodeId = this.layoutController.modelIdToNodeId(this.id); var widgetExistsInDOM = false; if (this.parentLayout) { this.domNode = $(this.parentLayout.domNode).find('#' + nodeId)[0]; // If a widget has already been loaded into the DOM (outside canvas) // bring it in the canvas if (!this.domNode) { this.domNode = document.getElementById(nodeId); if (this.domNode) { widgetExistsInDOM = true; } } } else { this.domNode = document.getElementById(nodeId); } this._attachedHandles = []; if (this._isTopLevelPage()) { this._attachedHandles.push(this.model.on('change:pageSize', this.onPageSizeChange.bind(this), this)); } // If the node does not exist, then we create one var htmlTemplate = options.services.getSvcSync('htmlTemplates'); if (!this.domNode) { this.domNode = $(htmlTemplate.getHtml(this.model, this.model.boardModel.widgetInstances))[0]; } else if (widgetExistsInDOM) { //bring existing widget into a layout view var n = $(this.domNode); var children = n.children(); var prev = n; this.domNode = $(htmlTemplate.getHtml(this.model, this.model.boardModel.widgetInstances))[0]; var n$ = $(this.domNode); children.appendTo(n$); prev.remove(); // resize the node since it previously existed outside of the canvas. this.layoutController.layoutReady(this.model.id).then(this.onResize.bind(this)); } this.domNode._layout = this; this.authorHelper = options.authorHelper; //author mode addon //static widgets end up using a LayoutBaseView (not a 'Widget') when moving to author mode //need to save onto the widget chrome event router this.widgetChromeEventRouter = options.widgetChromeEventRouter; this.widgetChromeEventRouter_disposeEvents = []; this.$el = $(this.domNode); this.logger = this.glassContext.getCoreSvc('.Logger'); this.services = options.services; this.api = new LayoutApi(this); if (!this.eventRouter) { this.eventRouter = this.layoutController.eventRouter; if (this.eventRouter) { this.eventRouter.on('properties:hasRendered', this.setInitialEnabledState, this); this._enableToggleEvents(); // While it would be better to listen to change:layoutPositioning and change:pageSize events, // those are listened to by the child layout classes and need to be processed before we resize // the layout. Listen to this event which will be fired after the properties are modified. this.eventRouter.on('layout:resize', this._onResize, this); } } this._debouncedOnResize = _.doubleDebounce(this._onResize.bind(this), 200); this._subLayoutViews = []; }, setInitialEnabledState: function setInitialEnabledState() { // TODO : using events to achieve this seems wrong and hacky. We must find a better way to achieve this if (this.model.getValueFromSelfOrParent('layoutPositioning') !== 'relative') { this.eventRouter.trigger('properties:updateEnabled', { propertyName: 'fitPage', enabled: false }); } }, _enableToggleEvents: function _enableToggleEvents() { this.model.on('change:fitPage', this._updatePropertiesPaneToggle, this); this.model.on('change:showGrid', this._updatePropertiesPaneToggle, this); this.model.on('change:snapGrid', this._updatePropertiesPaneToggle, this); this.model.on('change:snapObjects', this._updatePropertiesPaneToggle, this); }, _disableToggleEvents: function _disableToggleEvents() { this.model.off('change:fitPage', this._updatePropertiesPaneToggle.bind(this), this); this.model.off('change:showGrid', this._updatePropertiesPaneToggle.bind(this), this); this.model.off('change:snapGrid', this._updatePropertiesPaneToggle.bind(this), this); this.model.off('change:snapObjects', this._updatePropertiesPaneToggle.bind(this), this); }, /** * Updates the state of a properties pane toggle after an undo/redo */ _updatePropertiesPaneToggle: function _updatePropertiesPaneToggle(event) { var checked = event.value || false; this.eventRouter.trigger('properties:setValue', { propertyName: event.name, value: checked }); }, /** * Return the id of the node associated with the model id. If we start prefixing the node, this method can calculate the proper node id. */ getNodeId: function getNodeId(modelId) { return this.layoutController.modelIdToNodeId(modelId); }, _destroyAuthoringHelper: function _destroyAuthoringHelper() { if (this.authorHelper) { this.authorHelper.destroy(); this.authorHelper = null; } }, destroy: function destroy(event) { this._subLayoutViews = null; this.widgetChromeEventRouter_disposeEvents.forEach(function (dispose) { return dispose.remove(); }); this.widgetChromeEventRouter_disposeEvents = []; if (this._debouncedOnResize) { this._debouncedOnResize.cancel(); } this._destroyAuthoringHelper(); if (this.eventRouter) { this.eventRouter.off('properties:hasRendered', this.setInitialEnabledState, this); this._disableToggleEvents(); this.eventRouter.off('layout:resize', this._onResize, this); } this._attachedHandles.forEach(function (handle) { return handle.remove(); }); this._attachedHandles = []; if (this.domNode) { var $domNode = $(this.domNode); $domNode.off('contextmenu.baselayout'); $('.page,.widget', $domNode).each(function () { if (this._layout && this._layout.destroy) { this._layout.destroy(event); } }); $domNode.remove(); this.domNode._layout = null; } if (this._contentTitle) { this._contentTitle = null; } if (this.layoutController) { this.layoutController.removeView(this); } if (this.api) { this.api.destroy(); this.api = null; } this.layoutController = null; this.model = null; this.id = null; this.parentLayout = null; this.glassContext = null; this.appSettings = null; this.dashboardApi = null; this.colorsService = null; this.translationService = null; this.additionalWidgetData = null; this.contentFeatureLoader = null; this.content = null; this.widgetChromeEventRouter = null; this.logger = null; this.services = null; this.authorHelper = null; this.domNode = null; this.layoutController = null; this._debouncedOnResize = null; BaseLayout.inherited('destroy', this, arguments); }, _renderContentTitle: function _renderContentTitle() { var capabilitiesFeature = ContentRegistryUtil.getCapabilities(this.content); var supportTitle = capabilitiesFeature ? capabilitiesFeature.getCapabilities()['title'] : false; if (supportTitle) { this._contentTitle = this.content.getFeature('ContentTitle.internal'); this.$el.addClass('titleSupported'); return this._contentTitle.render({ id: this.domNode.id, model: this.model, widgetChromeEventRouter: this.widgetChromeEventRouter, eventRouter: this.eventRouter }); } }, // lifecycle_cleanup -- this should render but we currently have a render that call onShow. // clean this up renderContent: function renderContent() { var _this2 = this; var stateApi = this.content.getFeature('state.internal'); stateApi.setStatus(stateApi.STATUS.RENDERING); if (this._isRendered) { var promises = this._subLayoutViews.map(function (layout) { return layout.renderContent(); }); return Promise.all(promises).then(function () { stateApi.setStatus(stateApi.STATUS.RENDERED); }); } else { var _promises = []; var renderer = this.content.getFeature('renderer'); if (renderer) { var childNode = this.domNode; childNode.className = 'widget content ' + this.content.getType(); var canRotate = this.content.getPropertyValue('canRotate'); if (!canRotate) { childNode.className += ' noRotate'; } var rendererPromise = renderer.render({ parent: childNode }).then(function () { var titleContent = _this2._renderContentTitle(); if (titleContent) { childNode.prepend(titleContent); } _this2.contentFeatureLoader.registerFeature(_this2.content.getId(), 'ContentViewDOM', new ContentViewDomImpl(childNode)); }); _promises.push(rendererPromise); } (this.model.items || []).forEach(function (_ref) { var id = _ref.id; _promises.push(_this2.layoutController.createLayoutModule(id, _this2, _this2.additionalWidgetData).then(function (layout) { if (layout) { _this2._subLayoutViews.push(layout); return layout.renderContent(); } })); }); return Promise.all(_promises).then(function () { return _this2.registerViewFeatures(); }).then(function () { stateApi.setStatus(stateApi.STATUS.RENDERED); _this2.layoutController.markViewAsReady(_this2); _this2._isRendered = true; }); } }, registerViewFeatures: function registerViewFeatures() { var _this3 = this; // this will be called from the render content.. which might be called more than once as we switch between author and consume mode // make sure we only call it once. if (!this._viewFeatureRegistered) { this._viewFeatureRegistered = true; // Add a view dom feature and then register the content-view-features collection return this.contentFeatureLoader.registerFeature(this.id, 'ContentViewDOM', new ContentViewDomImpl(this.domNode)).then(function () { return _this3.contentFeatureLoader.registerFeatureCollection(_this3.id, 'com.ibm.bi.dashboard.content-view-features'); }); } else { return Promise.resolve(); } }, onResize: function onResize(renderOptions) { this._debouncedOnResize(renderOptions); }, _onResize: function _onResize(renderOptions) { var _this4 = this; if (this.model.items) { _.each(this.model.items, function (item) { var view = _this4.layoutController.getLayoutView(item.id); if (view && view.onResize) { // do not try re-sizing hidden elements, as their sizes won't be accurate if (!view.$el.is(':hidden')) { view.onResize(renderOptions); } else { view.resizeOnShow(); } } }); } }, /** * Invoked when Widget chrome is selected, derived classes to override */ onSelect: function onSelect(isGroupSelect) { if (this.contentTypeRegistry.isTypeRegistered(this.content.getType()) && this.widgetChromeEventRouter && this.isAuthoringMode && !isGroupSelect) { this.widgetChromeEventRouter.trigger('title:chromeSelected'); } }, /** * Invoked when Widget chrome is deselected, derived classes to override */ onDeselect: function onDeselect() { if (this.contentTypeRegistry.isTypeRegistered(this.content.getType()) && this.widgetChromeEventRouter && this.isAuthoringMode) { this.widgetChromeEventRouter.trigger('title:chromeDeselected'); } }, /** * Invoked when a hidden layout is shown */ onShow: function onShow() { this._callChildrenFunction('onShow'); if (this.isResizeOnShow) { this.isResizeOnShow = false; this.onResize(); } }, /** * Invoke when a layout is hidden */ onHide: function onHide() { this._callChildrenFunction('onHide'); }, /** * Invoke when a is transitioning from one view to another. */ onTransition: function onTransition(event) { return this._callChildrenFunctionAsync('onTransition', event); }, /** * Invoke when a layout has transitioned. */ afterTransition: function afterTransition(event) { return this._callChildrenFunctionAsync('afterTransition', event); }, /** * Indicates that we need to call resize the next time onShow is called. */ resizeOnShow: function resizeOnShow() { this.isResizeOnShow = true; }, _callChildrenFunctionAsync: function _callChildrenFunctionAsync(name, event) { var _this5 = this; var promises = []; _.each(this.model.items, function (item) { var view = _this5.layoutController.getLayoutView(item.id); if (view && view[name]) { promises.push(view[name](event)); } }); return Promise.all(promises); }, _callChildrenFunction: function _callChildrenFunction(name) { var _this6 = this; _.each(this.model.items, function (item) { var view = _this6.layoutController.getLayoutView(item.id); if (view && view[name]) { view[name](); } }); }, getProperties: function getProperties() { var _this7 = this; var items = []; items.push(this.getBannerProperty()); if (this._isTopLevelPage()) { var _items; // If this is the top level layout, then we add the dashboard properties (_items = items).push.apply(_items, this._getCanvasProperties()); return this._fetchThemes().then(function (themeListing) { items.push({ type: 'DropDown', label: StringResources.get('propTheme'), name: 'theme', id: 'theme', defaultValue: _this7.layoutController.boardModel.get('theme'), options: themeListing, tabName: StringResources.get('tabName_general'), sectionName: StringResources.get('sectionName_colorsAndThemes'), sectionPosition: 2, showLabels: true, onChange: function onChange(propertyName, propertyValue) { _this7.layoutController.boardModel.set({ 'theme': propertyValue }); } }); return Promise.all([_this7._getDashboardColorSetPropertySpec(), _this7._getBackgroundColorPropertySpec()]).then(function (propertySpecs) { items = items.concat(propertySpecs); items.push(_this7._getFredIsRedSpec()); items.push(_this7._getDataCacheSpec()); if (_this7.translationService.getDefaultLanguage() === 'Default') { _this7._addLanguageSpecWhenNoLocaleSet(items); } else { _this7._addMultilingualProperties(items); } return items; }); }).catch(function (error) { _this7.logger.error(error, _this7); throw error; }); } else { // Not a top level layout (e.g. embedded layout that is not a widget) // We only support the background color for now return this._getBackgroundColorPropertySpec().then(function (backgroundSpec) { items.push(backgroundSpec); return items; }); } }, _getDashboardColorSetPropertySpec: function _getDashboardColorSetPropertySpec() { var _this8 = this; var dashboardColorSet = { id: 'dashboardColorSet', name: 'dashboardColorSet', type: 'NewPalette', sectionLabel: StringResources.get('propDashboardColorPalette'), newPaletteLabel: StringResources.get('propNewDashboardPaletteLabel'), linkLabel: StringResources.get('propDashboardChangePaletteLink'), ariaLabel: StringResources.get('propDashboardColorPalette'), tabName: StringResources.get('tabName_general'), sectionName: StringResources.get('sectionName_colorsAndThemes'), paletteType: 'ColorPalette', defaultValue: 'userPaletteColor', includeUserColorPalette: true }; var onChangeCallback = function onChangeCallback(propertyName, paletteItem) { _this8.layoutController.boardModel.get('properties').set({ dashboardColorSet: paletteItem.id }); _this8.eventRouter.trigger('properties:refreshPane', { 'sender': _this8.layoutController.boardModel.get('properties').id }); }; return PropertiesUtil.handleNewPaletteProperty(dashboardColorSet, this.layoutController.boardModel.get('properties'), this.dashboardApi, onChangeCallback); }, _getBackgroundColorPropertySpec: function _getBackgroundColorPropertySpec() { var _this9 = this; var colorPickerSpec = { 'name': 'fillColor', 'id': 'fillColor', 'type': 'ColorPicker', 'label': StringResources.get('propDashboardBackgroundColor'), 'open': false, 'ariaLabel': StringResources.get('propDashboardBackgroundColor'), 'paletteType': 'DashboardColorSet', 'showHexValue': true, 'addButton': true, 'tabName': StringResources.get('tabName_general'), 'sectionName': StringResources.get('sectionName_colorsAndThemes'), 'defaultValue': 'transparent' }; return PropertiesUtil.handleColorPickerProperty(colorPickerSpec, this, this.dashboardApi).then(function (colorPickerSpec) { colorPickerSpec.onChange = function (propertyName, propertyValueInfo) { // get layout and update CSS var oLayout = _this9.model; var data = { undoRedoTransactionId: _.uniqueId('layout_fillColorChange_') }; var payload = { fillColor: propertyValueInfo.name }; // This step is done because we used to set the "css" model property to have the fill color classname in addition to fillColor property // This is bad because it was overriding the css values that are part of the layout definition // We stopped doing this, but the following code is to handle an older dashboard that has the fill set in the css property (e.g. css = fill-color1) // We either have an upgrade to remove the layout 'css' property that contains the fill color, or we do it here. if (oLayout.get('css') === 'fill-' + oLayout.get('fillColor')) { // If the css was prreviously set , just remove it payload.css = undefined; } _this9.colorsService.prepareForColorModelChange(payload, 'fillColor'); oLayout.set(payload, { sender: _this9.senderId, payloadData: data }); }; return colorPickerSpec; }); }, _getFredIsRedSpec: function _getFredIsRedSpec() { var _this10 = this; return { type: 'ToggleButton', label: StringResources.get('propFredIsRed'), name: 'fredIsRed', id: 'fredIsRed', checked: this.model.boardModel && this.model.boardModel.properties && this.model.boardModel.properties.fredIsRed, 'tabName': StringResources.get('tabName_general'), 'sectionName': StringResources.get('sectionName_advanced'), onChange: function onChange(propertyName, propertyValue) { _this10.layoutController.boardModel.properties.set({ 'fredIsRed': propertyValue }); } }; }, _getDataCacheSpec: function _getDataCacheSpec() { var _this11 = this; return { 'type': 'DropDown', 'label': StringResources.get('dataCache'), 'name': 'dataCache', 'id': 'dataCache', 'defaultValue': this.model.boardModel && this.model.boardModel.properties && this.model.boardModel.properties.localCache || 'automatic', 'options': [{ label: StringResources.get('dataCache_auto'), value: 'automatic' }, { label: StringResources.get('dataCache_on'), value: 'yes' }, { label: StringResources.get('dataCache_off'), value: 'no' }], sectionPosition: 99, 'tabName': StringResources.get('tabName_general'), 'sectionName': StringResources.get('sectionName_advanced'), onChange: function onChange(propertyName, propertyValue) { if (propertyValue === 'automatic') { _this11.model.boardModel.properties.set({ 'localCache': undefined }); } else { _this11.model.boardModel.properties.set({ 'localCache': propertyValue }); } } }; }, _addMultilingualProperties: function _addMultilingualProperties(properties) { var _this12 = this; var defaultLanguage = this.translationService.getDefaultLanguage(); var tabName = StringResources.get('tabName_general'); properties.push({ type: 'SingleLineLinks', tabName: tabName, name: 'Languages', sectionName: StringResources.get('sectionName_advanced'), disabled: this.translationService.translationModeOn, items: [{ align: 'left', items: [{ type: 'text', value: StringResources.get('multilingualDashboardsLabel') }] }] }); // Create the Default language row - it's always first properties.push(this._createLanguageRow(defaultLanguage, tabName, true)); // Rows for all other available languages this.translationService.getSelectedLanguages().sort(function (a, b) { return _this12.translationService.getLanguageName(a) > _this12.translationService.getLanguageName(b) ? 1 : -1; }).forEach(function (language) { if (language !== defaultLanguage && language !== 'Default') { properties.push(_this12._createLanguageRow(language, tabName, false)); } }); properties.push({ type: 'SingleLineLinks', tabName: tabName, name: 'addLanguage', sectionName: StringResources.get('sectionName_advanced'), disabled: this.translationService.translationModeOn, items: [{ align: 'right', items: [{ type: 'text', value: StringResources.get('multilingualDashboardsAddLinkText'), clickCallback: function clickCallback() { _this12.launchLanguageSelectSlideout(null, true); } }] }] }); return properties; }, /** * Language property spec when no language has been selected yet */ _addLanguageSpecWhenNoLocaleSet: function _addLanguageSpecWhenNoLocaleSet(properties) { var _this13 = this; var tabName = StringResources.get('tabName_general'); var defaultLanguage = this.translationService.getDefaultLanguage(); properties.push({ type: 'SingleLineLinks', tabName: tabName, name: 'language_' + defaultLanguage, sectionName: StringResources.get('sectionName_advanced'), items: [{ align: 'left', items: [{ type: 'text', id: 'text' + defaultLanguage, value: StringResources.get('multilingualDashboardsLabel') }] }, { align: 'right', items: [{ type: 'text', value: StringResources.get('multilingualDashboardsSetDefault'), clickCallback: function clickCallback() { _this13.launchLanguageSelectSlideout(defaultLanguage, false); } }] }] }); }, /** * Creats a SingleLineLink property spec that describes a language */ _createLanguageRow: function _createLanguageRow(locale, tabName, isDefaultLocale) { var _this14 = this; var leftItems = []; var rightItems = []; var languageName = this.translationService.getLanguageName(locale); rightItems.push({ type: 'text', id: 'text' + locale, value: isDefaultLocale ? StringResources.get('multilingualDashboardsDefault', { defaultLanguage: languageName }) : languageName }); rightItems.push({ type: 'icon', svgIcon: 'common-menuoverflow', iconTooltip: StringResources.get('multilingualDashboardIconMore', { currentLanguage: languageName }), clickCallback: function clickCallback(evt) { _this14.launchLanguageMenu(evt, locale); } }); return { type: 'SingleLineLinks', tabName: tabName, id: 'language_' + locale, sectionName: StringResources.get('sectionName_advanced'), indent: 2, disabled: this.translationService.translationModeOn, items: [{ align: 'left', items: leftItems }, { align: 'right', items: rightItems }] }; }, /** * When picking a language, we launch an overlay slideout with all the content locales */ launchLanguageSelectSlideout: function launchLanguageSelectSlideout(selectedLocale, addLanguage) { var _this15 = this; var propertiesManager = this.layoutController.canvasController.getExtension('propertiesManager'); propertiesManager.addChild({ overlay: true, label: StringResources.get('multilingualSelectLanguage'), content: { module: 'dashboard-core/js/dashboard/contentpane/PropertyUIControlView', items: [{ value: StringResources.get('multilingualSelectLanguage'), centerLabel: true, type: 'Banner', backButton: true, ariaLabel: StringResources.get('multilingualSelectLanguage') }, { type: 'RadioButtonGroup', name: 'language', separator: true, ariaLabel: StringResources.get('multilingualSelectLanguage'), value: selectedLocale, selectOnNavigation: false, items: this.translationService.getLanguageRadioItems(), onChange: function onChange(name, newLocale) { propertiesManager.closeChild(); if (selectedLocale === 'Default') { _this15.translationService.changeDefaultLanguage(newLocale); } else if (addLanguage) { _this15.translationService.startTranslationMode(newLocale); } else { _this15.translationService.switchLanguage({ from: selectedLocale, to: newLocale }); } } }] } }); }, /** * User clicked on the ..., so show the menu */ launchLanguageMenu: function launchLanguageMenu(evt, locale) { var position = {}; if (evt.pageX === undefined || evt.gesture && (evt.gesture.center === undefined || evt.gesture.center.pageX === undefined)) { position = $(evt.target).offset(); } else { position.left = evt.pageX || evt.gesture.center.pageX; position.top = evt.pageY || evt.gesture.center.pageY; } this.glassContext.appController.showContextMenu({ position: { pageX: position.left, pageY: position.top }, menuId: 'com.ibm.bi.dashboard.languageMenu', activeObject: { selectedLocale: locale, translationService: this.translationService, layoutBaseView: this } }); }, _isTopLevelPage: function _isTopLevelPage() { return this.model && this.layoutController.boardModel && this.model === this.layoutController.boardModel.layout; }, getBannerProperty: function getBannerProperty() { return { 'value': this._isTopLevelPage() ? StringResources.get('dashboardProperties') : StringResources.get('settings'), 'name': 'banner', 'type': 'Banner', 'editable': false }; }, _getCanvasProperties: function _getCanvasProperties() { var items = [this._getLayoutPositioningSpec()]; items = items.concat(this._getPageSizeSpec()); items = items.concat(this._getGridOptionsSpec()); return items; }, _getLayoutPositioningSpec: function _getLayoutPositioningSpec() { var defaultValue = this._getLayoutPositioning(); var layoutPositioningOptions = this._getLayoutPositioningSpecOptions(defaultValue); var _this = this; return { 'type': 'DropDown', 'label': StringResources.get('propLayoutPositioning'), 'name': 'layoutPositioning', 'id': 'layoutPositioning', 'defaultValue': defaultValue, 'options': layoutPositioningOptions, 'tabName': StringResources.get('tabName_general'), 'sectionName': StringResources.get('sectionName_canvas'), sectionPosition: 1, 'coachMark': { render: function render(options) { Utils.addCoachmark({ id: 'com.ibm.bi.dashboard.dashboardCoreProperties.layoutPositioning', title: StringResources.get('propLayoutPositioningCoachmarkTitle'), contents: StringResources.get('propLayoutPositioningCoachmarkContent'), placement: 'bottom', glassContext: options.glassContext, $el: options.$el }); } }, onChange: function onChange(propertyName, propertyValue) { _this._onChangeLayoutPosition(this, propertyValue); } }; }, _getLayoutPositioningSpecOptions: function _getLayoutPositioningSpecOptions(defaultValue) { var layoutPositioningOptions = []; if (!defaultValue) { layoutPositioningOptions.push({ label: '', value: 'undefined' }); } layoutPositioningOptions.push({ label: StringResources.get('propLayoutRelative'), value: 'relative' }, { label: StringResources.get('propLayoutAbsolute'), value: 'absolute' }); return layoutPositioningOptions; }, _getLayoutPositioning: function _getLayoutPositioning() { return this.model.getValueFromSelfOrParent('layoutPositioning'); }, _setLayoutPositioning: function _setLayoutPositioning(value) { var data = { undoRedoTransactionId: _.uniqueId('layout_layoutPositioningChange_') }; this.model.set({ layoutPositioning: value }, { payloadData: data, sender: this.senderId }); this.onResize(); }, _setFitPageLayoutPositioning: function _setFitPageLayoutPositioning(layoutPos, fitPage) { var data = { undoRedoTransactionId: _.uniqueId('layout_layoutPositioningChange_') }; this.model.set({ layoutPositioning: layoutPos, fitPage: fitPage }, { payloadData: data, sender: this.senderId }); this.onResize(); }, _onChangeLayoutPosition: function _onChangeLayoutPosition(property, propertyValue) { var _this16 = this; var oldLayoutPositioning = this._getLayoutPositioning(); if (oldLayoutPositioning) { if (propertyValue === 'absolute') { this.eventRouter.trigger('properties:updateEnabled', { propertyName: 'fitPage', enabled: false }); this.eventRouter.trigger('properties:setValue', { propertyName: 'fitPage', value: false }); this._setFitPageLayoutPositioning(propertyValue, false); } else { this.eventRouter.trigger('properties:updateEnabled', { propertyName: 'fitPage', enabled: true }); var pageSize = this.model.get('pageSize'); var preset = this._getPageSizePreset(pageSize.width, pageSize.height); if (preset === '16:9' || preset === '4:3') { this.eventRouter.trigger('properties:setValue', { propertyName: 'fitPage', value: true }); this._setFitPageLayoutPositioning(propertyValue, true); } else { this.eventRouter.trigger('properties:setValue', { propertyName: 'fitPage', value: false }); this._setFitPageLayoutPositioning(propertyValue, false); } } } else { // Confirm layout upgrade with user var onOK = function onOK() { _this16._setLayoutPositioning(propertyValue); // Update property control - which should remove "undefined" as an option // Ideally we should only be updating the one control of interest. // But, this doesn't currently work right for items in a section block. // let event = { // 'propertySpec': this._getLayoutPositioningSpec(), // 'removeProperty': false // }; // this.eventRouter.trigger('properties:refreshProperty', event); _this16.eventRouter.trigger('properties:refreshPane'); }; var onCancel = function onCancel() { // Reset back to undefined state property._lastChangedValue = 'undefined'; // Bug in glass, setValue doesn't reset _lastChangedValue. property.setValue('undefined'); }; var oConfirmationDialog = new ConfirmationDialog('warning', StringResources.get('confirmLayoutStyleUpgradeTitle'), StringResources.get('confirmLayoutStyleUpgrade')); oConfirmationDialog.confirm(onOK, onCancel); } }, _getGridOptionsSpec: function _getGridOptionsSpec() { var _this17 = this; return [{ type: 'SectionLabel', label: StringResources.get('gridLabel'), id: 'gridLabel', name: 'gridLabel', 'tabName': StringResources.get('tabName_general'), 'sectionName': StringResources.get('sectionName_canvas') }, { type: 'ToggleButton', label: StringResources.get('gridText'), name: 'showGrid', id: 'showGrid', checked: this.model.get('showGrid') === undefined ? false : this.model.get('showGrid'), 'tabName': StringResources.get('tabName_general'), 'sectionName': StringResources.get('sectionName_canvas'), onChange: function onChange(propertyName, propertyValue) { _this17.model.set({ 'showGrid': propertyValue }); } }, { type: 'ToggleButton', label: StringResources.get('snapText'), name: 'snapGrid', id: 'snapGrid', checked: this.model.get('snapGrid') === undefined ? false : this.model.get('snapGrid'), 'tabName': StringResources.get('tabName_general'), 'sectionName': StringResources.get('sectionName_canvas'), onChange: function onChange(propertyName, propertyValue) { _this17.model.set({ 'snapGrid': propertyValue }); } }, { type: 'ToggleButton', label: StringResources.get('snapObjectsText'), name: 'snapObjects', id: 'snapObjects', checked: this.model.get('snapObjects') === undefined ? true : this.model.get('snapObjects'), 'tabName': StringResources.get('tabName_general'), 'sectionName': StringResources.get('sectionName_canvas'), onChange: function onChange(propertyName, propertyValue) { _this17.model.set({ 'snapObjects': propertyValue }); } }]; }, _getPageSizePreset: function _getPageSizePreset(width, height) { var preset = 'custom'; var checkWidth = parseInt(width, 10), checkHeight = parseInt(height, 10); for (var value in PAGESIZES) { if (PAGESIZES.hasOwnProperty(value)) { var currentPreset = PAGESIZES[value]; if (currentPreset.width === checkWidth && currentPreset.height === checkHeight) { preset = value; break; } } } return preset; }, _wrapPageSizeTextProperty: function _wrapPageSizeTextProperty() { var action = this; return function () { var args = [action].concat(Array.prototype.slice.call(arguments)); action._pageSizeTextOnChange.apply(this, args); }; }, _pageSizeTextOnChange: function _pageSizeTextOnChange(action, id, value) { if (this.propertiesManagerChange) { this.onChangeValueHold = value; } else { var numericValue = parseInt(value, 10); var model = action.model; var direction = id === 'pageSizeWidth' ? 'width' : 'height'; var newPageSize = {}; if (model.pageSize) { newPageSize = _.extend(newPageSize, model.pageSize); } if (!isNaN(numericValue) && newPageSize[direction] !== numericValue) { newPageSize[direction] = numericValue; newPageSize = action._setPageSize(newPageSize); numericValue = newPageSize[direction]; } var propertyNameList = ['pageSizeSplit', id]; if (isNaN(numericValue)) { action.eventRouter.trigger('properties:setValue', { propertyName: propertyNameList, value: this.onChangeValueHold }); } else if (value !== this.onChangeValueHold) { this.onChangeValueHold = PropertiesListUtil.getPropertyDisplayString(numericValue, StringResources.get('pixelUnit'), true); action.eventRouter.trigger('properties:setValue', { propertyName: propertyNameList, value: this.onChangeValueHold }); } } }, _setPageSize: function _setPageSize(newSize, undoRedoTransactionId) { var model = this.model; var sizeChanged = false; var minimumSize = LayoutHelper.getBoundingPageSize(model, MINIMUM_PAGESIZE); if (minimumSize) { if (isNaN(newSize.width) || minimumSize.width > newSize.width) { newSize.width = minimumSize.width; sizeChanged = true; } if (isNaN(newSize.height) || minimumSize.height > newSize.height) { newSize.height = minimumSize.height; sizeChanged = true; } if (sizeChanged) { this.layoutController.glassContext.appController.showToast(StringResources.get('propPageSizeAdjustedMessage'), { type: 'info' }); } } model.set({ 'pageSize': newSize }, { payloadData: { undoRedoTransactionId: undoRedoTransactionId } }); this.onResize(); return newSize; }, _getPageSizeDisplayValues: function _getPageSizeDisplayValues() { var model = this.model; var widthValue = model.pageSize && model.pageSize.width ? PropertiesListUtil.getPropertyDisplayString(model.pageSize.width, StringResources.get('pixelUnit'), true) : ''; var heightValue = model.pageSize && model.pageSize.height ? PropertiesListUtil.getPropertyDisplayString(model.pageSize.height, StringResources.get('pixelUnit'), true) : ''; return { width: widthValue, height: heightValue, preset: this._getPageSizePreset(widthValue, heightValue) }; }, onPageSizeChange: function onPageSizeChange() { var currentValues = this._getPageSizeDisplayValues(); this.eventRouter.trigger('properties:setValue', { propertyName: ['pageSizeSplit', 'pageSizeWidth'], value: currentValues.width }); this.eventRouter.trigger('properties:setValue', { propertyName: ['pageSizeSplit', 'pageSizeHeight'], value: currentValues.height }); this.eventRouter.trigger('properties:setValue', { propertyName: 'pageSizePreset', value: currentValues.preset }); }, _getPageSizeSpec: function _getPageSizeSpec() { var _this18 = this; var defaultValues = this._getPageSizeDisplayValues(); var properties = [{ 'type': 'SectionLabel', 'id': 'pageSizeLabel', 'name': 'pageSizeLabel', 'label': StringResources.get('propPageSizeLabel'), 'tabName': StringResources.get('tabName_general'), 'sectionName': StringResources.get('sectionName_canvas') }, { 'type': 'DropDown', 'label': StringResources.get('propPageSizePreset'), 'ariaLabel': StringResources.get('propPageSizePresetAriaLabel'), 'name': 'pageSizePreset', 'id': 'pageSizePreset', 'readOnly': false, 'defaultValue': defaultValues.preset, 'options': [{ label: StringResources.get('propPageSizePresetCustom'), value: 'custom' }, { label: StringResources.get('propPageSizePreset16x9'), value: '16:9' }, { label: StringResources.get('propPageSizePreset4x3'), value: '4:3' }, { label: StringResources.get('letter'), value: 'letter' }, { label: StringResources.get('legal'), value: 'legal' }, { label: StringResources.get('a4'), value: 'a4' }, { label: StringResources.get('tabloid'), value: 'tabloid' }, { label: StringResources.get('propPageSizePresetInfographic'), value: 'infographic' }], 'onChange': this._onChangePageSizePreset.bind(this), 'tabName': StringResources.get('tabName_general'), 'sectionName': StringResources.get('sectionName_canvas'), sectionPosition: 1 }, { type: 'ToggleButton', label: StringResources.get('fitPageText'), name: 'fitPage', id: 'fitPage', checked: this.getFitPage(), 'tabName': StringResources.get('tabName_general'), 'sectionName': StringResources.get('sectionName_canvas'), onChange: function onChange(propertyName, propertyValue) { _this18.model.set({ 'fitPage': propertyValue }); } }, { 'type': 'Split', 'name': 'pageSizeSplit', 'id': 'pageSizeSplit', 'tabName': StringResources.get('tabName_general'), 'sectionName': StringResources.get('sectionName_canvas'), 'items': [{ align: 'left', items: [{ 'type': 'InputLabel', 'label': StringResources.get('propPageSizeWidth'), 'ariaLabel': StringResources.get('propPageSizeWidthAriaLabel'), 'name': 'pageSizeWidth', 'id': 'pageSizeWidth', 'readOnly': false, 'value': defaultValues.width, 'onChangeValueHold': defaultValues.width, 'multiline': true, 'handleReturnKey': true, 'onChange': this._wrapPageSizeTextProperty() }] }, { align: 'right', items: [{ 'type': 'InputLabel', 'label': StringResources.get('propPageSizeHeight'), 'ariaLabel': StringResources.get('propPageSizeHeightAriaLabel'), 'name': 'pageSizeHeight', 'id': 'pageSizeHeight', 'readOnly': false, 'value': defaultValues.height, 'onChangeValueHold': defaultValues.height, 'multiline': true, 'handleReturnKey': true, 'onChange': this._wrapPageSizeTextProperty() }] }] }]; return properties; }, _onChangePageSizePreset: function _onChangePageSizePreset(id, value) { var presetValues = PAGESIZES[value]; if (presetValues) { var payloadData = { undoRedoTransactionId: _.uniqueId('layout_layoutPageSizeChange_') }; var model = this.model; if (model.get('layoutPositioning') !== 'absolute') { var fitPageValue = value === '16:9' || value === '4:3'; model.set({ 'fitPage': fitPageValue }, { payloadData: payloadData }); this.eventRouter.trigger('properties:setValue', { propertyName: 'fitPage', value: fitPageValue }); } if (!model.pageSize || model.pageSize.width !== presetValues.width || model.pageSize.height !== presetValues.height) { this._setPageSize({ width: presetValues.width, height: presetValues.height }, payloadData.undoRedoTransactionId); } } }, getFitPage: function getFitPage() { var fitPage = false; if (this.model.getValueFromSelfOrParent('layoutPositioning') !== 'absolute') { fitPage = this.model.getValueFromSelfOrParent('fitPage') || false; } return fitPage; }, _fetchThemes: function _fetchThemes() { var _this19 = this; if (!this._themeListing) { this._themeListing = []; return ClassFactory.loadModule('text!dashboard-core/js/lib/@waca/dashboard-common/dist/themes/themeListing.json').then(function (themeListing) { var themesInfo = JSON.parse(themeListing); _.each(themesInfo.themes, function (theme) { _this19._themeListing.push({ label: StringResources.get(theme.name + 'Label'), value: theme.name }); }); return _this19._themeListing; }, function () { _this19._themeListing = []; return _this19._themeListing; }); } else { return Promise.resolve(this._themeListing); } }, /** * Reduces this view and all its descendants to a single value. * * @param {function} callback function(view, accumulator) * @param {object} acc accumulator. * * returns the accumulated value returned by the last invocation of callback. */ reduce: function reduce(callback, acc) { acc = callback(this, acc); if (this.model.items) { this.model.items.forEach(function (model) { var view = this.layoutController.getLayoutView(model.id); if (view && view.reduce) { acc = view.reduce(callback, acc); } }, this); } return acc; }, /** * @param {Boolean} loading indicating if the loading animation should be displayed. */ setLoading: function setLoading(loading) { if (!this._$loadingIndicatorBlocker && !loading) { // if we are called for the first time with false, don't bother loading the animation at all. return Promise.resolve(null); } if (!this._$loadingIndicatorBlocker) { this._$loadingIndicatorBlocker = $('
'); } if (loading) { this.$el.append(this._$loadingIndicatorBlocker); ReactDOM.render(CenterLoadingSpinner({ size: 'normal', variant: 'circle' }), this._$loadingIndicatorBlocker[0]); } else { ReactDOM.unmountComponentAtNode(this._$loadingIndicatorBlocker[0]); this._$loadingIndicatorBlocker.remove(); this._$loadingIndicatorBlocker = null; } return Promise.resolve(null); }, /** If the layout has 'subviews' (ie. tabs or scenes), return the id of the current one. */ getSelectedSubViewId: function getSelectedSubViewId() { // sub-classes are expected to implement this as appropriate. return this.id; }, /** If the layout has 'subviews' (ie. tabs or scenes), return the title of the current one. */ getSelectedSubViewTitle: function getSelectedSubViewTitle() { // sub-classes are expected to implement this as appropriate. return null; }, /** * Get layout ratio from parent layout * * Note: the method has a limitation. * The method only supports horizontal layout ratios, so the Y axis * ratio (height) will always be fixed to 1. * This should be implemented when vertical ratios are needed. (TODO) * * @return {Object} ratio; has properties `width` and `height` with the appropriate ratios */ getSelfRatio: function getSelfRatio() { var childCount = this.parentLayout.model.getVisualizations().length || 1; var width = 1 / childCount; var height = 1; return { width: width, height: height }; }, /** * Get parent layout API * * @return {Object} */ getParentLayout: function getParentLayout() { return this.parentLayout.getAPI(); }, /** * Get layout API * * @return {LayoutApi} layout API */ getAPI: function getAPI() { return this.api.getAPI(); }, /** * Render layout * * Note: * historically we were calling onShow when we needed a widget to render. * renaming this across so many components is hard right now, but * let's try to use `render` instead of `onShow`. */ render: function render(options) { this.onShow(options); } }); return BaseLayout; }); //# sourceMappingURL=LayoutBaseView.js.map