You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1279 lines
48 KiB
1279 lines
48 KiB
/* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
flowableModule
|
|
.directive('restrictInput', ["$parse", function ($parse) {
|
|
return {
|
|
restrict: 'A',
|
|
require: 'ngModel',
|
|
priority: 1002,
|
|
|
|
link: function postLink(scope, elm, attrs, ctrl) {
|
|
|
|
var acceptedFormat = attrs["restrictInput"];
|
|
if (acceptedFormat == undefined || acceptedFormat == null || acceptedFormat == "") {
|
|
acceptedFormat = attrs["dateFormat"];
|
|
}
|
|
|
|
scope.field.acceptedFormat=acceptedFormat;
|
|
|
|
function calculateAcceptedFormats(format) {
|
|
var format1 = format.toUpperCase(); //d-m-yyyy
|
|
var format2 = format1.replace(/-D-/,"-DD-").replace(/^D-/,"DD-").replace(/-D$/,"-DD"); //dd-m-yyyy
|
|
var format3 = format1.replace(/-M-/,"-MM-").replace(/^M-/,"MM-").replace(/-M$/,"-MM"); //d-mm-yyyy
|
|
var format4 = format2.replace(/-M-/,"-MM-").replace(/^M-/,"MM-").replace(/-M$/,"-MM"); //dd-mm-yyyy
|
|
return [format1,format2,format3,format4];
|
|
}
|
|
|
|
var acceptedFormats = calculateAcceptedFormats(acceptedFormat);
|
|
var skipValidation = false;
|
|
|
|
if (acceptedFormat == undefined || acceptedFormat == null || acceptedFormat == "") {
|
|
skipValidation = true;
|
|
}
|
|
var oldRenderer = ctrl.$render;
|
|
|
|
ctrl.$render = function () {
|
|
elm.val(ctrl.$viewValue);
|
|
if (ctrl.$dateValue && !isNaN(ctrl.$dateValue.getTime())) {
|
|
if (oldRenderer) {
|
|
oldRenderer();
|
|
}
|
|
}
|
|
};
|
|
|
|
function isValidText(viewValue, format) {
|
|
if (viewValue === undefined || viewValue == null || viewValue ==='') return true;
|
|
if (viewValue.length > format.length) return false;
|
|
|
|
for (var i = 0; i < Math.min(format.length, viewValue.length); i++) {
|
|
var charType = format[i];
|
|
if (charType.toUpperCase().match(/D|M|Y/)) {
|
|
if (viewValue[i].match(/\d/) == null) return false;
|
|
}else {
|
|
if (viewValue[i] != charType) return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
ctrl.$parsers.unshift(function (viewValue) {
|
|
|
|
if (skipValidation) return viewValue; //just skip this parser
|
|
|
|
var isValid = false;
|
|
for(var i =0 ; i < acceptedFormats.length && !isValid; i++){
|
|
isValid |= isValidText(viewValue,acceptedFormats[i]);
|
|
}
|
|
|
|
if (!isValid) {
|
|
//should restore to the latest known well formated date.
|
|
ctrl.$dateValue = null;
|
|
} else {
|
|
ctrl.$lastValidText = viewValue;
|
|
|
|
//by default the date picker in angular strap does not reset the dateValue if the viewValue is null or empty.
|
|
if (viewValue === undefined || viewValue == null || viewValue === '') {
|
|
ctrl.$dateValue = null;
|
|
}
|
|
|
|
return viewValue;
|
|
}
|
|
|
|
ctrl.$setViewValue(ctrl.$lastValidText);
|
|
ctrl.$render();
|
|
return ctrl.$lastValidText;
|
|
});
|
|
|
|
}
|
|
}
|
|
}
|
|
]);
|
|
|
|
|
|
flowableModule
|
|
.directive('autoHeight', ['$rootScope', '$timeout', function($rootScope, $timeout) {
|
|
return {
|
|
restrict: 'AC',
|
|
scope: {
|
|
'toWatch': '=autoHeight'
|
|
},
|
|
compile: function (element, attr) {
|
|
return function ($scope, $element, $attrs) {
|
|
var offset = 0;
|
|
if($attrs['offset']) {
|
|
offset = parseInt($attrs['offset']);
|
|
if(isNaN(offset) || offset == undefined) {
|
|
offset = 0;
|
|
}
|
|
}
|
|
|
|
var update = function($element) {
|
|
// Get hold of parent and iterate all the children to get available height
|
|
|
|
$timeout(function() {
|
|
var total = $element.parent().outerHeight() - offset;
|
|
var found = false;
|
|
$element.parent().children().each(function() {
|
|
if(!found) {
|
|
if($element[0] == this) {
|
|
found = true;
|
|
} else {
|
|
// Substract preceding child's height
|
|
total -= angular.element(this).outerHeight();
|
|
}
|
|
}
|
|
});
|
|
|
|
if(found) {
|
|
$element.height(total);
|
|
}
|
|
}, 0);
|
|
};
|
|
|
|
if($scope.unregisterWatcher) {
|
|
$scope.unregisterWatcher();
|
|
}
|
|
$scope.unregisterWatcher = $rootScope.$watch('window.height', function(windowHeight) {
|
|
update($element);
|
|
});
|
|
|
|
if($scope.unregisterForceWatcher) {
|
|
$scope.unregisterForceWatcher();
|
|
}
|
|
$scope.unregisterForceWatcher = $rootScope.$watch('window.forceRefresh', function(forceValue) {
|
|
update($element);
|
|
});
|
|
|
|
|
|
$scope.$on('$destroy', function() {
|
|
// Cleanup watcher for window-height
|
|
if($scope.unregisterWatcher) {
|
|
$scope.unregisterWatcher();
|
|
}
|
|
if($scope.unregisterForceWatcher) {
|
|
$scope.unregisterForceWatcher();
|
|
}
|
|
});
|
|
}
|
|
}
|
|
};
|
|
}]);
|
|
|
|
/**
|
|
* Directive that ensures the child-element with class .active is visible and scrolls if needed. Watches the value
|
|
* of the directive and will re-apply if this value is changes.
|
|
*/
|
|
flowableModule
|
|
.directive('scrollToActive', ['$timeout', function($timeout) {
|
|
return {
|
|
restrict: 'AC',
|
|
scope: {
|
|
toWatch: "=scrollToActiveModel"
|
|
},
|
|
compile: function (element, attr) {
|
|
return function ($scope, $element, $attrs) {
|
|
$scope.$watch('toWatch', function() {
|
|
$timeout(function() {
|
|
var useParent = $attrs['useParent'];
|
|
var offsetTop = $attrs['offsetTop'];
|
|
if(offsetTop) {
|
|
offsetTop = parseInt(offsetTop);
|
|
if(isNaN(offsetTop)) {
|
|
offsetTop = 0;
|
|
}
|
|
}
|
|
if (!offsetTop) {
|
|
offsetTop = 0;
|
|
}
|
|
|
|
var selectedArr = $element.children('.active');
|
|
if(selectedArr && selectedArr.length > 0) {
|
|
var selected = angular.element(selectedArr[0]);
|
|
|
|
if(useParent) {
|
|
$element = angular.element($element.parent());
|
|
}
|
|
var selectedTop = selected.position().top - $element.position().top + $element.scrollTop();
|
|
var selectedBottom = selectedTop + selected.outerHeight();
|
|
var elementBottom = $element.scrollTop() + $element.innerHeight();
|
|
var elementTop = elementBottom - $element.innerHeight();
|
|
|
|
if(selectedTop <= elementTop) {
|
|
// scroll up
|
|
$element.scrollTop(selectedTop - selected.outerHeight() - offsetTop);
|
|
} else if(selectedBottom > elementBottom) {
|
|
// scroll down
|
|
$element.scrollTop(elementTop + selected.outerHeight() - offsetTop);
|
|
}
|
|
}
|
|
}, 0);
|
|
});
|
|
}
|
|
}
|
|
};
|
|
}]);
|
|
|
|
/**
|
|
* Directive that ensures the popup is scrolled into view, using the first parent as scroll-pane that has
|
|
* a class 'scroll-container' set on it. Is applied when the popup is shown.
|
|
*/
|
|
flowableModule
|
|
.directive('autoScroll', ['$timeout', function($timeout) {
|
|
return {
|
|
restrict: 'AC',
|
|
compile: function (element, attr) {
|
|
return function ($scope, $element, $attrs) {
|
|
$scope.$on('tooltip.show', function() {
|
|
$timeout(function() {
|
|
// Find appropriate parent
|
|
var parent = $element[0];
|
|
while(parent) {
|
|
if(parent.className && parent.className.indexOf('scroll-container') >= 0) {
|
|
break;
|
|
}
|
|
parent = parent.parentNode;
|
|
}
|
|
|
|
if(parent) {
|
|
parent = angular.element(parent);
|
|
var selectedTop = $element.offset().top - parent.offset().top + $element.scrollTop();
|
|
var selectedBottom = selectedTop + $element.outerHeight();
|
|
|
|
if(selectedBottom + 30 >= parent.outerHeight()) {
|
|
parent.scrollTop(selectedTop);
|
|
}
|
|
}
|
|
}, 50);
|
|
});
|
|
}
|
|
}
|
|
};
|
|
}]);
|
|
|
|
flowableModule
|
|
.directive('userName', function() {
|
|
var directive = {};
|
|
directive.template = '{{user.firstName && user.firstName || ""}} {{user.lastName && user.lastName || ""}} {{ (user.email && !user.firstName && !user.lastName) && user.email || ""}}';
|
|
directive.scope = {
|
|
user: "=userName"
|
|
};
|
|
return directive;
|
|
});
|
|
|
|
|
|
/**
|
|
* Executes the method that is set on the directive attribute value when ANY OTHER element is clicked, which is not the element the
|
|
* directive is on, or any of it's children.
|
|
*
|
|
*/
|
|
flowableModule
|
|
.directive('clickAnywhere', ["$document", "$parse", function ($document, $parse) {
|
|
|
|
var linkFunction = function ($scope, $element, $attributes) {
|
|
|
|
var scopeExpression = $attributes.clickAnywhere;
|
|
var invoker = $parse(scopeExpression);
|
|
|
|
var ignoreId = $attributes.ignore;
|
|
var ignoreClass = $attributes.ignoreClass;
|
|
var ignorePopupEvents = $attributes.ignorePopupEvents == 'true';
|
|
|
|
var handler = function (event) {
|
|
// Check source of event
|
|
var parent = event.target;
|
|
while(parent) {
|
|
if(parent == $element[0] ||
|
|
(ignoreId && parent.id == ignoreId) ||
|
|
(ignoreClass && parent.className && parent.className.indexOf(ignoreClass) >= 0)) {
|
|
|
|
event.stopPropagation();
|
|
event.preventDefault();
|
|
return;
|
|
}
|
|
parent = parent.parentNode;
|
|
}
|
|
|
|
$scope.$apply(
|
|
function () {
|
|
invoker($scope, {$event: event});
|
|
}
|
|
);
|
|
};
|
|
|
|
$document.on("click", handler);
|
|
|
|
$scope.$on('$destroy', function () {
|
|
$document.off("click", handler);
|
|
});
|
|
|
|
// Special handling for tooltips which don't destroy the scope
|
|
var hideReg = $scope.$on('tooltip.hide', function () {
|
|
if(!ignorePopupEvents) {
|
|
$document.off("click", handler);
|
|
hideReg();
|
|
}
|
|
});
|
|
|
|
};
|
|
|
|
// Return the linking function.
|
|
return( linkFunction );
|
|
}
|
|
]);
|
|
|
|
|
|
|
|
|
|
flowableModule
|
|
.directive('autoFocus', ['$timeout', '$parse', function($timeout, $parse) {
|
|
return {
|
|
restrict: 'AC',
|
|
compile: function($element, attr) {
|
|
var selectText;
|
|
|
|
if(attr["selectText"]) {
|
|
selectText = $parse(attr["selectText"]);
|
|
}
|
|
|
|
return function(_scope, _element, _attrs) {
|
|
var firstChild = (_attrs.focusFirstChild !== undefined);
|
|
$timeout(function () {
|
|
if (firstChild) {
|
|
// look for first input-element in child-tree and focus that
|
|
var inputs = _element.find('input');
|
|
if (inputs && inputs.length > 0) {
|
|
inputs[0].focus();
|
|
|
|
if(selectText && selectText(_scope.$parent)) {
|
|
input[0].setSelectionRange(0,input[0].value.length);
|
|
}
|
|
}
|
|
} else {
|
|
// Focus element where the directive is put on
|
|
_element[0].focus();
|
|
if(selectText && selectText(_scope.$parent)) {
|
|
_element[0].setSelectionRange(0,_element[0].value.length);
|
|
}
|
|
}
|
|
}, 100);
|
|
}
|
|
}
|
|
};
|
|
}]);
|
|
|
|
flowableModule
|
|
.directive('focusWhen', ['$timeout', function ($timeout) {
|
|
return {
|
|
link: function (scope, element, attrs) {
|
|
scope.$watch(attrs.ngFocus, function (val) {
|
|
if (angular.isDefined(val) && val) {
|
|
$timeout(function () {
|
|
element[0].focus();
|
|
});
|
|
}
|
|
}, true);
|
|
|
|
element.bind('blur', function () {
|
|
if (angular.isDefined(attrs.ngFocusLost)) {
|
|
scope.safeApply(attrs.ngFocusLost);
|
|
}
|
|
});
|
|
}
|
|
};
|
|
}]);
|
|
|
|
|
|
flowableModule
|
|
.directive('loading', [function() {
|
|
var directive = {};
|
|
directive.restrict = 'A';
|
|
directive.template = '<div class="loading" ng-show="loading"><div class="l1"></div><div class="l2"></div><div class="l3"></div></div>';
|
|
directive.scope = {
|
|
loading : "=loading",
|
|
loadingText: "=loadingText"
|
|
};
|
|
return directive;
|
|
}]);
|
|
|
|
// Workaround for https://github.com/twbs/bootstrap/issues/8379 :
|
|
// prototype.js interferes with regular dropdown behavior
|
|
flowableModule
|
|
.directive('activitiFixDropdownBug', function() {
|
|
return {
|
|
restrict: 'AEC',
|
|
link: function(scope, element, attrs) {
|
|
element.on('hidden.bs.dropdown ', function () {
|
|
element.show(); // evil prototype.js has added display:none to it ...
|
|
})
|
|
}
|
|
};
|
|
});
|
|
|
|
/**
|
|
* Directive for rendering user link.
|
|
*/
|
|
flowableModule
|
|
.directive('userLink', function() {
|
|
var directive = {};
|
|
directive.template = '{{user.firstName && user.firstName || ""}} {{user.lastName && user.lastName || ""}} {{ (user.email && !user.firstName && !user.lastName) && user.email || ""}}';
|
|
directive.scope = {
|
|
user: "=userLink"
|
|
};
|
|
|
|
directive.compile = function(element, attributes) {
|
|
element.addClass('people-link');
|
|
};
|
|
|
|
return directive;
|
|
});
|
|
|
|
/**
|
|
* Directive for rendering a form field.
|
|
*/
|
|
flowableModule
|
|
.directive('formField', function () {
|
|
var directive = {};
|
|
|
|
directive.template = ' {{field.name || ""}} - {{field.id}}';
|
|
directive.scope = {
|
|
field: "=formField"
|
|
};
|
|
|
|
directive.compile = function (element, attributes) {
|
|
element.addClass('form-field');
|
|
};
|
|
return directive;
|
|
});
|
|
/**
|
|
* Directive to capture mouse up, down, enter and escape on input fields (eg. list navigation)
|
|
*/
|
|
flowableModule
|
|
.directive('customKeys', ["$parse", function ($parse) {
|
|
var directive = {};
|
|
directive.compile = function($element, attr) {
|
|
var up, down, enter, escape;
|
|
|
|
if(attr["upPressed"]) {
|
|
up = $parse(attr["upPressed"]);
|
|
}
|
|
if(attr["downPressed"]) {
|
|
down = $parse(attr["downPressed"]);
|
|
}
|
|
if(attr["enterPressed"]) {
|
|
enter = $parse(attr["enterPressed"]);
|
|
}
|
|
|
|
if(attr["escapePressed"]) {
|
|
escape = $parse(attr["escapePressed"]);
|
|
}
|
|
|
|
return function(scope, element, attr) {
|
|
element.on('keyup', function(e) {
|
|
if(e.keyCode === 38) {
|
|
scope.$apply(function() {
|
|
if(up) {
|
|
up(scope, {$event:e});
|
|
}
|
|
});
|
|
} else if(e.keyCode === 40) {
|
|
scope.$apply(function() {
|
|
if(down) {
|
|
down(scope, {$event:e});
|
|
}
|
|
});
|
|
} else if(e.keyCode === 13) {
|
|
scope.$apply(function() {
|
|
if(enter) {
|
|
enter(scope, {$event:e});
|
|
}
|
|
});
|
|
} else if(e.keyCode === 27) {
|
|
scope.$apply(function() {
|
|
if(escape) {
|
|
escape(scope, {$event:e});
|
|
}
|
|
});
|
|
}
|
|
});
|
|
|
|
element.on('keydown', element, function (e) {
|
|
if (e.keyCode === 38 || e.keyCode === 40 || e.keyCode === 13 || e.keyCode === 27)
|
|
e.preventDefault();
|
|
});
|
|
};
|
|
};
|
|
return directive;
|
|
}]);
|
|
|
|
// Delayed setting of model value in scope, based on input value unchanged after a number of millis
|
|
// See below: ngDebounce is preferred (as it hooks into ngModel, meaning that ng-change will keep working - but not with delayedModel)
|
|
flowableModule
|
|
.directive('delayedModel', ['$timeout', function($timeout) {
|
|
return {
|
|
scope: {
|
|
targetModel: '=delayedModel'
|
|
},
|
|
link: function(scope, element, attrs) {
|
|
|
|
element.val(scope.targetModel);
|
|
|
|
// Also watch model for any changes not triggered by timer
|
|
scope.$watch('targetModel', function(newVal, oldVal) {
|
|
if(scheduled) {
|
|
$timeout.cancel(scheduled);
|
|
}
|
|
if (newVal !== oldVal) {
|
|
element.val(scope.targetModel);
|
|
}
|
|
});
|
|
|
|
var scheduled;
|
|
element.on('keyup paste search', function() {
|
|
if(element.val() !== scope.targetModel) {
|
|
if(scheduled) {
|
|
$timeout.cancel(scheduled);
|
|
}
|
|
scheduled = $timeout(function() {
|
|
scope.targetModel = element[0].value;
|
|
element.val(scope.targetModel);
|
|
scope.$apply();
|
|
}, attrs.delay || 200);
|
|
}
|
|
});
|
|
}
|
|
};
|
|
}]);
|
|
|
|
|
|
// From https://gist.github.com/benbrandt22/bb44184a2eddcd4b0b8a
|
|
flowableModule.directive('ngDebounce', ['$timeout', function($timeout) {
|
|
return {
|
|
restrict: 'A',
|
|
require: 'ngModel',
|
|
priority: 99,
|
|
link: function(scope, elm, attr, ngModelCtrl) {
|
|
if (attr.type === 'radio' || attr.type === 'checkbox') return;
|
|
|
|
elm.unbind('input');
|
|
|
|
var debounce;
|
|
elm.bind('input', function() {
|
|
$timeout.cancel(debounce);
|
|
debounce = $timeout( function() {
|
|
scope.$apply(function() {
|
|
ngModelCtrl.$setViewValue(elm.val());
|
|
});
|
|
}, attr.ngDebounce || 1000);
|
|
});
|
|
elm.bind('blur', function() {
|
|
scope.$apply(function() {
|
|
ngModelCtrl.$setViewValue(elm.val());
|
|
});
|
|
});
|
|
}
|
|
|
|
}
|
|
}]);
|
|
|
|
flowableModule.
|
|
directive('selectPeoplePopover', ['$rootScope', '$http', '$popover', 'UserService', '$parse', function($rootScope, $http, $popover, UserService, $parse) {
|
|
var directive = {};
|
|
directive.restrict = 'A';
|
|
|
|
directive.scope = {
|
|
excludeTaskId: '=excludeTaskId',
|
|
excludeProcessId: '=excludeProcessId',
|
|
excludeUserId: '=excludeUserId',
|
|
excludeUserIds: '=excludeUserIds',
|
|
tenantId: '=tenantId',
|
|
type: '=type',
|
|
restrictWithGroup: '=restrictWithGroup',
|
|
selectPeopleFormFields: '=selectPeopleFormFields',
|
|
ignoreContainer: '=ignoreContainer'
|
|
};
|
|
|
|
directive.link = function($scope, $element, attrs) {
|
|
// Set defaults
|
|
var placement = "bottom";
|
|
|
|
$element.addClass("toggle-people-select");
|
|
|
|
if(attrs.placement) {
|
|
placement = attrs.placement;
|
|
}
|
|
|
|
var closeOnSelect = true;
|
|
if(attrs.closeOnSelect !== undefined) {
|
|
closeOnSelect = attrs.closeOnSelect;
|
|
}
|
|
|
|
if ($scope.ignoreContainer) {
|
|
$scope.popover = $popover($element, {template: 'views/common/popover/select-people-popover.html?' +
|
|
Date.now(), placement: placement});
|
|
|
|
} else {
|
|
$scope.popover = $popover($element, {template: 'views/common/popover/select-people-popover.html?' +
|
|
Date.now(), placement: placement, container: 'body'});
|
|
}
|
|
|
|
// Parse callbacks
|
|
var selectedCallback, cancelledCallback, emailSelectedCallback;
|
|
if (attrs['onPeopleSelected']) {
|
|
selectedCallback = $parse(attrs['onPeopleSelected']);
|
|
}
|
|
if (attrs['onCancel']) {
|
|
cancelledCallback = $parse(attrs['onCancel']);
|
|
}
|
|
if (attrs['onEmailSelected']) {
|
|
emailSelectedCallback = $parse(attrs['onEmailSelected']);
|
|
}
|
|
|
|
// Parse type
|
|
// Can be 'workflow' or 'idm'. In 'workflow', the users are retrieved for filling task assignments etc. This is the default if this param is omitted. 'idm' is more strict.
|
|
var backendType = 'workflow';
|
|
if ($scope.type !== null && $scope.type !== undefined) {
|
|
backendType = $scope.type;
|
|
}
|
|
|
|
var popoverScope = $scope.popover.$scope;
|
|
popoverScope.title = attrs['popoverTitle'];
|
|
|
|
popoverScope.popupModel = {
|
|
emailMode: false,
|
|
showRecentResults: false, // Disabled recent for the moment. Put this on true to set it back
|
|
userResults: [],
|
|
userField: {},
|
|
userFieldFilter: ['people']
|
|
};
|
|
|
|
if ($scope.selectPeopleFormFields) {
|
|
popoverScope.popupModel.formFields = $scope.selectPeopleFormFields;
|
|
}
|
|
|
|
if (attrs['emailModeDisabled']) {
|
|
var emailModeDisabledValue = attrs['emailModeDisabled'];
|
|
if (emailModeDisabledValue === 'true') {
|
|
popoverScope.popupModel.emailDisabled = true;
|
|
}
|
|
}
|
|
|
|
popoverScope.popupModel.emailMode = false;
|
|
|
|
|
|
popoverScope.setSearchType = function() {
|
|
popoverScope.popupModel.userSourceType = 'search';
|
|
};
|
|
|
|
popoverScope.setFormFieldType = function() {
|
|
popoverScope.popupModel.userSourceType = 'field';
|
|
};
|
|
|
|
popoverScope.$watch('popupModel.userField', function() {
|
|
if (popoverScope.popupModel.userField && popoverScope.popupModel.userField.id) {
|
|
if (selectedCallback) {
|
|
// Run callback in parent scope of directive
|
|
var simpleUserField = {
|
|
id: popoverScope.popupModel.userField.id,
|
|
name: popoverScope.popupModel.userField.name,
|
|
type: popoverScope.popupModel.userField.type
|
|
}
|
|
|
|
selectedCallback($scope.$parent, {'userField': simpleUserField});
|
|
popoverScope.popupModel.userField = {};
|
|
}
|
|
|
|
if (closeOnSelect || closeOnSelect === 'true') {
|
|
popoverScope.$hide();
|
|
}
|
|
}
|
|
});
|
|
|
|
popoverScope.$watch('popupModel.filter', function() {
|
|
if (popoverScope.popupModel.filter && popoverScope.popupModel.filter.length > 0) {
|
|
|
|
var userGetPromise;
|
|
if (backendType === 'idm') {
|
|
userGetPromise = UserService.getFilteredUsersStrict(popoverScope.popupModel.filter, $scope.tenantId, $scope.restrictWithGroup);
|
|
} else {
|
|
// Default: go to workflow users backend
|
|
userGetPromise = UserService.getFilteredUsers(popoverScope.popupModel.filter, $scope.excludeTaskId,
|
|
$scope.excludeProcessId, $scope.tenantId, $scope.restrictWithGroup);
|
|
}
|
|
|
|
userGetPromise.then(function(result) {
|
|
popoverScope.popupModel.showRecentResults = false;
|
|
|
|
var users = [];
|
|
var excludeUserIdSet = $scope.excludeUserId !== null && $scope.excludeUserId !== undefined;
|
|
var excludeUserIdsSet = $scope.excludeUserIds !== null && $scope.excludeUserIds !== undefined;
|
|
if (excludeUserIdSet === true || excludeUserIdsSet === true) {
|
|
for (var userIndex=0; userIndex < result.data.length; userIndex++) {
|
|
|
|
var userExcluded = false;
|
|
if (excludeUserIdSet === true && result.data[userIndex].id === $scope.excludeUserId) {
|
|
userExcluded = true;
|
|
}
|
|
if (excludeUserIdsSet === true && $scope.excludeUserIds.indexOf(result.data[userIndex].id) >= 0) {
|
|
userExcluded = true;
|
|
}
|
|
|
|
if (!userExcluded) {
|
|
users.push(result.data[userIndex]);
|
|
}
|
|
|
|
}
|
|
} else {
|
|
users = result.data;
|
|
}
|
|
popoverScope.popupModel.userResults = users;
|
|
popoverScope.resetSelection();
|
|
});
|
|
} else {
|
|
popoverScope.resetSelection();
|
|
popoverScope.popupModel.userResults = [];
|
|
}
|
|
});
|
|
|
|
popoverScope.resetSelection = function() {
|
|
popoverScope.popupModel.selectedUser = undefined;
|
|
popoverScope.popupModel.selectedIndex = -1;
|
|
};
|
|
|
|
popoverScope.nextUser = function() {
|
|
var users = popoverScope.popupModel.userResults;
|
|
if(users && users.length > 0 && popoverScope.popupModel.selectedIndex < users.length -1) {
|
|
popoverScope.popupModel.selectedIndex+=1;
|
|
popoverScope.popupModel.selectedUser = users[popoverScope.popupModel.selectedIndex];
|
|
}
|
|
};
|
|
|
|
popoverScope.previousUser = function() {
|
|
var users = popoverScope.popupModel.userResults;
|
|
if(users && users.length > 0 && popoverScope.popupModel.selectedIndex > 0) {
|
|
popoverScope.popupModel.selectedIndex-=1;
|
|
popoverScope.popupModel.selectedUser = users[popoverScope.popupModel.selectedIndex];
|
|
}
|
|
};
|
|
|
|
popoverScope.confirmUser = function(user) {
|
|
if (!user) {
|
|
// Selection is done with keyboard, use selection index
|
|
var users = popoverScope.popupModel.userResults;
|
|
if (popoverScope.popupModel.selectedIndex >= 0 && popoverScope.popupModel.selectedIndex <users.length) {
|
|
user = users[popoverScope.popupModel.selectedIndex];
|
|
}
|
|
}
|
|
|
|
if (user) {
|
|
if (selectedCallback) {
|
|
// Run callback in parent scope of directive
|
|
selectedCallback($scope.$parent, {'user': user});
|
|
}
|
|
|
|
if (closeOnSelect === 'true') {
|
|
popoverScope.$hide();
|
|
} else {
|
|
var users = popoverScope.popupModel.userResults;
|
|
users.splice(jQuery.inArray(user, users),1);
|
|
popoverScope.popupModel.selectedIndex=0;
|
|
popoverScope.popupModel.selectedUser = users[popoverScope.popupModel.selectedIndex];
|
|
}
|
|
}
|
|
};
|
|
|
|
popoverScope.selectPersonByEmail = function(validEmail) { // Not so nice we have to pass the valid email agrument, but couldnt make it work properly
|
|
if (validEmail) {
|
|
if (emailSelectedCallback) {
|
|
emailSelectedCallback($scope.$parent, {email: popoverScope.popupModel.email});
|
|
popoverScope.$hide();
|
|
}
|
|
}
|
|
};
|
|
|
|
popoverScope.$on('tooltip.hide', function() {
|
|
// Invalidate recent results
|
|
if(popoverScope.popupModel.showRecentResults && popoverScope.popupModel.added) {
|
|
popoverScope.popupModel.recentUsers = [];
|
|
}
|
|
popoverScope.popupModel.userResults = [];
|
|
popoverScope.popupModel.filter = '';
|
|
popoverScope.popupModel.emailMode = false;
|
|
|
|
if(popoverScope.popupModel.added) {
|
|
popoverScope.popupModel.added = false;
|
|
} else {
|
|
if(cancelledCallback) {
|
|
// Run callback in parent scope of directive
|
|
cancelledCallback($scope.$parent);
|
|
}
|
|
}
|
|
});
|
|
|
|
};
|
|
return directive;
|
|
}]);
|
|
|
|
flowableModule.
|
|
directive('selectFunctionalGroupPopover', ['$rootScope', '$http', '$popover', 'FunctionalGroupService', '$parse',
|
|
function($rootScope, $http, $popover, FunctionalGroupService, $parse) {
|
|
|
|
var directive = {};
|
|
directive.restrict = 'A';
|
|
|
|
directive.scope = {
|
|
type: '=type',
|
|
ignoreContainer: '=ignoreContainer',
|
|
restrictWithGroup: '=restrictWithGroup',
|
|
excludeGroupIds: '=excludeGroupIds'
|
|
};
|
|
|
|
directive.link = function($scope, $element, attrs) {
|
|
// Set defaults
|
|
var placement = "bottom";
|
|
|
|
$element.addClass("toggle-functional-group-select");
|
|
|
|
if (attrs.placement) {
|
|
placement = attrs.placement;
|
|
}
|
|
|
|
var closeOnSelect = true;
|
|
if (attrs.closeOnSelect !== undefined) {
|
|
closeOnSelect = attrs.closeOnSelect;
|
|
}
|
|
|
|
if ($scope.ignoreContainer) {
|
|
$scope.popover = $popover($element, {template: 'views/common/popover/select-functional-group-popover.html?' +
|
|
Date.now(), placement: placement});
|
|
|
|
} else {
|
|
$scope.popover = $popover($element, {template: 'views/common/popover/select-functional-group-popover.html?' +
|
|
Date.now(), placement: placement, container: 'body'});
|
|
}
|
|
|
|
// Parse callbacks
|
|
var selectedCallback, cancelledCallback;
|
|
if (attrs['onGroupSelected']) {
|
|
selectedCallback = $parse(attrs['onGroupSelected']);
|
|
}
|
|
if (attrs['onCancel']) {
|
|
cancelledCallback = $parse(attrs['onCancel']);
|
|
}
|
|
|
|
var popoverScope = $scope.popover.$scope;
|
|
popoverScope.title = attrs['popoverTitle'];
|
|
|
|
popoverScope.popupModel = {
|
|
groupResults: []
|
|
};
|
|
|
|
popoverScope.$watch('popupModel.filter', function() {
|
|
if (popoverScope.popupModel.filter && popoverScope.popupModel.filter.length > 0) {
|
|
|
|
var tenantId;
|
|
if ($rootScope.common !== null && $rootScope.common !== undefined && $rootScope.common.selectedTenantId !== null && $rootScope.common.selectedTenantId !== undefined) {
|
|
tenantId = $rootScope.common.selectedTenantId > 0 ? $rootScope.common.selectedTenantId : undefined;
|
|
}
|
|
|
|
FunctionalGroupService.getFilteredGroups(popoverScope.popupModel.filter, $scope.restrictWithGroup, tenantId).then(function(result) {
|
|
var groups = [];
|
|
if ($scope.excludeGroupId != null && $scope.excludeGroupId) {
|
|
for (var groupIndex=0; groupIndex < result.data.length; groupIndex++) {
|
|
if (result.data[groupIndex].id !== $scope.excludeGroupId) {
|
|
groups.push(result.data[groupIndex]);
|
|
}
|
|
}
|
|
} else if ($scope.excludeGroupIds != null && $scope.excludeGroupIds !== undefined) {
|
|
for (var groupIndex=0; groupIndex < result.data.length; groupIndex++) {
|
|
if ($scope.excludeGroupIds.indexOf(result.data[groupIndex].id) < 0) {
|
|
groups.push(result.data[groupIndex]);
|
|
}
|
|
}
|
|
} else {
|
|
groups = result.data;
|
|
}
|
|
popoverScope.popupModel.groupResults = groups;
|
|
popoverScope.resetSelection();
|
|
});
|
|
} else {
|
|
popoverScope.resetSelection();
|
|
popoverScope.popupModel.groupResults = [];
|
|
}
|
|
});
|
|
|
|
popoverScope.resetSelection = function() {
|
|
popoverScope.popupModel.selectedGroup = undefined;
|
|
popoverScope.popupModel.selectedIndex = -1;
|
|
};
|
|
|
|
popoverScope.nextGroup = function() {
|
|
var groups = popoverScope.popupModel.groupResults;
|
|
if (groups && groups.length > 0 && popoverScope.popupModel.selectedIndex < groups.length -1) {
|
|
popoverScope.popupModel.selectedIndex+=1;
|
|
popoverScope.popupModel.groupUser = groups[popoverScope.popupModel.selectedIndex];
|
|
}
|
|
};
|
|
|
|
popoverScope.previousGroup = function() {
|
|
var groups = popoverScope.popupModel.groupResults;
|
|
if (groups && groups.length > 0 && popoverScope.popupModel.selectedIndex > 0) {
|
|
popoverScope.popupModel.selectedIndex-=1;
|
|
popoverScope.popupModel.selectedGroup = groups[popoverScope.popupModel.selectedIndex];
|
|
}
|
|
};
|
|
|
|
popoverScope.confirmGroup = function(group) {
|
|
if (!group) {
|
|
// Selection is done with keyboard, use selection index
|
|
var groups = popoverScope.popupModel.groupResults;
|
|
if (popoverScope.popupModel.selectedIndex >= 0 && popoverScope.popupModel.selectedIndex < groups.length) {
|
|
group = groups[popoverScope.popupModel.selectedIndex];
|
|
}
|
|
}
|
|
|
|
if (group) {
|
|
if(selectedCallback) {
|
|
// Run callback in parent scope of directive
|
|
selectedCallback($scope.$parent, {'group': group});
|
|
}
|
|
|
|
if (closeOnSelect === 'true') {
|
|
popoverScope.$hide();
|
|
} else {
|
|
var groups = popoverScope.popupModel.groupResults;
|
|
groups.splice(jQuery.inArray(group, groups), 1);
|
|
popoverScope.popupModel.selectedIndex = 0;
|
|
popoverScope.popupModel.selectedGroup = groups[popoverScope.popupModel.selectedIndex];
|
|
}
|
|
}
|
|
};
|
|
|
|
popoverScope.$on('tooltip.hide', function() {
|
|
popoverScope.popupModel.groupResults = [];
|
|
popoverScope.popupModel.filter = '';
|
|
|
|
if (popoverScope.popupModel.added) {
|
|
popoverScope.popupModel.added = false;
|
|
} else {
|
|
if (cancelledCallback) {
|
|
// Run callback in parent scope of directive
|
|
cancelledCallback($scope.$parent);
|
|
}
|
|
}
|
|
});
|
|
|
|
};
|
|
return directive;
|
|
}]);
|
|
|
|
flowableModule.directive('tabControl', ['$compile', '$http', '$templateCache', function($compile, $http, $templateCache) {
|
|
|
|
var updateTemplate = function($scope, element, attributes) {
|
|
if(!$scope.activeTemplate || $scope.activeTemplate != $scope.activeTab.id) {
|
|
// Check if current loaded template is still the right one
|
|
var contentDiv = $(element.children()[1]);
|
|
|
|
var childScope = angular.element(element.children()[1]).scope();
|
|
if($scope.activeTemplate && childScope != $scope) {
|
|
// Child-scope created by the included element, should be destroyed
|
|
childScope.$destroy();
|
|
}
|
|
|
|
if($scope.activeTab && $scope.activeTab.templateUrl) {
|
|
// Load the HTML-fragment or get from cache
|
|
var loader = $http.get($scope.activeTab.templateUrl, {cache: $templateCache});
|
|
var promise = loader.success(function(html) {
|
|
contentDiv.html(html);
|
|
}).then(function (response) {
|
|
$scope.activeTemplate = $scope.activeTab.id;
|
|
contentDiv.replaceWith($compile(contentDiv.html())($scope));
|
|
});
|
|
} else {
|
|
// No templates are being used, no need to use the contentDiv for this tab, clear it
|
|
contentDiv.empty();
|
|
}
|
|
}
|
|
};
|
|
|
|
var directive = {};
|
|
directive.restrict = 'A';
|
|
directive.replace = true;
|
|
directive.transclude = true;
|
|
directive.template = '<div><div class="clearfix"><ul class="tabs clearfix">' +
|
|
'<li ng-repeat="tab in tabs" ng-class="{\'active\': tab.id == activeTab.id}"><a ng-click="tabClicked(tab)">{{tab.title && (tab.title | translate) || (tab.name | translate)}}</a></li>' +
|
|
'</ul></div>' +
|
|
'<div></div>' +
|
|
'</div>';
|
|
|
|
directive.scope = {
|
|
possibleTabs : "=tabControl",
|
|
model: "=model",
|
|
activeTabReference: "=activeTab"
|
|
};
|
|
|
|
|
|
directive.controller = ['$scope', '$element', function($scope, $element) {
|
|
|
|
$scope.refreshTabs = function() {
|
|
var tabs = [];
|
|
for(var i=0; i < $scope.possibleTabs.length; i++) {
|
|
var tab = $scope.possibleTabs[i];
|
|
if(!tab.hide) {
|
|
tabs.push(tab);
|
|
}
|
|
}
|
|
$scope.tabs = tabs;
|
|
};
|
|
|
|
$scope.$watch('possibleTabs', function() {
|
|
$scope.refreshTabs();
|
|
}, true);
|
|
|
|
$scope.$watch('activeTabReference', function(newValue, oldValue) {
|
|
if(!$scope.activeTab || $scope.activeTab.id != newValue) {
|
|
// Active tab ID changed from outside of the directive controller, need to switch to the
|
|
// right tab within this scope
|
|
var newTab = $scope.findTab(newValue);
|
|
if(newTab) {
|
|
$scope.tabClicked(newTab);
|
|
}
|
|
}
|
|
});
|
|
|
|
$scope.findTab = function(tabId) {
|
|
if($scope.possibleTabs) {
|
|
for(var i=0; i< $scope.possibleTabs.length; i++) {
|
|
if($scope.possibleTabs[i].id == tabId) {
|
|
return $scope.possibleTabs[i];
|
|
}
|
|
}
|
|
}
|
|
return undefined;
|
|
};
|
|
|
|
$scope.tabClicked = function(tab) {
|
|
if (tab.hide) {
|
|
tab.hide = false;
|
|
$scope.refreshTabs();
|
|
}
|
|
$scope.activeTab = tab;
|
|
if (tab) {
|
|
$scope.activeTabReference = tab.id;
|
|
} else {
|
|
$scope.activeTabReference = undefined;
|
|
}
|
|
updateTemplate($scope, $element);
|
|
};
|
|
|
|
$scope.refreshTabs();
|
|
|
|
if($scope.tabs && $scope.tabs.length > 0) {
|
|
if($scope.activeTabReference) {
|
|
$scope.activeTab = $scope.findTab($scope.activeTabReference);
|
|
}
|
|
|
|
if(!$scope.activeTab) {
|
|
// Revert to the first tab, if no tab is forced to be shown first
|
|
$scope.activeTab = $scope.tabs[0];
|
|
}
|
|
$scope.tabClicked($scope.activeTab);
|
|
}
|
|
}];
|
|
|
|
directive.link = updateTemplate;
|
|
|
|
return directive;
|
|
}]);
|
|
|
|
/**
|
|
* Directive that calls the function present in the toggle-dragover attribute with a single parameter (over) when
|
|
* dragging over the element has started (over = true) or ended (over = false)
|
|
*/
|
|
flowableModule
|
|
.directive('toggleDragover', ["$document", "$parse", function ($document, $parse) {
|
|
var linkFunction = function ($scope, $element, $attributes) {
|
|
|
|
|
|
var toggleFunction = $attributes.toggleDragover;
|
|
var callback = $parse(toggleFunction);
|
|
|
|
var el = $element[0];
|
|
|
|
el.addEventListener('dragenter',function(e) {
|
|
$scope.$apply(function() {
|
|
callback($scope, {'over': true});
|
|
});
|
|
return false;
|
|
},
|
|
false
|
|
);
|
|
|
|
el.addEventListener('dragleave', function(e) {
|
|
$scope.$apply(function() {
|
|
callback($scope, {'over': false});
|
|
});
|
|
return false;
|
|
},
|
|
false
|
|
);
|
|
};
|
|
|
|
return( linkFunction );
|
|
}]);
|
|
|
|
flowableModule.directive('editInPlace', function () {
|
|
return {
|
|
restrict: 'E',
|
|
scope: {
|
|
value: '='
|
|
},
|
|
template: '<span ng-click="edit()" ng-bind="value"></span><span class="glyphicon glyphicon-pencil edit-in-place-icon"></span><input ng-model="value" class="inline-edit-value form-control" ng-blur="stopEdit()" custom-keys enter-pressed="stopEdit()">',
|
|
link: function ($scope, element, attrs) {
|
|
|
|
var iconElement = angular.element(element.children()[1]);
|
|
var inputElement = angular.element(element.children()[2]);
|
|
|
|
// This directive should have a set class so we can style it.
|
|
element.addClass('edit-in-place');
|
|
|
|
// Initially, we're not editing.
|
|
$scope.editing = false;
|
|
|
|
// ng-click handler to activate edit-in-place
|
|
$scope.edit = function () {
|
|
$scope.editing = true;
|
|
|
|
// We control display through a class on the directive itself. See the CSS.
|
|
element.addClass('active');
|
|
|
|
// And we must focus the element.
|
|
// `angular.element()` provides a chainable array, like jQuery so to access a native DOM function,
|
|
// we have to reference the first element in the array.
|
|
inputElement[0].focus();
|
|
};
|
|
|
|
$scope.stopEdit = function() {
|
|
$scope.editing = false;
|
|
element.removeClass('active');
|
|
};
|
|
|
|
// // When we leave the input, we're done editing.
|
|
// inputElement.prop('onblur', function () {
|
|
// console.log('ONBLUR');
|
|
// $scope.editing = false;
|
|
// element.removeClass('active');
|
|
// });
|
|
}
|
|
};
|
|
});
|
|
|
|
|
|
/* UTILITY METHODS */
|
|
|
|
/**
|
|
* This creates a modal window that auto closes on route change.
|
|
* By default, this is NOT the case, and leads to some funny behaviour.
|
|
*
|
|
* Use this method vs the default $modal({myJson}) approach
|
|
*/
|
|
var _internalCreateModal = function(modalConfig, $modal, $scope) {
|
|
|
|
if ($scope !== null && $scope !== undefined) {
|
|
$scope.modal = $modal(modalConfig);
|
|
|
|
$scope.$on('$routeChangeStart', function () {
|
|
if ($scope.modal) {
|
|
$scope.modal.hide();
|
|
}
|
|
});
|
|
|
|
return $scope.modal;
|
|
} else {
|
|
return $modal(modalConfig);
|
|
}
|
|
|
|
};
|
|
|
|
flowableModule.
|
|
directive('numberInputCheck', function() {
|
|
|
|
return {
|
|
require: 'ngModel',
|
|
link: function(scope, element, attrs, modelCtrl) {
|
|
|
|
modelCtrl.$parsers.push(function (inputValue) {
|
|
|
|
var transformedInput;
|
|
if (inputValue && inputValue.indexOf('-') == 0) {
|
|
transformedInput = inputValue.substr(1).replace(/([^0-9])/g, '');
|
|
transformedInput = '-' + transformedInput;
|
|
} else {
|
|
transformedInput = inputValue.replace(/([^0-9])/g, '');
|
|
}
|
|
|
|
if (transformedInput != inputValue) {
|
|
modelCtrl.$setViewValue(transformedInput);
|
|
modelCtrl.$render();
|
|
}
|
|
|
|
return transformedInput;
|
|
});
|
|
}
|
|
};
|
|
});
|
|
|
|
flowableModule.
|
|
directive('decimalNumberInputCheck', function() {
|
|
|
|
return {
|
|
require: 'ngModel',
|
|
link: function(scope, element, attrs, modelCtrl) {
|
|
|
|
modelCtrl.$parsers.push(function (inputValue) {
|
|
|
|
var transformedInput = inputValue;
|
|
var negativeSign = '';
|
|
|
|
if (transformedInput && transformedInput.indexOf('-') == 0) {
|
|
negativeSign = '-';
|
|
transformedInput = inputValue.substr(1);
|
|
}
|
|
|
|
if(transformedInput && transformedInput.indexOf('.') == 0 ){
|
|
transformedInput = "0" + transformedInput;
|
|
}
|
|
|
|
if(transformedInput && transformedInput.indexOf('.') > -1){
|
|
var dotIndex = transformedInput.indexOf('.');
|
|
var left = transformedInput.substr(0, dotIndex);
|
|
var right = transformedInput.substr(dotIndex+1);
|
|
|
|
left = left.replace(/([^0-9])/g, '');
|
|
right = right.replace(/([^0-9])/g, '');
|
|
transformedInput = negativeSign + left + '.' + right;
|
|
}
|
|
else{
|
|
transformedInput = negativeSign + transformedInput.replace(/([^0-9])/g, '');
|
|
}
|
|
|
|
|
|
if (transformedInput != inputValue) {
|
|
modelCtrl.$setViewValue(transformedInput);
|
|
modelCtrl.$render();
|
|
}
|
|
|
|
return transformedInput;
|
|
});
|
|
}
|
|
};
|
|
});
|
|
|