Merge remote-tracking branch 'origin/v1.0'

Signed-off-by: KIRAN KASHALKAR <kiran.kashalkar@riftio.com>
diff --git a/skyquake/framework/widgets/skyquake_container/skyquakeContainerStore.js b/skyquake/framework/widgets/skyquake_container/skyquakeContainerStore.js
index aa4e744..d1a8a9e 100644
--- a/skyquake/framework/widgets/skyquake_container/skyquakeContainerStore.js
+++ b/skyquake/framework/widgets/skyquake_container/skyquakeContainerStore.js
@@ -173,8 +173,8 @@
 
         } else {
             state.notificationMessage = data.msg;
-            if(data.type == 'success') {
-                state.notificationType = 'success';
+            if(data.type) {
+                state.notificationType = data.type;
             }
         }
         this.setState(state);
diff --git a/skyquake/plugins/accounts/src/account/account.jsx b/skyquake/plugins/accounts/src/account/account.jsx
index 45b31fc..37063ca 100644
--- a/skyquake/plugins/accounts/src/account/account.jsx
+++ b/skyquake/plugins/accounts/src/account/account.jsx
@@ -47,7 +47,6 @@
         }
     }
     componentWillUnmount() {
-        this.props.store.closeSocket();
         this.props.store.unlisten(this.storeListener);
     }
     setUp(props){
diff --git a/skyquake/plugins/composer/src/src/components/EditDescriptorModelProperties.js b/skyquake/plugins/composer/src/src/components/EditDescriptorModelProperties.js
index a2373e6..20c84da 100644
--- a/skyquake/plugins/composer/src/src/components/EditDescriptorModelProperties.js
+++ b/skyquake/plugins/composer/src/src/components/EditDescriptorModelProperties.js
@@ -245,9 +245,10 @@
 
 				event.preventDefault();
 
-				const name = event.target.name;
+				let name = event.target.name;
 				const value = event.target.value;
 
+
 				/*
 					Transient State is stored for convenience in the uiState field.
 					The choice yang type uses case elements to describe the "options".
@@ -255,22 +256,22 @@
 					the system to determine which type is selected by the name of
 					the element contained within the field.
 				 */
-
-				//const stateExample = {
-				//	uiState: {
-				//		choice: {
-				//			'conf-config': {
-				//				selected: 'rest',
-				//				'case': {
-				//					rest: {},
-				//					netconf: {},
-				//					script: {}
-				//				}
-				//			}
-				//		}
-				//	}
-				//};
-
+				/*
+					const stateExample = {
+						uiState: {
+							choice: {
+								'conf-config': {
+									selected: 'rest',
+									'case': {
+										rest: {},
+										netconf: {},
+										script: {}
+									}
+								}
+							}
+						}
+					};
+				*/
 				const statePath = ['uiState.choice'].concat(name);
 				const stateObject = utils.resolvePath(this.model, statePath.join('.')) || {};
 				const selected = stateObject.selected ? stateObject.selected.split('.')[1] : undefined;
@@ -278,19 +279,34 @@
 				utils.assignPathValue(this.model, statePath.join('.'), stateObject);
 
 				// write the current choice value into the state
-				const choiceObject = utils.resolvePath(this.model, [name, selected].join('.'));
-				if (choiceObject) {
-					utils.assignPathValue(stateObject, ['case', selected].join('.'), _.cloneDeep(choiceObject));
+				let choiceObject = utils.resolvePath(this.model, [name, selected].join('.'));
+				let isTopCase = false;
+				if (!choiceObject) {
+					isTopCase = true;
+					choiceObject = utils.resolvePath(this.model, [selected].join('.'));
+				}
+				utils.assignPathValue(stateObject, [selected].join('.'), _.cloneDeep(choiceObject));
+
+				if(this.model.uiState.choice.hasOwnProperty(name)) {
+					delete this.model[selected];
+					utils.removePathValue(this.model, [name, selected].join('.'), isTopCase);
+				} else {
+					// remove the current choice value from the model
+				utils.removePathValue(this.model, [name, selected].join('.'), isTopCase);
 				}
 
-				// remove the current choice value from the model
-				utils.removePathValue(this.model, [name, selected].join('.'));
+
 
 				// get any state for the new selected choice
-				const newChoiceObject = utils.resolvePath(stateObject, ['case', value].join('.')) || {};
+				const newChoiceObject = utils.resolvePath(stateObject, [value].join('.')) || {};
 
 				// assign new choice value to the model
-				utils.assignPathValue(this.model, [name, value].join('.'), newChoiceObject);
+				if (isTopCase) {
+					utils.assignPathValue(this.model, [name, value].join('.'), newChoiceObject);
+				} else {
+					utils.assignPathValue(this.model, [value].join('.'), newChoiceObject)
+				}
+
 
 				// update the selected name
 				utils.assignPathValue(this.model, statePath.concat('selected').join('.'), value);
@@ -342,6 +358,14 @@
 					}
 				});
 				selectedOptionValue = utils.resolvePath(container.model, ['uiState.choice', selectName, 'selected'].join('.'));
+			} else {
+				property.properties.map(function(p) {
+					let pname = p.properties[0].name;
+					if(container.model.hasOwnProperty(pname)) {
+						utils.assignPathValue(container.model, ['uiState.choice', selectName, 'selected'].join('.'), [p.name, pname].join('.'));
+					}
+				})
+				selectedOptionValue = utils.resolvePath(container.model, ['uiState.choice', selectName, 'selected'].join('.'));
 			}
 		}
 		//If selectedOptionValue is present, take first item in string which represents the case name.
@@ -358,7 +382,7 @@
 					{build(container, d, childPath, childValue, props)}
 				</div>
 			);
-		}) : (!isMissingDescriptorMeta) ? build(container, valueProperty, path.concat(valueProperty.name), utils.resolvePath(container.model, path.concat(valueProperty.name).join('.'))) : null
+		}) : (!isMissingDescriptorMeta) ? build(container, valueProperty, path.concat(valueProperty.name), utils.resolvePath(container.model, path.concat(valueProperty.name).join('.')) || container.model[valueProperty.name]) : null
 		// end magic
 		const onFocus = onFocusPropertyFormInputElement.bind(container, property, path, value);
 
@@ -510,6 +534,14 @@
 
 		if (property.type === 'choice') {
 			value = utils.resolvePath(container.model, ['uiState.choice'].concat(path, 'selected').join('.'));
+			if(!value) {
+				property.properties.map(function(p) {
+					let pname = p.properties[0].name;
+					if(container.model.hasOwnProperty(pname)) {
+						value = container.model[pname];
+					}
+				})
+			}
 		}
 
 		let displayValue = typeof value === 'object' ? '' : value;
diff --git a/skyquake/plugins/composer/src/src/libraries/model/DescriptorModelFields.js b/skyquake/plugins/composer/src/src/libraries/model/DescriptorModelFields.js
index 40228ee..bc55760 100644
--- a/skyquake/plugins/composer/src/src/libraries/model/DescriptorModelFields.js
+++ b/skyquake/plugins/composer/src/src/libraries/model/DescriptorModelFields.js
@@ -1,5 +1,5 @@
 /*
- * 
+ *
  *   Copyright 2016 RIFT.IO Inc
  *
  *   Licensed under the Apache License, Version 2.0 (the "License");
@@ -27,7 +27,7 @@
 	nsd: common.concat(['constituent-vnfd', 'vnffgd', 'vld']),
 	vld: common.concat([]),
 	vnfd: common.concat(['vdu', 'internal-vld']),
-	'vnfd.vdu': common.concat(['image', 'external-interface', 'vm-flavor', 'cloud-init']),
+	'vnfd.vdu': common.concat(['image', 'external-interface', 'vm-flavor', 'cloud-init', 'filename']),
 	// white-list valid fields to send in the meta field
 	meta: ['containerPositionMap']
 };
diff --git a/skyquake/plugins/composer/src/src/libraries/model/DescriptorModelMetaFactory.js b/skyquake/plugins/composer/src/src/libraries/model/DescriptorModelMetaFactory.js
index 258b894..d3ef200 100644
--- a/skyquake/plugins/composer/src/src/libraries/model/DescriptorModelMetaFactory.js
+++ b/skyquake/plugins/composer/src/src/libraries/model/DescriptorModelMetaFactory.js
@@ -82,7 +82,20 @@
 		// resolve paths like 'nsd' or 'vnfd.vdu' or 'nsd.constituent-vnfd'
 		const found = utils.resolvePath(modelMetaByPropertyNameMap, getPathForType(typeOrPath));
 		if (found) {
-			return found[':meta'].properties.map(p => p.name);
+			let result = [];
+			found[':meta'].properties.map((p) => {
+				// if(false) {
+				if(p.type == 'choice') {
+					result.push(p.name)
+					return p.properties.map(function(q){
+						result.push(q.properties[0].name);
+					})
+
+				} else  {
+					return result.push(p.name);
+				}
+			})
+			return result;
 		}
 		console.warn('no model uiState found for type', typeOrPath);
 	}
diff --git a/skyquake/plugins/composer/src/src/libraries/model/DescriptorModelSerializer.js b/skyquake/plugins/composer/src/src/libraries/model/DescriptorModelSerializer.js
index 4e6b792..014beb3 100644
--- a/skyquake/plugins/composer/src/src/libraries/model/DescriptorModelSerializer.js
+++ b/skyquake/plugins/composer/src/src/libraries/model/DescriptorModelSerializer.js
@@ -1,6 +1,6 @@
 
 /*
- * 
+ *
  *   Copyright 2016 RIFT.IO Inc
  *
  *   Licensed under the Apache License, Version 2.0 (the "License");
@@ -121,7 +121,9 @@
 				return constituentVNFD;
 
 			});
-
+			for (var key in confd) {
+				checkForChoiceAndRemove(key, confd, nsdModel);
+			}
 			// serialize the VLD instances
 			confd.vld = confd.vld.map(d => {
 				return DescriptorModelSerializer.serialize(d);
@@ -142,11 +144,15 @@
 			// once that is fixed, remove this piece of code.
 			// fix-start
 			for (var key in confd) {
-				if (confd.hasOwnProperty(key) && confd[key] === '') {
-					delete confd[key];
-				}
+			  	if (confd.hasOwnProperty(key) && confd[key] === '') {
+                	delete confd[key];
+                } else {
+                	//removes choice properties from top level object and copies immediate children onto it.
+					checkForChoiceAndRemove(key, confd, vldModel);
+                }
 			}
 
+
 			const deepProperty = 'provider-network';
 			for (var key in confd[deepProperty]) {
 				if (confd[deepProperty].hasOwnProperty(key) && confd[deepProperty][key] === '') {
@@ -154,8 +160,6 @@
 				}
 			}
 			// fix-end
-
-
 			confd[property] = confd[property].map(d => DescriptorModelSerializer[property].serialize(d));
 			return confd;
 		}
@@ -186,10 +190,31 @@
 	},
 	vdu: {
 		serialize(vduModel) {
-			const confd = _.omit(vduModel, ['uiState']);
+			const copy = _.cloneDeep(vduModel);
+			for (let k in copy) {
+				checkForChoiceAndRemove(k, copy, vduModel)
+			}
+			const confd = _.omit(copy, ['uiState']);
 			return confd;
 		}
 	}
 };
 
+
+function checkForChoiceAndRemove(k, confd, model) {
+	let state = model.uiState;
+	if (state.choice) {
+		let choice = state.choice[k]
+		if(choice) {
+			for (let key in confd[k]) {
+				if(choice && (choice.selected.indexOf(key) > -1)) {
+					confd[key] = confd[k][key]
+				}
+			};
+			delete confd[k];
+		}
+	}
+	return confd;
+}
+
 export default DescriptorModelSerializer;
diff --git a/skyquake/plugins/composer/src/src/libraries/utils.js b/skyquake/plugins/composer/src/src/libraries/utils.js
index 01e6675..ec69bc4 100644
--- a/skyquake/plugins/composer/src/src/libraries/utils.js
+++ b/skyquake/plugins/composer/src/src/libraries/utils.js
@@ -1,5 +1,5 @@
 /*
- * 
+ *
  *   Copyright 2016 RIFT.IO Inc
  *
  *   Licensed under the Apache License, Version 2.0 (the "License");
@@ -71,7 +71,7 @@
 		}, obj);
 		resolvedObj[name] = value;
 	},
-	updatePathValue(obj, path, value) {
+	updatePathValue(obj, path, value, isCase) {
 		// todo: replace implementation of assignPathValue with this impl and
 		// remove updatePathValue (only need one function, not both)
 		// same as assignPathValue except removes property if value is undefined
@@ -92,18 +92,27 @@
 				if (isArray) {
 					r[p] = r[p].filter((d, i) => i !== index);
 				} else {
-					delete r[p][name];
+					if(isCase) {
+						delete r[name];
+					} else {
+						delete r[p][name];
+					}
 				}
 			}
-			return r[p];
+			if(isCase) {
+				return r;
+			} else {
+				return r[p];
+			}
+
 		}, obj);
 		if (!isRemove) {
 			resolvedObj[name] = value;
 		}
 	},
-	removePathValue(obj, path) {
+	removePathValue(obj, path, isCase) {
 		// note updatePathValue removes value if third argument is undefined
-		return this.updatePathValue(obj, path);
+		return this.updatePathValue(obj, path, undefined, isCase);
 	},
 
 	suffixAsInteger: (field) => {
diff --git a/skyquake/plugins/launchpad/api/launchpad.js b/skyquake/plugins/launchpad/api/launchpad.js
index 96818e0..509ff77 100644
--- a/skyquake/plugins/launchpad/api/launchpad.js
+++ b/skyquake/plugins/launchpad/api/launchpad.js
@@ -1664,7 +1664,7 @@
 CloudAccount.get = function(req) {
     var api_server = req.query["api_server"];
     var uri = utils.confdPort(api_server);
-    uri += APIVersion + '/api/config/cloud/account?deep';
+    uri += APIVersion + '/api/operational/cloud/account?deep';
     var headers = _.extend({}, constants.HTTP_HEADERS.accept.collection, {
         'Authorization': req.get('Authorization')
     });
diff --git a/skyquake/plugins/launchpad/src/instantiate/instantiateStore.js b/skyquake/plugins/launchpad/src/instantiate/instantiateStore.js
index 94ba391..2e6e242 100644
--- a/skyquake/plugins/launchpad/src/instantiate/instantiateStore.js
+++ b/skyquake/plugins/launchpad/src/instantiate/instantiateStore.js
@@ -131,7 +131,13 @@
     }
     getLaunchCloudAccountSuccess(cloudAccounts) {
         let newState = {};
-        newState.cloudAccounts = cloudAccounts || [];
+        newState.cloudAccounts = cloudAccounts.filter(function(v) {
+            console.log(v)
+                return v['connection-status'].status == 'success';
+            }) || [];
+        if(cloudAccounts.length != newState.cloudAccounts.length) {
+            Alt.actions.global.showNotification.defer({type: 'warning', msg: 'One or more VIM accounts have failed to connect'});
+        }
         if(cloudAccounts && cloudAccounts.length > 0) {
             newState.selectedCloudAccount = cloudAccounts[0];
             if (cloudAccounts[0]['account-type'] == 'openstack') {
diff --git a/skyquake/plugins/launchpad/src/launchpad_card/launchpadHeader.jsx b/skyquake/plugins/launchpad/src/launchpad_card/launchpadHeader.jsx
index a310c27..9ee7ec4 100644
--- a/skyquake/plugins/launchpad/src/launchpad_card/launchpadHeader.jsx
+++ b/skyquake/plugins/launchpad/src/launchpad_card/launchpadHeader.jsx
@@ -1,6 +1,6 @@
 
 /*
- * 
+ *
  *   Copyright 2016 RIFT.IO Inc
  *
  *   Licensed under the Apache License, Version 2.0 (the "License");
@@ -127,12 +127,12 @@
             <h3 className="launchpadCard_header-link">
               {
                 isLoading ?
-                            <a title="Open Viewport Dashboard" style={{cursor:'default'}}>
+                            <a title={this.props.name} className="title" style={{cursor:'default'}}>
                               {this.props.name}
                             </a>
                           : <Link to={{pathname: '/viewport', query: {id: this.props.nsr.id,
                               sdnpresent: sdnpresent}}}
-                              title="Open Viewport Dashboard">
+                              title={"Open Viewport Dashboard for " + this.props.name}>
                               {this.props.name}
                               <span className="oi" data-glyph="external-link" aria-hidden="true"></span>
                             </Link>
diff --git a/skyquake/plugins/launchpad/src/launchpad_card/launchpad_card.scss b/skyquake/plugins/launchpad/src/launchpad_card/launchpad_card.scss
index 01a798a..1f2680d 100644
--- a/skyquake/plugins/launchpad/src/launchpad_card/launchpad_card.scss
+++ b/skyquake/plugins/launchpad/src/launchpad_card/launchpad_card.scss
@@ -1,6 +1,6 @@
 
 /*
- * 
+ *
  *   Copyright 2016 RIFT.IO Inc
  *
  *   Licensed under the Apache License, Version 2.0 (the "License");
@@ -19,23 +19,33 @@
 @import 'style/_colors.scss';
 @import '../../node_modules/open-iconic/font/css/open-iconic.css';
 .launchpadCard {
+  display:-ms-flexbox;
   display:flex;
   margin-top: 0.75rem;
   &-body{
     position: relative;
   }
   .deletingIndicator {
+    display: -ms-flexbox;
     display: flex;
-    flex:1;
-    align-items: center;
-    justify-content: center;
+    -ms-flex:1;
+        flex:1;
+    -ms-flex-align: center;
+        align-items: center;
+    -ms-flex-pack: center;
+        justify-content: center;
   }
   &_launch {
+    display:-ms-flexbox;
     display:flex;
-    flex-direction:column;
-    flex: 1;
-    justify-content: center;
-    align-items: center;
+    -ms-flex-direction:column;
+        flex-direction:column;
+    -ms-flex: 1;
+        flex: 1;
+    -ms-flex-pack: center;
+        justify-content: center;
+    -ms-flex-align: center;
+        align-items: center;
     img {
       width: 88px;
       padding:1rem;
@@ -47,27 +57,39 @@
     min-width: 693px;
     max-width: 693px;
     &-link, a {
+      display:-ms-flexbox;
       display:flex;
-      align-items:center;
+      -ms-flex-align:center;
+          align-items:center;
       text-decoration: none;
       color: inherit;
     }
     &-link {
       margin-left:0.5rem;
       cursor:pointer;
+      .title {
+        overflow: hidden;
+        text-overflow: ellipsis;
+        width: 460px;
+        display: block;
+      }
     }
     .oi {
       margin:0 0 -0.25rem 0.5rem;
     }
     &-title {
+      display: -ms-flexbox;
       display: flex;
       line-height: 3rem;
       background-color: $brand-green-light;
-      //background-color: $primary-header;
+      /*background-color: $primary-header;*/
       position: relative;
-      flex-direction: row;
-      align-items: center;
-      justify-content: flex-start;
+      -ms-flex-direction: row;
+          flex-direction: row;
+      -ms-flex-align: center;
+          align-items: center;
+      -ms-flex-pack: start;
+          justify-content: flex-start;
       padding-right:1rem;
       &-off {
         background-color:white;
@@ -92,9 +114,12 @@
         position:relative;
       }
     &-actions {
-      flex: 1;
+      -ms-flex: 1;
+          flex: 1;
+      display: -ms-flexbox;
       display: flex;
-      justify-content: flex-end;
+      -ms-flex-pack: end;
+          justify-content: flex-end;
       padding-right: 1rem;
       h3{
         padding-right:0.25rem;
@@ -105,8 +130,10 @@
     &-status {
       background-color: $brand-green;
       &-current {
+        display:-ms-flexbox;
         display:flex;
-        justify-content:space-between;
+        -ms-flex-pack:justify;
+            justify-content:space-between;
         padding: 0.5rem 0;
         span,a {
           padding: 0 0.25rem;
@@ -121,11 +148,15 @@
     }
     &-operational-status {
       &_loading {
+        display:-ms-flexbox;
         display:flex;
-        flex-direction:column;
+        -ms-flex-direction:column;
+            flex-direction:column;
+        -ms-flex-pack: center;
+            justify-content: center;
         justify-content: center;
-        justify-content: center;
-        align-items: center;
+        -ms-flex-align: center;
+            align-items: center;
         color:$gray-darkest;
      }
      &_open {
@@ -137,11 +168,16 @@
         position: absolute;
         width: 100%;
         z-index: 99;
+        display: -ms-flexbox;
         display: flex;
-        flex-direction: column;
-        align-items: center;
-        justify-content: center;
-        align-content: center;
+        -ms-flex-direction: column;
+            flex-direction: column;
+        -ms-flex-align: center;
+            align-items: center;
+        -ms-flex-pack: center;
+            justify-content: center;
+        -ms-flex-line-pack: center;
+            align-content: center;
         color: black;
         h2{
               padding: 2rem;
@@ -174,10 +210,14 @@
     &-vnfr_management-links {
 
       &_loading {
+        display:-ms-flexbox;
         display:flex;
-        flex-direction:column;
-        justify-content: center;
-        align-items: center;
+        -ms-flex-direction:column;
+            flex-direction:column;
+        -ms-flex-pack: center;
+            justify-content: center;
+        -ms-flex-align: center;
+            align-items: center;
         color:$gray-darkest;
      }
       &_open {
@@ -189,11 +229,16 @@
         position:absolute;
         width:100%;
         z-index: 99;
+        display: -ms-flexbox;
         display: flex;
-        flex-direction: column;
-        align-items: center;
-        justify-content: center;
-        align-content: center;
+        -ms-flex-direction: column;
+            flex-direction: column;
+        -ms-flex-align: center;
+            align-items: center;
+        -ms-flex-pack: center;
+            justify-content: center;
+        -ms-flex-line-pack: center;
+            align-content: center;
         color:black;
 
         .notice {
@@ -229,7 +274,9 @@
           height: inherit;
         }
         li {
-          justify-content: space-between;
+          -ms-flex-pack: justify;
+              justify-content: space-between;
+          display: -ms-flexbox;
           display: flex;
         }
       }
@@ -256,31 +303,39 @@
     .empty {
       min-height:50px;
       max-width: 100%;
+      display:-ms-flexbox;
       display:flex;
-      justify-content: center;
-      align-items: center;
+      -ms-flex-pack: center;
+          justify-content: center;
+      -ms-flex-align: center;
+          align-items: center;
     }
   }
   &_title {
       color:$black;
       background:$secondary-header;
       padding: 0.5rem;
-      // margin: 0 0 0.5rem 0;
-      // border-top:1px solid #666;
-      // border-bottom:1px solid #666;
+      /* margin: 0 0 0.5rem 0;*/
+      /* border-top:1px solid #666;*/
+      /* border-bottom:1px solid #666;*/
       border-top:1px solid #f1f1f1;
       border-bottom:1px solid #f1f1f1;
   }
 
   &_data-list {
     ul, dl{
+      display:-ms-flexbox;
       display:flex;
-      flex-direction:row;
-      flex-wrap:wrap;
-      align-items:flex-start;
+      -ms-flex-direction:row;
+          flex-direction:row;
+      -ms-flex-wrap:wrap;
+          flex-wrap:wrap;
+      -ms-flex-align:start;
+          align-items:flex-start;
        font-size: 0.825rem;
       li{
-        flex:1 1 30%;
+        -ms-flex:1 1 30%;
+            flex:1 1 30%;
       }
     }
     dl {
@@ -305,7 +360,7 @@
     }
   }
   &_data-list.EPA-PARAMS {
-      // height: 105px;
+      /* height: 105px;*/
       width: 715px;
       overflow-y: scroll;
       h1 {
@@ -316,10 +371,13 @@
       }
   }
   &_controls{
+    display:-ms-flexbox;
     display:flex;
-    flex-direction:row;
+    -ms-flex-direction:row;
+        flex-direction:row;
     .react-tabs {
-      flex: auto;
+      -ms-flex: auto;
+          flex: auto;
     [role=tab][aria-selected=true] {
       background:none;
       border:none;
@@ -343,11 +401,14 @@
       border:none;
       margin: 0 0 10px;
       padding: 0;
+      display: -ms-flexbox;
       display: flex;
-      justify-content: flex-end;
+      -ms-flex-pack: end;
+          justify-content: flex-end;
 
          [role=tab]:first-child {
-            flex: 1;
+            -ms-flex: 1;
+                flex: 1;
          }
     }
 
@@ -355,7 +416,7 @@
 
   }
   .slick-initialized.slick-slider {
-    // min-height:200px;
+    /* min-height:200px;*/
   }
 
   .close-btn {
diff --git a/skyquake/plugins/launchpad/src/recordViewer/recordDetails.jsx b/skyquake/plugins/launchpad/src/recordViewer/recordDetails.jsx
index 0a3e35e..9b35d1b 100644
--- a/skyquake/plugins/launchpad/src/recordViewer/recordDetails.jsx
+++ b/skyquake/plugins/launchpad/src/recordViewer/recordDetails.jsx
@@ -27,7 +27,17 @@
   }
   render(){
     let html;
-    let text = JSON.stringify(this.props.data, undefined, 2);
+    // Prism can't handle escaped \n and other characters
+    let text = JSON.stringify(this.props.data, undefined, 2)
+                .replace(/\r\n/g, '\n')
+                .replace(/\\\\n/g, "\n")
+                .replace(/\\\\'/g, "\'")
+                .replace(/\\\\"/g, '\"')
+                .replace(/\\\\&/g, "\&")
+                .replace(/\\\\r/g, "\r")
+                .replace(/\\\\t/g, "\t")
+                .replace(/\\\\b/g, "\b")
+                .replace(/\\\\f/g, "\f");
     // html = this.props.isLoading ? <LoadingIndicator size={10} show={true} /> : <pre className="json">{JSON.stringify(this.props.data, undefined, 2)}</pre>;
     html = this.props.isLoading ? <LoadingIndicator size={10} show={true} /> : Prism.highlight(text, Prism.languages.javascript, 'javascript');
     return (
diff --git a/skyquake/plugins/launchpad/src/ssh_keys/sshKeyCard.scss b/skyquake/plugins/launchpad/src/ssh_keys/sshKeyCard.scss
index f72af71..cbb6691 100644
--- a/skyquake/plugins/launchpad/src/ssh_keys/sshKeyCard.scss
+++ b/skyquake/plugins/launchpad/src/ssh_keys/sshKeyCard.scss
@@ -1,5 +1,5 @@
 /*
- * 
+ *
  *   Copyright 2016 RIFT.IO Inc
  *
  *   Licensed under the Apache License, Version 2.0 (the "License");
@@ -17,12 +17,20 @@
  */
 @import "style/_colors.scss";
 
+.sshKeyCards {
+    display:-ms-flexbox;
+    display:flex;
+    -ms-flex-direction:column;
+        flex-direction:column;
+}
+
 .sshKeyCard {
     display:-ms-flexbox;
     display:flex;
     -ms-flex-align: start;
         align-items: flex-start;
-    height:250px;
+    -ms-flex: 1 0 auto;
+        flex: 1 0 auto;
     border-top: 1px solid $neutral-white;
     &:nth-child(odd) {
         background:$gray-lighter;
@@ -58,20 +66,15 @@
     }
     &-key.sqTextInput {
         font-size:1rem;
-        -ms-flex-align: stretch;
-            -ms-grid-row-align: stretch;
-            align-items: stretch;
-            -ms-flex:1;
-                flex:1;
+
         > textarea {
             padding:0.5rem;
             min-height:190px;
         }
         > .readonly {
-            -ms-flex: 1;
-                flex: 1;
             background: none !important;
             word-break: break-all;
+            height:auto;
         }
     }
     &-name {
@@ -95,8 +98,8 @@
         -ms-flex-align: center;
         -ms-grid-row-align: center;
     align-items: center;
-        height: 100%;
-        border-left: 1px solid $neutral-white;
+        -ms-flex-item-align: center;
+            align-self: center;
         >div {
             display:-ms-flexbox;
             display:flex;
diff --git a/skyquake/plugins/launchpad/src/ssh_keys/sshKeys.jsx b/skyquake/plugins/launchpad/src/ssh_keys/sshKeys.jsx
index 1676cff..f832215 100644
--- a/skyquake/plugins/launchpad/src/ssh_keys/sshKeys.jsx
+++ b/skyquake/plugins/launchpad/src/ssh_keys/sshKeys.jsx
@@ -1,5 +1,5 @@
 /*
- * 
+ *
  *   Copyright 2016 RIFT.IO Inc
  *
  *   Licensed under the Apache License, Version 2.0 (the "License");
@@ -21,7 +21,7 @@
 import SshKeyStore from './sshKeyStore.js';
 import SkyquakeComponent from 'widgets/skyquake_container/skyquakeComponent.jsx';
 import '../../node_modules/open-iconic/font/css/open-iconic.css';
-import 'style/base.scss';
+// import 'style/base.scss';
 
 
 class SshKeys extends Component {
@@ -45,7 +45,7 @@
         let Store = self.Store;
         // return <div>test</div>
         return (
-          <div>
+          <div className="sshKeyCards">
             {
               self.state.data && self.state.data.keys.map(function(k, i) {
                 let sshKey = self.state.data.entities[k];