"use strict"; (function () { 'use strict'; angular.module('myitsmApp') .directive('expressionBuilder', [function () { return { restrict: 'E', scope: { fieldName: '=', expression: '=', propertyName: '=', fieldAcceleratorsList: '=', onExpressionChange: '&', isRequired: '=?' }, templateUrl: 'views/admin/screen-configuration/expression-builder.html', controller: ['$scope', '$element', '$filter', '$timeout', 'expressionSyntaxTreeBuilder', 'i18nService', 'screenConfigurationModel', function ($scope, $element, $filter, $timeout, expressionSyntaxTreeBuilder, i18nService, screenConfigurationModel) { var typeAheadMode = false, gotFullMatch = false, actualTypeAheadText, expressionStart = 0, expressionEnd = 0; var textFieldExpression = $element.find('.text-input')[0]; var keyCodes = { enter: 13, escape: 27, tab: 9, upArrow: 38, downArrow: 40, space: 32 }; var preventDefaultForKeys = [keyCodes.enter, keyCodes.upArrow, keyCodes.downArrow, keyCodes.tab]; $scope.accelerators = { expression: '', showAcceleratorsList: false, availableExpressions: $scope.fieldAcceleratorsList }; $scope.errorMsg = ''; function validateExpression(exp) { var parsedExp, errorType, mapperObject, pos, params, key; $scope.errorMsg = ''; try { parsedExp = expressionSyntaxTreeBuilder(exp); } catch (e) { mapperObject = { errorMessage1: 'Expected expression after', errorMessage2: 'Expected :', errorMessage3: 'Expected expression', errorMessage4: 'Expected exponent (', errorMessage5: 'Variable names cannot start with a number (', errorMessage6: 'Unexpected period', errorMessage7: 'Unclosed quote after', errorMessage8: 'Unexpected', errorMessage9: 'Unclosed [', errorMessage10: 'Unclosed (', errorMessage11: 'Expected [', errorMessage12: 'Expected ]', errorMessage13: 'Expected (', errorMessage14: 'Expected )', errorMessage15: 'Expected comma' }; for (key in mapperObject) { if (_.includes(e.description, mapperObject[key])) { errorType = key; break; } } if (mapperObject[errorType]) { pos = e.message.substring(mapperObject[errorType].length, e.message.indexOf('at character')); } params = [pos, e.index]; if (errorType) { $scope.errorMsg = i18nService.getLocalizedStringwithParams('expression.builder.' + errorType, params); } else { $scope.errorMsg = i18nService.getLocalizedString('expression.builder.standard.error'); } } if (!$scope.errorMsg) { $scope.errorMsg = screenConfigurationModel.checkExpressionValid(parsedExp, $scope.fieldAcceleratorsList); } } if ($scope.expression) { validateExpression($scope.expression); } $scope.handleBodyKeyDown = function ($event) { if (($event.which === 52 || $event.keyCode === 52 || $event.key === '$')) { $scope.accelerators.showAcceleratorsList = true; expressionStart = $event.target.selectionStart; typeAheadMode = true; } if ($scope.accelerators.showAcceleratorsList && (preventDefaultForKeys.indexOf($event.keyCode) >= 0 || ($event.keyCode === keyCodes.space && gotFullMatch))) { $event.preventDefault(); } }; $scope.handleBodyChange = function ($event) { var actionText = textFieldExpression.value; var pos = -1, flag = false, temp; if (!$scope.accelerators.showAcceleratorsList) { for (temp = 0; temp < actionText.length; temp++) { if (actionText[temp] === '$' && !flag) { flag = true; pos = temp; } else if (actionText[temp] === ' ' && flag) { flag = false; pos = -1; } } if (pos > -1) { $scope.accelerators.showAcceleratorsList = true; expressionStart = pos; typeAheadMode = true; if (preventDefaultForKeys.indexOf($event.keyCode) >= 0 || ($event.keyCode === keyCodes.space && gotFullMatch)) { $event.preventDefault(); } } } expressionEnd = $event.target.selectionEnd; if (textFieldExpression.value.length > 1 && typeAheadMode && (expressionStart > expressionEnd)) { $scope.accelerators.showAcceleratorsList = false; return; } if ($scope.accelerators.showAcceleratorsList) { if (gotFullMatch && [keyCodes.tab, keyCodes.rightArrow, keyCodes.space].indexOf($event.keyCode) >= 0) { gotFullMatch = false; return; } if (typeAheadMode && ($event.keyCode === keyCodes.tab)) { var urlText = textFieldExpression.value; ($scope.typeAheadListPos === $scope.acceleratorsList.length - 1) ? $scope.typeAheadListPos = 0 : $scope.typeAheadListPos++; var selectedItem = $scope.acceleratorsList[$scope.typeAheadListPos] ? $scope.acceleratorsList[$scope.typeAheadListPos].name : ''; var updatedText = [urlText.slice(0, expressionStart), selectedItem, urlText.slice(expressionEnd)].join(''); expressionEnd = parseInt(expressionStart + selectedItem.length); actualTypeAheadText = updatedText.substring(expressionStart, expressionEnd); textFieldExpression.value = updatedText; $timeout(function () { textFieldExpression.setSelectionRange(expressionStart, expressionEnd); textFieldExpression.selectionStart = expressionStart; textFieldExpression.selectionEnd = expressionEnd; }); return; } // ignore if control or alternate key was pressed if ($event.ctrlKey === true || $event.altKey === true || $event.keyCode === keyCodes.leftArrow || $event.keyCode === keyCodes.rightArrow) { return; } else if ($event.keyCode === keyCodes.upArrow) { if ($scope.typeAheadListPos > 0) { $scope.typeAheadListPos--; } return; } else if ($event.keyCode === keyCodes.downArrow) { if ($scope.typeAheadListPos < ($scope.acceleratorsList.length - 1)) { $scope.typeAheadListPos++; } return; } else if ($event.keyCode === keyCodes.enter) { $scope.accelerators.expression = actualTypeAheadText; $scope.insertAcceleratorText(); return; } else if ($event.keyCode === keyCodes.escape) { $scope.disableTypeAheadMode(); return; } } if ($scope.expression && $scope.expression.length) { if (typeAheadMode) { actualTypeAheadText = textFieldExpression.value.substring(expressionStart, expressionEnd); $scope.typeAheadListPos = 0; } } if (typeAheadMode) { if (actualTypeAheadText && actualTypeAheadText.length > 0 && actualTypeAheadText !== $scope.accelerators.expression) { $scope.acceleratorsList = $filter('filter')($scope.accelerators.availableExpressions, { name: actualTypeAheadText.substring(1) }); if ($scope.acceleratorsList.length > 0 || $scope.accelerators.expression.length === 0) { ($scope.acceleratorsList.length > 0) ? $scope.accelerators.showAcceleratorsList = true : angular.noop(); $scope.accelerators.expression = actualTypeAheadText; $scope.popupLeftPosition = 0; $scope.popupTopPosition = 0; } else if ($scope.acceleratorsList.length === 0 && $scope.accelerators.expression.length > 1) { $scope.hideTypeAheadPopup(); } } else if (actualTypeAheadText === '$' || textFieldExpression.value === '$') { $scope.acceleratorsList = $scope.accelerators.availableExpressions; } } }; $scope.handleBodyClick = function ($event) { if ($event.target.value.length > 1 && $scope.accelerators.showAcceleratorsList && expressionStart > expressionEnd) { $scope.accelerators.showAcceleratorsList = false; } }; $scope.acceleratorMouseover = function ($index) { $scope.typeAheadListPos = $index; }; $scope.insertAcceleratorText = function (selectedItem) { var urlText = textFieldExpression.value, accelerator = selectedItem || $scope.acceleratorsList[$scope.typeAheadListPos], insertText = accelerator.name, updatedText = urlText; if ($scope.accelerators.expression) { if (expressionStart === 0) { updatedText = insertText + ' '; } else { if (expressionEnd < urlText.length) { updatedText = [urlText.slice(0, expressionStart), insertText + ' ', urlText.slice(expressionEnd)].join(''); } else if (expressionEnd === urlText.length) { updatedText = urlText.substr(0, urlText.length - actualTypeAheadText.length) + insertText + ' '; } } } else { if (urlText.length === 0) { updatedText = insertText; } else { if (expressionEnd > 0) { updatedText = [urlText.slice(0, expressionStart), insertText + ' ', urlText.slice(expressionEnd)].join(''); } else { updatedText += insertText + ' '; } } } $scope.disableTypeAheadMode(); $scope.expression = updatedText; $scope.handleExpressionChange(); }; $scope.hideTypeAheadPopup = function () { $scope.accelerators.showAcceleratorsList = false; }; $scope.disableTypeAheadMode = function () { actualTypeAheadText = ''; $scope.accelerators.expression = ''; $scope.accelerators.showAcceleratorsList = false; $scope.typeAheadListPos = 0; $scope.acceleratorsList = null; typeAheadMode = false; expressionStart = 0; expressionEnd = 0; }; $scope.acceleratorsHelp = function () { actualTypeAheadText = '$'; $scope.accelerators.expression = ''; $scope.accelerators.showAcceleratorsList = true; $scope.typeAheadListPos = 0; $scope.acceleratorsList = $scope.accelerators.availableExpressions; expressionStart = $scope.expression.length; }; $scope.handleExpressionChange = function () { validateExpression($scope.expression); if (angular.isFunction($scope.onExpressionChange)) { $scope.onExpressionChange(); } }; }] }; }]); }());