define([
'dojo/_base/declare',
'dojo/_base/lang',
'dojo/_base/array',
'dojo/_base/html',
'dojo/topic',
'dojo/on',
'dojo/dom-construct',
'dojo/dom-geometry',
'dojo/Deferred',
'dojo/debounce',
'require',
'../WidgetManager',
'../PanelManager',
'../utils',
'../dijit/LoadingShelter',
'./BaseLayoutManager',
'./GridMobileController'
],
function(declare, lang, array, html, topic, on, domConstruct, domGeometry,
Deferred, debounce, require, WidgetManager, PanelManager,
utils, LoadingShelter, BaseLayoutManager, MobileController) {
/* global jimuConfig:true */
/* global $:true */
var instance = null, clazz;
var LAYOUT_TYPE_STACK = 'stack', LAYOUT_TYPE_COMPONENT = 'component';
var PORTRAIT_MODE = 1, LANDSCAPE_MODE = 2, DEBOUNCE_LIMIE = 200;
clazz = declare([BaseLayoutManager], {
isEditing: false,
maxStackId: 0,
dashboardWidgets: [],
dashboardPanels: {},
currentMode: 0,
_createLayoutDeferred: null,
_isLastLayoutMobile: false,
_isDestroying: false,
nls: null,
_addWidgetTipTemplate: '
' +
'
${groupIndex}
' +
'
${this.nls.addWidgetTip}
' +
'
',
name: 'GridLayoutManager',
constructor: function(options, domId) {
/*jshint unused: false*/
this.widgetManager = WidgetManager.getInstance();
this.panelManager = PanelManager.getInstance();
this.widgetPlaceholders = [];
this.preloadWidgetIcons = [];
this.preloadGroupPanels = [];
this.invisibleWidgetIds = [];
//avoid mobileKeyboard resize
if (!utils.isMobileUa()) {
this.own(on(window, 'resize', lang.hitch(this, this.resize)));
} else {
this.own(on(window, 'orientationchange', lang.hitch(this, this.resize)));
}
this.id = domId;
this._createLayoutDeferred = null;
},
postMixInProperties: function() {
this.inherited(arguments);
this.nls = {};
lang.mixin(this.nls, window.jimuNls.gridLayout, window.jimuNls.common);
this._addWidgetTipTemplate =
this._addWidgetTipTemplate.replace('${this.nls.addWidgetTip}', this.nls.addWidgetTip);
},
isSupportEdit: function(){
return true;
},
postCreate: function(){
this.containerNode = this.domNode;
this._isLastLayoutMobile = window.appInfo.isRunInMobile;
},
layout: null,
map: null,
mapContainer: null,
mapId: 'map',
hlDiv: null,
animTime: 500,
resize: function() {
//resize golden layout container
this._resizeLayout();
//resize widgets. the panel's resize is called by the panel manager.
//widgets which is in panel is resized by panel
array.forEach(this.widgetManager.getAllWidgets(), function(w) {
if (w.inPanel === false) {
w.resize();
}
}, this);
},
setMap: function(map){
this.inherited(arguments);
this.panelManager.setMap(map);
},
_resizeLayout: function() {
if(window.appInfo.isRunInMobile){
var box = domGeometry.getMarginBox(window.jimuConfig.layoutId);
var mode;
if (box.w > box.h) {
mode = LANDSCAPE_MODE;
} else {
mode = PORTRAIT_MODE;
}
if(this.mobilePanel) {
if(this.currentMode !== mode){
this.currentMode = mode;
this.mobilePanel.setMobileLayout(mode);
}
this.mobilePanel.resize();
}
}
var container, width, height;
//resize golden layout container
if (!this.isEditing && this.layout) {
container = $(this.layoutContainer);
width = container.width();
height = container.height();
this.layout.updateSize(width, height);
} else if (this.isEditing && this.editingLayout) {
container = $(this.editLayoutDiv);
width = container.width();
height = container.height();
this.editingLayout.updateSize(width, height);
}
},
// Two conditions to invoke this method:
// 1. Map loaded or map changed.
// 2. Theme changed.
// Create and arrange widgets in this method
loadAndLayout: function(appConfig){
this.appConfig = appConfig;
// this.setMapPosition(appConfig.map.position);
var loading = new LoadingShelter(), def;
loading.placeAt(this.layoutId);
loading.startup();
// load dashboard grouops
if (this._createLayoutDeferred && !this._createLayoutDeferred.isResolved()) {
def = this._createLayoutDeferred;
} else {
def = new Deferred();
def.resolve();
}
def.then(lang.hitch(this, this._loadOnScreenGroups))
.then(lang.hitch(this, this._reArrangeWidgetsLayout, true))
.then(lang.hitch(this, function(){
if(loading){
loading.destroy();
loading = null;
}
console.timeEnd('Load widgetOnScreen');
topic.publish('preloadWidgetsLoaded');
}), lang.hitch(this, function(){
if(loading){
loading.destroy();
loading = null;
}
//if error when load widget, let the others continue
console.timeEnd('Load widgetOnScreen');
topic.publish('preloadWidgetsLoaded');
}));
},
createMapDiv: function(mapId){
if(html.byId(mapId)){
this.mapDiv = html.byId(mapId);
}else{
this.mapDiv = html.create('div', {
id: mapId,
style: lang.mixin({
position: 'absolute',
backgroundColor: '#EEEEEE',
overflow: 'hidden',
minWidth:'1px',
minHeight:'1px'
}, utils.getPositionStyle(this.appConfig.map.position))
}, this.layoutId);
}
},
getMapDiv: function() {
return this.mapDiv;
},
onThemeLoad: function() {
this._resizeLayout();
},
_enableEditing: function() {
if (this.isEditing) {
return;
}
this._createEditingLayout();
$(this.layoutContainer).addClass('hidden');
$(this.editContainer).removeClass('hidden');
$(this.modifyLayoutBtn).addClass('hidden');
this.isEditing = true;
this._resizeLayout();
},
_disableEditing: function() {
$(this.layoutContainer).removeClass('hidden');
$(this.editContainer).addClass('hidden');
$(this.modifyLayoutBtn).removeClass('hidden');
this.isEditing = false;
this._resizeLayout();
},
_resetLayoutDefinition: function() {
this._disableEditing();
this._createLayout(false, false);
topic.publish('editLayoutCancelled');
},
_saveLayoutDefinition: function() {
var newConfig = lang.clone(this.editingLayout.toConfig());
var newLayoutDefinition = lang.clone(this.appConfig.layoutDefinition);
this._simplifyLayoutDefinition(newConfig.content[0]);
this._disableEditing();
var rootElem = this.editingLayout.root.contentItems[0];
var stacks = rootElem.getItemsByType('stack');
var groupIds = [];
array.forEach(stacks, function(item) {
var isMapStack = array.some(item.contentItems, lang.hitch(this, function(contentItem) {
if(contentItem.isComponent && contentItem.config.componentName === 'map') {
return true;
}
}));
if (!isMapStack && /^dd_group_\d+$/.test(item.config.id)) {
groupIds.push(item.config.id);
}
});
newLayoutDefinition.layout.config.content = newConfig.content;
topic.publish('layoutDefinitionChanged', {
layoutDefinition: newLayoutDefinition,
groupIds: groupIds
});
},
// Remove all items of type 'component'
_simplifyLayoutDefinition: function(contentItem) {
if (contentItem.type === 'component') {
return;
} else if (contentItem.type === 'stack') {
var mapComponent;
contentItem.activeItemIndex = 0;
array.some(contentItem.content, lang.hitch(this, function(item) {
if(item.type === 'component' && item.componentName === 'map') {
mapComponent = item;
return true;
}
}));
if (mapComponent) {
contentItem.id = 'map';
contentItem.content = [mapComponent];
} else {
contentItem.content = [];
}
return;
}
array.forEach(contentItem.content, lang.hitch(this, function(item) {
this._simplifyLayoutDefinition(item);
}));
},
// Invoke this method every time a new layout is created.
_bindLayoutEvents: function() {
this.editingLayout.on( 'initialised', lang.hitch(this, function(){
// Make the create button as a drag source
this.editingLayout.createDragSource( $(this.dragCreateBtn), {
title: ' ',
type: 'component',
reorderEnabled: false,
componentName: 'widget panel'
});
// Get the max index of dashboard group panel
var rootElem = this.editingLayout.root.contentItems[0];
var stacks = rootElem.getItemsByType('stack');
var stackIds = [];
array.forEach(stacks, function(item) {
if (/^dd_group_\d+$/.test(item.config.id)) {
var idx = parseInt(item.config.id.split('_')[2], 10);
stackIds.push(idx);
}
});
if (stackIds.length > 0) {
stackIds = stackIds.sort(function(a, b) {
return a > b;
});
this.maxStackId = stackIds[stackIds.length - 1];
} else {
this.maxStackId = 0;
}
this._arrangeWidgetsInEditingLayout();
}));
this.editingLayout.on( 'stackCreated', lang.hitch(this, function(stack){
// Assign id to stack that has no id attribute
this.maxStackId++;
if (!stack.config.id) {
stack.config.id = 'dd_group_' + this.maxStackId;
}
}));
},
_arrangeWidgetsInEditingLayout: function() {
var groups = this.appConfig.widgetOnScreen.groups;
array.forEach(groups, lang.hitch(this, function(group){
var groupConfig = this.appConfig.getConfigElementById(group.id);
var rootElem = this.editingLayout.root.contentItems[0];
var result = rootElem.getItemsById(groupConfig.id), stack;
if (result && result.length > 0 &&
LAYOUT_TYPE_STACK === result[0].type) {
stack = result[0];
}
if (stack ) {
// Layout has been recreated. Each stack contains only one component.
array.forEach(groupConfig.widgets, lang.hitch(this, function(widget, index) {
var itemConfig = {
id: widget.id,
type: 'component',
title: widget.label,
reorderEnabled: false,
componentName: 'widget panel',
componentState: {
label: widget.label
}
};
if (stack.contentItems.length > index) {
var contentItem = stack.contentItems[index];
// replace the first component
contentItem.config.id = widget.id;
contentItem.setTitle(widget.label);
} else {
stack.addChild(itemConfig);
}
}));
}
}));
},
_createActionBar: function(container) {
if (!this.actionBar) {
this.actionBar = domConstruct.create('div', {
"class": 'layout-actionbar'
}, container);
this.dragCreateBtn = domConstruct.create('div', {
"class": 'jimu-btn jimu-float-leading jimu-leading-margin2 add-btn',
innerHTML: this.nls.dragToAdd
}, this.actionBar);
var cancelBtn = domConstruct.create('div', {
"class": 'jimu-btn-vacation jimu-float-trailing jimu-trailing-margin2 cancel-btn',
innerHTML: this.nls.cancel
}, this.actionBar);
var saveBtn = domConstruct.create('div', {
"class": 'jimu-btn jimu-float-trailing save-btn',
innerHTML: this.nls.ok
}, this.actionBar);
this.own(on(cancelBtn, 'click', lang.hitch(this, this._resetLayoutDefinition)));
this.own(on(saveBtn, 'click', lang.hitch(this, this._saveLayoutDefinition)));
}
},
_destroyActionBar: function() {
domConstruct.destroy(this.actionBar);
this.actionBar = null;
},
/**
* There are three types of layout:
* 1. Normal layout for PC to view the app
* 2. Mobile layout for mobile device to view the app
* 3. Editing layout for builder to edit the layout. Only available for PC.
* @param {groupsChanged} boolean Indicate whether the on screen widget groups have
* been changed. If true, we need to invoke this._loadOnScreenGroups() to update
* this.dashboardWidgets and this.dashboardPanels. If false, all widgets and panels
* are ready to use, just re-arrange the widget groups.
* @param {reloadOnScreenWidgets} boolean Whether the on screen widgets should be recreated.
*/
_createLayout: function(groupsChanged, reloadOnScreenWidgets, createLayoutOnly) {
// Previous createlayout operation
if (this._createLayoutDeferred && !this._createLayoutDeferred.isResolved()) {
return this._createLayoutDeferred.then(lang.hitch(this, function() {
return this._doCreateLayout(groupsChanged, reloadOnScreenWidgets, createLayoutOnly);
}));
} else {
return this._doCreateLayout(groupsChanged, reloadOnScreenWidgets, createLayoutOnly);
}
},
_doCreateLayout: function(groupsChanged, reloadOnScreenWidgets, createLayoutOnly) {
var createLayoutFunc, def = new Deferred();
this._createLayoutDeferred = new Deferred();
if(this.isEditing) {
return this._createEditingLayout();
}
if(window.appInfo.isRunInMobile) {
createLayoutFunc = lang.hitch(this, this._createMobileLayout);
} else {
if (this.mobileController) {
this.mobileController.destroyOnScreenWidgets();
this.mobileController.destroy();
this.mobileController = null;
}
createLayoutFunc = lang.hitch(this, this._createNormalLayout);
}
if(createLayoutOnly) { // map not initialized, only create layout
createLayoutFunc().then(lang.hitch(this, function() {
this._createLayoutDeferred.resolve();
def.resolve();
}));
} else if(groupsChanged) {
createLayoutFunc()
.then(lang.hitch(this, this._loadOnScreenGroups))
.then(lang.hitch(this, this._reArrangeWidgetsLayout, reloadOnScreenWidgets))
.then(lang.hitch(this, function() {
this._createLayoutDeferred.resolve();
def.resolve();
}));
} else {
createLayoutFunc()
.then(lang.hitch(this, this._reArrangeWidgetsLayout, reloadOnScreenWidgets))
.then(lang.hitch(this, function() {
this._createLayoutDeferred.resolve();
def.resolve();
}));
}
return def;
},
// detach all widget panels
_detachWidgets: function(contentItem) {
if (contentItem.type === 'stack') {
while (contentItem.contentItems.length > 1) {
contentItem.removeChild(contentItem.contentItems[0], true);
}
var lastChild = contentItem.contentItems[0];
contentItem.addChild({
type: "component",
componentName: "widget panel",
title: ' ',
isClosable: false
});
contentItem.removeChild(lastChild, true);
return;
}
array.forEach(contentItem.contentItems, lang.hitch(this, function(item) {
this._detachWidgets(item);
}));
},
_setupWidgets: function(contentItem, isClosable) {
contentItem.isClosable = isClosable;
if (contentItem.type === 'component') {
return;
}else if (contentItem.type === 'stack') {
if (contentItem.content.length === 0) {
contentItem.content = [{
type: "component",
componentName: "widget panel",
title: ' ',
isClosable: isClosable
}];
}
return;
}
array.forEach(contentItem.content, lang.hitch(this, function(item) {
this._setupWidgets(item);
}));
},
_onActivePanelChanged: function(contentItem) {
var panel;
if (this._isDestroying) {
return;
}
if (contentItem.componentName === 'widget panel') {
if (contentItem.config.id) {
panel = this.dashboardPanels[contentItem.config.id];
if (!panel || !panel.domNode) { // Load panel when it is first activated.
this._loadDashboardWidget(contentItem.config.id).then(lang.hitch(this, function(panel) {
if (panel) {
contentItem.container.getElement().html(panel.domNode);
contentItem.container.on('resize', debounce(lang.hitch(this, function() {
if (contentItem.container.width > 0 && contentItem.container.height > 0) {
panel.resize();
}
}), DEBOUNCE_LIMIE));
panel.resize();
this.panelManager.openPanel(panel);
}
}));
} else {
if (contentItem.container.getElement().find('.jimu-panel').length === 0) {
contentItem.container.getElement().html(panel.domNode);
contentItem.container.on('resize', debounce(lang.hitch(this, function() {
if (contentItem.container.width > 0 && contentItem.container.height > 0) {
if (panel) {
panel.resize();
}
}
}), DEBOUNCE_LIMIE));
}
panel.resize();
this.panelManager.openPanel(panel);
}
// Close all other panels
var groupId = contentItem.parent.config.id;
var groups = this.appConfig.widgetOnScreen.groups, widgets;
array.some(groups, lang.hitch(this, function(groupConfig) {
if (groupConfig.id === groupId) {
widgets = groupConfig.widgets;
return true;
}
}));
array.forEach(widgets, lang.hitch(this, function(widgetConfig) {
if(widgetConfig.id !== contentItem.config.id) {
panel = this.dashboardPanels[widgetConfig.id];
if(panel) {
this.panelManager.closePanel(panel);
}
}
}));
}
}
},
_createNormalLayout: function() {
var def = new Deferred();
this._isDestroying = false;
// recreate golden layout instance
if(!this.layoutContainer) {
this.layoutContainer = domConstruct.create('div', {
"class": this.appConfig.mode === 'config' ? 'jimu-dnd-layout config' : 'jimu-dnd-layout'
}, this.layoutId);
if (this.appConfig.mode === 'config') {
this.modifyLayoutBtn = domConstruct.create('div', {
"class": 'jimu-dnd-layout modify-btn',
innerHTML: '' +
this.nls.modifyLayout + '
'
}, this.layoutId);
this.own(on(this.modifyLayoutBtn, 'click', lang.hitch(this, function(){
topic.publish('editLayout');
})));
}
}
array.some(this.appConfig.widgetOnScreen.widgets, function(widgetConfig) {
if (widgetConfig.uri === 'themes/DashboardTheme/widgets/Header/Widget') {
if (widgetConfig.visible) {
html.setStyle(this.layoutContainer, 'top', '80px');
} else {
this._removeHighLight(widgetConfig.id);
html.setStyle(this.layoutContainer, 'top', 0);
}
return true;
}
}, this);
var oldLayout = this.layout;
var config = lang.clone(this.appConfig.layoutDefinition.layout.config);
if (this.appConfig.mode === 'config') {
config.settings.reorderEnabled = false;
config.settings.resizeEnabled = false;
config.settings.enableHeaderDragging = false;
config.dimensions = {
borderWidth: 5,
dragProxyWidth: 0,
dragProxyHeight: 0
};
}
this._setupWidgets(config.content[0], false);
require(['libs/goldenlayout/goldenlayout'], lang.hitch(this, function(GodenLayout){
if (this.appConfig.mode === 'config') {
$(this.modifyLayoutBtn).removeClass('hidden');
}
this.layout = new GodenLayout(config, this.layoutContainer);
this.layout.registerComponent( 'widget panel', lang.hitch(this, function( container, componentState) {
var stack = container.parent.parent;
var groupId = stack.config.id, index, content;
if (!componentState.widgetId) {
content = this._addWidgetTipTemplate;
array.some(this.appConfig.widgetOnScreen.groups, function(groupConfig) {
if(groupConfig.id === groupId) {
if(this.appConfig.mode === 'config' && groupConfig.widgets.length === 0){
index = groupConfig.placeholderIndex;
}
return true;
}
}, this);
if(index){
content = content.replace('${groupIndex}', index);
container.getElement().html(content);
}
}
}));
this.layout.registerComponent( 'map', lang.hitch(this, function( container ){
container.setTitle('');
this.mapContainer = container.getElement();
$('#' + this.mapId).appendTo(this.mapContainer);
if (oldLayout) {
this._isDestroying = true;
this._detachWidgets(oldLayout.root.contentItems[0]);
oldLayout.destroy();
this._isDestroying = false;
}
if (this.mobilePanel) {
this.mobilePanel.destroy();
this.mobilePanel = null;
}
}));
this.layout.on( 'initialised', lang.hitch(this, function(){
var container = $(this.layoutContainer);
var width = container.width();
var height = container.height() > 0 ? container.height() : $('#' + this.layoutId).height();
this.layout.updateSize(width, height);
def.resolve();
}));
this.layout.on( 'stackCreated', lang.hitch(this, function(stack){
stack.on('activeContentItemChanged', lang.hitch(this, function(contentItem) {
this._onActivePanelChanged(contentItem);
}));
}));
this.layout.init();
}));
return def;
},
_createMobileLayout: function() {
var def = new Deferred();
if (this.modifyLayoutBtn) {
$(this.modifyLayoutBtn).addClass('hidden');
}
if (!this.mobilePanel) {
if (this.layout) {
this._isDestroying = true;
this._detachWidgets(this.layout.root.contentItems[0]);
this._isDestroying = false;
}
var box = domGeometry.getMarginBox(window.jimuConfig.layoutId), mobileLayout;
if (box.w > box.h) {
mobileLayout = LANDSCAPE_MODE;
} else {
mobileLayout = PORTRAIT_MODE;
}
this._loadMobilePanel(this.layoutId, mobileLayout).then(lang.hitch(this, function() {
if (this.layout) {
this.layout.destroy();
this.layout = null;
}
def.resolve();
}));
} else {
this.mobilePanel.clearPanels();
def.resolve();
}
return def;
},
_createEditingLayout: function() {
if(!this.editContainer) {
this.editContainer = domConstruct.create('div', {
"class": 'jimu-edit-layout hidden'
}, this.layoutId);
this._createActionBar(this.editContainer);
this.editLayoutDiv = domConstruct.create('div', {
"class": 'layout-container'
}, this.editContainer);
}
var oldLayout = this.editingLayout;
var config = lang.clone(this.appConfig.layoutDefinition.layout.config);
config.settings.enableHeaderDropping = false;
config.settings.reorderEnabled = false;
config.dimensions = {
borderWidth: 5,
dragProxyWidth: 0,
dragProxyHeight: 0
};
this._setupWidgets(config.content[0], true);
require(['libs/goldenlayout/goldenlayout'], lang.hitch(this, function(GodenLayout){
this.editingLayout = new GodenLayout(config, this.editLayoutDiv);
this.editingLayout.registerComponent( 'widget panel', lang.hitch(this, function( container){
container.getElement().html('');
}));
this.editingLayout.registerComponent( 'map', lang.hitch(this, function( container ){
container.setTitle('');
container.getElement().html( '' + this.nls.mapArea + '
' );
if (oldLayout) {
oldLayout.destroy();
}
}));
this._bindLayoutEvents();
this.editingLayout.init();
}));
},
_destroyLayout: function() {
$('#' + this.mapId).appendTo('#' + this.layoutId);
if(this.layout) {
this.layout.destroy();
this.layout = null;
domConstruct.destroy(this.layoutContainer);
this.layoutContainer = null;
if (this.modifyLayoutBtn) {
domConstruct.destroy(this.modifyLayoutBtn);
this.modifyLayoutBtn = null;
}
}
if (this.editingLayout) {
this._destroyActionBar();
this.editingLayout.destroy();
this.editingLayout = null;
domConstruct.destroy(this.editContainer);
this.editContainer = null;
}
},
/**
* before map destroy
* before theme change
*/
destroyOnScreenWidgetsAndGroups: function(){
this._destroyOnScreenWidgets();
// Destroy dashboard group widgets
this._destroyOnScreenGroups();
},
onActionTriggered: function(info){
if(window.appInfo.isRunInMobile) { // do not hanble action in mobile layout
return;
}
if (info.action === 'editLayout') {
this._enableEditing();
} else if(info.action === 'highLight'){
var goldenItem = this._findContentItemById(info.elementId);
if (goldenItem) {
if (goldenItem.isStack) {
$(goldenItem.element).addClass('highlight');
} else if(goldenItem.isComponent) {
$(goldenItem.tab.element).addClass('highlight');
}
return;
}
array.forEach(this.widgetPlaceholders, function(placehoder){
if(placehoder.configId === info.elementId){
this._highLight(placehoder);
}
}, this);
array.forEach(this.onScreenWidgetIcons, function(widgetIcon){
if (widgetIcon.configId === info.elementId){
this._highLight(widgetIcon);
}
}, this);
array.forEach(this.widgetManager.getOnScreenOffPanelWidgets(), function(panelessWidget){
if (panelessWidget.configId === info.elementId){
this._highLight(panelessWidget);
}
}, this);
} else if(info.action === 'removeHighLight'){
this._removeHighLight(info.elementId);
} else if(info.action === 'showLoading'){
html.setStyle(jimuConfig.loadingId, 'display', 'block');
html.setStyle(jimuConfig.mainPageId, 'display', 'none');
} else if(info.action === 'showApp'){
html.setStyle(jimuConfig.loadingId, 'display', 'none');
html.setStyle(jimuConfig.mainPageId, 'display', 'block');
}
},
/**
* Find item from the layout.
* type parameter is optional. Its value can be 'stack' or 'component'
*/
_findContentItemById: function(id, type) {
var rootElem = this.layout.root.contentItems[0];
var result = rootElem.getItemsById(id);
if (result && result.length > 0 &&
(!type || type === result[0].type)) {
return result[0];
}
return null;
},
_highLight: function(dijit){
if(!dijit.domNode){
//the dijit may be destroyed
return;
}
if (this.hlDiv){
this._removeHighLight(dijit);
}
var position = domGeometry.getMarginBox(dijit.domNode);
var hlStyle = {
position: 'absolute',
left: position.l + 'px',
top: position.t + 'px',
width: position.w + 'px',
height: position.h + 'px'
};
this.hlDiv = domConstruct.create('div', {
"style": hlStyle,
"class": 'icon-highlight'
}, dijit.domNode, 'before');
},
_removeHighLight: function(elementId){
if (/^dd_group_\d+$/.test(elementId)) { // dashboard group
var stack = this._findContentItemById(elementId, LAYOUT_TYPE_STACK);
if (stack) {
$(stack.element).removeClass('highlight');
}
} else if(/^widgets_\w+_\d+$/.test(elementId)) { // a specific widget
var component = this._findContentItemById(elementId, LAYOUT_TYPE_COMPONENT);
if (component) {
$(component.tab.element).removeClass('highlight');
}
}
if (this.hlDiv){
domConstruct.destroy(this.hlDiv);
this.hlDiv = null;
}
},
onEnter: function(appConfig, mapId){
this.appConfig = appConfig;
this.mapId = mapId;
this.isEditing = false;
this.createMapDiv(mapId);
// create layout
return this._createLayout(false, false, true);
},
onLeave: function(){
this._destroyLayout();
this.map = null;
},
// Widget configuration changed.
onWidgetChange: function(appConfig, widgetConfig){
this.appConfig = appConfig;
if (widgetConfig.uri === 'themes/DashboardTheme/widgets/Header/Widget') {
if (widgetConfig.visible) {
html.setStyle(this.layoutContainer, 'top', '80px');
} else {
this._removeHighLight(widgetConfig.id);
html.setStyle(this.layoutContainer, 'top', 0);
}
this._resizeLayout();
}
widgetConfig = appConfig.getConfigElementById(widgetConfig.id);
var panel = this.dashboardPanels[widgetConfig.id];
if (panel) {
panel.reloadWidget(widgetConfig);
var rootElem = this.layout.root.contentItems[0];
var result = rootElem.getItemsByType('component');
if (result && result.length > 0) {
result.forEach(function(component) {
if(component.config.id === widgetConfig.id &&
component.config.title !== widgetConfig.label) {
component.config.title = widgetConfig.label;
component.setTitle(widgetConfig.label);
}
});
}
return;
}
if (this.mobileController) {
this.mobileController.destroy();
this.mobileController = new MobileController({
appConfig: this.appConfig,
panelContainerNode: this.mobilePanel.widgetContainerNode
});
this.mobileController.placeAt(this.mapId);
}
this.onOnScreenWidgetChange(appConfig, widgetConfig);
},
/*
* Group changes. One widget is dragged from a group to another.
* Doesn't change the number of widgets.
*/
onOnScreenGroupsChange: function(appConfig) {
this.appConfig = appConfig;
this._createLayout(true, false);
},
onGroupChange: function(appConfig, groupConfig){
this.appConfig = appConfig;
groupConfig = appConfig.getConfigElementById(groupConfig.id);
if(groupConfig.isOnScreen){
this._createLayout(true, false);
}else{
array.forEach(this.widgetManager.getControllerWidgets(), function(controllerWidget){
if(controllerWidget.isControlled(groupConfig.id)){
this.reloadControllerWidget(appConfig, controllerWidget.id);
}
}, this);
array.forEach(this.panelManager.panels, function(panel){
if(panel.config.id === groupConfig.id){
panel.updateConfig(groupConfig);
}
}, this);
// TODO if in mobile layout, group change will affect the MobileController
}
},
_reArrangeWidgetsLayout: function(reloadOnScreenWidgets) {
// destroy all onscreen widgets
if (reloadOnScreenWidgets) {
this._destroyOnScreenWidgets();
}
if(window.appInfo.isRunInMobile) {
// recreate onscreen widgets according to mobile layout config
if (reloadOnScreenWidgets) {
this._loadMobileOnScreenWidgets();
}
this.mobilePanel.setPanels(this.dashboardWidgets, this.dashboardPanels);
} else {
if (reloadOnScreenWidgets) {
this.loadOnScreenWidgets(this.appConfig);
}
this._reArrangeWidgetsInDesktopLayout();
}
},
_reArrangeWidgetsInDesktopLayout: function() {
var groups = this.appConfig.widgetOnScreen.groups;
array.forEach(groups, lang.hitch(this, function(group){
var groupConfig = this.appConfig.getConfigElementById(group.id);
var stack = this._findContentItemById(groupConfig.id, LAYOUT_TYPE_STACK);
if (stack ) {
// Layout has been recreated. Each stack contains only one component.
array.forEach(groupConfig.widgets, lang.hitch(this, function(widget, index) {
var itemConfig = {
id: widget.id,
type: 'component',
title: widget.label,
componentName: 'widget panel',
componentState: {
widgetId: widget.id
}
};
if (stack.contentItems.length > index) {
var contentItem = stack.contentItems[index];
// replace the first component
contentItem.config.id = widget.id;
contentItem.config.title = widget.label;
contentItem.config.componentState = {
widgetId: widget.id
};
contentItem.setTitle(widget.label);
stack.setActiveContentItem(contentItem);
} else {
stack.addChild(itemConfig);
}
}));
}
}));
},
_loadMobileOnScreenWidgets: function() {
// in panel widgets
this.mobileController = new MobileController({
appConfig: this.appConfig,
panelContainerNode: this.mobilePanel.widgetContainerNode
});
this.mobileController.placeAt(this.mapId);
// onscreen widgets not in placeholder
array.forEach(this.appConfig.widgetOnScreen.widgets, function(widget) {
if (widget.uri && widget.visible && !widget.closeable) {
this.loadOnScreenWidget(widget, this.appConfig);
}
}, this);
},
_loadMobilePanel: function(container, mobileLayout) {
var panelConfig = this.appConfig.layoutDefinition.mobileLayout.panel,
def = new Deferred();
require([panelConfig.uri], lang.hitch(this, function(Panel){
var options = {
layoutId: this.layoutId,
mapId: this.mapId,
mobileLayout: mobileLayout,
config: {},
layoutManager: this
};
this.mobilePanel = new Panel(options);
this.own(on(this.mobilePanel, 'resized', debounce(lang.hitch(this, function(pos) {
if (this.mobileController) {
this.mobileController.setPanelPosition(pos);
}
}), DEBOUNCE_LIMIE)));
domConstruct.place(this.mobilePanel.domNode, container);
def.resolve();
}));
return def;
},
// return the widget contained in specified panel
_loadDashboardWidget: function(widgetId) {
var def = new Deferred(), panel = this.dashboardPanels[widgetId];
if (panel && panel.domNode) {
def.resolve(this.dashboardPanels[widgetId]);
return def;
}
var groups = this.appConfig.widgetOnScreen.groups, widgetConfig;
array.some(groups, lang.hitch(this, function(groupConfig) {
return array.some(groupConfig.widgets, lang.hitch(this, function(widget) {
if (widget.id === widgetId) {
widgetConfig = widget;
return true;
}
}));
}));
if (!widgetConfig) {
def.resolve(null);
return def;
}
var panelConfig = this.appConfig.layoutDefinition.layout.panel;
require([panelConfig.uri], lang.hitch(this, function(Panel){
var options = {
config: widgetConfig,
uri: panelConfig.uri,
map: this.map,
widgetManager: this.widgetManager,
panelManager: this.panelManager,
id: widgetId + '_panel',
position: {}
}, panel;
lang.mixin(options, widgetConfig.options);
try{
panel = new Panel(options);
this.dashboardPanels[widgetId] = panel;
def.resolve(panel);
console.log('panel [' + options.id + '] created.');
}catch(error){
console.log('create panel error: ' + error + ', panelId: ' + options.id);
def.reject(error);
return;
}
}));
return def;
},
onLayoutDefinitionChange: function(appConfig){
this.appConfig = appConfig;
this._createLayout(false, false);
},
onLayoutChange: function(appConfig){
var isMobile = window.appInfo.isRunInMobile, reload = false;
if(this.isEditing) { // Do not response layout change when editing layout
return;
}
if (isMobile !== this._isLastLayoutMobile) {
reload = true;
this._isLastLayoutMobile = isMobile;
}
this.appConfig = appConfig;
// group and widgets not changed. But if switch between mobile and desktop layout,
// we should recreate onscreen widget.
this._createLayout(false, reload).then(lang.hitch(this, function() {
//relayout placehoder
array.forEach(this.widgetPlaceholders, function(placeholder){
placeholder.moveTo(appConfig.getConfigElementById(placeholder.configId).position);
}, this);
//relayout icons
array.forEach(this.onScreenWidgetIcons, function(icon){
icon.moveTo(appConfig.getConfigElementById(icon.configId).position);
}, this);
//relayout paneless widget
array.forEach(this.widgetManager.getOnScreenOffPanelWidgets(), function(widget){
if(widget.closeable){
//this widget position is controlled by icon
return;
}
var position = appConfig.getConfigElementById(widget.id).position;
widget.setPosition(position);
}, this);
}));
},
openWidget: function(widgetId){
var contentItem, parent;
//check on screen widgets, we don't check not-closeable off-panel widget
array.forEach(this.onScreenWidgetIcons, function(widgetIcon){
if(widgetIcon.configId === widgetId){
widgetIcon.switchToOpen();
}
}, this);
//check grid items
contentItem = this._findContentItemById(widgetId, LAYOUT_TYPE_COMPONENT);
if(contentItem) {
parent = contentItem.parent;
if (parent && parent.isStack) {
parent.setActiveContentItem(contentItem);
}
}
},
_loadOnScreenGroups: function() {
// Update widgets in golden layout, remove unused widgets
var groups = this.appConfig.widgetOnScreen.groups;
var widgetIds = [], def = new Deferred();
array.forEach(groups, lang.hitch(this, function(group){
var groupConfig = this.appConfig.getConfigElementById(group.id);
array.forEach(groupConfig.widgets, function(widget) {
widgetIds.push(widget.id);
});
}));
array.forEach(this.dashboardWidgets, lang.hitch(this, function(widgetId) {
if (widgetIds.indexOf(widgetId) < 0) { // not in current groups
var panel = this.dashboardPanels[widgetId];
if (panel) {
this.panelManager.closePanel(panel);
panel.destroy();
}
this.dashboardPanels[widgetId] = null;
}
}));
this.dashboardWidgets = widgetIds;
def.resolve();
return def;
},
_destroyOnScreenWidgets: function() {
this.destroyOnScreenOffPanelWidgets();
this.destroyWidgetPlaceholders();
this.destroyOnScreenWidgetIcons();
if (this.mobileController) {
this.mobileController.destroy();
this.mobileController = null;
}
},
_destroyOnScreenGroups: function() {
array.forEach(this.dashboardWidgets, lang.hitch(this, function(widgetId){
var panel = this.dashboardPanels[widgetId];
if (panel) {
this.panelManager.closePanel(panel);
panel.destroy();
panel = null;
}
}));
this.panelManager.destroyAllPanels();
this.dashboardWidgets = [];
this.dashboardPanels = {};
if (this.mobilePanel) {
this.mobilePanel.clearPanels();
}
}
});
clazz.getInstance = function(options, domId) {
if (instance === null) {
instance = new clazz(options, domId);
window._layoutManager = instance;
}
return instance;
};
return clazz;
});