Attaching components to the tutorial

Change-Id: Ic24ec04872864421dc2b503f2e1c0cfed2852b0b
diff --git a/views/ngXosViews/UITutorial/bower.json b/views/ngXosViews/UITutorial/bower.json
index 009304e..0778e98 100644
--- a/views/ngXosViews/UITutorial/bower.json
+++ b/views/ngXosViews/UITutorial/bower.json
@@ -15,7 +15,8 @@
     "tests"
   ],
   "dependencies": {
-    "josh.js": "teone/josh.js#~0.2.10"
+    "josh.js": "teone/josh.js#~0.2.10",
+    "angular-ui-ace": "bower"
   },
   "devDependencies": {
     "jquery": "2.1.4",
diff --git a/views/ngXosViews/UITutorial/src/css/main.css b/views/ngXosViews/UITutorial/src/css/main.css
index d008cb5..cc63179 100644
--- a/views/ngXosViews/UITutorial/src/css/main.css
+++ b/views/ngXosViews/UITutorial/src/css/main.css
@@ -1,5 +1,7 @@
 #xosUITutorial {
   width: 100%; }
+  #xosUITutorial .ace_editor {
+    height: 200px; }
   #xosUITutorial js-shell {
     display: block; }
     #xosUITutorial js-shell #shell-panel {
diff --git a/views/ngXosViews/UITutorial/src/index.html b/views/ngXosViews/UITutorial/src/index.html
index 00f0a42..17b625b 100644
--- a/views/ngXosViews/UITutorial/src/index.html
+++ b/views/ngXosViews/UITutorial/src/index.html
@@ -17,13 +17,14 @@
 <!-- bower:js -->
 <script src="vendor/jquery/dist/jquery.js"></script>
 <script src="vendor/lodash/lodash.js"></script>
-<script src="vendor/josh.js/js/killring.js"></script>
 <script src="vendor/josh.js/js/readline.js"></script>
 <script src="vendor/josh.js/js/history.js"></script>
+<script src="vendor/josh.js/js/killring.js"></script>
 <script src="vendor/josh.js/js/input.js"></script>
 <script src="vendor/josh.js/js/pathhandler.js"></script>
 <script src="vendor/josh.js/js/shell.js"></script>
 <script src="vendor/angular/angular.js"></script>
+<script src="vendor/angular-ui-ace/ui-ace.js"></script>
 <script src="vendor/angular-mocks/angular-mocks.js"></script>
 <script src="vendor/angular-ui-router/release/angular-ui-router.js"></script>
 <script src="vendor/angular-cookies/angular-cookies.js"></script>
@@ -36,13 +37,16 @@
 <script src="vendor/angular-recursion/angular-recursion.js"></script>
 <!-- endbower -->
 <!-- endjs -->
+<script src="vendor/ace-builds/src-min-noconflict/ace.js"></script>
 <!-- inject:js -->
 <script src="/vendor/ng-xos-lib/dist/ngXosHelpers.min.js"></script>
 <script src="/.tmp/main.js"></script>
 <script src="/.tmp/templateHandler.js"></script>
 <script src="/.tmp/responseHandler.js"></script>
+<script src="/.tmp/playCmd.js"></script>
 <script src="/.tmp/lessonsInfo.js"></script>
 <script src="/.tmp/learnCmd.js"></script>
 <script src="/.tmp/exploreCmd.js"></script>
 <script src="/.tmp/errorHandler.js"></script>
+<script src="/.tmp/codeToString.js"></script>
 <!-- endinject -->
\ No newline at end of file
diff --git a/views/ngXosViews/UITutorial/src/js/codeToString.js b/views/ngXosViews/UITutorial/src/js/codeToString.js
new file mode 100644
index 0000000..b8ae242
--- /dev/null
+++ b/views/ngXosViews/UITutorial/src/js/codeToString.js
@@ -0,0 +1,65 @@
+(function () {
+  angular.module('xos.UITutorial')
+  .service('codeToString', function(){
+    this.toString = code => {
+      if(angular.isArray(code)){
+        return code.map(item => this.toString(item));
+      }
+      else if(angular.isObject(code)){
+        let tmp = {};
+        Object.keys(code).forEach(key => {
+          tmp[key] = this.toString(code[key])
+        });
+        return tmp;
+      }
+      else{
+        return code.toString().split('\n').join('').replace(/ +(?= )/gmi, '');
+      }
+    };
+
+    this.toCode = string => {
+      let code;
+
+      try {
+        code = JSON.parse(string);
+      }
+      catch(e){
+        code = string;
+      }
+      
+      if(angular.isArray(code)){
+        return code.map(item => this.toCode(item));
+      }
+      else if(angular.isObject(code)){
+        let tmp = {};
+        Object.keys(code).forEach(key => {
+          tmp[key] = this.toCode(code[key])
+        });
+        return tmp;
+      }
+      else{
+        if(!angular.isNumber(code) && code.indexOf('function') !== -1){
+          try {
+            return function(){
+              // create a closure to host our arguments
+              var func = new Function(`return ${code}`);
+              
+              // invoke the original function passing arguments
+              func()(...arguments);
+            }
+          }
+          catch(e){
+            // in this case it is a string
+            return code;
+          }
+        }
+        else if(Number.isNaN(code)){
+          return parseFloat(code);
+        }
+        return code;
+      }
+
+      return code;
+    };
+  });
+})();
\ No newline at end of file
diff --git a/views/ngXosViews/UITutorial/src/js/main.js b/views/ngXosViews/UITutorial/src/js/main.js
index 627a560..a686fe6 100644
--- a/views/ngXosViews/UITutorial/src/js/main.js
+++ b/views/ngXosViews/UITutorial/src/js/main.js
@@ -4,7 +4,8 @@
   'ngResource',
   'ngCookies',
   'ui.router',
-  'xos.helpers'
+  'xos.helpers',
+  'ui.ace'
 ])
 .config(($stateProvider) => {
   $stateProvider
@@ -16,14 +17,14 @@
 .config(function($httpProvider){
   $httpProvider.interceptors.push('NoHyperlinks');
 })
-.directive('jsShell', function(TemplateHandler){
+.directive('jsShell', function($rootScope, TemplateHandler, codeToString){
   return {
     restrict: 'E',
     scope: {},
     bindToController: true,
     controllerAs: 'vm',
     templateUrl: 'templates/js-shell.tpl.html',
-    controller: function(ExploreCmd,LearnCmd){
+    controller: function(ExploreCmd, PlayCmd, LearnCmd){
       var history = new Josh.History({ key: 'jsshell.history'});
       this.shell = Josh.Shell({history: history});
 
@@ -58,8 +59,43 @@
           }));
         }
       });
+      
+      this.shell.setCommandHandler('play', {
+        exec: (cmd, args, done) => {
+          PlayCmd.setup(this.shell);
+          done(TemplateHandler.instructions({
+            title: `You can now play with UI components!`,
+            messages: [
+              `Use <code>component list</code> to list all the available component and <code>component {componentName}</code> to startusing it.`,
+              `An example command is <code>component xosTable</code>`
+            ]
+          }));
+        }
+      });
 
       this.shell.activate();
+
+      this.componentScope = null;
+
+      $rootScope.$on('uiTutorial.attachScope', (e, scope) => {
+        this.componentScope = {
+          config: JSON.stringify(codeToString.toString(scope.config), null, 2),
+          data: JSON.stringify(codeToString.toString(scope.data), null, 2)
+        };
+      });
+
+      this.applyScope = (scope) => {
+        
+        // let a = codeToString.toCode(scope.config);
+        // console.log(a);
+        const newScope = {
+          config: codeToString.toCode(scope.config),
+          data: eval(`(${scope.data})`)
+        };
+
+        $rootScope.$emit('uiTutorial.applyScope', newScope);
+      }
+
     }
   };
 });
\ No newline at end of file
diff --git a/views/ngXosViews/UITutorial/src/js/playCmd.js b/views/ngXosViews/UITutorial/src/js/playCmd.js
new file mode 100644
index 0000000..0ed81c9
--- /dev/null
+++ b/views/ngXosViews/UITutorial/src/js/playCmd.js
@@ -0,0 +1,109 @@
+(function () {
+  'use strict';
+  angular.module('xos.UITutorial')
+  .service('PlayCmd', function($compile, $rootScope, _, ErrorHandler){
+
+    // TODO investigate if we can load directives from an app
+    const components = [
+      {
+        name: 'xosTable',
+        template: '<xos-table config="config" data="data"></xos-table>',
+        scope: {
+          config: {
+            columns: [
+              {label: 'Name', prop: 'name'},
+              {label: 'Age', prop: 'age'}
+            ]
+          },
+          data: [
+            {name: 'Jhon', age: 23},
+            {name: 'Mike', age: 24}
+          ]
+        }
+      },
+      {
+        name: 'xosForm',
+        template: '<xos-form config="config" ng-model="data"></xos-form>',
+        scope: {
+          config: {
+            fields: {
+              name: {
+                type: 'text'
+              },
+              age: {
+                type: 'number'
+              }
+            },
+            actions: [
+              {
+                label: 'Print',
+                cb: (model) => {
+                  console.log(model);
+                }
+              }
+            ]
+          },
+          data: {name: 'Jhon', age: 23}
+        }
+      }
+    ];
+
+    this.componentCompletion = (cmd, arg, line, done) => {
+      const componentsName = components.map(c => c.name);
+      return done(this.shell.bestMatch(arg, componentsName));
+    };
+
+    this.componentExec = (cmd, args, done) => {
+      const targetComponent = args[0];
+
+      if(!targetComponent){
+        return ErrorHandler.print(`Component "${targetComponent}" does not exists`, done);
+      }
+
+      this.attachComponent(targetComponent, done);
+    };
+
+    this.getComponentsDetails = (componentName, components) => {
+      return _.find(components, {name: componentName});
+    };
+
+    this.attachComponent = (targetComponent, done) => {
+      this.scope = $rootScope.$new();
+      targetComponent = this.getComponentsDetails(targetComponent, components);
+
+      angular.extend(this.scope, targetComponent.scope);
+
+      $rootScope.$emit('uiTutorial.attachScope', this.scope);
+
+      const directive = $compile(targetComponent.template)(this.scope);
+      const container = $('#directive-container');
+      container.html('');
+      container.append( directive );
+      done('Component added');
+    };
+
+    $rootScope.$on('uiTutorial.applyScope', (e, scope) => {
+      this.scope.config = scope.config;
+      this.scope.data = scope.data;
+    });
+
+    this.setup = (shell) => {
+      this.shell = shell;
+      shell.setCommandHandler('component', {
+        exec: this.componentExec,
+        completion: this.componentCompletion
+      });
+
+      // activate listener to enable/disable shell
+      $('.component-container').click((e) => {
+        this.shell.deactivate();
+      });
+
+      $('#shell-panel').click((e) => {
+        this.shell.activate();
+      });
+    };
+
+    
+  });
+})();
\ No newline at end of file
diff --git a/views/ngXosViews/UITutorial/src/sass/main.scss b/views/ngXosViews/UITutorial/src/sass/main.scss
index f3b0496..a6e8003 100644
--- a/views/ngXosViews/UITutorial/src/sass/main.scss
+++ b/views/ngXosViews/UITutorial/src/sass/main.scss
@@ -19,6 +19,9 @@
 
 #xosUITutorial {
   width: 100%;
+
+  .ace_editor { height: 200px; }
+
   js-shell {
     display: block;
 
diff --git a/views/ngXosViews/UITutorial/src/templates/js-shell.tpl.html b/views/ngXosViews/UITutorial/src/templates/js-shell.tpl.html
index 8799fd4..aae18db 100644
--- a/views/ngXosViews/UITutorial/src/templates/js-shell.tpl.html
+++ b/views/ngXosViews/UITutorial/src/templates/js-shell.tpl.html
@@ -3,4 +3,29 @@
     Type <code>help</code> or hit <code>TAB</code> for a list of commands.
   </div>
   <div id="shell-view"></div>
-</div>
\ No newline at end of file
+</div>
+
+<section class="component-container" ng-show="vm.componentScope">
+  <div class="row">
+    <div class="col-xs-6">
+      <label>Config:</label>
+    </div>
+    <div class="col-xs-6">
+      <label>data:</label>
+    </div>
+  </div>
+  <div class="row">
+    <div class="col-xs-6">
+      <div ng-model="vm.componentScope.config" ui-ace></div>
+    </div>
+    <div class="col-xs-6">
+      <div ng-model="vm.componentScope.data" ui-ace></div>
+    </div>
+  </div>
+  <div class="row">
+    <div class="col-xs-12 text-right">
+      <a href="" ng-click="vm.applyScope(vm.componentScope)" class="btn btn-success">Apply Changes</a>
+    </div>
+  </div>
+  <div id="directive-container"></div>
+</section>
\ No newline at end of file