SmartIT_Extensions/BMC/smart-it-full-helix/components/email/email-window-directive.js

493 lines
30 KiB
JavaScript

"use strict";
(function () {
'use strict';
/**
* @ngdoc directive
* @name myitsmApp:emailWindow
* @restrict E
* @desription Email windows are used for user to send email based on context of current ticket. It's a draggable window which contains
* email controls bar. From email window one can search and include email recipients from ITSM DB . Once email is sent, it will also be logged to instance timeline
*/
angular.module('myitsmApp')
//TODO: remove not use dependency
.directive('emailWindow', ['$window', '$filter', '$timeout', '$state', 'ticketModel', 'emailModel', 'systemAlertService', 'events', 'accelerators', 'i18nService', 'relationModel', 'browser', 'elementFocus',
function ($window, $filter, $timeout, $state, ticketModel, emailModel, systemAlertService, events, accelerators, i18nService, relationModel, browser, elementFocus) {
return {
restrict: 'E',
templateUrl: 'components/email/email-window.html',
replace: true,
scope: {
emailInstance: '=',
isPopupWindow: '@'
},
link: function (scope, $element) {
var $textArea = $element.find('textarea.email__message-editor'), typeAheadMode = false, gotFullMatch = false, actualTypeAheadText, expressionStart = 0, expressionEnd = 0;
var keyCodes = {
enter: 13,
escape: 27,
tab: 9,
leftArrow: 37,
rightArrow: 39,
upArrow: 38,
downArrow: 40,
space: 32
};
var preventDefaultForKeys = [keyCodes.leftArrow, keyCodes.rightArrow, keyCodes.enter, keyCodes.upArrow, keyCodes.downArrow, keyCodes.tab];
var popupMode = !!scope.isPopupWindow;
if (popupMode) {
$window.document.body.style.minWidth = 0;
$window.document.body.style.overflow = 'hidden';
scope.emailContext = scope.emailInstance.parentEmailContext;
}
else {
$element.draggable({ containment: 'parent', scroll: false, handle: '.drag-handle' });
scope.emailContext = {
headerText: '',
recipientInputText: '',
senderList: scope.emailInstance.senderList || [],
subjectText: '',
messageBody: ''
};
if (scope.emailInstance.ticketData.length > 1) {
scope.emailContext.ticketList = _.map(scope.emailInstance.ticketData, function (ticket) {
return {
id: ticket.id,
type: ticket.type
};
});
}
else {
scope.emailContext.ticketType = scope.emailInstance.ticketData[0].type;
scope.emailContext.ticketId = scope.emailInstance.ticketData[0].id;
}
}
scope.sendingEmail = false;
scope.showArticles = false;
scope.articlesLoading = false;
scope.emailActions = {};
scope.accelerators = {
expression: '',
showAcceleratorsList: false,
availableExpressions: []
};
if (!scope.emailContext.ticketList) {
scope.accelerators.availableExpressions = _.filter(accelerators.ticket, function (accelerator) {
if (accelerator.onlyFor && accelerator.onlyFor.indexOf(scope.emailInstance.ticketData[0].type) < 0) {
return false;
}
accelerator.expr = '!' + i18nService.getLocalizedString(accelerator.key);
accelerator.text = scope.$eval(accelerator.valueExpr, { exprContext: scope.emailInstance.ticketData[0] });
if (accelerator.type) {
accelerator.text = $filter('localizeLabel')(accelerator.text, accelerator.type, scope.emailInstance.ticketData[0].type);
}
return accelerator.text ? accelerator.text.trim() : false;
});
}
/*List of available email window actions*/
scope.emailControls = [
{
iconClass: 'icon-trash',
name: 'deleteEmail',
inactive: false
},
{
iconClass: 'icon-pop_up',
name: 'popoutEmail',
inactive: !!scope.isPopupWindow
}
];
/*Email controls click event transmitter. Uses controls item, to execute applicable click handler */
scope.handleEmailControlsClick = function ($e, action) {
return scope.emailActions[action.name]();
};
//Backend allows 255 bytes, not 255 characters, so checking input length in bytes.
scope.checkByteLength = function (maxSize) {
if (scope.emailContext.subjectText.length > maxSize) {
scope.emailContext.subjectText = scope.emailContext.subjectText.substring(0, maxSize);
}
var byteLength = new Blob([scope.emailContext.subjectText]).size, strLength = scope.emailContext.subjectText.length;
while (byteLength > maxSize) {
strLength = strLength - Math.ceil((byteLength - strLength) / 4); //Max length of a character in UTF-8 is 4 byte currently
scope.emailContext.subjectText = scope.emailContext.subjectText.substring(0, strLength);
byteLength = new Blob([scope.emailContext.subjectText]).size;
}
};
/**
* @ngdoc method
* @name myitsmApp.emailWindow#leaveEmail
* @methodOf myitsmApp.emailWindow
*
* @description
* Action can be triggered from email controls, by clicking leave email button. It prompts
* confirmation modal to ensure user is intended to delete the email.
*
*/
scope.emailActions.deleteEmail = function () {
var modalSettings = {
title: $filter('i18n')('email.deleteEmailModal.title'),
text: $filter('i18n')('email.deleteEmailModal.label'),
buttons: [
{
text: $filter('i18n')('email.button.keepEmail')
},
{
text: $filter('i18n')('email.button.deleteEmail'),
data: { delete: true }
}
]
};
var modalInstance = systemAlertService.modal(modalSettings);
modalInstance.result.then(function (data) {
if (!_.isEmpty(data)) {
if (data.delete) {
if (scope.isPopupWindow) {
$window.close();
}
emailModel.deleteEmailWindow(scope.emailInstance);
}
}
});
};
scope.emailActions.popoutEmail = function () {
scope.emailInstance.parentEmailContext = scope.emailContext;
$window.popupDetails = { emailWindow: scope.emailInstance };
var width = Math.round((screen.width / 3) / 10) * 10, height = Math.round(screen.height * 70 / 100);
$window.open('#/emailPopupWindow', '', 'location=no, scrollbars=no, resizable=yes, width=' + width + ', height=' + height);
emailModel.deleteEmailWindow(scope.emailInstance);
};
scope.sendEmail = function () {
var inputValue = $element.find('.email__recipient-list span').text(), rootScope = scope.$root, validEmailInput = false;
if (inputValue) {
validEmailInput = validateEmail(inputValue);
}
var internalRecip = _.filter(scope.emailContext.senderList, { internal: true }), externalRecip = _.filter(scope.emailContext.senderList, { internal: false });
if (validEmailInput) {
externalRecip.push({ email: inputValue, internal: false });
}
try {
rootScope = window.opener.angular.element('body').scope();
}
catch (e) {
}
scope.sendingEmail = true;
emailModel.sendEmail(scope.emailContext, internalRecip, externalRecip)
.finally(function () {
_.delay(function () {
rootScope.$broadcast(events.REFRESH_ACTIVITY_FEED);
}, 400);
emailModel.deleteEmailWindow(scope.emailInstance);
if (scope.isPopupWindow) {
window.close();
}
});
};
scope.handleEmailBodyKeyDown = function ($event) {
// disable for bulk email
if (scope.emailContext.ticketList) {
return;
}
if (($event.which === 49 || $event.keyCode === 49) && $event.shiftKey) {
scope.accelerators.showAcceleratorsList = true;
expressionStart = $event.target.selectionStart;
typeAheadMode = true;
// Tere is a open defect on IE 11, for incorrect definition of selectionStart propery after Enter click
// https://developer.microsoft.com/en-us/microsoft-edge/platform/issues/14692189/
// Workaround for IE
if (browser.isIE11 && $textArea[0].value.length < expressionStart) {
expressionStart = $textArea[0].value.length;
}
}
if (scope.accelerators.showAcceleratorsList && (preventDefaultForKeys.indexOf($event.keyCode) >= 0 || ($event.keyCode == keyCodes.space && gotFullMatch))) {
$event.preventDefault();
}
};
scope.handleEmailBodyChange = function ($event) {
// disable for bulk email
if (scope.emailContext.ticketList) {
return;
}
expressionEnd = $event.target.selectionEnd;
$textArea = $element.find('textarea.email__message-editor');
var textArea = $textArea[0];
if (textArea.value.length > 1 && typeAheadMode && (expressionStart > expressionEnd)) {
scope.accelerators.showAcceleratorsList = false;
return;
}
// When text blocks were cut and paste to the text area, individual adjacent text block could be formed
// Need to normalize textArea to merge adjacent text nodes before processing
if (textArea.normalize) {
textArea.normalize();
}
if (scope.accelerators.showAcceleratorsList) {
if (gotFullMatch && [keyCodes.tab, keyCodes.rightArrow, keyCodes.space].indexOf($event.keyCode) >= 0) {
scope.insertAcceleratorText(scope.acceleratorsList[0]);
gotFullMatch = false;
return;
}
if (typeAheadMode && ($event.keyCode == keyCodes.tab)) {
var emailText = textArea.value;
(scope.typeAheadListPos == scope.acceleratorsList.length - 1) ? scope.typeAheadListPos = 0 : scope.typeAheadListPos++;
var selectedItem = scope.acceleratorsList[scope.typeAheadListPos] ? scope.acceleratorsList[scope.typeAheadListPos].expr : '';
var updatedText = [emailText.slice(0, expressionStart), selectedItem, emailText.slice(expressionEnd)].join('');
expressionEnd = parseInt(expressionStart + selectedItem.length);
actualTypeAheadText = updatedText.substring(expressionStart, expressionEnd);
scope.emailContext.messageBody = updatedText;
$timeout(function () {
textArea.setSelectionRange(expressionStart, expressionEnd);
textArea.selectionStart = expressionStart;
textArea.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) { // up key
if (scope.typeAheadListPos > 0) {
scope.typeAheadListPos--;
}
return;
}
else if ($event.keyCode === keyCodes.downArrow) { // down key
if (scope.typeAheadListPos < (scope.acceleratorsList.length - 1)) {
scope.typeAheadListPos++;
}
return;
}
else if ($event.keyCode === keyCodes.enter) { // enter key
scope.accelerators.expression = actualTypeAheadText;
var selectedItem = _.filter(scope.accelerators.availableExpressions, { expr: scope.accelerators.expression })[0];
if (selectedItem) {
scope.insertAcceleratorText(selectedItem);
}
else {
scope.insertAcceleratorText(scope.acceleratorsList[scope.typeAheadListPos]);
}
return;
}
else if ($event.keyCode === keyCodes.escape) {
scope.disableTypeAheadMode();
return;
}
}
if (scope.emailContext.messageBody.length > 0) {
if (typeAheadMode) {
actualTypeAheadText = textArea.value.substring(expressionStart, expressionEnd);
scope.typeAheadListPos = 0;
}
}
else if (typeAheadMode) {
scope.hideTypeAheadPopup();
}
if (typeAheadMode) {
if (actualTypeAheadText.length > 0 && actualTypeAheadText != scope.accelerators.expression) {
scope.acceleratorsList = $filter('filter')(scope.accelerators.availableExpressions, { expr: 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;
if (scope.acceleratorsList.length === 1) {
var accelerator = scope.acceleratorsList[0], regExpr = new RegExp(accelerator.expr, 'gi');
gotFullMatch = regExpr.test(actualTypeAheadText);
if (gotFullMatch) {
scope.emailContext.messageBody.replace(regExpr, accelerator.expr);
textArea.setSelectionRange(expressionStart, expressionEnd);
textArea.selectionStart = expressionStart;
textArea.selectionEnd = expressionEnd;
}
}
}
else if (scope.acceleratorsList.length === 0 && scope.accelerators.expression.length >= 1) {
scope.hideTypeAheadPopup();
scope.accelerators.expression = "";
}
}
}
};
scope.handleEmailBodyClick = function ($event) {
expressionEnd = $event.target.selectionEnd;
$textArea = $element.find('textarea.email__message-editor');
if ($textArea[0] && $textArea[0].value.length > 1 && scope.accelerators.showAcceleratorsList && expressionStart > expressionEnd) {
scope.accelerators.showAcceleratorsList = false;
return;
}
};
scope.handleTextPaste = function ($event) {
$textArea = $element.find('textarea.email__message-editor');
var content;
$event.preventDefault();
$event.stopImmediatePropagation();
if ($event.originalEvent && $event.originalEvent.clipboardData) {
content = $event.originalEvent.clipboardData.getData('text/plain');
}
else if ($window.clipboardData) {
content = $window.clipboardData.getData('Text');
}
else {
return; // not support, let the default handle it
}
var parsedContentVersions = [content];
_(scope.accelerators.availableExpressions).forEach(function (accelerator, index) {
var updatedText = parsedContentVersions[index];
var regExpr = new RegExp(accelerator.expr, 'gi'), replaceText = accelerator.text;
parsedContentVersions.push(updatedText.replace(regExpr, replaceText));
});
var emailText = $textArea[0].value, start = 0, end = 0;
if ($event.target.selectionStart == $event.target.selectionEnd && $event.target.selectionStart > 0) {
start = end = $event.target.selectionStart;
}
else if ($event.target.selectionStart != $event.target.selectionEnd) {
if ($event.target.selectionStart > $event.target.selectionEnd) {
start = $event.target.selectionEnd;
end = $event.target.selectionStart;
}
else {
start = $event.target.selectionStart;
end = $event.target.selectionEnd;
}
}
var pasteText = parsedContentVersions.pop();
scope.emailContext.messageBody = emailText.substring(0, start) + pasteText + emailText.substring(end, emailText.length);
};
scope.acceleratorMouseover = function ($index) {
scope.typeAheadListPos = $index;
};
scope.insertAcceleratorText = function (selectedItem) {
var emailText = $textArea[0].value, accelerator = selectedItem || scope.acceleratorsList[scope.typeAheadListPos], insertText = accelerator.text, updatedText = emailText;
if (scope.accelerators.expression) {
if (expressionStart === 0) {
updatedText = insertText + ' ';
}
else {
insertText += ' ';
updatedText = [emailText.slice(0, expressionStart), insertText, emailText.slice(expressionEnd)].join('');
}
}
else {
if (emailText.length === 0) {
updatedText = insertText;
}
else {
if (expressionEnd > 0) {
updatedText = [emailText.slice(0, expressionEnd), ' ' + insertText + ' ', emailText.slice(expressionEnd)].join('');
}
else {
updatedText += ' ' + insertText;
}
}
}
scope.disableTypeAheadMode();
scope.emailContext.messageBody = updatedText;
};
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.emailContext.messageBody.length;
$textArea = $element.find('textarea.email__message-editor');
$textArea.focus();
};
scope.addEmailAttachment = function (fileInput) {
if (scope.emailContext.attachment) {
return false;
}
scope.emailContext.attachment = emailModel.addAttachment(fileInput);
elementFocus('emailBtn');
};
scope.openPinnedArticles = function () {
//scope.articlesLoading = true;
scope.showArticles = true;
};
scope.selectResource = function ($event, resource) {
$event.stopPropagation();
scope.emailContext.kba = (scope.emailContext.kba === resource) ? null : resource;
};
scope.isResourceSelected = function (resource) {
return (scope.emailContext.kba && scope.emailContext.kba.displayId === resource.displayId);
};
scope.attachKBA = function () {
scope.showArticles = false;
};
scope.showEmailBody = function () {
scope.emailContext.kba = null;
scope.showArticles = false;
};
scope.removeKBAttachment = function () {
scope.emailContext.kba = null;
};
scope.removeEmailAttachment = function () {
if (scope.emailContext.attachment) {
scope.emailContext.attachment = null;
}
};
scope.handleAttachClick = function () {
setTimeout(function () {
document.getElementById('email-ka-attach-btn').click();
}, 0);
};
function init() {
scope.showPopOver = false;
var idField, summaryField;
if (scope.emailInstance.ticketData) {
if (scope.emailInstance.ticketData.length === 1 && scope.emailInstance.ticketData[0].type === EntityVO.TYPE_INCIDENT) {
scope.showPopOver = true;
relationModel.getRelations(scope.emailInstance.ticketData[0].id, scope.emailInstance.ticketData[0].type).then(function () {
scope.pinnedKBA = _.filter(relationModel.cache[scope.emailInstance.ticketData[0].id], function (item) {
return item.type === EntityVO.TYPE_KNOWLEDGE && item.realObject.internalUse === false && !item.isDecisionTree();
});
scope.disableKBAttachment = (scope.pinnedKBA.length) ? false : true;
scope.showArticles = false;
});
}
if (scope.emailInstance.ticketData[0].type === EntityVO.TYPE_KNOWLEDGE) {
idField = 'articleId';
summaryField = 'title';
}
else if (scope.emailInstance.ticketData[0].type === EntityVO.TYPE_SBEREQUEST) {
idField = 'id';
summaryField = 'serviceName';
}
else {
idField = 'displayId';
summaryField = 'summary';
}
if (scope.emailInstance.ticketData.length > 1) {
scope.emailContext.headerText = _.map(scope.emailInstance.ticketData, function (ticket) {
return ticket[idField];
}).join('; ');
}
else {
scope.emailContext.headerText = scope.emailInstance.ticketData[0][idField] + ' : ' + scope.emailInstance.ticketData[0][summaryField];
}
//scope.emailContext.headerText = scope.emailInstance.ticketData.displayId + ': ' + scope.emailInstance.ticketData.summary;
scope.emailContext.subjectText = scope.emailContext.headerText;
scope.checkByteLength(255); //TicketID + Summary should not be greater than 255 bytes
}
}
function validateEmail(email) {
var re = /^(([^<>()[\]\\.,;:\s@\"]+(\.[^<>()[\]\\.,;:\s@\"]+)*)|(\".+\"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/;
return re.test(email);
}
init();
}
};
}]);
}());