function link()

in ui-modules/blueprint-composer/app/components/dsl-editor/dsl-editor.js [71:295]


    function link(scope) {

        const blueprint = blueprintService.get();
        blueprint.isInDslEdit = true;

        scope.$on('$destroy', () => {
            blueprint.isInDslEdit = false;
        });

        scope.$on('d3.entity-selected', (event, entity) => {
            scope.state.filter = scope.filters.find(filter => filter.id === entity._id);
        });

        scope.DSL_KINDS = DSL_KINDS;

        scope.kinds = Object.values(DSL_KINDS);

        scope.filters = [{
            id: 'blueprint',
            label: 'Anywhere on the blueprint',
            scope: 'Global'
        }].concat(getEntityItems(blueprintService.get()).map(item => {
            let attrs = [];
            if (item.entity === scope.entity) {
                attrs.push('this');
            }
            if (!item.entity.hasParent()) {
                attrs.push('root');
            }
            if (scope.entity.parent === item.entity) {
                attrs.push('parent');
            }

            let { name, entity, id } = item;
            name += attrs.length > 0 ? ` (${attrs.join(', ')})` : ` (${entity.id || id})`;

            return {
                id,
                label: name,
                scope: 'On specific entity'
            };
        }));

        scope.orders = [{
            label: 'name',
            property: 'name'
        }, {
            label: 'entity',
            property: 'entity.type'
        }];

        scope.items = [].concat(
            getConfigItems(blueprintService.get(), scope.definition),
            getSensorItems(blueprintService.get()),
            getEntityItems(blueprintService.get(), scope.definition.type)
        );

        scope.state = {
            kind: scope.kinds[0],
            filter: scope.filters[0],
            orderBy: scope.orders[0],
            search: '',
            sensor: false,
            arguments: [],
            toggles: [],
            entityId: '',
        };

        if (scope.dsl) {
            let lastMethod = scope.dsl.getLastMethod();
            if (lastMethod.kind === KIND.METHOD && lastMethod.name === 'config') {
                scope.state.kind = scope.kinds[1];
                scope.state.search = lastMethod.params[0].name;
                scope.state.item = scope.items.find(item => (item.type === DSL_KINDS.CONFIG && item.name === lastMethod.params[0].name));
            }
            if (lastMethod.kind === KIND.METHOD && ['attributeWhenReady', 'sensor'].includes(lastMethod.name)) {
                scope.state.kind = scope.kinds[2];
                scope.state.search = lastMethod.params[0].name;
                scope.state.item = scope.items.find(item => (item.type === DSL_KINDS.SENSOR && item.name === lastMethod.params[0].name));
                scope.state.sensor = lastMethod.name === 'sensor';
            }
            if (lastMethod.kind === KIND.UTILITY && lastMethod.name === 'formatString') {
                scope.state.kind = scope.kinds[4];
                scope.state.pattern = scope.dsl.params[0].name;
                scope.state.arguments = Array.from(scope.dsl.params).splice(1).map(argument => {
                    return argument.kind === KIND.STRING ? argument.name : argument.toString();
                });
            }
            try {
                let relatedEntity = scope.dsl.params.length && scope.dsl.getRoot().relationships.find(entity => entity.id === scope.dsl.params[0].name);
                if (relatedEntity) {
                    scope.state.filter = scope.filters.find(filter => filter.id === relatedEntity._id);
                }
            } catch (e) {
                console.log("Error analysing DSL (ignore)", scope.dsl, scope.filters, e);
            }
        }

        scope.$watch('state.pattern', (newValue, oldValue) => {
            if (!newValue || angular.equals(newValue, oldValue)) {
                return;
            }
            scope.dsl.params.splice(0, 1, new Dsl(KIND.STRING, newValue));
        });

        scope.$watchCollection('state.arguments', (newValue, oldValue) => {
            if (!newValue || angular.equals(newValue, oldValue)) {
                return;
            }
            newValue.forEach((argument, index) => {
                let dsl;
                try {
                    dsl = new DslParser().parseString(argument, scope.entity, blueprintService.get());
                    scope.dsl.params.splice(index + 1, 1, dsl);
                } catch (ex) {
                    $log.debug(`Argument ${index} is not a DSL. Defaulting to string`, ex);
                    dsl = new Dsl(KIND.STRING, argument);
                }
                scope.dsl.params.splice(index + 1, 1, dsl);
            })
        });

        scope.isDsl = (index) => {
            return scope.dsl.params[index + 1] instanceof Dsl && scope.dsl.params[index + 1].kind !== KIND.STRING;
        };

        scope.predicate = (value, index, array) => {
            let predicates = [];

            let validTypes = scope.state.kind.id === DSL_KINDS.ALL.id ? Object.values(DSL_KINDS).filter(type => type !== DSL_KINDS.FORMAT_STRING) : [scope.state.kind];
            predicates.push(validTypes.map(type => type.id).includes(value.type.id));

            if (scope.state.filter.id !== 'blueprint') {
                predicates.push(scope.state.filter.id === value.entity._id);
            }

            if (scope.state.search) {
                let searchPredicate = value.name.toLowerCase().indexOf(scope.state.search.toLowerCase()) > -1;
                if (value.description) {
                    searchPredicate |= value.description.toLowerCase().indexOf(scope.state.search.toLowerCase()) > -1;
                }
                if ([ DSL_KINDS.ENTITY.id, DSL_KINDS.ALL.id ].includes(scope.state.kind.id) ) {
                    // if searching for entity or config/sensors/entity, show everything.
                    // but searching just for a config or sensor doesn't show everything.
                    // (not sure that's the right semantics?)
                    searchPredicate |= value.entity.id && value.entity.id.toLowerCase().indexOf(scope.state.search.toLowerCase()) > -1;
                    searchPredicate |= value.entity.name && value.entity.name.toLowerCase().indexOf(scope.state.search.toLowerCase()) > -1;
                }
                predicates.push(searchPredicate);
            }
            return predicates.reduce((ret, predicate) => (ret && predicate), true);
        };

        scope.selectItem = (item, event) => {
            scope.state.item = item;

            event.preventDefault();
            event.stopPropagation();
        };

        scope.selectDsl = () => {
            scope.dsl = buildDsl();

            $rootScope.$broadcast(`${MODULE_NAME}.select`, {
                dsl: scope.dsl,
                definition: scope.definition
            });
        };

        scope.nestDsl = (index) => {
            scope.dsl = buildDsl();

            $rootScope.$broadcast(`${MODULE_NAME}.nest`, {
                dsl: scope.dsl,
                definition: scope.definition,
                index: index
            });
        };

        scope.isDone = () => {
            if (scope.state.kind.id !== DSL_KINDS.FORMAT_STRING.id) {
                return angular.isDefined(scope.state.item);
            } else {
                return brUtilsGeneral.isNonEmpty(scope.state.pattern) && brUtilsGeneral.isNonEmpty(scope.state.arguments);
            }
        };

        function buildDsl() {
            let dsl;
            let funcDsl;

            if (scope.state.kind.id !== DSL_KINDS.FORMAT_STRING.id) {
                let scopedDsl = getScopedDsl(scope.entity, scope.state.item.entity, scope.state);

                switch (scope.state.item.type) {
                    case DSL_KINDS.CONFIG:
                        funcDsl = new Dsl(KIND.METHOD, 'config').param(new Dsl(KIND.STRING, scope.state.item.name));
                        scopedDsl.chain(funcDsl);
                        dsl = isSelfDsl(scopedDsl) ? funcDsl : scopedDsl;
                        break;
                    case DSL_KINDS.SENSOR:
                        funcDsl = new Dsl(KIND.METHOD, scope.state.sensor ? 'sensor' : 'attributeWhenReady').param(new Dsl(KIND.STRING, scope.state.item.name));
                        scopedDsl.chain(funcDsl);
                        dsl = isSelfDsl(scopedDsl) ? funcDsl : scopedDsl;
                        break;
                    case DSL_KINDS.ENTITY:
                        dsl = scopedDsl;
                        break;
                }
            } else {
                dsl = new Dsl(KIND.UTILITY, 'formatString').param(new Dsl(KIND.STRING, scope.state.pattern));
                scope.state.arguments.forEach((arg, index) => {
                    dsl.param(scope.isDsl(index) ? scope.dsl.params[index + 1] : new Dsl(KIND.STRING, arg));
                });
            }

            try {
                dsl = new DslParser().parseString(dsl.toString(), scope.entity, blueprintService.get());
            } catch (ex) {
                $log.debug(`Cannot get DSL relationship for DSL "${dsl}`, ex);
            }

            return dsl;
        }
    }