4 * Copyright 2016 RIFT.IO Inc
6 * Licensed under the Apache License, Version 2.0 (the "License");
7 * you may not use this file except in compliance with the License.
8 * You may obtain a copy of the License at
10 * http://www.apache.org/licenses/LICENSE-2.0
12 * Unless required by applicable law or agreed to in writing, software
13 * distributed under the License is distributed on an "AS IS" BASIS,
14 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 * See the License for the specific language governing permissions and
16 * limitations under the License.
21 import _
from 'lodash'
23 import alt
from '../alt'
24 import UID
from '../libraries/UniqueId'
25 import DescriptorModelFactory
from '../libraries/model/DescriptorModelFactory'
26 import PanelResizeAction
from '../actions/PanelResizeAction'
27 import CatalogItemsActions
from '../actions/CatalogItemsActions'
28 import CanvasEditorActions
from '../actions/CanvasEditorActions'
29 import ComposerAppActions
from '../actions/ComposerAppActions'
30 import CatalogFilterActions
from '../actions/CatalogFilterActions'
31 import CanvasPanelTrayActions
from '../actions/CanvasPanelTrayActions'
32 import SelectionManager
from '../libraries/SelectionManager'
33 import CatalogDataStore
from '../stores/CatalogDataStore'
34 import isFullScreen
from '../libraries/isFullScreen'
36 const getDefault
= (name
, defaultValue
) => {
37 const val
= window
.localStorage
.getItem('defaults-' + name
);
39 if (_
.isNumber(val
)) {
41 return setDefault(name
, 0);
46 setDefault(name
, defaultValue
);
50 const setDefault
= (name
, defaultValue
) => {
51 window
.localStorage
.setItem('defaults-' + name
, defaultValue
);
55 /* the top and bottom positions are managed by css; requires div to be display: absolute*/
57 left
: getDefault('catalog-panel-start-width', 300),
58 right
: getDefault('details-panel-start-width', 365),
59 bottom
: 25 + getDefault('defaults-forwarding-graphs-panel-start-height', 0),
61 zoom
: getDefault('zoom', 100),
62 filterCatalogBy
: 'nsd',
63 defaultPanelTrayOpenZoom
: (() => {
64 let zoom
= parseFloat(getDefault('panel-tray-zoom', 75));
68 zoom
= Math
.min(100, zoom
);
69 zoom
= Math
.max(25, zoom
);
70 setDefault('panel-tray-zoom', zoom
);
75 const autoZoomCanvasScale
= d3
.scale
.linear().domain([0, 300]).range([100, 50]).clamp(true);
77 const uiTransientState
= {};
79 class ComposerAppStore
{
82 // the catalog item currently being edited in the composer
84 // the left and right sides of the canvas area
87 right
: defaults
.right
,
88 bottom
: defaults
.bottom
90 uiTransientState
.restoreLayout
= this.layout
;
91 this.zoom
= defaults
.zoom
;
92 this.showMore
= defaults
.showMore
;
93 this.filterCatalogByTypeValue
= defaults
.filterCatalogBy
;
97 this.messageType
= '';
98 this.showJSONViewer
= false;
99 this.showClassifiers
= {};
100 this.editPathsMode
= false;
101 this.fullScreenMode
= false;
103 onResize
: PanelResizeAction
.RESIZE
,
104 editCatalogItem
: CatalogItemsActions
.EDIT_CATALOG_ITEM
,
105 catalogItemMetaDataChanged
: CatalogItemsActions
.CATALOG_ITEM_META_DATA_CHANGED
,
106 catalogItemDescriptorChanged
: CatalogItemsActions
.CATALOG_ITEM_DESCRIPTOR_CHANGED
,
107 toggleShowMoreInfo
: CanvasEditorActions
.TOGGLE_SHOW_MORE_INFO
,
108 showMoreInfo
: CanvasEditorActions
.SHOW_MORE_INFO
,
109 showLessInfo
: CanvasEditorActions
.SHOW_LESS_INFO
,
110 applyDefaultLayout
: CanvasEditorActions
.APPLY_DEFAULT_LAYOUT
,
111 addVirtualLinkDescriptor
: CanvasEditorActions
.ADD_VIRTUAL_LINK_DESCRIPTOR
,
112 addForwardingGraphDescriptor
: CanvasEditorActions
.ADD_FORWARDING_GRAPH_DESCRIPTOR
,
113 addVirtualDeploymentDescriptor
: CanvasEditorActions
.ADD_VIRTUAL_DEPLOYMENT_DESCRIPTOR
,
114 selectModel
: ComposerAppActions
.SELECT_MODEL
,
115 outlineModel
: ComposerAppActions
.OUTLINE_MODEL
,
116 showError
: ComposerAppActions
.SHOW_ERROR
,
117 clearError
: ComposerAppActions
.CLEAR_ERROR
,
118 setDragState
: ComposerAppActions
.SET_DRAG_STATE
,
119 filterCatalogByType
: CatalogFilterActions
.FILTER_BY_TYPE
,
120 setCanvasZoom
: CanvasEditorActions
.SET_CANVAS_ZOOM
,
121 showJsonViewer
: ComposerAppActions
.SHOW_JSON_VIEWER
,
122 closeJsonViewer
: ComposerAppActions
.CLOSE_JSON_VIEWER
,
123 toggleCanvasPanelTray
: CanvasPanelTrayActions
.TOGGLE_OPEN_CLOSE
,
124 openCanvasPanelTray
: CanvasPanelTrayActions
.OPEN
,
125 closeCanvasPanelTray
: CanvasPanelTrayActions
.CLOSE
,
126 enterFullScreenMode
: ComposerAppActions
.ENTER_FULL_SCREEN_MODE
,
127 exitFullScreenMode
: ComposerAppActions
.EXIT_FULL_SCREEN_MODE
132 if (e
.type
=== 'resize-manager.resize.catalog-panel') {
133 const layout
= Object
.assign({}, this.layout
);
134 layout
.left
= Math
.max(0, layout
.left
- e
.moved
.x
);
135 if (layout
.left
!== this.layout
.left
) {
136 this.setState({layout
: layout
});
138 } else if (e
.type
=== 'resize-manager.resize.details-panel') {
139 const layout
= Object
.assign({}, this.layout
);
140 layout
.right
= Math
.max(0, layout
.right
+ e
.moved
.x
);
141 if (layout
.right
!== this.layout
.right
) {
142 this.setState({layout
: layout
});
144 } else if (/^resize-manager\.resize\.canvas-panel-tray/.test(e
.type
)) {
145 const layout
= Object
.assign({}, this.layout
);
146 layout
.bottom
= Math
.max(25, layout
.bottom
+ e
.moved
.y
);
147 if (layout
.bottom
!== this.layout
.bottom
) {
148 const zoom
= autoZoomCanvasScale(layout
.bottom
) ;
149 if (this.zoom
!== zoom
) {
150 this.setState({layout
: layout
, zoom
: zoom
});
152 this.setState({layout
: layout
});
155 } else if (e
.type
!== 'resize') {
156 console
.log('no resize handler for ', e
.type
, '. Do you need to add a handler in ComposerAppStore::onResize()?')
158 SelectionManager
.refreshOutline();
162 if(!document
.body
.classList
.contains('resizing')) {
163 this.setState({item
: _
.cloneDeep(item
)});
165 SelectionManager
.refreshOutline();
168 editCatalogItem(item
) {
169 if (item
&& item
.uiState
) {
170 item
.uiState
.isOpenForEdit
= true;
171 if (item
.uiState
.type
!== 'nsd') {
172 this.closeCanvasPanelTray();
175 SelectionManager
.select(item
);
176 this.updateItem(item
);
179 catalogItemMetaDataChanged(item
) {
180 this.updateItem(item
);
183 catalogItemDescriptorChanged(itemDescriptor
) {
184 this.catalogItemMetaDataChanged(itemDescriptor
.model
);
188 this.setState({showMore
: true});
192 this.setState({showMore
: false});
196 this.setState({message
: data
.errorMessage
, messageType
: 'error'});
200 this.setState({message
: '', messageType
: ''});
203 toggleShowMoreInfo() {
204 this.setState({showMore
: !this.showMore
});
207 applyDefaultLayout() {
208 if (this.item
&& this.item
.uiState
&& this.item
.uiState
.containerPositionMap
) {
209 if (!_
.isEmpty(this.item
.uiState
.containerPositionMap
)) {
210 this.item
.uiState
.containerPositionMap
= {};
211 CatalogItemsActions
.catalogItemMetaDataChanged
.defer(this.item
);
216 addVirtualLinkDescriptor(dropCoordinates
= null) {
219 if (this.item
.uiState
.type
=== 'nsd') {
220 const nsdc
= DescriptorModelFactory
.newNetworkService(this.item
);
221 vld
= nsdc
.createVld();
222 } else if (this.item
.uiState
.type
=== 'vnfd') {
223 const vnfd
= DescriptorModelFactory
.newVirtualNetworkFunction(this.item
);
224 vld
= vnfd
.createVld();
227 vld
.uiState
.dropCoordinates
= dropCoordinates
;
228 SelectionManager
.clearSelectionAndRemoveOutline();
229 SelectionManager
.addSelection(vld
);
230 this.updateItem(vld
.getRoot().model
);
231 CatalogItemsActions
.catalogItemDescriptorChanged
.defer(vld
.getRoot());
236 addForwardingGraphDescriptor(dropCoordinates
= null) {
237 if (this.item
&& this.item
.uiState
.type
=== 'nsd') {
238 const nsdc
= DescriptorModelFactory
.newNetworkService(this.item
);
239 const fg
= nsdc
.createVnffgd();
240 fg
.uiState
.dropCoordinates
= dropCoordinates
;
241 SelectionManager
.clearSelectionAndRemoveOutline();
242 SelectionManager
.addSelection(fg
);
243 this.updateItem(nsdc
.model
);
244 CatalogItemsActions
.catalogItemDescriptorChanged
.defer(nsdc
);
248 addVirtualDeploymentDescriptor(dropCoordinates
= null) {
249 if (this.item
.uiState
.type
=== 'vnfd') {
250 const vnfd
= DescriptorModelFactory
.newVirtualNetworkFunction(this.item
);
251 const vdu
= vnfd
.createVdu();
252 vdu
.uiState
.dropCoordinates
= dropCoordinates
;
253 SelectionManager
.clearSelectionAndRemoveOutline();
254 SelectionManager
.addSelection(vdu
);
255 this.updateItem(vdu
.getRoot().model
);
256 CatalogItemsActions
.catalogItemDescriptorChanged
.defer(vdu
.getRoot());
260 selectModel(container
) {
261 if (SelectionManager
.select(container
)) {
262 const model
= DescriptorModelFactory
.isContainer(container
) ? container
.getRoot().model
: container
;
263 this.catalogItemMetaDataChanged(model
);
268 const uid
= UID
.from(obj
);
269 requestAnimationFrame(() => {
270 SelectionManager
.outline(Array
.from(document
.querySelectorAll(`[data-uid="${uid}"]`)));
275 SelectionManager
.clearSelectionAndRemoveOutline();
276 this.catalogItemMetaDataChanged(this.item
);
279 setDragState(dragState
) {
280 this.setState({drag
: dragState
});
283 filterCatalogByType(typeValue
) {
284 this.setState({filterCatalogByTypeValue
: typeValue
})
287 setCanvasZoom(zoom
) {
288 this.setState({zoom
: zoom
});
292 this.setState({showJSONViewer
: true});
296 this.setState({showJSONViewer
: false});
299 toggleCanvasPanelTray() {
300 const layout
= this.layout
;
301 if (layout
.bottom
> 25) {
302 this.closeCanvasPanelTray();
304 this.openCanvasPanelTray();
308 openCanvasPanelTray() {
310 left
: this.layout
.left
,
311 right
: this.layout
.right
,
314 const zoom
= defaults
.defaultPanelTrayOpenZoom
;
315 if (this.zoom
!== zoom
) {
316 this.setState({layout
: layout
, zoom
: zoom
, restoreZoom
: this.zoom
});
318 this.setState({layout
: layout
});
322 closeCanvasPanelTray() {
324 left
: this.layout
.left
,
325 right
: this.layout
.right
,
328 const zoom
= this.restoreZoom
|| autoZoomCanvasScale(layout
.bottom
);
329 if (this.zoom
!== zoom
) {
330 this.setState({layout
: layout
, zoom
: zoom
, restoreZoom
: null});
332 this.setState({layout
: layout
, restoreZoom
: null});
336 enterFullScreenMode() {
339 * https://developer.mozilla.org/en-US/docs/Web/API/Fullscreen_API
340 * This is an experimental api but works our target browsers and ignored by others
342 const eventNames
= ['fullscreenchange', 'mozfullscreenchange', 'webkitfullscreenchange', 'msfullscreenchange'];
344 const appRoot
= document
.body
;//.getElementById('RIFT_wareLaunchpadComposerAppRoot');
348 function onFullScreenChange() {
350 if (isFullScreen()) {
351 const layout
= comp
.layout
;
352 const restoreLayout
= _
.cloneDeep(layout
);
353 uiTransientState
.restoreLayout
= restoreLayout
;
356 comp
.setState({fullScreenMode
: true, layout
: layout
, restoreLayout
: restoreLayout
});
358 comp
.setState({fullScreenMode
: false, layout
: uiTransientState
.restoreLayout
});
363 if (this.fullScreenMode
=== false) {
365 if (appRoot
.requestFullscreen
) {
366 appRoot
.requestFullscreen();
367 } else if (appRoot
.msRequestFullscreen
) {
368 appRoot
.msRequestFullscreen();
369 } else if (appRoot
.mozRequestFullScreen
) {
370 appRoot
.mozRequestFullScreen();
371 } else if (appRoot
.webkitRequestFullscreen
) {
372 appRoot
.webkitRequestFullscreen(Element
.ALLOW_KEYBOARD_INPUT
);
375 eventNames
.map(name
=> {
376 document
.removeEventListener(name
, onFullScreenChange
);
377 document
.addEventListener(name
, onFullScreenChange
);
384 exitFullScreenMode() {
386 if (document
.exitFullscreen
) {
387 document
.exitFullscreen();
388 } else if (document
.msExitFullscreen
) {
389 document
.msExitFullscreen();
390 } else if (document
.mozCancelFullScreen
) {
391 document
.mozCancelFullScreen();
392 } else if (document
.webkitExitFullscreen
) {
393 document
.webkitExitFullscreen();
396 this.setState({fullScreenMode
: false});
402 export default alt
.createStore(ComposerAppStore
, 'ComposerAppStore');