Merge v1.1 into master_vca_intg

Signed-off-by: Laurence Maultsby <laurence.maultsby@riftio.com>
diff --git a/skyquake/plugins/composer/src/src/components/ComposerApp.js b/skyquake/plugins/composer/src/src/components/ComposerApp.js
index d626ea4..6507b34 100644
--- a/skyquake/plugins/composer/src/src/components/ComposerApp.js
+++ b/skyquake/plugins/composer/src/src/components/ComposerApp.js
@@ -62,218 +62,210 @@
 
 
 const ComposerApp = React.createClass({
-    mixins: [PureRenderMixin],
-    getInitialState() {
-        return ComposerAppStore.getState();
-    },
-    getDefaultProps() {
-        return {};
-    },
-    componentWillMount() {
-        if (clearLocalStorage) {
-            window.localStorage.clear();
-        }
+	mixins: [PureRenderMixin],
+	getInitialState() {
+		return ComposerAppStore.getState();
+	},
+	getDefaultProps() {
+		return {};
+	},
+	componentWillMount() {
+		if (clearLocalStorage) {
+			window.localStorage.clear();
+		}
         if(this.item) {
             FileManagerActions.openFileManagerSockets();
         }
-        this.state.isLoading = CatalogDataStore.getState().isLoading;
-        ComposerAppStore.listen(this.onChange);
-        CatalogDataStore.listen(this.onCatalogDataChanged);
-        window.addEventListener('resize', this.resize);
-        window.onbeforeunload = this.onBeforeUnload;
-        // prevent browser from downloading any drop outside of our specific drop zones
-        window.addEventListener('dragover', preventDefault);
-        window.addEventListener('drop', preventDefault);
-        // ensure drags initiated in the app clear the state on drop
-        window.addEventListener('drop', clearDragState);
-        DeletionManager.addEventListeners();
-    },
-    componentWillUnmount() {
-        window.removeEventListener('resize', this.resize);
-        window.removeEventListener('dragover', preventDefault);
-        window.removeEventListener('drop', preventDefault);
-        window.removeEventListener('drop', clearDragState);
+		this.state.isLoading = CatalogDataStore.getState().isLoading;
+		ComposerAppStore.listen(this.onChange);
+		CatalogDataStore.listen(this.onCatalogDataChanged);
+		window.addEventListener('resize', this.resize);
+		window.onbeforeunload = this.onBeforeUnload;
+		// prevent browser from downloading any drop outside of our specific drop zones
+		window.addEventListener('dragover', preventDefault);
+		window.addEventListener('drop', preventDefault);
+		// ensure drags initiated in the app clear the state on drop
+		window.addEventListener('drop', clearDragState);
+		DeletionManager.addEventListeners();
+	},
+	componentWillUnmount() {
+		window.removeEventListener('resize', this.resize);
+		window.removeEventListener('dragover', preventDefault);
+		window.removeEventListener('drop', preventDefault);
+		window.removeEventListener('drop', clearDragState);
         FileManagerActions.closeFileManagerSockets();
-        // resizeManager automatically registered its event handlers
-        resizeManager.removeAllEventListeners();
-        ComposerAppStore.unlisten(this.onChange);
-        CatalogDataStore.unlisten(this.onCatalogDataChanged);
-        DeletionManager.removeEventListeners();
-        TooltipManager.removeEventListeners();
-    },
-    componentDidMount() {
-        resizeManager.addAllEventListeners();
-        const snapshot = window.localStorage.getItem('composer');
-        if (snapshot) {
-            alt.bootstrap(snapshot);
-        }
-        document.body.addEventListener('keydown', (event) => {
-            // prevent details editor form from blowing up the app
-            const ENTER_KEY = 13;
-            if (event.which === ENTER_KEY) {
-                event.preventDefault();
-                return false;
-            }
-        });
-        const appRootElement = ReactDOM.findDOMNode(this.refs.appRoot);
-        TooltipManager.addEventListeners(appRootElement);
-        SelectionManager.onClearSelection = () => {
-            if (this.state.item) {
-                CatalogItemsActions.catalogItemMetaDataChanged.defer(this.state.item);
-            }
-        };
-    },
-    componentDidUpdate() {
-        if (this.state.fullScreenMode) {
-            document.body.classList.add('-is-full-screen');
-        } else {
-            document.body.classList.remove('-is-full-screen');
-        }
-        SelectionManager.refreshOutline();
-    },
-    resize(e) {
-        PanelResizeAction.resize(e);
-    },
-    getModel() {
-        let html;
-        let self = this;
-        DescriptorModelMetaFactory.init().then(function(){
+		// resizeManager automatically registered its event handlers
+		resizeManager.removeAllEventListeners();
+		ComposerAppStore.unlisten(this.onChange);
+		CatalogDataStore.unlisten(this.onCatalogDataChanged);
+		DeletionManager.removeEventListeners();
+		TooltipManager.removeEventListeners();
+	},
+	componentDidMount() {
+		resizeManager.addAllEventListeners();
+		const snapshot = window.localStorage.getItem('composer');
+		if (snapshot) {
+			alt.bootstrap(snapshot);
+		}
+		document.body.addEventListener('keydown', (event) => {
+			// prevent details editor form from blowing up the app
+			const ENTER_KEY = 13;
+			if (event.which === ENTER_KEY) {
+				event.preventDefault();
+				return false;
+			}
+		});
+		const appRootElement = ReactDOM.findDOMNode(this.refs.appRoot);
+		TooltipManager.addEventListeners(appRootElement);
+		SelectionManager.onClearSelection = () => {
+			if (this.state.item) {
+				CatalogItemsActions.catalogItemMetaDataChanged.defer(this.state.item);
+			}
+		};
+	},
+	componentDidUpdate() {
+		if (this.state.fullScreenMode) {
+			document.body.classList.add('-is-full-screen');
+		} else {
+			document.body.classList.remove('-is-full-screen');
+		}
+		SelectionManager.refreshOutline();
+	},
+	resize(e) {
+		PanelResizeAction.resize(e);
+	},
+	getModel() {
+		let html;
+		let self = this;
+		DescriptorModelMetaFactory.init().then(function(){
 
-            self.setState({
-                hasModel: true
-            })
-        });
-    },
-    render() {
-        let html = null;
-        let self = this;
-        if(this.state.hasModel) {
+			self.setState({
+				hasModel: true
+			})
+		});
+	},
+	render() {
+		let html = null;
+		let self = this;
+		if(this.state.hasModel) {
 
-            function onClickUpdateSelection(event) {
-                if (event.defaultPrevented) {
-                    return
-                }
-                const element = SelectionManager.getClosestElementWithUID(event.target);
-                if (element) {
-                    SelectionManager.select(element);
-                    SelectionManager.refreshOutline();
-                    event.preventDefault();
-                } else {
-                    if(event.target.offsetParent && !event.target.offsetParent.classList.contains("tray-body")) {
-                        SelectionManager.clearSelectionAndRemoveOutline();
-                    }
-                }
-            }
+			function onClickUpdateSelection(event) {
+				if (event.defaultPrevented) {
+					return
+				}
+				const element = SelectionManager.getClosestElementWithUID(event.target);
+				if (element) {
+					SelectionManager.select(element);
+					SelectionManager.refreshOutline();
+					event.preventDefault();
+				} else {
+					SelectionManager.clearSelectionAndRemoveOutline();
+				}
+			}
 
-            let cpNumber = 0;
-            let AppHeader = (<div className="AppHeader">
-                                <RiftHeader />
-                            </div>);
-            // AppHeader = null;
-            const classNames = ClassNames('ComposerApp');
-            const isNew = self.state.item && self.state.item.uiState.isNew;
-            const hasItem = self.state.item && self.state.item.uiState;
-            const isModified = self.state.item && self.state.item.uiState.modified;
-            const isEditingNSD = self.state.item && self.state.item.uiState && /nsd/.test(self.state.item.uiState.type);
-            const isEditingVNFD = self.state.item && self.state.item.uiState && /vnfd/.test(self.state.item.uiState.type);
-            const containers = [self.state.item].reduce(DescriptorModelFactory.buildCatalogItemFactory(CatalogDataStore.getState().catalogs), []);
 
-            containers.filter(d => DescriptorModelFactory.isConnectionPoint(d)).forEach(d => {
-                d.cpNumber = ++cpNumber;
-                containers.filter(d => DescriptorModelFactory.isVnfdConnectionPointRef(d)).filter(ref => ref.key === d.key).forEach(ref => ref.cpNumber = d.cpNumber);
-            });
-            const canvasTitle = containers.length ? containers[0].model.name : '';
-            const hasNoCatalogs = CatalogDataStore.getState().catalogs.length === 0;
-            const isLoading = self.state.isLoading;
+			let AppHeader = (<div className="AppHeader">
+								<RiftHeader />
+							</div>);
+			// AppHeader = null;
+			const classNames = ClassNames('ComposerApp');
+			const isNew = self.state.item && self.state.item.uiState.isNew;
+			const hasItem = self.state.item && self.state.item.uiState;
+			const isModified = self.state.item && self.state.item.uiState.modified;
+			const isEditingNSD = self.state.item && self.state.item.uiState && /nsd/.test(self.state.item.uiState.type);
+			const isEditingVNFD = self.state.item && self.state.item.uiState && /vnfd/.test(self.state.item.uiState.type);
+            const containers = self.state.containers;
+			const canvasTitle = containers.length ? containers[0].model.name : '';
+			const hasNoCatalogs = CatalogDataStore.getState().catalogs.length === 0;
+			const isLoading = self.state.isLoading;
 
             //Bridge element for Crouton fix. Should eventually put Composer on same flux context
             const Bridge = this.state.ComponentBridgeElement;
 
-            html = (
-                <div ref="appRoot" id="RIFT_wareLaunchpadComposerAppRoot" className="AppRoot" onClick={onClickUpdateSelection}>
+			html = (
+				<div ref="appRoot" id="RIFT_wareLaunchpadComposerAppRoot" className="AppRoot" onClick={onClickUpdateSelection}>
                     <Bridge />
-                    <i className="corner-accent top left" />
-                    <i className="corner-accent top right" />
-                    <i className="corner-accent bottom left" />
-                    <i className="corner-accent bottom right" />
-                    {AppHeader}
-                    <div className="AppBody">
-                        <div className={classNames}>
-                            <CatalogPanel layout={self.state.layout}
-                                          isLoading={isLoading}
-                                          hasNoCatalogs={hasNoCatalogs}
-                                          filterByType={self.state.filterCatalogByTypeValue} />
-                            <CanvasPanel layout={self.state.layout}
-                                         hasNoCatalogs={hasNoCatalogs}
-                                         showMore={self.state.showMore}
-                                         containers={containers}
-                                         title={canvasTitle}
-                                         zoom={self.state.zoom}
-                                         panelTabShown={self.state.panelTabShown}
-                                         files={self.state.files}
-                                         filesState={self.state.filesState}
-                                         item={self.state.item}
-                                         type={self.state.filterCatalogByTypeValue}
-                                         displayedPanel={self.state.displayedPanel}
-                                          />
-                            {
-                                (self.state.panelTabShown == 'descriptor') ?
-                                <DetailsPanel layout={self.state.layout}
-                                          hasNoCatalogs={hasNoCatalogs}
-                                          showMore={self.state.showMore}
-                                          containers={containers}
-                                          showJSONViewer={self.state.showJSONViewer} />
-                              : null
-                            }
+					<i className="corner-accent top left" />
+					<i className="corner-accent top right" />
+					<i className="corner-accent bottom left" />
+					<i className="corner-accent bottom right" />
+					{AppHeader}
+					<div className="AppBody">
+						<div className={classNames}>
+							<CatalogPanel layout={self.state.layout}
+										  isLoading={isLoading}
+										  hasNoCatalogs={hasNoCatalogs}
+										  filterByType={self.state.filterCatalogByTypeValue} />
+							<CanvasPanel layout={self.state.layout}
+										 hasNoCatalogs={hasNoCatalogs}
+										 showMore={self.state.showMore}
+										 containers={containers}
+										 title={canvasTitle}
+										 zoom={self.state.zoom}
+										 panelTabShown={self.state.panelTabShown}
+										 files={self.state.files}
+										 filesState={self.state.filesState}
+										 item={self.state.item}
+										 type={self.state.filterCatalogByTypeValue}
+										  />
+						 	{
+ 						 		(self.state.panelTabShown == 'descriptor') ?
+ 						 		<DetailsPanel layout={self.state.layout}
+										  hasNoCatalogs={hasNoCatalogs}
+										  showMore={self.state.showMore}
+										  containers={containers}
+										  showJSONViewer={self.state.showJSONViewer} />
+							  : null
+ 						 	}
 
-                            <ComposerAppToolbar layout={self.state.layout}
-                                                showMore={self.state.showMore}
-                                                isEditingNSD={isEditingNSD}
-                                                isEditingVNFD={isEditingVNFD}
-                                                isModified={isModified}
-                                                isNew={isNew}
-                                                disabled={!hasItem}
-                                                onClick={event => event.stopPropagation()}
-                                                panelTabShown={self.state.panelTabShown}/>
-                        </div>
-                    </div>
-                    <ModalOverlay />
-                </div>
-            );
-        } else {
-            this.getModel();
-        }
-        return html;
-    },
-    onChange(state) {
-        this.setState(state);
-    },
-    onCatalogDataChanged(catalogDataState) {
-        const catalogs = catalogDataState.catalogs;
-        const unsavedChanges = catalogs.reduce((result, catalog) => {
-            if (result) {
-                return result;
-            }
-            return catalog.descriptors.reduce((result, descriptor) => {
-                if (result) {
-                    return result;
-                }
-                return descriptor.uiState.modified;
-            }, false);
-        }, false);
-        this.setState({
-            unsavedChanges: unsavedChanges,
-            isLoading: catalogDataState.isLoading
-        });
-    },
-    onBeforeUnload() {
-        // https://trello.com/c/c8v321Xx/160-prompt-user-to-save-changes
-        //const snapshot = alt.takeSnapshot();
-        //window.localStorage.setItem('composer', snapshot);
-        if (this.state.unsavedChanges) {
-            return 'You have unsaved changes. If you do not onboard (or update) your changes they will be lost.';
-        }
-    }
+							<ComposerAppToolbar layout={self.state.layout}
+												showMore={self.state.showMore}
+												isEditingNSD={isEditingNSD}
+												isEditingVNFD={isEditingVNFD}
+												isModified={isModified}
+												isNew={isNew}
+												disabled={!hasItem}
+												onClick={event => event.stopPropagation()}
+												panelTabShown={self.state.panelTabShown}/>
+						</div>
+					</div>
+					<ModalOverlay />
+				</div>
+			);
+		} else {
+			this.getModel();
+		}
+		return html;
+	},
+	onChange(state) {
+		this.setState(state);
+	},
+	onCatalogDataChanged(catalogDataState) {
+		const catalogs = catalogDataState.catalogs;
+		const unsavedChanges = catalogs.reduce((result, catalog) => {
+			if (result) {
+				return result;
+			}
+			return catalog.descriptors.reduce((result, descriptor) => {
+				if (result) {
+					return result;
+				}
+				return descriptor.uiState.modified;
+			}, false);
+		}, false);
+		this.setState({
+			unsavedChanges: unsavedChanges,
+			isLoading: catalogDataState.isLoading
+		});
+	},
+	onBeforeUnload() {
+		// https://trello.com/c/c8v321Xx/160-prompt-user-to-save-changes
+		//const snapshot = alt.takeSnapshot();
+		//window.localStorage.setItem('composer', snapshot);
+		if (this.state.unsavedChanges) {
+			return 'You have unsaved changes. If you do not onboard (or update) your changes they will be lost.';
+		}
+	}
 
 });
 
diff --git a/skyquake/plugins/composer/src/src/stores/ComposerAppStore.js b/skyquake/plugins/composer/src/src/stores/ComposerAppStore.js
index 156fe76..75276d2 100644
--- a/skyquake/plugins/composer/src/src/stores/ComposerAppStore.js
+++ b/skyquake/plugins/composer/src/src/stores/ComposerAppStore.js
@@ -125,7 +125,7 @@
 		this.files = false;
 		this.filesState = {};
 		this.downloadJobs = {};
-		this.displayedPanel = 'forwarding' //or parameter
+		this.containers = [];
 		//End File  manager values
 		this.bindListeners({
 			onResize: PanelResizeAction.RESIZE,
@@ -202,8 +202,17 @@
 	}
 
 	updateItem(item) {
+		const self = this;
+		let containers = [];
+		let cpNumber = 0;
 		if(!document.body.classList.contains('resizing')) {
-			this.setState({item: _.cloneDeep(item)});
+			containers = [item].reduce(DescriptorModelFactory.buildCatalogItemFactory(CatalogDataStore.getState().catalogs), []);
+
+			containers.filter(d => DescriptorModelFactory.isConnectionPoint(d)).forEach(d => {
+				d.cpNumber = ++cpNumber;
+				containers.filter(d => DescriptorModelFactory.isVnfdConnectionPointRef(d)).filter(ref => ref.key === d.key).forEach(ref => ref.cpNumber = d.cpNumber);
+			});
+			this.setState({containers: containers, item: _.cloneDeep(item)});
 		}
 		SelectionManager.refreshOutline();
 	}
@@ -342,23 +351,13 @@
 		this.setState({showJSONViewer: false});
 	}
 
-	toggleCanvasPanelTray(event) {
+	toggleCanvasPanelTray() {
 		const layout = this.layout;
-		const attrMap = event.target.attributes;
-		let panelEvent = null;
-		for(let k in attrMap) {
-			if(attrMap[k].name == 'data-event') {
-				panelEvent = attrMap[k].nodeValue;
-			}
-		}
-		if ((layout.bottom > 25) && ((panelEvent == this.displayedPanel) || panelEvent == 'arrow')) {
+		if (layout.bottom > 25) {
 			this.closeCanvasPanelTray();
 		} else {
 			this.openCanvasPanelTray();
 		}
-		if(panelEvent != 'arrow'){
-			this.setState({displayedPanel: panelEvent})
-		}
 	}
 
 	openCanvasPanelTray() {
diff --git a/skyquake/plugins/launchpad/src/recordViewer/recordViewStore.js b/skyquake/plugins/launchpad/src/recordViewer/recordViewStore.js
index b9f7499..a7770a7 100644
--- a/skyquake/plugins/launchpad/src/recordViewer/recordViewStore.js
+++ b/skyquake/plugins/launchpad/src/recordViewer/recordViewStore.js
@@ -136,7 +136,7 @@
         let vnfrIndex = data.vnfrIndex;
         let configPrimitiveIndex = data.configPrimitiveIndex;
         let payload = {};
-        let isValid = false;
+        let isValid = true;
         let configPrimitive = vnfrs[vnfrIndex]['vnf-configuration']['service-primitive'][configPrimitiveIndex];
 
         payload['name'] = '';
@@ -144,7 +144,7 @@
         payload['vnf-list'] = [];
         payload['triggered-by'] = 'vnf-primitive';
         let parameters = [];
-        configPrimitive['parameter'].map((parameter) => {
+        configPrimitive['parameter'] && configPrimitive['parameter'].map((parameter) => {
             if(!isValid) {
                 isValid = validateParameter(parameter);
             }
@@ -157,8 +157,11 @@
         let vnfPrimitive = [];
         vnfPrimitive[0] = {
             name: configPrimitive['name'],
-            index: configPrimitiveIndex,
-            parameter: parameters
+            index: configPrimitiveIndex
+        };
+
+        if (parameters.length > 0) {
+            vnfPrimitive[0].parameter = parameters;
         }
 
         payload['vnf-list'].push({