You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@brooklyn.apache.org by he...@apache.org on 2018/09/13 16:21:48 UTC

[1/5] brooklyn-ui git commit: fix more generics issue in UI wizard, for entity spec selection

Repository: brooklyn-ui
Updated Branches:
  refs/heads/master d1581af9f -> 3fffb6094


fix more generics issue in UI wizard, for entity spec selection


Project: http://git-wip-us.apache.org/repos/asf/brooklyn-ui/repo
Commit: http://git-wip-us.apache.org/repos/asf/brooklyn-ui/commit/0afa7e77
Tree: http://git-wip-us.apache.org/repos/asf/brooklyn-ui/tree/0afa7e77
Diff: http://git-wip-us.apache.org/repos/asf/brooklyn-ui/diff/0afa7e77

Branch: refs/heads/master
Commit: 0afa7e77feeed35190ee10a43a3fd186d524f2f7
Parents: d1581af
Author: Alex Heneveld <al...@cloudsoftcorp.com>
Authored: Thu Sep 13 11:19:44 2018 +0100
Committer: Alex Heneveld <al...@cloudsoftcorp.com>
Committed: Thu Sep 13 11:19:44 2018 +0100

----------------------------------------------------------------------
 .../spec-editor/spec-editor.directive.js        | 14 ++++++++-----
 .../app/components/util/model/entity.model.js   | 22 ++++++++++++++------
 2 files changed, 25 insertions(+), 11 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/brooklyn-ui/blob/0afa7e77/ui-modules/blueprint-composer/app/components/spec-editor/spec-editor.directive.js
----------------------------------------------------------------------
diff --git a/ui-modules/blueprint-composer/app/components/spec-editor/spec-editor.directive.js b/ui-modules/blueprint-composer/app/components/spec-editor/spec-editor.directive.js
index 8272d01..6f37077 100644
--- a/ui-modules/blueprint-composer/app/components/spec-editor/spec-editor.directive.js
+++ b/ui-modules/blueprint-composer/app/components/spec-editor/spec-editor.directive.js
@@ -20,7 +20,7 @@ import angular from 'angular';
 import onEnter from 'brooklyn-ui-utils/on-enter/index';
 import autoGrow from 'brooklyn-ui-utils/autogrow/index';
 import blurOnEnter from 'brooklyn-ui-utils/blur-on-enter/index';
-import {EntityFamily} from '../util/model/entity.model';
+import {EntityFamily, baseType} from '../util/model/entity.model';
 import {Dsl} from '../util/model/dsl.model';
 import {Issue, ISSUE_LEVEL} from '../util/model/issue.model';
 import {RESERVED_KEYS, DSL_ENTITY_SPEC} from '../providers/blueprint-service.provider';
@@ -357,6 +357,13 @@ export function specEditorDirective($rootScope, $templateCache, $injector, $sani
             return issues.some(issue => issue.level === ISSUE_LEVEL.ERROR) ? 'badge-danger' : 'badge-warning';
         };
 
+        function baseType(s) {
+            if (s && s.indexOf("<")>=0) {
+                s = s.substring(0, s.indexOf("<")); 
+            }
+            return s;
+        }
+        
         function getConfigWidgetModeInternal(item, val) {
             if (angular.element($document[0].activeElement).hasClass("form-control") && item.widgetMode) {
                 // don't switch mode in mid-edit, e.g. if you are manually typing $brooklyn:component("x").config("y")
@@ -371,10 +378,7 @@ export function specEditorDirective($rootScope, $templateCache, $injector, $sani
             }
             
             if (!scope.defined(val)) val = scope.config[item.name];
-            let type = item.type;
-            if (type && type.indexOf("<")>=0) {
-                type = type.substring(0, type.indexOf("<")); 
-            }
+            let type = baseType(item.type);
             
             // if actual value's type does not match declared type,
             // e.g. object is a map when declared type is object or string or something else,

http://git-wip-us.apache.org/repos/asf/brooklyn-ui/blob/0afa7e77/ui-modules/blueprint-composer/app/components/util/model/entity.model.js
----------------------------------------------------------------------
diff --git a/ui-modules/blueprint-composer/app/components/util/model/entity.model.js b/ui-modules/blueprint-composer/app/components/util/model/entity.model.js
index ed0ea81..e09f7c6 100644
--- a/ui-modules/blueprint-composer/app/components/util/model/entity.model.js
+++ b/ui-modules/blueprint-composer/app/components/util/model/entity.model.js
@@ -644,6 +644,13 @@ function isCluster() {
     }).length > 0;
 }
 
+export function baseType(s) {
+    if (s && s.indexOf("<")>=0) {
+        s = s.substring(0, s.indexOf("<")); 
+    }
+    return s;
+}
+
 /**
  * Returns a map of <configkey> => Entity of all spec {Entity} defined in the configuration
  * @returns {*}
@@ -653,7 +660,7 @@ function getClusterMemberspecEntities() {
         return {};
     }
     return MISC_DATA.get(this).get('config')
-        .filter((config)=>(config.type === 'org.apache.brooklyn.api.entity.EntitySpec'))
+        .filter((config)=>(baseType(config.type) === EntityFamily.SPEC.superType))
         .reduce((acc, config)=> {
             if (CONFIG.get(this).has(config.name)) {
                 acc[config.name] = CONFIG.get(this).get(config.name)[DSL.ENTITY_SPEC];
@@ -661,7 +668,7 @@ function getClusterMemberspecEntities() {
             return acc;
         }, {});
 }
-
+       
 /**
  * Returns the first memberspec that matches the given predicate
  *
@@ -674,10 +681,13 @@ function getClusterMemberspecEntity(predicate = ()=>(true)) {
     }
 
     return MISC_DATA.get(this).get('config')
-        .filter((config)=>(config.type === 'org.apache.brooklyn.api.entity.EntitySpec'))
+        .filter((config)=>(baseType(config.type) === EntityFamily.SPEC.superType))
         .reduce((acc, config)=> {
-            if (CONFIG.get(this).has(config.name) && predicate(config, CONFIG.get(this).get(config.name)[DSL.ENTITY_SPEC])) {
-                return CONFIG.get(this).get(config.name)[DSL.ENTITY_SPEC];
+            if (CONFIG.get(this).has(config.name)) {
+                let entityV = CONFIG.get(this).get(config.name)[DSL.ENTITY_SPEC];
+                if (entityV && predicate(config, entityV)) {
+                    return entityV;
+                }
             }
             return acc;
         }, undefined);
@@ -688,7 +698,7 @@ function setClusterMemberspecEntity(key, entity) {
         return this;
     }
     let definition = MISC_DATA.get(this).get('config')
-        .filter((config)=>(config.type === 'org.apache.brooklyn.api.entity.EntitySpec' && config.name === key));
+        .filter((config)=>(baseType(config.type) === EntityFamily.SPEC.superType && config.name === key));
     if (definition.length !== 1) {
         return this;
     }


[5/5] brooklyn-ui git commit: This closes #69

Posted by he...@apache.org.
This closes #69


Project: http://git-wip-us.apache.org/repos/asf/brooklyn-ui/repo
Commit: http://git-wip-us.apache.org/repos/asf/brooklyn-ui/commit/3fffb609
Tree: http://git-wip-us.apache.org/repos/asf/brooklyn-ui/tree/3fffb609
Diff: http://git-wip-us.apache.org/repos/asf/brooklyn-ui/diff/3fffb609

Branch: refs/heads/master
Commit: 3fffb6094da9b63aadd90faaf8f30dbabe9644fd
Parents: d1581af 1d2705c
Author: Alex Heneveld <al...@cloudsoftcorp.com>
Authored: Thu Sep 13 17:21:39 2018 +0100
Committer: Alex Heneveld <al...@cloudsoftcorp.com>
Committed: Thu Sep 13 17:21:39 2018 +0100

----------------------------------------------------------------------
 .../suggestion-dropdown.html                    | 10 ++-
 .../custom-config-widget/suggestion-dropdown.js |  8 +-
 .../spec-editor/spec-editor.directive.js        | 83 ++++++++++----------
 .../spec-editor/spec-editor.template.html       | 60 +++++++-------
 .../app/components/util/model/entity.model.js   | 22 ++++--
 5 files changed, 98 insertions(+), 85 deletions(-)
----------------------------------------------------------------------



[2/5] brooklyn-ui git commit: custom widgets should not copy scope values from parent

Posted by he...@apache.org.
custom widgets should not copy scope values from parent

but rather reference them explicitly.  copying scope means if the parent changes that var, which it does e.g. for `config`, the child's scope value becomes disconnected.


Project: http://git-wip-us.apache.org/repos/asf/brooklyn-ui/repo
Commit: http://git-wip-us.apache.org/repos/asf/brooklyn-ui/commit/cd2ac803
Tree: http://git-wip-us.apache.org/repos/asf/brooklyn-ui/tree/cd2ac803
Diff: http://git-wip-us.apache.org/repos/asf/brooklyn-ui/diff/cd2ac803

Branch: refs/heads/master
Commit: cd2ac803f1fab71505b7400f86e9de630ba48314
Parents: 0afa7e7
Author: Alex Heneveld <al...@cloudsoftcorp.com>
Authored: Thu Sep 13 11:48:27 2018 +0100
Committer: Alex Heneveld <al...@cloudsoftcorp.com>
Committed: Thu Sep 13 11:48:27 2018 +0100

----------------------------------------------------------------------
 .../custom-config-widget/suggestion-dropdown.html   | 16 +++++++++-------
 .../custom-config-widget/suggestion-dropdown.js     |  2 +-
 .../components/spec-editor/spec-editor.directive.js |  8 --------
 3 files changed, 10 insertions(+), 16 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/brooklyn-ui/blob/cd2ac803/ui-modules/blueprint-composer/app/components/custom-config-widget/suggestion-dropdown.html
----------------------------------------------------------------------
diff --git a/ui-modules/blueprint-composer/app/components/custom-config-widget/suggestion-dropdown.html b/ui-modules/blueprint-composer/app/components/custom-config-widget/suggestion-dropdown.html
index 82ecfea..6c4a807 100644
--- a/ui-modules/blueprint-composer/app/components/custom-config-widget/suggestion-dropdown.html
+++ b/ui-modules/blueprint-composer/app/components/custom-config-widget/suggestion-dropdown.html
@@ -30,30 +30,32 @@
     <span class="label-rhs-buttons">
         <span class="spacer"> </span>
         <span class="custom-widget-enable-button custom-widget-active">
-            <i class="fa fa-fw fa-sun-o" ng-click="toggleCustomConfigWidgetMode(item, false)" aria-hidden="true" title="{{getCustomConfigWidgetModeTitle(item)}} [{{item.name}}]"></i>
-            <span class="sr-only">{{getCustomConfigWidgetModeTitle(item)}} [{{item.name}}]</span>
+            <i class="fa fa-fw fa-sun-o" ng-click="specEditor.toggleCustomConfigWidgetMode(item, false)" aria-hidden="true" title="{{specEditor.getCustomConfigWidgetModeTitle(item)}} [{{item.name}}]"></i>
+            <span class="sr-only">{{specEditor.getCustomConfigWidgetModeTitle(item)}} [{{item.name}}]</span>
         </span>
     </span>
 
-    <div class="control-value" ng-class="{ 'inline-control': item.widgetMode==='boolean', 'unset': !defined(config[item.name]), 'has-default': defined(item.defaultValue) && item.defaultValue!=null }">
+    <div class="control-value" ng-class="{ 'inline-control': item.widgetMode==='boolean', 'unset': !specEditor.defined(specEditor.config[item.name]), 'has-default': specEditor.defined(item.defaultValue) && item.defaultValue!=null }">
         <div class="hide-when-collapsed extra-label" ng-if="params['label-expanded']">
             {{ params['label-expanded'] || "" }}
         </div>
         <div class="config-flex-row">
             <span class="hide-when-expanded inline-label extra-label" ng-if="params['label-collapsed']">{{ params['label-collapsed'] || "" }}</span>
             <div class="input-group">
-                <textarea ng-model="config[item.name]" class="form-control" name="{{item.name}}" id="{{item.name}}" auto-grow
-                          placeholder="{{defined(config[item.name]) ? null : item.defaultValue=='' || item.defaultValue==null ? '(empty)' : item.defaultValue}}"
-                          ng-focus="recordFocus(item)" on-enter="advanceOutToFormGroupInPanel"
+                <textarea ng-model="specEditor.config[item.name]" class="form-control" name="{{item.name}}" id="{{item.name}}" auto-grow
+                          placeholder="{{specEditor.defined(config[item.name]) ? null : item.defaultValue=='' || item.defaultValue==null ? '(empty)' : item.defaultValue}}"
+                          ng-focus="specEditor.recordFocus(item)" on-enter="specEditor.advanceOutToFormGroupInPanel"
                           uib-typeahead="suggestion.value for suggestion in getSuggestions() | filter:{value:$viewValue}"
                           typeahead-min-length=0
                           typeahead-template-url="SuggestionTemplate.html"></textarea>
-                <span class="input-group-btn dsl-wizard-button" ng-if="isDslWizardButtonAllowed(item.name)">
+                <span class="input-group-btn dsl-wizard-button" ng-if="specEditor.isDslWizardButtonAllowed(item.name)">
                     <a ui-sref="main.graphical.edit.dsl({entityId: model._id, for: item.name})" class="btn btn-default" title="Open in DSL editor"><i class="fa fa-bolt"></i></a>
                 </span>
             </div>
         </div>
     </div>
+    
+    <small ng-repeat="issue in specEditor.model.issues | filter:{ref: item.name}:true" class="help-block issue" ng-bind-html="issue.message"></small>
 
 </div>
 

http://git-wip-us.apache.org/repos/asf/brooklyn-ui/blob/cd2ac803/ui-modules/blueprint-composer/app/components/custom-config-widget/suggestion-dropdown.js
----------------------------------------------------------------------
diff --git a/ui-modules/blueprint-composer/app/components/custom-config-widget/suggestion-dropdown.js b/ui-modules/blueprint-composer/app/components/custom-config-widget/suggestion-dropdown.js
index 3dfa860..1667d98 100644
--- a/ui-modules/blueprint-composer/app/components/custom-config-widget/suggestion-dropdown.js
+++ b/ui-modules/blueprint-composer/app/components/custom-config-widget/suggestion-dropdown.js
@@ -39,7 +39,7 @@ export function suggestionDropdownDirective($rootScope) {
     };
 
     function link(scope) {
-        scope.$parent.copyScopeForCustomConfigWidget(scope);
+        scope.specEditor = scope.$parent;
         scope.getSuggestions = () => {
             var result = [];
             if (scope.params['suggestion-values']) {

http://git-wip-us.apache.org/repos/asf/brooklyn-ui/blob/cd2ac803/ui-modules/blueprint-composer/app/components/spec-editor/spec-editor.directive.js
----------------------------------------------------------------------
diff --git a/ui-modules/blueprint-composer/app/components/spec-editor/spec-editor.directive.js b/ui-modules/blueprint-composer/app/components/spec-editor/spec-editor.directive.js
index 6f37077..870a472 100644
--- a/ui-modules/blueprint-composer/app/components/spec-editor/spec-editor.directive.js
+++ b/ui-modules/blueprint-composer/app/components/spec-editor/spec-editor.directive.js
@@ -558,14 +558,6 @@ export function specEditorDirective($rootScope, $templateCache, $injector, $sani
             }
             return widgetMetadata.enabled ? "Use standard widget" : "Use custom widget";
         };
-        scope.copyScopeForCustomConfigWidget = (descendantScope) => {
-            descendantScope.toggleCustomConfigWidgetMode = scope.toggleCustomConfigWidgetMode;
-            descendantScope.getCustomConfigWidgetModeTitle = scope.getCustomConfigWidgetModeTitle;
-            descendantScope.defined = scope.defined;
-            descendantScope.config = scope.config;
-            descendantScope.state = scope.state;
-            descendantScope.copyScopeForCustomConfigWidget = scope.copyScopeForCustomConfigWidget;
-        };
         scope.getCustomConfigWidgetTemplate = (item) => {
             var widgetMetadata = scope.state.config.customConfigWidgetMetadata[item.name];
             var widgetName = $sanitize(widgetMetadata.widget || '--no-widget--');


[4/5] brooklyn-ui git commit: minor tidy as per PR review

Posted by he...@apache.org.
minor tidy as per PR review


Project: http://git-wip-us.apache.org/repos/asf/brooklyn-ui/repo
Commit: http://git-wip-us.apache.org/repos/asf/brooklyn-ui/commit/1d2705c4
Tree: http://git-wip-us.apache.org/repos/asf/brooklyn-ui/tree/1d2705c4
Diff: http://git-wip-us.apache.org/repos/asf/brooklyn-ui/diff/1d2705c4

Branch: refs/heads/master
Commit: 1d2705c4d06b153408ad1e7983b93a3838d9a236
Parents: b8d7743
Author: Alex Heneveld <al...@cloudsoftcorp.com>
Authored: Thu Sep 13 13:50:06 2018 +0100
Committer: Alex Heneveld <al...@cloudsoftcorp.com>
Committed: Thu Sep 13 13:50:06 2018 +0100

----------------------------------------------------------------------
 .../app/components/spec-editor/spec-editor.directive.js       | 7 -------
 1 file changed, 7 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/brooklyn-ui/blob/1d2705c4/ui-modules/blueprint-composer/app/components/spec-editor/spec-editor.directive.js
----------------------------------------------------------------------
diff --git a/ui-modules/blueprint-composer/app/components/spec-editor/spec-editor.directive.js b/ui-modules/blueprint-composer/app/components/spec-editor/spec-editor.directive.js
index 4ff2213..a6b19e8 100644
--- a/ui-modules/blueprint-composer/app/components/spec-editor/spec-editor.directive.js
+++ b/ui-modules/blueprint-composer/app/components/spec-editor/spec-editor.directive.js
@@ -365,13 +365,6 @@ export function specEditorDirective($rootScope, $templateCache, $injector, $sani
             return issues.some(issue => issue.level === ISSUE_LEVEL.ERROR) ? 'badge-danger' : 'badge-warning';
         };
 
-        function baseType(s) {
-            if (s && s.indexOf("<")>=0) {
-                s = s.substring(0, s.indexOf("<")); 
-            }
-            return s;
-        }
-        
         function getConfigWidgetModeInternal(item, val) {
             if (angular.element($document[0].activeElement).hasClass("form-control") && item.widgetMode) {
                 // don't switch mode in mid-edit, e.g. if you are manually typing $brooklyn:component("x").config("y")


[3/5] brooklyn-ui git commit: put functions on controller so there is less data we need to pass to the custom widget

Posted by he...@apache.org.
put functions on controller so there is less data we need to pass to the custom widget

avoids entirely the use of $parent, and follows better the angular way


Project: http://git-wip-us.apache.org/repos/asf/brooklyn-ui/repo
Commit: http://git-wip-us.apache.org/repos/asf/brooklyn-ui/commit/b8d77434
Tree: http://git-wip-us.apache.org/repos/asf/brooklyn-ui/tree/b8d77434
Diff: http://git-wip-us.apache.org/repos/asf/brooklyn-ui/diff/b8d77434

Branch: refs/heads/master
Commit: b8d774344cbcb8b188198e4440dc4b5279faa792
Parents: cd2ac80
Author: Alex Heneveld <al...@cloudsoftcorp.com>
Authored: Thu Sep 13 13:47:10 2018 +0100
Committer: Alex Heneveld <al...@cloudsoftcorp.com>
Committed: Thu Sep 13 13:47:10 2018 +0100

----------------------------------------------------------------------
 .../suggestion-dropdown.html                    |  8 +--
 .../custom-config-widget/suggestion-dropdown.js |  8 ++-
 .../spec-editor/spec-editor.directive.js        | 68 +++++++++++---------
 .../spec-editor/spec-editor.template.html       | 60 ++++++++---------
 4 files changed, 78 insertions(+), 66 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/brooklyn-ui/blob/b8d77434/ui-modules/blueprint-composer/app/components/custom-config-widget/suggestion-dropdown.html
----------------------------------------------------------------------
diff --git a/ui-modules/blueprint-composer/app/components/custom-config-widget/suggestion-dropdown.html b/ui-modules/blueprint-composer/app/components/custom-config-widget/suggestion-dropdown.html
index 6c4a807..56395a6 100644
--- a/ui-modules/blueprint-composer/app/components/custom-config-widget/suggestion-dropdown.html
+++ b/ui-modules/blueprint-composer/app/components/custom-config-widget/suggestion-dropdown.html
@@ -35,15 +35,15 @@
         </span>
     </span>
 
-    <div class="control-value" ng-class="{ 'inline-control': item.widgetMode==='boolean', 'unset': !specEditor.defined(specEditor.config[item.name]), 'has-default': specEditor.defined(item.defaultValue) && item.defaultValue!=null }">
+    <div class="control-value" ng-class="{ 'inline-control': item.widgetMode==='boolean', 'unset': !defined(config[item.name]), 'has-default': defined(item.defaultValue) && item.defaultValue!=null }">
         <div class="hide-when-collapsed extra-label" ng-if="params['label-expanded']">
             {{ params['label-expanded'] || "" }}
         </div>
         <div class="config-flex-row">
             <span class="hide-when-expanded inline-label extra-label" ng-if="params['label-collapsed']">{{ params['label-collapsed'] || "" }}</span>
             <div class="input-group">
-                <textarea ng-model="specEditor.config[item.name]" class="form-control" name="{{item.name}}" id="{{item.name}}" auto-grow
-                          placeholder="{{specEditor.defined(config[item.name]) ? null : item.defaultValue=='' || item.defaultValue==null ? '(empty)' : item.defaultValue}}"
+                <textarea ng-model="config[item.name]" class="form-control" name="{{item.name}}" id="{{item.name}}" auto-grow
+                          placeholder="{{defined(config[item.name]) ? null : item.defaultValue=='' || item.defaultValue==null ? '(empty)' : item.defaultValue}}"
                           ng-focus="specEditor.recordFocus(item)" on-enter="specEditor.advanceOutToFormGroupInPanel"
                           uib-typeahead="suggestion.value for suggestion in getSuggestions() | filter:{value:$viewValue}"
                           typeahead-min-length=0
@@ -55,7 +55,7 @@
         </div>
     </div>
     
-    <small ng-repeat="issue in specEditor.model.issues | filter:{ref: item.name}:true" class="help-block issue" ng-bind-html="issue.message"></small>
+    <small ng-repeat="issue in model.issues | filter:{ref: item.name}:true" class="help-block issue" ng-bind-html="issue.message"></small>
 
 </div>
 

http://git-wip-us.apache.org/repos/asf/brooklyn-ui/blob/b8d77434/ui-modules/blueprint-composer/app/components/custom-config-widget/suggestion-dropdown.js
----------------------------------------------------------------------
diff --git a/ui-modules/blueprint-composer/app/components/custom-config-widget/suggestion-dropdown.js b/ui-modules/blueprint-composer/app/components/custom-config-widget/suggestion-dropdown.js
index 1667d98..c32bc72 100644
--- a/ui-modules/blueprint-composer/app/components/custom-config-widget/suggestion-dropdown.js
+++ b/ui-modules/blueprint-composer/app/components/custom-config-widget/suggestion-dropdown.js
@@ -29,17 +29,21 @@ export default MODULE_NAME;
 
 export function suggestionDropdownDirective($rootScope) {
     return {
+        require: "^^specEditor",  // only intended for use in spec editor, and calls functions on that controller
         restrict: 'E',
         scope: {
             item: '=',
             params: '=',
+            config: '=',
+            model: '=',
         },
         template: template,
         link: link,
     };
 
-    function link(scope) {
-        scope.specEditor = scope.$parent;
+    function link(scope, element, attrs, specEditor) {
+        scope.specEditor = specEditor;
+        scope.defined = specEditor.defined;
         scope.getSuggestions = () => {
             var result = [];
             if (scope.params['suggestion-values']) {

http://git-wip-us.apache.org/repos/asf/brooklyn-ui/blob/b8d77434/ui-modules/blueprint-composer/app/components/spec-editor/spec-editor.directive.js
----------------------------------------------------------------------
diff --git a/ui-modules/blueprint-composer/app/components/spec-editor/spec-editor.directive.js b/ui-modules/blueprint-composer/app/components/spec-editor/spec-editor.directive.js
index 870a472..4ff2213 100644
--- a/ui-modules/blueprint-composer/app/components/spec-editor/spec-editor.directive.js
+++ b/ui-modules/blueprint-composer/app/components/spec-editor/spec-editor.directive.js
@@ -76,11 +76,19 @@ export function specEditorDirective($rootScope, $templateCache, $injector, $sani
         scope: {
             model: '='
         },
+        controller: controller,
         template: template,
-        link: link
+        link: link,
+        controllerAs: 'specEditor',
     };
+    
+    function controller() {
+        // does very little currently, but link adds to this
+        return this;
+    }
 
-    function link(scope) {
+    function link(scope, element, attrs, specEditor) {
+        scope.specEditor = specEditor;
         scope.addConfigKey = addConfigKey;
         scope.FAMILIES = EntityFamily;
         scope.RESERVED_KEYS = RESERVED_KEYS;
@@ -170,7 +178,7 @@ export function specEditorDirective($rootScope, $templateCache, $injector, $sani
         }, true);
 
         scope.getObjectSize = (object)=> {
-            return scope.defined(object) && object!=null ? Object.keys(object).length : 0;
+            return specEditor.defined(object) && object!=null ? Object.keys(object).length : 0;
         };
         function findNext(element, clazz, stopClazz) {
             let el = element, last = null;
@@ -234,13 +242,13 @@ export function specEditorDirective($rootScope, $templateCache, $injector, $sani
             }
         }
         scope.nonempty = (o) => o && Object.keys(o).length;
-        scope.defined = (o) => (typeof o !== 'undefined');
-        scope.advanceOutToFormGroupInPanel = (element, event) => {
+        scope.defined = specEditor.defined = (o) => (typeof o !== 'undefined');
+        specEditor.advanceOutToFormGroupInPanel = (element, event) => {
             focusIfPossible(event, findAncestor(element, "form-group", "panel-body")) || element[0].blur(); 
         };
-        scope.advanceControlInFormGroup = (element, event) => {
+        specEditor.advanceControlInFormGroup = (element, event) => {
             focusIfPossible(event, findNext(element, "form-control", "form-group")) ||
-                scope.advanceOutToFormGroupInPanel(element, event);
+                specEditor.advanceOutToFormGroupInPanel(element, event);
         };
         
         scope.onAddMapProperty = (configKey, key, ev)=> {
@@ -256,7 +264,7 @@ export function specEditorDirective($rootScope, $templateCache, $injector, $sani
                         let element = angular.element(ev.target);
                         let prev = findPrev(element, "form-control", "form-group");
                         focusIfPossible(null, prev) ||
-                            scope.advanceOutToFormGroupInPanel(element, null);
+                            specEditor.advanceOutToFormGroupInPanel(element, null);
                     }, 0);
                 } else {
                     // user entered a key that already exists;
@@ -306,7 +314,7 @@ export function specEditorDirective($rootScope, $templateCache, $injector, $sani
                 scope.state.config.filter.values.all = true;
             }
         };
-        scope.recordFocus = ($item)=> {
+        scope.recordFocus = specEditor.recordFocus = ($item)=> {
             scope.state.config.focus = $item.name;
         };
 
@@ -371,13 +379,13 @@ export function specEditorDirective($rootScope, $templateCache, $injector, $sani
                 return item.widgetMode;
             }
             
-            if (scope.isDsl(item.name)) {
+            if (specEditor.isDsl(item.name)) {
                 return scope.state.config.dslViewerManualOverride && scope.state.config.dslViewerManualOverride[item.name] ? 
                     "dsl-manual" // causes default string editor
                     : "dsl-viewer"; 
             }
             
-            if (!scope.defined(val)) val = scope.config[item.name];
+            if (!specEditor.defined(val)) val = scope.config[item.name];
             let type = baseType(item.type);
             
             // if actual value's type does not match declared type,
@@ -389,7 +397,7 @@ export function specEditorDirective($rootScope, $templateCache, $injector, $sani
             else if (type === 'java.util.Map') type = 'map';
             else if (type === 'java.util.Set' || type === 'java.util.List' || type === 'java.util.Collection') type = 'array';
             
-            if (scope.defined(val)) {
+            if (specEditor.defined(val)) {
                 // override to use string editor if the editor doesn't support the value
                 // (probably this is an error, though type-coercion might make it not so)
                 if (type === 'boolean') { 
@@ -435,7 +443,7 @@ export function specEditorDirective($rootScope, $templateCache, $injector, $sani
             // else don't force unless the parse confirms it
             scope.state.config.codeModeForced[item.name] = false;
             // otherwise true if the text entered is json
-            if (!scope.defined(val)) return false;
+            if (!specEditor.defined(val)) return false;
             if (typeof val == 'string') {
                 try {
                     // a YAML parse would be nicer, but JSON is simpler, and sufficient
@@ -467,7 +475,7 @@ export function specEditorDirective($rootScope, $templateCache, $injector, $sani
             let oldMode = !!(scope.state.config.codeModeActive[item.name]);
             // convert local config from json to non-json or vice-versa
             let value = scope.config[item.name];
-            if (!scope.defined(value)) {
+            if (!specEditor.defined(value)) {
                 value = null;
             }
             if (value!=null) {
@@ -536,21 +544,21 @@ export function specEditorDirective($rootScope, $templateCache, $injector, $sani
             }
         };
         /** returns 'enabled' or 'disabled' if a widget is defined, or null if no special widget is defined */ 
-        scope.getCustomConfigWidgetMode = (item) => {
+        specEditor.getCustomConfigWidgetMode = (item) => {
             var widgetMetadata = scope.state.config.customConfigWidgetMetadata[item.name];
             if (!widgetMetadata || widgetMetadata["error"]) return null;
             return widgetMetadata["enabled"] ? 'enabled' : 'disabled';
         };
-        scope.toggleCustomConfigWidgetMode = (item, newval) => {
+        specEditor.toggleCustomConfigWidgetMode = (item, newval) => {
             var widgetMetadata = scope.state.config.customConfigWidgetMetadata[item.name];
             if (!widgetMetadata) {
                 $log.error('Custom widget mode should not be toggled when not available: '+item.name);
                 return null;
             }
-            if (!scope.defined(newval)) newval = !widgetMetadata.enabled;
+            if (!specEditor.defined(newval)) newval = !widgetMetadata.enabled;
             widgetMetadata.enabled = newval;
         }
-        scope.getCustomConfigWidgetModeTitle = (item) => {
+        specEditor.getCustomConfigWidgetModeTitle = (item) => {
             var widgetMetadata = scope.state.config.customConfigWidgetMetadata[item.name];
             if (!widgetMetadata) {
                 // shouldn't be visible
@@ -558,14 +566,14 @@ export function specEditorDirective($rootScope, $templateCache, $injector, $sani
             }
             return widgetMetadata.enabled ? "Use standard widget" : "Use custom widget";
         };
-        scope.getCustomConfigWidgetTemplate = (item) => {
+        specEditor.getCustomConfigWidgetTemplate = (item) => {
             var widgetMetadata = scope.state.config.customConfigWidgetMetadata[item.name];
             var widgetName = $sanitize(widgetMetadata.widget || '--no-widget--');
             var templateName = 'custom-config-widget-'+widgetName;
             if (!$templateCache.get(templateName)) {
                 var widgetDirective = widgetName.replace(/(-[a-z])/g, function($1){return $1[1].toUpperCase();})+'Directive';
                 if ($injector.has(widgetDirective)) {
-                    $templateCache.put(templateName, '<'+widgetName+' item="item" params="state.config.customConfigWidgetMetadata[item.name]"/>');
+                    $templateCache.put(templateName, '<'+widgetName+' item="item" params="state.config.customConfigWidgetMetadata[item.name]" config="config" model="model"/>');
                 } else {
                     $log.error('Missing directive '+widgetDirective+' for custom widget for '+item.name+'; falling back to default widget');
                     scope.state.config.customConfigWidgetMetadata[item.name].error = "Missing directive";
@@ -576,20 +584,20 @@ export function specEditorDirective($rootScope, $templateCache, $injector, $sani
             return templateName;
         };
         
-        scope.isDsl = (key, index) => {
+        specEditor.isDsl = (key, index) => {
             let val = scope.model.config.get(key);
-            if (scope.defined(val) && scope.defined(index) && index!=null) val = val[index];
-            return scope.isDslVal(val);
+            if (specEditor.defined(val) && specEditor.defined(index) && index!=null) val = val[index];
+            return specEditor.isDslVal(val);
         };
-        scope.isDslVal = (val) => {
+        specEditor.isDslVal = (val) => {
             // don't treat constants as DSL (not helpful, and the DSL editor doesn't support it)
             return (val instanceof Dsl) && val.kind && val.kind.family != 'constant';
         };
-        scope.isDslWizardButtonAllowed = (key, index) => {
+        specEditor.isDslWizardButtonAllowed = (key, index) => {
             let val = scope.model.config.get(key);
-            if (scope.defined(val) && scope.defined(index) && index!=null) val = val[index];
-            if (!scope.defined(val) || val===null || val==='') return true;
-            if (scope.isDslVal(val)) {
+            if (specEditor.defined(val) && specEditor.defined(index) && index!=null) val = val[index];
+            if (!specEditor.defined(val) || val===null || val==='') return true;
+            if (specEditor.isDslVal(val)) {
                 return true;
             }
             // non-DSL values cannot be opened in DSL editor
@@ -642,7 +650,7 @@ export function specEditorDirective($rootScope, $templateCache, $injector, $sani
         }
 
         function getLocalConfigValueFromModelValue(key, value) {
-            if (!scope.defined(value) || value==null) {
+            if (!specEditor.defined(value) || value==null) {
                 return value;
             }
              
@@ -722,7 +730,7 @@ export function specEditorDirective($rootScope, $templateCache, $injector, $sani
         }
 
         function getModelValueFromString(val) {
-            if (!scope.defined(val) || val==null || typeof val !== 'string') {
+            if (!specEditor.defined(val) || val==null || typeof val !== 'string') {
                 // only strings will have primitive inference applied
                 // (and this is only invoked when not in code mode)
                 return val;

http://git-wip-us.apache.org/repos/asf/brooklyn-ui/blob/b8d77434/ui-modules/blueprint-composer/app/components/spec-editor/spec-editor.template.html
----------------------------------------------------------------------
diff --git a/ui-modules/blueprint-composer/app/components/spec-editor/spec-editor.template.html b/ui-modules/blueprint-composer/app/components/spec-editor/spec-editor.template.html
index 6100ef6..04ffdfc 100644
--- a/ui-modules/blueprint-composer/app/components/spec-editor/spec-editor.template.html
+++ b/ui-modules/blueprint-composer/app/components/spec-editor/spec-editor.template.html
@@ -133,14 +133,14 @@
                         auto-focus="state.config.focus === item.name"
                         auto-focus-listen-to-window="true"
                         auto-focus-not-if-within="true"
-                        ng-focus="recordFocus(item)"
-                        on-enter="advanceControlInFormGroup">
-                  <div ng-if="getCustomConfigWidgetMode(item) === 'enabled'" class="custom-config-widget">
-                    <ng-include src="getCustomConfigWidgetTemplate(item)" class="custom-config-widget"/>
+                        ng-focus="specEditor.recordFocus(item)"
+                        on-enter="specEditor.advanceControlInFormGroup">
+                  <div ng-if="specEditor.getCustomConfigWidgetMode(item) === 'enabled'" class="custom-config-widget">
+                    <ng-include src="specEditor.getCustomConfigWidgetTemplate(item)" class="custom-config-widget"/>
                   </div>
                   
                   <!-- have to hide it; excluding it conditionally via if doesn't play nice with switch -->
-                  <div ng-show="getCustomConfigWidgetMode(item) !== 'enabled'" class="config-flex-row">
+                  <div ng-show="specEditor.getCustomConfigWidgetMode(item) !== 'enabled'" class="config-flex-row">
                     <label class="control-label" for="{{item.name}}">
                         <span class="label-spec-configuration">{{item.label || item.name}}</span>
                         <span class="info-spec-configuration">
@@ -153,15 +153,15 @@
                     
                     <span class="label-rhs-buttons">
                         <span class="spacer"> </span>
-                        <span class="custom-widget-enable-button" ng-if="getCustomConfigWidgetMode(item) !== null">
-                            <i class="fa fa-fw fa-sun-o" ng-click="toggleCustomConfigWidgetMode(item)" aria-hidden="true" title="{{getCustomConfigWidgetModeTitle(item)}} [{{item.name}}]"></i>
-                            <span class="sr-only">{{getCustomConfigWidgetModeTitle(item)}} [{{item.name}}]</span>
+                        <span class="custom-widget-enable-button" ng-if="specEditor.getCustomConfigWidgetMode(item) !== null">
+                            <i class="fa fa-fw fa-sun-o" ng-click="specEditor.toggleCustomConfigWidgetMode(item)" aria-hidden="true" title="{{specEditor.getCustomConfigWidgetModeTitle(item)}} [{{item.name}}]"></i>
+                            <span class="sr-only">{{specEditor.getCustomConfigWidgetModeTitle(item)}} [{{item.name}}]</span>
                         </span>
                         <span class="json-config" ng-class="{'code-mode-active': state.config.codeModeActive[item.name], 'code-mode-forced': state.config.codeModeForced[item.name]}" ng-if="isCodeModeAvailable(item)">
                             <i class="fa fa-fw fa-code" ng-click="codeModeClick(item)" aria-hidden="true" title="{{getJsonModeTitle(item.name)}} [{{item.name}}]"></i>
                             <span class="sr-only">{{getJsonModeTitle(item.name)}} [{{item.name}}]</span>
                         </span>
-                        <span ng-if="isDsl(item.name)" class="rhs-buttons-subspan">
+                        <span ng-if="specEditor.isDsl(item.name)" class="rhs-buttons-subspan">
                             <span class="dsl-open-wizard-from-toolbar" ng-if="!state.config.dslViewerManualOverride[item.name]">
                                 <i class="fa fa-fw fa-bolt" ui-sref="main.graphical.edit.dsl({entityId: model._id, for: item.name})" aria-hidden="true" title="Open in DSL editor [{{item.name}}]"></i>
                                 <span class="sr-only">Open in DSL editor [{{item.name}}]</span>
@@ -180,10 +180,10 @@
                     <div class="control-value" ng-class="{ 'inline-control': item.widgetMode==='boolean', 'unset': !defined(config[item.name]), 'has-default': defined(item.defaultValue) && item.defaultValue!=null, 'code-mode-active': state.config.codeModeActive[item.name] }">
                         <div ng-switch-when="boolean" class="boolean">
                             <div class="btn-group btn-block" role="group">
-                                <button type="button" class="btn btn-xs btn-default" ng-class="{'btn-success active': config[item.name] === false, 'active': config[item.name] === undefined && item.defaultValue === false}" ng-click="config[item.name] = false" ng-focus="recordFocus(item)">false</button>
-                                <button type="button" class="btn btn-xs btn-default" ng-class="{'btn-success active': config[item.name] === true, 'active': config[item.name] === undefined && item.defaultValue === true}" ng-click="config[item.name] = true" ng-focus="recordFocus(item)">true</button>
-                                <span class="input-group-btn dsl-wizard-button" ng-if="isDslWizardButtonAllowed(item.name)">
-                                    <a ui-sref="main.graphical.edit.dsl({entityId: model._id, for: item.name})" class="btn btn-xs btn-default" ng-class="{'btn-success active': config[item.name].length > 0}" title="Open in DSL editor" ng-focus="recordFocus(item)"><i class="fa fa-bolt"></i></a>
+                                <button type="button" class="btn btn-xs btn-default" ng-class="{'btn-success active': config[item.name] === false, 'active': config[item.name] === undefined && item.defaultValue === false}" ng-click="config[item.name] = false" ng-focus="specEditor.recordFocus(item)">false</button>
+                                <button type="button" class="btn btn-xs btn-default" ng-class="{'btn-success active': config[item.name] === true, 'active': config[item.name] === undefined && item.defaultValue === true}" ng-click="config[item.name] = true" ng-focus="specEditor.recordFocus(item)">true</button>
+                                <span class="input-group-btn dsl-wizard-button" ng-if="specEditor.isDslWizardButtonAllowed(item.name)">
+                                    <a ui-sref="main.graphical.edit.dsl({entityId: model._id, for: item.name})" class="btn btn-xs btn-default" ng-class="{'btn-success active': config[item.name].length > 0}" title="Open in DSL editor" ng-focus="specEditor.recordFocus(item)"><i class="fa fa-bolt"></i></a>
                                 </span>
                             </div>
                         </div>
@@ -191,7 +191,7 @@
                         <select ng-switch-when="java.lang.Enum"
                                 ng-model="config[item.name]"
                                 ng-options="s.value as (s.description + (item.defaultValue === s.value ? ' --- default' : '')) for s in item.possibleValues"
-                                ng-focus="recordFocus(item)"
+                                ng-focus="specEditor.recordFocus(item)"
                                 class="form-control"
                                 name="{{item.name}}"
                                 id="{{item.name}}"></select>
@@ -199,7 +199,7 @@
                         <div ng-switch-when="map" 
                                 ng-init="expandMode = 'default'" class="collection"
                                 ng-class="{ 'open-when-unfocused': expandMode=='open' }">
-                            <p class="collection-toggle" ng-click="expandMode = cycleExpandMode(expandMode, 'map', item, state)" ng-focus="recordFocus(item)"
+                            <p class="collection-toggle" ng-click="expandMode = cycleExpandMode(expandMode, 'map', item, state)" ng-focus="specEditor.recordFocus(item)"
                                     ng-class="{ 'has-default': item.defaultValue && getObjectSize(item.defaultValue) }">
                                 <i class="fa collection-caret" ng-class="{'fa-caret-square-o-down': expandMode=='closed', 'fa-caret-square-o-up': expandMode=='open', 'caret-default': expandMode=='default' }" aria-hidden="true" title="{{expandMode=='closed' ? 'Unpin' : expandMode=='open' ? 'Close (pin)' : 'Open (pin)'}} map"></i>
                                 <span class="sr-only">{{expandMode=='closed' ? 'Unpin' : expandMode=='open' ? 'Close (pin)' : 'Open (pin)'}} map</span>
@@ -225,9 +225,9 @@
                                     <div class="collection-item-grow">
                                         <span class="input-group">
                                             <input type="text" ng-model="config[item.name][mapKey]" class="form-control" placeholder="(empty)" 
-                                                    ng-focus="recordFocus(item)"
-                                                    on-enter="advanceControlInFormGroup" />
-                                            <span class="input-group-btn dsl-wizard-button" ng-if="isDslWizardButtonAllowed(item.name, mapKey)">
+                                                    ng-focus="specEditor.recordFocus(item)"
+                                                    on-enter="specEditor.advanceControlInFormGroup" />
+                                            <span class="input-group-btn dsl-wizard-button" ng-if="specEditor.isDslWizardButtonAllowed(item.name, mapKey)">
                                                 <a ui-sref="main.graphical.edit.dsl({entityId: model._id, for: item.name, index: mapKey})" class="btn btn-default" title="Open in DSL editor"><i class="fa fa-bolt"></i></a>
                                             </span>
                                         </span>
@@ -235,8 +235,8 @@
                                 </li>
                                 <li class="collection-item collection-add" ng-class="{'nonempty': nonempty(config[item.name])}">
                                     <input ng-model="newKey" type="text" placeholder="Add property key" class="form-control" 
-                                        ng-focus="recordFocus(item)"
-                                        on-enter="advanceOutToFormGroupInPanel" ng-blur="onAddMapProperty(item.name, newKey, $event); newKey = '';" required />
+                                        ng-focus="specEditor.recordFocus(item)"
+                                        on-enter="specEditor.advanceOutToFormGroupInPanel" ng-blur="onAddMapProperty(item.name, newKey, $event); newKey = '';" required />
                                 </li>
                             </ul>
                         </div>
@@ -244,7 +244,7 @@
                         <div ng-switch-when="array" ng-switch-when-separator="|" 
                                 ng-init="expandMode = 'default'" class="collection"
                                 ng-class="{ 'open-when-unfocused': expandMode=='open' }">
-                            <p class="collection-toggle" ng-click="expandMode = cycleExpandMode(expandMode, 'map', item, state)" ng-focus="recordFocus(item)"
+                            <p class="collection-toggle" ng-click="expandMode = cycleExpandMode(expandMode, 'map', item, state)" ng-focus="specEditor.recordFocus(item)"
                                     ng-class="{ 'has-default': item.defaultValue && item.defaultValue.length }">
                                 <i class="fa collection-caret" ng-class="{'fa-caret-square-o-down': expandMode=='closed', 'fa-caret-square-o-up': expandMode=='open', 'caret-default': expandMode=='default' }" aria-hidden="true" title="{{expandMode=='closed' ? 'Unpin' : expandMode=='open' ? 'Close (pin)' : 'Open (pin)'}} list"></i>
                                 <span class="sr-only">{{expandMode=='closed' ? 'Unpin' : expandMode=='open' ? 'Close (pin)' : 'Open (pin)'}} list</span>
@@ -269,9 +269,9 @@
                                     <div class="collection-item-grow">
                                         <span class="input-group">
                                             <input type="text" ng-model="config[item.name][$index]" class="form-control" placeholder="(empty)" 
-                                                    ng-focus="recordFocus(item)"
-                                                    on-enter="advanceControlInFormGroup" />
-                                            <span class="input-group-btn dsl-wizard-button" ng-if="isDslWizardButtonAllowed(item.name, $index)" ng-focus="recordFocus(item)">
+                                                    ng-focus="specEditor.recordFocus(item)"
+                                                    on-enter="specEditor.advanceControlInFormGroup" />
+                                            <span class="input-group-btn dsl-wizard-button" ng-if="specEditor.isDslWizardButtonAllowed(item.name, $index)" ng-focus="specEditor.recordFocus(item)">
                                                 <a ui-sref="main.graphical.edit.dsl({entityId: model._id, for: item.name, index: $index})" class="btn btn-default" title="Open in DSL editor"><i class="fa fa-bolt"></i></a>
                                             </span>
                                         </span>
@@ -280,9 +280,9 @@
                                 <li class="collection-item collection-add" ng-class="{'nonempty': config[item.name].length>0}">
                                     <span class="input-group">
                                         <input ng-model="newItem" type="text" placeholder="Add item" class="form-control" auto-focus="expandMode != 'closed'" 
-                                            ng-focus="recordFocus(item)"
-                                            on-enter="advanceOutToFormGroupInPanel" ng-blur="onAddListItem(item.name, newItem, $event, $element); newItem = '';" required />
-                                        <span class="input-group-btn dsl-wizard-button" ng-if="isDslWizardButtonAllowed(item.name, -1)">
+                                            ng-focus="specEditor.recordFocus(item)"
+                                            on-enter="specEditor.advanceOutToFormGroupInPanel" ng-blur="onAddListItem(item.name, newItem, $event, $element); newItem = '';" required />
+                                        <span class="input-group-btn dsl-wizard-button" ng-if="specEditor.isDslWizardButtonAllowed(item.name, -1)">
                                             <a ui-sref="main.graphical.edit.dsl({entityId: model._id, for: item.name, index: config[item.name].length})" class="btn btn-default" title="Open in DSL editor"><i class="fa fa-bolt"></i></a>
                                         </span>
                                     </span>
@@ -295,7 +295,7 @@
                                 <a ui-sref="main.graphical.edit.spec({entityId: model._id, specId: adjunct._id})"
                                             class="open-entity-spec"
                                             title="Open in spec editor"
-                                            ng-focus="recordFocus(item)"></a>
+                                            ng-focus="specEditor.recordFocus(item)"></a>
                                 <ng-include src="'AdjunctTemplate.html'"></ng-include>
                             </div>
                             <a ng-if="!config[item.name][REPLACED_DSL_ENTITYSPEC]" ui-sref="main.graphical.edit.add({entityId: model._id, family: 'spec', configKey: item.name})" class="no-spec">
@@ -306,8 +306,8 @@
                         <div class="input-group" ng-switch-default>
                             <textarea ng-model="config[item.name]" class="form-control" name="{{item.name}}" id="{{item.name}}" auto-grow
                                       placeholder="{{defined(config[item.name]) ? null : item.defaultValue=='' || item.defaultValue==null ? '(empty)' : item.defaultValue}}"
-                                      ng-focus="recordFocus(item)" on-enter="advanceOutToFormGroupInPanel"></textarea>
-                            <span class="input-group-btn dsl-wizard-button" ng-if="isDslWizardButtonAllowed(item.name)">
+                                      ng-focus="specEditor.recordFocus(item)" on-enter="specEditor.advanceOutToFormGroupInPanel"></textarea>
+                            <span class="input-group-btn dsl-wizard-button" ng-if="specEditor.isDslWizardButtonAllowed(item.name)">
                                 <a ui-sref="main.graphical.edit.dsl({entityId: model._id, for: item.name})" class="btn btn-default" title="Open in DSL editor"><i class="fa fa-bolt"></i></a>
                             </span>
                         </div>