component specific code moved downstream
Project: http://git-wip-us.apache.org/repos/asf/brooklyn-ui/repo Commit: http://git-wip-us.apache.org/repos/asf/brooklyn-ui/commit/c5db2cb8 Tree: http://git-wip-us.apache.org/repos/asf/brooklyn-ui/tree/c5db2cb8 Diff: http://git-wip-us.apache.org/repos/asf/brooklyn-ui/diff/c5db2cb8 Branch: refs/heads/master Commit: c5db2cb8c5bbb7e14b6842435ae72b263e4287d1 Parents: 2a4d31c Author: frogfather <j.a.campb...@icloud.com> Authored: Fri Oct 26 10:02:04 2018 +0100 Committer: frogfather <j.a.campb...@icloud.com> Committed: Fri Oct 26 10:02:04 2018 +0100 ---------------------------------------------------------------------- .../spec-editor/spec-editor.directive.js | 108 +++++++++---------- 1 file changed, 52 insertions(+), 56 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/brooklyn-ui/blob/c5db2cb8/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 df979d7..a5341b9 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 @@ -34,7 +34,7 @@ const ANY_MEMBERSPEC_REGEX = /(^.*[m,M]ember[s,S]pec$)/; const REPLACED_DSL_ENTITYSPEC = '___brooklyn:entitySpec'; angular.module(MODULE_NAME, [onEnter, autoGrow, blurOnEnter, brooklynDslEditor, brooklynDslViewer]) - .directive('specEditor', ['$rootScope', '$templateCache', '$injector', '$sanitize', '$filter', '$log', '$sce', '$timeout', '$document', '$state', 'blueprintService', 'composerOverrides', specEditorDirective]) + .directive('specEditor', ['$rootScope', '$templateCache', '$injector', '$sanitize', '$filter', '$log', '$sce', '$timeout', '$document', '$state', '$compile', 'blueprintService', 'composerOverrides', specEditorDirective]) .filter('specEditorConfig', specEditorConfigFilter) .filter('specEditorType', specEditorTypeFilter); @@ -72,7 +72,7 @@ export const CONFIG_FILTERS = [ } ]; -export function specEditorDirective($rootScope, $templateCache, $injector, $sanitize, $filter, $log, $sce, $timeout, $document, $state, blueprintService, composerOverrides) { +export function specEditorDirective($rootScope, $templateCache, $injector, $sanitize, $filter, $log, $sce, $timeout, $document, $state, $compile, blueprintService, composerOverrides) { return { restrict: 'E', scope: { @@ -83,7 +83,7 @@ export function specEditorDirective($rootScope, $templateCache, $injector, $sani link: link, controllerAs: 'specEditor', }; - + function controller() { // does very little currently, but link adds to this return this; @@ -95,7 +95,7 @@ export function specEditorDirective($rootScope, $templateCache, $injector, $sani scope.FAMILIES = EntityFamily; scope.RESERVED_KEYS = RESERVED_KEYS; scope.REPLACED_DSL_ENTITYSPEC = REPLACED_DSL_ENTITYSPEC; - + let defaultState = { config: { add: { @@ -111,7 +111,7 @@ export function specEditorDirective($rootScope, $templateCache, $injector, $sani // TODO would be nice to set null here, then have it go true if there is filtered config // but the collapsible widget doesn't seem to respond to subsequent state changed open: true, - + dslViewerManualOverride: {}, codeModeActive: {}, codeModeForced: {}, @@ -131,12 +131,8 @@ export function specEditorDirective($rootScope, $templateCache, $injector, $sani } }; // allow downstream to configure this controller and/or scope - (composerOverrides.configureSpecEditor || function() {})(this, $scope, $element); - var headerTemplate = angular.element($templateCache.get('spec-editor/header.html')); - if (headerTemplate){ - var compiledHTML = $compile(headerTemplate[0])(scope) - element[0].querySelector('.spec-type-header').append(compiledHTML[0]); - } + (composerOverrides.configureSpecEditor || function() {})(specEditor, scope, element, $state, $compile, $templateCache); + scope.filters = { config: CONFIG_FILTERS }; @@ -144,7 +140,7 @@ export function specEditorDirective($rootScope, $templateCache, $injector, $sani scope.onFilterClicked = (filter) => { if (!scope.isFilterDisabled(filter)) scope.state.config.filter.values[ filter.id ] = !scope.state.config.filter.values[ filter.id ]; }; - + scope.state = sessionStorage && sessionStorage.getItem(scope.model._id) ? JSON.parse(sessionStorage.getItem(scope.model._id)) : defaultState; @@ -162,7 +158,7 @@ export function specEditorDirective($rootScope, $templateCache, $injector, $sani }); loadCustomConfigWidgetMetadata(scope); - + scope.config = {}; scope.$watch('model', (newVal, oldVal)=> { if (newVal && !newVal.equals(oldVal)) { @@ -252,13 +248,13 @@ export function specEditorDirective($rootScope, $templateCache, $injector, $sani scope.nonempty = (o) => o && Object.keys(o).length; scope.defined = specEditor.defined = (o) => (typeof o !== 'undefined'); specEditor.advanceOutToFormGroupInPanel = (element, event) => { - focusIfPossible(event, findAncestor(element, "form-group", "panel-body")) || element[0].blur(); + focusIfPossible(event, findAncestor(element, "form-group", "panel-body")) || element[0].blur(); }; specEditor.advanceControlInFormGroup = (element, event) => { focusIfPossible(event, findNext(element, "form-control", "form-group")) || specEditor.advanceOutToFormGroupInPanel(element, event); }; - + scope.onAddMapProperty = (configKey, key, ev)=> { if (key) { if (!scope.config[configKey]) { @@ -266,7 +262,7 @@ export function specEditorDirective($rootScope, $templateCache, $injector, $sani } if (!scope.config[configKey].hasOwnProperty(key)) { scope.config[configKey][key] = ''; - + // defer so control exists, then focus on previous item (new control) $timeout(() => { let element = angular.element(ev.target); @@ -383,7 +379,7 @@ export function specEditorDirective($rootScope, $templateCache, $injector, $sani scope.getBadgeClass = (issues) => { return issues.some(issue => issue.level === ISSUE_LEVEL.ERROR) ? 'badge-danger' : 'badge-warning'; }; - + specEditor.descriptionHtml = (text) => { let out = []; for (let item of text.split(/\n\n+/)) { @@ -391,7 +387,7 @@ export function specEditorDirective($rootScope, $templateCache, $injector, $sani out.push($sanitize(item)); } out.splice(0,1); - return $sce.trustAsHtml(out.join("\n")); + return $sce.trustAsHtml(out.join("\n")); }; function getConfigWidgetModeInternal(item, val) { @@ -400,29 +396,29 @@ export function specEditorDirective($rootScope, $templateCache, $injector, $sani // don't interrupt when the user has typed $brooklyn:component("x")! return item.widgetMode; } - + if (specEditor.isDsl(item.name)) { - return scope.state.config.dslViewerManualOverride && scope.state.config.dslViewerManualOverride[item.name] ? + return scope.state.config.dslViewerManualOverride && scope.state.config.dslViewerManualOverride[item.name] ? "dsl-manual" // causes default string editor - : "dsl-viewer"; + : "dsl-viewer"; } - + if (!specEditor.defined(val)) val = scope.config[item.name]; 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, // we could render e.g. w map editor as a convenience; // but for now the logic is only based on the declared type - + if (type === 'java.lang.Boolean') type = 'boolean'; 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 (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') { + if (type === 'boolean') { if (typeof val !== type) return type+'-manual'; // causes default string editor } else if (type === 'map') { if (typeof val !== 'object') return type+'-manual'; // causes default string editor @@ -434,16 +430,16 @@ export function specEditorDirective($rootScope, $templateCache, $injector, $sani // code mode forces manual editor return type+'-manual'; } - + return type; }; scope.getConfigWidgetMode = (item, value) => { // record the value as `item.widgetMode` so we can reference it subsequently, as well as returning it - + if (item.type) { return item.widgetMode = getConfigWidgetModeInternal(item, value); } - + // if type isn't set then infer if (value instanceof Array) { definition.widgetMode = 'array'; @@ -455,7 +451,7 @@ export function specEditorDirective($rootScope, $templateCache, $injector, $sani } scope.isCodeModeAvailable = (item) => { let val = scope.config[item.name]; - + if (scope.state.config.codeModeActive[item.name]) { // simple check to update whether it is forced when active -- if it starts with { or [ scope.state.config.codeModeForced[item.name] = (typeof val === 'string' && val.trim().match(/^[{\[]/)); @@ -475,7 +471,7 @@ export function specEditorDirective($rootScope, $templateCache, $injector, $sani // if parse returns a string or a complex object, then it looks like JSON was entered // (e.g. "foo" or {a:1}); if not, eg user entered boolean or number (45 or true), // then don't offer code mode; if it was complex, additionally force code mode - scope.state.config.codeModeForced[item.name] = typeof parsed === 'object' || typeof parsed === 'array'; + scope.state.config.codeModeForced[item.name] = typeof parsed === 'object' || typeof parsed === 'array'; return (scope.state.config.codeModeForced[item.name] || typeof parsed === 'string'); } catch (ex) { // not valid text input for code mode @@ -519,7 +515,7 @@ export function specEditorDirective($rootScope, $templateCache, $injector, $sani // parsed value is a primitive, make that be the value shown } else { // if type is not a string, then show unchanged value - // (would only come here on weird changes) + // (would only come here on weird changes) value = null; } } catch (notJson) { @@ -565,7 +561,7 @@ export function specEditorDirective($rootScope, $templateCache, $injector, $sani return "Edit in simple mode, unwrapping JSON if possible ["+itemName+"]"; } }; - /** returns 'enabled' or 'disabled' if a widget is defined, or null if no special widget is defined */ + /** returns 'enabled' or 'disabled' if a widget is defined, or null if no special widget is defined */ specEditor.getCustomConfigWidgetMode = (item) => { var widgetMetadata = scope.state.config.customConfigWidgetMetadata[item.name]; if (!widgetMetadata || widgetMetadata["error"]) return null; @@ -605,7 +601,7 @@ export function specEditorDirective($rootScope, $templateCache, $injector, $sani } return templateName; }; - + specEditor.isDsl = (key, index) => { let val = scope.model.config.get(key); if (specEditor.defined(val) && specEditor.defined(index) && index!=null) val = val[index]; @@ -626,7 +622,7 @@ export function specEditorDirective($rootScope, $templateCache, $injector, $sani // non-DSL values cannot be opened in DSL editor return false; }; - + function getAddListConfig() { if (!angular.isArray(scope.model.miscData.get('config'))) { return []; @@ -647,7 +643,7 @@ export function specEditorDirective($rootScope, $templateCache, $injector, $sani }); }); } - + /* config state for each item is stored in multiple places: * * scope.config = map of values used/set by editor (strings, map of strings, json code if using code mode, etc); * this should be suitable for ng-model to work with, so e.g. if using code mode we need to put JSON.stringify value in here, @@ -676,7 +672,7 @@ export function specEditorDirective($rootScope, $templateCache, $injector, $sani if (!specEditor.defined(value) || value==null) { return value; } - + if (ANY_MEMBERSPEC_REGEX.test(key) && value.hasOwnProperty(DSL_ENTITY_SPEC)) { let wrapper = {}; wrapper[REPLACED_DSL_ENTITYSPEC] = value[DSL_ENTITY_SPEC]; @@ -685,7 +681,7 @@ export function specEditorDirective($rootScope, $templateCache, $injector, $sani if (value instanceof Dsl) { return value.toString(); } - + let definition = scope.model.miscData.get('config').find(config => config.name === key); if (!definition) { // odd, no def'n for this key @@ -694,7 +690,7 @@ export function specEditorDirective($rootScope, $templateCache, $injector, $sani } else { if (!definition.widgetMode) scope.getConfigWidgetMode(definition); } - + // if json mode, then just stringify if (scope.state.config.codeModeActive[key]) { if (scope.state.config.codeModeError[key]) { @@ -703,9 +699,9 @@ export function specEditorDirective($rootScope, $templateCache, $injector, $sani } return JSON.stringify(value); } - + // else treat as value, with array/map special - + try { if (definition.widgetMode === 'array') { return value.map(item => { @@ -739,15 +735,15 @@ export function specEditorDirective($rootScope, $templateCache, $injector, $sani scope.state.config.codeModeActive[key] = true; // and the widget mode updated to be 'manual' scope.getConfigWidgetMode(definition, value); - + return JSON.stringify(value); } - + // if boolean, return as primitive type if (typeof value === 'boolean') { return value; } - + // all other primitives treat as string (as they will get a string-based widget) return ""+value; } @@ -768,13 +764,13 @@ export function specEditorDirective($rootScope, $templateCache, $injector, $sani // yaml editor from simplifying it to be "3" or "1". throw 'decimal ending with dot or 0, eg version, treat as string'; } - // numbers and booleans are unquoted + // numbers and booleans are unquoted return prim; } catch (ex) { return val; } } - + function setModelFromLocalConfig() { let localConfig = scope.config; let result = {}; @@ -787,28 +783,28 @@ export function specEditorDirective($rootScope, $templateCache, $injector, $sani result[keyRef][DSL_ENTITY_SPEC] = localConfig[keyRef][REPLACED_DSL_ENTITYSPEC]; continue; } - + let definition = scope.model.miscData.get('config').find(config => config.name === keyRef); if (!definition) { // odd, no def'n for this key; infer definition = {}; scope.getConfigWidgetMode(definition, localConfig[keyRef]) } - + // if JSON mode then parse scope.state.config.codeModeError[keyRef] = null; if (scope.state.config.codeModeActive && scope.state.config.codeModeActive[keyRef]) { try { result[keyRef] = JSON.parse(localConfig[keyRef]); } catch (ex) { - scope.state.config.codeModeError[keyRef] = "Invalid JSON"; + scope.state.config.codeModeError[keyRef] = "Invalid JSON"; result[keyRef] = localConfig[keyRef]; - } + } continue; } - + // else return as is, or introspect for array/map - + if (definition.widgetMode === 'array') { result[keyRef] = localConfig[keyRef].map(getModelValueFromString); continue; @@ -820,10 +816,10 @@ export function specEditorDirective($rootScope, $templateCache, $injector, $sani } continue; } - + result[keyRef] = getModelValueFromString(localConfig[keyRef]); } - + scope.model.setConfigFromJson(result); } @@ -846,7 +842,7 @@ export function specEditorDirective($rootScope, $templateCache, $injector, $sani let config = model.miscData.get('config').find(config => config.name === key); // Ideally code below would escaped or use template/directive; sanitize just catches attacks. // (These values are automatically sanitized in any case, so that's redundant.) - + // If we need an integer, check if the value is a number if (config.type === 'java.lang.Integer' && !angular.isNumber(value) && !(value instanceof Dsl)) { model.addIssue(Issue.builder().group('config').ref(key).message('<code>'+$sanitize(value)+'</code> is not a number').build()); @@ -863,7 +859,7 @@ export function specEditorConfigFilter() { return function (input, filtersMapById, model) { let filters = []; Object.keys(filtersMapById).forEach( (k) => { if (filtersMapById[k]) filters.push(k); } ); - + if (!filters.every(filterId => CONFIG_FILTERS.some(filter => filter.id === filterId))) { return input; }