"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(); } }; }]); }());