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:50 UTC

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

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>