425 lines
22 KiB
JavaScript
425 lines
22 KiB
JavaScript
"use strict";
|
|
(function () {
|
|
'use strict';
|
|
angular.module('myitsmApp')
|
|
.directive('editable', function () {
|
|
return {
|
|
require: 'ngModel',
|
|
link: function (scope, elm, attrs, ctrl) {
|
|
/* view -> model */
|
|
function keyUpClickHandler() {
|
|
scope.$apply(function () {
|
|
ctrl.$setViewValue(elm.text());
|
|
});
|
|
}
|
|
elm.on('keyup click', keyUpClickHandler);
|
|
scope.$on("$destroy", function () {
|
|
console.log("editable: unbind events");
|
|
elm.off('keyup', keyUpClickHandler);
|
|
elm.off('click', keyUpClickHandler);
|
|
});
|
|
/* model -> view */
|
|
ctrl.$render = function () {
|
|
if (_.isUndefined(ctrl.$viewValue)) {
|
|
elm.text('');
|
|
}
|
|
else {
|
|
elm.text(ctrl.$viewValue);
|
|
}
|
|
};
|
|
}
|
|
};
|
|
})
|
|
.directive('editSummary', ['events', '$timeout', 'attachmentService', 'ticketModel', '$q', 'systemAlertService', 'i18nService',
|
|
'$filter', '$compile', 'objectValueMapperService', '$rootScope',
|
|
function (events, $timeout, attachmentService, ticketModel, $q, systemAlertService, i18nService, $filter, $compile, objectValueMapperService, $rootScope) {
|
|
return {
|
|
restrict: 'E',
|
|
templateUrl: 'views/common/edit-summary-directive.html',
|
|
replace: true,
|
|
scope: {
|
|
ticket: '=',
|
|
context: '=',
|
|
textAreaName: '@',
|
|
textplaceholder: '=',
|
|
isDescRequired: '=',
|
|
updateIsHandledByParent: '=',
|
|
dropable: '=',
|
|
label: '@',
|
|
editDisabled: '=',
|
|
type: '=',
|
|
editMode: '=?',
|
|
data: '=?',
|
|
isEditable: '=?',
|
|
showAttachmentIcon: '=?',
|
|
descLimit: '=?'
|
|
},
|
|
link: function (scope, element, attr) {
|
|
var container = element.find('.ticket-summary__content'), textAreaObject = container.find('textarea.content');
|
|
var originalAttachment = _.cloneDeep(scope.ticket.attachments);
|
|
function init() {
|
|
if (!_.isBoolean(scope.isEditable)) {
|
|
scope.isEditable = true;
|
|
}
|
|
if (_.isUndefined(scope.ticket.attachments)) {
|
|
scope.ticket.attachments = [];
|
|
}
|
|
if (_.isUndefined(scope.ticket.desc)) {
|
|
scope.ticket.desc = '';
|
|
}
|
|
if (scope.isDescRequired) {
|
|
textAreaObject.prop('required', true);
|
|
textAreaObject.attr('aria-required', 'true');
|
|
}
|
|
if ((scope.ticket.workNote && scope.ticket.workNote.locked) || !scope.isEditable) {
|
|
scope.isLocked = true;
|
|
}
|
|
scope.descCopy = scope.ticket.desc;
|
|
scope.attachments = scope.ticket.attachments;
|
|
scope.state = {
|
|
descriptionChanged: false
|
|
};
|
|
scope.showAttachment = scope.ticket.type === EntityVO.TYPE_CHANGE ? false : attr.attachment === 'true';
|
|
scope.descLimit = attr.descLimit ? parseInt(attr.descLimit, 10) : '';
|
|
scope.attachmentLimit = scope.showAttachment && attr.attachmentLimit ? parseInt(attr.attachmentLimit, 10) : 9999;
|
|
if (scope.ticket.type === EntityVO.TYPE_TASK) {
|
|
scope.attachmentLimit = 3;
|
|
}
|
|
scope.attachmentToUpload = [];
|
|
scope.attachmentToDelete = [];
|
|
scope.attachmentDropped = false;
|
|
scope.textAreaIsFocused = false;
|
|
if (scope.data) {
|
|
_.each(scope.data.members, function (member) {
|
|
if (member.required) {
|
|
scope.data.isRequired = member.required;
|
|
}
|
|
});
|
|
}
|
|
scope.$on(events.AFTER_SAVED_CHANGES, afterSaveChangesHandler);
|
|
scope.$on(events.TOGGLE_EDIT_MODE, handleToggleEditMode);
|
|
scope.$on(events.SAVE_CHANGES, handleSaveChanges);
|
|
scope.$on(events.SAVE_ALL_CHANGES_COMPLETE, handleSaveAllChangesComplete);
|
|
scope.$on(events.DISCARD_CHANGES, handleDiscardChanges);
|
|
scope.$on(events.DESC_CHANGED, function () {
|
|
scope.descCopy = scope.ticket.desc;
|
|
});
|
|
scope.$on(events.CLEAR_DESC, function () {
|
|
scope.descCopy = '';
|
|
if (scope.ticket) {
|
|
scope.ticket.desc = '';
|
|
}
|
|
});
|
|
var unbindRootScopeListener = $rootScope.$on(events.TICKET_TEMPLATE_UPDATED, function (e, value) {
|
|
if (scope.ticket.ticketType !== value.ticketType) {
|
|
return;
|
|
}
|
|
scope.descCopy = value.desc;
|
|
if (scope.data) {
|
|
scope.data.value.desc = scope.descCopy;
|
|
scope.$emit(events.WIDGET_VALUE_CHANGE, { fieldName: scope.data.name });
|
|
}
|
|
});
|
|
scope.$watch('descCopy', function () {
|
|
adjustDescriptionHeight();
|
|
});
|
|
scope.$on('$destroy', function () {
|
|
unbindRootScopeListener();
|
|
container = null;
|
|
textAreaObject = null;
|
|
});
|
|
}
|
|
if (scope.data) {
|
|
scope.$watch('data.setValueFlag', function (value) {
|
|
if (value && value !== '#$#') {
|
|
scope.descCopy = value;
|
|
scope.onTicketDescriptionChange();
|
|
scope.data.setValueFlag = '#$#';
|
|
}
|
|
});
|
|
}
|
|
/**
|
|
* Handle file change
|
|
* @param inputDOM
|
|
*/
|
|
scope.handleFileChange = function (inputDOM) {
|
|
var attachment = attachmentService.prepareFileToUpload({ fileInput: inputDOM });
|
|
if (attachment && attachment.size <= 0) {
|
|
systemAlertService.warning({
|
|
text: $filter('i18n')('attachment.file_empty'),
|
|
icon: 'icon-exclamation_triangle',
|
|
clear: true,
|
|
hide: 5000
|
|
});
|
|
return;
|
|
}
|
|
if (attachment) {
|
|
//Defect #SW00488589. When attaching a file, then removing it and then attaching it again
|
|
//onchange event is not firing, because input receives same file, so from DOM state perspective nothing has changed
|
|
inputDOM.value = null;
|
|
var inputClone = angular.element(inputDOM.cloneNode());
|
|
$compile(inputClone)(scope);
|
|
angular.element(inputDOM)
|
|
.before(inputClone)
|
|
.remove();
|
|
$timeout(function () {
|
|
inputClone.focus();
|
|
scope.attachments.push(attachment);
|
|
if (scope.context && scope.context === 'detail') {
|
|
scope.attachmentToUpload.push(attachment);
|
|
}
|
|
var descField = objectValueMapperService.getFieldByName('desc');
|
|
if (descField) {
|
|
// process attachments to upload
|
|
if (_.size(scope.attachmentToUpload)) {
|
|
descField.value.attachmentToUpload = scope.attachmentToUpload;
|
|
}
|
|
}
|
|
});
|
|
}
|
|
};
|
|
/**
|
|
* Handle attachment dismissal
|
|
*
|
|
* @param $event
|
|
* @param attachment
|
|
*/
|
|
scope.dismissAttachment = function ($event, attachment) {
|
|
attachmentService.dismissAttachment(scope, $event, attachment, scope.context);
|
|
var node = $event.currentTarget;
|
|
angular.element(node).parents('div.editable-summary').find('textarea').focus();
|
|
};
|
|
/**
|
|
* Handle click on attachment
|
|
*
|
|
* @param attachment
|
|
*/
|
|
scope.handleAttachmentClick = function (attachment) {
|
|
if (scope.context === 'draft' || scope.context === 'create') {
|
|
return false;
|
|
}
|
|
else {
|
|
attachmentService.getAttachmentFile(scope.ticket.type ? scope.ticket.type : scope.type, attachment);
|
|
}
|
|
};
|
|
/**
|
|
* Switch to edit mode.
|
|
*/
|
|
scope.edit = function () {
|
|
//May have set value so refer data object if present
|
|
if (scope.data && scope.data.value) {
|
|
scope.descCopy = scope.data.value.desc;
|
|
}
|
|
else {
|
|
scope.descCopy = _.clone(scope.ticket.desc);
|
|
}
|
|
scope.editMode = true;
|
|
adjustDescriptionHeight();
|
|
};
|
|
/**
|
|
* Collect changes
|
|
*
|
|
* @returns {Object}
|
|
*/
|
|
function collectChanges() {
|
|
if (scope.state.descriptionChanged) {
|
|
return { desc: scope.descCopy };
|
|
}
|
|
else {
|
|
return {};
|
|
}
|
|
}
|
|
function draftModeEnabled() {
|
|
return scope.context === 'draft';
|
|
}
|
|
/**
|
|
* Save changes
|
|
* @returns {Array}
|
|
*/
|
|
scope.save = function () {
|
|
var promiseArray = [];
|
|
var isDraft = draftModeEnabled();
|
|
var changes = collectChanges();
|
|
var singleMode = !scope.updateIsHandledByParent;
|
|
if (!isDraft) {
|
|
if (_.size(changes) && singleMode) {
|
|
promiseArray.push(ticketModel.update(scope.ticket.id, scope.ticket.type, changes));
|
|
}
|
|
// process attachments to upload
|
|
if (_.size(scope.attachmentToUpload)) {
|
|
promiseArray.push(attachmentService.uploadAttachment(scope.ticket.type, scope.ticket.id, scope.attachmentToUpload)
|
|
.then(function (response) {
|
|
var attachments = _.union(scope.attachments, response);
|
|
scope.attachments = _.filter(attachments, function (attach) {
|
|
return !attach.pendingSave;
|
|
});
|
|
}));
|
|
}
|
|
// process attachments to delete
|
|
if (_.size(scope.attachmentToDelete)) {
|
|
_.each(scope.attachmentToDelete, function (attachment) {
|
|
promiseArray.push(attachmentService.deleteAttachment(scope.ticket.type, {
|
|
dataSourceId: attachment.attachmentReference.dataSourceId,
|
|
attachmentId: attachment.attachmentReference.attachmentId
|
|
}));
|
|
});
|
|
}
|
|
}
|
|
$q.all(promiseArray).then(function () {
|
|
//scope.ticket.desc = scope.descCopy; //SW00560919 - custom filter description issue
|
|
closeEditor();
|
|
// remove attachmentToDelete from attachments
|
|
scope.ticket.attachments = scope.attachments = _.without(scope.attachments, scope.attachmentToDelete);
|
|
scope.attachmentToUpload = [];
|
|
scope.attachmentToDelete = [];
|
|
scope.$emit(events.SAVE_CHANGES_REQUEST, changes, singleMode || isDraft);
|
|
}).catch(function (error) {
|
|
scope.cancel();
|
|
if (error) {
|
|
systemAlertService.error({
|
|
text: (error.data && error.data.error) || error,
|
|
clear: false
|
|
});
|
|
}
|
|
//scope.$emit(events.SAVE_CHANGES_REQUEST, changes, singleMode || isDraft); //if wants to complete request in case of failure then uncomment this code
|
|
});
|
|
return promiseArray;
|
|
};
|
|
scope.cancel = function () {
|
|
scope.attachmentToDelete = [];
|
|
scope.attachmentToUpload = [];
|
|
scope.descCopy = _.clone(scope.ticket.desc); //to avoid having edited text back in view mode
|
|
scope.attachments = _.cloneDeep(originalAttachment); //to avoid attachment edit reflecting in view mode
|
|
scope.ticket.attachments = _.cloneDeep(originalAttachment);
|
|
if (scope.data) {
|
|
scope.data.value.desc = scope.descCopy;
|
|
scope.$emit(events.WIDGET_VALUE_CHANGE, { fieldName: scope.data.name });
|
|
}
|
|
closeEditor();
|
|
};
|
|
/**
|
|
* Handle ticket description change
|
|
*/
|
|
scope.onTicketDescriptionChange = function () {
|
|
var elem = element.find('.ticket-summary__content');
|
|
var textAreaObject = elem.find('textarea.content');
|
|
var editableContent = angular.element.find('.editable-content-section__content');
|
|
var prevHeight = textAreaObject[0].offsetHeight;
|
|
var height = textAreaObject[0].scrollHeight;
|
|
textAreaObject.css('height', 'auto');
|
|
if (height > 0) {
|
|
textAreaObject.css('height', height + 'px');
|
|
if (editableContent && editableContent.length) {
|
|
jQuery(editableContent).scrollTop(editableContent[0].scrollTop + (height - prevHeight));
|
|
}
|
|
}
|
|
scope.$emit(events.FIELD_FORM_IS_DIRTY);
|
|
scope.state.descriptionChanged = true;
|
|
if (scope.context === 'create') {
|
|
scope.ticket.desc = scope.descCopy;
|
|
}
|
|
if (scope.descLimit && scope.ticket.desc && scope.ticket.desc.length > scope.descLimit) {
|
|
scope.ticket.desc = $filter('limitTo')(scope.ticket.desc, scope.descLimit);
|
|
}
|
|
if (scope.data && (scope.descCopy || scope.descCopy === '') && scope.data.value.desc !== scope.descCopy) {
|
|
scope.data.value.desc = scope.descCopy;
|
|
scope.$emit(events.WIDGET_VALUE_CHANGE, { fieldName: scope.data.name, memberName: scope.data.name });
|
|
}
|
|
};
|
|
if (scope.descLimit > 0) {
|
|
element.find('.content').attr('maxlength', scope.descLimit);
|
|
}
|
|
scope.viewSummaryExpandable = function () {
|
|
var maxDescriptionHeight = 100, elem = element.find('.ticket-summary__content'), viewSummaryDiv = elem.find('div.content');
|
|
return !scope.editMode
|
|
&& scope.context !== "create"
|
|
&& (viewSummaryDiv[0].scrollHeight > maxDescriptionHeight
|
|
|| scope.attachmentsCountOverLimit() > 0);
|
|
};
|
|
scope.summaryExpandable = function () {
|
|
var maxDescriptionHeight = 100;
|
|
return !scope.editMode
|
|
&& scope.context !== "create"
|
|
&& (textAreaObject[0].scrollHeight > maxDescriptionHeight
|
|
|| scope.attachmentsCountOverLimit() > 0);
|
|
};
|
|
scope.attachmentsCountOverLimit = function () {
|
|
var attachmentCountLimit = 4;
|
|
return (scope.attachments || []).length > attachmentCountLimit
|
|
? scope.attachments.length - attachmentCountLimit
|
|
: 0;
|
|
};
|
|
scope.summaryCollapsed = function () {
|
|
return !scope.summaryExpanded && scope.summaryExpandable();
|
|
};
|
|
scope.viewSummaryCollapsed = function () {
|
|
return !scope.summaryExpanded && scope.viewSummaryExpandable();
|
|
};
|
|
scope.showMoreVisible = function () {
|
|
return !scope.summaryExpanded && scope.viewSummaryExpandable();
|
|
};
|
|
scope.showLessVisible = function () {
|
|
return scope.summaryExpanded && scope.viewSummaryExpandable();
|
|
};
|
|
scope.toggleSummary = function () {
|
|
scope.summaryExpanded = !scope.summaryExpanded;
|
|
$timeout(function () {
|
|
textAreaObject.scrollTop(0);
|
|
});
|
|
};
|
|
scope.filteredAttachments = function () {
|
|
return scope.summaryCollapsed()
|
|
? scope.attachments.slice(0, 4)
|
|
: scope.attachments;
|
|
};
|
|
/**
|
|
* Close edit mode.
|
|
*/
|
|
function closeEditor() {
|
|
scope.editMode = false;
|
|
scope.state.descriptionChanged = false;
|
|
if (!scope.updateIsHandledByParent || draftModeEnabled()) {
|
|
scope.$emit(events.SAVE_CHANGES_COMPLETE);
|
|
}
|
|
}
|
|
function handleToggleEditMode() {
|
|
if (!scope.editMode && !scope.editDisabled) {
|
|
scope.edit();
|
|
}
|
|
}
|
|
function handleSaveChanges() {
|
|
scope.save();
|
|
}
|
|
function handleSaveAllChangesComplete() {
|
|
scope.ticket.desc = scope.descCopy;
|
|
if (scope.updateIsHandledByParent && !scope.ticket.isDraft) {
|
|
closeEditor();
|
|
}
|
|
}
|
|
function handleDiscardChanges() {
|
|
scope.cancel();
|
|
}
|
|
function adjustDescriptionHeight() {
|
|
var scrollHeightAdjustment = 14;
|
|
$timeout(function () {
|
|
textAreaObject && textAreaObject.height(textAreaObject[0].scrollHeight - scrollHeightAdjustment);
|
|
});
|
|
}
|
|
function afterSaveChangesHandler() {
|
|
var descField = objectValueMapperService.getFieldByName('desc');
|
|
if (!scope.ticket.isDraft && descField) {
|
|
descField.value.attachmentToUpload = [];
|
|
descField.value.attachmentToDelete = [];
|
|
scope.attachmentToDelete = [];
|
|
scope.attachmentToUpload = [];
|
|
scope.attachments = _.cloneDeep(scope.ticket.attachments);
|
|
originalAttachment = _.cloneDeep(scope.ticket.attachments);
|
|
}
|
|
closeEditor();
|
|
}
|
|
init();
|
|
}
|
|
};
|
|
}]);
|
|
}());
|