parent
e5d3c5fa33
commit
f0b4e1b719
1
NEWS
1
NEWS
|
@ -13,6 +13,7 @@ Bug fixes
|
|||
- [core] avoid displaying empty signed emails when using GNU TLS (#4433)
|
||||
- [web] improve popup window detection in message viewer (#4518)
|
||||
- [web] enable save button when editing the members of a list
|
||||
- [web] restore caret position when replying or forwarding a message (#4517)
|
||||
|
||||
4.0.1 (2018-07-10)
|
||||
------------------
|
||||
|
|
|
@ -72,7 +72,7 @@
|
|||
md-add-on-blur="true">
|
||||
<md-autocomplete
|
||||
class="sg-chips-autocomplete"
|
||||
md-autofocus="true"
|
||||
md-autofocus="editor.isNew()"
|
||||
md-search-text="editor.autocomplete.to.searchText"
|
||||
md-selected-item="editor.autocomplete.to.selected"
|
||||
md-items="user in editor.contactFilter(editor.autocomplete.to.searchText)"
|
||||
|
@ -244,10 +244,13 @@
|
|||
<!-- MESSAGE CONTENT -->
|
||||
<md-input-container class="md-block sg-mail-editor-content">
|
||||
<textarea name="content" var:class="editorClass"
|
||||
rows="9"
|
||||
ck-locale="editor.localeCode"
|
||||
ck-margin="16px"
|
||||
rows="9"
|
||||
ck-focus="editor.onHTMLFocus($event)"
|
||||
ng-model="editor.message.editable.text"
|
||||
ng-focus="editor.onTextFocus($event)"
|
||||
md-autofocus="!editor.isNew()"
|
||||
md-no-resize="md-no-resize"
|
||||
md-detect-hidden="md-detect-hidden" />
|
||||
</md-input-container>
|
||||
|
|
|
@ -509,6 +509,33 @@ Date.prototype.format = function(localeProvider, format) {
|
|||
return date.join('');
|
||||
};
|
||||
|
||||
Element.prototype.setCaretTo = function(pos) {
|
||||
if (this.setSelectionRange) { // For Mozilla and Safari
|
||||
this.focus();
|
||||
this.setSelectionRange(pos, pos);
|
||||
}
|
||||
else if (this.createTextRange) { // For IE
|
||||
var range = this.createTextRange();
|
||||
range.move('character', pos);
|
||||
range.select();
|
||||
}
|
||||
};
|
||||
|
||||
Element.prototype.selectText = function(start, end) {
|
||||
if (this.setSelectionRange) { // For Mozilla and Safari
|
||||
this.setSelectionRange(start, end);
|
||||
}
|
||||
else if (this.createTextRange) { // For IE
|
||||
var textRange = this.createTextRange();
|
||||
textRange.moveStart('character', start);
|
||||
textRange.moveEnd('character', end-element.value.length);
|
||||
textRange.select();
|
||||
}
|
||||
else {
|
||||
this.select();
|
||||
}
|
||||
};
|
||||
|
||||
/* Functions */
|
||||
|
||||
function l() {
|
||||
|
|
|
@ -171,7 +171,7 @@
|
|||
};
|
||||
|
||||
this.newMessage = function($event, inPopup) {
|
||||
var message;
|
||||
var message, onCompleteDeferred = $q.defer();
|
||||
|
||||
if (vm.messageDialog === null) {
|
||||
if (inPopup || Preferences.defaults.SOGoMailComposeWindow == 'popup')
|
||||
|
@ -187,9 +187,15 @@
|
|||
templateUrl: 'UIxMailEditor',
|
||||
controller: 'MessageEditorController',
|
||||
controllerAs: 'editor',
|
||||
onComplete: function (scope, element) {
|
||||
return onCompleteDeferred.resolve(element);
|
||||
},
|
||||
locals: {
|
||||
stateAccount: vm.account,
|
||||
stateMessage: message
|
||||
stateMessage: message,
|
||||
onCompletePromise: function () {
|
||||
return onCompleteDeferred.promise;
|
||||
}
|
||||
}
|
||||
})
|
||||
.catch(_.noop) // Cancel
|
||||
|
|
|
@ -6,8 +6,8 @@
|
|||
/**
|
||||
* @ngInject
|
||||
*/
|
||||
MessageController.$inject = ['$window', '$scope', '$state', '$mdMedia', '$mdDialog', 'sgConstant', 'stateAccounts', 'stateAccount', 'stateMailbox', 'stateMessage', 'sgHotkeys', 'encodeUriFilter', 'sgSettings', 'ImageGallery', 'sgFocus', 'Dialog', 'Preferences', 'Calendar', 'Component', 'Account', 'Mailbox', 'Message'];
|
||||
function MessageController($window, $scope, $state, $mdMedia, $mdDialog, sgConstant, stateAccounts, stateAccount, stateMailbox, stateMessage, sgHotkeys, encodeUriFilter, sgSettings, ImageGallery, focus, Dialog, Preferences, Calendar, Component, Account, Mailbox, Message) {
|
||||
MessageController.$inject = ['$window', '$scope', '$q', '$state', '$mdMedia', '$mdDialog', 'sgConstant', 'stateAccounts', 'stateAccount', 'stateMailbox', 'stateMessage', 'sgHotkeys', 'encodeUriFilter', 'sgSettings', 'ImageGallery', 'sgFocus', 'Dialog', 'Preferences', 'Calendar', 'Component', 'Account', 'Mailbox', 'Message'];
|
||||
function MessageController($window, $scope, $q, $state, $mdMedia, $mdDialog, sgConstant, stateAccounts, stateAccount, stateMailbox, stateMessage, sgHotkeys, encodeUriFilter, sgSettings, ImageGallery, focus, Dialog, Preferences, Calendar, Component, Account, Mailbox, Message) {
|
||||
var vm = this, popupWindow = null, hotkeys = [];
|
||||
|
||||
this.$onInit = function() {
|
||||
|
@ -277,6 +277,7 @@
|
|||
|
||||
function _showMailEditor($event, message) {
|
||||
if (_messageDialog() === null) {
|
||||
var onCompleteDeferred = $q.defer();
|
||||
_messageDialog(
|
||||
$mdDialog
|
||||
.show({
|
||||
|
@ -287,9 +288,15 @@
|
|||
templateUrl: 'UIxMailEditor',
|
||||
controller: 'MessageEditorController',
|
||||
controllerAs: 'editor',
|
||||
onComplete: function (scope, element) {
|
||||
return onCompleteDeferred.resolve(element);
|
||||
},
|
||||
locals: {
|
||||
stateAccount: vm.account,
|
||||
stateMessage: message
|
||||
stateMessage: message,
|
||||
onCompletePromise: function () {
|
||||
return onCompleteDeferred.promise;
|
||||
}
|
||||
}
|
||||
})
|
||||
.catch(_.noop) // Cancel
|
||||
|
|
|
@ -6,8 +6,8 @@
|
|||
/**
|
||||
* @ngInject
|
||||
*/
|
||||
MessageEditorController.$inject = ['$scope', '$window', '$stateParams', '$mdConstant', '$mdUtil', '$mdDialog', '$mdToast', 'FileUploader', 'stateAccount', 'stateMessage', 'encodeUriFilter', '$timeout', 'Dialog', 'AddressBook', 'Card', 'Preferences'];
|
||||
function MessageEditorController($scope, $window, $stateParams, $mdConstant, $mdUtil, $mdDialog, $mdToast, FileUploader, stateAccount, stateMessage, encodeUriFilter, $timeout, Dialog, AddressBook, Card, Preferences) {
|
||||
MessageEditorController.$inject = ['$scope', '$window', '$stateParams', '$mdConstant', '$mdUtil', '$mdDialog', '$mdToast', 'FileUploader', 'stateAccount', 'stateMessage', 'onCompletePromise', 'encodeUriFilter', '$timeout', 'sgFocus', 'Dialog', 'AddressBook', 'Card', 'Preferences'];
|
||||
function MessageEditorController($scope, $window, $stateParams, $mdConstant, $mdUtil, $mdDialog, $mdToast, FileUploader, stateAccount, stateMessage, onCompletePromise, encodeUriFilter, $timeout, focus, Dialog, AddressBook, Card, Preferences) {
|
||||
var vm = this;
|
||||
|
||||
this.$onInit = function() {
|
||||
|
@ -33,6 +33,7 @@
|
|||
vm.send = send;
|
||||
vm.sendState = false;
|
||||
vm.toggleFullscreen = toggleFullscreen;
|
||||
this.firstFocus = true;
|
||||
|
||||
_initFileUploader();
|
||||
|
||||
|
@ -43,6 +44,12 @@
|
|||
// Set the locale of CKEditor
|
||||
vm.localeCode = Preferences.defaults.LocaleCode;
|
||||
|
||||
this.replyPlacement = Preferences.defaults.SOGoMailReplyPlacement;
|
||||
if (this.message.origin && this.message.origin.action == 'forward') {
|
||||
// For forwards, place caret at top unconditionally
|
||||
this.replyPlacement = 'above';
|
||||
}
|
||||
|
||||
// Destroy file uploader when the controller is being deactivated
|
||||
$scope.$on('$destroy', function() { vm.uploader.destroy(); });
|
||||
|
||||
|
@ -338,6 +345,96 @@
|
|||
vm.autosave = $timeout(vm.autosaveDrafts, Preferences.defaults.SOGoMailAutoSave*1000*60);
|
||||
}
|
||||
|
||||
this.isNew = function () {
|
||||
return typeof this.message.origin == 'undefined';
|
||||
};
|
||||
|
||||
this.onTextFocus = function ($event) {
|
||||
var textArea = $event.target;
|
||||
|
||||
function adjustOffset(val, offset) {
|
||||
var newOffset = offset, matches;
|
||||
if (val.indexOf("\r\n") > -1) {
|
||||
matches = val.replace(/\r\n/g, "\n").slice(0, offset).match(/\n/g);
|
||||
newOffset -= matches ? matches.length - 1 : 0;
|
||||
}
|
||||
return newOffset;
|
||||
}
|
||||
|
||||
if (this.firstFocus) {
|
||||
onCompletePromise().then(function(element) {
|
||||
var textContent = angular.element(textArea).val(),
|
||||
hasSignature = (Preferences.defaults.SOGoMailSignature &&
|
||||
Preferences.defaults.SOGoMailSignature.length > 0),
|
||||
signatureLength = 0,
|
||||
sigLimit,
|
||||
caretPosition;
|
||||
|
||||
if (vm.replyPlacement == 'above') {
|
||||
textArea.setCaretTo(0);
|
||||
element.find('md-dialog-content')[0].scrollTop = 0;
|
||||
}
|
||||
else {
|
||||
if (hasSignature) {
|
||||
sigLimit = textContent.lastIndexOf("--");
|
||||
if (sigLimit > -1)
|
||||
signatureLength = (textContent.length - sigLimit);
|
||||
}
|
||||
caretPosition = textContent.length - signatureLength;
|
||||
caretPosition = adjustOffset(textContent, caretPosition);
|
||||
if (hasSignature)
|
||||
caretPosition -= 2;
|
||||
textArea.setCaretTo(caretPosition);
|
||||
}
|
||||
});
|
||||
|
||||
this.firstFocus = false;
|
||||
}
|
||||
};
|
||||
|
||||
this.onHTMLFocus = function ($event) {
|
||||
var caretAtTop = (this.replyPlacement == 'above');
|
||||
|
||||
if (this.firstFocus) {
|
||||
onCompletePromise().then(function(element) {
|
||||
var selected = $event.editor.getSelection(),
|
||||
selected_ranges = selected.getRanges(),
|
||||
children = $event.editor.document.getBody().getChildren(),
|
||||
node;
|
||||
|
||||
if (caretAtTop) {
|
||||
node = children.getItem(0);
|
||||
}
|
||||
else {
|
||||
// Search for signature starting from bottom
|
||||
node = children.getItem(children.count() - 1);
|
||||
while (true) {
|
||||
var x = node.getPrevious();
|
||||
if (x === null) {
|
||||
break;
|
||||
}
|
||||
if (x.getText() == '--') {
|
||||
node = x.getPrevious().getPrevious();
|
||||
break;
|
||||
}
|
||||
node = x;
|
||||
}
|
||||
}
|
||||
selected.selectElement(node);
|
||||
|
||||
// Place the caret
|
||||
if (caretAtTop)
|
||||
selected.scrollIntoView(); // top
|
||||
selected_ranges = selected.getRanges();
|
||||
selected_ranges[0].collapse(true);
|
||||
selected.selectRanges(selected_ranges);
|
||||
if (!caretAtTop)
|
||||
selected.scrollIntoView(); // bottom
|
||||
});
|
||||
|
||||
this.firstFocus = false;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
SendMessageToastController.$inject = ['$scope', '$mdToast'];
|
||||
|
|
|
@ -81,6 +81,17 @@
|
|||
|
||||
ck = CKEDITOR.replace(elm[0], options);
|
||||
|
||||
if (attr.mdAutofocus && $parse(attr.mdAutofocus)($scope)) {
|
||||
// Autofocus is enabled
|
||||
ck.on('instanceReady', function(event) {
|
||||
ck.focus();
|
||||
});
|
||||
ck.on('focus', function(event) {
|
||||
if (attr.ckFocus) {
|
||||
$parse(attr.ckFocus)($scope, {'$event': event});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Update the model whenever the content changes
|
||||
ck.on('change', function() {
|
||||
|
|
Loading…
Reference in New Issue