'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 = $('<div></div>');
			}
			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