3 * Copyright 2016 RIFT.IO Inc
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
9 * http://www.apache.org/licenses/LICENSE-2.0
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
20 import _isNumber
from 'lodash/isNumber'
21 import _cloneDeep
from 'lodash/cloneDeep'
22 import _isEmpty
from 'lodash/isEmpty'
23 import _isArray
from 'lodash/isArray'
24 import _mergeWith
from 'lodash/mergeWith'
25 import _uniqBy
from 'lodash/uniqBy'
26 import _isEqual
from 'lodash/isEqual'
27 import _findIndex
from 'lodash/findIndex'
28 import _remove
from 'lodash/remove'
29 import _get
from 'lodash/get';
30 import _set
from 'lodash/set';
32 import alt
from '../alt'
33 import UID
from '../libraries/UniqueId'
34 import DescriptorModel
from '../libraries/model/DescriptorModel'
35 import DescriptorModelFactory
from '../libraries/model/DescriptorModelFactory'
36 import PanelResizeAction
from '../actions/PanelResizeAction'
37 import CatalogItemsActions
from '../actions/CatalogItemsActions'
38 import CanvasEditorActions
from '../actions/CanvasEditorActions'
39 import DescriptorEditorActions
from '../actions/DescriptorEditorActions'
40 import ComposerAppActions
from '../actions/ComposerAppActions'
41 import CatalogFilterActions
from '../actions/CatalogFilterActions'
42 import CanvasPanelTrayActions
from '../actions/CanvasPanelTrayActions'
43 import SelectionManager
from '../libraries/SelectionManager'
44 import CatalogDataStore
from '../stores/CatalogDataStore'
45 import isFullScreen
from '../libraries/isFullScreen'
47 import FileManagerSource
from '../components/filemanager/FileManagerSource';
48 import FileManagerActions
from '../components/filemanager/FileManagerActions';
50 import Property
from '../libraries/model/DescriptorModelMetaProperty'
51 import DescriptorModelMetaFactory
from '../libraries/model/DescriptorModelMetaFactory'
53 import React
from 'react';
55 //Hack for crouton fix. Should eventually put composer in skyquake alt context
56 import SkyquakeComponent
from 'widgets/skyquake_container/skyquakeComponent.jsx';
57 let NotificationError
= null;
59 import utils
from '../libraries/utils';
61 class ComponentBridge
extends React
.Component
{
64 NotificationError
= this.props
.flux
.actions
.global
.showNotification
;
70 const getDefault
= (name
, defaultValue
) => {
71 const val
= window
.localStorage
.getItem('defaults-' + name
);
75 return setDefault(name
, 0);
80 setDefault(name
, defaultValue
);
84 const setDefault
= (name
, defaultValue
) => {
85 window
.localStorage
.setItem('defaults-' + name
, defaultValue
);
89 /* the top and bottom positions are managed by css; requires div to be display: absolute*/
91 left
: getDefault('catalog-panel-start-width', 300),
92 right
: getDefault('details-panel-start-width', 365),
93 bottom
: 25 + getDefault('defaults-forwarding-graphs-panel-start-height', 0),
95 zoom
: getDefault('zoom', 100),
96 filterCatalogBy
: 'nsd',
97 defaultPanelTrayOpenZoom
: (() => {
98 let zoom
= parseFloat(getDefault('panel-tray-zoom', 75));
102 zoom
= Math
.min(100, zoom
);
103 zoom
= Math
.max(25, zoom
);
104 setDefault('panel-tray-zoom', zoom
);
109 const autoZoomCanvasScale
= d3
.scale
.linear().domain([0, 300]).range([100, 50]).clamp(true);
111 const uiTransientState
= {};
113 class ComposerAppStore
{
116 //Bridge for crouton fix
117 this.ComponentBridgeElement
= SkyquakeComponent(ComponentBridge
);
119 this.exportAsync(FileManagerSource
)
120 // the catalog item currently being edited in the composer
122 // the left and right sides of the canvas area
125 right
: defaults
.right
,
126 bottom
: defaults
.bottom
128 uiTransientState
.restoreLayout
= this.layout
;
129 this.zoom
= defaults
.zoom
;
130 this.showMore
= defaults
.showMore
;
131 this.filterCatalogByTypeValue
= defaults
.filterCatalogBy
;
132 // transient ui state
135 this.messageType
= '';
136 this.showHelp
= { onFocus
: false, forAll
: true, forNothing
: false },
137 this.detailPanel
= { collapsed
: true, hidden
: false }
138 this.showJSONViewer
= false;
139 this.showClassifiers
= {};
140 this.editPathsMode
= false;
141 this.fullScreenMode
= false;
142 this.panelTabShown
= 'descriptor';
143 //File manager values
145 this.filesState
= {};
146 this.downloadJobs
= {};
147 this.containers
= [];
148 this.newPathName
= '';
149 this.displayedPanel
= 'forwarding'; //or parameter
150 //End File manager values
152 onResize
: PanelResizeAction
.RESIZE
,
153 editCatalogItem
: CatalogItemsActions
.EDIT_CATALOG_ITEM
,
154 catalogItemDescriptorChanged
: CatalogItemsActions
.CATALOG_ITEM_DESCRIPTOR_CHANGED
,
155 toggleShowMoreInfo
: CanvasEditorActions
.TOGGLE_SHOW_MORE_INFO
,
156 showMoreInfo
: CanvasEditorActions
.SHOW_MORE_INFO
,
157 showLessInfo
: CanvasEditorActions
.SHOW_LESS_INFO
,
158 applyDefaultLayout
: CanvasEditorActions
.APPLY_DEFAULT_LAYOUT
,
159 addVirtualLinkDescriptor
: CanvasEditorActions
.ADD_VIRTUAL_LINK_DESCRIPTOR
,
160 addForwardingGraphDescriptor
: CanvasEditorActions
.ADD_FORWARDING_GRAPH_DESCRIPTOR
,
161 addVirtualDeploymentDescriptor
: CanvasEditorActions
.ADD_VIRTUAL_DEPLOYMENT_DESCRIPTOR
,
162 selectModel
: ComposerAppActions
.SELECT_MODEL
,
163 outlineModel
: ComposerAppActions
.OUTLINE_MODEL
,
164 modelSaveError
: ComposerAppActions
.RECORD_DESCRIPTOR_ERROR
,
165 showError
: ComposerAppActions
.SHOW_ERROR
,
166 clearError
: ComposerAppActions
.CLEAR_ERROR
,
167 setDragState
: ComposerAppActions
.SET_DRAG_STATE
,
168 filterCatalogByType
: CatalogFilterActions
.FILTER_BY_TYPE
,
169 setCanvasZoom
: CanvasEditorActions
.SET_CANVAS_ZOOM
,
170 showJsonViewer
: ComposerAppActions
.SHOW_JSON_VIEWER
,
171 closeJsonViewer
: ComposerAppActions
.CLOSE_JSON_VIEWER
,
172 toggleCanvasPanelTray
: CanvasPanelTrayActions
.TOGGLE_OPEN_CLOSE
,
173 openCanvasPanelTray
: CanvasPanelTrayActions
.OPEN
,
174 closeCanvasPanelTray
: CanvasPanelTrayActions
.CLOSE
,
175 enterFullScreenMode
: ComposerAppActions
.ENTER_FULL_SCREEN_MODE
,
176 exitFullScreenMode
: ComposerAppActions
.EXIT_FULL_SCREEN_MODE
,
177 showAssets
: ComposerAppActions
.showAssets
,
178 showDescriptor
: ComposerAppActions
.showDescriptor
,
179 getFilelistSuccess
: FileManagerActions
.getFilelistSuccess
,
180 updateFileLocationInput
: FileManagerActions
.updateFileLocationInput
,
181 sendDownloadFileRequest
: FileManagerActions
.sendDownloadFileRequest
,
182 addFileSuccess
: FileManagerActions
.addFileSuccess
,
183 deletePackageFile
: FileManagerActions
.deletePackageFile
,
184 deleteFileSuccess
: FileManagerActions
.deleteFileSuccess
,
185 deleteFileError
: FileManagerActions
.deleteFileError
,
186 closeFileManagerSockets
: FileManagerActions
.closeFileManagerSockets
,
187 openFileManagerSockets
: FileManagerActions
.openFileManagerSockets
,
188 openDownloadMonitoringSocketSuccess
: FileManagerActions
.openDownloadMonitoringSocketSuccess
,
189 getFilelistSocketSuccess
: FileManagerActions
.getFilelistSocketSuccess
,
190 newPathNameUpdated
: FileManagerActions
.newPathNameUpdated
,
191 createDirectory
: FileManagerActions
.createDirectory
,
192 modelSetOpenState
: DescriptorEditorActions
.setOpenState
,
193 modelError
: DescriptorEditorActions
.setError
,
194 modelSet
: DescriptorEditorActions
.setValue
,
195 modelAssign
: DescriptorEditorActions
.assignValue
,
196 modelListNew
: DescriptorEditorActions
.addListItem
,
197 modelListRemove
: DescriptorEditorActions
.removeListItem
,
198 showHelpForNothing
: DescriptorEditorActions
.showHelpForNothing
,
199 showHelpForAll
: DescriptorEditorActions
.showHelpForAll
,
200 showHelpOnFocus
: DescriptorEditorActions
.showHelpOnFocus
,
201 collapseAllPanels
: DescriptorEditorActions
.collapseAllPanels
,
202 expandAllPanels
: DescriptorEditorActions
.expandAllPanels
,
203 modelForceOpenState
: DescriptorEditorActions
.expandPanel
,
204 showPanelsWithData
: DescriptorEditorActions
.showPanelsWithData
207 this.exportPublicMethods({
208 closeFileManagerSockets
: this.closeFileManagerSockets
.bind(this)
213 const layout
= Object
.assign({}, this.layout
);
214 if (e
.type
=== 'resize-manager.resize.catalog-panel') {
215 layout
.left
= Math
.max(0, layout
.left
- e
.moved
.x
);
216 if (layout
.left
!== this.layout
.left
) {
221 } else if (e
.type
=== 'resize-manager.resize.details-panel') {
222 layout
.right
= Math
.max(0, layout
.right
+ e
.moved
.x
);
223 if (layout
.right
!== this.layout
.right
) {
228 } else if (/^resize-manager\.resize\.canvas-panel-tray/.test(e
.type
)) {
229 layout
.bottom
= Math
.max(25, layout
.bottom
+ e
.moved
.y
);
230 if (layout
.bottom
!== this.layout
.bottom
) {
231 const zoom
= autoZoomCanvasScale(layout
.bottom
);
232 if (this.zoom
!== zoom
) {
243 } else if (e
.type
== 'resize') {
244 layout
.height
= e
.target
.innerHeight
;
249 console
.log('no resize handler for ', e
.type
, '. Do you need to add a handler in ComposerAppStore::onResize()?')
251 SelectionManager
.refreshOutline();
258 if (!document
.body
.classList
.contains('resizing')) {
259 containers
= [item
].reduce(DescriptorModelFactory
.buildCatalogItemFactory(CatalogDataStore
.getState().catalogs
), []);
261 containers
.filter(d
=> DescriptorModelFactory
.isConnectionPoint(d
)).forEach(d
=> {
262 d
.cpNumber
= ++cpNumber
;
263 containers
.filter(d
=> DescriptorModelFactory
.isVnfdConnectionPointRef(d
)).filter(ref
=> ref
.key
=== d
.key
).forEach(ref
=> ref
.cpNumber
= d
.cpNumber
);
266 containers
: containers
,
267 item
: _cloneDeep(item
)
270 SelectionManager
.refreshOutline();
273 editCatalogItem(item
) {
275 self
.closeFileManagerSockets();
276 if (item
&& item
.uiState
) {
277 item
.uiState
.isOpenForEdit
= true;
278 if (item
.uiState
.type
!== 'nsd') {
279 this.closeCanvasPanelTray();
282 SelectionManager
.select(item
);
283 this.updateItem(item
);
285 this.openFileManagerSockets(item
);
289 catalogItemDescriptorChanged(itemDescriptor
) {
290 this.updateItem(itemDescriptor
.model
);
293 setItemError(descriptor
, path
, message
) {
294 // save locally and globally
295 descriptor
.setUiState('error', path
, message
);
296 if (descriptor
.parent
) {
298 while (descriptor
.parent
) {
299 parentPath
.push(descriptor
.type
);
300 const index
= descriptor
.parent
.model
[descriptor
.type
].findIndex(item
=> item
.id
=== descriptor
.id
);
301 parentPath
.push(index
);
302 descriptor
= descriptor
.parent
;
304 parentPath
.length
&& descriptor
.setUiState('error', parentPath
.concat(path
), message
);
305 } else if (path
.length
> 1 && descriptor
[path
[0]]) {
306 // if we are indirectly editing a sub model set the error state in the sub model
307 descriptor
[path
[0]][path
[1]].setUiState('error', path
.slice(2), message
);
311 modelSaveError(input
) {
312 const { descriptor
, type
, id
} = input
;
313 const errorMessage
= {
314 'data-missing': "Required value.",
315 'missing-element': "Incomplete configuration."
317 if (descriptor
.id
=== id
) {
318 let error
= input
.error
;
320 if (typeof error
=== 'string') {
322 error
= JSON
.parse(error
);
327 rpcError
= error
['rcp-error']
328 || (error
['rcp-reply'] && error
['rcp-reply']['rcp-error'])
329 || (error
.body
&& error
.body
['rpc-reply'] && error
.body
['rpc-reply']['rpc-error']);
331 const errorTag
= rpcError
['error-tag'];
332 const message
= errorMessage
[errorTag
] || errorTag
;
333 let errPath
= rpcError
['error-path'].trim();
334 errPath
= errPath
.replace(/[-\w]*:/g, '');
335 errPath
= errPath
.slice(errPath
.indexOf('/' + type
+ '[') + 1);
337 let splitIndex
= errPath
.indexOf('/');
338 function ripOutNodeExpression(str
, complexNode
) {
339 const expressionEnd
= str
.indexOf(']');
340 complexNode
.push(str
.slice(1, expressionEnd
));
341 if (str
.charAt(expressionEnd
+ 1) === '[') {
342 str
= str
.slice(expressionEnd
+ 1);
343 return ripOutNodeExpression(str
, complexNode
)
345 return str
.slice(expressionEnd
+ 2);
347 while (splitIndex
> -1) {
348 const expressionStart
= errPath
.indexOf('[');
349 if (expressionStart
> 0 && expressionStart
< splitIndex
) {
350 const complexNode
= [];
351 complexNode
.push(errPath
.slice(0, expressionStart
));
352 errPath
= errPath
.slice(expressionStart
);
353 errPath
= ripOutNodeExpression(errPath
, complexNode
);
354 path
.push(complexNode
);
356 path
.push(errPath
.slice(0, splitIndex
))
357 errPath
= errPath
.slice(splitIndex
+ 1);
359 splitIndex
= errPath
.indexOf('/');
361 const expressionStart
= errPath
.indexOf('[');
362 if (expressionStart
> 0) {
363 const complexNode
= [];
364 complexNode
.push(errPath
.slice(0, expressionStart
));
365 errPath
= errPath
.slice(expressionStart
);
366 errPath
= ripOutNodeExpression(errPath
, complexNode
);
368 path
.push(errPath
.slice(0))
370 let model
= descriptor
.model
;
372 let fullPath
= path
.reduce((a
, p
, i
) => {
375 if (Array
.isArray(p
)) {
380 model
= model
[element
];
381 const match
= subPath
.reduce((m
, e
) => {
382 const id
= e
.split('=');
385 value
= value
.charAt(0) === "'" ? value
.split("'")[1] : value
;
386 m
.push({ key
, value
});
390 const index
= model
.findIndex(obj
=> match
.every(e
=> obj
[e
.key
] == e
.value
));
392 model
= model
[index
];
396 this.setItemError(descriptor
, fullPath
, message
);
397 this.updateItem(descriptor
.getRoot().model
);
403 const { descriptor
, path
, message
} = input
;
404 this.setItemError(descriptor
, path
, message
);
405 this.updateItem(descriptor
.getRoot().model
)
409 const { descriptor
, path
, value
} = input
;
410 _set(descriptor
.model
, path
, value
);
411 this.setItemError(descriptor
, path
, null);
412 this.updateItem(descriptor
.getRoot().model
)
413 CatalogItemsActions
.catalogItemDescriptorChanged
.defer(descriptor
.getRoot());
416 modelSetOpenState(input
) {
417 const { descriptor
, path
, isOpen
} = input
;
418 descriptor
.setUiState('opened', path
, isOpen
);
419 this.updateItem(descriptor
.getRoot().model
)
422 modelForceOpenState(input
) {
423 const { descriptor
, path
} = input
;
425 const targetPath
= _isArray(path
) ? path
: [path
];
426 targetPath
.forEach(p
=> {
428 descriptor
.setUiState('opened', openPath
, true);
430 this.updateItem(descriptor
.getRoot().model
)
434 const { descriptor
, path
, source
} = input
;
435 let obj
= _get(descriptor
.model
, path
) || {};
436 Object
.assign(obj
, source
);
437 this.updateItem(descriptor
.getRoot().model
)
438 CatalogItemsActions
.catalogItemDescriptorChanged
.defer(descriptor
.getRoot());
441 modelListNew(input
) {
442 const { descriptor
, path
, property
} = input
;
443 const create
= Property
.getContainerCreateMethod(property
, descriptor
);
446 create(model
, path
, property
);
448 // get a unique name for the new list item based on the current list content
449 // some lists, based on the key, may not get a uniqueName generated here
450 const uniqueName
= DescriptorModelMetaFactory
.generateItemUniqueName(descriptor
.model
[property
.name
], property
);
451 const value
= Property
.createModelInstance(property
, uniqueName
);
452 let list
= _get(descriptor
.model
, path
) || [];
454 _set(descriptor
.model
, path
, list
);
456 const list
= _get(descriptor
.model
, path
);
457 let openPath
= path
.slice();
458 descriptor
.setUiState('opened', openPath
, true);
459 openPath
.push((list
.length
- 1).toString());
460 descriptor
.setUiState('opened', openPath
, true);
461 function traverseProps(properties
) {
462 properties
.forEach((p
) => {
463 if (p
.type
=== 'list' || p
.type
=== 'container') {
464 openPath
.push(p
.name
);
465 descriptor
.setUiState('opened', openPath
, true);
466 p
.type
=== 'container' && traverseProps(p
.properties
);
471 traverseProps(property
.properties
);
472 this.updateItem(descriptor
.getRoot().model
);
473 CatalogItemsActions
.catalogItemDescriptorChanged
.defer(descriptor
.getRoot());
476 modelListRemove(input
) {
477 const { descriptor
, path
, property
} = input
;
478 const removeMethod
= Property
.getContainerMethod(property
, descriptor
, 'remove');
480 removeMethod(_get(descriptor
.model
, path
));
482 const index
= path
.pop();
483 let list
= _get(descriptor
.model
, path
);
484 list
= list
.splice(index
, 1);
486 this.updateItem(descriptor
.getRoot().model
);
487 CatalogItemsActions
.catalogItemDescriptorChanged
.defer(descriptor
.getRoot());
490 showHelpForNothing() {
491 this.setState({ showHelp
: { onFocus
: false, forAll
: false, forNothing
: true } })
495 this.setState({ showHelp
: { onFocus
: false, forAll
: true, forNothing
: false } })
499 this.setState({ showHelp
: { onFocus
: true, forAll
: false, forNothing
: false } })
502 clearOpenPanelState(descriptor
) {
504 descriptor
.setUiState('opened', [], {});
505 this.updateItem(descriptor
.getRoot().model
);
508 collapseAllPanels(input
) {
509 this.setState({ openPanelsWithData
: false, collapsePanelsByDefault
: true });
510 this.clearOpenPanelState(input
.descriptor
);
513 expandAllPanels(input
) {
514 this.setState({ openPanelsWithData
: false, collapsePanelsByDefault
: false });
515 this.clearOpenPanelState(input
.descriptor
);
518 showPanelsWithData(input
) {
519 this.setState({ openPanelsWithData
: true, collapsePanelsByDefault
: true });
520 this.clearOpenPanelState(input
.descriptor
);
536 NotificationError
.defer({
537 msg
: data
.errorMessage
,
539 rpcError
: data
.rpcError
541 // this.setState({message: data.errorMessage, messageType: 'error'});
551 toggleShowMoreInfo() {
553 showMore
: !this.showMore
557 applyDefaultLayout() {
558 if (this.item
&& this.item
.uiState
&& this.item
.uiState
.containerPositionMap
) {
559 if (!_isEmpty(this.item
.uiState
.containerPositionMap
)) {
560 this.item
.uiState
.containerPositionMap
= {};
561 this.updateItem(this.item
);
562 CatalogItemsActions
.catalogItemMetaDataChanged
.defer(this.item
);
567 updateItemNotifyLetSettleCheckMetaData(descriptor
) {
568 this.updateItem(descriptor
.model
);
569 CatalogItemsActions
.catalogItemDescriptorChanged
.defer(descriptor
);
570 setTimeout(() => CatalogItemsActions
.catalogItemMetaDataChanged(this.item
), 100);
573 addVirtualLinkDescriptor(dropCoordinates
= null) {
576 if (this.item
.uiState
.type
=== 'nsd') {
577 const nsdc
= DescriptorModelFactory
.newNetworkService(this.item
);
578 vld
= nsdc
.createVld();
579 } else if (this.item
.uiState
.type
=== 'vnfd') {
580 const vnfd
= DescriptorModelFactory
.newVirtualNetworkFunction(this.item
);
581 vld
= vnfd
.createVld();
584 vld
.uiState
.dropCoordinates
= dropCoordinates
;
585 SelectionManager
.clearSelectionAndRemoveOutline();
586 SelectionManager
.addSelection(vld
);
587 this.updateItemNotifyLetSettleCheckMetaData(vld
.getRoot());
592 addForwardingGraphDescriptor(dropCoordinates
= null) {
593 if (this.item
&& this.item
.uiState
.type
=== 'nsd') {
594 const nsdc
= DescriptorModelFactory
.newNetworkService(this.item
);
595 const fg
= nsdc
.createVnffgd();
596 fg
.uiState
.dropCoordinates
= dropCoordinates
;
597 SelectionManager
.clearSelectionAndRemoveOutline();
598 SelectionManager
.addSelection(fg
);
599 this.updateItemNotifyLetSettleCheckMetaData(nsdc
);
603 addVirtualDeploymentDescriptor(dropCoordinates
= null) {
604 if (this.item
.uiState
.type
=== 'vnfd') {
605 const vnfd
= DescriptorModelFactory
.newVirtualNetworkFunction(this.item
);
606 const vdu
= vnfd
.createVdu();
607 vdu
.uiState
.dropCoordinates
= dropCoordinates
;
608 SelectionManager
.clearSelectionAndRemoveOutline();
609 SelectionManager
.addSelection(vdu
);
610 this.updateItemNotifyLetSettleCheckMetaData(vdu
.getRoot());
614 selectModel(container
) {
615 if (SelectionManager
.select(container
)) {
616 const model
= DescriptorModelFactory
.isContainer(container
) ? container
.getRoot().model
: container
;
617 this.updateItem(model
);
622 const uid
= UID
.from(obj
);
623 requestAnimationFrame(() => {
624 SelectionManager
.outline(Array
.from(document
.querySelectorAll(`[data-uid="${uid}"]`)));
629 SelectionManager
.clearSelectionAndRemoveOutline();
632 setDragState(dragState
) {
638 filterCatalogByType(typeValue
) {
640 filterCatalogByTypeValue
: typeValue
644 setCanvasZoom(zoom
) {
658 showJSONViewer
: false
662 toggleCanvasPanelTray(event
) {
663 const layout
= this.layout
;
664 const attrMap
= event
.target
.attributes
;
665 let panelEvent
= null;
666 for (let k
in attrMap
) {
667 if (attrMap
[k
].name
== 'data-event') {
668 panelEvent
= attrMap
[k
].nodeValue
;
671 if ((layout
.bottom
> 25) && ((panelEvent
== this.displayedPanel
) || panelEvent
== 'arrow')) {
672 this.closeCanvasPanelTray();
674 this.openCanvasPanelTray();
676 if (panelEvent
!= 'arrow') {
678 displayedPanel
: panelEvent
683 openCanvasPanelTray() {
685 left
: this.layout
.left
,
686 right
: this.layout
.right
,
689 const zoom
= defaults
.defaultPanelTrayOpenZoom
;
690 if (this.zoom
!== zoom
) {
694 restoreZoom
: this.zoom
703 closeCanvasPanelTray() {
705 left
: this.layout
.left
,
706 right
: this.layout
.right
,
709 const zoom
= this.restoreZoom
|| autoZoomCanvasScale(layout
.bottom
);
710 if (this.zoom
!== zoom
) {
724 enterFullScreenMode() {
727 * https://developer.mozilla.org/en-US/docs/Web/API/Fullscreen_API
728 * This is an experimental api but works our target browsers and ignored by others
730 const eventNames
= ['fullscreenchange', 'mozfullscreenchange', 'webkitfullscreenchange', 'msfullscreenchange'];
732 const appRoot
= document
.body
; //.getElementById('RIFT_wareLaunchpadComposerAppRoot');
736 function onFullScreenChange() {
738 if (isFullScreen()) {
739 const layout
= comp
.layout
;
740 const restoreLayout
= _cloneDeep(layout
);
741 uiTransientState
.restoreLayout
= restoreLayout
;
745 fullScreenMode
: true,
747 restoreLayout
: restoreLayout
751 fullScreenMode
: false,
752 layout
: uiTransientState
.restoreLayout
758 if (this.fullScreenMode
=== false) {
760 if (appRoot
.requestFullscreen
) {
761 appRoot
.requestFullscreen();
762 } else if (appRoot
.msRequestFullscreen
) {
763 appRoot
.msRequestFullscreen();
764 } else if (appRoot
.mozRequestFullScreen
) {
765 appRoot
.mozRequestFullScreen();
766 } else if (appRoot
.webkitRequestFullscreen
) {
767 appRoot
.webkitRequestFullscreen(Element
.ALLOW_KEYBOARD_INPUT
);
770 eventNames
.map(name
=> {
771 document
.removeEventListener(name
, onFullScreenChange
);
772 document
.addEventListener(name
, onFullScreenChange
);
779 exitFullScreenMode() {
781 if (document
.exitFullscreen
) {
782 document
.exitFullscreen();
783 } else if (document
.msExitFullscreen
) {
784 document
.msExitFullscreen();
785 } else if (document
.mozCancelFullScreen
) {
786 document
.mozCancelFullScreen();
787 } else if (document
.webkitExitFullscreen
) {
788 document
.webkitExitFullscreen();
792 fullScreenMode
: false
798 panelTabShown
: 'assets'
803 panelTabShown
: 'descriptor'
807 //File Manager methods
808 getFilelistSuccess(data
) {
810 let filesState
= null;
811 if (self
.fileMonitoringSocketID
) {
813 if (data
.hasOwnProperty('contents')) {
814 filesState
= updateFileState(_cloneDeep(this.filesState
), data
);
815 let normalizedData
= normalizeTree(data
);
818 data
: _mergeWith(normalizedData
.data
, self
.files
.data
, function (obj
, src
) {
819 return _uniqBy(obj
? obj
.concat(src
) : src
, 'name');
821 id
: normalizedData
.id
823 filesState
: filesState
830 if (!_isEqual(newState
.files
, this.files
) || !_isEqual(newState
.fileState
, this.fileState
)) {
831 this.setState(newState
);
836 function normalizeTree(data
) {
842 function getContents(d
) {
843 if (d
.hasOwnProperty('contents')) {
845 d
.contents
.map(function (c
, i
) {
846 if (!c
.hasOwnProperty('contents')) {
853 f
.data
[d
.name
] = contents
;
860 function updateFileState(obj
, d
) {
862 if (d
.hasOwnProperty('contents')) {
863 d
.contents
.map(updateFileState
.bind(null, obj
))
865 // override any "pending" state we may have initialized
870 sendDownloadFileRequest(data
) {
871 let id
= data
.id
|| this.item
.id
;
872 let type
= data
.type
|| this.item
.uiState
.type
;
873 let assetType
= data
.assetType
;
874 let path
= data
.path
;
876 this.getInstance().addFile(id
, type
, assetType
, path
, url
, data
.refresh
);
878 updateFileLocationInput
= (data
) => {
879 let name
= data
.name
;
880 let value
= data
.value
;
881 var filesState
= _cloneDeep(this.filesState
);
882 filesState
[name
] = value
;
884 filesState
: filesState
887 addFileSuccess
= (data
) => {
889 let path
= data
.path
;
890 if (path
.startsWith('readme')) {
891 // this asset type stuff should be in a more common location
892 // this is a wee bit of a hack till it is
893 path
= '.' + path
.slice(6);
895 let fileName
= data
.fileName
;
896 let files
= _cloneDeep(this.files
);
897 let assetGroup
= files
.data
[path
] || [];
899 let name
= path
+ '/' + fileName
;
900 if (assetGroup
.findIndex(f
=> f
.name
=== name
) == -1) {
906 files
.data
[path
] = assetGroup
;
907 if (files
.id
.indexOf(path
) == -1) {
910 let filesState
= _cloneDeep(this.filesState
);
911 filesState
[name
] = "DOWNLOADING";
919 startWatchingJob
= () => {
920 let ws
= window
.multiplexer
.channel(this.jobSocketId
);
925 openDownloadMonitoringSocketSuccess
= (id
) => {
927 let ws
= window
.multiplexer
.channel(id
);
928 let downloadJobs
= _cloneDeep(self
.downloadJobs
);
929 let newFiles
= false;
930 ws
.onmessage
= (socket
) => {
931 if (self
.files
&& self
.files
.length
> 0) {
934 jobs
= JSON
.parse(socket
.data
);
936 newFiles
= _cloneDeep(self
.files
);
937 jobs
.map(function (j
) {
938 //check if not in completed state
939 let fullPath
= j
['package-path'];
940 let path
= fullPath
.split('/');
941 let fileName
= path
.pop();
942 path
= path
.join('/');
943 let index
= _findIndex(self
.files
.data
[path
], function (o
) {
944 return fullPath
== o
.name
946 if ((index
> -1) && newFiles
.data
[path
][index
]) {
947 newFiles
.data
[path
][index
].status
= j
.status
949 if (j
.status
.toUpperCase() == 'LOADING...' || j
.status
.toUpperCase() == 'IN_PROGRESS') {
950 newFiles
.data
[path
].push({
962 // console.log(JSON.parse(socket.data));
971 getFilelistSocketSuccess
= (id
) => {
973 let ws
= window
.multiplexer
.channel(id
);
974 ws
.onmessage
= (socket
) => {
975 if (self
.fileMonitoringSocketID
) {
978 data
= JSON
.parse(socket
.data
);
980 self
.getFilelistSuccess(data
)
990 fileMonitoringSocketID
: id
,
991 fileMonitoringSocket
: ws
995 closeFileManagerSockets() {
996 this.fileMonitoringSocketID
= null;
999 fileMonitoringSocketID
: null
1000 // jobSocket : null,
1001 // fileMonitoringSocket : null,
1003 this.jobSocket
&& this.jobSocket
.close();
1004 this.fileMonitoringSocket
&& this.fileMonitoringSocket
.close();
1005 console
.log('closing');
1007 openFileManagerSockets(i
) {
1009 let item
= i
|| self
.item
;
1010 // this.closeFileManagerSockets();
1011 this.getInstance().openFileMonitoringSocket(item
.id
, item
.uiState
.type
).then(function () {
1012 // // self.getInstance().openDownloadMonitoringSocket(item.id);
1014 this.getInstance().openDownloadMonitoringSocket(item
.id
);
1016 endWatchingJob(id
) {
1019 deletePackageFile(asset
) {
1024 let id
= this.item
.id
;
1025 let type
= this.item
.uiState
.type
;
1026 this.getInstance().deleteFile(id
, type
, assetType
, path
);
1028 deleteFileSuccess
= (data
) => {
1031 if (data
.assetFolder
=== 'readme') {
1032 // freak'n root folder is special
1036 name
= data
.assetFolder
+ '/' + data
.path
;
1037 path
= name
.split('/');
1040 let files
= _cloneDeep(this.files
);
1041 let filesForPath
= files
.data
[path
.join('/')]
1042 _remove(filesForPath
, function (c
) {
1043 return c
.name
== name
;
1050 deleteFileError
= (error
) => {
1051 const filepath
= error
.path
;
1052 const message
= error
.data
&& error
.data
.output
? ' (' + error
.data
.output
['error-trace'] + ')' : ' (server error)';
1053 console
.log('Unable to delete', filepath
, 'Error:', message
);
1054 ComposerAppActions
.showError
.defer({
1055 errorMessage
: 'Unable to delete ' + filepath
+ message
+ '. '
1059 newPathNameUpdated
= (event
) => {
1060 const value
= event
.target
.value
;
1065 createDirectory
= (assetType
) => {
1066 console
.log(this.newPathName
);
1067 this.sendDownloadFileRequest({
1069 type
: this.item
.uiState
.type
,
1070 assetType
: assetType
,
1071 path
: this.newPathName
,
1072 url
: utils
.getSearchParams(window
.location
).dev_download_server
|| window
.location
.protocol
+ '//' + window
.location
.host
,
1081 export default alt
.createStore(ComposerAppStore
, 'ComposerAppStore');