update from RIFT as of 696b75d2fe9fb046261b08c616f1bcf6c0b54a9b third try

Signed-off-by: Jeremy Mordkoff <Jeremy.Mordkoff@riftio.com>

Change-Id: Ib11aa03a2eff5a53c808342508a5d7bee7b202b8
diff --git a/skyquake/framework/widgets/button/button.scss b/skyquake/framework/widgets/button/button.scss
index c972e14..043a769 100644
--- a/skyquake/framework/widgets/button/button.scss
+++ b/skyquake/framework/widgets/button/button.scss
@@ -1,6 +1,6 @@
 
 /*
- * 
+ *
  *   Copyright 2016 RIFT.IO Inc
  *
  *   Licensed under the Apache License, Version 2.0 (the "License");
@@ -62,15 +62,18 @@
 ############################################################################ */
 
 .SqButton {
-  align-items: center;
+  -ms-flex-align: center;
+      align-items: center;
   border-style: solid;
   border-radius: 3px;
   border-width: 0px;
   cursor: pointer;
+  display: -ms-inline-flexbox;
   display: inline-flex;
   font-size: 1rem;
   height: 50px;
-  justify-content: center;
+  -ms-flex-pack: center;
+      justify-content: center;
   margin: 0 10px;
   outline: none;
   padding: 0 15px;
@@ -107,8 +110,9 @@
 
   /* Focus */
   &:focus {
-    // box-shadow: $focus-shadow;
-    border: 1px solid red;
+    /* box-shadow: $focus-shadow;*/
+    border: 1px solid;
+    border-color: darken($normalHoverBackground, 10%);
   }
 
   /* SIZES
@@ -256,11 +260,14 @@
       fill: $primaryForeground;
     }
   }
-
-
 }
 
-
+.sqButtonGroup {
+  display: -ms-flexbox;
+  display: flex;
+  -ms-flex-pack: center;
+      justify-content: center;
+}
 
 
 
diff --git a/skyquake/framework/widgets/button/sq-button.jsx b/skyquake/framework/widgets/button/sq-button.jsx
index ae93128..8d0ec77 100644
--- a/skyquake/framework/widgets/button/sq-button.jsx
+++ b/skyquake/framework/widgets/button/sq-button.jsx
@@ -36,17 +36,35 @@
             Class += " is-disabled";
         }
         return (
-                <div style={{display: 'flex'}}>
+        <div style={{display: 'flex'}} onClick={this.props.onClick}>
             <div className={Class} tabIndex="0">
-            {svgHTML}
-              <div className="SqButton-content">{label}</div>
+                {svgHTML}
+                <div className="SqButton-content">{label}</div>
             </div>
+        </div>
+        )
+    }
+}
+
+export class ButtonGroup extends React.Component {
+    render() {
+        let className = "sqButtonGroup";
+        if (this.props.className) {
+            className = `${className} ${this.props.className}`
+        }
+        return (
+            <div className={className} style={this.props.style}>
+                {this.props.children}
             </div>
         )
     }
 }
 
+
 SqButton.defaultProps = {
+    onClick: function(e) {
+        console.log('Clicked')
+    },
     icon: false,
     primary: false,
     disabled: false,
diff --git a/skyquake/framework/widgets/components.js b/skyquake/framework/widgets/components.js
index f38719a..461a783 100644
--- a/skyquake/framework/widgets/components.js
+++ b/skyquake/framework/widgets/components.js
@@ -26,355 +26,7 @@
 //  Histogram: Histogram,
   Multicomponent: require('./multicomponent/multicomponent.js'),
   Mixins: require('./mixins/ButtonEventListener.js'),
-  // Gauge: require('./gauge/gauge.js'),
+//  Gauge: require('./gauge/gauge.js'),
   Bullet: require('./bullet/bullet.js')
 };
 
-// require('../../assets/js/n3-line-chart.js');
-// var Gauge = require('../../assets/js/gauge-modified.js');
-// var bulletController = function($scope, $element) {
-//     this.$element = $element;
-//     this.vertical = false;
-//     this.value = 0;
-//     this.min = 0;
-//     this.max = 100;
-//     //this.range = this.max - this.min;
-//     //this.percent = (this.value - this.min) / this.range;
-//     this.displayValue = this.value;
-//     this.isPercent = (this.units == '')? true:false;
-//     this.bulletColor = "#6BB814";
-//     this.fontsize = 28;
-//     this.radius = 4;
-//     this.containerMarginX = 0;
-//     this.containerMarginY = 0;
-//     this.textMarginX = 5;
-//     this.textMarginY = 42;
-//     this.bulletMargin = 0;
-//     this.width = 512;
-//     this.height = 64;
-//     this.markerX = -100; // puts it off screen unless set
-//     var self = this;
-//     if (this.isPercent) {
-//         this.displayValue + "%";
-//     }
-//     $scope.$watch(
-//       function() {
-//         return self.value;
-//       },
-//       function() {
-//         self.valueChanged();
-//       }
-//     );
-
-//   }
-
-//   bulletController.prototype = {
-
-//     valueChanged: function() {
-//       var range = this.max - this.min;
-//       var normalizedValue = (this.value - this.min) / range;
-//       if (this.isPercent) {
-//         this.displayValue = String(Math.round(normalizedValue * 100)) + "%";
-//       } else {
-//         this.displayValue = this.value;
-//       }
-//       // All versions of IE as of Jan 2015 does not support inline CSS transforms on SVG
-//       if (platform.name == 'IE') {
-//         this.bulletWidth = Math.round(100 * normalizedValue) + '%';
-//       } else {
-//         this.bulletWidth = this.width - (2 * this.containerMarginX);
-//         var transform = 'scaleX(' + normalizedValue + ')';
-//         var bullet = $(this.$element).find('.bullet2');
-//         bullet.css('transform', transform);
-//         bullet.css('-webkit-transform', transform);
-//       }
-//     },
-
-//     markerChanged: function() {
-//       var range = this.max - this.min;
-//       var w = this.width - (2 * this.containerMarginX);
-//       this.markerX = this.containerMarginX + ((this.marker - this.min) / range ) * w;
-//       this.markerY1 = 7;
-//       this.markerY2 = this.width - 7;
-//     }
-//   }
-
-// angular.module('components', ['n3-line-chart'])
-//     .directive('rwBullet', function() {
-//       return {
-//         restrict : 'E',
-//         templateUrl: 'modules/views/rw.bullet.tmpl.html',
-//         bindToController: true,
-//         controllerAs: 'bullet',
-//         controller: bulletController,
-//         replace: true,
-//         scope: {
-//           min : '@?',
-//           max : '@?',
-//           value : '@',
-//           marker: '@?',
-//           units: '@?',
-//           bulletColor: '@?',
-//           label: '@?'
-//         }
-//       };
-//     })
-//     .directive('rwSlider', function() {
-//       var controller = function($scope, $element, $timeout) {
-//         // Q: is there a way to force attributes to be ints?
-//         $scope.min = $scope.min || "0";
-//         $scope.max = $scope.max || "100";
-//         $scope.step = $scope.step || "1";
-//         $scope.height = $scope.height || "30";
-//         $scope.orientation = $scope.orientation || 'horizontal';
-//         $scope.tooltipInvert = $scope.tooltipInvert || false;
-//         $scope.percent = $scope.percent || false;
-//         $scope.kvalue = $scope.kvalue || false;
-//         $scope.direction = $scope.direction || "ltr";
-//         $($element).noUiSlider({
-//           start: parseInt($scope.value),
-//           step: parseInt($scope.step),
-//           orientation: $scope.orientation,
-//           range: {
-//             min: parseInt($scope.min),
-//             max: parseInt($scope.max)
-//           },
-//           direction: $scope.direction
-//         });
-//         //$(".no-Ui-target").Link('upper').to('-inline-<div class="tooltip"></div>')
-//         var onSlide = function(e, value) {
-//           $timeout(function(){
-//             $scope.value = value;
-//           })
-
-//         };
-//         $($element).on({
-//           change: onSlide,
-//           slide: onSlide,
-//           set: $scope.onSet({value: $scope.value})
-//         });
-//         var val = String(Math.round($scope.value));
-//         if ($scope.percent) {
-//             val += "%"
-//         } else if ($scope.kvalue) {
-//             val += "k"
-//         }
-//         $($element).height($scope.height);
-//         if ($scope.tooltipInvert) {
-//             $($element).find('.noUi-handle').append("<div class='tooltip' style='position:relative;right:20px'>" + val + "</div>");
-//         } else {
-//             $($element).find('.noUi-handle').append("<div class='tooltip' style='position:relative;left:-20px'>" + val + "</div>");
-//         }
-//         $scope.$watch('value', function(value) {
-//         var val = String(Math.round($scope.value));
-//         if ($scope.percent) {
-//             val += "%"
-//         } else if($scope.kvalue) {
-//             val += "k"
-//         }
-//           $($element).val(value);
-//           $($element).find('.tooltip').html(val);
-//         if ($scope.tooltipInvert) {
-//             $($element).find('.tooltip').css('right', $($element).find('.tooltip').innerWidth() * -1);
-//         } else {
-//             $($element).find('.tooltip').css('left', $($element).find('.tooltip').innerWidth() * -1);
-//         }
-//         });
-//       };
-
-//       return {
-//         restrict : 'E',
-//         template: '<div></div>',
-//         controller : controller,
-//         replace: true,
-//         scope: {
-//           min : '@',
-//           max : '@',
-//           width: '@',
-//           height: '@',
-//           step : '@',
-//           orientation : '@',
-//           tooltipInvert: '@',
-//           percent: '@',
-//           kvalue: '@?',
-//           onSet:'&?',
-//           direction: '@?',
-//           value:'=?'
-//         }
-//       };
-//     })
-// .directive('rwGauge', function() {
-//     return {
-//         restrict: 'AE',
-//         template: '<canvas class="rwgauge" style="width:100%;height:100%;max-width:{{width}}px;max-height:240px;"></canvas>',
-//         replace: true,
-//         scope: {
-//             min: '@?',
-//             max: '@?',
-//             size: '@?',
-//             color: '@?',
-//             value: '@?',
-//             resize: '@?',
-//             isAggregate: '@?',
-//             units: '@?',
-//             valueFormat: '=?',
-//             width: '@?'
-//         },
-//         bindToController: true,
-//         controllerAs: 'gauge',
-//         controller: function($scope, $element) {
-//             var self = this;
-//             this.gauge = null;
-//             this.min = this.min || 0;
-//             this.max = this.max || 100;
-//             this.nSteps = 14;
-//             this.size = this.size || 300;
-//             this.units = this.units || '';
-//             $scope.width = this.width || 240;
-//             this.color = this.color || 'hsla(212, 57%, 50%, 1)';
-//             if (!this.valueFormat) {
-//                 if (this.max > 1000 || this.value) {
-//                     self.valueFormat = {
-//                         "int": 1,
-//                         "dec": 0
-//                     };
-//                 } else {
-//                     self.valueFormat = {
-//                         "int": 1,
-//                         "dec": 2
-//                     };
-//                 }
-//             }
-//             this.isAggregate = this.isAggregate || false;
-//             this.resize = this.resize || false;
-//             if (this.format == 'percent') {
-//                 self.valueFormat = {
-//                     "int": 3,
-//                     "dec": 0
-//                 };
-//             }
-//             $scope.$watch(function() {
-//                 return self.max;
-//             }, function(n, o) {
-//                 if(n !== o) {
-//                     renderGauge();
-//                 }
-//             });
-//             $scope.$watch(function() {
-//                 return self.valueFormat;
-//             }, function(n, o) {
-//                 if(n != 0) {
-//                     renderGauge();
-//                 }
-//             });
-//             $scope.$watch(function() {
-//                 return self.value;
-//             }, function() {
-//                 if (self.gauge) {
-//                     // w/o rounding gauge will unexplainably thrash round.
-//                     self.valueFormat = determineValueFormat(self.value);
-//                     self.gauge.setValue(Math.ceil(self.value * 100) / 100);
-//                     //self.gauge.setValue(Math.round(self.value));
-//                 }
-//             });
-//             angular.element($element).ready(function() {
-//                 console.log('rendering')
-//                 renderGauge();
-//             })
-//             window.testme = renderGauge;
-//             function determineValueFormat(value) {
-
-//                     if (value > 999 || self.units == "%") {
-//                         return {
-//                             "int": 1,
-//                             "dec": 0
-//                         }
-//                     }
-
-//                     return {
-//                         "int": 1,
-//                         "dec": 2
-//                     }
-//                 }
-//             function renderGauge(calcWidth) {
-//                 if (self.max == self.min) {
-//                     self.max = 14;
-//                 }
-//                 var range = self.max - self.min;
-//                 var step = Math.round(range / self.nSteps);
-//                 var majorTicks = [];
-//                 for (var i = 0; i <= self.nSteps; i++) {
-//                     majorTicks.push(self.min + (i * step));
-//                 };
-//                 var redLine = self.min + (range * 0.9);
-//                 var config = {
-//                     isAggregate: self.isAggregate,
-//                     renderTo: angular.element($element)[0],
-//                     width: calcWidth || self.size,
-//                     height: calcWidth || self.size,
-//                     glow: false,
-//                     units: self.units,
-//                     title: false,
-//                     minValue: self.min,
-//                     maxValue: self.max,
-//                     majorTicks: majorTicks,
-//                     valueFormat: determineValueFormat(self.value),
-//                     minorTicks: 0,
-//                     strokeTicks: false,
-//                     highlights: [],
-//                     colors: {
-//                         plate: 'rgba(0,0,0,0)',
-//                         majorTicks: 'rgba(15, 123, 182, .84)',
-//                         minorTicks: '#ccc',
-//                         title: 'rgba(50,50,50,100)',
-//                         units: 'rgba(50,50,50,100)',
-//                         numbers: '#fff',
-//                         needle: {
-//                             start: 'rgba(255, 255, 255, 1)',
-//                             end: 'rgba(255, 255, 255, 1)'
-//                         }
-//                     }
-//                 };
-//                 var min = config.minValue;
-//                 var max = config.maxValue;
-//                 var N = 1000;
-//                 var increment = (max - min) / N;
-//                 for (i = 0; i < N; i++) {
-//                     var temp_color = 'rgb(0, 172, 238)';
-//                     if (i > 0.5714 * N && i <= 0.6428 * N) {
-//                         temp_color = 'rgb(0,157,217)';
-//                     } else if (i >= 0.6428 * N && i < 0.7142 * N) {
-//                         temp_color = 'rgb(0,142,196)';
-//                     } else if (i >= 0.7142 * N && i < 0.7857 * N) {
-//                         temp_color = 'rgb(0,126,175)';
-//                     } else if (i >= 0.7857 * N && i < 0.8571 * N) {
-//                         temp_color = 'rgb(0,122,154)';
-//                     } else if (i >= 0.8571 * N && i < 0.9285 * N) {
-//                         temp_color = 'rgb(0,96,133)';
-//                     } else if (i >= 0.9285 * N) {
-//                         temp_color = 'rgb(0,80,112)';
-//                     }
-//                     config.highlights.push({
-//                         from: i * increment,
-//                         to: increment * (i + 2),
-//                         color: temp_color
-//                     })
-//                 }
-//                 var updateSize = _.debounce(function() {
-//                     config.maxValue = self.max;
-//                     var clientWidth = self.parentNode.parentNode.clientWidth / 2;
-//                     var calcWidth = (300 > clientWidth) ? clientWidth : 300;
-//                     self.gauge.config.width = self.gauge.config.height = calcWidth;
-//                     self.renderGauge(calcWidth);
-//                 }, 500);
-//                 if (self.resize) $(window).resize(updateSize)
-//                 if (self.gauge) {
-//                     self.gauge.updateConfig(config);
-//                 } else {
-//                     self.gauge = new Gauge(config);
-//                     self.gauge.draw();
-//                 }
-//             };
-//         },
-//     }
-// });
diff --git a/skyquake/framework/widgets/form_controls/formControls.jsx b/skyquake/framework/widgets/form_controls/formControls.jsx
new file mode 100644
index 0000000..e50fb7e
--- /dev/null
+++ b/skyquake/framework/widgets/form_controls/formControls.jsx
@@ -0,0 +1,113 @@
+import './formControls.jsx';
+
+import React from 'react'
+import SelectOption from 'widgets/form_controls/selectOption.jsx';
+import imgAdd from '../../../node_modules/open-iconic/svg/plus.svg'
+import imgRemove from '../../../node_modules/open-iconic/svg/trash.svg'
+import TextInput from 'widgets/form_controls/textInput.jsx';
+import Input from 'widgets/form_controls/input.jsx';
+
+export class FormSection extends React.Component {
+    render() {
+        let className = 'FormSection ' + this.props.className;
+        let html = (
+            <div
+                style={this.props.style}
+                className={className}
+            >
+                <div className="FormSection-title">
+                    {this.props.title}
+                </div>
+                <div className="FormSection-body">
+                    {this.props.children}
+                </div>
+            </div>
+        );
+        return html;
+    }
+}
+
+FormSection.defaultProps = {
+    className: ''
+}
+
+/**
+ * AddItemFn:
+ */
+export class InputCollection extends React.Component {
+    constructor(props) {
+        super(props);
+        this.collection = props.collection;
+    }
+    buildTextInput(onChange, v, i) {
+        return (
+            <Input
+                readonly={this.props.readonly}
+                style={{flex: '1 1'}}
+                key={i}
+                value={v}
+                onChange= {onChange.bind(null, i)}
+            />
+        )
+    }
+    buildSelectOption(initial, options, onChange, v, i) {
+        return (
+            <SelectOption
+                readonly={this.props.readonly}
+                key={`${i}-${v.replace(' ', '_')}`}
+                intial={initial}
+                defaultValue={v}
+                options={options}
+                onChange={onChange.bind(null, i)}
+            />
+        );
+    }
+    showInput() {
+
+    }
+    render() {
+        const props = this.props;
+        let inputType;
+        let className = "InputCollection";
+        if (props.className) {
+            className = `${className} ${props.className}`;
+        }
+        if (props.type == 'select') {
+            inputType = this.buildSelectOption.bind(this, props.initial, props.options, props.onChange);
+        } else {
+            inputType = this.buildTextInput.bind(this, props.onChange)
+        }
+        let html = (
+            <div className="InputCollection-wrapper">
+                {props.collection.map((v,i) => {
+                    return (
+                        <div key={i} className={className} >
+                            {inputType(v, i)}
+                            {
+                                props.readonly ? null : <span onClick={props.RemoveItemFn.bind(null, i)} className="removeInput"><img src={imgRemove} />Remove</span>}
+                        </div>
+                    )
+                })}
+                { props.readonly ? null : <span onClick={props.AddItemFn} className="addInput"><img src={imgAdd} />Add</span>}
+            </div>
+        );
+        return html;
+    }
+}
+
+InputCollection.defaultProps = {
+    input: Input,
+    collection: [],
+    onChange: function(i, e) {
+        console.log(`
+                        Updating with: ${e.target.value}
+                        At index of: ${i}
+                    `)
+    },
+    AddItemFn: function(e) {
+        console.log(`Adding a new item to collection`)
+    },
+    RemoveItemFn: function(i, e) {
+        console.log(`Removing item from collection at index of: ${i}`)
+    }
+}
diff --git a/skyquake/framework/widgets/form_controls/formControls.scss b/skyquake/framework/widgets/form_controls/formControls.scss
index 4a88435..ad95add 100644
--- a/skyquake/framework/widgets/form_controls/formControls.scss
+++ b/skyquake/framework/widgets/form_controls/formControls.scss
@@ -1,5 +1,5 @@
 /*
- * 
+ *
  *   Copyright 2016 RIFT.IO Inc
  *
  *   Licensed under the Apache License, Version 2.0 (the "License");
@@ -15,7 +15,7 @@
  *   limitations under the License.
  *
  */
-@import 'style/_colors.scss';
+@import '../../style/_colors.scss';
 
 .sqTextInput {
     display: -ms-flexbox;
@@ -32,9 +32,9 @@
         color:$darker-gray;
         text-transform:uppercase;
     }
-    input, .readonly, textarea {
+    input, textarea {
         height: 35px;
-        box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.39), 0 -1px 1px #ffffff, 0 1px 0 #ffffff;
+        /* box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.39), 0 -1px 1px #ffffff, 0 1px 0 #ffffff;*/
         font-size: 1rem;
         display: block;
         background: white !important;
@@ -49,6 +49,7 @@
     .readonly {
         line-height: 35px;
         box-shadow:none;
+        background:none !important;
     }
     textarea {
         -ms-flex-align: stretch;
@@ -57,4 +58,107 @@
         border:0px;
         height: 100%;
     }
+    &.checkbox {
+        -ms-flex-direction:row;
+            flex-direction:row;
+            -ms-flex-align:center;
+                align-items:center;
+        margin-bottom:0;
+            >span {
+                -ms-flex-order: 1;
+                    order: 1;
+                    padding-left:1rem;
+            }
+            >input {
+                -ms-flex-order: 0;
+                    order: 0;
+
+                    box-shadow:none;
+                    height:25px;
+            }
+    }
+    .invalid {
+        color: red;
+        font-weight:strong;
+    }
+     input:invalid {
+        border: 2px solid red;
+        &:after {
+            content: 'Invalid Value'
+        }
+    }
+}
+
+.sqCheckBox {
+    display:-ms-flexbox;
+    display:flex;
+    label {
+        display:-ms-flexbox;
+        display:flex;
+        -ms-flex-align: center;
+        align-items: center;
+        input {
+            box-shadow: none;
+            height: auto;
+            margin: 0 0.25rem;
+        }
+    }
+}
+
+.FormSection {
+    &-title {
+        color: #000;
+        background: lightgray;
+        padding: 0.5rem;
+        border-top: 1px solid #f1f1f1;
+        border-bottom: 1px solid #f1f1f1;
+    }
+    &-body {
+        padding: 0.5rem 0.75rem;
+    }
+    label {
+        -ms-flex: 1 0;
+            flex: 1 0;
+    }
+    /* label {*/
+    /*     display: -ms-flexbox;*/
+    /*     display: flex;*/
+    /*     -ms-flex-direction: column;*/
+    /*     flex-direction: column;*/
+    /*     width: 100%;*/
+    /*     margin: 0.5rem 0;*/
+    /*     -ms-flex-align: start;*/
+    /*     align-items: flex-start;*/
+    /*     -ms-flex-pack: start;*/
+    /*     justify-content: flex-start;*/
+    /* }*/
+    select {
+        font-size: 1rem;
+        min-width: 75%;
+        height: 35px;
+    }
+}
+
+
+
+
+.InputCollection {
+    display:-ms-flexbox;
+    display:flex;
+    -ms-flex-wrap: nowrap;
+        flex-wrap: nowrap;
+    -ms-flex-align: center;
+        align-items: center;
+    button {
+        padding: 0.25rem;
+        height: 1.5rem;
+        font-size: 0.75rem;
+    }
+    select {
+        min-width: 100%;
+    }
+    margin-bottom:0.5rem;
+    &-wrapper {
+
+    }
 }
diff --git a/skyquake/framework/widgets/form_controls/input.jsx b/skyquake/framework/widgets/form_controls/input.jsx
new file mode 100644
index 0000000..ca31c13
--- /dev/null
+++ b/skyquake/framework/widgets/form_controls/input.jsx
@@ -0,0 +1,134 @@
+/*
+ *
+ *   Copyright 2016 RIFT.IO Inc
+ *
+ *   Licensed under the Apache License, Version 2.0 (the "License");
+ *   you may not use this file except in compliance with the License.
+ *   You may obtain a copy of the License at
+ *
+ *       http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *   Unless required by applicable law or agreed to in writing, software
+ *   distributed under the License is distributed on an "AS IS" BASIS,
+ *   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *   See the License for the specific language governing permissions and
+ *   limitations under the License.
+ *
+ */
+import './formControls.scss';
+import SelectOption from 'widgets/form_controls/selectOption.jsx';
+import CheckSVG from '../../../node_modules/open-iconic/svg/check.svg'
+import React, {Component} from 'react';
+
+export default class Input extends Component {
+    render() {
+        let {label, value, defaultValue, ...props} = this.props;
+        let inputProperties = {
+            value: value
+        }
+        let isRequired;
+        let inputType;
+        let tester = null;
+        let className = `sqTextInput ${props.className}`;
+
+        if(this.props.required) {
+           isRequired = <span className="required">*</span>
+        }
+        if (defaultValue) {
+            inputProperties.defaultValue = defaultValue;
+        }
+        if (props.pattern) {
+            inputProperties.pattern = props.pattern;
+            tester = new RegExp(props.pattern);
+        }
+        if(props.hasOwnProperty('type') && (props.type.toLowerCase() == 'checkbox')) {
+            inputProperties.checked = props.checked;
+            className = `${className} checkbox`;
+        }
+        if (value == undefined) {
+            value = defaultValue;
+        }
+        switch(props.type) {
+            case 'textarea':
+                inputType = <textarea key={props.key} {...inputProperties} value={value} onChange={props.onChange} />
+                break;
+            case 'select':
+                inputType = <SelectOption
+                                key={props.key}
+                                initial={props.initial}
+                                defaultValue={defaultValue}
+                                options={props.options}
+                                onChange={props.onChange}
+                            />
+                break;
+            case 'radiogroup':
+                inputType = buildRadioButtons(this.props);
+                break;
+            default:
+                inputType = <input key={props.key} type={props.type} {...inputProperties} onChange={props.onChange} placeholder={props.placeholder}/>;
+        }
+        let displayedValue;
+        if(value === null) {
+            displayedValue = null;
+        } else {
+            displayedValue = value.toString();
+        }
+        if( props.readonly && props.type == "checkbox" && props.checked ) {
+            displayedValue = <img src={CheckSVG} />
+        }
+
+        if( props.readonly && props.type == "radiogroup" && props.readonlydisplay ) {
+            displayedValue = props.readonlydisplay
+        }
+
+        let html = (
+            <label className={className} style={props.style}>
+              <span> { label } {isRequired}</span>
+              {
+                !props.readonly ? inputType : <div className="readonly">{displayedValue}</div>
+              }
+              {
+                 !props.readonly && tester && value && !tester.test(value) ? <span className="invalid">The Value you entered is invalid</span> : null
+              }
+            </label>
+        );
+        return html;
+    }
+}
+
+
+function buildRadioButtons(props) {
+    let className = 'sqCheckBox';
+    return(
+       <div className={className}>
+            {
+                props.options.map((o,i) => {
+                    let label = o.label || o;
+                    let value = o.value || o;
+                    return (
+                        <label key={i}>
+                            {label}
+                            <input type="radio" checked={props.value == value} value={value} onChange={props.onChange} />
+                        </label>
+                    )
+                })
+            }
+       </div>
+
+    )
+}
+
+Input.defaultProps = {
+    onChange: function(e) {
+        console.log(e.target.value, e);
+        console.dir(e.target);
+    },
+    label: '',
+    defaultValue: null,
+    type: 'text',
+    readonly: false,
+    style:{},
+    className: ''
+
+}
+
diff --git a/skyquake/framework/widgets/form_controls/selectOption.jsx b/skyquake/framework/widgets/form_controls/selectOption.jsx
index 41a8b13..067d1d5 100644
--- a/skyquake/framework/widgets/form_controls/selectOption.jsx
+++ b/skyquake/framework/widgets/form_controls/selectOption.jsx
@@ -1,5 +1,5 @@
 /*
- * 
+ *
  *   Copyright 2016 RIFT.IO Inc
  *
  *   Licensed under the Apache License, Version 2.0 (the "License");
@@ -28,32 +28,65 @@
   render() {
     let html;
     let defaultValue = this.props.defaultValue;
-    let options =  this.props.options.map(function(op, i) {
-      let value = JSON.stringify(op.value);
-      return <option key={i} value={JSON.stringify(op.value)}>{op.label}</option>
-    });
+    let options =  this.props.options && this.props.options.map(function(op, i) {
+    let value;
+    let label;
+    if(typeof(op) == 'object') {
+      value = JSON.stringify(op.value);
+      label = op.label;
+    } else {
+      value = op;
+      label = op;
+    }
+
+      return <option key={i} value={JSON.stringify(value)}>{label}</option>
+    }) || [];
     if (this.props.initial) {
       options.unshift(<option key='blank' value={JSON.stringify(this.props.defaultValue)}></option>);
     }
     html = (
-        <label>
-            {this.props.label}
-            <select className={this.props.className} onChange={this.handleOnChange} defaultValue={JSON.stringify(defaultValue)} >
-                {
-                 options
-                }
-            </select>
+        <label key={this.props.key} className={this.props.className}>
+            <span>{this.props.label}</span>
+            {
+              this.props.readonly ? defaultValue
+              : (
+                  <select
+                    className={this.props.className}
+                    onChange={this.handleOnChange}
+                    value={JSON.stringify(this.props.value)}
+                    defaultValue={JSON.stringify(defaultValue)}>
+                      {
+                       options
+                      }
+                  </select>
+                )
+            }
         </label>
     );
     return html;
   }
 }
 SelectOption.defaultProps = {
+  /**
+   * [options description]
+   * @type {Array} - Expects items to contain objects with the properties 'label' and 'value' which are both string types. Hint: JSON.stringify()
+   */
   options: [],
   onChange: function(e) {
+    console.log(e.target.value)
     console.dir(e)
   },
-  defaultValue: false,
+  readonly: false,
+  /**
+   *  Selected or default value
+​
+   * @type {[type]}
+   */
+  defaultValue: null,
+  /**
+   * True if first entry in dropdown should be blank
+   * @type {Boolean}
+   */
   initial: false,
   label: null
 }
diff --git a/skyquake/framework/widgets/form_controls/textInput.jsx b/skyquake/framework/widgets/form_controls/textInput.jsx
index 03dfa9c..000dcf7 100644
--- a/skyquake/framework/widgets/form_controls/textInput.jsx
+++ b/skyquake/framework/widgets/form_controls/textInput.jsx
@@ -1,5 +1,5 @@
 /*
- * 
+ *
  *   Copyright 2016 RIFT.IO Inc
  *
  *   Licensed under the Apache License, Version 2.0 (the "License");
@@ -18,58 +18,12 @@
 import './formControls.scss';
 
 import React, {Component} from 'react';
-
-export default class TextInput extends Component {
-    render() {
-        let {label, onChange, value, defaultValue, ...props} = this.props;
-        let inputProperties = {
-            value: value,
-            onChange: onChange
-        }
-        let isRequired;
-        let inputType;
-        if(this.props.required) {
-           isRequired = <span className="required">*</span>
-        }
-        if (defaultValue) {
-            inputProperties.defaultValue = defaultValue;
-        }
-        if (props.pattern) {
-            inputProperties.pattern = props.pattern;
-        }
-        if (value == undefined) {
-            value = defaultValue;
-        }
-        switch(props.type) {
-            case 'textarea':
-                inputType = <textarea {...inputProperties} value={value}/>
-
-                break;
-            default:
-                inputType = <input type={props.type} {...inputProperties} placeholder={props.placeholder}/>;
-        }
-        let html = (
-            <label className={"sqTextInput " + props.className} style={props.style}>
-              <span> { label } {isRequired}</span>
-              {
-                !props.readonly ? inputType : <div className="readonly">{value}</div>
-              }
-
-            </label>
-        );
-        return html;
+import Input from './input.jsx';
+class TextInput extends Input {
+    constructor(props) {
+        super(props);
+        console.warn('TextInput is deprecated. Use Input component instead')
     }
 }
-
-TextInput.defaultProps = {
-    onChange: function(e) {
-        console.log(e.target.value);
-    },
-    label: '',
-    defaultValue: undefined,
-    type: 'text',
-    readonly: false,
-    style:{}
-
-}
+export default TextInput;
 
diff --git a/skyquake/framework/widgets/header/header.scss b/skyquake/framework/widgets/header/header.scss
index 5e1e717..6a2e56c 100644
--- a/skyquake/framework/widgets/header/header.scss
+++ b/skyquake/framework/widgets/header/header.scss
@@ -1,5 +1,5 @@
 /*
- * 
+ *
  *   Copyright 2016 RIFT.IO Inc
  *
  *   Licensed under the Apache License, Version 2.0 (the "License");
@@ -17,14 +17,20 @@
  */
 
 header.header-app-component {
-    padding: 20px 0px;
+    padding: 10px 0px;
+    display:-ms-flexbox;
     display:flex;
-    flex-direction:column;
+    -ms-flex-direction:column;
+        flex-direction:column;
     .header-app-main {
+        display:-ms-flexbox;
         display:flex;
-        flex-direction:row;
-        justify-content:space-between;
-        align-items:center;
+        -ms-flex-direction:row;
+            flex-direction:row;
+        -ms-flex-pack:justify;
+            justify-content:space-between;
+        -ms-flex-align:center;
+            align-items:center;
     }
     h1 {
         /*background: url('../../style/img/header-logo.png') no-repeat;*/
@@ -39,15 +45,19 @@
         font-size: 1.625rem;
         font-weight: 400;
         position:relative;
-        flex: 1 0 auto;
+        -ms-flex: 1 0 auto;
+            flex: 1 0 auto;
 
     }
     ul {
+            display:-ms-flexbox;
             display:flex;
         }
         li {
+            display:-ms-flexbox;
             display:flex;
-             flex:1 1 auto;
+             -ms-flex:1 1 auto;
+                 flex:1 1 auto;
              border-right:1px solid #e5e5e5;
              padding: 0 1rem;
             &:last-child {
@@ -55,12 +65,13 @@
             }
             a {
                 cursor:pointer;
-                // padding: 0.125rem;
-                // border-bottom:1px solid black;
+                /* padding: 0.125rem;*/
+                /* border-bottom:1px solid black;*/
                 text-decoration:underline;
             }
         }
     .header-app-nav {
+        display:-ms-flexbox;
         display:flex;
         margin-left: 0.25rem;
         a,span {
@@ -81,8 +92,11 @@
         }
     }
     nav {
+        display:-ms-flexbox;
         display:flex;
-        flex:0 1 auto;
-        align-items:center;
+        -ms-flex:0 1 auto;
+            flex:0 1 auto;
+        -ms-flex-align:center;
+            align-items:center;
     }
 }
diff --git a/skyquake/framework/widgets/operational-status/launchpadOperationalStatus.jsx b/skyquake/framework/widgets/operational-status/launchpadOperationalStatus.jsx
index ba77a79..0e12b6e 100644
--- a/skyquake/framework/widgets/operational-status/launchpadOperationalStatus.jsx
+++ b/skyquake/framework/widgets/operational-status/launchpadOperationalStatus.jsx
@@ -1,6 +1,6 @@
 
 /*
- * 
+ *
  *   Copyright 2016 RIFT.IO Inc
  *
  *   Licensed under the Apache License, Version 2.0 (the "License");
@@ -101,7 +101,7 @@
   }
   render() {
     if(!this.props.hasFailed) {
-      return (<span className='throttledMessageText'>{this.state.displayMessage}</span>)
+      return (<span className='throttledMessageText' style={{margin:'1rem'}}>{this.state.displayMessage}</span>)
     } else {
       return (<span> </span>)
     }
diff --git a/skyquake/framework/widgets/panel/panel.jsx b/skyquake/framework/widgets/panel/panel.jsx
index 4ef7c89..03877b0 100644
--- a/skyquake/framework/widgets/panel/panel.jsx
+++ b/skyquake/framework/widgets/panel/panel.jsx
@@ -18,6 +18,7 @@
 import React, {Component} from 'react';
 import 'style/core.css';
 import './panel.scss';
+import circleXImage from '../../../node_modules/open-iconic/svg/circle-x.svg';
 export class Panel extends Component {
     constructor(props) {
         super(props)
@@ -28,8 +29,15 @@
         let classRoot = className ? ' ' + className : ' ';
         let hasCorners = this.props['no-corners'];
         let titleTag = title ? <header className="skyquakePanel-title">{title}</header> : '';
+        let closeButton = (
+            <a onClick={self.props.hasCloseButton}
+              className={"close-btn"}>
+              <img src={circleXImage} title="Close card" />
+              </a>
+        );
         return (
             <section className={'skyquakePanel' + classRoot} style={props.style}>
+                {  self.props.hasCloseButton ? closeButton : null}
                 { !hasCorners ? <i className="corner-accent top left"></i> : null }
                 { !hasCorners ? <i className="corner-accent top right"></i> : null }
                 {titleTag}
@@ -51,13 +59,23 @@
 
 export class PanelWrapper extends Component {
     render() {
+        let wrapperClass = 'skyquakePanelWrapper';
+        let {className, column, style, ...props} = this.props;
+        if(className) {
+            wrapperClass = `${wrapperClass} ${className}`
+        }
+        if(column) {
+            style.flexDirection = 'column';
+        }
         return (
-        <div className={'skyquakePanelWrapper ' + this.props.className} style={this.props.style}>
+        <div className={wrapperClass} style={style} {...props}>
             {this.props.children}
         </div>)
     }
 }
-
+PanelWrapper.defaultProps = {
+    style: {}
+}
 export default Panel;
 
 
diff --git a/skyquake/framework/widgets/panel/panel.scss b/skyquake/framework/widgets/panel/panel.scss
index 2a31b1c..75dc8ac 100644
--- a/skyquake/framework/widgets/panel/panel.scss
+++ b/skyquake/framework/widgets/panel/panel.scss
@@ -53,9 +53,22 @@
             width:100%;
             height:100%;
         }
+    .close-btn {
+        cursor:pointer;
+        position: absolute;
+        right: -0.5rem;
+        top: -0.5rem;
+        img {
+            width: 1rem;
+        }
+    }
 }
 
 .skyquakePanelWrapper.column {
+    -ms-flex-direction:column;
+            flex-direction:column;
+        display:-ms-flexbox;
+        display:flex;
     .skyquakePanel-wrapper {
         height:auto;
     }
diff --git a/skyquake/framework/widgets/skyquake_container/eventCenter.jsx b/skyquake/framework/widgets/skyquake_container/eventCenter.jsx
index 7df4e3e..471efd1 100644
--- a/skyquake/framework/widgets/skyquake_container/eventCenter.jsx
+++ b/skyquake/framework/widgets/skyquake_container/eventCenter.jsx
@@ -16,12 +16,12 @@
  *
  */
 
- /**
-  * EventCenter module to display a list of events from the system
-  * @module framework/widgets/skyquake_container/EventCenter
-  * @author Kiran Kashalkar <kiran.kashalkar@riftio.com>
-  *
-  */
+/**
+ * EventCenter module to display a list of events from the system
+ * @module framework/widgets/skyquake_container/EventCenter
+ * @author Kiran Kashalkar <kiran.kashalkar@riftio.com>
+ *
+ */
 
 import React from 'react';
 import { Link } from 'react-router';
@@ -31,6 +31,8 @@
 import _isEqual from 'lodash/isEqual';
 import _merge from 'lodash/merge';
 import _indexOf from 'lodash/indexOf';
+import _isArray from 'lodash/isArray';
+import _sortBy from 'lodash/sortBy';
 import '../../../node_modules/react-treeview/react-treeview.css';
 import './eventCenter.scss';
 
@@ -44,7 +46,6 @@
 	componentWillReceiveProps(props) {
 		let stateObject = {};
 
-		let notificationList = sessionStorage.getItem('notificationList');
 		let latestNotification = sessionStorage.getItem('latestNotification');
 
 		if (props.newNotificationEvent && props.newNotificationMsg) {
@@ -67,21 +68,16 @@
 			stateObject.newNotificationEvent = false;
 			stateObject.newNotificationMsg = null;
 		}
+		stateObject.notifications = props.notifications;
 
-		if (notificationList) {
-			stateObject.notifications = _merge(notificationList, props.notifications);
-		} else {
-			stateObject.notifications = props.notifications;
-		}
-		sessionStorage.setItem('notifications', JSON.stringify(stateObject.notifications));
 		this.setState(stateObject);
 	}
 
 	newNotificationReset = () => {
-        this.setState({
-            newNotificationEvent: false
-        });
-    }
+		this.setState({
+			newNotificationEvent: false
+		});
+	}
 
 	onClickToggleOpenClose(event) {
 		this.props.onToggle();
@@ -92,13 +88,13 @@
 	constructTree(details) {
 		let markup = [];
 		for (let key in details) {
-			if (typeof(details[key]) == "object") {
-			    let html = (
-			      	<TreeView key={key} nodeLabel={key}>
-			          {this.constructTree(details[key])}
-			      	</TreeView>
-			    );
-			    markup.push(html);
+			if (typeof (details[key]) == "object") {
+				let html = (
+					<TreeView key={key} nodeLabel={key}>
+						{this.constructTree(details[key])}
+					</TreeView>
+				);
+				markup.push(html);
 			} else {
 				markup.push((<div key={key} className="info">{key} = {details[key].toString()}</div>));
 			}
@@ -142,40 +138,42 @@
 			);
 		}
 
-		this.state.notifications && this.state.notifications.map((notification, notifIndex) => {
-			let notificationFields = {};
+		this.state.notifications &&
+			_isArray(this.state.notifications) &&
+			this.state.notifications.map((notification, notifIndex) => {
+				let notificationFields = {};
 
-			notificationFields = this.getNotificationFields(notification);
+				notificationFields = this.getNotificationFields(notification);
 
-			displayNotifications.push(
-				<tr key={notifIndex} className='notificationItem'>
-					<td className='source column'> {notificationFields.source} </td>
-					<td className='timestamp column'> {notificationFields.eventTime} </td>
-					<td className='event column'> {notificationFields.eventKey} </td>
-					<td className='details column'>
-						<TreeView key={notifIndex + '-detail'} nodeLabel='Event Details'>
-							{this.constructTree(notificationFields.details)}
-						</TreeView>
-					</td>
-				</tr>
-			);
-		});
+				displayNotifications.push(
+					<tr key={notifIndex} className='notificationItem'>
+						<td className='source column'> {notificationFields.source} </td>
+						<td className='timestamp column'> {notificationFields.eventTime} </td>
+						<td className='event column'> {notificationFields.eventKey} </td>
+						<td className='details column'>
+							<TreeView key={notifIndex + '-detail'} nodeLabel='Event Details'>
+								{this.constructTree(notificationFields.details)}
+							</TreeView>
+						</td>
+					</tr>
+				);
+			});
 
 		let openedClassName = this.state.isOpen ? 'open' : 'closed';
 		html = (
 			<div className={'eventCenter ' + openedClassName}>
 
-                        <div className='notification'>
-                            <Crouton
-                                id={Date.now()}
-                                message={this.getNotificationFields(this.state.newNotificationMsg).eventKey +
-                                	' notification received. Check Event Center for more details.'}
-                                type={'info'}
-                                hidden={!(this.state.newNotificationEvent && this.state.newNotificationMsg)}
-                                onDismiss={this.newNotificationReset}
-                                timeout={this.props.dismissTimeout}
-                            />
-                        </div>
+				<div className='notification'>
+					<Crouton
+						id={Date.now()}
+						message={this.getNotificationFields(this.state.newNotificationMsg).eventKey +
+							' notification received. Check Event Center for more details.'}
+						type={'info'}
+						hidden={!(this.state.newNotificationEvent && this.state.newNotificationMsg)}
+						onDismiss={this.newNotificationReset}
+						timeout={this.props.dismissTimeout}
+					/>
+				</div>
 				<h1 className='title' data-open-close-icon={this.state.isOpen ? 'open' : 'closed'}
 					onClick={this.onClickToggleOpenClose.bind(this)}>
 					EVENT CENTER
diff --git a/skyquake/framework/widgets/skyquake_container/skyquakeApp.scss b/skyquake/framework/widgets/skyquake_container/skyquakeApp.scss
index 2869560..b6f688f 100644
--- a/skyquake/framework/widgets/skyquake_container/skyquakeApp.scss
+++ b/skyquake/framework/widgets/skyquake_container/skyquakeApp.scss
@@ -7,15 +7,18 @@
 
 .crouton {
     span {
-        white-space: pre;
+        /* white-space: pre;*/
     }
 }
 
 .skyquakeApp {
     display: -ms-flexbox;
+    display: -webkit-box;
     display: flex;
     -ms-flex-direction: column;
-        flex-direction: column;
+        -webkit-box-orient: vertical;
+        -webkit-box-direction: normal;
+            flex-direction: column;
     height: 100%;
     background: $gray-lightest;
     h1 {
@@ -28,6 +31,7 @@
     }
     .skyquakeNav {
         display: -ms-flexbox;
+        display: -webkit-box;
         display: flex;
         color:white;
         background:black;
@@ -36,11 +40,14 @@
         font-size:0.75rem;
         .secondaryNav {
             -ms-flex: 1 1 auto;
-                flex: 1 1 auto;
+                -webkit-box-flex: 1;
+                    flex: 1 1 auto;
             display: -ms-flexbox;
+            display: -webkit-box;
             display: flex;
             -ms-flex-pack: end;
-                justify-content: flex-end;
+                -webkit-box-pack: end;
+                    justify-content: flex-end;
         }
         .app {
             position:relative;
@@ -48,9 +55,11 @@
                 font-size:0.75rem;
                 border-right: 1px solid black;
                 display: -ms-flexbox;
+                display: -webkit-box;
                 display: flex;
                 -ms-flex-align: center;
-                    align-items: center;
+                    -webkit-box-align: center;
+                        align-items: center;
                 .oi {
                     padding-right: 0.5rem;
                 }
@@ -60,6 +69,11 @@
                 display:none;
                 z-index:2;
                 width: 100%;
+                &.project {
+                    a {
+                        text-transform:none;
+                    }
+                }
             }
             &:first-child{
                 h2 {
@@ -102,12 +116,13 @@
         &:before {
             content: '';
             height:1.85rem;
-            width:5.5rem;
-            /*margin:0 1rem;*/
-            /*padding:0 0.125rem;*/
+            width:2.5rem;
+            margin:auto 1rem;
+            padding:0 0.125rem;
+            /* background: url('../../style/img/header-logo.png') no-repeat center center white;*/
             /*background: url('../../style/img/svg/riftio_logo_white.svg') no-repeat center center;*/
             background: url('../../style/img/svg/osm-logo_color_rgb_white_text.svg') no-repeat center center;
-            background-size: contain;
+            background-size:contain;
         }
     }
     .skyquakeContainerWrapper {
@@ -127,7 +142,8 @@
         text-align:left;
         position:relative;
         -ms-flex: 1 0 auto;
-            flex: 1 0 auto;
+            -webkit-box-flex: 1;
+                flex: 1 0 auto;
         }
     }
     .corner-accent {
diff --git a/skyquake/framework/widgets/skyquake_container/skyquakeComponent.jsx b/skyquake/framework/widgets/skyquake_container/skyquakeComponent.jsx
index 893a4c1..cd489ac 100644
--- a/skyquake/framework/widgets/skyquake_container/skyquakeComponent.jsx
+++ b/skyquake/framework/widgets/skyquake_container/skyquakeComponent.jsx
@@ -7,7 +7,7 @@
         this.actions = context.flux.actions.global;
     }
     render(props) {
-        return <Component {...this.props} router={this.router} actions={this.actions} flux={this.context.flux} />
+        return <Component {...this.props} router={this.router} actions={this.actions} flux={this.context.flux}/>
     }
   }
   SkyquakeComponent.contextTypes = {
diff --git a/skyquake/framework/widgets/skyquake_container/skyquakeContainer.jsx b/skyquake/framework/widgets/skyquake_container/skyquakeContainer.jsx
index d83c836..5c4c97e 100644
--- a/skyquake/framework/widgets/skyquake_container/skyquakeContainer.jsx
+++ b/skyquake/framework/widgets/skyquake_container/skyquakeContainer.jsx
@@ -18,17 +18,23 @@
 import React from 'react';
 import AltContainer from 'alt-container';
 import Alt from './skyquakeAltInstance.js';
-import SkyquakeNav from './skyquakeNav.jsx';
+ import _cloneDeep from 'lodash/cloneDeep';
+import SkyquakeNav from '../skyquake_nav/skyquakeNav.jsx';
 import EventCenter from './eventCenter.jsx';
 import SkyquakeContainerActions from './skyquakeContainerActions.js'
 import SkyquakeContainerStore from './skyquakeContainerStore.js';
 // import Breadcrumbs from 'react-breadcrumbs';
 import Utils from 'utils/utils.js';
 import Crouton from 'react-crouton';
+import SkyquakeNotification from '../skyquake_notification/skyquakeNotification.jsx';
 import ScreenLoader from 'widgets/screen-loader/screenLoader.jsx';
+import {SkyquakeRBAC, isRBACValid} from 'widgets/skyquake_rbac/skyquakeRBAC.jsx';
+import ROLES from 'utils/roleConstants.js';
 import './skyquakeApp.scss';
 // import 'style/reset.css';
 import 'style/core.css';
+const PROJECT_ROLES = ROLES.PROJECT;
+const PLATFORM = ROLES.PLATFORM;
 export default class skyquakeContainer extends React.Component {
     constructor(props) {
         super(props);
@@ -39,13 +45,21 @@
         this.state.eventCenterIsOpen = false;
         this.state.currentPlugin = SkyquakeContainerStore.currentPlugin;
     }
-
+    getChildContext() {
+        return {
+          userProfile: this.state.user
+        };
+    }
+    getUserProfile() {
+        return this.state.user;
+    }
     componentWillMount() {
         let self = this;
 
         Utils.bootstrapApplication().then(function() {
             SkyquakeContainerStore.listen(self.listener);
             SkyquakeContainerStore.getNav();
+            SkyquakeContainerStore.getUserProfile();
             SkyquakeContainerStore.getEventStreams();
         });
 
@@ -82,12 +96,14 @@
     }
 
     render() {
-        const {displayNotification, notificationMessage, displayScreenLoader, notificationType, ...state} = this.state;
+        const {displayNotification, notificationData, displayScreenLoader,...state} = this.state;
+        const User = this.state.user || {};
+        const rbacValid = isRBACValid(User, [PLATFORM.SUPER, PLATFORM.ADMIN, PLATFORM.OPER]);
         var html;
-
+        let nav = _cloneDeep(this.state.nav);
         if (this.matchesLoginUrl()) {
             html = (
-                <AltContainer>
+                <AltContainer flux={Alt}>
                     <div className="skyquakeApp">
                         {this.props.children}
                     </div>
@@ -100,29 +116,33 @@
             html = (
                 <AltContainer flux={Alt}>
                     <div className="skyquakeApp wrap">
-                        <Crouton
-                            id={Date.now()}
-                            message={notificationMessage}
-                            type={notificationType}
-                            hidden={!(displayNotification && notificationMessage)}
+                        <SkyquakeNotification
+                            data={this.state.notificationData}
+                            visible={displayNotification}
                             onDismiss={SkyquakeContainerActions.hideNotification}
-                            timeout= {5000}
                         />
                         <ScreenLoader show={displayScreenLoader}/>
-                        <SkyquakeNav nav={this.state.nav}
+                        <SkyquakeNav nav={nav}
                             currentPlugin={this.state.currentPlugin}
-                            store={SkyquakeContainerStore} />
+                            currentUser={this.state.user.userId}
+                            currentProject={this.state.user.projectId}
+                            store={SkyquakeContainerStore}
+                            projects={this.state.projects} />
                         <div className="titleBar">
-                            <h1>{this.state.currentPlugin + tag}</h1>
+                            <h1>{(this.state.nav.name ? this.state.nav.name.replace('_', ' ').replace('-', ' ') : this.state.currentPlugin && this.state.currentPlugin.replace('_', ' ').replace('-', ' ')) + tag}</h1>
                         </div>
                         <div className={"application " + routeName}>
                             {this.props.children}
                         </div>
-                        <EventCenter className="eventCenter"
-                            notifications={this.state.notifications}
-                            newNotificationEvent={this.state.newNotificationEvent}
-                            newNotificationMsg={this.state.newNotificationMsg}
-                            onToggle={this.onToggle} />
+                        {
+                            rbacValid ?
+                                <EventCenter className="eventCenter"
+                                    notifications={this.state.notifications}
+                                    newNotificationEvent={this.state.newNotificationEvent}
+                                    newNotificationMsg={this.state.newNotificationMsg}
+                                    onToggle={this.onToggle} />
+                            : null
+                        }
                     </div>
                 </AltContainer>
             );
@@ -130,6 +150,9 @@
         return html;
     }
 }
+skyquakeContainer.childContextTypes = {
+  userProfile: React.PropTypes.object
+};
 skyquakeContainer.contextTypes = {
     router: React.PropTypes.object
   };
diff --git a/skyquake/framework/widgets/skyquake_container/skyquakeContainerActions.js b/skyquake/framework/widgets/skyquake_container/skyquakeContainerActions.js
index 773978b..1124658 100644
--- a/skyquake/framework/widgets/skyquake_container/skyquakeContainerActions.js
+++ b/skyquake/framework/widgets/skyquake_container/skyquakeContainerActions.js
@@ -1,5 +1,5 @@
 /*
- * 
+ *
  *   Copyright 2016 RIFT.IO Inc
  *
  *   Licensed under the Apache License, Version 2.0 (the "License");
@@ -25,9 +25,13 @@
 	'getEventStreamsSuccess',
 	'getEventStreamsError',
     //Notifications
+    'handleServerReportedError',
     'showNotification',
     'hideNotification',
     //Screen Loader
     'showScreenLoader',
-    'hideScreenLoader'
+    'hideScreenLoader',
+    'openProjectSocketSuccess',
+    'getUserProfileSuccess',
+    'selectActiveProjectSuccess'
 );
diff --git a/skyquake/framework/widgets/skyquake_container/skyquakeContainerSource.js b/skyquake/framework/widgets/skyquake_container/skyquakeContainerSource.js
index da7080f..f02e7ba 100644
--- a/skyquake/framework/widgets/skyquake_container/skyquakeContainerSource.js
+++ b/skyquake/framework/widgets/skyquake_container/skyquakeContainerSource.js
@@ -79,7 +79,7 @@
             remote: function(state, location, streamSource) {
                 return new Promise((resolve, reject) => {
                     $.ajax({
-                        url: '//' + window.location.hostname + ':' + window.location.port + '/socket-polling?api_server=' + API_SERVER,
+                        url: '//' + window.location.hostname + ':' + window.location.port + '/socket-polling',
                         type: 'POST',
                         beforeSend: Utils.addAuthorizationStub,
                         data: {
@@ -114,6 +114,79 @@
             success: SkyquakeContainerActions.openNotificationsSocketSuccess,
             error: SkyquakeContainerActions.openNotificationsSocketError
         }
+    },
+    openProjectSocket() {
+            return {
+                remote: function(state) {
+                    return new Promise(function(resolve, reject) {
+                        //If socket connection already exists, eat the request.
+                        if(state.socket) {
+                            return resolve(false);
+                        }
+                        $.ajax({
+                            url: '/socket-polling',
+                            type: 'POST',
+                            beforeSend: Utils.addAuthorizationStub,
+                            data: {
+                                url: '/project?api_server=' + API_SERVER
+                            },
+                            success: function(data, textStatus, jqXHR) {
+                                Utils.checkAndResolveSocketRequest(data, resolve, reject);
+                            }
+                            })
+                        .fail(function(xhr){
+                            //Authentication and the handling of fail states should be wrapped up into a connection class.
+                            Utils.checkAuthentication(xhr.status);
+                        });;
+                    });
+                },
+            success: SkyquakeContainerActions.openProjectSocketSuccess
+        }
+    },
+
+    getUserProfile() {
+        return {
+            remote: function(state, recordID) {
+                return new Promise(function(resolve, reject) {
+                    $.ajax({
+                        url: '/user-profile?api_server=' + API_SERVER,
+                        type: 'GET',
+                        beforeSend: Utils.addAuthorizationStub,
+                        success: function(data) {
+                            resolve(data);
+                        }
+                    }).fail(function(xhr) {
+                        //Authentication and the handling of fail states should be wrapped up into a connection class.
+                        Utils.checkAuthentication(xhr.status);
+                    });;
+                });
+            },
+            loading: Alt.actions.global.showScreenLoader,
+            success: SkyquakeContainerActions.getUserProfileSuccess
+        }
+    },
+
+    selectActiveProject() {
+        return {
+            remote: function(state, projectId) {
+                const {currentPlugin} = state;
+                const encodedProjectId = encodeURIComponent(projectId);
+                return new Promise(function(resolve, reject) {
+                    $.ajax({
+                        url: `/session/${encodedProjectId}?api_server=${API_SERVER}&app=${currentPlugin}`,
+                        type: 'PUT',
+                        beforeSend: Utils.addAuthorizationStub,
+                        success: function(data) {
+                            resolve(projectId);
+                        }
+                    }).fail(function(xhr) {
+                        //Authentication and the handling of fail states should be wrapped up into a connection class.
+                        Utils.checkAuthentication(xhr.status);
+                    });;
+                });
+            },
+            success: SkyquakeContainerActions.selectActiveProjectSuccess
+        }
     }
 }
 
diff --git a/skyquake/framework/widgets/skyquake_container/skyquakeContainerStore.js b/skyquake/framework/widgets/skyquake_container/skyquakeContainerStore.js
index 56ebdda..e9ba78e 100644
--- a/skyquake/framework/widgets/skyquake_container/skyquakeContainerStore.js
+++ b/skyquake/framework/widgets/skyquake_container/skyquakeContainerStore.js
@@ -20,19 +20,25 @@
 import Alt from './skyquakeAltInstance.js';
 import SkyquakeContainerSource from './skyquakeContainerSource.js';
 import SkyquakeContainerActions from './skyquakeContainerActions';
+let Utils = require('utils/utils.js');
 import _indexOf from 'lodash/indexOf';
+import _isEqual from 'lodash/isEqual';
 //Temporary, until api server is on same port as webserver
 import rw from 'utils/rw.js';
 
 var API_SERVER = rw.getSearchParams(window.location).api_server;
-var UPLOAD_SERVER = rw.getSearchParams(window.location).upload_server;
+const MAX_STORED_EVENTS = 20;
 
 class SkyquakeContainerStore {
     constructor() {
         this.currentPlugin = getCurrentPlugin();
         this.nav = {};
-        this.notifications = [];
+		let notificationList = null;
+		try {notificationList = JSON.parse(sessionStorage.getItem('notifications'));} catch (e) {}
+		this.notifications = notificationList || [];
         this.socket = null;
+        this.projects = null;
+        this.user = {};
         //Notification defaults
         this.notificationMessage = '';
         this.displayNotification = false;
@@ -96,6 +102,9 @@
         ws.onmessage = (socket) => {
             try {
                 var data = JSON.parse(socket.data);
+                if(data.hasOwnProperty('map')) {
+                    data = [];
+                }
                 if (!data.notification) {
                     console.warn('No notification in the received payload: ', data);
                 } else {
@@ -105,12 +114,14 @@
                         // newly appreared event.
                         // Add to the notifications list and setState
                         self.notifications.unshift(data.notification);
+                        (self.notifications.length > MAX_STORED_EVENTS) && self.notifications.pop();
                         self.setState({
                             newNotificationEvent: true,
                             newNotificationMsg: data.notification,
                             notifications: self.notifications,
                             isLoading: false
                         });
+                        sessionStorage.setItem('notifications', JSON.stringify(self.notifications));
                     }
                 }
             } catch(e) {
@@ -137,6 +148,7 @@
         console.log('Found streams: ', streams);
         let self = this;
 
+        streams &&
         streams['ietf-restconf-monitoring:streams'] &&
         streams['ietf-restconf-monitoring:streams']['stream'] &&
         streams['ietf-restconf-monitoring:streams']['stream'].map((stream) => {
@@ -162,23 +174,53 @@
         })
     }
 
+    openProjectSocketSuccess = (connection) => {
+        var self = this;
+        var ws = window.multiplexer.channel(connection);
+        if (!connection) return;
+        self.setState({
+            socket: ws.ws,
+            channelId: connection
+        });
+        ws.onmessage = function(socket) {
+            try {
+                var data = JSON.parse(socket.data);
+                Utils.checkAuthentication(data.statusCode, function() {
+                    self.closeSocket();
+                });
+                if (!data.project || !_isEqual(data.project, self.projects)) {
+                    let user = self.user;
+                    user.projects = data.project;
+                    self.setState({
+                        user: user,
+                        projects: data.project || {}
+                    });
+                }
+            } catch(e) {
+                console.log('HIT an exception in openProjectSocketSuccess', e);
+            }
+        };
+    }
+    getUserProfileSuccess = (user) => {
+        this.alt.actions.global.hideScreenLoader.defer();
+        this.setState({user})
+    }
+    selectActiveProjectSuccess = (projectId) => {
+        let user = this.user;
+        user.projectId = projectId;
+        this.setState({user});
+        window.location.href = window.location.origin;
+    }
     //Notifications
-    showNotification = (data) => {
-        let state = {
-                displayNotification: true,
-                notificationMessage: data,
-                notificationType: 'error',
-                displayScreenLoader: false
-            }
-        if(typeof(data) == 'string') {
-
-        } else {
-            state.notificationMessage = data.msg;
-            if(data.type) {
-                state.notificationType = data.type;
-            }
-        }
-        this.setState(state);
+    handleServerReportedError = (result) => {
+        this.hideScreenLoader();
+        this.alt.actions.global.showNotification.defer(result);
+    }
+    showNotification = (notificationData) => {
+        this.setState({
+            notificationData,
+            displayNotification: true
+        })
     }
     hideNotification = () => {
         this.setState({
@@ -211,7 +253,7 @@
             if (k != currentPlugin)  {
                 nav[k].routes.map(function(route, i) {
                     if (API_SERVER) {
-                        route.route = '/' + k + '/index.html?api_server=' + API_SERVER + '&upload_server=' + UPLOAD_SERVER + '#' + route.route;
+                        route.route = '/' + k + '/index.html?api_server=' + API_SERVER + '#' + route.route;
                     } else {
                         route.route = '/' + k + '/#' + route.route;
                     }
diff --git a/skyquake/framework/widgets/skyquake_container/skyquakeNav.jsx b/skyquake/framework/widgets/skyquake_container/skyquakeNav.jsx
deleted file mode 100644
index 8814a18..0000000
--- a/skyquake/framework/widgets/skyquake_container/skyquakeNav.jsx
+++ /dev/null
@@ -1,227 +0,0 @@
-/*
- * 
- *   Copyright 2016 RIFT.IO Inc
- *
- *   Licensed under the Apache License, Version 2.0 (the "License");
- *   you may not use this file except in compliance with the License.
- *   You may obtain a copy of the License at
- *
- *       http://www.apache.org/licenses/LICENSE-2.0
- *
- *   Unless required by applicable law or agreed to in writing, software
- *   distributed under the License is distributed on an "AS IS" BASIS,
- *   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- *   See the License for the specific language governing permissions and
- *   limitations under the License.
- *
- */
-
-import React from 'react';
-import { Link } from 'react-router';
-import Utils from 'utils/utils.js';
-import Crouton from 'react-crouton';
-import 'style/common.scss';
-
-//Temporary, until api server is on same port as webserver
-import rw from 'utils/rw.js';
-
-var API_SERVER = rw.getSearchParams(window.location).api_server;
-var UPLOAD_SERVER = rw.getSearchParams(window.location).upload_server;
-
-//
-// Internal classes/functions
-//
-
-class LogoutAppMenuItem extends React.Component {
-    handleLogout() {
-        Utils.clearAuthentication();
-    }
-    render() {
-        return (
-            <div className="app">
-                <h2>
-                    <a onClick={this.handleLogout}>
-                        Logout
-                    </a>
-                </h2>
-            </div>
-        );
-    }
-}
-
-
-//
-// Exported classes and functions
-//
-
-//
-/**
- * Skyquake Nav Component. Provides navigation functionality between all plugins
- */
-export default class skyquakeNav extends React.Component {
-    constructor(props) {
-        super(props);
-        this.state = {};
-        this.state.validateErrorEvent = 0;
-        this.state.validateErrorMsg = '';
-    }
-    validateError = (msg) => {
-        this.setState({
-            validateErrorEvent: true,
-            validateErrorMsg: msg
-        });
-    }
-    validateReset = () => {
-        this.setState({
-            validateErrorEvent: false
-        });
-    }
-    returnCrouton = () => {
-        return <Crouton
-            id={Date.now()}
-            message={this.state.validateErrorMsg}
-            type={"error"}
-            hidden={!(this.state.validateErrorEvent && this.state.validateErrorMsg)}
-            onDismiss={this.validateReset}
-        />;
-    }
-    render() {
-        let html;
-        html = (
-                <div>
-                {this.returnCrouton()}
-            <nav className="skyquakeNav">
-                {buildNav.call(this, this.props.nav, this.props.currentPlugin)}
-            </nav>
-
-            </div>
-        )
-        return html;
-    }
-}
-skyquakeNav.defaultProps = {
-    nav: {}
-}
-/**
- * Returns a React Component
- * @param  {object} link  Information about the nav link
- * @param  {string} link.route Hash route that the SPA should resolve
- * @param  {string} link.name Link name to be displayed
- * @param  {number} index index of current array item
- * @return {object} component A React LI Component
- */
-//This should be extended to also make use of internal/external links and determine if the link should refer to an outside plugin or itself.
-export function buildNavListItem (k, link, index) {
-    let html = false;
-    if (link.type == 'external') {
-        this.hasSubNav[k] = true;
-        html = (
-            <li key={index}>
-                {returnLinkItem(link)}
-            </li>
-        );
-    }
-    return html;
-}
-
-/**
- * Builds a link to a React Router route or a new plugin route.
- * @param  {object} link Routing information from nav object.
- * @return {object}  component   returns a react component that links to a new route.
- */
-export function returnLinkItem(link) {
-    let ref;
-    let route = link.route;
-    if(link.isExternal) {
-        ref = (
-            <a href={route}>{link.label}</a>
-        )
-    } else {
-        if(link.path && link.path.replace(' ', '') != '') {
-            route = link.path;
-        }
-        if(link.query) {
-            let query = {};
-            query[link.query] = '';
-            route = {
-                pathname: route,
-                query: query
-            }
-        }
-        ref = (
-           <Link to={route}>
-                {link.label}
-            </Link>
-        )
-    }
-    return ref;
-}
-
-/**
- * Constructs nav for each plugin, along with available subnavs
- * @param  {array} nav List returned from /nav endpoint.
- * @return {array}     List of constructed nav element for each plugin
- */
-export function buildNav(nav, currentPlugin) {
-    let navList = [];
-    let navListHTML = [];
-    let secondaryNav = [];
-    let self = this;
-    self.hasSubNav = {};
-    let secondaryNavHTML = (
-        <div className="secondaryNav" key="secondaryNav">
-        {secondaryNav}
-            <LogoutAppMenuItem />
-        </div>
-    )
-    for (let k in nav) {
-        if (nav.hasOwnProperty(k)) {
-            self.hasSubNav[k] = false;
-            let header = null;
-            let navClass = "app";
-            let routes = nav[k].routes;
-            let navItem = {};
-            //Primary plugin title and link to dashboard.
-            let route;
-            let NavList;
-            if (API_SERVER) {
-                route = routes[0].isExternal ? '/' + k + '/index.html?api_server=' + API_SERVER + '' + '&upload_server=' + UPLOAD_SERVER + '' : '';
-            } else {
-                route = routes[0].isExternal ? '/' + k + '/' : '';
-            }
-            let dashboardLink = returnLinkItem({
-                isExternal: routes[0].isExternal,
-                pluginName: nav[k].pluginName,
-                label: nav[k].label || k,
-                route: route
-            });
-            if (nav[k].pluginName == currentPlugin) {
-                navClass += " active";
-            }
-            NavList = nav[k].routes.map(buildNavListItem.bind(self, k));
-            navItem.priority = nav[k].priority;
-            navItem.order = nav[k].order;
-            navItem.html = (
-                <div key={k} className={navClass}>
-                    <h2>{dashboardLink} {self.hasSubNav[k] ? <span className="oi" data-glyph="caret-bottom"></span> : ''}</h2>
-                    <ul className="menu">
-                        {
-                            NavList
-                        }
-                    </ul>
-                </div>
-            );
-            navList.push(navItem)
-        }
-    }
-    //Sorts nav items by order and returns only the markup
-    navListHTML = navList.sort((a,b) => a.order - b.order).map(function(n) {
-        if((n.priority  < 2)){
-            return n.html;
-        } else {
-            secondaryNav.push(n.html);
-        }
-    });
-    navListHTML.push(secondaryNavHTML);
-    return navListHTML;
-}
diff --git a/skyquake/framework/widgets/skyquake_container/skyquakeRouter.jsx b/skyquake/framework/widgets/skyquake_container/skyquakeRouter.jsx
index fc3231d..7d8daa5 100644
--- a/skyquake/framework/widgets/skyquake_container/skyquakeRouter.jsx
+++ b/skyquake/framework/widgets/skyquake_container/skyquakeRouter.jsx
@@ -22,10 +22,24 @@
 from 'react-router';
 import SkyquakeContainer from 'widgets/skyquake_container/skyquakeContainer.jsx';
 
+/**
+ * This is the Skyquake App wrapper that all plugins use to be a Skyquake plugin. This 
+ * is not a react component although it does return a react component that will be the
+ * app.
+ * This function will also set the title into the <head>
+ * 
+ * @export
+ * @param {any} config 
+ * @param {any} context 
+ * @returns a react component to be rendered manually.
+ */
 export default function(config, context) {
     let routes = [];
     let index = null;
     let components = null;
+    
+    document.title = config.name || "OpenMANO";
+
     if (config && config.routes) {
         routes =  buildRoutes(config.routes)
         function buildRoutes(routes) {
@@ -83,7 +97,6 @@
             },
             childRoutes: routes
         }
-
         return((
             <Router history={hashHistory} routes={rootRoute}>
             </Router>
diff --git a/skyquake/framework/widgets/skyquake_nav/skyquakeNav.jsx b/skyquake/framework/widgets/skyquake_nav/skyquakeNav.jsx
new file mode 100644
index 0000000..7986d5e
--- /dev/null
+++ b/skyquake/framework/widgets/skyquake_nav/skyquakeNav.jsx
@@ -0,0 +1,377 @@
+/*
+ *
+ *   Copyright 2016 RIFT.IO Inc
+ *
+ *   Licensed under the Apache License, Version 2.0 (the "License");
+ *   you may not use this file except in compliance with the License.
+ *   You may obtain a copy of the License at
+ *
+ *       http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *   Unless required by applicable law or agreed to in writing, software
+ *   distributed under the License is distributed on an "AS IS" BASIS,
+ *   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *   See the License for the specific language governing permissions and
+ *   limitations under the License.
+ *
+ */
+
+import React from 'react';
+import { Link } from 'react-router';
+import Utils from 'utils/utils.js';
+import Crouton from 'react-crouton';
+import 'style/common.scss';
+
+import './skyquakeNav.scss';
+import SelectOption from '../form_controls/selectOption.jsx';
+import { FormSection } from '../form_controls/formControls.jsx';
+import { isRBACValid, SkyquakeRBAC } from 'widgets/skyquake_rbac/skyquakeRBAC.jsx';
+
+//Temporary, until api server is on same port as webserver
+import rw from 'utils/rw.js';
+
+var API_SERVER = rw.getSearchParams(window.location).api_server;
+var DOWNLOAD_SERVER = rw.getSearchParams(window.location).dev_download_server;
+
+//
+// Internal classes/functions
+//
+
+class SelectProject extends React.Component {
+    constructor(props) {
+        super(props);
+    }
+    selectProject(e) {
+        let value = JSON.parse(e.currentTarget.value);
+        // console.log('selected project', value)
+    }
+    render() {
+        let props = this.props;
+        let hasProjects = props.projects;
+        let userAssignedProjects = hasProjects && (props.projects.length > 0)
+        return (
+            <div className="app">
+                <h2>
+                    <a style={{textTransform:'none'}}>
+                        {
+                            hasProjects ?
+                            (userAssignedProjects ? 'PROJECT: ' + props.currentProject : 'No Projects Assigned')
+                            : 'Projects Loading...'
+                        }
+                    </a>
+                    {
+                        userAssignedProjects ? <span className="oi" data-glyph="caret-bottom"></span> : null
+                    }
+                </h2>
+                {
+                    userAssignedProjects ?
+                        <ul className="project menu">
+                            {
+                                props.projects.map(function (p, k) {
+                                    return <li key={k} onClick={props.onSelectProject.bind(null, p.name)}><a>{p.name}</a></li>
+                                })
+                            }
+                        </ul>
+                        : null
+                }
+            </div>
+        )
+    }
+}
+
+/*
+
+ <SelectOption
+                        options={projects}
+                        value={currentValue}
+                        defaultValue={currentValue}
+                        onChange={props.onSelectProject}
+                        className="projectSelect" />
+
+ */
+
+
+class UserNav extends React.Component {
+    constructor(props) {
+        super(props);
+    }
+    handleLogout() {
+        Utils.clearAuthentication();
+    }
+    selectProject(e) {
+        let value = JSON.parse(e.currentTarget.value)
+        // console.log('selected project', value)
+    }
+    render() {
+        let props = this.props;
+        let userProfileLink = null;
+        this.props.nav['user_management'] && this.props.nav['user_management'].routes.map((r) => {
+            if (r.unique) {
+                userProfileLink = r;
+            }
+        })
+        return !userProfileLink ? null : (
+            <div className="app">
+                <h2 className="username">
+                    USERNAME: {returnLinkItem(userProfileLink, props.currentUser)}
+                    <span className="oi" data-glyph="caret-bottom"></span>
+                </h2>
+                <ul className="menu">
+                    <li>
+                        {returnLinkItem(userProfileLink, "My Profile")}
+                    </li>
+                    <li>
+                        <a onClick={this.handleLogout}>
+                            Logout
+                        </a>
+                    </li>
+                </ul>
+            </div>
+        )
+    }
+}
+
+UserNav.defaultProps = {
+    projects: [
+
+    ]
+}
+
+//
+// Exported classes and functions
+//
+
+//
+/**
+ * Skyquake Nav Component. Provides navigation functionality between all plugins
+ */
+export default class skyquakeNav extends React.Component {
+    constructor(props) {
+        super(props);
+        this.state = {};
+        this.state.validateErrorEvent = 0;
+        this.state.validateErrorMsg = '';
+    }
+    componentDidMount() {
+        this.props.store.openProjectSocket();
+    }
+    validateError = (msg) => {
+        this.setState({
+            validateErrorEvent: true,
+            validateErrorMsg: msg
+        });
+    }
+    validateReset = () => {
+        this.setState({
+            validateErrorEvent: false
+        });
+    }
+    returnCrouton = () => {
+        return <Crouton
+            id={Date.now()}
+            message={this.state.validateErrorMsg}
+            type={"error"}
+            hidden={!(this.state.validateErrorEvent && this.state.validateErrorMsg)}
+            onDismiss={this.validateReset}
+        />;
+    }
+    render() {
+        let html;
+        html = (
+            <div>
+                {this.returnCrouton()}
+                <nav className="skyquakeNav">
+                    {buildNav.call(this, this.props.nav, this.props.currentPlugin, this.props)}
+                </nav>
+
+            </div>
+        )
+        return html;
+    }
+}
+skyquakeNav.defaultProps = {
+    nav: {}
+}
+skyquakeNav.contextTypes = {
+    userProfile: React.PropTypes.object
+};
+/**
+ * Returns a React Component
+ * @param  {object} link  Information about the nav link
+ * @param  {string} link.route Hash route that the SPA should resolve
+ * @param  {string} link.name Link name to be displayed
+ * @param  {number} index index of current array item
+ * @return {object} component A React LI Component
+ */
+//This should be extended to also make use of internal/external links and determine if the link should refer to an outside plugin or itself.
+export function buildNavListItem(k, link, index) {
+    let html = false;
+    if (link.type == 'external') {
+        this.hasSubNav[k] = true;
+        html = (
+            <li key={index}>
+                {returnLinkItem(link)}
+            </li>
+        );
+    }
+    return html;
+}
+
+/**
+ * Builds a link to a React Router route or a new plugin route.
+ * @param  {object} link Routing information from nav object.
+ * @return {object}  component   returns a react component that links to a new route.
+ */
+export function returnLinkItem(link, label) {
+    let ref;
+    let route = link.route;
+    if (link.isExternal) {
+        ref = (
+            <a href={route}>{label || link.label}</a>
+        )
+    } else {
+        if (link.path && link.path.replace(' ', '') != '') {
+            route = link.path;
+        }
+        if (link.query) {
+            let query = {};
+            query[link.query] = '';
+            route = {
+                pathname: route,
+                query: query
+            }
+        }
+        ref = (
+            <Link to={route}>
+                {label || link.label}
+            </Link>
+        )
+    }
+    return ref;
+}
+
+
+
+
+/**
+ * Constructs nav for each plugin, along with available subnavs
+ * @param  {array} nav List returned from /nav endpoint.
+ * @return {array}     List of constructed nav element for each plugin
+ */
+export function buildNav(navData, currentPlugin, props) {
+    let navList = [];
+    let navListHTML = [];
+    let secondaryNav = [];
+    let adminNav = [];
+    //For monitoring when admin panel is active
+    let adminNavList = [];
+    let self = this;
+    const User = this.context.userProfile;
+    //The way the nav is sorting needs to be refactored.
+    let navArray = navData && Object.keys(navData).sort((a, b) => navData[a].order - navData[b].order)
+    self.hasSubNav = {};
+    for (let i = 0; i < navArray.length; i++) {
+        let k = navArray[i];
+        if (navData.hasOwnProperty(k)) {
+            self.hasSubNav[k] = false;
+            let header = null;
+            let navClass = "app";
+            let routes = navData[k].routes;
+            let navItem = {};
+            //Primary plugin title and link to dashboard.
+            let route;
+            let NavList;
+            if (API_SERVER) {
+                route = routes[0].isExternal ?
+                    '/' + k + '/index.html?api_server=' + API_SERVER + '' + (DOWNLOAD_SERVER ? '&dev_download_server=' + DOWNLOAD_SERVER : '')
+                    : '';
+            } else {
+                route = routes[0].isExternal ? '/' + k + '/' : '';
+            }
+            if(navData[k].route) {
+                route = route + navData[k].route;
+            }
+            let dashboardLink = returnLinkItem({
+                isExternal: routes[0].isExternal,
+                pluginName: navData[k].pluginName,
+                label: navData[k].label || k,
+                route: route
+            });
+            let shouldAllow = navData[k].allow || ['*'];
+            if (navData[k].pluginName == currentPlugin) {
+                navClass += " active";
+            }
+            NavList = navData[k].routes.filter((r) => {
+                const User = self.context.userProfile;
+                const shouldAllow = r.allow || ['*'];
+                return isRBACValid(User, shouldAllow);
+            }).map(buildNavListItem.bind(self, k));
+            navItem.priority = navData[k].priority;
+            navItem.order = navData[k].order;
+            if (navData[k].admin_link) {
+                if (isRBACValid(User, shouldAllow)) {
+                    adminNavList.push(navData[k].pluginName);
+                    adminNav.push((
+                        <li key={navData[k].pluginName}>
+                            {dashboardLink}
+                        </li>
+                    ))
+                }
+            } else {
+                if (isRBACValid(User, shouldAllow)) {
+                    navItem.html = (
+                        <div key={k} className={navClass}>
+                            <h2>{dashboardLink} {self.hasSubNav[k] ? <span className="oi" data-glyph="caret-bottom"></span> : ''}</h2>
+                            <ul className="menu">
+                                {NavList}
+                            </ul>
+                        </div>
+                    );
+                }
+                navList.push(navItem)
+            }
+
+        }
+    }
+
+    //Sorts nav items by order and returns only the markup
+    navListHTML = navList.map(function (n) {
+        if ((n.priority < 2)) {
+            return n.html;
+        } else {
+            secondaryNav.push(n.html);
+        }
+    });
+    if (adminNav.length) {
+            navListHTML.push(
+                <div key="Adminstration" className={"app " + (adminNavList.indexOf(currentPlugin) > -1 ? 'active' : '')}>
+                    <h2>
+                        <a>
+                            ADMINISTRATION
+                        </a>
+                        <span className="oi" data-glyph="caret-bottom"></span>
+                    </h2>
+                    <ul className="menu">
+                        {
+                            adminNav
+                        }
+                    </ul>
+                </div>
+            );
+    }
+    let secondaryNavHTML = (
+        <div className="secondaryNav" key="secondaryNav">
+            {secondaryNav}
+            <SelectProject
+                onSelectProject={props.store.selectActiveProject}
+                projects={props.projects}
+                currentProject={props.currentProject} />
+            <UserNav
+                currentUser={props.currentUser}
+                nav={navData} />
+        </div>
+    )
+    // console.log("app admin " + (adminNavList.indexOf(currentPlugin) > -1 ? 'active' : ''))
+    navListHTML.push(secondaryNavHTML);
+    return navListHTML;
+}
diff --git a/skyquake/framework/widgets/skyquake_nav/skyquakeNav.scss b/skyquake/framework/widgets/skyquake_nav/skyquakeNav.scss
new file mode 100644
index 0000000..8a312ff
--- /dev/null
+++ b/skyquake/framework/widgets/skyquake_nav/skyquakeNav.scss
@@ -0,0 +1,127 @@
+@import '../../style/_colors.scss';
+.active {
+        background-color: $brand-blue!important;
+        border-color: $brand-blue!important;
+        color: #fff!important
+    }
+    .skyquakeNav {
+        display: -ms-flexbox;
+        display: -webkit-box;
+        display: flex;
+        color:white;
+        background:black;
+        position:relative;
+        z-index: 10;
+        font-size:0.75rem;
+        padding-right:1rem;
+        .secondaryNav {
+            -ms-flex: 1 1 auto;
+            -webkit-box-flex: 1;
+            flex: 1 1 auto;
+            display: -ms-flexbox;
+            display: -webkit-box;
+            display: flex;
+            -ms-flex-pack: end;
+            -webkit-box-pack: end;
+            justify-content: flex-end;
+
+            .username a{
+                text-transform: none;
+            }
+        }
+        .app {
+            display: -ms-flexbox;
+            display: block;
+            position:relative;
+            margin: auto 0.5rem;
+            h2 {
+                font-size:0.75rem;
+                border-right: 1px solid black;
+                display: -ms-flexbox;
+                display: -webkit-box;
+                display: flex;
+                -ms-flex-pack: start;
+                -ms-flex-pack: start;
+                -webkit-box-pack: start;
+                        justify-content: flex-start;
+                -ms-flex-align: center;
+                -webkit-box-align: center;
+                        align-items: center;
+                .oi {
+                    padding-right: 0.5rem;
+                }
+            }
+            .menu {
+                position:absolute;
+                display:none;
+                z-index:2;
+                width: 100%;
+                li {
+                    text-align:left;
+                }
+            }
+            &:first-child{
+                h2 {
+                    border-left: 1px solid black;
+                }
+            }
+            &:hover {
+                a {
+                    color:$brand-blue-light;
+                    cursor:pointer;
+                }
+                .menu {
+                    display:block;
+                    background:black;
+                    a {
+                        color:white;
+                    }
+                    li:hover {
+                        a {
+                            color:$brand-blue-light;
+                        }
+                    }
+                }
+            }
+            &.active {
+                color:white;
+                background:black;
+                a {
+                    color:white;
+                }
+            }
+        }
+        a{
+            display:block;
+            padding:0.5rem 1rem;
+            text-decoration:none;
+            text-transform:uppercase;
+            text-align:left;
+            color:white;
+        }
+        &:before {
+            content: '';
+            min-width: 5.5rem;
+            margin: 0.125rem 1rem;
+            /*background: url('../../style/img/svg/riftio_logo_white.svg') no-repeat center center;*/
+            background: url('../../style/img/svg/osm-logo_color_rgb_white_text.svg') no-repeat center center;
+            background-size: contain;
+        }
+        .userSection {
+            display:-ms-flexbox;
+            display:-webkit-box;
+            display:flex;
+            -ms-flex-align:center;
+            -webkit-box-align:center;
+                    align-items:center;
+            padding-left: 1rem;
+            text-transform:uppercase;
+            text-align: left;
+            .projectSelect {
+                padding: 0 0.5rem;
+                font-size: 1rem;
+                /* min-width: 75%;*/
+                height: 25px;
+            }
+        }
+    }
diff --git a/skyquake/framework/widgets/skyquake_notification/netConfErrors.js b/skyquake/framework/widgets/skyquake_notification/netConfErrors.js
new file mode 100644
index 0000000..cde7b5d
--- /dev/null
+++ b/skyquake/framework/widgets/skyquake_notification/netConfErrors.js
@@ -0,0 +1,58 @@
+const NETCONF_ERRORS = {
+    'in-use' : {
+        description: 'The request requires a resource that already is in use.'
+    },
+    'invalid-value' : {
+        description: 'The request specifies an unacceptable value for one or more parameters.'
+    },
+    'too-big' : {
+        description: 'The request or response (that would be generated) is too large for the implementation to handle.'
+    },
+    'missing-attribute' : {
+        description: 'An expected attribute is missing.'
+    },
+    'bad-attribute' : {
+        description: 'An attribute value is not correct; e.g., wrong type, out of range, pattern mismatch.'
+    },
+    'unknown-attribute' : {
+        description: 'An unexpected attribute is present.'
+    },
+    'missing-element' : {
+        description: 'An expected element is missing.'
+    },
+    'bad-element' : {
+        description: 'An element value is not correct; e.g., wrong type, out of range, pattern mismatch.'
+    },
+    'unknown-element' : {
+        description: 'An unexpected element is present.'
+    },
+    'unknown-namespace' : {
+        description: 'An unexpected namespace is present.'
+    },
+    'access-denied' : {
+        description: 'Access to the requested protocol operation or data model is denied because authorization failed.'
+    },
+    'lock-denied' : {
+        description: 'Access to the requested lock is denied because the lock is currently held by another entity.'
+    },
+    'resource-denied' : {
+        description: 'Request could not be completed because of insufficient resources.'
+    },
+    'rollback-failed' : {
+        description: 'Request to roll back some configuration change (via rollback-on-error or <discard-changes> operations) was not completed for some reason.'
+    },
+    'data-exists' : {
+        description: 'Request could not be completed because the relevant data model content already exists.  For example, a "create" operation was attempted on data that already exists.'
+    },
+    'data-missing' : {
+        description: 'Request could not be completed because the relevant data model content does not exist.  For example, a "delete" operation was attempted on data that does not exist.'
+    },
+    'operation-not-supported' : {
+        description: 'Request could not be completed because the requested operation is not supported by this implementation.'
+    },
+    'operation-failed' : {
+        description: 'Request could not be completed because the requested operation failed for some reason not covered by any other error condition.'
+    }
+}
+
+export default NETCONF_ERRORS;
diff --git a/skyquake/framework/widgets/skyquake_notification/skyquakeNotification.jsx b/skyquake/framework/widgets/skyquake_notification/skyquakeNotification.jsx
new file mode 100644
index 0000000..c8ce157
--- /dev/null
+++ b/skyquake/framework/widgets/skyquake_notification/skyquakeNotification.jsx
@@ -0,0 +1,89 @@
+import React from 'react';
+import Crouton from 'react-crouton';
+import NETCONF_ERRORS from './netConfErrors.js';
+
+class SkyquakeNotification extends React.Component {
+    constructor(props) {
+        super(props);
+        this.state = {};
+        this.state.displayNotification = props.visible;
+        this.state.notificationMessage = '';
+        this.state.notificationType = 'error';
+    }
+    componentWillReceiveProps(props) {
+        if(props.visible) {
+            this.processMessage(props.data);
+        } else {
+            this.setState({displayNotification: props.visible});
+        }
+    }
+    buildNetconfError(data) {
+        let error = data;
+        try {
+            let info = JSON.parse(data);
+            let rpcError = info.body || info.errorMessage.body || info.errorMessage.error;
+            if (rpcError && typeof rpcError === 'string') {
+                const index = rpcError.indexOf('{');
+                if (index >= 0) {
+                    rpcError = JSON.parse(rpcError.substr(index));
+                } else {
+                    return rpcError;
+                }
+            }
+            if (!rpcError) {
+                return error;
+            }
+            info = rpcError["rpc-reply"]["rpc-error"];
+            let errorTag = info['error-tag']
+            error = `
+                ${NETCONF_ERRORS[errorTag] && NETCONF_ERRORS[errorTag].description || 'Unknown NETCONF Error'}
+                PATH: ${info['error-path']}
+                INFO: ${JSON.stringify(info['error-info'])}
+            `
+        } catch (e) {
+            console.log('Unexpected string sent to buildNetconfError: ', e);
+        }
+        return error;
+    }
+    processMessage(data) {
+        let state = {
+                displayNotification: true,
+                notificationMessage: data,
+                notificationType: 'error',
+                displayScreenLoader: false
+            }
+        if(typeof(data) == 'string') {
+            //netconf errors will be json strings
+            state.notificationMessage = this.buildNetconfError(data);
+        } else {
+            let message = data.msg || '';
+            if(data.type) {
+                state.notificationType = data.type;
+            }
+            if(data.rpcError){
+                message += " " + this.buildNetconfError(data.rpcError);
+            }
+            state.notificationMessage = message;
+        }
+        console.log('NOTIFICATION: ', state.notificationMessage)
+        this.setState(state);
+    }
+    render() {
+        const {displayNotification, notificationMessage, notificationType, ...state} = this.state;
+        return (
+            <Crouton
+                id={Date.now()}
+                message={notificationMessage}
+                type={notificationType}
+                hidden={!(displayNotification && notificationMessage)}
+                onDismiss={this.props.onDismiss}
+                timeout={10000}
+            />
+        )
+    }
+}
+SkyquakeNotification.defaultProps = {
+    data: {},
+    onDismiss: function(){}
+}
+export default SkyquakeNotification;
diff --git a/skyquake/framework/widgets/skyquake_rbac/skyquakeRBAC.jsx b/skyquake/framework/widgets/skyquake_rbac/skyquakeRBAC.jsx
new file mode 100644
index 0000000..9097b4e
--- /dev/null
+++ b/skyquake/framework/widgets/skyquake_rbac/skyquakeRBAC.jsx
@@ -0,0 +1,119 @@
+/*
+ *
+ *   Copyright 2016 RIFT.IO Inc
+ *
+ *   Licensed under the Apache License, Version 2.0 (the "License");
+ *   you may not use this file except in compliance with the License.
+ *   You may obtain a copy of the License at
+ *
+ *       http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *   Unless required by applicable law or agreed to in writing, software
+ *   distributed under the License is distributed on an "AS IS" BASIS,
+ *   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *   See the License for the specific language governing permissions and
+ *   limitations under the License.
+ *
+ */
+
+
+import React from 'react';
+import ROLES from 'utils/roleConstants.js';
+const PLATFORM = ROLES.PLATFORM;
+
+export function isRBACValid(User, allow, Project){
+  const UserData = User && User.data;
+  if(UserData) {
+      const PlatformRole = UserData.platform.role;
+      const isPlatformSuper = PlatformRole[PLATFORM.SUPER];
+      const isPlatformAdmin = PlatformRole[PLATFORM.ADMIN];
+      const isPlatformOper = PlatformRole[PLATFORM.OPER];
+      const hasRoleAccess =  checkForRoleAccess(UserData.project[Project || User.projectId], PlatformRole, allow)//false//(this.props.roles.indexOf(userProfile.projectRole) > -1)
+      if (isPlatformSuper) {
+        return true;
+      } else {
+        if (hasRoleAccess) {
+          return true;
+        }
+      }
+    }
+  return false;
+}
+
+export default class SkyquakeRBAC extends React.Component {
+    constructor(props, context) {
+        super(props);
+    }
+    render() {
+      const User = this.context.userProfile;
+      const UserData = User.data;
+      const Project = this.props.project;
+      let HTML = null;
+      // If user object has platform property then it has been populated by the back end.
+      if(isRBACValid(User, this.props.allow, Project)) {
+        HTML = this.props.children;
+      }
+      return (<div className={this.props.className} style={this.props.style}>{HTML}</div>)
+    }
+}
+SkyquakeRBAC.defaultProps = {
+  allow: [],
+  project: false
+}
+SkyquakeRBAC.contextTypes = {
+  userProfile: React.PropTypes.object
+}
+
+function checkForRoleAccess(project, PlatformRole, allow) {
+    if (allow.indexOf('*') > -1) return true;
+    for (let i = 0; i<allow.length; i++) {
+      if((project && project.role[allow[i]])|| PlatformRole[allow[i]]) {
+        return true
+      }
+    }
+    return false;
+  }
+
+
+
+// export default function(Component) {
+//   class SkyquakeRBAC extends React.Component {
+//     constructor(props, context) {
+//         super(props);
+//             }
+//     render(props) {
+//       console.log(this.context.userProfile)
+//       const User = this.context.userProfile.data;
+//       // If user object has platform property then it has been populated by the back end.
+//       if(User) {
+//         const PlatformRole = User.platform.role;
+//         const HTML = <Component {...this.props} router={this.router} actions={this.actions} flux={this.context.flux}/>;
+//         const isPlatformSuper = PlatformRole[PLATFORM.SUPER];
+//         const isPlatformAdmin = PlatformRole[PLATFORM.ADMIN];
+//         const isPlatformOper = PlatformRole[PLATFORM.OPER];
+//         const hasRoleAccess =  false//(this.props.roles.indexOf(userProfile.projectRole) > -1)
+//         if (isPlatformSuper || isPlatformOper || isPlatformAdmin) {
+//           return HTML
+//         } else {
+//           if (hasRoleAccess) {
+//             return HTML
+//           } else {
+//             return null;
+//           }
+//         }
+//       }
+//       else {
+//         return null;
+
+//       }
+//     }
+//   }
+//   SkyquakeRBAC.defaultProps = {
+
+//   }
+//   SkyquakeRBAC.contextTypes = {
+//     userProfile: React.PropTypes.object,
+//     allowedRoles: []
+//   };
+//   return SkyquakeRBAC;
+// }